frame.py 30 KB

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