mapdisplay.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. """
  2. @package gcp.mapdisplay
  3. @brief Display to manage ground control points with two toolbars, one
  4. for various display management functions, one for manipulating GCPs.
  5. Classes:
  6. - mapdisplay::MapFrame
  7. (C) 2006-2011 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Markus Metz
  11. """
  12. import os
  13. import platform
  14. from core import globalvar
  15. import wx
  16. import wx.aui
  17. from mapdisp.toolbars import MapToolbar
  18. from gcp.toolbars import GCPDisplayToolbar, GCPManToolbar
  19. from mapdisp.gprint import PrintOptions
  20. from core.gcmd import GMessage
  21. from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
  22. from gui_core.mapdisp import SingleMapFrame
  23. from gui_core.wrap import Menu
  24. from mapwin.buffered import BufferedMapWindow
  25. from mapwin.base import MapWindowProperties
  26. import mapdisp.statusbar as sb
  27. import gcp.statusbar as sbgcp
  28. # for standalone app
  29. cmdfilename = None
  30. class MapFrame(SingleMapFrame):
  31. """Main frame for map display window. Drawing takes place in
  32. child double buffered drawing window.
  33. """
  34. def __init__(self, parent, giface,
  35. title=_("Manage Ground Control Points"),
  36. toolbars=["gcpdisp"], Map=None, auimgr=None,
  37. name='GCPMapWindow', **kwargs):
  38. """Main map display window with toolbars, statusbar and
  39. DrawWindow
  40. :param giface: GRASS interface instance
  41. :param title: window title
  42. :param toolbars: array of activated toolbars, e.g. ['map', 'digit']
  43. :param map: instance of render.Map
  44. :param auimgs: AUI manager
  45. :param kwargs: wx.Frame attribures
  46. """
  47. SingleMapFrame.__init__(
  48. self,
  49. parent=parent,
  50. giface=giface,
  51. title=title,
  52. Map=Map,
  53. auimgr=auimgr,
  54. name=name,
  55. **kwargs)
  56. self._giface = giface
  57. # properties are shared in other objects, so defining here
  58. self.mapWindowProperties = MapWindowProperties()
  59. self.mapWindowProperties.setValuesFromUserSettings()
  60. self.mapWindowProperties.alignExtent = True
  61. #
  62. # Add toolbars
  63. #
  64. for toolb in toolbars:
  65. self.AddToolbar(toolb)
  66. self.activemap = self.toolbars['gcpdisp'].togglemap
  67. self.activemap.SetSelection(0)
  68. self.SrcMap = self.grwiz.SrcMap # instance of render.Map
  69. self.TgtMap = self.grwiz.TgtMap # instance of render.Map
  70. self._mgr.SetDockSizeConstraint(0.5, 0.5)
  71. #
  72. # Add statusbar
  73. #
  74. # items for choice
  75. self.statusbarItems = [sb.SbCoordinates,
  76. sb.SbRegionExtent,
  77. sb.SbCompRegionExtent,
  78. sb.SbShowRegion,
  79. sb.SbResolution,
  80. sb.SbDisplayGeometry,
  81. sb.SbMapScale,
  82. sb.SbProjection,
  83. sbgcp.SbGoToGCP,
  84. sbgcp.SbRMSError]
  85. # create statusbar and its manager
  86. statusbar = self.CreateStatusBar(number=4, style=0)
  87. statusbar.SetStatusWidths([-5, -2, -1, -1])
  88. self.statusbarManager = sb.SbManager(
  89. mapframe=self, statusbar=statusbar)
  90. # fill statusbar manager
  91. self.statusbarManager.AddStatusbarItemsByClass(
  92. self.statusbarItems, mapframe=self, statusbar=statusbar)
  93. self.statusbarManager.AddStatusbarItem(
  94. sb.SbMask(self, statusbar=statusbar, position=2))
  95. self.statusbarManager.AddStatusbarItem(
  96. sb.SbRender(self, statusbar=statusbar, position=3))
  97. self.statusbarManager.SetMode(8) # goto GCP
  98. #
  99. # Init map display (buffered DC & set default cursor)
  100. #
  101. self.grwiz.SwitchEnv('source')
  102. self.SrcMapWindow = BufferedMapWindow(
  103. parent=self, giface=self._giface, id=wx.ID_ANY,
  104. properties=self.mapWindowProperties, Map=self.SrcMap)
  105. self.grwiz.SwitchEnv('target')
  106. self.TgtMapWindow = BufferedMapWindow(
  107. parent=self, giface=self._giface, id=wx.ID_ANY,
  108. properties=self.mapWindowProperties, Map=self.TgtMap)
  109. self.MapWindow = self.SrcMapWindow
  110. self.Map = self.SrcMap
  111. self._setUpMapWindow(self.SrcMapWindow)
  112. self._setUpMapWindow(self.TgtMapWindow)
  113. self.SrcMapWindow.SetNamedCursor('cross')
  114. self.TgtMapWindow.SetNamedCursor('cross')
  115. # used to switch current map (combo box in toolbar)
  116. self.SrcMapWindow.mouseEntered.connect(
  117. lambda:
  118. self._setActiveMapWindow(self.SrcMapWindow))
  119. self.TgtMapWindow.mouseEntered.connect(
  120. lambda:
  121. self._setActiveMapWindow(self.TgtMapWindow))
  122. #
  123. # initialize region values
  124. #
  125. self._initMap(Map=self.SrcMap)
  126. self._initMap(Map=self.TgtMap)
  127. self.GetMapToolbar().SelectDefault()
  128. #
  129. # Bind various events
  130. #
  131. self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
  132. self.Bind(wx.EVT_SIZE, self.OnSize)
  133. #
  134. # Update fancy gui style
  135. #
  136. # AuiManager wants a CentrePane, workaround to get two equally sized
  137. # windows
  138. self.list = self.CreateGCPList()
  139. #self.SrcMapWindow.SetSize((300, 300))
  140. #self.TgtMapWindow.SetSize((300, 300))
  141. self.list.SetSize((100, 150))
  142. self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
  143. Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
  144. RightDockable(False).PinButton().FloatingSize((600, 200)).
  145. CloseButton(False).DestroyOnClose(True).
  146. Top().Layer(1).MinSize((200, 100)))
  147. self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
  148. Name("source").Caption(_("Source Display")).Dockable(False).
  149. CloseButton(False).DestroyOnClose(True).Floatable(False).
  150. Centre())
  151. self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
  152. Name("target").Caption(_("Target Display")).Dockable(False).
  153. CloseButton(False).DestroyOnClose(True).Floatable(False).
  154. Right().Layer(0))
  155. srcwidth, srcheight = self.SrcMapWindow.GetSize()
  156. tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
  157. srcwidth = (srcwidth + tgtwidth) / 2
  158. self._mgr.GetPane("target").Hide()
  159. self._mgr.Update()
  160. self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
  161. self._mgr.GetPane("target").BestSize((srcwidth, srcheight))
  162. if self.show_target:
  163. self._mgr.GetPane("target").Show()
  164. else:
  165. self.activemap.Enable(False)
  166. # needed by Mac OS, does not harm on Linux, breaks display on Windows
  167. if platform.system() != 'Windows':
  168. self._mgr.Update()
  169. #
  170. # Init print module and classes
  171. #
  172. self.printopt = PrintOptions(self, self.MapWindow)
  173. #
  174. # Initialization of digitization tool
  175. #
  176. self.digit = None
  177. # set active map
  178. self.MapWindow = self.SrcMapWindow
  179. self.Map = self.SrcMap
  180. # do not init zoom history here, that happens when zooming to map(s)
  181. #
  182. # Re-use dialogs
  183. #
  184. self.dialogs = {}
  185. self.dialogs['attributes'] = None
  186. self.dialogs['category'] = None
  187. self.dialogs['barscale'] = None
  188. self.dialogs['legend'] = None
  189. self.decorationDialog = None # decoration/overlays
  190. # doing nice things in statusbar when other things are ready
  191. self.statusbarManager.Update()
  192. def _setUpMapWindow(self, mapWindow):
  193. # TODO: almost the same implementation as for MapFrameBase (only names differ)
  194. # enable or disable zoom history tool
  195. mapWindow.zoomHistoryAvailable.connect(
  196. lambda:
  197. self.GetMapToolbar().Enable('zoomback', enable=True))
  198. mapWindow.zoomHistoryUnavailable.connect(
  199. lambda:
  200. self.GetMapToolbar().Enable('zoomback', enable=False))
  201. mapWindow.mouseMoving.connect(self.CoordinatesChanged)
  202. def AddToolbar(self, name):
  203. """Add defined toolbar to the window
  204. Currently known toolbars are:
  205. - 'map' - basic map toolbar
  206. - 'vdigit' - vector digitizer
  207. - 'gcpdisp' - GCP Manager, Display
  208. - 'gcpman' - GCP Manager, points management
  209. - 'nviz' - 3D view mode
  210. """
  211. # default toolbar
  212. if name == "map":
  213. self.toolbars['map'] = MapToolbar(self, self._toolSwitcher)
  214. self._mgr.AddPane(self.toolbars['map'],
  215. wx.aui.AuiPaneInfo().
  216. Name("maptoolbar").Caption(_("Map Toolbar")).
  217. ToolbarPane().Top().
  218. LeftDockable(False).RightDockable(False).
  219. BottomDockable(False).TopDockable(True).
  220. CloseButton(False).Layer(2).
  221. BestSize((self.toolbars['map'].GetSize())))
  222. # GCP display
  223. elif name == "gcpdisp":
  224. self.toolbars['gcpdisp'] = GCPDisplayToolbar(
  225. self, self._toolSwitcher)
  226. self._mgr.AddPane(self.toolbars['gcpdisp'],
  227. wx.aui.AuiPaneInfo().
  228. Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
  229. ToolbarPane().Top().
  230. LeftDockable(False).RightDockable(False).
  231. BottomDockable(False).TopDockable(True).
  232. CloseButton(False).Layer(2))
  233. if not self.show_target:
  234. self.toolbars['gcpdisp'].Enable('zoommenu', enable=False)
  235. self.toolbars['gcpman'] = GCPManToolbar(self)
  236. self._mgr.AddPane(self.toolbars['gcpman'],
  237. wx.aui.AuiPaneInfo().
  238. Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
  239. ToolbarPane().Top().Row(1).
  240. LeftDockable(False).RightDockable(False).
  241. BottomDockable(False).TopDockable(True).
  242. CloseButton(False).Layer(2))
  243. self._mgr.Update()
  244. def OnUpdateProgress(self, event):
  245. """
  246. Update progress bar info
  247. """
  248. self.GetProgressBar().UpdateProgress(event.layer, event.map)
  249. event.Skip()
  250. def OnFocus(self, event):
  251. """
  252. Change choicebook page to match display.
  253. Or set display for georectifying
  254. """
  255. # was in if layer manager but considering the state it was executed
  256. # always, moreover, there is no layer manager dependent code
  257. # in GCP Management, set focus to current MapWindow for mouse actions
  258. self.OnPointer(event)
  259. self.MapWindow.SetFocus()
  260. event.Skip()
  261. def OnDraw(self, event):
  262. """Re-display current map composition
  263. """
  264. self.MapWindow.UpdateMap(render=False)
  265. def OnRender(self, event):
  266. """Re-render map composition (each map layer)
  267. """
  268. # FIXME: remove qlayer code or use RemoveQueryLayer() now in mapdisp.frame
  269. # delete tmp map layers (queries)
  270. qlayer = self.Map.GetListOfLayers(name=globalvar.QUERYLAYER)
  271. for layer in qlayer:
  272. self.Map.DeleteLayer(layer)
  273. self.SrcMapWindow.UpdateMap(render=True)
  274. if self.show_target:
  275. self.TgtMapWindow.UpdateMap(render=True)
  276. # update statusbar
  277. self.StatusbarUpdate()
  278. def OnPointer(self, event):
  279. """Pointer button clicked
  280. """
  281. self.SrcMapWindow.SetModePointer()
  282. self.TgtMapWindow.SetModePointer()
  283. # change the default cursor
  284. self.SrcMapWindow.SetNamedCursor('cross')
  285. self.TgtMapWindow.SetNamedCursor('cross')
  286. def OnZoomIn(self, event):
  287. """Zoom in the map."""
  288. self.SrcMapWindow.SetModeZoomIn()
  289. self.TgtMapWindow.SetModeZoomIn()
  290. def OnZoomOut(self, event):
  291. """Zoom out the map."""
  292. self.SrcMapWindow.SetModeZoomOut()
  293. self.TgtMapWindow.SetModeZoomOut()
  294. def OnPan(self, event):
  295. """Panning, set mouse to drag"""
  296. self.SrcMapWindow.SetModePan()
  297. self.TgtMapWindow.SetModePan()
  298. def OnErase(self, event):
  299. """
  300. Erase the canvas
  301. """
  302. self.MapWindow.EraseMap()
  303. if self.MapWindow == self.SrcMapWindow:
  304. win = self.TgtMapWindow
  305. elif self.MapWindow == self.TgtMapWindow:
  306. win = self.SrcMapWindow
  307. win.EraseMap()
  308. def SaveToFile(self, event):
  309. """Save map to image
  310. """
  311. img = self.MapWindow.img
  312. if not img:
  313. GMessage(parent=self, message=_(
  314. "Nothing to render (empty map). Operation canceled."))
  315. return
  316. filetype, ltype = GetImageHandlers(img)
  317. # get size
  318. dlg = ImageSizeDialog(self)
  319. dlg.CentreOnParent()
  320. if dlg.ShowModal() != wx.ID_OK:
  321. dlg.Destroy()
  322. return
  323. width, height = dlg.GetValues()
  324. dlg.Destroy()
  325. # get filename
  326. dlg = wx.FileDialog(parent=self,
  327. message=_("Choose a file name to save the image "
  328. "(no need to add extension)"),
  329. wildcard=filetype,
  330. style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
  331. if dlg.ShowModal() == wx.ID_OK:
  332. path = dlg.GetPath()
  333. if not path:
  334. dlg.Destroy()
  335. return
  336. base, ext = os.path.splitext(path)
  337. fileType = ltype[dlg.GetFilterIndex()]['type']
  338. extType = ltype[dlg.GetFilterIndex()]['ext']
  339. if ext != extType:
  340. path = base + '.' + extType
  341. self.MapWindow.SaveToFile(path, fileType,
  342. width, height)
  343. dlg.Destroy()
  344. def PrintMenu(self, event):
  345. """
  346. Print options and output menu for map display
  347. """
  348. point = wx.GetMousePosition()
  349. printmenu = Menu()
  350. # Add items to the menu
  351. setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
  352. printmenu.AppendItem(setup)
  353. self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
  354. preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
  355. printmenu.AppendItem(preview)
  356. self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
  357. doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
  358. printmenu.AppendItem(doprint)
  359. self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
  360. # Popup the menu. If an item is selected then its handler
  361. # will be called before PopupMenu returns.
  362. self.PopupMenu(printmenu)
  363. printmenu.Destroy()
  364. def OnZoomToRaster(self, event):
  365. """
  366. Set display extents to match selected raster map (ignore NULLs)
  367. """
  368. self.MapWindow.ZoomToMap(ignoreNulls=True)
  369. def OnZoomToSaved(self, event):
  370. """Set display geometry to match extents in
  371. saved region file
  372. """
  373. self.MapWindow.SetRegion(zoomOnly=True)
  374. def OnDisplayToWind(self, event):
  375. """Set computational region (WIND file) to match display
  376. extents
  377. """
  378. self.MapWindow.DisplayToWind()
  379. def SaveDisplayRegion(self, event):
  380. """Save display extents to named region file.
  381. """
  382. self.MapWindow.SaveDisplayRegion()
  383. def OnZoomMenu(self, event):
  384. """Popup Zoom menu
  385. """
  386. point = wx.GetMousePosition()
  387. zoommenu = Menu()
  388. # Add items to the menu
  389. zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
  390. 'Zoom to computational region (set with g.region)'))
  391. zoommenu.AppendItem(zoomwind)
  392. self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
  393. zoomdefault = wx.MenuItem(
  394. zoommenu, wx.ID_ANY, _('Zoom to default region'))
  395. zoommenu.AppendItem(zoomdefault)
  396. self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
  397. zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
  398. zoommenu.AppendItem(zoomsaved)
  399. self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
  400. savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
  401. 'Set computational region from display'))
  402. zoommenu.AppendItem(savewind)
  403. self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
  404. savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _(
  405. 'Save display geometry to named region'))
  406. zoommenu.AppendItem(savezoom)
  407. self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
  408. # Popup the menu. If an item is selected then its handler
  409. # will be called before PopupMenu returns.
  410. self.PopupMenu(zoommenu)
  411. zoommenu.Destroy()
  412. def IsStandalone(self):
  413. """Check if Map display is standalone"""
  414. # we do not know and we do not care, so always False
  415. return True
  416. def GetLayerManager(self):
  417. """Get reference to Layer Manager
  418. :return: always None
  419. """
  420. return None
  421. def GetSrcWindow(self):
  422. return self.SrcMapWindow
  423. def GetTgtWindow(self):
  424. return self.TgtMapWindow
  425. def GetShowTarget(self):
  426. return self.show_target
  427. def GetMapToolbar(self):
  428. """Returns toolbar with zooming tools"""
  429. return self.toolbars['gcpdisp']
  430. def _setActiveMapWindow(self, mapWindow):
  431. if not self.MapWindow == mapWindow:
  432. self.MapWindow = mapWindow
  433. self.Map = mapWindow.Map
  434. self.UpdateActive(mapWindow)
  435. # needed for wingrass
  436. self.SetFocus()