frame.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. """
  2. @package mapswipe.frame
  3. @brief Map Swipe Frame
  4. Classes:
  5. - dialogs::SwipeMapDialog
  6. (C) 2012 by the GRASS Development Team
  7. This program is free software under the GNU General Public License
  8. (>=v2). Read the file COPYING that comes with GRASS for details.
  9. @author Anna Kratochvilova <kratochanna gmail.com>
  10. """
  11. import os
  12. import sys
  13. import wx
  14. import grass.script as grass
  15. from gui_core.mapdisp import DoubleMapFrame
  16. from gui_core.dialogs import GetImageHandlers
  17. from mapwin.base import MapWindowProperties
  18. from core import globalvar
  19. from core.render import Map
  20. from mapdisp import statusbar as sb
  21. from core.debug import Debug
  22. from core.gcmd import GError, GMessage
  23. from core.layerlist import LayerListToRendererConverter
  24. from gui_core.query import QueryDialog, PrepareQueryResults
  25. from mapswipe.toolbars import SwipeMapToolbar, SwipeMainToolbar, SwipeMiscToolbar
  26. from mapswipe.mapwindow import SwipeBufferedWindow
  27. from mapswipe.dialogs import SwipeMapDialog, PreferencesDialog
  28. class SwipeMapFrame(DoubleMapFrame):
  29. def __init__(self, parent=None, giface=None,
  30. title=_("GRASS GIS Map Swipe"), name="swipe", **kwargs):
  31. DoubleMapFrame.__init__(self, parent=parent, title=title, name=name,
  32. firstMap=Map(), secondMap=Map(), **kwargs)
  33. Debug.msg(1, "SwipeMapFrame.__init__()")
  34. #
  35. # Add toolbars
  36. #
  37. toolbars = ['swipeMisc', 'swipeMap', 'swipeMain']
  38. if sys.platform == 'win32':
  39. self.AddToolbar(toolbars.pop(1))
  40. toolbars.reverse()
  41. else:
  42. self.AddToolbar(toolbars.pop(0))
  43. for toolb in toolbars:
  44. self.AddToolbar(toolb)
  45. self._giface = giface
  46. #
  47. # create widgets
  48. #
  49. self.splitter = MapSplitter(parent=self, id=wx.ID_ANY)
  50. self.sliderH = wx.Slider(self, id=wx.ID_ANY, style=wx.SL_HORIZONTAL)
  51. self.sliderV = wx.Slider(self, id=wx.ID_ANY, style=wx.SL_VERTICAL)
  52. self.mapWindowProperties = MapWindowProperties()
  53. self.mapWindowProperties.setValuesFromUserSettings()
  54. self.mapWindowProperties.autoRenderChanged.connect(
  55. self.OnAutoRenderChanged)
  56. self.firstMapWindow = SwipeBufferedWindow(
  57. parent=self.splitter, giface=self._giface,
  58. properties=self.mapWindowProperties, Map=self.firstMap)
  59. self.secondMapWindow = SwipeBufferedWindow(
  60. parent=self.splitter, giface=self._giface,
  61. properties=self.mapWindowProperties, Map=self.secondMap)
  62. # bind query signal
  63. self.firstMapWindow.mapQueried.connect(self.Query)
  64. self.secondMapWindow.mapQueried.connect(self.Query)
  65. # bind tracking cursosr to mirror it
  66. self.firstMapWindow.Bind(
  67. wx.EVT_MOTION,
  68. lambda evt: self.TrackCursor(evt))
  69. self.secondMapWindow.Bind(
  70. wx.EVT_MOTION,
  71. lambda evt: self.TrackCursor(evt))
  72. self.MapWindow = self.firstMapWindow # current by default
  73. self.firstMapWindow.zoomhistory = self.secondMapWindow.zoomhistory
  74. self.SetBindRegions(True)
  75. self._mode = 'swipe'
  76. self._addPanes()
  77. self._bindWindowsActivation()
  78. self._setUpMapWindow(self.firstMapWindow)
  79. self._setUpMapWindow(self.secondMapWindow)
  80. self._mgr.GetPane('sliderV').Hide()
  81. self._mgr.GetPane('sliderH').Show()
  82. self.slider = self.sliderH
  83. self.InitStatusbar()
  84. self.Bind(wx.EVT_SIZE, self.OnSize)
  85. self.Bind(wx.EVT_IDLE, self.OnIdle)
  86. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  87. self.SetSize((800, 600))
  88. self._mgr.Update()
  89. self.rasters = {'first': None, 'second': None}
  90. self._inputDialog = None
  91. self._preferencesDialog = None
  92. self._queryDialog = None
  93. # default action in map toolbar
  94. self.GetMapToolbar().SelectDefault()
  95. self.resize = False
  96. wx.CallAfter(self.CallAfterInit)
  97. def TrackCursor(self, event):
  98. """Track cursor in one window and show cross in the other.
  99. Only for mirror mode.
  100. """
  101. if self._mode == 'swipe':
  102. event.Skip()
  103. return
  104. coords = event.GetPosition()
  105. if event.GetId() == self.secondMapWindow.GetId():
  106. self.firstMapWindow.DrawMouseCursor(coords=coords)
  107. else:
  108. self.secondMapWindow.DrawMouseCursor(coords=coords)
  109. event.Skip()
  110. def ActivateFirstMap(self, event=None):
  111. """Switch tracking direction"""
  112. super(SwipeMapFrame, self).ActivateFirstMap(event)
  113. self.firstMapWindow.ClearLines()
  114. self.firstMapWindow.Refresh()
  115. def ActivateSecondMap(self, event=None):
  116. """Switch tracking direction"""
  117. super(SwipeMapFrame, self).ActivateSecondMap(event)
  118. self.secondMapWindow.ClearLines()
  119. self.secondMapWindow.Refresh()
  120. def CallAfterInit(self):
  121. self.InitSliderBindings()
  122. self.splitter.SplitVertically(
  123. self.firstMapWindow, self.secondMapWindow, 0)
  124. self.splitter.Init()
  125. if not (self.rasters['first'] and self.rasters['second']):
  126. self.OnSelectLayers(event=None)
  127. def InitStatusbar(self):
  128. """Init statusbar (default items)."""
  129. # items for choice
  130. self.statusbarItems = [sb.SbCoordinates,
  131. sb.SbRegionExtent,
  132. sb.SbCompRegionExtent,
  133. sb.SbShowRegion,
  134. sb.SbAlignExtent,
  135. sb.SbResolution,
  136. sb.SbDisplayGeometry,
  137. sb.SbMapScale,
  138. sb.SbGoTo,
  139. sb.SbProjection]
  140. # create statusbar and its manager
  141. statusbar = self.CreateStatusBar(number=4, style=0)
  142. if globalvar.wxPython3:
  143. statusbar.SetMinHeight(24)
  144. statusbar.SetStatusWidths([-5, -2, -1, -1])
  145. self.statusbarManager = sb.SbManager(
  146. mapframe=self, statusbar=statusbar)
  147. # fill statusbar manager
  148. self.statusbarManager.AddStatusbarItemsByClass(
  149. self.statusbarItems, mapframe=self, statusbar=statusbar)
  150. self.statusbarManager.AddStatusbarItem(
  151. sb.SbMask(self, statusbar=statusbar, position=2))
  152. sbRender = sb.SbRender(self, statusbar=statusbar, position=3)
  153. self.statusbarManager.AddStatusbarItem(sbRender)
  154. self.statusbarManager.Update()
  155. def ResetSlider(self):
  156. if self.splitter.GetSplitMode() == wx.SPLIT_VERTICAL:
  157. size = self.splitter.GetSize()[0]
  158. else:
  159. size = self.splitter.GetSize()[1]
  160. self.slider.SetRange(0, size)
  161. self.slider.SetValue(self.splitter.GetSashPosition())
  162. def InitSliderBindings(self):
  163. self.sliderH.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  164. self.sliderH.Bind(
  165. wx.EVT_SCROLL_THUMBRELEASE,
  166. self.OnSliderPositionChanged)
  167. self.sliderV.Bind(wx.EVT_SPIN, self.OnSliderPositionChanging)
  168. self.sliderV.Bind(
  169. wx.EVT_SCROLL_THUMBRELEASE,
  170. self.OnSliderPositionChanged)
  171. self.splitter.Bind(
  172. wx.EVT_SPLITTER_SASH_POS_CHANGING,
  173. self.OnSashChanging)
  174. self.splitter.Bind(
  175. wx.EVT_SPLITTER_SASH_POS_CHANGED,
  176. self.OnSashChanged)
  177. def OnSliderPositionChanging(self, event):
  178. """Slider changes its position, sash must be moved too."""
  179. Debug.msg(5, "SwipeMapFrame.OnSliderPositionChanging()")
  180. self.GetFirstWindow().movingSash = True
  181. self.GetSecondWindow().movingSash = True
  182. pos = event.GetPosition()
  183. if pos > 0:
  184. self.splitter.SetSashPosition(pos)
  185. self.splitter.OnSashChanging(None)
  186. def OnSliderPositionChanged(self, event):
  187. """Slider position changed, sash must be moved too."""
  188. Debug.msg(5, "SwipeMapFrame.OnSliderPositionChanged()")
  189. self.splitter.SetSashPosition(event.GetPosition())
  190. self.splitter.OnSashChanged(None)
  191. def OnSashChanging(self, event):
  192. """Sash position is changing, slider must be moved too."""
  193. Debug.msg(5, "SwipeMapFrame.OnSashChanging()")
  194. self.slider.SetValue(self.splitter.GetSashPosition())
  195. event.Skip()
  196. def OnSashChanged(self, event):
  197. """Sash position changed, slider must be moved too."""
  198. Debug.msg(5, "SwipeMapFrame.OnSashChanged()")
  199. self.OnSashChanging(event)
  200. event.Skip()
  201. def OnSize(self, event):
  202. Debug.msg(4, "SwipeMapFrame.OnSize()")
  203. self.resize = grass.clock()
  204. super(SwipeMapFrame, self).OnSize(event)
  205. def OnIdle(self, event):
  206. if self.resize and grass.clock() - self.resize > 0.2:
  207. w1 = self.GetFirstWindow()
  208. w2 = self.GetSecondWindow()
  209. sizeAll = self.splitter.GetSize()
  210. w1.SetClientSize(sizeAll)
  211. w2.SetClientSize(sizeAll)
  212. w1.OnSize(event)
  213. w2.OnSize(event)
  214. self.ResetSlider()
  215. self.resize = False
  216. def OnAutoRenderChanged(self, value):
  217. """Auto rendering state changed."""
  218. style = self.splitter.GetWindowStyle()
  219. style ^= wx.SP_LIVE_UPDATE
  220. self.splitter.SetWindowStyle(style)
  221. def AddToolbar(self, name):
  222. """Add defined toolbar to the window
  223. Currently known toolbars are:
  224. - 'swipeMap' - basic map toolbar
  225. - 'swipeMain' - swipe functionality
  226. """
  227. if name == "swipeMap":
  228. self.toolbars[name] = SwipeMapToolbar(self, self._toolSwitcher)
  229. self._mgr.AddPane(self.toolbars[name],
  230. wx.aui.AuiPaneInfo().
  231. Name(name).Caption(_("Map Toolbar")).
  232. ToolbarPane().Top().
  233. LeftDockable(False).RightDockable(False).
  234. BottomDockable(False).TopDockable(True).
  235. CloseButton(False).Layer(2).Row(1).
  236. BestSize((self.toolbars[name].GetBestSize())))
  237. if name == "swipeMain":
  238. self.toolbars[name] = SwipeMainToolbar(self)
  239. self._mgr.AddPane(self.toolbars[name],
  240. wx.aui.AuiPaneInfo().
  241. Name(name).Caption(_("Main Toolbar")).
  242. ToolbarPane().Top().
  243. LeftDockable(False).RightDockable(False).
  244. BottomDockable(False).TopDockable(True).
  245. CloseButton(False).Layer(2).Row(1).
  246. BestSize((self.toolbars[name].GetBestSize())))
  247. if name == "swipeMisc":
  248. self.toolbars[name] = SwipeMiscToolbar(self)
  249. self._mgr.AddPane(self.toolbars[name],
  250. wx.aui.AuiPaneInfo().
  251. Name(name).Caption(_("Misc Toolbar")).
  252. ToolbarPane().Top().
  253. LeftDockable(False).RightDockable(False).
  254. BottomDockable(False).TopDockable(True).
  255. CloseButton(False).Layer(2).Row(1).
  256. BestSize((self.toolbars[name].GetBestSize())))
  257. def _addPanes(self):
  258. """Add splitter window and sliders to aui manager"""
  259. # splitter window
  260. self._mgr.AddPane(self.splitter, wx.aui.AuiPaneInfo().
  261. Name('splitter').CaptionVisible(False).PaneBorder(True).
  262. Dockable(False).Floatable(False).CloseButton(False).
  263. Center().Layer(1).BestSize((self.splitter.GetBestSize())))
  264. # sliders
  265. self._mgr.AddPane(self.sliderH, wx.aui.AuiPaneInfo().
  266. Name('sliderH').CaptionVisible(False).PaneBorder(False).
  267. CloseButton(False).Gripper(True).GripperTop(False).
  268. BottomDockable(True).TopDockable(True).
  269. LeftDockable(False).RightDockable(False).
  270. Bottom().Layer(1).BestSize((self.sliderH.GetBestSize())))
  271. self._mgr.AddPane(self.sliderV, wx.aui.AuiPaneInfo().
  272. Name('sliderV').CaptionVisible(False).PaneBorder(False).
  273. CloseButton(False).Gripper(True).GripperTop(True).
  274. BottomDockable(False).TopDockable(False).
  275. LeftDockable(True).RightDockable(True).
  276. Right().Layer(1).BestSize((self.sliderV.GetBestSize())))
  277. def ZoomToMap(self):
  278. """
  279. Set display extents to match selected raster (including NULLs)
  280. or vector map.
  281. """
  282. layers = []
  283. if self.rasters['first']:
  284. layers += self.firstMap.GetListOfLayers()
  285. if self.rasters['second']:
  286. layers += self.secondMap.GetListOfLayers()
  287. if layers:
  288. self.GetFirstWindow().ZoomToMap(layers=layers)
  289. self.GetSecondWindow().ZoomToMap(layers=layers)
  290. def OnZoomToMap(self, event):
  291. """Zoom to map"""
  292. self.ZoomToMap()
  293. def OnZoomBack(self, event):
  294. self.GetFirstWindow().ZoomBack()
  295. self.secondMap.region = self.firstMap.region
  296. self.Render(self.GetSecondWindow())
  297. def OnSelectLayers(self, event):
  298. if self._inputDialog is None:
  299. dlg = SwipeMapDialog(self, first=self.rasters['first'],
  300. second=self.rasters['second'],
  301. firstLayerList=None, secondLayerList=None)
  302. dlg.applyChanges.connect(self.OnApplyInputChanges)
  303. # connect to convertor object to convert to Map
  304. # store reference to convertor is needed otherwise it would be
  305. # discarded
  306. self._firstConverter = self._connectSimpleLmgr(
  307. dlg.GetFirstSimpleLmgr(), self.GetFirstMap())
  308. self._secondConverter = self._connectSimpleLmgr(
  309. dlg.GetSecondSimpleLmgr(), self.GetSecondMap())
  310. self._inputDialog = dlg
  311. dlg.CentreOnParent()
  312. dlg.Show()
  313. else:
  314. if self._inputDialog.IsShown():
  315. self._inputDialog.Raise()
  316. self._inputDialog.SetFocus()
  317. else:
  318. self._inputDialog.Show()
  319. def _connectSimpleLmgr(self, lmgr, renderer):
  320. converter = LayerListToRendererConverter(renderer)
  321. lmgr.opacityChanged.connect(converter.ChangeLayerOpacity)
  322. lmgr.cmdChanged.connect(converter.ChangeLayerCmd)
  323. lmgr.layerAdded.connect(converter.AddLayer)
  324. lmgr.layerRemoved.connect(converter.RemoveLayer)
  325. lmgr.layerActivated.connect(converter.ChangeLayerActive)
  326. lmgr.layerMovedUp.connect(converter.MoveLayerUp)
  327. lmgr.layerMovedDown.connect(converter.MoveLayerDown)
  328. lmgr.anyChange.connect(self._simpleLmgrChanged)
  329. return converter
  330. def _simpleLmgrChanged(self):
  331. if self.IsAutoRendered():
  332. self.OnRender(event=None)
  333. def OnApplyInputChanges(self):
  334. first, second = self._inputDialog.GetValues()
  335. if self._inputDialog.IsSimpleMode():
  336. self.rasters['first'], self.rasters['second'] = first, second
  337. res1 = self.SetFirstRaster(name=self.rasters['first'])
  338. res2 = self.SetSecondRaster(name=self.rasters['second'])
  339. if not (res1 and res2) and first and second:
  340. message = ''
  341. if not res1:
  342. message += _("Map <%s> not found. ") % self.rasters[
  343. 'first']
  344. if not res2:
  345. message += _("Map <%s> not found.") % self.rasters[
  346. 'second']
  347. GError(parent=self, message=message)
  348. return
  349. self.ZoomToMap()
  350. else:
  351. LayerListToRendererConverter(self.GetFirstMap()).ConvertAll(first)
  352. LayerListToRendererConverter(
  353. self.GetSecondMap()).ConvertAll(second)
  354. self.SetRasterNames()
  355. if self.IsAutoRendered():
  356. self.OnRender(event=None)
  357. def SetFirstRaster(self, name):
  358. """Set raster map to first Map"""
  359. raster = grass.find_file(name=name, element='cell')
  360. if raster['fullname']:
  361. self.rasters['first'] = raster['fullname']
  362. self.SetLayer(
  363. name=raster['fullname'],
  364. mapInstance=self.GetFirstMap())
  365. return True
  366. return False
  367. def SetSecondRaster(self, name):
  368. """Set raster map to second Map"""
  369. raster = grass.find_file(name=name, element='cell')
  370. if raster['fullname']:
  371. self.rasters['second'] = raster['fullname']
  372. self.SetLayer(
  373. name=raster['fullname'],
  374. mapInstance=self.GetSecondMap())
  375. return True
  376. return False
  377. def SetLayer(self, name, mapInstance):
  378. """Sets layer in Map.
  379. :param name: layer (raster) name
  380. """
  381. Debug.msg(3, "SwipeMapFrame.SetLayer(): name=%s" % name)
  382. # this simple application enables to keep only one raster
  383. mapInstance.DeleteAllLayers()
  384. cmdlist = ['d.rast', 'map=%s' % name]
  385. # add layer to Map instance (core.render)
  386. newLayer = mapInstance.AddLayer(
  387. ltype='raster',
  388. command=cmdlist,
  389. active=True,
  390. name=name,
  391. hidden=False,
  392. opacity=1.0,
  393. render=True)
  394. def OnSwitchWindows(self, event):
  395. """Switch windows position."""
  396. Debug.msg(3, "SwipeMapFrame.OnSwitchWindows()")
  397. splitter = self.splitter
  398. w1, w2 = splitter.GetWindow1(), splitter.GetWindow2()
  399. splitter.ReplaceWindow(w1, w2)
  400. splitter.ReplaceWindow(w2, w1)
  401. # self.OnSize(None)
  402. splitter.OnSashChanged(None)
  403. def _saveToFile(self, fileName, fileType):
  404. """Creates composite image by rendering both images and
  405. pasting them into the new one.
  406. .. todo::
  407. specify size of the new image (problem is inaccurate scaling)
  408. .. todo::
  409. make dividing line width and color optional
  410. """
  411. w1 = self.splitter.GetWindow1()
  412. w2 = self.splitter.GetWindow2()
  413. lineWidth = 1
  414. # render to temporary files
  415. filename1 = grass.tempfile(False) + '1'
  416. filename2 = grass.tempfile(False) + '2'
  417. width, height = self.splitter.GetClientSize()
  418. if self._mode == 'swipe':
  419. x, y = w2.GetImageCoords()
  420. w1.SaveToFile(filename1, fileType, width, height)
  421. w2.SaveToFile(filename2, fileType, width, height)
  422. else:
  423. fw, fh = w1.GetClientSize()
  424. w1.SaveToFile(filename1, fileType, fw, fh)
  425. sw, sh = w2.GetClientSize()
  426. w2.SaveToFile(filename2, fileType, sw, sh)
  427. # create empty white image - needed for line
  428. im = wx.EmptyImage(width, height)
  429. im.Replace(0, 0, 0, 255, 255, 255)
  430. # paste images
  431. if self._mode == 'swipe':
  432. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  433. im1 = wx.Image(filename1).GetSubImage((0, 0, width, -y))
  434. im.Paste(im1, 0, 0)
  435. im.Paste(wx.Image(filename2), -x, -y + lineWidth)
  436. else:
  437. im1 = wx.Image(filename1).GetSubImage((0, 0, -x, height))
  438. im.Paste(im1, 0, 0)
  439. im.Paste(wx.Image(filename2), -x + lineWidth, -y)
  440. else:
  441. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  442. im1 = wx.Image(filename1)
  443. im.Paste(im1, 0, 0)
  444. im.Paste(wx.Image(filename2), 0, fh + lineWidth)
  445. else:
  446. im1 = wx.Image(filename1)
  447. im.Paste(im1, 0, 0)
  448. im.Paste(wx.Image(filename2), fw + lineWidth, 0)
  449. im.SaveFile(fileName, fileType)
  450. # remove temporary files
  451. grass.try_remove(filename1)
  452. grass.try_remove(filename2)
  453. def SaveToFile(self, event):
  454. """Save map to image
  455. """
  456. img = self.firstMapWindow.img or self.secondMapWindow.img
  457. if not img:
  458. GMessage(parent=self, message=_(
  459. "Nothing to render (empty map). Operation canceled."))
  460. return
  461. filetype, ltype = GetImageHandlers(img)
  462. # get filename
  463. dlg = wx.FileDialog(parent=self,
  464. message=_("Choose a file name to save the image "
  465. "(no need to add extension)"),
  466. wildcard=filetype,
  467. style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
  468. if dlg.ShowModal() == wx.ID_OK:
  469. path = dlg.GetPath()
  470. if not path:
  471. dlg.Destroy()
  472. return
  473. base, ext = os.path.splitext(path)
  474. fileType = ltype[dlg.GetFilterIndex()]['type']
  475. extType = ltype[dlg.GetFilterIndex()]['ext']
  476. if ext != extType:
  477. path = base + '.' + extType
  478. self._saveToFile(path, fileType)
  479. dlg.Destroy()
  480. def OnSwitchOrientation(self, event):
  481. """Switch orientation of the sash."""
  482. Debug.msg(3, "SwipeMapFrame.OnSwitchOrientation()")
  483. splitter = self.splitter
  484. splitter.Unsplit()
  485. if splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  486. splitter.SplitVertically(
  487. self.firstMapWindow, self.secondMapWindow, 0)
  488. self.slider = self.sliderH
  489. if self._mode == 'swipe':
  490. self._mgr.GetPane('sliderH').Show()
  491. self._mgr.GetPane('sliderV').Hide()
  492. else:
  493. splitter.SplitHorizontally(
  494. self.firstMapWindow, self.secondMapWindow, 0)
  495. self.slider = self.sliderV
  496. if self._mode == 'swipe':
  497. self._mgr.GetPane('sliderV').Show()
  498. self._mgr.GetPane('sliderH').Hide()
  499. self._mgr.Update()
  500. splitter.OnSashChanged(None)
  501. self.OnSize(None)
  502. self.SetRasterNames()
  503. def OnAddText(self, event):
  504. """Double click on text overlay
  505. So far not implemented.
  506. """
  507. pass
  508. def SetViewMode(self, mode):
  509. """Sets view mode.
  510. :param mode: view mode ('swipe', 'mirror')
  511. """
  512. if self._mode == mode:
  513. return
  514. self._mode = mode
  515. self.toolbars['swipeMain'].SetMode(mode)
  516. # set window mode
  517. self.GetFirstWindow().SetMode(mode)
  518. self.GetSecondWindow().SetMode(mode)
  519. # hide/show slider
  520. if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL:
  521. self._mgr.GetPane('sliderV').Show(mode == 'swipe')
  522. size = self.splitter.GetSize()[1] / 2
  523. else:
  524. self._mgr.GetPane('sliderH').Show(mode == 'swipe')
  525. size = self.splitter.GetSize()[0] / 2
  526. # set sash in the middle
  527. self.splitter.SetSashPosition(size)
  528. self.slider.SetValue(size)
  529. self._mgr.Update()
  530. # enable / disable sash
  531. self.splitter.EnableSash(mode == 'swipe')
  532. # hack to make it work
  533. self.splitter.OnSashChanged(None)
  534. self.SendSizeEvent()
  535. def SetRasterNames(self):
  536. if not self._inputDialog or self._inputDialog.IsSimpleMode():
  537. if self.rasters['first']:
  538. self.GetFirstWindow().SetRasterNameText(
  539. self.rasters['first'], 101)
  540. if self.rasters['second']:
  541. self.GetSecondWindow().SetRasterNameText(
  542. self.rasters['second'], 102)
  543. else:
  544. self.GetFirstWindow().SetRasterNameText('', 101)
  545. self.GetSecondWindow().SetRasterNameText('', 102)
  546. def Query(self, x, y):
  547. """Query active layers from both mapwindows.
  548. :param x,y: coordinates
  549. """
  550. rasters = (
  551. [layer.GetName()
  552. for layer in self.GetFirstMap().GetListOfLayers(
  553. ltype='raster', active=True)],
  554. [layer.GetName()
  555. for layer in self.GetSecondMap().GetListOfLayers(
  556. ltype='raster', active=True)])
  557. vectors = (
  558. [layer.GetName()
  559. for layer in self.GetFirstMap().GetListOfLayers(
  560. ltype='vector', active=True)],
  561. [layer.GetName()
  562. for layer in self.GetSecondMap().GetListOfLayers(
  563. ltype='vector', active=True)])
  564. if not (rasters[0] + rasters[1] + vectors[0] + vectors[1]):
  565. GMessage(
  566. parent=self,
  567. message=_(
  568. 'No raster or vector map layer selected for querying.'))
  569. return
  570. # set query snap distance for v.what at map unit equivalent of 10
  571. # pixels
  572. qdist = 10.0 * (
  573. (self.GetFirstMap().region['e'] - self.GetFirstMap().region['w']) /
  574. self.GetFirstMap().width)
  575. east, north = self.GetFirstWindow().Pixel2Cell((x, y))
  576. # use display region settings instead of computation region settings
  577. self.tmpreg = os.getenv("GRASS_REGION")
  578. os.environ["GRASS_REGION"] = self.GetFirstMap(
  579. ).SetRegion(windres=False)
  580. result = []
  581. if rasters[0]:
  582. result.extend(
  583. grass.raster_what(
  584. map=rasters[0],
  585. coord=(east, north),
  586. localized=True))
  587. if vectors[0]:
  588. result.extend(
  589. grass.vector_what(
  590. map=vectors[0],
  591. coord=(east, north),
  592. distance=qdist))
  593. if rasters[1]:
  594. result.extend(
  595. grass.raster_what(
  596. map=rasters[1],
  597. coord=(east, north),
  598. localized=True))
  599. if vectors[1]:
  600. result.extend(
  601. grass.vector_what(
  602. map=vectors[1],
  603. coord=(east, north),
  604. distance=qdist))
  605. self._QueryMapDone()
  606. result = PrepareQueryResults(coordinates=(east, north), result=result)
  607. if self._queryDialog:
  608. self._queryDialog.Raise()
  609. self._queryDialog.SetData(result)
  610. else:
  611. self._queryDialog = QueryDialog(parent=self, data=result)
  612. self._queryDialog.Bind(wx.EVT_CLOSE, self._oncloseQueryDialog)
  613. self._queryDialog.redirectOutput.connect(
  614. lambda output: self._giface.WriteLog(output))
  615. self._queryDialog.Show()
  616. def _oncloseQueryDialog(self, event):
  617. self._queryDialog = None
  618. event.Skip()
  619. def _QueryMapDone(self):
  620. """Restore settings after querying (restore GRASS_REGION)
  621. """
  622. if hasattr(self, "tmpreg"):
  623. if self.tmpreg:
  624. os.environ["GRASS_REGION"] = self.tmpreg
  625. elif 'GRASS_REGION' in os.environ:
  626. del os.environ["GRASS_REGION"]
  627. elif 'GRASS_REGION' in os.environ:
  628. del os.environ["GRASS_REGION"]
  629. if hasattr(self, "tmpreg"):
  630. del self.tmpreg
  631. def GetMapToolbar(self):
  632. """Returns toolbar with zooming tools"""
  633. return self.toolbars['swipeMap']
  634. def IsStandalone(self):
  635. """Since we do not need layer manager, we are standalone"""
  636. return True
  637. def OnHelp(self, event):
  638. self._giface.Help(entry='wxGUI.mapswipe')
  639. def OnPreferences(self, event):
  640. if not self._preferencesDialog:
  641. dlg = PreferencesDialog(parent=self, giface=self._giface)
  642. self._preferencesDialog = dlg
  643. self._preferencesDialog.CenterOnParent()
  644. self._preferencesDialog.ShowModal()
  645. def OnCloseWindow(self, event):
  646. self.GetFirstMap().Clean()
  647. self.GetSecondMap().Clean()
  648. self._mgr.UnInit()
  649. self._inputDialog.UnInit()
  650. self.Destroy()
  651. class MapSplitter(wx.SplitterWindow):
  652. """Splitter window for displaying two maps"""
  653. def __init__(self, parent, id):
  654. wx.SplitterWindow.__init__(self, parent=parent, id=id,
  655. style=wx.SP_LIVE_UPDATE
  656. )
  657. Debug.msg(2, "MapSplitter.__init__()")
  658. self.sashWidthMin = 1
  659. self.sashWidthMax = 10
  660. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
  661. self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
  662. self._moveSash = True
  663. def EnableSash(self, enable):
  664. self._moveSash = enable
  665. def Init(self):
  666. self.OnSashChanged(evt=None)
  667. self.SetMinimumPaneSize(0)
  668. self.SetSashSize(self.sashWidthMin)
  669. # def OnMotion(self, event):
  670. # w = self.GetSashSize()
  671. # w1, w2 = self.GetWindow1(), self.GetWindow2()
  672. # if self.SashHitTest(event.GetX(), event.GetY(), tolerance = 20):
  673. # if w == self.sashWidthMin:
  674. # self.SetSashSize(self.sashWidthMax)
  675. # self.SetNeedUpdating(True)
  676. # w1.movingSash = True
  677. # w2.movingSash = True
  678. # else:
  679. # w1.movingSash = False
  680. # w1.movingSash = False
  681. # else:
  682. # if w == self.sashWidthMax:
  683. # self.SetSashSize(self.sashWidthMin)
  684. # self.SetNeedUpdating(True)
  685. # w1.movingSash = True
  686. # w2.movingSash = True
  687. # else:
  688. # w1.movingSash = False
  689. # w2.movingSash = False
  690. # event.Skip()
  691. def OnSashChanged(self, evt):
  692. Debug.msg(5, "MapSplitter.OnSashChanged()")
  693. if not self._moveSash:
  694. return
  695. w1, w2 = self.GetWindow1(), self.GetWindow2()
  696. w1.movingSash = False
  697. w2.movingSash = False
  698. wx.CallAfter(self.SashChanged)
  699. def SashChanged(self):
  700. Debug.msg(5, "MapSplitter.SashChanged()")
  701. w1, w2 = self.GetWindow1(), self.GetWindow2()
  702. w1.SetImageCoords((0, 0))
  703. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  704. w = w1.GetSize()[0]
  705. w2.SetImageCoords((-w, 0))
  706. else:
  707. h = w1.GetSize()[1]
  708. w2.SetImageCoords((0, -h))
  709. w1.UpdateMap(render=False, renderVector=False)
  710. w2.UpdateMap(render=False, renderVector=False)
  711. pos = self.GetSashPosition()
  712. self.last = pos
  713. def OnSashChanging(self, event):
  714. Debug.msg(5, "MapSplitter.OnSashChanging()")
  715. if not self._moveSash:
  716. event.SetSashPosition(-1)
  717. return
  718. if not (self.GetWindowStyle() & wx.SP_LIVE_UPDATE):
  719. if event:
  720. event.Skip()
  721. return
  722. pos = self.GetSashPosition()
  723. dpos = pos - self.last
  724. self.last = pos
  725. if self.GetSplitMode() == wx.SPLIT_VERTICAL:
  726. dx = -dpos
  727. dy = 0
  728. else:
  729. dx = 0
  730. dy = -dpos
  731. self.GetWindow2().TranslateImage(dx, dy)
  732. self.GetWindow1().movingSash = True
  733. self.GetWindow2().movingSash = True