mapdisp.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. """
  2. @package gui_core.mapdisp
  3. @brief Base classes for Map display window
  4. Classes:
  5. - mapdisp::MapFrameBase
  6. - mapdisp::SingleMapFrame
  7. - mapdisp::DoubleMapFrame
  8. (C) 2009-2014 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Martin Landa <landa.martin gmail.com>
  12. @author Michael Barton <michael.barton@asu.edu>
  13. @author Vaclav Petras <wenzeslaus gmail.com>
  14. @author Anna Kratochvilova <kratochanna gmail.com>
  15. """
  16. import os
  17. import sys
  18. import six
  19. import wx
  20. from core import globalvar
  21. from core.debug import Debug
  22. from gui_core.toolbars import ToolSwitcher
  23. from gui_core.wrap import NewId
  24. from grass.script import core as grass
  25. class MapFrameBase(wx.Frame):
  26. """Base class for map display window
  27. Derived class must use (create and initialize) \c statusbarManager
  28. or override
  29. GetProperty(), SetProperty() and HasProperty() methods.
  30. Several methods has to be overridden or
  31. \c NotImplementedError("MethodName") will be raised.
  32. If derived class enables and disables auto-rendering,
  33. it should override IsAutoRendered method.
  34. It is expected that derived class will call _setUpMapWindow().
  35. Derived class can has one or more map windows (and map renders)
  36. but implementation of MapFrameBase expects that one window and
  37. one map will be current.
  38. Current instances of map window and map renderer should be returned
  39. by methods GetWindow() and GetMap() respectively.
  40. AUI manager is stored in \c self._mgr.
  41. """
  42. def __init__(
  43. self,
  44. parent=None,
  45. id=wx.ID_ANY,
  46. title="",
  47. style=wx.DEFAULT_FRAME_STYLE,
  48. auimgr=None,
  49. name="",
  50. **kwargs,
  51. ):
  52. """
  53. .. warning::
  54. Use \a auimgr parameter only if you know what you are doing.
  55. :param parent: gui parent
  56. :param id: wx id
  57. :param title: window title
  58. :param style: \c wx.Frame style
  59. :param toolbars: array of activated toolbars, e.g. ['map', 'digit']
  60. :param auimgr: AUI manager (if \c None, wx.aui.AuiManager is used)
  61. :param name: frame name
  62. :param kwargs: arguments passed to \c wx.Frame
  63. """
  64. self.parent = parent
  65. wx.Frame.__init__(self, parent, id, title, style=style, name=name, **kwargs)
  66. #
  67. # set the size & system icon
  68. #
  69. self.SetClientSize(self.GetSize())
  70. self.iconsize = (16, 16)
  71. self.SetIcon(
  72. wx.Icon(
  73. os.path.join(globalvar.ICONDIR, "grass_map.ico"), wx.BITMAP_TYPE_ICO
  74. )
  75. )
  76. # toolbars
  77. self.toolbars = {}
  78. #
  79. # Fancy gui
  80. #
  81. if auimgr is None:
  82. from wx.aui import AuiManager
  83. self._mgr = AuiManager(self)
  84. else:
  85. self._mgr = auimgr
  86. # handles switching between tools in different toolbars
  87. self._toolSwitcher = ToolSwitcher()
  88. self._toolSwitcher.toggleToolChanged.connect(self._onToggleTool)
  89. self._initShortcuts()
  90. def _initShortcuts(self):
  91. # set accelerator table (fullscreen, close window)
  92. shortcuts_table = (
  93. (self.OnFullScreen, wx.ACCEL_NORMAL, wx.WXK_F11),
  94. (self.OnCloseWindow, wx.ACCEL_CTRL, ord("W")),
  95. (self.OnRender, wx.ACCEL_CTRL, ord("R")),
  96. (self.OnRender, wx.ACCEL_NORMAL, wx.WXK_F5),
  97. )
  98. accelTable = []
  99. for handler, entry, kdb in shortcuts_table:
  100. wxId = NewId()
  101. self.Bind(wx.EVT_MENU, handler, id=wxId)
  102. accelTable.append((entry, kdb, wxId))
  103. self.SetAcceleratorTable(wx.AcceleratorTable(accelTable))
  104. def _initMap(self, Map):
  105. """Initialize map display, set dimensions and map region"""
  106. if not grass.find_program("g.region", "--help"):
  107. sys.exit(
  108. _("GRASS module '%s' not found. Unable to start map " "display window.")
  109. % "g.region"
  110. )
  111. Debug.msg(2, "MapFrame._initMap():")
  112. Map.ChangeMapSize(self.GetClientSize())
  113. Map.region = Map.GetRegion() # g.region -upgc
  114. # self.Map.SetRegion() # adjust region to match display window
  115. def _resize(self):
  116. Debug.msg(1, "MapFrame._resize():")
  117. wm, hw = self.MapWindow.GetClientSize()
  118. wf, hf = self.GetSize()
  119. dw = wf - wm
  120. dh = hf - hw
  121. self.SetSize((wf + dw, hf + dh))
  122. def _onToggleTool(self, id):
  123. if self._toolSwitcher.IsToolInGroup(id, "mouseUse"):
  124. self.GetWindow().UnregisterAllHandlers()
  125. def OnSize(self, event):
  126. """Adjust statusbar on changing size"""
  127. # reposition checkbox in statusbar
  128. self.StatusbarReposition()
  129. # update statusbar
  130. self.StatusbarUpdate()
  131. def OnFullScreen(self, event):
  132. """!Switch fullscreen mode, hides also toolbars"""
  133. for toolbar in self.toolbars.keys():
  134. self._mgr.GetPane(self.toolbars[toolbar]).Show(self.IsFullScreen())
  135. self._mgr.Update()
  136. self.ShowFullScreen(not self.IsFullScreen())
  137. event.Skip()
  138. def OnCloseWindow(self, event):
  139. self.Destroy()
  140. def GetToolSwitcher(self):
  141. return self._toolSwitcher
  142. def SetProperty(self, name, value):
  143. """Sets property"""
  144. self.statusbarManager.SetProperty(name, value)
  145. def GetProperty(self, name):
  146. """Returns property"""
  147. return self.statusbarManager.GetProperty(name)
  148. def HasProperty(self, name):
  149. """Checks whether object has property"""
  150. return self.statusbarManager.HasProperty(name)
  151. def GetPPM(self):
  152. """Get pixel per meter
  153. .. todo::
  154. now computed every time, is it necessary?
  155. .. todo::
  156. enable user to specify ppm (and store it in UserSettings)
  157. """
  158. # TODO: need to be fixed...
  159. # screen X region problem
  160. # user should specify ppm
  161. dc = wx.ScreenDC()
  162. dpSizePx = wx.DisplaySize() # display size in pixels
  163. dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
  164. dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
  165. sysPpi = dc.GetPPI()
  166. comPpi = (dpSizePx[0] / dpSizeIn[0], dpSizePx[1] / dpSizeIn[1])
  167. ppi = comPpi # pixel per inch
  168. ppm = ((ppi[0] / 2.54) * 100, (ppi[1] / 2.54) * 100) # pixel per meter
  169. Debug.msg(
  170. 4,
  171. "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
  172. "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f"
  173. % (
  174. dpSizePx[0],
  175. dpSizePx[1],
  176. dpSizeMM[0],
  177. dpSizeMM[1],
  178. dpSizeIn[0],
  179. dpSizeIn[1],
  180. sysPpi[0],
  181. sysPpi[1],
  182. comPpi[0],
  183. comPpi[1],
  184. ppm[0],
  185. ppm[1],
  186. ),
  187. )
  188. return ppm
  189. def SetMapScale(self, value, map=None):
  190. """Set current map scale
  191. :param value: scale value (n if scale is 1:n)
  192. :param map: Map instance (if none self.Map is used)
  193. """
  194. if not map:
  195. map = self.Map
  196. region = self.Map.region
  197. dEW = value * (region["cols"] / self.GetPPM()[0])
  198. dNS = value * (region["rows"] / self.GetPPM()[1])
  199. region["n"] = region["center_northing"] + dNS / 2.0
  200. region["s"] = region["center_northing"] - dNS / 2.0
  201. region["w"] = region["center_easting"] - dEW / 2.0
  202. region["e"] = region["center_easting"] + dEW / 2.0
  203. # add to zoom history
  204. self.GetWindow().ZoomHistory(region["n"], region["s"], region["e"], region["w"])
  205. def GetMapScale(self, map=None):
  206. """Get current map scale
  207. :param map: Map instance (if none self.Map is used)
  208. """
  209. if not map:
  210. map = self.GetMap()
  211. region = map.region
  212. ppm = self.GetPPM()
  213. heightCm = region["rows"] / ppm[1] * 100
  214. widthCm = region["cols"] / ppm[0] * 100
  215. Debug.msg(
  216. 4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" % (widthCm, heightCm)
  217. )
  218. xscale = (region["e"] - region["w"]) / (region["cols"] / ppm[0])
  219. yscale = (region["n"] - region["s"]) / (region["rows"] / ppm[1])
  220. scale = (xscale + yscale) / 2.0
  221. Debug.msg(
  222. 3,
  223. "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f"
  224. % (xscale, yscale, scale),
  225. )
  226. return scale
  227. def GetProgressBar(self):
  228. """Returns progress bar
  229. Progress bar can be used by other classes.
  230. """
  231. return self.statusbarManager.GetProgressBar()
  232. def GetMap(self):
  233. """Returns current map (renderer) instance"""
  234. raise NotImplementedError("GetMap")
  235. def GetWindow(self):
  236. """Returns current map window"""
  237. raise NotImplementedError("GetWindow")
  238. def GetWindows(self):
  239. """Returns list of map windows"""
  240. raise NotImplementedError("GetWindows")
  241. def GetMapToolbar(self):
  242. """Returns toolbar with zooming tools"""
  243. raise NotImplementedError("GetMapToolbar")
  244. def GetToolbar(self, name):
  245. """Returns toolbar if exists and is active, else None."""
  246. if name in self.toolbars and self.toolbars[name].IsShown():
  247. return self.toolbars[name]
  248. return None
  249. def StatusbarUpdate(self):
  250. """Update statusbar content"""
  251. if self.statusbarManager:
  252. Debug.msg(5, "MapFrameBase.StatusbarUpdate()")
  253. self.statusbarManager.Update()
  254. def IsAutoRendered(self):
  255. """Check if auto-rendering is enabled"""
  256. # TODO: this is now not the right place to access this attribute
  257. # TODO: add mapWindowProperties to init parameters
  258. # and pass the right object in the init of derived class?
  259. # or do not use this method at all, let mapwindow decide
  260. return self.mapWindowProperties.autoRender
  261. def CoordinatesChanged(self):
  262. """Shows current coordinates on statusbar."""
  263. # assuming that the first mode is coordinates
  264. # probably shold not be here but good solution is not available now
  265. if self.statusbarManager:
  266. if self.statusbarManager.GetMode() == 0:
  267. self.statusbarManager.ShowItem("coordinates")
  268. def StatusbarReposition(self):
  269. """Reposition items in statusbar"""
  270. if self.statusbarManager:
  271. self.statusbarManager.Reposition()
  272. def StatusbarEnableLongHelp(self, enable=True):
  273. """Enable/disable toolbars long help"""
  274. for toolbar in six.itervalues(self.toolbars):
  275. toolbar.EnableLongHelp(enable)
  276. def IsStandalone(self):
  277. """Check if map frame is standalone"""
  278. raise NotImplementedError("IsStandalone")
  279. def OnRender(self, event):
  280. """Re-render map composition (each map layer)"""
  281. raise NotImplementedError("OnRender")
  282. def OnDraw(self, event):
  283. """Re-display current map composition"""
  284. self.MapWindow.UpdateMap(render=False)
  285. def OnErase(self, event):
  286. """Erase the canvas"""
  287. self.MapWindow.EraseMap()
  288. def OnZoomIn(self, event):
  289. """Zoom in the map."""
  290. self.MapWindow.SetModeZoomIn()
  291. def OnZoomOut(self, event):
  292. """Zoom out the map."""
  293. self.MapWindow.SetModeZoomOut()
  294. def _setUpMapWindow(self, mapWindow):
  295. """Binds map windows' zoom history signals to map toolbar."""
  296. # enable or disable zoom history tool
  297. if self.GetMapToolbar():
  298. mapWindow.zoomHistoryAvailable.connect(
  299. lambda: self.GetMapToolbar().Enable("zoomBack", enable=True)
  300. )
  301. mapWindow.zoomHistoryUnavailable.connect(
  302. lambda: self.GetMapToolbar().Enable("zoomBack", enable=False)
  303. )
  304. mapWindow.mouseMoving.connect(self.CoordinatesChanged)
  305. def OnPointer(self, event):
  306. """Sets mouse mode to pointer."""
  307. self.MapWindow.SetModePointer()
  308. def OnPan(self, event):
  309. """Panning, set mouse to drag"""
  310. self.MapWindow.SetModePan()
  311. def OnZoomBack(self, event):
  312. """Zoom last (previously stored position)"""
  313. self.MapWindow.ZoomBack()
  314. def OnZoomToMap(self, event):
  315. """
  316. Set display extents to match selected raster (including NULLs)
  317. or vector map.
  318. """
  319. self.MapWindow.ZoomToMap(layers=self.Map.GetListOfLayers())
  320. def OnZoomToWind(self, event):
  321. """Set display geometry to match computational region
  322. settings (set with g.region)
  323. """
  324. self.MapWindow.ZoomToWind()
  325. def OnZoomToDefault(self, event):
  326. """Set display geometry to match default region settings"""
  327. self.MapWindow.ZoomToDefault()
  328. class SingleMapFrame(MapFrameBase):
  329. """Frame with one map window.
  330. It is base class for frames which needs only one map.
  331. Derived class should have \c self.MapWindow or
  332. it has to override GetWindow() methods.
  333. @note To access maps use getters only
  334. (when using class or when writing class itself).
  335. """
  336. def __init__(
  337. self,
  338. parent=None,
  339. giface=None,
  340. id=wx.ID_ANY,
  341. title="",
  342. style=wx.DEFAULT_FRAME_STYLE,
  343. Map=None,
  344. auimgr=None,
  345. name="",
  346. **kwargs,
  347. ):
  348. """
  349. :param parent: gui parent
  350. :param id: wx id
  351. :param title: window title
  352. :param style: \c wx.Frame style
  353. :param map: instance of render.Map
  354. :param name: frame name
  355. :param kwargs: arguments passed to MapFrameBase
  356. """
  357. MapFrameBase.__init__(
  358. self,
  359. parent=parent,
  360. id=id,
  361. title=title,
  362. style=style,
  363. auimgr=auimgr,
  364. name=name,
  365. **kwargs,
  366. )
  367. self.Map = Map # instance of render.Map
  368. #
  369. # initialize region values
  370. #
  371. if self.Map:
  372. self._initMap(Map=self.Map)
  373. def GetMap(self):
  374. """Returns map (renderer) instance"""
  375. return self.Map
  376. def GetWindow(self):
  377. """Returns map window"""
  378. return self.MapWindow
  379. def GetWindows(self):
  380. """Returns list of map windows"""
  381. return [self.MapWindow]
  382. def OnRender(self, event):
  383. """Re-render map composition (each map layer)"""
  384. self.GetWindow().UpdateMap(render=True, renderVector=True)
  385. # update statusbar
  386. self.StatusbarUpdate()
  387. class DoubleMapFrame(MapFrameBase):
  388. """Frame with two map windows.
  389. It is base class for frames which needs two maps.
  390. There is no primary and secondary map. Both maps are equal.
  391. However, one map is current.
  392. It is expected that derived class will call _bindWindowsActivation()
  393. when both map windows will be initialized.
  394. Drived class should have method GetMapToolbar() returns toolbar
  395. which has methods SetActiveMap() and Enable().
  396. @note To access maps use getters only
  397. (when using class or when writing class itself).
  398. .. todo:
  399. Use it in GCP manager (probably changes to both DoubleMapFrame
  400. and GCP MapFrame will be necessary).
  401. """
  402. def __init__(
  403. self,
  404. parent=None,
  405. id=wx.ID_ANY,
  406. title=None,
  407. style=wx.DEFAULT_FRAME_STYLE,
  408. firstMap=None,
  409. secondMap=None,
  410. auimgr=None,
  411. name=None,
  412. **kwargs,
  413. ):
  414. """
  415. \a firstMap is set as active (by assign it to \c self.Map).
  416. Derived class should assging to \c self.MapWindow to make one
  417. map window current by dafault.
  418. :param parent: gui parent
  419. :param id: wx id
  420. :param title: window title
  421. :param style: \c wx.Frame style
  422. :param name: frame name
  423. :param kwargs: arguments passed to MapFrameBase
  424. """
  425. MapFrameBase.__init__(
  426. self,
  427. parent=parent,
  428. id=id,
  429. title=title,
  430. style=style,
  431. auimgr=auimgr,
  432. name=name,
  433. **kwargs,
  434. )
  435. self.firstMap = firstMap
  436. self.secondMap = secondMap
  437. self.Map = firstMap
  438. #
  439. # initialize region values
  440. #
  441. self._initMap(Map=self.firstMap)
  442. self._initMap(Map=self.secondMap)
  443. self._bindRegions = False
  444. def _bindWindowsActivation(self):
  445. self.GetFirstWindow().Bind(wx.EVT_ENTER_WINDOW, self.ActivateFirstMap)
  446. self.GetSecondWindow().Bind(wx.EVT_ENTER_WINDOW, self.ActivateSecondMap)
  447. def _onToggleTool(self, id):
  448. if self._toolSwitcher.IsToolInGroup(id, "mouseUse"):
  449. self.GetFirstWindow().UnregisterAllHandlers()
  450. self.GetSecondWindow().UnregisterAllHandlers()
  451. def GetFirstMap(self):
  452. """Returns first Map instance"""
  453. return self.firstMap
  454. def GetSecondMap(self):
  455. """Returns second Map instance"""
  456. return self.secondMap
  457. def GetFirstWindow(self):
  458. """Get first map window"""
  459. return self.firstMapWindow
  460. def GetSecondWindow(self):
  461. """Get second map window"""
  462. return self.secondMapWindow
  463. def GetMap(self):
  464. """Returns current map (renderer) instance
  465. @note Use this method to access current map renderer.
  466. (It is not guarented that current map will be stored in
  467. \c self.Map in future versions.)
  468. """
  469. return self.Map
  470. def GetWindow(self):
  471. """Returns current map window
  472. :func:`GetMap()`
  473. """
  474. return self.MapWindow
  475. def GetWindows(self):
  476. """Return list of all windows"""
  477. return [self.firstMapWindow, self.secondMapWindow]
  478. def ActivateFirstMap(self, event=None):
  479. """Make first Map and MapWindow active and (un)bind regions of the two Maps."""
  480. if self.MapWindow == self.firstMapWindow:
  481. return
  482. self.Map = self.firstMap
  483. self.MapWindow = self.firstMapWindow
  484. self.GetMapToolbar().SetActiveMap(0)
  485. # bind/unbind regions
  486. if self._bindRegions:
  487. self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
  488. self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
  489. def ActivateSecondMap(self, event=None):
  490. """Make second Map and MapWindow active and (un)bind regions of the two Maps."""
  491. if self.MapWindow == self.secondMapWindow:
  492. return
  493. self.Map = self.secondMap
  494. self.MapWindow = self.secondMapWindow
  495. self.GetMapToolbar().SetActiveMap(1)
  496. if self._bindRegions:
  497. self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
  498. self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
  499. def SetBindRegions(self, on):
  500. """Set or unset binding display regions."""
  501. self._bindRegions = on
  502. if on:
  503. if self.MapWindow == self.firstMapWindow:
  504. self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
  505. else:
  506. self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
  507. else:
  508. if self.MapWindow == self.firstMapWindow:
  509. self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
  510. else:
  511. self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
  512. def OnZoomChangedFirstMap(self):
  513. """Display region of the first window (Map) changed.
  514. Synchronize the region of the second map and re-render it.
  515. This is the default implementation which can be overridden.
  516. """
  517. region = self.GetFirstMap().GetCurrentRegion()
  518. self.GetSecondMap().region.update(region)
  519. self.Render(mapToRender=self.GetSecondWindow())
  520. def OnZoomChangedSecondMap(self):
  521. """Display region of the second window (Map) changed.
  522. Synchronize the region of the second map and re-render it.
  523. This is the default implementation which can be overridden.
  524. """
  525. region = self.GetSecondMap().GetCurrentRegion()
  526. self.GetFirstMap().region.update(region)
  527. self.Render(mapToRender=self.GetFirstWindow())
  528. def OnZoomIn(self, event):
  529. """Zoom in the map."""
  530. self.GetFirstWindow().SetModeZoomIn()
  531. self.GetSecondWindow().SetModeZoomIn()
  532. def OnZoomOut(self, event):
  533. """Zoom out the map."""
  534. self.GetFirstWindow().SetModeZoomOut()
  535. self.GetSecondWindow().SetModeZoomOut()
  536. def OnPan(self, event):
  537. """Panning, set mouse to pan"""
  538. self.GetFirstWindow().SetModePan()
  539. self.GetSecondWindow().SetModePan()
  540. def OnPointer(self, event):
  541. """Set pointer mode (dragging overlays)"""
  542. self.GetFirstWindow().SetModePointer()
  543. self.GetSecondWindow().SetModePointer()
  544. def OnQuery(self, event):
  545. """Set query mode"""
  546. self.GetFirstWindow().SetModeQuery()
  547. self.GetSecondWindow().SetModeQuery()
  548. def OnRender(self, event):
  549. """Re-render map composition (each map layer)"""
  550. self.Render(mapToRender=self.GetFirstWindow())
  551. self.Render(mapToRender=self.GetSecondWindow())
  552. def Render(self, mapToRender):
  553. """Re-render map composition"""
  554. mapToRender.UpdateMap(
  555. render=True, renderVector=mapToRender == self.GetFirstWindow()
  556. )
  557. # update statusbar
  558. self.StatusbarUpdate()
  559. def OnErase(self, event):
  560. """Erase the canvas"""
  561. self.Erase(mapToErase=self.GetFirstWindow())
  562. self.Erase(mapToErase=self.GetSecondWindow())
  563. def Erase(self, mapToErase):
  564. """Erase the canvas"""
  565. mapToErase.EraseMap()
  566. def OnDraw(self, event):
  567. """Re-display current map composition"""
  568. self.Draw(mapToDraw=self.GetFirstWindow())
  569. self.Draw(mapToDraw=self.GetSecondWindow())
  570. def Draw(self, mapToDraw):
  571. """Re-display current map composition"""
  572. mapToDraw.UpdateMap(render=False)