gcpmapdisp.py 42 KB

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