gcpmapdisp.py 42 KB

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