frame.py 31 KB

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