gcpmapdisp.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. """!
  2. @package gcpmapdisp.py
  3. @brief display to manage ground control points with two toolbars, one for
  4. various display management functions, one for manipulating GCPs.
  5. Classes:
  6. - MapFrame
  7. (C) 2006-2010 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. Derived from mapdisp.py
  12. @author Markus Metz
  13. """
  14. import os
  15. import sys
  16. import glob
  17. import math
  18. import tempfile
  19. import copy
  20. import platform
  21. import globalvar
  22. if not os.getenv("GRASS_WXBUNDLED"):
  23. globalvar.CheckForWx()
  24. import wx
  25. import wx.aui
  26. try:
  27. import subprocess
  28. except:
  29. CompatPath = os.path.join(globalvar.ETCWXDIR)
  30. sys.path.append(CompatPath)
  31. from compat import subprocess
  32. gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
  33. sys.path.append(gmpath)
  34. grassPath = os.path.join(globalvar.ETCDIR, "python")
  35. sys.path.append(grassPath)
  36. import render
  37. import toolbars
  38. import menuform
  39. import gselect
  40. import disp_print
  41. import gcmd
  42. import dbm
  43. import dbm_dialogs
  44. import globalvar
  45. import utils
  46. import gdialogs
  47. from grass.script import core as grass
  48. from debug import Debug
  49. from icon import Icons
  50. from preferences import globalSettings as UserSettings
  51. from mapdisp_command import Command
  52. from mapdisp_window import BufferedWindow
  53. # for standalone app
  54. cmdfilename = None
  55. class MapFrame(wx.Frame):
  56. """!Main frame for map display window. Drawing takes place in
  57. child double buffered drawing window.
  58. """
  59. def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS Manage Ground Control Points"),
  60. style=wx.DEFAULT_FRAME_STYLE, toolbars=["gcpdisp"],
  61. tree=None, notebook=None, lmgr=None, page=None,
  62. Map=None, auimgr=None, **kwargs):
  63. """!Main map display window with toolbars, statusbar and
  64. DrawWindow
  65. @param toolbars array of activated toolbars, e.g. ['map', 'digit']
  66. @param tree reference to layer tree
  67. @param notebook control book ID in Layer Manager
  68. @param lmgr Layer Manager
  69. @param page notebook page with layer tree
  70. @param Map instance of render.Map
  71. @param auimgs AUI manager
  72. @param kwargs wx.Frame attribures
  73. """
  74. self._layerManager = lmgr # Layer Manager object
  75. self.Map = Map # instance of render.Map
  76. self.tree = tree # Layer Manager layer tree object
  77. self.page = page # Notebook page holding the layer tree
  78. self.layerbook = notebook # Layer Manager layer tree notebook
  79. self.parent = parent
  80. if not kwargs.has_key('name'):
  81. kwargs['name'] = 'GCPMapWindow'
  82. wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
  83. # available cursors
  84. self.cursors = {
  85. # default: cross
  86. # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
  87. "default" : wx.StockCursor(wx.CURSOR_ARROW),
  88. "cross" : wx.StockCursor(wx.CURSOR_CROSS),
  89. "hand" : wx.StockCursor(wx.CURSOR_HAND),
  90. "pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
  91. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  92. }
  93. #
  94. # set the size & system icon
  95. #
  96. self.SetClientSize(self.GetSize())
  97. self.iconsize = (16, 16)
  98. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
  99. #
  100. # Fancy gui
  101. #
  102. self._mgr = wx.aui.AuiManager(self)
  103. #
  104. # Add toolbars
  105. #
  106. self.toolbars = { 'map' : None,
  107. 'vdigit' : None,
  108. 'georect' : None,
  109. 'gcpdisp' : None,
  110. 'gcpman' : None,
  111. 'nviz' : None }
  112. for toolb in toolbars:
  113. self.AddToolbar(toolb)
  114. self.activemap = self.toolbars['gcpdisp'].togglemap
  115. self.activemap.SetSelection(0)
  116. self.SrcMap = self.grwiz.SrcMap # instance of render.Map
  117. self.TgtMap = self.grwiz.TgtMap # instance of render.Map
  118. self._mgr.SetDockSizeConstraint(0.5, 0.5)
  119. #
  120. # Add statusbar
  121. #
  122. self.statusbar = self.CreateStatusBar(number=4, style=0)
  123. self.statusbar.SetStatusWidths([-5, -2, -1, -1])
  124. self.statusbarWin = dict()
  125. self.statusbarWin['toggle'] = wx.Choice(self.statusbar, wx.ID_ANY,
  126. choices = [_("Coordinates"),
  127. _("Extent"),
  128. _("Comp. region"),
  129. _("Show comp. extent"),
  130. _("Display mode"),
  131. _("Display geometry"),
  132. _("Map scale"),
  133. _("Go to GCP No."),
  134. _("RMS error")])
  135. # set StatusBar to Go to GCP No.
  136. self.statusbarWin['toggle'].SetSelection(7)
  137. self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.statusbarWin['toggle'])
  138. # auto-rendering checkbox
  139. self.statusbarWin['render'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  140. label=_("Render"))
  141. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.statusbarWin['render'])
  142. self.statusbarWin['render'].SetValue(UserSettings.Get(group='display',
  143. key='autoRendering',
  144. subkey='enabled'))
  145. self.statusbarWin['render'].SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
  146. # show region
  147. self.statusbarWin['region'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  148. label=_("Show computational extent"))
  149. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.statusbarWin['region'])
  150. self.statusbarWin['region'].SetValue(False)
  151. self.statusbarWin['region'].Hide()
  152. self.statusbarWin['region'].SetToolTip(wx.ToolTip (_("Show/hide computational "
  153. "region extent (set with g.region). "
  154. "Display region drawn as a blue box inside the "
  155. "computational region, "
  156. "computational region inside a display region "
  157. "as a red box).")))
  158. # set resolution
  159. self.statusbarWin['resolution'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  160. label=_("Constrain display resolution to computational settings"))
  161. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.statusbarWin['resolution'])
  162. self.statusbarWin['resolution'].SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
  163. self.statusbarWin['resolution'].Hide()
  164. self.statusbarWin['resolution'].SetToolTip(wx.ToolTip (_("Constrain display resolution "
  165. "to computational region settings. "
  166. "Default value for new map displays can "
  167. "be set up in 'User GUI settings' dialog.")))
  168. # map scale
  169. self.statusbarWin['mapscale'] = wx.ComboBox(parent = self.statusbar, id = wx.ID_ANY,
  170. style = wx.TE_PROCESS_ENTER,
  171. size=(150, -1))
  172. self.statusbarWin['mapscale'].SetItems(['1:1000',
  173. '1:5000',
  174. '1:10000',
  175. '1:25000',
  176. '1:50000',
  177. '1:100000',
  178. '1:1000000'])
  179. self.statusbarWin['mapscale'].Hide()
  180. self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.statusbarWin['mapscale'])
  181. self.statusbar.Bind(wx.EVT_COMBOBOX, self.OnChangeMapScale, self.statusbarWin['mapscale'])
  182. # go to
  183. self.statusbarWin['goto'] = wx.SpinCtrl(parent=self.statusbar, id=wx.ID_ANY,
  184. min=0)
  185. self.statusbar.Bind(wx.EVT_SPINCTRL, self.OnGoTo, self.statusbarWin['goto'])
  186. self.statusbarWin['goto'].Hide()
  187. self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo, self.statusbarWin['goto'])
  188. # projection, unused but BufferedWindow checks for it
  189. self.statusbarWin['projection'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  190. label=_("Use defined projection"))
  191. self.statusbarWin['projection'].SetValue(False)
  192. size = self.statusbarWin['projection'].GetSize()
  193. self.statusbarWin['projection'].SetMinSize((size[0] + 150, size[1]))
  194. self.statusbarWin['projection'].SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
  195. "in the statusbar. Projection can be "
  196. "defined in GUI preferences dialog "
  197. "(tab 'Display')")))
  198. self.statusbarWin['projection'].Hide()
  199. # mask
  200. self.statusbarWin['mask'] = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
  201. label = '')
  202. self.statusbarWin['mask'].SetForegroundColour(wx.Colour(255, 0, 0))
  203. # on-render gauge
  204. self.statusbarWin['progress'] = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
  205. range=0, style=wx.GA_HORIZONTAL)
  206. self.statusbarWin['progress'].Hide()
  207. self.StatusbarReposition() # reposition statusbar
  208. #
  209. # Init map display (buffered DC & set default cursor)
  210. #
  211. self.grwiz.SwitchEnv('source')
  212. self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY,
  213. Map=self.SrcMap, tree=self.tree, lmgr=self._layerManager)
  214. self.grwiz.SwitchEnv('target')
  215. self.TgtMapWindow = BufferedWindow(self, id=wx.ID_ANY,
  216. Map=self.TgtMap, tree=self.tree, lmgr=self._layerManager)
  217. self.MapWindow = self.SrcMapWindow
  218. self.Map = self.SrcMap
  219. self.SrcMapWindow.SetCursor(self.cursors["cross"])
  220. self.TgtMapWindow.SetCursor(self.cursors["cross"])
  221. #
  222. # initialize region values
  223. #
  224. self.__InitDisplay()
  225. #
  226. # Bind various events
  227. #
  228. self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
  229. self.Bind(render.EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
  230. self.Bind(wx.EVT_SIZE, self.OnDispResize)
  231. self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
  232. #
  233. # Update fancy gui style
  234. #
  235. # AuiManager wants a CentrePane, workaround to get two equally sized windows
  236. self.list = self.CreateGCPList()
  237. #self.SrcMapWindow.SetSize((300, 300))
  238. #self.TgtMapWindow.SetSize((300, 300))
  239. self.list.SetSize((100, 150))
  240. self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
  241. Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
  242. RightDockable(False).PinButton().FloatingSize((600,200)).
  243. CloseButton(False).DestroyOnClose(True).
  244. Top().Layer(1).MinSize((200,100)))
  245. self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
  246. Name("source").Caption(_("Source Display")).Dockable(False).
  247. CloseButton(False).DestroyOnClose(True).Floatable(False).
  248. Centre())
  249. self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
  250. Name("target").Caption(_("Target Display")).Dockable(False).
  251. CloseButton(False).DestroyOnClose(True).Floatable(False).
  252. Right().Layer(0))
  253. srcwidth, srcheight = self.SrcMapWindow.GetSize()
  254. tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
  255. srcwidth = (srcwidth + tgtwidth) / 2
  256. self._mgr.GetPane("target").Hide()
  257. self._mgr.Update()
  258. self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
  259. self._mgr.GetPane("target").BestSize((srcwidth, srcheight))
  260. if self.show_target:
  261. self._mgr.GetPane("target").Show()
  262. else:
  263. self.activemap.Enable(False)
  264. # needed by Mac OS, does not harm on Linux, breaks display on Windows
  265. if platform.system() != 'Windows':
  266. self._mgr.Update()
  267. #
  268. # Init print module and classes
  269. #
  270. self.printopt = disp_print.PrintOptions(self, self.MapWindow)
  271. #
  272. # Initialization of digitization tool
  273. #
  274. self.digit = None
  275. # set active map
  276. self.MapWindow = self.SrcMapWindow
  277. self.Map = self.SrcMap
  278. # do not init zoom history here, that happens when zooming to map(s)
  279. #
  280. # Re-use dialogs
  281. #
  282. self.dialogs = {}
  283. self.dialogs['attributes'] = None
  284. self.dialogs['category'] = None
  285. self.dialogs['barscale'] = None
  286. self.dialogs['legend'] = None
  287. self.decorationDialog = None # decoration/overlays
  288. def AddToolbar(self, name):
  289. """!Add defined toolbar to the window
  290. Currently known toolbars are:
  291. - 'map' - basic map toolbar
  292. - 'vdigit' - vector digitizer
  293. - 'gcpdisp' - GCP Manager, Display
  294. - 'gcpman' - GCP Manager, points management
  295. - 'georect' - georectifier
  296. - 'nviz' - 3D view mode
  297. """
  298. # default toolbar
  299. if name == "map":
  300. self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
  301. self._mgr.AddPane(self.toolbars['map'],
  302. wx.aui.AuiPaneInfo().
  303. Name("maptoolbar").Caption(_("Map Toolbar")).
  304. ToolbarPane().Top().
  305. LeftDockable(False).RightDockable(False).
  306. BottomDockable(False).TopDockable(True).
  307. CloseButton(False).Layer(2).
  308. BestSize((self.toolbars['map'].GetSize())))
  309. # GCP display
  310. elif name == "gcpdisp":
  311. self.toolbars['gcpdisp'] = toolbars.GCPDisplayToolbar(self)
  312. self._mgr.AddPane(self.toolbars['gcpdisp'],
  313. wx.aui.AuiPaneInfo().
  314. Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
  315. ToolbarPane().Top().
  316. LeftDockable(False).RightDockable(False).
  317. BottomDockable(False).TopDockable(True).
  318. CloseButton(False).Layer(2))
  319. if self.show_target == False:
  320. self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
  321. self.toolbars['gcpman'] = toolbars.GCPManToolbar(self)
  322. self._mgr.AddPane(self.toolbars['gcpman'],
  323. wx.aui.AuiPaneInfo().
  324. Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
  325. ToolbarPane().Top().Row(1).
  326. LeftDockable(False).RightDockable(False).
  327. BottomDockable(False).TopDockable(True).
  328. CloseButton(False).Layer(2))
  329. self._mgr.Update()
  330. def __InitDisplay(self):
  331. """
  332. Initialize map display, set dimensions and map region
  333. """
  334. self.width, self.height = self.GetClientSize()
  335. Debug.msg(2, "MapFrame.__InitDisplay():")
  336. self.grwiz.SwitchEnv('source')
  337. self.SrcMap.ChangeMapSize(self.GetClientSize())
  338. self.SrcMap.region = self.SrcMap.GetRegion() # g.region -upgc
  339. self.grwiz.SwitchEnv('target')
  340. self.TgtMap.ChangeMapSize(self.GetClientSize())
  341. self.TgtMap.region = self.TgtMap.GetRegion() # g.region -upgc
  342. # self.SrcMap.SetRegion() # adjust region to match display window
  343. # self.TgtMap.SetRegion() # adjust region to match display window
  344. def OnUpdateProgress(self, event):
  345. """
  346. Update progress bar info
  347. """
  348. self.statusbarWin['progress'].SetValue(event.value)
  349. event.Skip()
  350. def OnFocus(self, event):
  351. """
  352. Change choicebook page to match display.
  353. Or set display for georectifying
  354. """
  355. if self._layerManager and \
  356. self._layerManager.gcpmanagement:
  357. # in GCP Management, set focus to current MapWindow for mouse actions
  358. self.OnPointer(event)
  359. self.MapWindow.SetFocus()
  360. else:
  361. # change bookcontrol page to page associated with display
  362. # GCP Manager: use bookcontrol?
  363. if self.page:
  364. pgnum = self.layerbook.GetPageIndex(self.page)
  365. if pgnum > -1:
  366. self.layerbook.SetSelection(pgnum)
  367. event.Skip()
  368. def OnDraw(self, event):
  369. """!Re-display current map composition
  370. """
  371. self.MapWindow.UpdateMap(render = False)
  372. def OnRender(self, event):
  373. """!Re-render map composition (each map layer)
  374. """
  375. # delete tmp map layers (queries)
  376. qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
  377. for layer in qlayer:
  378. self.Map.DeleteLayer(layer)
  379. self.SrcMapWindow.UpdateMap(render=True)
  380. if self.show_target:
  381. self.TgtMapWindow.UpdateMap(render=True)
  382. # update statusbar
  383. self.StatusbarUpdate()
  384. def OnPointer(self, event):
  385. """!Pointer button clicked
  386. """
  387. # change the cursor
  388. self.SrcMapWindow.SetCursor(self.cursors["cross"])
  389. self.SrcMapWindow.mouse['use'] = "pointer"
  390. self.SrcMapWindow.mouse['box'] = "point"
  391. self.TgtMapWindow.SetCursor(self.cursors["cross"])
  392. self.TgtMapWindow.mouse['use'] = "pointer"
  393. self.TgtMapWindow.mouse['box'] = "point"
  394. def OnZoomIn(self, event):
  395. """
  396. Zoom in the map.
  397. Set mouse cursor, zoombox attributes, and zoom direction
  398. """
  399. if self.toolbars['map']:
  400. self.toolbars['map'].OnTool(event)
  401. self.toolbars['map'].action['desc'] = ''
  402. self.MapWindow.mouse['use'] = "zoom"
  403. self.MapWindow.mouse['box'] = "box"
  404. self.MapWindow.zoomtype = 1
  405. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  406. # change the cursor
  407. self.MapWindow.SetCursor(self.cursors["cross"])
  408. if self.MapWindow == self.SrcMapWindow:
  409. win = self.TgtMapWindow
  410. elif self.MapWindow == self.TgtMapWindow:
  411. win = self.SrcMapWindow
  412. win.mouse['use'] = "zoom"
  413. win.mouse['box'] = "box"
  414. win.zoomtype = 1
  415. win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  416. # change the cursor
  417. win.SetCursor(self.cursors["cross"])
  418. def OnZoomOut(self, event):
  419. """
  420. Zoom out the map.
  421. Set mouse cursor, zoombox attributes, and zoom direction
  422. """
  423. if self.toolbars['map']:
  424. self.toolbars['map'].OnTool(event)
  425. self.toolbars['map'].action['desc'] = ''
  426. self.MapWindow.mouse['use'] = "zoom"
  427. self.MapWindow.mouse['box'] = "box"
  428. self.MapWindow.zoomtype = -1
  429. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  430. # change the cursor
  431. self.MapWindow.SetCursor(self.cursors["cross"])
  432. if self.MapWindow == self.SrcMapWindow:
  433. win = self.TgtMapWindow
  434. elif self.MapWindow == self.TgtMapWindow:
  435. win = self.SrcMapWindow
  436. win.mouse['use'] = "zoom"
  437. win.mouse['box'] = "box"
  438. win.zoomtype = -1
  439. win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  440. # change the cursor
  441. win.SetCursor(self.cursors["cross"])
  442. def OnZoomBack(self, event):
  443. """
  444. Zoom last (previously stored position)
  445. """
  446. self.MapWindow.ZoomBack()
  447. def OnPan(self, event):
  448. """
  449. Panning, set mouse to drag
  450. """
  451. if self.toolbars['map']:
  452. self.toolbars['map'].OnTool(event)
  453. self.toolbars['map'].action['desc'] = ''
  454. self.MapWindow.mouse['use'] = "pan"
  455. self.MapWindow.mouse['box'] = "pan"
  456. self.MapWindow.zoomtype = 0
  457. # change the cursor
  458. self.MapWindow.SetCursor(self.cursors["hand"])
  459. if self.MapWindow == self.SrcMapWindow:
  460. win = self.TgtMapWindow
  461. elif self.MapWindow == self.TgtMapWindow:
  462. win = self.SrcMapWindow
  463. win.mouse['use'] = "pan"
  464. win.mouse['box'] = "pan"
  465. win.zoomtype = 0
  466. # change the cursor
  467. win.SetCursor(self.cursors["hand"])
  468. def OnErase(self, event):
  469. """
  470. Erase the canvas
  471. """
  472. self.MapWindow.EraseMap()
  473. if self.MapWindow == self.SrcMapWindow:
  474. win = self.TgtMapWindow
  475. elif self.MapWindow == self.TgtMapWindow:
  476. win = self.SrcMapWindow
  477. win.EraseMap()
  478. def OnZoomRegion(self, event):
  479. """
  480. Zoom to region
  481. """
  482. self.Map.getRegion()
  483. self.Map.getResolution()
  484. self.UpdateMap()
  485. # event.Skip()
  486. def OnAlignRegion(self, event):
  487. """
  488. Align region
  489. """
  490. if not self.Map.alignRegion:
  491. self.Map.alignRegion = True
  492. else:
  493. self.Map.alignRegion = False
  494. # event.Skip()
  495. def OnToggleRender(self, event):
  496. """
  497. Enable/disable auto-rendering
  498. """
  499. if self.statusbarWin['render'].GetValue():
  500. self.OnRender(None)
  501. def OnToggleShowRegion(self, event):
  502. """
  503. Show/Hide extent in map canvas
  504. """
  505. if self.statusbarWin['region'].GetValue():
  506. # show extent
  507. self.MapWindow.regionCoords = []
  508. else:
  509. del self.MapWindow.regionCoords
  510. # redraw map if auto-rendering is enabled
  511. if self.statusbarWin['render'].GetValue():
  512. self.OnRender(None)
  513. def OnToggleResolution(self, event):
  514. """
  515. Use resolution of computation region settings
  516. for redering image instead of display resolution
  517. """
  518. # redraw map if auto-rendering is enabled
  519. if self.statusbarWin['render'].GetValue():
  520. self.OnRender(None)
  521. def OnToggleStatus(self, event):
  522. """
  523. Toggle status text
  524. """
  525. self.StatusbarUpdate()
  526. def OnChangeMapScale(self, event):
  527. """
  528. Map scale changed by user
  529. """
  530. scale = event.GetString()
  531. try:
  532. if scale[:2] != '1:':
  533. raise ValueError
  534. value = int(scale[2:])
  535. except ValueError:
  536. self.statusbarWin['mapscale'].SetValue('1:%ld' % int(self.mapScaleValue))
  537. return
  538. dEW = value * (self.Map.region['cols'] / self.ppm[0])
  539. dNS = value * (self.Map.region['rows'] / self.ppm[1])
  540. self.Map.region['n'] = self.Map.region['center_northing'] + dNS / 2.
  541. self.Map.region['s'] = self.Map.region['center_northing'] - dNS / 2.
  542. self.Map.region['w'] = self.Map.region['center_easting'] - dEW / 2.
  543. self.Map.region['e'] = self.Map.region['center_easting'] + dEW / 2.
  544. # add to zoom history
  545. self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  546. self.Map.region['e'], self.Map.region['w'])
  547. # redraw a map
  548. self.MapWindow.UpdateMap()
  549. self.statusbarWin['mapscale'].SetFocus()
  550. def OnGoTo(self, event):
  551. """
  552. Go to position
  553. """
  554. #GCPNo = int(event.GetString())
  555. GCPNo = self.statusbarWin['goto'].GetValue()
  556. if GCPNo < 0 or GCPNo > len(self.mapcoordlist):
  557. wx.MessageBox(parent=self,
  558. message="%s 1 - %s." % (_("Valid Range:"),
  559. len(self.mapcoordlist)),
  560. caption=_("Invalid GCP Number"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  561. return
  562. if GCPNo == 0:
  563. return
  564. self.list.selectedkey = GCPNo
  565. self.list.selected = self.list.FindItemData(-1, GCPNo)
  566. self.list.render = False
  567. self.list.SetItemState(self.list.selected,
  568. wx.LIST_STATE_SELECTED,
  569. wx.LIST_STATE_SELECTED)
  570. self.list.render = True
  571. # Source MapWindow:
  572. begin = (self.mapcoordlist[GCPNo][1], self.mapcoordlist[GCPNo][2])
  573. begin = self.SrcMapWindow.Cell2Pixel(begin)
  574. end = begin
  575. self.SrcMapWindow.Zoom(begin, end, 0)
  576. # redraw map
  577. self.SrcMapWindow.UpdateMap()
  578. if self.show_target:
  579. # Target MapWindow:
  580. begin = (self.mapcoordlist[GCPNo][3], self.mapcoordlist[GCPNo][4])
  581. begin = self.TgtMapWindow.Cell2Pixel(begin)
  582. end = begin
  583. self.TgtMapWindow.Zoom(begin, end, 0)
  584. # redraw map
  585. self.TgtMapWindow.UpdateMap()
  586. self.statusbarWin['goto'].SetFocus()
  587. def StatusbarUpdate(self):
  588. """!Update statusbar content"""
  589. self.statusbarWin['region'].Hide()
  590. self.statusbarWin['resolution'].Hide()
  591. self.statusbarWin['mapscale'].Hide()
  592. self.statusbarWin['goto'].Hide()
  593. self.mapScaleValue = self.ppm = None
  594. if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
  595. self.statusbar.SetStatusText("", 0)
  596. # enable long help
  597. self.StatusbarEnableLongHelp()
  598. elif self.statusbarWin['toggle'].GetSelection() in (1, 2): # Extent
  599. sel = self.statusbarWin['toggle'].GetSelection()
  600. if sel == 1:
  601. region = self.Map.region
  602. else:
  603. region = self.Map.GetRegion() # computation region
  604. precision = int(UserSettings.Get(group = 'projection', key = 'format',
  605. subkey = 'precision'))
  606. format = UserSettings.Get(group = 'projection', key = 'format',
  607. subkey = 'll')
  608. if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
  609. w, s = utils.Deg2DMS(region["w"], region["s"],
  610. string = False, precision = precision)
  611. e, n = utils.Deg2DMS(region["e"], region["n"],
  612. string = False, precision = precision)
  613. if sel == 1:
  614. self.statusbar.SetStatusText("%s - %s, %s - %s" %
  615. (w, e, s, n), 0)
  616. else:
  617. ewres, nsres = utils.Deg2DMS(region['ewres'], region['nsres'],
  618. string = False, precision = precision)
  619. self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
  620. (w, e, s, n, ewres, nsres), 0)
  621. else:
  622. w, s = region["w"], region["s"]
  623. e, n = region["e"], region["n"]
  624. if sel == 1:
  625. self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
  626. (precision, w, precision, e,
  627. precision, s, precision, n), 0)
  628. else:
  629. ewres, nsres = region['ewres'], region['nsres']
  630. self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
  631. (precision, w, precision, e,
  632. precision, s, precision, n,
  633. precision, ewres, precision, nsres), 0)
  634. # enable long help
  635. self.StatusbarEnableLongHelp()
  636. elif self.statusbarWin['toggle'].GetSelection() == 3: # Show comp. extent
  637. self.statusbar.SetStatusText("", 0)
  638. self.statusbarWin['region'].Show()
  639. # disable long help
  640. self.StatusbarEnableLongHelp(False)
  641. elif self.statusbarWin['toggle'].GetSelection() == 4: # Display mode
  642. self.statusbar.SetStatusText("", 0)
  643. self.statusbarWin['resolution'].Show()
  644. # disable long help
  645. self.StatusbarEnableLongHelp(False)
  646. elif self.statusbarWin['toggle'].GetSelection() == 5: # Display geometry
  647. self.statusbar.SetStatusText("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
  648. (self.Map.region["rows"], self.Map.region["cols"],
  649. self.Map.region["nsres"], self.Map.region["ewres"]), 0)
  650. # enable long help
  651. self.StatusbarEnableLongHelp()
  652. elif self.statusbarWin['toggle'].GetSelection() == 6: # Map scale
  653. # TODO: need to be fixed...
  654. ### screen X region problem
  655. ### user should specify ppm
  656. dc = wx.ScreenDC()
  657. dpSizePx = wx.DisplaySize() # display size in pixels
  658. dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
  659. dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
  660. sysPpi = dc.GetPPI()
  661. comPpi = (dpSizePx[0] / dpSizeIn[0],
  662. dpSizePx[1] / dpSizeIn[1])
  663. ppi = comPpi # pixel per inch
  664. self.ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
  665. (ppi[1] / 2.54) * 100)
  666. Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): size: px=%d,%d mm=%f,%f "
  667. "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
  668. (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
  669. dpSizeIn[0], dpSizeIn[1],
  670. sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
  671. self.ppm[0], self.ppm[1]))
  672. region = self.Map.region
  673. heightCm = region['rows'] / self.ppm[1] * 100
  674. widthCm = region['cols'] / self.ppm[0] * 100
  675. Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): width_cm=%f, height_cm=%f" %
  676. (widthCm, heightCm))
  677. xscale = (region['e'] - region['w']) / (region['cols'] / self.ppm[0])
  678. yscale = (region['n'] - region['s']) / (region['rows'] / self.ppm[1])
  679. scale = (xscale + yscale) / 2.
  680. Debug.msg(3, "MapFrame.StatusbarUpdate(mapscale): xscale=%f, yscale=%f -> scale=%f" % \
  681. (xscale, yscale, scale))
  682. self.statusbar.SetStatusText("")
  683. try:
  684. self.statusbarWin['mapscale'].SetValue("1:%ld" % (scale + 0.5))
  685. except TypeError:
  686. pass
  687. self.mapScaleValue = scale
  688. self.statusbarWin['mapscale'].Show()
  689. # disable long help
  690. self.StatusbarEnableLongHelp(False)
  691. elif self.statusbarWin['toggle'].GetSelection() == 7: # go to
  692. self.statusbar.SetStatusText("")
  693. max = self.list.GetItemCount()
  694. if max < 1:
  695. max = 1
  696. self.statusbarWin['goto'].SetRange(0, max)
  697. self.statusbarWin['goto'].Show()
  698. # disable long help
  699. self.StatusbarEnableLongHelp(False)
  700. elif self.statusbarWin['toggle'].GetSelection() == 8: # RMS error
  701. self.statusbar.SetStatusText(_("Forward: %(forw)s, Backward: %(back)s") %
  702. { 'forw' : self.fwd_rmserror,
  703. 'back' : self.bkw_rmserror })
  704. # disable long help
  705. # self.StatusbarEnableLongHelp(False)
  706. else:
  707. self.statusbar.SetStatusText("", 1)
  708. def StatusbarEnableLongHelp(self, enable=True):
  709. """!Enable/disable toolbars long help"""
  710. for toolbar in self.toolbars.itervalues():
  711. if toolbar:
  712. toolbar.EnableLongHelp(enable)
  713. def StatusbarReposition(self):
  714. """!Reposition checkbox in statusbar"""
  715. # reposition checkbox
  716. widgets = [(0, self.statusbarWin['region']),
  717. (0, self.statusbarWin['resolution']),
  718. (0, self.statusbarWin['mapscale']),
  719. (0, self.statusbarWin['progress']),
  720. (0, self.statusbarWin['goto']),
  721. (1, self.statusbarWin['toggle']),
  722. (2, self.statusbarWin['mask']),
  723. (3, self.statusbarWin['render'])]
  724. for idx, win in widgets:
  725. rect = self.statusbar.GetFieldRect(idx)
  726. wWin, hWin = win.GetBestSize()
  727. if idx == 0: # show region / mapscale / process bar
  728. # -> size
  729. if win == self.statusbarWin['progress']:
  730. wWin = rect.width - 6
  731. # -> position
  732. # if win == self.statusbarWin['region']:
  733. # x, y = rect.x + rect.width - wWin, rect.y - 1
  734. # align left
  735. # else:
  736. x, y = rect.x + 3, rect.y - 1
  737. w, h = wWin, rect.height + 2
  738. else: # choice || auto-rendering
  739. x, y = rect.x, rect.y - 1
  740. w, h = rect.width, rect.height + 2
  741. if idx == 1: # choice
  742. h = hWin
  743. elif idx == 2: # mask
  744. x += 5
  745. y += 4
  746. elif idx == 3: # render
  747. x += 5
  748. win.SetPosition((x, y))
  749. win.SetSize((w, h))
  750. def SaveToFile(self, event):
  751. """!Save map to image
  752. """
  753. img = self.MapWindow.img
  754. if not img:
  755. gcmd.GMessage(parent = self,
  756. message = _("Nothing to render (empty map). Operation canceled."))
  757. return
  758. filetype, ltype = gdialogs.GetImageHandlers(img)
  759. # get size
  760. dlg = gdialogs.ImageSizeDialog(self)
  761. dlg.CentreOnParent()
  762. if dlg.ShowModal() != wx.ID_OK:
  763. dlg.Destroy()
  764. return
  765. width, height = dlg.GetValues()
  766. dlg.Destroy()
  767. # get filename
  768. dlg = wx.FileDialog(parent = self,
  769. message = _("Choose a file name to save the image "
  770. "(no need to add extension)"),
  771. wildcard = filetype,
  772. style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
  773. if dlg.ShowModal() == wx.ID_OK:
  774. path = dlg.GetPath()
  775. if not path:
  776. dlg.Destroy()
  777. return
  778. base, ext = os.path.splitext(path)
  779. fileType = ltype[dlg.GetFilterIndex()]['type']
  780. extType = ltype[dlg.GetFilterIndex()]['ext']
  781. if ext != extType:
  782. path = base + '.' + extType
  783. self.MapWindow.SaveToFile(path, fileType,
  784. width, height)
  785. dlg.Destroy()
  786. def PrintMenu(self, event):
  787. """
  788. Print options and output menu for map display
  789. """
  790. point = wx.GetMousePosition()
  791. printmenu = wx.Menu()
  792. # Add items to the menu
  793. setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
  794. printmenu.AppendItem(setup)
  795. self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
  796. preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
  797. printmenu.AppendItem(preview)
  798. self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
  799. doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
  800. printmenu.AppendItem(doprint)
  801. self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
  802. # Popup the menu. If an item is selected then its handler
  803. # will be called before PopupMenu returns.
  804. self.PopupMenu(printmenu)
  805. printmenu.Destroy()
  806. def GetRender(self):
  807. """!Returns current instance of render.Map()
  808. """
  809. return self.Map
  810. def GetWindow(self):
  811. """!Get map window"""
  812. return self.MapWindow
  813. def FormatDist(self, dist):
  814. """!Format length numbers and units in a nice way,
  815. as a function of length. From code by Hamish Bowman
  816. Grass Development Team 2006"""
  817. mapunits = self.Map.projinfo['units']
  818. if mapunits == 'metres': mapunits = 'meters'
  819. outunits = mapunits
  820. dist = float(dist)
  821. divisor = 1.0
  822. # figure out which units to use
  823. if mapunits == 'meters':
  824. if dist > 2500.0:
  825. outunits = 'km'
  826. divisor = 1000.0
  827. else: outunits = 'm'
  828. elif mapunits == 'feet':
  829. # nano-bug: we match any "feet", but US Survey feet is really
  830. # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
  831. # miles the tick markers are rounded to the nearest 10th of a
  832. # mile (528'), the difference in foot flavours is ignored.
  833. if dist > 5280.0:
  834. outunits = 'miles'
  835. divisor = 5280.0
  836. else:
  837. outunits = 'ft'
  838. elif 'degree' in mapunits:
  839. if dist < 1:
  840. outunits = 'min'
  841. divisor = (1/60.0)
  842. else:
  843. outunits = 'deg'
  844. # format numbers in a nice way
  845. if (dist/divisor) >= 2500.0:
  846. outdist = round(dist/divisor)
  847. elif (dist/divisor) >= 1000.0:
  848. outdist = round(dist/divisor,1)
  849. elif (dist/divisor) > 0.0:
  850. outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
  851. else:
  852. outdist = float(dist/divisor)
  853. return (outdist, outunits)
  854. def OnZoomToMap(self, event):
  855. """!
  856. Set display extents to match selected raster (including NULLs)
  857. or vector map.
  858. """
  859. self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
  860. def OnZoomToRaster(self, event):
  861. """!
  862. Set display extents to match selected raster map (ignore NULLs)
  863. """
  864. self.MapWindow.ZoomToMap(ignoreNulls = True)
  865. def OnZoomToWind(self, event):
  866. """!Set display geometry to match computational region
  867. settings (set with g.region)
  868. """
  869. self.MapWindow.ZoomToWind()
  870. def OnZoomToDefault(self, event):
  871. """!Set display geometry to match default region settings
  872. """
  873. self.MapWindow.ZoomToDefault()
  874. def OnZoomToSaved(self, event):
  875. """!Set display geometry to match extents in
  876. saved region file
  877. """
  878. self.MapWindow.ZoomToSaved()
  879. def OnDisplayToWind(self, event):
  880. """!Set computational region (WIND file) to match display
  881. extents
  882. """
  883. self.MapWindow.DisplayToWind()
  884. def SaveDisplayRegion(self, event):
  885. """!Save display extents to named region file.
  886. """
  887. self.MapWindow.SaveDisplayRegion()
  888. def OnZoomMenu(self, event):
  889. """!Popup Zoom menu
  890. """
  891. point = wx.GetMousePosition()
  892. zoommenu = wx.Menu()
  893. # Add items to the menu
  894. zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
  895. zoommenu.AppendItem(zoomwind)
  896. self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
  897. zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
  898. zoommenu.AppendItem(zoomdefault)
  899. self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
  900. zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
  901. zoommenu.AppendItem(zoomsaved)
  902. self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
  903. savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
  904. zoommenu.AppendItem(savewind)
  905. self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
  906. savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
  907. zoommenu.AppendItem(savezoom)
  908. self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
  909. # Popup the menu. If an item is selected then its handler
  910. # will be called before PopupMenu returns.
  911. self.PopupMenu(zoommenu)
  912. zoommenu.Destroy()
  913. def SetProperties(self, render=False, mode=0, showCompExtent=False,
  914. constrainRes=False, projection=False):
  915. """!Set properies of map display window"""
  916. self.statusbarWin['render'].SetValue(render)
  917. self.statusbarWin['toggle'].SetSelection(mode)
  918. self.StatusbarUpdate()
  919. self.statusbarWin['region'].SetValue(showCompExtent)
  920. self.statusbarWin['resolution'].SetValue(constrainRes)
  921. if showCompExtent:
  922. self.MapWindow.regionCoords = []
  923. def IsStandalone(self):
  924. """!Check if Map display is standalone"""
  925. if self._layerManager:
  926. return False
  927. return True
  928. def GetLayerManager(self):
  929. """!Get reference to Layer Manager
  930. @return window reference
  931. @return None (if standalone)
  932. """
  933. return self._layerManager
  934. # end of class MapFrame