sampling_frame.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. # -*- coding: utf-8 -*-
  2. """!
  3. @package rlisetup.sampling_frame
  4. @brief r.li.setup - draw sample frame
  5. Classes:
  6. - sampling_frame::RLiSetupMapPanel
  7. - sampling_frame::RLiSetupToolbar
  8. - sampling_frame::GraphicsSetItem
  9. (C) 2013 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Anna Petrasova <kratochanna gmail.com>
  13. """
  14. import os
  15. import sys
  16. import wx
  17. import wx.aui
  18. #start new import
  19. import tempfile
  20. from core.gcmd import RunCommand
  21. import grass.script.core as grass
  22. from core import gcmd
  23. try:
  24. from grass.lib.gis import *
  25. from grass.lib.vector import *
  26. from grass.lib.raster import *
  27. except ImportError:
  28. pass
  29. #end new import
  30. # adding a path to wxGUI modules
  31. if __name__ == '__main__':
  32. WXGUIBASE = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
  33. if WXGUIBASE not in sys.path:
  34. sys.path.append(WXGUIBASE)
  35. from core.utils import _
  36. from core.giface import StandaloneGrassInterface
  37. from mapwin.base import MapWindowProperties
  38. from mapwin.buffered import BufferedMapWindow
  39. from core.render import Map
  40. from gui_core.toolbars import BaseToolbar, BaseIcons, ToolSwitcher
  41. from icons.icon import MetaIcon
  42. from grass.pydispatch.signal import Signal
  43. from grass.pydispatch.errors import DispatcherKeyError
  44. from functions import SamplingType
  45. class Circle:
  46. def __init__(self, pt, r):
  47. self.point = pt
  48. self.radius = r
  49. class MaskedArea(object):
  50. def __init__(self, region, raster, radius):
  51. self.region = region
  52. self.raster = raster
  53. self.radius = radius
  54. class RLiSetupMapPanel(wx.Panel):
  55. """!Panel with mapwindow used in r.li.setup"""
  56. def __init__(self, parent, samplingType, icon=None, map_=None):
  57. wx.Panel.__init__(self, parent=parent)
  58. self.mapWindowProperties = MapWindowProperties()
  59. self.mapWindowProperties.setValuesFromUserSettings()
  60. giface = StandaloneGrassInterface()
  61. self.samplingtype = samplingType
  62. self.parent = parent
  63. if map_:
  64. self.map_ = map_
  65. else:
  66. self.map_ = Map()
  67. self.map_.region = self.map_.GetRegion()
  68. self._mgr = wx.aui.AuiManager(self)
  69. self.mapWindow = BufferedMapWindow(parent=self, giface=giface,
  70. Map=self.map_,
  71. properties=self.mapWindowProperties)
  72. self._mgr.AddPane(self.mapWindow, wx.aui.AuiPaneInfo().CentrePane().
  73. Dockable(True).BestSize((-1, -1)).Name('mapwindow').
  74. CloseButton(False).DestroyOnClose(True).
  75. Layer(0))
  76. self._toolSwitcher = ToolSwitcher()
  77. self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged)
  78. self.toolbar = RLiSetupToolbar(self, self._toolSwitcher)
  79. self.catId = 1
  80. self._mgr.AddPane(self.toolbar,
  81. wx.aui.AuiPaneInfo().
  82. Name("maptoolbar").Caption(_("Map Toolbar")).
  83. ToolbarPane().Left().Name('mapToolbar').
  84. CloseButton(False).Layer(1).Gripper(False).
  85. BestSize((self.toolbar.GetBestSize())))
  86. self._mgr.Update()
  87. if self.samplingtype == SamplingType.REGIONS:
  88. self.afterRegionDrawn = Signal('RLiSetupMapPanel.afterRegionDrawn')
  89. self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line')
  90. elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
  91. self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged')
  92. self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle')
  93. elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]:
  94. self.afterCircleDrawn = Signal('RLiSetupMapPanel.afterCircleDrawn')
  95. self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line')
  96. else:
  97. self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged')
  98. self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle')
  99. self._registeredGraphics.AddPen('rlisetup', wx.Pen(wx.GREEN, width=2,
  100. style=wx.SOLID))
  101. self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]],
  102. penName='rlisetup', hide=True)
  103. if self.samplingtype != SamplingType.VECT:
  104. self.toolbar.SelectDefault()
  105. def GetMap(self):
  106. return self.map_
  107. def OnPan(self, event):
  108. """!Panning, set mouse to drag."""
  109. self.mapWindow.SetModePan()
  110. def OnZoomIn(self, event):
  111. """!Zoom in the map."""
  112. self.mapWindow.SetModeZoomIn()
  113. def OnZoomOut(self, event):
  114. """!Zoom out the map."""
  115. self.mapWindow.SetModeZoomOut()
  116. def OnZoomToMap(self, event):
  117. layers = self.map_.GetListOfLayers()
  118. self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
  119. def OnDrawRadius(self, event):
  120. """!Start draw mode"""
  121. self.mapWindow.mouse['use'] = "None"
  122. self.mapWindow.mouse['box'] = "line"
  123. self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
  124. style=wx.SHORT_DASH)
  125. self.mapWindow.SetNamedCursor('cross')
  126. self.mapWindow.mouseLeftUp.connect(self._radiusDrawn)
  127. def OnDigitizeRegion(self, event):
  128. """!Start draw mode"""
  129. self.mapWindow.mouse['use'] = "None"
  130. self.mapWindow.mouse['box'] = "line"
  131. self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
  132. style=wx.SHORT_DASH)
  133. self.mapWindow.SetNamedCursor('cross')
  134. self.mapWindow.mouseLeftUp.connect(self._lineSegmentDrawn)
  135. self.mapWindow.mouseDClick.connect(self._mouseDbClick)
  136. self._registeredGraphics.GetItem(0).SetCoords([])
  137. def OnDraw(self, event):
  138. """!Start draw mode"""
  139. self.mapWindow.mouse['use'] = "None"
  140. self.mapWindow.mouse['box'] = "box"
  141. self.mapWindow.pen = wx.Pen(colour=wx.RED, width=2,
  142. style=wx.SHORT_DASH)
  143. self.mapWindow.SetNamedCursor('cross')
  144. self.mapWindow.mouseLeftUp.connect(self._rectangleDrawn)
  145. def _lineSegmentDrawn(self, x, y):
  146. item = self._registeredGraphics.GetItem(0)
  147. coords = item.GetCoords()
  148. if len(coords) == 0:
  149. coords.extend([self.mapWindow.Pixel2Cell(self.mapWindow.mouse['begin'])])
  150. coords.extend([[x, y]])
  151. item.SetCoords(coords)
  152. item.SetPropertyVal('hide', False)
  153. self.mapWindow.ClearLines()
  154. self._registeredGraphics.Draw(self.mapWindow.pdcTmp)
  155. def _mouseDbClick(self, x, y):
  156. item = self._registeredGraphics.GetItem(0)
  157. coords = item.GetCoords()
  158. coords.extend([[x, y]])
  159. item.SetCoords(coords)
  160. item.SetPropertyVal('hide', False)
  161. self.mapWindow.ClearLines()
  162. self._registeredGraphics.Draw(self.mapWindow.pdc)
  163. self.createRegion()
  164. def createRegion(self):
  165. dlg = wx.TextEntryDialog(None, 'Name of sample region',
  166. 'Create region', 'region' + str(self.catId))
  167. ret = dlg.ShowModal()
  168. if ret == wx.ID_OK:
  169. raster = dlg.GetValue()
  170. marea = self.writeArea(self._registeredGraphics.GetItem(0).GetCoords(), raster)
  171. self.nextRegion(next=True, area=marea)
  172. else:
  173. self.nextRegion(next=False)
  174. dlg.Destroy()
  175. def nextRegion(self, next=True, area=None):
  176. self.mapWindow.ClearLines()
  177. item = self._registeredGraphics.GetItem(0)
  178. item.SetCoords([])
  179. item.SetPropertyVal('hide', True)
  180. layers = self.map_.GetListOfLayers()
  181. self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
  182. if next is True:
  183. self.afterRegionDrawn.emit(marea=area)
  184. else:
  185. gcmd.GMessage(parent=self.parent,
  186. message=_("Raster map not created. Please redraw region."))
  187. def writeArea(self, coords, rasterName):
  188. polyfile = tempfile.NamedTemporaryFile(delete=False)
  189. polyfile.write("AREA\n")
  190. for coor in coords:
  191. east, north = coor
  192. point = " %s %s\n" % (east, north)
  193. polyfile.write(point)
  194. catbuf = "=%d a\n" % self.catId
  195. polyfile.write(catbuf)
  196. self.catId = self.catId + 1
  197. polyfile.close()
  198. region_settings = grass.parse_command('g.region', flags='p',
  199. delimiter=':')
  200. pname = polyfile.name.split('/')[-1]
  201. tmpraster = "rast_" + pname
  202. tmpvector = "vect_" + pname
  203. wx.BeginBusyCursor()
  204. wx.Yield()
  205. RunCommand('r.in.poly', input=polyfile.name, output=tmpraster,
  206. rows=region_settings['rows'], overwrite=True)
  207. RunCommand('r.to.vect', input=tmpraster, output=tmpvector,
  208. type='area', overwrite=True)
  209. RunCommand('v.to.rast', input=tmpvector, output=rasterName,
  210. value=1, use='val', overwrite=True)
  211. wx.EndBusyCursor()
  212. grass.use_temp_region()
  213. grass.run_command('g.region', vect=tmpvector)
  214. region = grass.region()
  215. marea = MaskedArea(region, rasterName)
  216. RunCommand('g.remove', rast=tmpraster)
  217. RunCommand('g.remove', vect=tmpvector)
  218. os.unlink(polyfile.name)
  219. return marea
  220. def _onToolChanged(self):
  221. """!Helper function to disconnect drawing"""
  222. try:
  223. self.mapWindow.mouseLeftUp.disconnect(self._rectangleDrawn)
  224. self.mapWindow.mouseLeftUp.disconnect(self._radiusDrawn)
  225. self.mapWindow.mouseMoving.disconnect(self._mouseMoving)
  226. self.mapWindow.mouseLeftDown.disconnect(self._mouseLeftDown)
  227. self.mapWindow.mouseDClick.disconnect(self._mouseDbClick)
  228. except DispatcherKeyError:
  229. pass
  230. def _radiusDrawn(self, x, y):
  231. """!When drawing finished, get region values"""
  232. mouse = self.mapWindow.mouse
  233. item = self._registeredGraphics.GetItem(0)
  234. p1 = mouse['begin']
  235. p2 = mouse['end']
  236. dist, (north, east) = self.mapWindow.Distance(p1, p2, False)
  237. circle = Circle(p1, dist)
  238. self.mapWindow.ClearLines()
  239. self.mapWindow.pdcTmp.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  240. pen = wx.Pen(colour=wx.RED, width=2)
  241. self.mapWindow.pdcTmp.SetPen(pen)
  242. self.mapWindow.pdcTmp.DrawCircle(circle.point[0], circle.point[1],
  243. circle.radius)
  244. self._registeredGraphics.Draw(self.mapWindow.pdcTmp)
  245. self.createCricle(circle)
  246. def createCricle(self, c):
  247. dlg = wx.TextEntryDialog(None, 'Name of sample circle region',
  248. 'Create circle region', 'circle' + str(self.catId))
  249. ret = dlg.ShowModal()
  250. if ret == wx.ID_OK:
  251. raster = dlg.GetValue()
  252. circle = self.writeCircle(c, raster)
  253. self.nextCircle(next=True, circle=circle)
  254. else:
  255. self.nextCircle(next=False)
  256. dlg.Destroy()
  257. def nextCircle(self, next=True, circle=None):
  258. self.mapWindow.ClearLines()
  259. item = self._registeredGraphics.GetItem(0)
  260. item.SetPropertyVal('hide', True)
  261. layers = self.map_.GetListOfLayers()
  262. self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
  263. if next is True:
  264. self.afterCircleDrawn.emit(region=circle)
  265. else:
  266. gcmd.GMessage(parent=self.parent,
  267. message=_("Raster map not created. redraw region again."))
  268. def writeCircle(self, circle, rasterName):
  269. coords = self.mapWindow.Pixel2Cell(circle.point)
  270. RunCommand('r.circle', output=rasterName, max=circle.radius,
  271. coordinate=coords, flags="b")
  272. grass.use_temp_region()
  273. grass.run_command('g.region', zoom=rasterName)
  274. region = grass.region()
  275. marea = MaskedArea(region, rasterName, circle.radius)
  276. return marea
  277. def _rectangleDrawn(self):
  278. """!When drawing finished, get region values"""
  279. mouse = self.mapWindow.mouse
  280. item = self._registeredGraphics.GetItem(0)
  281. p1 = self.mapWindow.Pixel2Cell(mouse['begin'])
  282. p2 = self.mapWindow.Pixel2Cell(mouse['end'])
  283. item.SetCoords([p1, p2])
  284. region = {'n': max(p1[1], p2[1]),
  285. 's': min(p1[1], p2[1]),
  286. 'w': min(p1[0], p2[0]),
  287. 'e': max(p1[0], p2[0])}
  288. item.SetPropertyVal('hide', False)
  289. self.mapWindow.ClearLines()
  290. self._registeredGraphics.Draw(self.mapWindow.pdcTmp)
  291. if self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
  292. dlg = wx.MessageDialog(self, "Is this area ok?",
  293. "select sampling unit",
  294. wx.YES_NO | wx.ICON_QUESTION)
  295. ret = dlg.ShowModal()
  296. if ret == wx.ID_YES:
  297. grass.use_temp_region()
  298. grass.run_command('g.region', n=region['n'], s=region['s'],
  299. e=region['e'], w=region['w'])
  300. tregion = grass.region()
  301. self.sampleFrameChanged.emit(region=tregion)
  302. self.mapWindow.ClearLines()
  303. item = self._registeredGraphics.GetItem(0)
  304. item.SetPropertyVal('hide', True)
  305. layers = self.map_.GetListOfLayers()
  306. self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False,
  307. render=True)
  308. else:
  309. self.nextRegion(next=False)
  310. dlg.Destroy()
  311. elif self.samplingtype != SamplingType.WHOLE:
  312. """!When drawing finished, get region values"""
  313. self.sampleFrameChanged.emit(region=region)
  314. icons = {'draw': MetaIcon(img='edit',
  315. label=_('Draw sampling frame'),
  316. desc=_('Draw sampling frame by clicking and dragging')),
  317. 'digitizeunit': MetaIcon(img='edit',
  318. label=_('Draw sampling rectangle'),
  319. desc=_('Draw sampling rectangle by clicking and dragging')),
  320. 'digitizeunitc': MetaIcon(img='line-create',
  321. label=_('Draw sampling circle'),
  322. desc=_('Draw sampling circle radius by clicking and dragging')),
  323. 'digitizeregion': MetaIcon(img='polygon-create',
  324. label=_('Draw sampling region'),
  325. desc=_('Draw sampling region by polygon. Right Double click to end drawing'))}
  326. class RLiSetupToolbar(BaseToolbar):
  327. """!IClass toolbar
  328. """
  329. def __init__(self, parent, toolSwitcher):
  330. """!RLiSetup toolbar constructor
  331. """
  332. BaseToolbar.__init__(self, parent, toolSwitcher,
  333. style=wx.NO_BORDER | wx.TB_VERTICAL)
  334. self.InitToolbar(self._toolbarData())
  335. if self.parent.samplingtype == SamplingType.REGIONS:
  336. self._default = self.digitizeregion
  337. elif self.parent.samplingtype in [SamplingType.MUNITSR,
  338. SamplingType.MMVWINR]:
  339. self._default = self.digitizeunit
  340. elif self.parent.samplingtype in [SamplingType.MUNITSC,
  341. SamplingType.MMVWINC]:
  342. self._default = self.digitizeunitc
  343. elif self.parent.samplingtype == SamplingType.VECT:
  344. self._default = None
  345. else:
  346. self._default = self.draw
  347. for tool in (self._default, self.pan, self.zoomIn, self.zoomOut):
  348. if tool:
  349. self.toolSwitcher.AddToolToGroup(group='mouseUse',
  350. toolbar=self, tool=tool)
  351. # realize the toolbar
  352. self.Realize()
  353. def _toolbarData(self):
  354. """!Toolbar data"""
  355. if self.parent.samplingtype == SamplingType.REGIONS:
  356. drawTool = ('digitizeregion', icons['digitizeregion'],
  357. self.parent.OnDigitizeRegion, wx.ITEM_CHECK)
  358. elif self.parent.samplingtype in [SamplingType.MUNITSR,
  359. SamplingType.MMVWINR]:
  360. drawTool = ('digitizeunit', icons['digitizeunit'],
  361. self.parent.OnDraw, wx.ITEM_CHECK)
  362. elif self.parent.samplingtype in [SamplingType.MUNITSC,
  363. SamplingType.MMVWINC]:
  364. drawTool = ('digitizeunitc', icons['digitizeunitc'],
  365. self.parent.OnDrawRadius, wx.ITEM_CHECK)
  366. else:
  367. drawTool = ('draw', icons['draw'], self.parent.OnDraw,
  368. wx.ITEM_CHECK)
  369. if self.parent.samplingtype == SamplingType.VECT:
  370. return self._getToolbarData((
  371. ('pan', BaseIcons['pan'], self.parent.OnPan,
  372. wx.ITEM_CHECK),
  373. ('zoomIn', BaseIcons['zoomIn'], self.parent.OnZoomIn,
  374. wx.ITEM_CHECK),
  375. ('zoomOut', BaseIcons['zoomOut'], self.parent.OnZoomOut,
  376. wx.ITEM_CHECK),
  377. ('zoomExtent', BaseIcons['zoomExtent'],
  378. self.parent.OnZoomToMap),))
  379. else:
  380. return self._getToolbarData((drawTool, (None, ),
  381. ('pan', BaseIcons['pan'], self.parent.OnPan,
  382. wx.ITEM_CHECK),
  383. ('zoomIn', BaseIcons['zoomIn'], self.parent.OnZoomIn,
  384. wx.ITEM_CHECK),
  385. ('zoomOut', BaseIcons['zoomOut'], self.parent.OnZoomOut,
  386. wx.ITEM_CHECK),
  387. ('zoomExtent', BaseIcons['zoomExtent'],
  388. self.parent.OnZoomToMap),))