frame.py 30 KB

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