gcpmapdisp.py 42 KB

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