mapdisp.py 25 KB

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