sampling_frame.py 19 KB

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