gcpmapdisp.py 42 KB

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