frame.py 31 KB

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