mapdisp.py 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774
  1. """!
  2. @package mapdisp.py
  3. @brief Map display with toolbar for various display management
  4. functions, and additional toolbars (vector digitizer, 3d view).
  5. Can be used either from Layer Manager or as d.mon backend.
  6. Classes:
  7. - MapFrameBase
  8. - MapFrame
  9. - MapApp
  10. Usage:
  11. python mapdisp.py monitor-identifier /path/to/map/file /path/to/command/file /path/to/env/file
  12. (C) 2006-2011 by the GRASS Development Team
  13. This program is free software under the GNU General Public
  14. License (>=v2). Read the file COPYING that comes with GRASS
  15. 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. import glob
  25. import math
  26. import tempfile
  27. import copy
  28. import globalvar
  29. import wx
  30. import wx.aui
  31. sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
  32. sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
  33. import render
  34. import toolbars
  35. import menuform
  36. import gselect
  37. import disp_print
  38. import gcmd
  39. import dbm
  40. import dbm_dialogs
  41. import globalvar
  42. import utils
  43. import gdialogs
  44. import mapdisp_statusbar as sb
  45. from debug import Debug
  46. from icon import Icons
  47. from preferences import globalSettings as UserSettings
  48. from mapdisp_window import BufferedWindow
  49. from histogram import HistFrame
  50. from wxplot import HistFrame as HistFramePyPlot, ProfileFrame, ScatterFrame
  51. from grass.script import core as grass
  52. # for standalone app
  53. monFile = { 'cmd' : None,
  54. 'map' : None,
  55. 'env' : None,
  56. }
  57. monName = None
  58. monSize = list(globalvar.MAP_WINDOW_SIZE)
  59. haveCtypes = False
  60. class MapFrameBase(wx.Frame):
  61. """!Base class for map display window
  62. Derived class must use statusbarManager or override
  63. GetProperty, SetProperty and HasProperty methods.
  64. If derived class enables and disables auto-rendering,
  65. it should override IsAutoRendered method.
  66. """
  67. def __init__(self, parent = None, id = wx.ID_ANY, title = None,
  68. style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
  69. Map = None, auimgr = None, name = None, **kwargs):
  70. """!
  71. @param toolbars array of activated toolbars, e.g. ['map', 'digit']
  72. @param Map instance of render.Map
  73. @param auimgs AUI manager
  74. @param name frame name
  75. @param kwargs wx.Frame attributes
  76. """
  77. self.Map = Map # instance of render.Map
  78. self.parent = parent
  79. wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
  80. # available cursors
  81. self.cursors = {
  82. # default: cross
  83. # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
  84. "default" : wx.StockCursor(wx.CURSOR_ARROW),
  85. "cross" : wx.StockCursor(wx.CURSOR_CROSS),
  86. "hand" : wx.StockCursor(wx.CURSOR_HAND),
  87. "pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
  88. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  89. }
  90. #
  91. # set the size & system icon
  92. #
  93. self.SetClientSize(self.GetSize())
  94. self.iconsize = (16, 16)
  95. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
  96. # toolbars
  97. self.toolbars = {}
  98. #
  99. # Fancy gui
  100. #
  101. self._mgr = wx.aui.AuiManager(self)
  102. def _initMap(self, map):
  103. """!Initialize map display, set dimensions and map region
  104. """
  105. if not grass.find_program('g.region', ['--help']):
  106. sys.exit(_("GRASS module '%s' not found. Unable to start map "
  107. "display window.") % 'g.region')
  108. self.width, self.height = self.GetClientSize()
  109. Debug.msg(2, "MapFrame._initMap():")
  110. map.ChangeMapSize(self.GetClientSize())
  111. map.region = map.GetRegion() # g.region -upgc
  112. # self.Map.SetRegion() # adjust region to match display window
  113. def SetProperty(self, name, value):
  114. """!Sets property"""
  115. self.statusbarManager.SetProperty(name, value)
  116. def GetProperty(self, name):
  117. """!Returns property"""
  118. return self.statusbarManager.GetProperty(name)
  119. def HasProperty(self, name):
  120. """!Checks whether object has property"""
  121. return self.statusbarManager.HasProperty(name)
  122. def GetPPM(self):
  123. """! Get pixel per meter
  124. @todo now computed every time, is it necessary?
  125. @todo enable user to specify ppm (and store it in UserSettings)
  126. """
  127. # TODO: need to be fixed...
  128. ### screen X region problem
  129. ### user should specify ppm
  130. dc = wx.ScreenDC()
  131. dpSizePx = wx.DisplaySize() # display size in pixels
  132. dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
  133. dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
  134. sysPpi = dc.GetPPI()
  135. comPpi = (dpSizePx[0] / dpSizeIn[0],
  136. dpSizePx[1] / dpSizeIn[1])
  137. ppi = comPpi # pixel per inch
  138. ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
  139. (ppi[1] / 2.54) * 100)
  140. Debug.msg(4, "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
  141. "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
  142. (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
  143. dpSizeIn[0], dpSizeIn[1],
  144. sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
  145. ppm[0], ppm[1]))
  146. return ppm
  147. def SetMapScale(self, value, map = None):
  148. """! Set current map scale
  149. @param value scale value (n if scale is 1:n)
  150. @param map Map instance (if none self.Map is used)
  151. """
  152. if not map:
  153. map = self.Map
  154. region = self.Map.region
  155. dEW = value * (region['cols'] / self.GetPPM()[0])
  156. dNS = value * (region['rows'] / self.GetPPM()[1])
  157. region['n'] = region['center_northing'] + dNS / 2.
  158. region['s'] = region['center_northing'] - dNS / 2.
  159. region['w'] = region['center_easting'] - dEW / 2.
  160. region['e'] = region['center_easting'] + dEW / 2.
  161. # add to zoom history
  162. self.GetWindow().ZoomHistory(region['n'], region['s'],
  163. region['e'], region['w'])
  164. def GetMapScale(self, map = None):
  165. """! Get current map scale
  166. @param map Map instance (if none self.Map is used)
  167. """
  168. if not map:
  169. map = self.Map
  170. region = map.region
  171. ppm = self.GetPPM()
  172. heightCm = region['rows'] / ppm[1] * 100
  173. widthCm = region['cols'] / ppm[0] * 100
  174. Debug.msg(4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" %
  175. (widthCm, heightCm))
  176. xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
  177. yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
  178. scale = (xscale + yscale) / 2.
  179. Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
  180. (xscale, yscale, scale))
  181. return scale
  182. def GetProgressBar(self):
  183. """!Returns progress bar
  184. Progress bar can be used by other classes.
  185. """
  186. return self.statusbarManager.GetProgressBar()
  187. def GetRender(self):
  188. """!Returns current instance of render.Map()
  189. @todo make this method obsolate (name GetMap is better)
  190. """
  191. return self.Map
  192. def GetMap(self):
  193. """!Returns current Map instance
  194. """
  195. return self.Map
  196. def GetWindow(self):
  197. """!Get map window"""
  198. return self.MapWindow
  199. def GetMapToolbar(self):
  200. """!Returns toolbar with zooming tools"""
  201. raise NotImplementedError()
  202. def GetToolbar(self, name):
  203. """!Returns toolbar if exists else None.
  204. Toolbars dictionary contains currently used toolbars only.
  205. """
  206. if name in self.toolbars:
  207. return self.toolbars[name]
  208. return None
  209. def StatusbarUpdate(self):
  210. """!Update statusbar content"""
  211. self.statusbarManager.Update()
  212. def IsAutoRendered(self):
  213. """!Check if auto-rendering is enabled"""
  214. return self.GetProperty('render')
  215. def CoordinatesChanged(self):
  216. """!Shows current coordinates on statusbar.
  217. Used in BufferedWindow to report change of map coordinates (under mouse cursor).
  218. """
  219. self.statusbarManager.ShowItem('coordinates')
  220. def StatusbarReposition(self):
  221. """!Reposition items in statusbar"""
  222. self.statusbarManager.Reposition()
  223. def StatusbarEnableLongHelp(self, enable = True):
  224. """!Enable/disable toolbars long help"""
  225. for toolbar in self.toolbars.itervalues():
  226. toolbar.EnableLongHelp(enable)
  227. def IsStandalone(self):
  228. """!Check if Map display is standalone"""
  229. raise NotImplementedError("IsStandalone")
  230. def OnRender(self, event):
  231. """!Re-render map composition (each map layer)
  232. """
  233. raise NotImplementedError("OnRender")
  234. class MapFrame(MapFrameBase):
  235. """!Main frame for map display window. Drawing takes place in
  236. child double buffered drawing window.
  237. """
  238. def __init__(self, parent = None, title = _("GRASS GIS - Map display"),
  239. toolbars = ["map"], tree = None, notebook = None, lmgr = None,
  240. page = None, Map = None, auimgr = None, name = 'MapWindow', **kwargs):
  241. """!Main map display window with toolbars, statusbar and
  242. BufferedWindow (map canvas)
  243. @param toolbars array of activated toolbars, e.g. ['map', 'digit']
  244. @param tree reference to layer tree
  245. @param notebook control book ID in Layer Manager
  246. @param lmgr Layer Manager
  247. @param page notebook page with layer tree
  248. @param Map instance of render.Map
  249. @param auimgs AUI manager
  250. @param name frame name
  251. @param kwargs wx.Frame attributes
  252. """
  253. MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars,
  254. Map = Map, auimgr = auimgr, name = name, **kwargs)
  255. self._layerManager = lmgr # Layer Manager object
  256. self.tree = tree # Layer Manager layer tree object
  257. self.page = page # Notebook page holding the layer tree
  258. self.layerbook = notebook # Layer Manager layer tree notebook
  259. #
  260. # Add toolbars
  261. #
  262. for toolb in toolbars:
  263. self.AddToolbar(toolb)
  264. #
  265. # Add statusbar
  266. #
  267. # items for choice
  268. self.statusbarItems = [sb.SbCoordinates,
  269. sb.SbRegionExtent,
  270. sb.SbCompRegionExtent,
  271. sb.SbShowRegion,
  272. sb.SbAlignExtent,
  273. sb.SbResolution,
  274. sb.SbDisplayGeometry,
  275. sb.SbMapScale,
  276. sb.SbGoTo,
  277. sb.SbProjection]
  278. self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
  279. sb.SbDisplayGeometry,
  280. sb.SbShowRegion,
  281. sb.SbResolution,
  282. sb.SbMapScale)
  283. # create statusbar and its manager
  284. statusbar = self.CreateStatusBar(number = 4, style = 0)
  285. statusbar.SetStatusWidths([-5, -2, -1, -1])
  286. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  287. # fill statusbar manager
  288. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  289. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  290. self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
  291. self.statusbarManager.Update()
  292. #
  293. # Init map display (buffered DC & set default cursor)
  294. #
  295. self.MapWindow2D = BufferedWindow(self, id = wx.ID_ANY,
  296. Map = self.Map, tree = self.tree, lmgr = self._layerManager)
  297. # default is 2D display mode
  298. self.MapWindow = self.MapWindow2D
  299. self.MapWindow.SetCursor(self.cursors["default"])
  300. # used by vector digitizer
  301. self.MapWindowVDigit = None
  302. # used by Nviz (3D display mode)
  303. self.MapWindow3D = None
  304. #
  305. # initialize region values
  306. #
  307. self._initMap(map = self.Map)
  308. #
  309. # Bind various events
  310. #
  311. self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
  312. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  313. self.Bind(render.EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
  314. #
  315. # Update fancy gui style
  316. #
  317. self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
  318. Dockable(False).BestSize((-1,-1)).Name('2d').
  319. CloseButton(False).DestroyOnClose(True).
  320. Layer(0))
  321. self._mgr.Update()
  322. #
  323. # Init print module and classes
  324. #
  325. self.printopt = disp_print.PrintOptions(self, self.MapWindow)
  326. #
  327. # Init zoom history
  328. #
  329. self.MapWindow.ZoomHistory(self.Map.region['n'],
  330. self.Map.region['s'],
  331. self.Map.region['e'],
  332. self.Map.region['w'])
  333. #
  334. # Re-use dialogs
  335. #
  336. self.dialogs = {}
  337. self.dialogs['attributes'] = None
  338. self.dialogs['category'] = None
  339. self.dialogs['barscale'] = None
  340. self.dialogs['legend'] = None
  341. self.decorationDialog = None # decoration/overlays
  342. def _addToolbarVDigit(self):
  343. """!Add vector digitizer toolbar
  344. """
  345. from vdigit import haveVDigit
  346. if not haveVDigit:
  347. from vdigit import errorMsg
  348. msg = _("Unable to start wxGUI vector digitizer.\nDo you want to start "
  349. "TCL/TK digitizer (v.digit) instead?\n\n"
  350. "Details: %s" % errorMsg)
  351. self.toolbars['map'].combo.SetValue(_("2D view"))
  352. dlg = wx.MessageDialog(parent = self,
  353. message = msg,
  354. caption=_("Vector digitizer failed"),
  355. style = wx.YES_NO | wx.CENTRE)
  356. if dlg.ShowModal() == wx.ID_YES:
  357. mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetName()
  358. self._layerManager.goutput.RunCmd(['v.digit', 'map=%s' % mapName],
  359. switchPage = False)
  360. dlg.Destroy()
  361. self.toolbars['map'].combo.SetValue(_("2D view"))
  362. return
  363. if self._layerManager:
  364. log = self._layerManager.goutput
  365. else:
  366. log = None
  367. if not self.MapWindowVDigit:
  368. from mapdisp_vdigit import VDigitWindow
  369. self.MapWindowVDigit = VDigitWindow(self, id = wx.ID_ANY,
  370. Map = self.Map, tree = self.tree,
  371. lmgr = self._layerManager)
  372. self.MapWindowVDigit.Show()
  373. self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
  374. Dockable(False).BestSize((-1,-1)).Name('vdigit').
  375. CloseButton(False).DestroyOnClose(True).
  376. Layer(0))
  377. self.MapWindow = self.MapWindowVDigit
  378. if self._mgr.GetPane('2d').IsShown():
  379. self._mgr.GetPane('2d').Hide()
  380. elif self._mgr.GetPane('3d').IsShown():
  381. self._mgr.GetPane('3d').Hide()
  382. self._mgr.GetPane('vdigit').Show()
  383. self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent = self, mapcontent = self.Map,
  384. layerTree = self.tree,
  385. log = log)
  386. self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
  387. self._mgr.AddPane(self.toolbars['vdigit'],
  388. wx.aui.AuiPaneInfo().
  389. Name("vdigittoolbar").Caption(_("Vector Digitizer Toolbar")).
  390. ToolbarPane().Top().Row(1).
  391. LeftDockable(False).RightDockable(False).
  392. BottomDockable(False).TopDockable(True).
  393. CloseButton(False).Layer(2).
  394. BestSize((self.toolbars['vdigit'].GetBestSize())))
  395. # change mouse to draw digitized line
  396. self.MapWindow.mouse['box'] = "point"
  397. self.MapWindow.zoomtype = 0
  398. self.MapWindow.pen = wx.Pen(colour = 'red', width = 2, style = wx.SOLID)
  399. self.MapWindow.polypen = wx.Pen(colour = 'green', width = 2, style = wx.SOLID)
  400. def AddNviz(self):
  401. """!Add 3D view mode window
  402. """
  403. import nviz
  404. # check for GLCanvas and OpenGL
  405. if not nviz.haveNviz:
  406. self.toolbars['map'].combo.SetValue(_("2D view"))
  407. gcmd.GError(parent = self,
  408. message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
  409. "was not found or loaded properly.\n"
  410. "Switching back to 2D display mode.\n\nDetails: %s" % nviz.errorMsg))
  411. return
  412. # disable 3D mode for other displays
  413. for page in range(0, self._layerManager.gm_cb.GetPageCount()):
  414. if self._layerManager.gm_cb.GetPage(page) != self._layerManager.curr_page:
  415. if '3D' in self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.GetString(1):
  416. self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.Delete(1)
  417. self.toolbars['map'].Enable2D(False)
  418. # add rotate tool to map toolbar
  419. self.toolbars['map'].InsertTool((('rotate', Icons['nviz']['rotate'],
  420. self.OnRotate, wx.ITEM_CHECK,7),)) # 7 is position
  421. self.toolbars['map'].ChangeToolsDesc(mode2d = False)
  422. # update status bar
  423. self.statusbarManager.HideStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
  424. self.statusbarManager.SetMode(0)
  425. # erase map window
  426. self.MapWindow.EraseMap()
  427. self._layerManager.goutput.WriteCmdLog(_("Starting 3D view mode..."),
  428. switchPage = False)
  429. self.SetStatusText(_("Please wait, loading data..."), 0)
  430. # create GL window
  431. if not self.MapWindow3D:
  432. self.MapWindow3D = nviz.GLWindow(self, id = wx.ID_ANY,
  433. Map = self.Map, tree = self.tree, lmgr = self._layerManager)
  434. self.MapWindow = self.MapWindow3D
  435. self.MapWindow.SetCursor(self.cursors["default"])
  436. # add Nviz notebookpage
  437. self._layerManager.AddNvizTools()
  438. # switch from MapWindow to MapWindowGL
  439. self._mgr.GetPane('2d').Hide()
  440. self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
  441. Dockable(False).BestSize((-1,-1)).Name('3d').
  442. CloseButton(False).DestroyOnClose(True).
  443. Layer(0))
  444. self.MapWindow3D.OnPaint(None) # -> LoadData
  445. self.MapWindow3D.Show()
  446. self.MapWindow3D.ResetViewHistory()
  447. self.MapWindow3D.UpdateView(None)
  448. else:
  449. self.MapWindow = self.MapWindow3D
  450. os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True)
  451. self.MapWindow3D.GetDisplay().Init()
  452. del os.environ['GRASS_REGION']
  453. # switch from MapWindow to MapWindowGL
  454. self._mgr.GetPane('2d').Hide()
  455. self._mgr.GetPane('3d').Show()
  456. # add Nviz notebookpage
  457. self._layerManager.AddNvizTools()
  458. self.MapWindow3D.ResetViewHistory()
  459. for page in ('view', 'light', 'fringe', 'constant', 'cplane'):
  460. self._layerManager.nviz.UpdatePage(page)
  461. self.SetStatusText("", 0)
  462. self._mgr.Update()
  463. def RemoveNviz(self):
  464. """!Restore 2D view"""
  465. self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
  466. # update status bar
  467. self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
  468. self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
  469. key = 'statusbarMode',
  470. subkey = 'selection'))
  471. self.SetStatusText(_("Please wait, unloading data..."), 0)
  472. self._layerManager.goutput.WriteCmdLog(_("Switching back to 2D view mode..."),
  473. switchPage = False)
  474. self.MapWindow3D.UnloadDataLayers(force = True)
  475. # switch from MapWindowGL to MapWindow
  476. self._mgr.GetPane('2d').Show()
  477. self._mgr.GetPane('3d').Hide()
  478. self.MapWindow = self.MapWindow2D
  479. # remove nviz notebook page
  480. self._layerManager.RemoveNvizTools()
  481. self.MapWindow.UpdateMap()
  482. self._mgr.Update()
  483. def AddToolbar(self, name):
  484. """!Add defined toolbar to the window
  485. Currently known toolbars are:
  486. - 'map' - basic map toolbar
  487. - 'vdigit' - vector digitizer
  488. - 'gcpdisp' - GCP Manager Display
  489. """
  490. # default toolbar
  491. if name == "map":
  492. self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
  493. self._mgr.AddPane(self.toolbars['map'],
  494. wx.aui.AuiPaneInfo().
  495. Name("maptoolbar").Caption(_("Map Toolbar")).
  496. ToolbarPane().Top().Name('mapToolbar').
  497. LeftDockable(False).RightDockable(False).
  498. BottomDockable(False).TopDockable(True).
  499. CloseButton(False).Layer(2).
  500. BestSize((self.toolbars['map'].GetBestSize())))
  501. # vector digitizer
  502. elif name == "vdigit":
  503. self._addToolbarVDigit()
  504. self._mgr.Update()
  505. def RemoveToolbar (self, name):
  506. """!Removes defined toolbar from the window
  507. @todo Only hide, activate by calling AddToolbar()
  508. """
  509. # cannot hide main toolbar
  510. if name == "map":
  511. return
  512. self._mgr.DetachPane(self.toolbars[name])
  513. self.toolbars[name].Destroy()
  514. self.toolbars.pop(name)
  515. if name == 'vdigit':
  516. self._mgr.GetPane('vdigit').Hide()
  517. self._mgr.GetPane('2d').Show()
  518. self.MapWindow = self.MapWindow2D
  519. self.toolbars['map'].combo.SetValue(_("2D view"))
  520. self.toolbars['map'].Enable2D(True)
  521. self._mgr.Update()
  522. def IsPaneShown(self, name):
  523. """!Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
  524. if self._mgr.GetPane(name).IsOk():
  525. return self._mgr.GetPane(name).IsShown()
  526. return False
  527. def OnUpdateProgress(self, event):
  528. """!Update progress bar info
  529. """
  530. self.GetProgressBar().SetValue(event.value)
  531. event.Skip()
  532. def OnFocus(self, event):
  533. """!Change choicebook page to match display.
  534. """
  535. # change bookcontrol page to page associated with display
  536. if self.page:
  537. pgnum = self.layerbook.GetPageIndex(self.page)
  538. if pgnum > -1:
  539. self.layerbook.SetSelection(pgnum)
  540. self._layerManager.curr_page = self.layerbook.GetCurrentPage()
  541. event.Skip()
  542. def OnDraw(self, event):
  543. """!Re-display current map composition
  544. """
  545. self.MapWindow.UpdateMap(render = False)
  546. def OnRender(self, event):
  547. """!Re-render map composition (each map layer)
  548. """
  549. # delete tmp map layers (queries)
  550. qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)
  551. for layer in qlayer:
  552. self.Map.DeleteLayer(layer)
  553. # delete tmp lines
  554. if self.MapWindow.mouse["use"] in ("measure",
  555. "profile"):
  556. self.MapWindow.polycoords = []
  557. self.MapWindow.ClearLines()
  558. # deselect features in vdigit
  559. if self.GetToolbar('vdigit'):
  560. self.MapWindow.digit.GetDisplay().SetSelected([])
  561. self.MapWindow.UpdateMap(render = True, renderVector = True)
  562. else:
  563. self.MapWindow.UpdateMap(render = True)
  564. # update statusbar
  565. self.StatusbarUpdate()
  566. def OnPointer(self, event):
  567. """!Pointer button clicked
  568. """
  569. if self.GetMapToolbar():
  570. if event:
  571. self.toolbars['map'].OnTool(event)
  572. self.toolbars['map'].action['desc'] = ''
  573. self.MapWindow.mouse['use'] = "pointer"
  574. self.MapWindow.mouse['box'] = "point"
  575. # change the cursor
  576. if self.GetToolbar('vdigit'):
  577. # digitization tool activated
  578. self.MapWindow.SetCursor(self.cursors["cross"])
  579. # reset mouse['box'] if needed
  580. if self.toolbars['vdigit'].GetAction() in ['addLine']:
  581. if self.toolbars['vdigit'].GetAction('type') in ['point', 'centroid']:
  582. self.MapWindow.mouse['box'] = 'point'
  583. else: # line, boundary
  584. self.MapWindow.mouse['box'] = 'line'
  585. elif self.toolbars['vdigit'].GetAction() in ['addVertex', 'removeVertex', 'splitLine',
  586. 'editLine', 'displayCats', 'queryMap',
  587. 'copyCats']:
  588. self.MapWindow.mouse['box'] = 'point'
  589. else: # moveLine, deleteLine
  590. self.MapWindow.mouse['box'] = 'box'
  591. else:
  592. self.MapWindow.SetCursor(self.cursors["default"])
  593. def OnZoomIn(self, event):
  594. """!Zoom in the map.
  595. Set mouse cursor, zoombox attributes, and zoom direction
  596. """
  597. if self.GetMapToolbar():
  598. self.toolbars['map'].OnTool(event)
  599. self.toolbars['map'].action['desc'] = ''
  600. self.MapWindow.mouse['use'] = "zoom"
  601. self.MapWindow.mouse['box'] = "box"
  602. self.MapWindow.zoomtype = 1
  603. self.MapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
  604. # change the cursor
  605. self.MapWindow.SetCursor(self.cursors["cross"])
  606. def OnZoomOut(self, event):
  607. """!Zoom out the map.
  608. Set mouse cursor, zoombox attributes, and zoom direction
  609. """
  610. if self.GetMapToolbar():
  611. self.toolbars['map'].OnTool(event)
  612. self.toolbars['map'].action['desc'] = ''
  613. self.MapWindow.mouse['use'] = "zoom"
  614. self.MapWindow.mouse['box'] = "box"
  615. self.MapWindow.zoomtype = -1
  616. self.MapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
  617. # change the cursor
  618. self.MapWindow.SetCursor(self.cursors["cross"])
  619. def OnZoomBack(self, event):
  620. """!Zoom last (previously stored position)
  621. """
  622. self.MapWindow.ZoomBack()
  623. def OnPan(self, event):
  624. """!Panning, set mouse to drag
  625. """
  626. if self.GetMapToolbar():
  627. self.toolbars['map'].OnTool(event)
  628. self.toolbars['map'].action['desc'] = ''
  629. self.MapWindow.mouse['use'] = "pan"
  630. self.MapWindow.mouse['box'] = "pan"
  631. self.MapWindow.zoomtype = 0
  632. # change the cursor
  633. self.MapWindow.SetCursor(self.cursors["hand"])
  634. def OnRotate(self, event):
  635. """!Rotate 3D view
  636. """
  637. if self.GetMapToolbar():
  638. self.toolbars['map'].OnTool(event)
  639. self.toolbars['map'].action['desc'] = ''
  640. self.MapWindow.mouse['use'] = "rotate"
  641. # change the cursor
  642. self.MapWindow.SetCursor(self.cursors["hand"])
  643. def OnErase(self, event):
  644. """!Erase the canvas
  645. """
  646. self.MapWindow.EraseMap()
  647. def OnZoomRegion(self, event):
  648. """!Zoom to region
  649. """
  650. self.Map.getRegion()
  651. self.Map.getResolution()
  652. self.UpdateMap()
  653. # event.Skip()
  654. def OnAlignRegion(self, event):
  655. """!Align region
  656. """
  657. if not self.Map.alignRegion:
  658. self.Map.alignRegion = True
  659. else:
  660. self.Map.alignRegion = False
  661. # event.Skip()
  662. def SaveToFile(self, event):
  663. """!Save map to image
  664. """
  665. if self.IsPaneShown('3d'):
  666. filetype = "PPM file (*.ppm)|*.ppm|TIF file (*.tif)|*.tif"
  667. ltype = [{ 'ext' : 'ppm', 'type' : 'ppm' },
  668. { 'ext' : 'tif', 'type' : 'tif' }]
  669. else:
  670. img = self.MapWindow.img
  671. if not img:
  672. gcmd.GMessage(parent = self,
  673. message = _("Nothing to render (empty map). Operation canceled."))
  674. return
  675. filetype, ltype = gdialogs.GetImageHandlers(img)
  676. # get size
  677. dlg = gdialogs.ImageSizeDialog(self)
  678. dlg.CentreOnParent()
  679. if dlg.ShowModal() != wx.ID_OK:
  680. dlg.Destroy()
  681. return
  682. width, height = dlg.GetValues()
  683. dlg.Destroy()
  684. # get filename
  685. dlg = wx.FileDialog(parent = self,
  686. message = _("Choose a file name to save the image "
  687. "(no need to add extension)"),
  688. wildcard = filetype,
  689. style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
  690. if dlg.ShowModal() == wx.ID_OK:
  691. path = dlg.GetPath()
  692. if not path:
  693. dlg.Destroy()
  694. return
  695. base, ext = os.path.splitext(path)
  696. fileType = ltype[dlg.GetFilterIndex()]['type']
  697. extType = ltype[dlg.GetFilterIndex()]['ext']
  698. if ext != extType:
  699. path = base + '.' + extType
  700. self.MapWindow.SaveToFile(path, fileType,
  701. width, height)
  702. dlg.Destroy()
  703. def PrintMenu(self, event):
  704. """
  705. Print options and output menu for map display
  706. """
  707. point = wx.GetMousePosition()
  708. printmenu = wx.Menu()
  709. # Add items to the menu
  710. setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
  711. printmenu.AppendItem(setup)
  712. self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
  713. preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
  714. printmenu.AppendItem(preview)
  715. self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
  716. doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
  717. printmenu.AppendItem(doprint)
  718. self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
  719. # Popup the menu. If an item is selected then its handler
  720. # will be called before PopupMenu returns.
  721. self.PopupMenu(printmenu)
  722. printmenu.Destroy()
  723. def OnCloseWindow(self, event):
  724. """!Window closed.
  725. Also close associated layer tree page
  726. """
  727. pgnum = None
  728. self.Map.Clean()
  729. # close edited map and 3D tools properly
  730. if self.GetToolbar('vdigit'):
  731. maplayer = self.toolbars['vdigit'].GetLayer()
  732. if maplayer:
  733. self.toolbars['vdigit'].OnExit()
  734. if self.IsPaneShown('3d'):
  735. self.RemoveNviz()
  736. if not self._layerManager:
  737. self.Destroy()
  738. elif self.page:
  739. pgnum = self.layerbook.GetPageIndex(self.page)
  740. if pgnum > -1:
  741. self.layerbook.DeletePage(pgnum)
  742. def QueryMap(self, x, y):
  743. """!Query raster or vector map layers by r/v.what
  744. @param x,y coordinates
  745. """
  746. # set query snap distance for v.what at map unit equivalent of 10 pixels
  747. qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
  748. east, north = self.MapWindow.Pixel2Cell((x, y))
  749. if not self.IsStandalone():
  750. num = 0
  751. for layer in self.tree.GetSelections():
  752. ltype = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
  753. if ltype in ('raster', 'rgb', 'his',
  754. 'vector', 'thememap', 'themechart'):
  755. num += 1
  756. if num < 1:
  757. gcmd.GMessage(parent = self,
  758. message = _('No raster or vector map layer selected for querying.'))
  759. return
  760. rast = list()
  761. vect = list()
  762. rcmd = ['r.what', '--v']
  763. vcmd = ['v.what', '--v']
  764. if self.IsStandalone():
  765. pass
  766. else:
  767. for layer in self.tree.GetSelections():
  768. ltype = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
  769. dcmd = self.tree.GetPyData(layer)[0]['cmd']
  770. name, found = utils.GetLayerNameFromCmd(dcmd)
  771. if not found:
  772. continue
  773. if ltype == 'raster':
  774. rast.append(name)
  775. elif ltype in ('rgb', 'his'):
  776. for iname in name.split('\n'):
  777. rast.append(iname)
  778. elif ltype in ('vector', 'thememap', 'themechart'):
  779. vect.append(name)
  780. # rasters are not queried this way in 3D, we don't want them now
  781. if self.IsPaneShown('3d'):
  782. rast = list()
  783. # use display region settings instead of computation region settings
  784. self.tmpreg = os.getenv("GRASS_REGION")
  785. os.environ["GRASS_REGION"] = self.Map.SetRegion(windres = False)
  786. # build query commands for any selected rasters and vectors
  787. if rast:
  788. rcmd.append('-f')
  789. rcmd.append('-n')
  790. rcmd.append('input=%s' % ','.join(rast))
  791. rcmd.append('east_north=%f,%f' % (float(east), float(north)))
  792. if vect:
  793. # check for vector maps open to be edited
  794. digitToolbar = self.toolbars['vdigit']
  795. if digitToolbar:
  796. lmap = digitToolbar.GetLayer().GetName()
  797. for name in vect:
  798. if lmap == name:
  799. self._layerManager.goutput.WriteWarning(_("Vector map <%s> "
  800. "opened for editing - skipped.") % map)
  801. vect.remove(name)
  802. if len(vect) < 1:
  803. self._layerManager.goutput.WriteCmdLog(_("Nothing to query."))
  804. return
  805. vcmd.append('-a')
  806. vcmd.append('map=%s' % ','.join(vect))
  807. vcmd.append('layer=%s' % ','.join(['-1'] * len(vect)))
  808. vcmd.append('east_north=%f,%f' % (float(east), float(north)))
  809. vcmd.append('distance=%f' % float(qdist))
  810. Debug.msg(1, "QueryMap(): raster=%s vector=%s" % (','.join(rast),
  811. ','.join(vect)))
  812. # parse query command(s)
  813. if not self.IsStandalone():
  814. if rast:
  815. self._layerManager.goutput.RunCmd(rcmd,
  816. compReg = False,
  817. onDone = self._QueryMapDone)
  818. if vect:
  819. self._layerManager.goutput.RunCmd(vcmd,
  820. onDone = self._QueryMapDone)
  821. else:
  822. if rast:
  823. gcmd.RunCommand(rcmd)
  824. if vect:
  825. gcmd.RunCommand(vcmd)
  826. def _QueryMapDone(self, cmd, returncode):
  827. """!Restore settings after querying (restore GRASS_REGION)
  828. @param returncode command return code
  829. """
  830. if hasattr(self, "tmpreg"):
  831. if self.tmpreg:
  832. os.environ["GRASS_REGION"] = self.tmpreg
  833. elif 'GRASS_REGION' in os.environ:
  834. del os.environ["GRASS_REGION"]
  835. elif 'GRASS_REGION' in os.environ:
  836. del os.environ["GRASS_REGION"]
  837. if hasattr(self, "tmpreg"):
  838. del self.tmpreg
  839. def QueryVector(self, x, y):
  840. """!Query vector map layer features
  841. Attribute data of selected vector object are displayed in GUI dialog.
  842. Data can be modified (On Submit)
  843. """
  844. if not self.tree.layer_selected or \
  845. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] != 'vector':
  846. gcmd.GMessage(parent = self,
  847. message = _("No map layer selected for querying."))
  848. return
  849. posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
  850. y + self.MapWindow.dialogOffset))
  851. qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) /
  852. self.Map.width)
  853. east, north = self.MapWindow.Pixel2Cell((x, y))
  854. mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
  855. if self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetMapset() != \
  856. grass.gisenv()['MAPSET']:
  857. mode = 'display'
  858. else:
  859. mode = 'update'
  860. if self.dialogs['attributes'] is None:
  861. dlg = dbm_dialogs.DisplayAttributesDialog(parent = self.MapWindow,
  862. map = mapName,
  863. query = ((east, north), qdist),
  864. pos = posWindow,
  865. action = mode)
  866. self.dialogs['attributes'] = dlg
  867. else:
  868. # selection changed?
  869. if not self.dialogs['attributes'].mapDBInfo or \
  870. self.dialogs['attributes'].mapDBInfo.map != mapName:
  871. self.dialogs['attributes'].UpdateDialog(map = mapName, query = ((east, north), qdist),
  872. action = mode)
  873. else:
  874. self.dialogs['attributes'].UpdateDialog(query = ((east, north), qdist),
  875. action = mode)
  876. if not self.dialogs['attributes'].IsFound():
  877. self._layerManager.goutput.WriteLog(_('Nothing found.'))
  878. cats = self.dialogs['attributes'].GetCats()
  879. qlayer = None
  880. if not self.IsPaneShown('3d'):
  881. try:
  882. qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)[0]
  883. except IndexError:
  884. pass
  885. if self.dialogs['attributes'].mapDBInfo and cats:
  886. if not self.IsPaneShown('3d'):
  887. # highlight feature & re-draw map
  888. if qlayer:
  889. qlayer.SetCmd(self.AddTmpVectorMapLayer(mapName, cats,
  890. useId = False,
  891. addLayer = False))
  892. else:
  893. qlayer = self.AddTmpVectorMapLayer(mapName, cats, useId = False)
  894. # set opacity based on queried layer
  895. opacity = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetOpacity(float = True)
  896. qlayer.SetOpacity(opacity)
  897. self.MapWindow.UpdateMap(render = False, renderVector = False)
  898. if not self.dialogs['attributes'].IsShown():
  899. self.dialogs['attributes'].Show()
  900. else:
  901. if qlayer:
  902. self.Map.DeleteLayer(qlayer)
  903. self.MapWindow.UpdateMap(render = False, renderVector = False)
  904. if self.dialogs['attributes'].IsShown():
  905. self.dialogs['attributes'].Hide()
  906. def OnQuery(self, event):
  907. """!Query tools menu"""
  908. if self.GetMapToolbar():
  909. self.toolbars['map'].OnTool(event)
  910. action = self.toolbars['map'].GetAction()
  911. self.toolbars['map'].action['desc'] = 'queryMap'
  912. self.MapWindow.mouse['use'] = "query"
  913. if not self.IsStandalone():
  914. # switch to output console to show query results
  915. self._layerManager.notebook.SetSelectionByName('output')
  916. self.MapWindow.mouse['box'] = "point"
  917. self.MapWindow.zoomtype = 0
  918. # change the cursor
  919. self.MapWindow.SetCursor(self.cursors["cross"])
  920. def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
  921. """!Add temporal vector map layer to map composition
  922. @param name name of map layer
  923. @param useId use feature id instead of category
  924. """
  925. # color settings from ATM
  926. color = UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'color')
  927. colorStr = str(color[0]) + ":" + \
  928. str(color[1]) + ":" + \
  929. str(color[2])
  930. # icon used in vector display and its size
  931. icon = ''
  932. size = 0
  933. vparam = self.tree.GetPyData(self.tree.layer_selected)[0]['cmd']
  934. for p in vparam:
  935. if '=' in p:
  936. parg,pval = p.split('=')
  937. if parg == 'icon': icon = pval
  938. elif parg == 'size': size = int(pval)
  939. pattern = ["d.vect",
  940. "map=%s" % name,
  941. "color=%s" % colorStr,
  942. "fcolor=%s" % colorStr,
  943. "width=%d" % UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'width')]
  944. if icon != '':
  945. pattern.append('icon=%s' % icon)
  946. if size > 0:
  947. pattern.append('size=%i' % size)
  948. if useId:
  949. cmd = pattern
  950. cmd.append('-i')
  951. cmd.append('cats=%s' % str(cats))
  952. else:
  953. cmd = []
  954. for layer in cats.keys():
  955. cmd.append(copy.copy(pattern))
  956. lcats = cats[layer]
  957. cmd[-1].append("layer=%d" % layer)
  958. cmd[-1].append("cats=%s" % utils.ListOfCatsToRange(lcats))
  959. if addLayer:
  960. if useId:
  961. return self.Map.AddLayer(type = 'vector', name = globalvar.QUERYLAYER, command = cmd,
  962. l_active = True, l_hidden = True, l_opacity = 1.0)
  963. else:
  964. return self.Map.AddLayer(type = 'command', name = globalvar.QUERYLAYER, command = cmd,
  965. l_active = True, l_hidden = True, l_opacity = 1.0)
  966. else:
  967. return cmd
  968. def OnAnalyze(self, event):
  969. """!Analysis tools menu
  970. """
  971. point = wx.GetMousePosition()
  972. toolsmenu = wx.Menu()
  973. icons = Icons['displayWindow']
  974. # Add items to the menu
  975. measure = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["measure"].GetLabel())
  976. measure.SetBitmap(icons["measure"].GetBitmap(self.iconsize))
  977. toolsmenu.AppendItem(measure)
  978. self.Bind(wx.EVT_MENU, self.OnMeasure, measure)
  979. profile = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["profile"].GetLabel())
  980. profile.SetBitmap(icons["profile"].GetBitmap(self.iconsize))
  981. toolsmenu.AppendItem(profile)
  982. self.Bind(wx.EVT_MENU, self.OnProfile, profile)
  983. scatterplot = wx.MenuItem(toolsmenu, wx.ID_ANY, _("Create bivariate scatterplot of raster maps"))
  984. scatterplot.SetBitmap(icons["profile"].GetBitmap(self.iconsize))
  985. toolsmenu.AppendItem(scatterplot)
  986. self.Bind(wx.EVT_MENU, self.OnScatterplot, scatterplot)
  987. histogram2 = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["histogram"].GetLabel())
  988. histogram2.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
  989. toolsmenu.AppendItem(histogram2)
  990. self.Bind(wx.EVT_MENU, self.OnHistogramPyPlot, histogram2)
  991. histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, _("Create histogram with d.histogram"))
  992. histogram.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
  993. toolsmenu.AppendItem(histogram)
  994. self.Bind(wx.EVT_MENU, self.OnHistogram, histogram)
  995. # Popup the menu. If an item is selected then its handler
  996. # will be called before PopupMenu returns.
  997. self.PopupMenu(toolsmenu)
  998. toolsmenu.Destroy()
  999. def OnMeasure(self, event):
  1000. """!Init measurement routine that calculates map distance
  1001. along transect drawn on map display
  1002. """
  1003. self.totaldist = 0.0 # total measured distance
  1004. # switch Layer Manager to output console to show measure results
  1005. self._layerManager.notebook.SetSelectionByName('output')
  1006. # change mouse to draw line for measurement
  1007. self.MapWindow.mouse['use'] = "measure"
  1008. self.MapWindow.mouse['box'] = "line"
  1009. self.MapWindow.zoomtype = 0
  1010. self.MapWindow.pen = wx.Pen(colour = 'red', width = 2, style = wx.SHORT_DASH)
  1011. self.MapWindow.polypen = wx.Pen(colour = 'green', width = 2, style = wx.SHORT_DASH)
  1012. # change the cursor
  1013. self.MapWindow.SetCursor(self.cursors["pencil"])
  1014. # initiating output
  1015. style = self._layerManager.goutput.cmd_output.StyleWarning
  1016. self._layerManager.goutput.WriteLog(_('Click and drag with left mouse button '
  1017. 'to measure.%s'
  1018. 'Double click with left button to clear.') % \
  1019. (os.linesep), style)
  1020. if self.Map.projinfo['proj'] != 'xy':
  1021. units = self.Map.projinfo['units']
  1022. self._layerManager.goutput.WriteCmdLog(_('Measuring distance') + ' ('
  1023. + units + '):')
  1024. else:
  1025. self._layerManager.goutput.WriteCmdLog(_('Measuring distance:'))
  1026. if self.Map.projinfo['proj'] == 'll':
  1027. try:
  1028. import grass.lib.gis as gislib
  1029. global haveCtypes
  1030. haveCtypes = True
  1031. gislib.G_begin_distance_calculations()
  1032. except ImportError, e:
  1033. self._layerManager.goutput.WriteWarning(_('Geodesic distance is not yet '
  1034. 'supported by this tool.\n'
  1035. 'Reason: %s' % e))
  1036. def MeasureDist(self, beginpt, endpt):
  1037. """!Calculate map distance from screen distance
  1038. and print to output window
  1039. """
  1040. self._layerManager.notebook.SetSelectionByName('output')
  1041. dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
  1042. dist = round(dist, 3)
  1043. d, dunits = self.FormatDist(dist)
  1044. self.totaldist += dist
  1045. td, tdunits = self.FormatDist(self.totaldist)
  1046. strdist = str(d)
  1047. strtotdist = str(td)
  1048. if self.Map.projinfo['proj'] == 'xy' or 'degree' not in self.Map.projinfo['unit']:
  1049. angle = int(math.degrees(math.atan2(north,east)) + 0.5)
  1050. angle = 180 - angle
  1051. if angle < 0:
  1052. angle = 360 + angle
  1053. mstring = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
  1054. % (_('segment'), strdist, dunits,
  1055. _('total distance'), strtotdist, tdunits,
  1056. _('bearing'), angle, _('deg'),
  1057. '-' * 60)
  1058. else:
  1059. mstring = '%s = %s %s\n%s = %s %s\n%s' \
  1060. % (_('segment'), strdist, dunits,
  1061. _('total distance'), strtotdist, tdunits,
  1062. '-' * 60)
  1063. self._layerManager.goutput.WriteLog(mstring)
  1064. return dist
  1065. def OnProfile(self, event):
  1066. """!Init profile canvas and tools
  1067. """
  1068. raster = []
  1069. if self.tree.layer_selected and \
  1070. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
  1071. raster.append(self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
  1072. self.profile = ProfileFrame(self,
  1073. id = wx.ID_ANY, pos = wx.DefaultPosition, size = (700,300),
  1074. style = wx.DEFAULT_FRAME_STYLE,
  1075. rasterList = raster)
  1076. self.profile.Show()
  1077. # Open raster select dialog to make sure that a raster (and the desired raster)
  1078. # is selected to be profiled
  1079. self.profile.OnSelectRaster(None)
  1080. def FormatDist(self, dist):
  1081. """!Format length numbers and units in a nice way,
  1082. as a function of length. From code by Hamish Bowman
  1083. Grass Development Team 2006"""
  1084. mapunits = self.Map.projinfo['units']
  1085. if mapunits == 'metres':
  1086. mapunits = 'meters'
  1087. outunits = mapunits
  1088. dist = float(dist)
  1089. divisor = 1.0
  1090. # figure out which units to use
  1091. if mapunits == 'meters':
  1092. if dist > 2500.0:
  1093. outunits = 'km'
  1094. divisor = 1000.0
  1095. else: outunits = 'm'
  1096. elif mapunits == 'feet':
  1097. # nano-bug: we match any "feet", but US Survey feet is really
  1098. # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
  1099. # miles the tick markers are rounded to the nearest 10th of a
  1100. # mile (528'), the difference in foot flavours is ignored.
  1101. if dist > 5280.0:
  1102. outunits = 'miles'
  1103. divisor = 5280.0
  1104. else:
  1105. outunits = 'ft'
  1106. elif 'degree' in mapunits and \
  1107. not haveCtypes:
  1108. if dist < 1:
  1109. outunits = 'min'
  1110. divisor = (1/60.0)
  1111. else:
  1112. outunits = 'deg'
  1113. else:
  1114. outunits = 'meters'
  1115. # format numbers in a nice way
  1116. if (dist/divisor) >= 2500.0:
  1117. outdist = round(dist/divisor)
  1118. elif (dist/divisor) >= 1000.0:
  1119. outdist = round(dist/divisor,1)
  1120. elif (dist/divisor) > 0.0:
  1121. outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
  1122. else:
  1123. outdist = float(dist/divisor)
  1124. return (outdist, outunits)
  1125. def OnHistogramPyPlot(self, event):
  1126. """!Init PyPlot histogram display canvas and tools
  1127. """
  1128. raster = []
  1129. for layer in self.tree.GetSelections():
  1130. if self.tree.GetPyData(layer)[0]['maplayer'].GetType() != 'raster':
  1131. continue
  1132. raster.append(self.tree.GetPyData(layer)[0]['maplayer'].GetName())
  1133. self.histogramPyPlot = HistFramePyPlot(self, id = wx.ID_ANY,
  1134. pos = wx.DefaultPosition, size = (700,300),
  1135. style = wx.DEFAULT_FRAME_STYLE,
  1136. rasterList = raster)
  1137. self.histogramPyPlot.Show()
  1138. # Open raster select dialog to make sure that a raster (and the desired raster)
  1139. # is selected to be histogrammed
  1140. self.histogramPyPlot.OnSelectRaster(None)
  1141. def OnScatterplot(self, event):
  1142. """!Init PyPlot scatterplot display canvas and tools
  1143. """
  1144. raster = []
  1145. for layer in self.tree.GetSelections():
  1146. if self.tree.GetPyData(layer)[0]['maplayer'].GetType() != 'raster':
  1147. continue
  1148. raster.append(self.tree.GetPyData(layer)[0]['maplayer'].GetName())
  1149. self.scatterplot = ScatterFrame(self, id = wx.ID_ANY,
  1150. pos = wx.DefaultPosition, size = (700,300),
  1151. style = wx.DEFAULT_FRAME_STYLE,
  1152. rasterList = raster)
  1153. self.scatterplot.Show()
  1154. # Open raster select dialog to make sure that at least 2 rasters (and the desired rasters)
  1155. # are selected to be plotted
  1156. self.scatterplot.OnSelectRaster(None)
  1157. def OnHistogram(self, event):
  1158. """!Init histogram display canvas and tools
  1159. """
  1160. self.histogram = HistFrame(parent = self, id = wx.ID_ANY, size = globalvar.HIST_WINDOW_SIZE,
  1161. style = wx.DEFAULT_FRAME_STYLE)
  1162. # show new display
  1163. self.histogram.Show()
  1164. self.histogram.Refresh()
  1165. self.histogram.Update()
  1166. def OnDecoration(self, event):
  1167. """!Decorations overlay menu
  1168. """
  1169. point = wx.GetMousePosition()
  1170. decmenu = wx.Menu()
  1171. icons = Icons['displayWindow']
  1172. # Add items to the menu
  1173. AddScale = wx.MenuItem(decmenu, wx.ID_ANY, icons["addBarscale"].GetLabel())
  1174. AddScale.SetBitmap(icons["addBarscale"].GetBitmap(self.iconsize))
  1175. decmenu.AppendItem(AddScale)
  1176. self.Bind(wx.EVT_MENU, self.OnAddBarscale, AddScale)
  1177. # temporary
  1178. if self.IsPaneShown('3d'):
  1179. AddScale.Enable(False)
  1180. AddArrow = wx.MenuItem(decmenu, wx.ID_ANY, _("Add north arrow"))
  1181. AddArrow.SetBitmap(icons["addBarscale"].GetBitmap(self.iconsize))
  1182. decmenu.AppendItem(AddArrow)
  1183. self.Bind(wx.EVT_MENU, self.OnAddArrow, AddArrow)
  1184. AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, icons["addLegend"].GetLabel())
  1185. AddLegend.SetBitmap(icons["addLegend"].GetBitmap(self.iconsize))
  1186. decmenu.AppendItem(AddLegend)
  1187. self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
  1188. AddText = wx.MenuItem(decmenu, wx.ID_ANY, icons["addText"].GetLabel())
  1189. AddText.SetBitmap(icons["addText"].GetBitmap(self.iconsize))
  1190. decmenu.AppendItem(AddText)
  1191. self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
  1192. # Popup the menu. If an item is selected then its handler
  1193. # will be called before PopupMenu returns.
  1194. self.PopupMenu(decmenu)
  1195. decmenu.Destroy()
  1196. def OnAddBarscale(self, event):
  1197. """!Handler for scale/arrow map decoration menu selection.
  1198. """
  1199. if self.dialogs['barscale']:
  1200. return
  1201. id = 0 # unique index for overlay layer
  1202. # If location is latlon, only display north arrow (scale won't work)
  1203. # proj = self.Map.projinfo['proj']
  1204. # if proj == 'll':
  1205. # barcmd = 'd.barscale -n'
  1206. # else:
  1207. # barcmd = 'd.barscale'
  1208. # decoration overlay control dialog
  1209. self.dialogs['barscale'] = \
  1210. gdialogs.DecorationDialog(parent = self, title = _('Scale and North arrow'),
  1211. size = (350, 200),
  1212. style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
  1213. cmd = ['d.barscale', 'at=0,95'],
  1214. ovlId = id,
  1215. name = 'barscale',
  1216. checktxt = _("Show/hide scale and North arrow"),
  1217. ctrltxt = _("scale object"))
  1218. self.dialogs['barscale'].CentreOnParent()
  1219. ### dialog cannot be show as modal - in the result d.barscale is not selectable
  1220. ### self.dialogs['barscale'].ShowModal()
  1221. self.dialogs['barscale'].Show()
  1222. self.MapWindow.mouse['use'] = 'pointer'
  1223. def OnAddLegend(self, event):
  1224. """!Handler for legend map decoration menu selection.
  1225. """
  1226. if self.dialogs['legend']:
  1227. return
  1228. id = 1 # index for overlay layer in render
  1229. cmd = ['d.legend', 'at=5,50,2,5']
  1230. if self.tree.layer_selected and \
  1231. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
  1232. cmd.append('map=%s' % self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
  1233. # Decoration overlay control dialog
  1234. self.dialogs['legend'] = \
  1235. gdialogs.DecorationDialog(parent = self, title = ('Legend'),
  1236. size = (350, 200),
  1237. style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
  1238. cmd = cmd,
  1239. ovlId = id,
  1240. name = 'legend',
  1241. checktxt = _("Show/hide legend"),
  1242. ctrltxt = _("legend object"))
  1243. self.dialogs['legend'].CentreOnParent()
  1244. ### dialog cannot be show as modal - in the result d.legend is not selectable
  1245. ### self.dialogs['legend'].ShowModal()
  1246. self.dialogs['legend'].Show()
  1247. self.MapWindow.mouse['use'] = 'pointer'
  1248. def OnAddText(self, event):
  1249. """!Handler for text decoration menu selection.
  1250. """
  1251. if self.MapWindow.dragid > -1:
  1252. id = self.MapWindow.dragid
  1253. self.MapWindow.dragid = -1
  1254. else:
  1255. # index for overlay layer in render
  1256. if len(self.MapWindow.textdict.keys()) > 0:
  1257. id = max(self.MapWindow.textdict.keys()) + 1
  1258. else:
  1259. id = 101
  1260. self.dialogs['text'] = gdialogs.TextLayerDialog(parent = self, ovlId = id,
  1261. title = _('Add text layer'),
  1262. size = (400, 200))
  1263. self.dialogs['text'].CenterOnParent()
  1264. # If OK button pressed in decoration control dialog
  1265. if self.dialogs['text'].ShowModal() == wx.ID_OK:
  1266. text = self.dialogs['text'].GetValues()['text']
  1267. active = self.dialogs['text'].GetValues()['active']
  1268. # delete object if it has no text or is not active
  1269. if text == '' or active == False:
  1270. try:
  1271. self.MapWindow2D.pdc.ClearId(id)
  1272. self.MapWindow2D.pdc.RemoveId(id)
  1273. del self.MapWindow.textdict[id]
  1274. if self.IsPaneShown('3d'):
  1275. self.MapWindow3D.UpdateOverlays()
  1276. self.MapWindow.UpdateMap()
  1277. else:
  1278. self.MapWindow2D.UpdateMap(render = False, renderVector = False)
  1279. except:
  1280. pass
  1281. return
  1282. self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
  1283. if self.IsPaneShown('3d'):
  1284. self.MapWindow3D.UpdateOverlays()
  1285. self.MapWindow3D.UpdateMap()
  1286. else:
  1287. self.MapWindow2D.pdc.ClearId(id)
  1288. self.MapWindow2D.pdc.SetId(id)
  1289. self.MapWindow2D.UpdateMap(render = False, renderVector = False)
  1290. self.MapWindow.mouse['use'] = 'pointer'
  1291. def OnAddArrow(self, event):
  1292. """!Handler for north arrow menu selection.
  1293. Opens Appearance page of nviz notebook.
  1294. """
  1295. self._layerManager.nviz.SetPage('decoration')
  1296. self.MapWindow3D.SetDrawArrow((70, 70))
  1297. def GetOptData(self, dcmd, type, params, propwin):
  1298. """!Callback method for decoration overlay command generated by
  1299. dialog created in menuform.py
  1300. """
  1301. # Reset comand and rendering options in render.Map. Always render decoration.
  1302. # Showing/hiding handled by PseudoDC
  1303. self.Map.ChangeOverlay(ovltype = type, type = 'overlay', name = '', command = dcmd,
  1304. l_active = True, l_render = False)
  1305. self.params[type] = params
  1306. self.propwin[type] = propwin
  1307. def OnZoomToMap(self, event):
  1308. """!Set display extents to match selected raster (including
  1309. NULLs) or vector map.
  1310. """
  1311. self.MapWindow.ZoomToMap()
  1312. def OnZoomToRaster(self, event):
  1313. """!Set display extents to match selected raster map (ignore NULLs)
  1314. """
  1315. self.MapWindow.ZoomToMap(ignoreNulls = True)
  1316. def OnZoomToWind(self, event):
  1317. """!Set display geometry to match computational region
  1318. settings (set with g.region)
  1319. """
  1320. self.MapWindow.ZoomToWind()
  1321. def OnZoomToDefault(self, event):
  1322. """!Set display geometry to match default region settings
  1323. """
  1324. self.MapWindow.ZoomToDefault()
  1325. def OnZoomToSaved(self, event):
  1326. """!Set display geometry to match extents in
  1327. saved region file
  1328. """
  1329. self.MapWindow.ZoomToSaved()
  1330. def OnDisplayToWind(self, event):
  1331. """!Set computational region (WIND file) to match display
  1332. extents
  1333. """
  1334. self.MapWindow.DisplayToWind()
  1335. def SaveDisplayRegion(self, event):
  1336. """!Save display extents to named region file.
  1337. """
  1338. self.MapWindow.SaveDisplayRegion()
  1339. def OnZoomMenu(self, event):
  1340. """!Popup Zoom menu
  1341. """
  1342. point = wx.GetMousePosition()
  1343. zoommenu = wx.Menu()
  1344. # Add items to the menu
  1345. zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region'))
  1346. zoommenu.AppendItem(zoomwind)
  1347. self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
  1348. zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
  1349. zoommenu.AppendItem(zoomdefault)
  1350. self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
  1351. zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
  1352. zoommenu.AppendItem(zoomsaved)
  1353. self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
  1354. savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display extent'))
  1355. zoommenu.AppendItem(savewind)
  1356. self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
  1357. savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
  1358. zoommenu.AppendItem(savezoom)
  1359. self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
  1360. # Popup the menu. If an item is selected then its handler
  1361. # will be called before PopupMenu returns.
  1362. self.PopupMenu(zoommenu)
  1363. zoommenu.Destroy()
  1364. def SetProperties(self, render = False, mode = 0, showCompExtent = False,
  1365. constrainRes = False, projection = False, alignExtent = True):
  1366. """!Set properies of map display window"""
  1367. self.SetProperty('render', render)
  1368. self.statusbarManager.SetMode(mode)
  1369. self.StatusbarUpdate()
  1370. self.SetProperty('region', showCompExtent)
  1371. self.SetProperty('alignExtent', alignExtent)
  1372. self.SetProperty('resolution', constrainRes)
  1373. self.SetProperty('projection', projection)
  1374. def IsStandalone(self):
  1375. """!Check if Map display is standalone"""
  1376. if self._layerManager:
  1377. return False
  1378. return True
  1379. def GetLayerManager(self):
  1380. """!Get reference to Layer Manager
  1381. @return window reference
  1382. @return None (if standalone)
  1383. """
  1384. return self._layerManager
  1385. def GetMapToolbar(self):
  1386. """!Returns toolbar with zooming tools"""
  1387. return self.toolbars['map']
  1388. class MapApp(wx.App):
  1389. def OnInit(self):
  1390. wx.InitAllImageHandlers()
  1391. if __name__ == "__main__":
  1392. self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
  1393. Map = render.Map(cmdfile = monFile['cmd'], mapfile = monFile['map'],
  1394. envfile = monFile['env'], monitor = monName)
  1395. else:
  1396. Map = None
  1397. self.mapFrm = MapFrame(parent = None, id = wx.ID_ANY, Map = Map,
  1398. size = monSize)
  1399. # self.SetTopWindow(Map)
  1400. self.mapFrm.Show()
  1401. if __name__ == "__main__":
  1402. self.timer = wx.PyTimer(self.watcher)
  1403. #check each 0.5s
  1404. global mtime
  1405. mtime = 500
  1406. self.timer.Start(mtime)
  1407. return True
  1408. def OnExit(self):
  1409. if __name__ == "__main__":
  1410. # stop the timer
  1411. # self.timer.Stop()
  1412. # terminate thread
  1413. for f in monFile.itervalues():
  1414. grass.try_remove(f)
  1415. def watcher(self):
  1416. """!Redraw, if new layer appears (check's timestamp of
  1417. cmdfile)
  1418. """
  1419. # todo: events
  1420. if os.path.getmtime(monFile['cmd']) > self.cmdTimeStamp:
  1421. self.timer.Stop()
  1422. self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
  1423. self.mapFrm.OnDraw(None)
  1424. self.timer.Start(mtime)
  1425. if __name__ == "__main__":
  1426. # set command variable
  1427. if len(sys.argv) < 5:
  1428. print __doc__
  1429. sys.exit(1)
  1430. monName = sys.argv[1]
  1431. monFile = { 'map' : sys.argv[2],
  1432. 'cmd' : sys.argv[3],
  1433. 'env' : sys.argv[4],
  1434. }
  1435. if len(sys.argv) >= 6:
  1436. try:
  1437. monSize[0] = int(sys.argv[5])
  1438. except ValueError:
  1439. pass
  1440. if len(sys.argv) == 7:
  1441. try:
  1442. monSize[1] = int(sys.argv[6])
  1443. except ValueError:
  1444. pass
  1445. import gettext
  1446. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  1447. grass.verbose(_("Starting map display <%s>...") % (monName))
  1448. gcmd.RunCommand('g.gisenv',
  1449. set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
  1450. gm_map = MapApp(0)
  1451. # set title
  1452. gm_map.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
  1453. monName +
  1454. " - Location: " + grass.gisenv()["LOCATION_NAME"]))
  1455. gm_map.MainLoop()
  1456. grass.verbose(_("Stopping map display <%s>...") % (monName))
  1457. # clean up GRASS env variables
  1458. env = grass.gisenv()
  1459. env_name = 'MONITOR_%s' % monName
  1460. for key in env.keys():
  1461. if key.find(env_name) == 0:
  1462. gcmd.RunCommand('g.gisenv',
  1463. set = '%s=' % key)
  1464. if key == 'MONITOR' and env[key] == monName:
  1465. gcmd.RunCommand('g.gisenv',
  1466. set = '%s=' % key)
  1467. sys.exit(0)