gcpmapdisp.py 42 KB

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