main.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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, cmdfile=None, mapfile=None, envfile=None, monitor=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. @param envfile full path to the env file (defined by d.mon)
  49. @param monitor name of monitor (defined by d.mon)
  50. """
  51. Map.__init__(self)
  52. # environment settings
  53. self.env = dict()
  54. self.cmdfile = cmdfile
  55. self.envfile = envfile
  56. self.monitor = monitor
  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 = grass.tempfile(create = False) + '.ppm'
  62. self._writeEnvFile(self.env) # self.env is expected to be defined in parent class
  63. self._writeEnvFile({"GRASS_PNG_READ" : "TRUE"})
  64. def GetLayersFromCmdFile(self):
  65. """!Get list of map layers from cmdfile
  66. """
  67. if not self.cmdfile:
  68. return
  69. nlayers = 0
  70. try:
  71. fd = open(self.cmdfile, 'r')
  72. existingLayers = self.GetListOfLayers()
  73. for line in fd.readlines():
  74. cmd = utils.split(line.strip())
  75. ltype = None
  76. try:
  77. ltype = utils.command2ltype[cmd[0]]
  78. except KeyError:
  79. grass.warning(_("Unsupported command %s.") % cmd[0])
  80. continue
  81. name = utils.GetLayerNameFromCmd(cmd, fullyQualified = True,
  82. layerType = ltype)[0]
  83. # creating temporary layer object to compare commands
  84. # neccessary to get the same format
  85. # supposing that there are no side effects
  86. tmpMapLayer = MapLayer(ltype = ltype, name = name,
  87. cmd = cmd, Map = None,
  88. active = False, hidden = True,
  89. opacity = 0)
  90. exists = False
  91. for layer in existingLayers:
  92. if layer.GetCmd(string=True) == tmpMapLayer.GetCmd(string=True):
  93. exists = True
  94. break
  95. if exists:
  96. continue
  97. self.AddLayer(ltype = ltype, command = cmd, active = False, name = name)
  98. nlayers += 1
  99. except IOError, e:
  100. grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
  101. { 'cmd' : self.cmdfile, 'det' : e })
  102. return
  103. fd.close()
  104. Debug.msg(1, "Map.GetLayersFromCmdFile(): cmdfile=%s" % self.cmdfile)
  105. Debug.msg(1, " nlayers=%d" % nlayers)
  106. def _parseCmdFile(self):
  107. """!Parse cmd file for standalone application
  108. """
  109. nlayers = 0
  110. try:
  111. fd = open(self.cmdfile, 'r')
  112. grass.try_remove(self.mapfile)
  113. cmdLines = fd.readlines()
  114. RunCommand('g.gisenv',
  115. set = 'MONITOR_%s_CMDFILE=' % self.monitor)
  116. for cmd in cmdLines:
  117. cmdStr = utils.split(cmd.strip())
  118. cmd = utils.CmdToTuple(cmdStr)
  119. RunCommand(cmd[0], **cmd[1])
  120. nlayers += 1
  121. RunCommand('g.gisenv',
  122. set = 'MONITOR_%s_CMDFILE=%s' % (self.monitor, self.cmdfile))
  123. except IOError, e:
  124. grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
  125. { 'cmd' : self.cmdfile, 'det' : e })
  126. return
  127. fd.close()
  128. Debug.msg(1, "Map.__parseCmdFile(): cmdfile=%s" % self.cmdfile)
  129. Debug.msg(1, " nlayers=%d" % nlayers)
  130. return nlayers
  131. def _renderCmdFile(self, force, windres):
  132. if not force:
  133. return ([self.mapfileCmd],
  134. [self.maskfileCmd],
  135. ['1.0'])
  136. region = os.environ["GRASS_REGION"] = self.SetRegion(windres)
  137. self._writeEnvFile({'GRASS_REGION' : region})
  138. currMon = grass.gisenv()['MONITOR']
  139. if currMon != self.monitor:
  140. RunCommand('g.gisenv',
  141. set = 'MONITOR=%s' % self.monitor)
  142. grass.try_remove(self.mapfileCmd) # GRASS_PNG_READ is TRUE
  143. nlayers = self._parseCmdFile()
  144. if self.overlays:
  145. RunCommand('g.gisenv',
  146. unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
  147. driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
  148. if driver == 'png':
  149. os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
  150. else:
  151. os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
  152. self._renderLayers(overlaysOnly = True)
  153. del os.environ["GRASS_RENDER_IMMEDIATE"]
  154. RunCommand('g.gisenv',
  155. set = 'MONITOR=%s' % currMon)
  156. if currMon != self.monitor:
  157. RunCommand('g.gisenv',
  158. set = 'MONITOR=%s' % currMon)
  159. if nlayers > 0:
  160. return ([self.mapfileCmd],
  161. [self.maskfileCmd],
  162. ['1.0'])
  163. else:
  164. return ([], [], [])
  165. def _writeEnvFile(self, data):
  166. """!Write display-related variable to the file (used for
  167. standalone app)
  168. """
  169. if not self.envfile:
  170. return
  171. try:
  172. fd = open(self.envfile, "r")
  173. for line in fd.readlines():
  174. key, value = line.split('=')
  175. if key not in data.keys():
  176. data[key] = value
  177. fd.close()
  178. fd = open(self.envfile, "w")
  179. for k, v in data.iteritems():
  180. fd.write('%s=%s\n' % (k.strip(), str(v).strip()))
  181. except IOError, e:
  182. grass.warning(_("Unable to open file '%(file)s' for writting. Details: %(det)s") % \
  183. { 'cmd' : self.envfile, 'det' : e })
  184. return
  185. fd.close()
  186. def ChangeMapSize(self, (width, height)):
  187. """!Change size of rendered map.
  188. @param width,height map size
  189. """
  190. Map.ChangeMapSize(self, (width, height))
  191. self._writeEnvFile({'GRASS_WIDTH' : self.width,
  192. 'GRASS_HEIGHT' : self.height})
  193. def GetMapsMasksAndOpacities(self, force, windres):
  194. """!
  195. Used by Render function.
  196. @return maps, masks, opacities
  197. """
  198. return self._renderCmdFile(force, windres)
  199. class Layer(object):
  200. def __init__(self, maplayer):
  201. self._maplayer = maplayer
  202. def __getattr__(self, name):
  203. if name == 'cmd':
  204. return utils.CmdTupleToList(self._maplayer.GetCmd())
  205. elif hasattr(self._maplayer, name):
  206. return getattr(self._maplayer, name)
  207. elif name == 'maplayer':
  208. return self._maplayer
  209. elif name == 'type':
  210. return self._maplayer.GetType()
  211. #elif name == 'ctrl':
  212. elif name == 'label':
  213. return self._maplayer.GetName()
  214. #elif name == 'maplayer' : None,
  215. #elif name == 'propwin':
  216. class LayerList(object):
  217. def __init__(self, map):
  218. self._map = map
  219. # def __iter__(self):
  220. # for in :
  221. # yield
  222. def GetSelectedLayers(self, checkedOnly=True):
  223. # hidden and selected vs checked and selected
  224. items = self._map.GetListOfLayers()
  225. layers = []
  226. for item in items:
  227. layer = Layer(item)
  228. layers.append(layer)
  229. return layers
  230. class DMonGrassInterface(StandaloneGrassInterface):
  231. """!@implements GrassInterface"""
  232. def __init__(self, mapframe):
  233. StandaloneGrassInterface.__init__(self)
  234. self._mapframe = mapframe
  235. def GetLayerList(self):
  236. return LayerList(self._mapframe.GetMap())
  237. class DMonFrame(MapFrame):
  238. def OnZoomToMap(self, event):
  239. layers = self.MapWindow.GetMap().GetListOfLayers()
  240. self.MapWindow.ZoomToMap(layers = layers)
  241. class MapApp(wx.App):
  242. def OnInit(self):
  243. if not globalvar.CheckWxVersion([2, 9]):
  244. wx.InitAllImageHandlers()
  245. if __name__ == "__main__":
  246. self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
  247. self.Map = DMonMap(cmdfile = monFile['cmd'], mapfile = monFile['map'],
  248. envfile = monFile['env'], monitor = monName)
  249. else:
  250. self.Map = None
  251. # actual use of StandaloneGrassInterface not yet tested
  252. # needed for adding functionality in future
  253. giface = DMonGrassInterface(None)
  254. self.mapFrm = DMonFrame(parent = None, id = wx.ID_ANY, Map = self.Map,
  255. giface = giface, size = monSize)
  256. # FIXME: hack to solve dependency
  257. giface._mapframe = self.mapFrm
  258. # self.SetTopWindow(Map)
  259. self.mapFrm.GetMapWindow().SetAlwaysRenderEnabled(True)
  260. self.mapFrm.Show()
  261. if __name__ == "__main__":
  262. self.timer = wx.PyTimer(self.watcher)
  263. #check each 0.5s
  264. global mtime
  265. mtime = 500
  266. self.timer.Start(mtime)
  267. return True
  268. def OnExit(self):
  269. if __name__ == "__main__":
  270. # stop the timer
  271. # self.timer.Stop()
  272. # terminate thread
  273. for f in monFile.itervalues():
  274. grass.try_remove(f)
  275. def watcher(self):
  276. """!Redraw, if new layer appears (check's timestamp of
  277. cmdfile)
  278. """
  279. try:
  280. # GISBASE and other sytem enviromental variables can not be used
  281. # since the process inherited them from GRASS
  282. # raises exception when vaiable does not exists
  283. grass.gisenv()['GISDBASE']
  284. except KeyError:
  285. self.timer.Stop()
  286. return
  287. # todo: events
  288. try:
  289. currentCmdFileTime = os.path.getmtime(monFile['cmd'])
  290. if currentCmdFileTime > self.cmdTimeStamp:
  291. self.timer.Stop()
  292. self.cmdTimeStamp = currentCmdFileTime
  293. self.mapFrm.OnDraw(None)
  294. self.mapFrm.GetMap().GetLayersFromCmdFile()
  295. self.timer.Start(mtime)
  296. except OSError, e:
  297. grass.warning("%s" % e)
  298. self.timer.Stop()
  299. def GetMapFrame(self):
  300. """!Get Map Frame instance"""
  301. return self.mapFrm
  302. if __name__ == "__main__":
  303. # set command variable
  304. if len(sys.argv) < 5:
  305. print __doc__
  306. sys.exit(1)
  307. monName = sys.argv[1]
  308. monFile = { 'map' : sys.argv[2],
  309. 'cmd' : sys.argv[3],
  310. 'env' : sys.argv[4],
  311. }
  312. if len(sys.argv) >= 6:
  313. try:
  314. monSize[0] = int(sys.argv[5])
  315. except ValueError:
  316. pass
  317. if len(sys.argv) == 7:
  318. try:
  319. monSize[1] = int(sys.argv[6])
  320. except ValueError:
  321. pass
  322. import gettext
  323. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  324. grass.verbose(_("Starting map display <%s>...") % (monName))
  325. RunCommand('g.gisenv',
  326. set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
  327. gmMap = MapApp(0)
  328. # set title
  329. gmMap.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
  330. monName +
  331. " - Location: " + grass.gisenv()["LOCATION_NAME"]))
  332. gmMap.MainLoop()
  333. grass.verbose(_("Stopping map display <%s>...") % (monName))
  334. # clean up GRASS env variables
  335. env = grass.gisenv()
  336. env_name = 'MONITOR_%s' % monName
  337. for key in env.keys():
  338. if key.find(env_name) == 0:
  339. RunCommand('g.gisenv',
  340. unset = '%s' % key)
  341. if key == 'MONITOR' and env[key] == monName:
  342. RunCommand('g.gisenv',
  343. unset = '%s' % key)
  344. sys.exit(0)