mapdisp.py 88 KB

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