main.py 13 KB

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