main.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. """!
  2. @package mapdisp.main
  3. @brief Start Map Display as standalone application
  4. Classes:
  5. - mapdisp::DMonMap
  6. - mapdisp::Layer
  7. - mapdisp::LayerList
  8. - mapdisp::DMonGrassInterface
  9. - mapdisp::DMonFrame
  10. - mapdisp::MapApp
  11. Usage:
  12. python mapdisp/main.py monitor-identifier /path/to/map/file /path/to/command/file /path/to/env/file
  13. (C) 2006-2013 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Michael Barton
  17. @author Jachym Cepicky
  18. @author Martin Landa <landa.martin gmail.com>
  19. @author Vaclav Petras <wenzeslaus gmail.com> (MapFrameBase)
  20. @author Anna Kratochvilova <kratochanna gmail.com> (MapFrameBase)
  21. """
  22. import os
  23. import sys
  24. if __name__ == "__main__":
  25. gui_wx_path = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
  26. if gui_wx_path not in sys.path:
  27. sys.path.append(gui_wx_path)
  28. from core import globalvar
  29. import wx
  30. from core import utils
  31. from core.giface import StandaloneGrassInterface
  32. from core.gcmd import RunCommand
  33. from core.render import Map, MapLayer
  34. from core.utils import _
  35. from mapdisp.frame import MapFrame
  36. from grass.script import core as grass
  37. from core.debug import Debug
  38. from core.settings import UserSettings
  39. # for standalone app
  40. monFile = { 'cmd' : None,
  41. 'map' : None,
  42. 'env' : None,
  43. }
  44. monName = None
  45. monSize = list(globalvar.MAP_WINDOW_SIZE)
  46. class DMonMap(Map):
  47. def __init__(self, giface, cmdfile=None, mapfile=None):
  48. """!Map composition (stack of map layers and overlays)
  49. @param cmdline full path to the cmd file (defined by d.mon)
  50. @param mapfile full path to the map file (defined by d.mon)
  51. """
  52. Map.__init__(self)
  53. self._giface = giface
  54. # environment settings
  55. self.env = dict()
  56. self.cmdfile = cmdfile
  57. # list of layers for rendering added from cmd file
  58. # TODO temporary solution, layer managment by different tools in GRASS should be resovled
  59. self.ownedLayers = []
  60. if mapfile:
  61. self.mapfileCmd = mapfile
  62. self.maskfileCmd = os.path.splitext(mapfile)[0] + '.pgm'
  63. # generated file for g.pnmcomp output for rendering the map
  64. self.mapfile = monFile['map'] + '.ppm'
  65. def GetLayersFromCmdFile(self):
  66. """!Get list of map layers from cmdfile
  67. """
  68. if not self.cmdfile:
  69. return
  70. nlayers = 0
  71. try:
  72. fd = open(self.cmdfile, 'r')
  73. existingLayers = self.GetListOfLayers()
  74. # holds new rendreing order for every layer in existingLayers
  75. layersOrder = [-1] * len(self.GetListOfLayers())
  76. # next number in rendering order
  77. next_layer = 0;
  78. for line in fd.readlines():
  79. cmd = utils.split(line.strip())
  80. ltype = None
  81. try:
  82. ltype = utils.command2ltype[cmd[0]]
  83. except KeyError:
  84. grass.warning(_("Unsupported command %s.") % cmd[0])
  85. continue
  86. name = utils.GetLayerNameFromCmd(cmd, fullyQualified = True,
  87. layerType = ltype)[0]
  88. # creating temporary layer object to compare commands
  89. # neccessary to get the same format
  90. # supposing that there are no side effects
  91. tmpMapLayer = MapLayer(ltype = ltype, name = name,
  92. cmd = cmd, Map = None,
  93. active = False, hidden = True,
  94. opacity = 0)
  95. exists = False
  96. for i, layer in enumerate(existingLayers):
  97. if layer.GetCmd(string=True) == tmpMapLayer.GetCmd(string=True):
  98. exists = True
  99. if layersOrder[i] == -1:
  100. layersOrder[i] = next_layer;
  101. next_layer += 1
  102. # layer must be put higher in render order (same cmd was insered more times)
  103. # TODO delete rendurant cmds from cmd file?
  104. else:
  105. for j, l_order in enumerate(layersOrder):
  106. if l_order > layersOrder[i]:
  107. layersOrder[j] -= 1;
  108. layersOrder[i] = next_layer - 1;
  109. break
  110. if exists:
  111. continue
  112. newLayer = Map.AddLayer(self, ltype = ltype, command = cmd, active = True, name = name)
  113. existingLayers.append(newLayer)
  114. self.ownedLayers.append(newLayer)
  115. layersOrder.append(next_layer)
  116. next_layer += 1
  117. nlayers += 1
  118. reorderedLayers = [-1] * next_layer
  119. for i, layer in enumerate(existingLayers):
  120. # owned layer was not found in cmd file -> is deleted
  121. if layersOrder[i] == -1 and layer in self.ownedLayers:
  122. self.ownedLayers.remove(layer)
  123. self.DeleteLayer(layer)
  124. # other layer e. g. added by wx.vnet are added to the top
  125. elif layersOrder[i] == -1 and layer not in self.ownedLayers:
  126. reorderedLayers.append(layer)
  127. # owned layer found in cmd file is added into proper rendering position
  128. else:
  129. reorderedLayers[layersOrder[i]] = layer
  130. self.SetLayers(reorderedLayers)
  131. except IOError, e:
  132. grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
  133. { 'cmd' : self.cmdfile, 'det' : e })
  134. return
  135. fd.close()
  136. self._giface.updateMap.emit()
  137. Debug.msg(1, "Map.GetLayersFromCmdFile(): cmdfile=%s" % self.cmdfile)
  138. Debug.msg(1, " nlayers=%d" % nlayers)
  139. def Render(self, *args, **kwargs):
  140. """!Render layer to image.
  141. For input params and returned data see overridden method in Map class.
  142. """
  143. currMon = grass.gisenv()['MONITOR']
  144. RunCommand('g.gisenv',
  145. unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
  146. ret = Map.Render(self, *args, **kwargs)
  147. RunCommand('g.gisenv',
  148. set = 'MONITOR=%s' % currMon)
  149. return ret
  150. def AddLayer(self, *args, **kwargs):
  151. """!Adds generic map layer to list of layers.
  152. For input params and returned data see overridden method in Map class.
  153. """
  154. currMon = grass.gisenv()['MONITOR']
  155. RunCommand('g.gisenv',
  156. unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
  157. driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
  158. if driver == 'png':
  159. os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
  160. else:
  161. os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
  162. layer = Map.AddLayer(self, *args, **kwargs)
  163. del os.environ["GRASS_RENDER_IMMEDIATE"]
  164. RunCommand('g.gisenv',
  165. set='MONITOR=%s' % currMon)
  166. return layer
  167. class Layer(object):
  168. """!@implements core::giface::Layer"""
  169. def __init__(self, maplayer):
  170. self._maplayer = maplayer
  171. def __getattr__(self, name):
  172. if name == 'cmd':
  173. return utils.CmdTupleToList(self._maplayer.GetCmd())
  174. elif hasattr(self._maplayer, name):
  175. return getattr(self._maplayer, name)
  176. elif name == 'maplayer':
  177. return self._maplayer
  178. elif name == 'type':
  179. return self._maplayer.GetType()
  180. #elif name == 'ctrl':
  181. elif name == 'label':
  182. return self._maplayer.GetName()
  183. #elif name == 'propwin':
  184. class LayerList(object):
  185. def __init__(self, map, giface):
  186. """!@implements core::giface::LayerList"""
  187. self._map = map
  188. self._giface = giface
  189. def GetSelectedLayers(self, checkedOnly=True):
  190. # hidden and selected vs checked and selected
  191. items = self._map.GetListOfLayers()
  192. layers = []
  193. for item in items:
  194. layer = Layer(item)
  195. layers.append(layer)
  196. return layers
  197. def GetSelectedLayer(self, checkedOnly=False):
  198. """!Returns selected layer or None when there is no selected layer."""
  199. layers = self.GetSelectedLayers()
  200. if len(layers) > 0:
  201. return layers[0]
  202. else:
  203. return None
  204. def AddLayer(self, ltype, name=None, checked=None,
  205. opacity=1.0, cmd=None):
  206. """!Adds a new layer to the layer list.
  207. Launches property dialog if needed (raster, vector, etc.)
  208. @param ltype layer type (raster, vector, 3d-raster, ...)
  209. @param name layer name
  210. @param checked if True layer is checked
  211. @param opacity layer opacity level
  212. @param cmd command (given as a list)
  213. """
  214. self._map.AddLayer(ltype=ltype, command=cmd,
  215. name=name, active=True,
  216. opacity=opacity, render=True,
  217. pos=-1)
  218. # TODO: this should be solved by signal
  219. # (which should be introduced everywhere,
  220. # alternative is some observer list)
  221. self._giface.updateMap.emit(render=True, renderVector=True)
  222. def GetLayersByName(self, name):
  223. items = self._map.GetListOfLayers()
  224. layers = []
  225. for item in items:
  226. if item.GetName() == name:
  227. layer = Layer(item)
  228. layers.append(layer)
  229. return layers
  230. def GetLayerByData(self, key, value):
  231. # TODO: implementation was not tested
  232. items = self._map.GetListOfLayers()
  233. for item in items:
  234. layer = Layer(item)
  235. try:
  236. if getattr(layer, key) == value:
  237. return layer
  238. except AttributeError:
  239. pass
  240. return None
  241. class DMonGrassInterface(StandaloneGrassInterface):
  242. """!@implements GrassInterface"""
  243. def __init__(self, mapframe):
  244. StandaloneGrassInterface.__init__(self)
  245. self._mapframe = mapframe
  246. def GetLayerList(self):
  247. return LayerList(self._mapframe.GetMap(), giface=self)
  248. def GetMapWindow(self):
  249. return self._mapframe.GetMapWindow()
  250. class DMonFrame(MapFrame):
  251. def OnZoomToMap(self, event):
  252. layers = self.MapWindow.GetMap().GetListOfLayers()
  253. self.MapWindow.ZoomToMap(layers = layers)
  254. class MapApp(wx.App):
  255. def OnInit(self):
  256. if not globalvar.CheckWxVersion([2, 9]):
  257. wx.InitAllImageHandlers()
  258. # actual use of StandaloneGrassInterface not yet tested
  259. # needed for adding functionality in future
  260. giface = DMonGrassInterface(None)
  261. if __name__ == "__main__":
  262. self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
  263. self.Map = DMonMap(giface=giface, cmdfile=monFile['cmd'],
  264. mapfile = monFile['map'])
  265. else:
  266. self.Map = None
  267. self.mapFrm = DMonFrame(parent = None, id = wx.ID_ANY, Map = self.Map,
  268. giface = giface, size = monSize)
  269. # FIXME: hack to solve dependency
  270. giface._mapframe = self.mapFrm
  271. # self.SetTopWindow(Map)
  272. self.mapFrm.GetMapWindow().SetAlwaysRenderEnabled(True)
  273. self.mapFrm.Show()
  274. if __name__ == "__main__":
  275. self.timer = wx.PyTimer(self.watcher)
  276. #check each 0.5s
  277. global mtime
  278. mtime = 500
  279. self.timer.Start(mtime)
  280. return True
  281. def OnExit(self):
  282. if __name__ == "__main__":
  283. # stop the timer
  284. # self.timer.Stop()
  285. # terminate thread
  286. for f in monFile.itervalues():
  287. grass.try_remove(f)
  288. def watcher(self):
  289. """!Redraw, if new layer appears (check's timestamp of
  290. cmdfile)
  291. """
  292. try:
  293. # GISBASE and other sytem enviromental variables can not be used
  294. # since the process inherited them from GRASS
  295. # raises exception when vaiable does not exists
  296. grass.gisenv()['GISDBASE']
  297. except KeyError:
  298. self.timer.Stop()
  299. return
  300. # todo: events
  301. try:
  302. currentCmdFileTime = os.path.getmtime(monFile['cmd'])
  303. if currentCmdFileTime > self.cmdTimeStamp:
  304. self.timer.Stop()
  305. self.cmdTimeStamp = currentCmdFileTime
  306. self.mapFrm.GetMap().GetLayersFromCmdFile()
  307. self.timer.Start(mtime)
  308. except OSError, e:
  309. grass.warning("%s" % e)
  310. self.timer.Stop()
  311. def GetMapFrame(self):
  312. """!Get Map Frame instance"""
  313. return self.mapFrm
  314. if __name__ == "__main__":
  315. # set command variable
  316. if len(sys.argv) < 5:
  317. print __doc__
  318. sys.exit(1)
  319. monName = sys.argv[1]
  320. monFile = { 'map' : sys.argv[2],
  321. 'cmd' : sys.argv[3],
  322. 'env' : sys.argv[4],
  323. }
  324. if len(sys.argv) >= 6:
  325. try:
  326. monSize[0] = int(sys.argv[5])
  327. except ValueError:
  328. pass
  329. if len(sys.argv) == 7:
  330. try:
  331. monSize[1] = int(sys.argv[6])
  332. except ValueError:
  333. pass
  334. grass.verbose(_("Starting map display <%s>...") % (monName))
  335. RunCommand('g.gisenv',
  336. set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
  337. gmMap = MapApp(0)
  338. # set title
  339. gmMap.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
  340. monName +
  341. " - Location: " + grass.gisenv()["LOCATION_NAME"]))
  342. gmMap.MainLoop()
  343. grass.verbose(_("Stopping map display <%s>...") % (monName))
  344. # clean up GRASS env variables
  345. env = grass.gisenv()
  346. env_name = 'MONITOR_%s' % monName
  347. for key in env.keys():
  348. if key.find(env_name) == 0:
  349. RunCommand('g.gisenv',
  350. unset = '%s' % key)
  351. if key == 'MONITOR' and env[key] == monName:
  352. RunCommand('g.gisenv',
  353. unset = '%s' % key)
  354. sys.exit(0)