controllers.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. """
  2. @package iscatt.controllers
  3. @brief Controller layer wx.iscatt.
  4. Classes:
  5. - controllers::ScattsManager
  6. - controllers::PlotsRenderingManager
  7. - controllers::CategoriesManager
  8. - controllers::IMapWinDigitConnection
  9. - controllers::IClassDigitConnection
  10. - controllers::IMapDispConnection
  11. - controllers::IClassConnection
  12. (C) 2013 by the GRASS Development Team
  13. This program is free software under the GNU General Public License
  14. (>=v2). Read the file COPYING that comes with GRASS for details.
  15. @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
  16. """
  17. import os
  18. import sys
  19. from copy import deepcopy
  20. import wx
  21. import six
  22. from core.gcmd import GException, GError, GMessage, RunCommand, GWarning
  23. from core.settings import UserSettings
  24. from core.gthread import gThread
  25. from iscatt.iscatt_core import Core, idBandsToidScatt, GetRasterInfo, GetRegion, \
  26. MAX_SCATT_SIZE, WARN_SCATT_SIZE, MAX_NCELLS, WARN_NCELLS
  27. from iscatt.dialogs import AddScattPlotDialog, ExportCategoryRaster
  28. from iclass.dialogs import IClassGroupDialog
  29. import grass.script as grass
  30. from grass.pydispatch.signal import Signal
  31. class ScattsManager:
  32. """Main controller
  33. """
  34. def __init__(self, guiparent, giface, iclass_mapwin=None):
  35. self.giface = giface
  36. self.mapDisp = giface.GetMapDisplay()
  37. if iclass_mapwin:
  38. self.mapWin = iclass_mapwin
  39. else:
  40. self.mapWin = giface.GetMapWindow()
  41. self.guiparent = guiparent
  42. self.show_add_scatt_plot = False
  43. self.core = Core()
  44. self.cats_mgr = CategoriesManager(self, self.core)
  45. self.render_mgr = PlotsRenderingManager(scatt_mgr=self,
  46. cats_mgr=self.cats_mgr,
  47. core=self.core)
  48. self.thread = gThread()
  49. self.plots = {}
  50. self.plot_mode = None
  51. self.pol_sel_mode = [False, None]
  52. self.data_set = False
  53. self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
  54. self.renderingStarted = self.render_mgr.renderingStarted
  55. self.renderingFinished = self.render_mgr.renderingFinished
  56. self.computingStarted = Signal("ScattsManager.computingStarted")
  57. if iclass_mapwin:
  58. self.digit_conn = IClassDigitConnection(self,
  59. self.mapWin,
  60. self.core.CatRastUpdater())
  61. self.iclass_conn = IClassConnection(self,
  62. iclass_mapwin.parent,
  63. self.cats_mgr)
  64. else:
  65. self.digit_conn = IMapWinDigitConnection()
  66. self.iclass_conn = IMapDispConnection(scatt_mgr=self,
  67. cats_mgr=self.cats_mgr,
  68. giface=self.giface)
  69. self._initSettings()
  70. self.modeSet = Signal("ScattsManager.mondeSet")
  71. def CleanUp(self):
  72. self.thread.Terminate()
  73. # there should be better way hot to clean up the thread
  74. # than calling the clean up function outside the thread,
  75. # which still may running
  76. self.core.CleanUp()
  77. def CleanUpDone(self):
  78. for scatt_id, scatt in self.plots.items():
  79. if scatt['scatt']:
  80. scatt['scatt'].CleanUp()
  81. self.plots.clear()
  82. def _initSettings(self):
  83. """Initialization of settings (if not already defined)
  84. """
  85. # initializes default settings
  86. initSettings = [
  87. ['selection', 'sel_pol', (255, 255, 0)],
  88. ['selection', 'sel_pol_vertex', (255, 0, 0)],
  89. ['selection', 'sel_area', (0, 255, 19)],
  90. ['selection', "snap_tresh", 10],
  91. ['selection', 'sel_area_opacty', 50],
  92. ['ellipses', 'show_ellips', True],
  93. ]
  94. for init in initSettings:
  95. UserSettings.ReadSettingsFile()
  96. UserSettings.Append(dict=UserSettings.userSettings,
  97. group='scatt',
  98. key=init[0],
  99. subkey=init[1],
  100. value=init[2],
  101. overwrite=False)
  102. def SetData(self):
  103. self.iclass_conn.SetData()
  104. self.digit_conn.SetData()
  105. def SetBands(self, bands):
  106. self.busy = wx.BusyInfo(_("Loading data..."))
  107. self.data_set = False
  108. self.thread.Run(callable=self.core.CleanUp,
  109. ondone=lambda event: self.CleanUpDone())
  110. if self.show_add_scatt_plot:
  111. show_add = True
  112. else:
  113. show_add = False
  114. self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands)))
  115. self.all_bands = bands
  116. self.region = GetRegion()
  117. ncells = self.region["rows"] * self.region["cols"]
  118. if ncells > MAX_NCELLS:
  119. del self.busy
  120. self.data_set = True
  121. return
  122. self.bands = bands[:]
  123. self.bands_info = {}
  124. valid_bands = []
  125. for b in self.bands[:]:
  126. i = GetRasterInfo(b)
  127. self.bands_info[b] = i
  128. if i is not None:
  129. valid_bands.append(b)
  130. for i, b in enumerate(valid_bands):
  131. # name : index in core bands -
  132. # if not in core bands (not CELL type) -> index = -1
  133. self.all_bands_to_bands[b] = i
  134. self.thread.Run(callable=self.core.SetData,
  135. bands=valid_bands,
  136. ondone=self.SetDataDone,
  137. userdata={"show_add": show_add})
  138. def SetDataDone(self, event):
  139. del self.busy
  140. self.data_set = True
  141. todo = event.ret
  142. self.bad_bands = event.ret
  143. bands = self.core.GetBands()
  144. self.bad_rasts = event.ret
  145. self.cats_mgr.SetData()
  146. if event.userdata['show_add']:
  147. self.AddScattPlot()
  148. def GetBands(self):
  149. return self.core.GetBands()
  150. def AddScattPlot(self):
  151. if not self.data_set and self.iclass_conn:
  152. self.show_add_scatt_plot = True
  153. self.iclass_conn.SetData()
  154. self.show_add_scatt_plot = False
  155. return
  156. if not self.data_set:
  157. GError(_('No data set.'))
  158. return
  159. self.computingStarted.emit()
  160. bands = self.core.GetBands()
  161. #added_bands_ids = []
  162. # for scatt_id in self.plots):
  163. # added_bands_ids.append[idBandsToidScatt(scatt_id)]
  164. self.digit_conn.Update()
  165. ncells = self.region["rows"] * self.region["cols"]
  166. if ncells > MAX_NCELLS:
  167. GError(
  168. _(
  169. parent=self.guiparent, mmessage=_(
  170. "Interactive Scatter Plot Tool can not be used.\n"
  171. "Number of cells (rows*cols) <%d> in current region"
  172. "is higher than maximum limit <%d>.\n\n"
  173. "You can reduce number of cells in current region using <g.region> command." %
  174. (ncells, MAX_NCELLS))))
  175. return
  176. elif ncells > WARN_NCELLS:
  177. dlg = wx.MessageDialog(
  178. parent=self.guiparent,
  179. message=_("Number of cells (rows*cols) <%d> in current region is "
  180. "higher than recommended threshold <%d>.\n"
  181. "It is strongly advised to reduce number of cells "
  182. "in current region below recommend threshold.\n "
  183. "It can be done by <g.region> command.\n\n"
  184. "Do you want to continue using "
  185. "Interactive Scatter Plot Tool with this region?"
  186. % (ncells, WARN_NCELLS)),
  187. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
  188. ret = dlg.ShowModal()
  189. if ret != wx.ID_YES:
  190. return
  191. dlg = AddScattPlotDialog(parent=self.guiparent,
  192. bands=self.all_bands,
  193. check_bands_callback=self.CheckBands)
  194. if dlg.ShowModal() == wx.ID_OK:
  195. scatt_ids = []
  196. sel_bands = dlg.GetBands()
  197. for b_1, b_2 in sel_bands:
  198. transpose = False
  199. if b_1 > b_2:
  200. transpose = True
  201. tmp_band = b_2
  202. b_2 = b_1
  203. b_1 = tmp_band
  204. b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
  205. b_2_id = self.all_bands_to_bands[self.all_bands[b_2]]
  206. scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
  207. if scatt_id in self.plots:
  208. continue
  209. self.plots[scatt_id] = {'transpose': transpose,
  210. 'scatt': None}
  211. scatt_ids.append(scatt_id)
  212. self._addScattPlot(scatt_ids)
  213. dlg.Destroy()
  214. def CheckBands(self, b_1, b_2):
  215. bands = self.core.GetBands()
  216. added_scatts_ids = self.plots.keys()
  217. b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
  218. b_2_id = self.all_bands_to_bands[self.all_bands[b_1]]
  219. scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
  220. if scatt_id in added_scatts_ids:
  221. GWarning(
  222. parent=self.guiparent, message=_(
  223. "Scatter plot with same band combination (regardless x y order) "
  224. "is already displayed."))
  225. return False
  226. b_1_name = self.all_bands[b_1]
  227. b_2_name = self.all_bands[b_2]
  228. b_1_i = self.bands_info[b_1_name]
  229. b_2_i = self.bands_info[b_2_name]
  230. err = ""
  231. for b in [b_1_name, b_2_name]:
  232. if self.bands_info[b] is None:
  233. err += _("Band <%s> is not CELL (integer) type.\n" % b)
  234. if err:
  235. GMessage(parent=self.guiparent,
  236. message=_("Scatter plot cannot be added.\n" + err))
  237. return False
  238. mrange = b_1_i['range'] * b_2_i['range']
  239. if mrange > MAX_SCATT_SIZE:
  240. GWarning(parent=self.guiparent,
  241. message=_("Scatter plot cannot be added.\n"
  242. "Multiple of bands ranges <%s:%d * %s:%d = %d> "
  243. "is higher than maximum limit <%d>.\n"
  244. % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'],
  245. mrange, MAX_SCATT_SIZE)))
  246. return False
  247. elif mrange > WARN_SCATT_SIZE:
  248. dlg = wx.MessageDialog(
  249. parent=self.guiparent,
  250. message=_(
  251. "Multiple of bands ranges <%s:%d * %s:%d = %d> "
  252. "is higher than recommended limit <%d>.\n"
  253. "It is strongly advised to reduce range extend of bands"
  254. "(e. g. using r.rescale) below recommended threshold.\n\n"
  255. "Do you really want to add this scatter plot?" %
  256. (b_1_name, b_1_i['range'],
  257. b_1_name, b_2_i['range'],
  258. mrange, WARN_SCATT_SIZE)),
  259. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
  260. ret = dlg.ShowModal()
  261. if ret != wx.ID_YES:
  262. return False
  263. return True
  264. def _addScattPlot(self, scatt_ids):
  265. self.render_mgr.NewRunningProcess()
  266. self.thread.Run(callable=self.core.AddScattPlots,
  267. scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
  268. def AddScattPlotDone(self, event):
  269. if not self.data_set:
  270. return
  271. scatt_ids = event.kwds['scatt_ids']
  272. for s_id in scatt_ids:
  273. trans = self.plots[s_id]['transpose']
  274. self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(
  275. scatt_id=s_id, transpose=trans)
  276. self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
  277. self.plots[s_id]['scatt'].cursorMove.connect(
  278. lambda x, y, scatt_id:
  279. self.cursorPlotMove.emit(x=x, y=y,
  280. scatt_id=scatt_id))
  281. if self.plot_mode:
  282. self.plots[s_id]['scatt'].SetMode(self.plot_mode)
  283. self.plots[s_id]['scatt'].ZoomToExtend()
  284. self.render_mgr.RunningProcessDone()
  285. def PlotClosed(self, scatt_id):
  286. del self.plots[scatt_id]
  287. def SetPlotsMode(self, mode):
  288. self.plot_mode = mode
  289. for scatt in six.itervalues(self.plots):
  290. if scatt['scatt']:
  291. scatt['scatt'].SetMode(mode)
  292. self.modeSet.emit(mode=mode)
  293. def ActivateSelectionPolygonMode(self, activate):
  294. self.pol_sel_mode[0] = activate
  295. for scatt in six.itervalues(self.plots):
  296. if not scatt['scatt']:
  297. continue
  298. scatt['scatt'].SetSelectionPolygonMode(activate)
  299. if not activate and self.plot_mode not in [
  300. 'zoom', 'pan', 'zoom_extend']:
  301. self.SetPlotsMode(None)
  302. self.render_mgr.RunningProcessDone()
  303. return activate
  304. def ProcessSelectionPolygons(self, process_mode):
  305. scatts_polygons = {}
  306. for scatt_id, scatt in six.iteritems(self.plots):
  307. if not scatt['scatt']:
  308. continue
  309. coords = scatt['scatt'].GetCoords()
  310. if coords is not None:
  311. scatts_polygons[scatt_id] = coords
  312. if not scatts_polygons:
  313. return
  314. value = 1
  315. if process_mode == 'remove':
  316. value = 0
  317. sel_cat_id = self.cats_mgr.GetSelectedCat()
  318. if not sel_cat_id:
  319. dlg = wx.MessageDialog(
  320. parent=self.guiparent,
  321. message=_(
  322. "In order to select arrea in scatter plot, "
  323. "you have to select class first.\n\n"
  324. "There is no class yet, "
  325. "do you want to create one?"),
  326. caption=_("No class selected"),
  327. style=wx.YES_NO)
  328. if dlg.ShowModal() == wx.ID_YES:
  329. self.iclass_conn.EmptyCategories()
  330. sel_cat_id = self.cats_mgr.GetSelectedCat()
  331. if not sel_cat_id:
  332. return
  333. for scatt in six.itervalues(self.plots):
  334. if scatt['scatt']:
  335. scatt['scatt'].SetEmpty()
  336. self.computingStarted.emit()
  337. self.render_mgr.NewRunningProcess()
  338. self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
  339. self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
  340. self.thread.Run(callable=self.core.UpdateCategoryWithPolygons,
  341. cat_id=sel_cat_id,
  342. scatts_pols=scatts_polygons,
  343. value=value, ondone=self.SetEditCatDataDone)
  344. def SetEditCatDataDone(self, event):
  345. if not self.data_set:
  346. return
  347. self.render_mgr.RunningProcessDone()
  348. if event.exception:
  349. GError(
  350. _("Error occurred during computation of scatter plot category:\n%s"),
  351. parent=self.guiparent,
  352. showTraceback=False)
  353. cat_id = event.ret
  354. self.iclass_conn.RenderCatRast(cat_id)
  355. def SettingsUpdated(self, chanaged_setts):
  356. self.render_mgr.RenderRequest()
  357. #['ellipses', 'show_ellips']
  358. def GetCategoriesManager(self):
  359. return self.cats_mgr
  360. class PlotsRenderingManager:
  361. """Manages rendering of scatter plot.
  362. .. todo::
  363. still space for optimalization
  364. """
  365. def __init__(self, scatt_mgr, cats_mgr, core):
  366. self.scatt_mgr = scatt_mgr
  367. self.cats_mgr = cats_mgr
  368. self.core = core
  369. self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
  370. self.runningProcesses = 0
  371. self.data_to_render = {}
  372. self.render_queue = []
  373. self.cat_ids = []
  374. self.cat_cond_ids = []
  375. self.renderingStarted = Signal("ScattsManager.renderingStarted")
  376. self.renderingFinished = Signal("ScattsManager.renderingFinished")
  377. def AddRenderRequest(self, scatts):
  378. for scatt_id, cat_ids in scatts:
  379. if not self.data_to_render.has_key[scatt_id]:
  380. self.data_to_render = cat_ids
  381. else:
  382. for c in cat_ids:
  383. if c not in self.data_to_render[scatt_id]:
  384. self.data_to_render[scatt_id].append(c)
  385. def NewRunningProcess(self):
  386. self.runningProcesses += 1
  387. def RunningProcessDone(self):
  388. self.runningProcesses -= 1
  389. if self.runningProcesses <= 1:
  390. self.RenderScattPlts()
  391. def RenderRequest(self):
  392. if self.runningProcesses <= 1:
  393. self.RenderScattPlts()
  394. def CategoryChanged(self, cat_ids):
  395. for c in cat_ids:
  396. if c not in self.cat_ids:
  397. self.cat_ids.append(c)
  398. def CategoryCondsChanged(self, cat_ids):
  399. for c in cat_ids:
  400. if c not in self.cat_cond_ids:
  401. self.cat_cond_ids.append(c)
  402. def RenderScattPlts(self, scatt_ids=None):
  403. if len(self.render_queue) > 1:
  404. return
  405. self.renderingStarted.emit()
  406. self.render_queue.append(self.scatt_mgr.thread.GetId())
  407. cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
  408. cats = self.cats_mgr.GetCategories()[:]
  409. self.scatt_mgr.thread.Run(
  410. callable=self._renderscattplts,
  411. scatt_ids=scatt_ids,
  412. cats=cats,
  413. cats_attrs=cats_attrs,
  414. ondone=self.RenderingDone)
  415. def _renderscattplts(self, scatt_ids, cats, cats_attrs):
  416. cats.reverse()
  417. cats.insert(0, 0)
  418. for i_scatt_id, scatt in self.scatt_mgr.plots.items():
  419. if scatt_ids is not None and \
  420. i_scatt_id not in scatt_ids:
  421. continue
  422. if not scatt['scatt']:
  423. continue
  424. scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
  425. if self._showConfEllipses():
  426. ellipses_dt = self.scatts_dt.GetEllipses(
  427. i_scatt_id, cats_attrs)
  428. else:
  429. ellipses_dt = {}
  430. for c in six.iterkeys(scatt_dt):
  431. try:
  432. self.cat_ids.remove(c)
  433. scatt_dt[c]['render'] = True
  434. except:
  435. scatt_dt[c]['render'] = False
  436. if self.scatt_mgr.pol_sel_mode[0]:
  437. self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
  438. scatt['scatt'].Plot(cats_order=cats,
  439. scatts=scatt_dt,
  440. ellipses=ellipses_dt,
  441. styles=cats_attrs)
  442. def RenderingDone(self, event):
  443. self.render_queue.remove(event.pid)
  444. if not self.render_queue:
  445. self.renderingFinished.emit()
  446. def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
  447. cat_id = self.cats_mgr.GetSelectedCat()
  448. if not cat_id:
  449. return
  450. sel_a_cat_id = -1
  451. s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
  452. if not s:
  453. return
  454. cats_order.append(sel_a_cat_id)
  455. col = UserSettings.Get(group='scatt',
  456. key='selection',
  457. subkey='sel_area')
  458. col = ":".join(map(str, col))
  459. opac = UserSettings.Get(group='scatt',
  460. key='selection',
  461. subkey='sel_area_opacty') / 100.0
  462. cats_attrs[sel_a_cat_id] = {'color': col,
  463. 'opacity': opac,
  464. 'show': True}
  465. scatt_dt[sel_a_cat_id] = s[cat_id]
  466. scatt_dt[sel_a_cat_id]['render'] = False
  467. if cat_id in self.cat_cond_ids:
  468. scatt_dt[sel_a_cat_id]['render'] = True
  469. self.cat_cond_ids.remove(cat_id)
  470. def _showConfEllipses(self):
  471. return UserSettings.Get(group='scatt',
  472. key="ellipses",
  473. subkey="show_ellips")
  474. class CategoriesManager:
  475. """Manages categories list of scatter plot.
  476. """
  477. def __init__(self, scatt_mgr, core):
  478. self.core = core
  479. self.scatt_mgr = scatt_mgr
  480. self.cats = {}
  481. self.cats_ids = []
  482. self.sel_cat_id = None
  483. self.exportRaster = None
  484. self.initialized = Signal('CategoriesManager.initialized')
  485. self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
  486. self.deletedCategory = Signal('CategoriesManager.deletedCategory')
  487. self.addedCategory = Signal('CategoriesManager.addedCategory')
  488. def ChangePosition(self, cat_id, new_pos):
  489. if new_pos >= len(self.cats_ids):
  490. return False
  491. try:
  492. pos = self.cats_ids.index(cat_id)
  493. except:
  494. return False
  495. if pos > new_pos:
  496. pos -= 1
  497. self.cats_ids.remove(cat_id)
  498. self.cats_ids.insert(new_pos, cat_id)
  499. self.scatt_mgr.render_mgr.RenderRequest()
  500. return True
  501. def _addCategory(self, cat_id):
  502. self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
  503. cat_id=cat_id)
  504. def SetData(self):
  505. if not self.scatt_mgr.data_set:
  506. return
  507. for cat_id in self.cats_ids:
  508. self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
  509. cat_id=cat_id)
  510. def AddCategory(self, cat_id=None, name=None, color=None, nstd=None):
  511. if cat_id is None:
  512. if self.cats_ids:
  513. cat_id = max(self.cats_ids) + 1
  514. else:
  515. cat_id = 1
  516. if self.scatt_mgr.data_set:
  517. self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
  518. cat_id=cat_id)
  519. # TODO check number of cats
  520. # if ret < 0: #TODO
  521. # return -1;
  522. self.cats[cat_id] = {
  523. 'name': 'class_%d' % cat_id,
  524. 'color': "0:0:0",
  525. 'opacity': 1.0,
  526. 'show': True,
  527. 'nstd': 1.0,
  528. }
  529. self.cats_ids.insert(0, cat_id)
  530. if name is not None:
  531. self.cats[cat_id]["name"] = name
  532. if color is not None:
  533. self.cats[cat_id]["color"] = color
  534. if nstd is not None:
  535. self.cats[cat_id]["nstd"] = nstd
  536. self.addedCategory.emit(cat_id=cat_id,
  537. name=self.cats[cat_id]["name"],
  538. color=self.cats[cat_id]["color"])
  539. return cat_id
  540. def SetCategoryAttrs(self, cat_id, attrs_dict):
  541. render = False
  542. update_cat_rast = []
  543. for k, v in six.iteritems(attrs_dict):
  544. if not render and k in ['color', 'opacity', 'show', 'nstd']:
  545. render = True
  546. if k in ['color', 'name']:
  547. update_cat_rast.append(k)
  548. self.cats[cat_id][k] = v
  549. if render:
  550. self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
  551. self.scatt_mgr.render_mgr.RenderRequest()
  552. if update_cat_rast:
  553. self.scatt_mgr.iclass_conn.UpdateCategoryRaster(
  554. cat_id, update_cat_rast)
  555. self.setCategoryAttrs.emit(cat_id=cat_id, attrs_dict=attrs_dict)
  556. def DeleteCategory(self, cat_id):
  557. if self.scatt_mgr.data_set:
  558. self.scatt_mgr.thread.Run(callable=self.core.DeleteCategory,
  559. cat_id=cat_id)
  560. del self.cats[cat_id]
  561. self.cats_ids.remove(cat_id)
  562. self.deletedCategory.emit(cat_id=cat_id)
  563. # TODO emit event?
  564. def SetSelectedCat(self, cat_id):
  565. self.sel_cat_id = cat_id
  566. if self.scatt_mgr.pol_sel_mode[0]:
  567. self.scatt_mgr.render_mgr.RenderRequest()
  568. def GetSelectedCat(self):
  569. return self.sel_cat_id
  570. def GetCategoryAttrs(self, cat_id):
  571. #TODO is mutable
  572. return self.cats[cat_id]
  573. def GetCategoriesAttrs(self):
  574. #TODO is mutable
  575. return self.cats
  576. def GetCategories(self):
  577. return self.cats_ids[:]
  578. def SetCategoryPosition(self):
  579. if newindex > oldindex:
  580. newindex -= 1
  581. self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
  582. def ExportCatRast(self, cat_id):
  583. cat_attrs = self.GetCategoryAttrs(cat_id)
  584. dlg = ExportCategoryRaster(
  585. parent=self.scatt_mgr.guiparent,
  586. rasterName=self.exportRaster,
  587. title=_("Export scatter plot raster of class <%s>") %
  588. cat_attrs['name'])
  589. if dlg.ShowModal() == wx.ID_OK:
  590. self.exportCatRast = dlg.GetRasterName()
  591. dlg.Destroy()
  592. self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
  593. userdata={'name': cat_attrs['name']},
  594. cat_id=cat_id,
  595. rast_name=self.exportCatRast,
  596. ondone=self.OnExportCatRastDone)
  597. def OnExportCatRastDone(self, event):
  598. ret, err = event.ret
  599. if ret == 0:
  600. cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
  601. GMessage(
  602. _("Scatter plot raster of class <%s> exported to raster map <%s>.") %
  603. (event.userdata['name'], event.kwds['rast_name']))
  604. else:
  605. GMessage(
  606. _("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") %
  607. (event.userdata['name'], event.kwds['rast_name'], err))
  608. class IMapWinDigitConnection:
  609. """Manage communication of the scatter plot with digitizer in
  610. mapwindow (does not work).
  611. """
  612. def Update(self):
  613. pass
  614. def SetData(self):
  615. pass
  616. class IClassDigitConnection:
  617. """Manages communication of the scatter plot with digitizer in
  618. wx.iclass.
  619. """
  620. def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
  621. self.mapWin = mapWin
  622. self.vectMap = None
  623. self.scatt_rast_updater = scatt_rast_updater
  624. self.scatt_mgr = scatt_mgr
  625. self.cats_mgr = scatt_mgr.cats_mgr
  626. self.cats_to_update = []
  627. self.pids = {'mapwin_conn': []}
  628. self.thread = self.scatt_mgr.thread
  629. # TODO
  630. self.mapWin.parent.toolbars[
  631. "vdigit"].editingStarted.connect(self.DigitDataChanged)
  632. def Update(self):
  633. self.thread.Run(callable=self.scatt_rast_updater.SyncWithMap)
  634. def SetData(self):
  635. self.cats_to_update = []
  636. self.pids = {'mapwin_conn': []}
  637. def _connectSignals(self):
  638. self.digit.featureAdded.connect(self.AddFeature)
  639. self.digit.areasDeleted.connect(self.DeleteAreas)
  640. self.digit.featuresDeleted.connect(self.DeleteAreas)
  641. self.digit.vertexMoved.connect(self.EditedFeature)
  642. self.digit.vertexRemoved.connect(self.EditedFeature)
  643. self.digit.lineEdited.connect(self.EditedFeature)
  644. self.digit.featuresMoved.connect(self.EditedFeature)
  645. def AddFeature(self, new_bboxs, new_areas_cats):
  646. if not self.scatt_mgr.data_set:
  647. return
  648. self.scatt_mgr.computingStarted.emit()
  649. self.pids['mapwin_conn'].append(self.thread.GetId())
  650. self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
  651. new_bboxs=new_bboxs,
  652. old_bboxs=[],
  653. old_areas_cats=[],
  654. new_areas_cats=new_areas_cats,
  655. ondone=self.OnDone)
  656. def DeleteAreas(self, old_bboxs, old_areas_cats):
  657. if not self.scatt_mgr.data_set:
  658. return
  659. self.scatt_mgr.computingStarted.emit()
  660. self.pids['mapwin_conn'].append(self.thread.GetId())
  661. self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
  662. new_bboxs=[],
  663. old_bboxs=old_bboxs,
  664. old_areas_cats=old_areas_cats,
  665. new_areas_cats=[],
  666. ondone=self.OnDone)
  667. def EditedFeature(self, new_bboxs, new_areas_cats,
  668. old_bboxs, old_areas_cats):
  669. if not self.scatt_mgr.data_set:
  670. return
  671. self.scatt_mgr.computingStarted.emit()
  672. self.pids['mapwin_conn'].append(self.thread.GetId())
  673. self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
  674. new_bboxs=new_bboxs,
  675. old_bboxs=old_bboxs,
  676. old_areas_cats=old_areas_cats,
  677. new_areas_cats=new_areas_cats,
  678. ondone=self.OnDone)
  679. def DigitDataChanged(self, vectMap, digit):
  680. self.digit = digit
  681. self.vectMap = vectMap
  682. self.digit.EmitSignals(emit=True)
  683. self.scatt_rast_updater.SetVectMap(vectMap)
  684. self._connectSignals()
  685. def OnDone(self, event):
  686. if not self.scatt_mgr.data_set:
  687. return
  688. self.pids['mapwin_conn'].remove(event.pid)
  689. updated_cats = event.ret
  690. for cat in updated_cats:
  691. if cat not in self.cats_to_update:
  692. self.cats_to_update.append(cat)
  693. if not self.pids['mapwin_conn']:
  694. self.thread.Run(
  695. callable=self.scatt_mgr.core.ComputeCatsScatts,
  696. cats_ids=self.cats_to_update[:],
  697. ondone=self.Render)
  698. del self.cats_to_update[:]
  699. def Render(self, event):
  700. self.scatt_mgr.render_mgr.RenderScattPlts()
  701. class IMapDispConnection:
  702. """Manage comunication of the scatter plot with mapdisplay in mapwindow.
  703. """
  704. def __init__(self, scatt_mgr, cats_mgr, giface):
  705. self.scatt_mgr = scatt_mgr
  706. self.cats_mgr = cats_mgr
  707. self.set_g = {'group': None, 'subg': None}
  708. self.giface = giface
  709. self.added_cats_rasts = {}
  710. def SetData(self):
  711. dlg = IClassGroupDialog(self.scatt_mgr.guiparent,
  712. group=self.set_g['group'],
  713. subgroup=self.set_g['subg'])
  714. bands = []
  715. while True:
  716. if dlg.ShowModal() == wx.ID_OK:
  717. bands = dlg.GetGroupBandsErr(parent=self.scatt_mgr.guiparent)
  718. if bands:
  719. name, s = dlg.GetData()
  720. group = grass.find_file(name=name, element='group')
  721. self.set_g['group'] = group['name']
  722. self.set_g['subg'] = s
  723. break
  724. else:
  725. break
  726. dlg.Destroy()
  727. self.added_cats_rasts = {}
  728. if bands:
  729. self.scatt_mgr.SetBands(bands)
  730. def EmptyCategories(self):
  731. return None
  732. def UpdateCategoryRaster(self, cat_id, attrs, render=True):
  733. cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
  734. if not grass.find_file(cat_rast, element='cell', mapset='.')['file']:
  735. return
  736. cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  737. if "color" in attrs:
  738. ret, err_msg = RunCommand('r.colors',
  739. map=cat_rast,
  740. rules="-",
  741. stdin="1 %s" % cats_attrs["color"],
  742. getErrorMsg=True)
  743. if ret != 0:
  744. GError("r.colors failed\n%s" % err_msg)
  745. if render:
  746. self.giface.updateMap.emit()
  747. if "name" in attrs:
  748. # TODO hack
  749. self.giface.GetLayerList()._tree.SetItemText(
  750. self.added_cats_rasts[cat_id], cats_attrs['name'])
  751. cats_attrs["name"]
  752. def RenderCatRast(self, cat_id):
  753. if not cat_id in six.iterkeys(self.added_cats_rasts):
  754. cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
  755. cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
  756. self.UpdateCategoryRaster(cat_id, ['color'], render=False)
  757. cmd = ['d.rast', 'map=%s' % cat_rast]
  758. # TODO HACK
  759. layer = self.giface.GetLayerList()._tree.AddLayer(ltype="raster",
  760. lname=cat_name,
  761. lcmd=cmd,
  762. lchecked=True)
  763. self.added_cats_rasts[cat_id] = layer
  764. else: # TODO settings
  765. self.giface.updateMap.emit()
  766. class IClassConnection:
  767. """Manage comunication of the scatter plot with mapdisplay in wx.iclass.
  768. """
  769. def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
  770. self.iclass_frame = iclass_frame
  771. self.stats_data = self.iclass_frame.stats_data
  772. self.cats_mgr = cats_mgr
  773. self.scatt_mgr = scatt_mgr
  774. self.added_cats_rasts = []
  775. self.stats_data.statisticsAdded.connect(self.AddCategory)
  776. self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
  777. self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
  778. self.stats_data.statisticsSet.connect(self.SetCategory)
  779. self.iclass_frame.groupSet.connect(self.GroupSet)
  780. self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
  781. self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
  782. self.cats_mgr.addedCategory.connect(self.AddStatistics)
  783. self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
  784. self.SyncCats()
  785. def UpdateCategoryRaster(self, cat_id, attrs, render=True):
  786. if not self.scatt_mgr.data_set:
  787. return
  788. cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
  789. if not cat_rast:
  790. return
  791. if not grass.find_file(cat_rast, element='cell', mapset='.')['file']:
  792. return
  793. cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  794. train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
  795. if "color" in attrs:
  796. ret, err_msg = RunCommand('r.colors',
  797. map=cat_rast,
  798. rules="-",
  799. stdin="1 %s" % cats_attrs["color"],
  800. getErrorMsg=True)
  801. if ret != 0:
  802. GError("r.colors failed\n%s" % err_msg)
  803. if render:
  804. train_mgr.Render()
  805. if "name" in attrs:
  806. cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
  807. train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
  808. cats_attrs["name"]
  809. def RenderCatRast(self, cat_id):
  810. train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
  811. if not cat_id in self.added_cats_rasts:
  812. cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
  813. cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
  814. self.UpdateCategoryRaster(cat_id, ['color'], render=False)
  815. train_mgr.AddLayer(cat_rast, alias=cat_name)
  816. self.added_cats_rasts.append(cat_id)
  817. else: # TODO settings
  818. train_mgr.Render()
  819. def SetData(self):
  820. self.iclass_frame.AddBands()
  821. self.added_cats_rasts = []
  822. def EmptyCategories(self):
  823. self.iclass_frame.OnCategoryManager(None)
  824. def SyncCats(self, cats_ids=None):
  825. self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
  826. cats = self.stats_data.GetCategories()
  827. for c in cats:
  828. if cats_ids and c not in cats_ids:
  829. continue
  830. stats = self.stats_data.GetStatistics(c)
  831. self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
  832. self.cats_mgr.addedCategory.connect(self.AddStatistics)
  833. def CategoryChanged(self, cat):
  834. self.cats_mgr.SetSelectedCat(cat)
  835. def AddCategory(self, cat, name, color):
  836. self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
  837. stats = self.stats_data.GetStatistics(cat)
  838. self.cats_mgr.AddCategory(
  839. cat_id=cat,
  840. name=name,
  841. color=color,
  842. nstd=stats.nstd)
  843. self.cats_mgr.addedCategory.connect(self.AddStatistics)
  844. def DeleteCategory(self, cat):
  845. self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
  846. self.cats_mgr.DeleteCategory(cat)
  847. self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
  848. def DeletAllCategories(self):
  849. self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
  850. cats = self.stats_data.GetCategories()
  851. for c in cats:
  852. self.cats_mgr.DeleteCategory(c)
  853. self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
  854. def SetCategory(self, cat, stats):
  855. self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
  856. cats_attr = {}
  857. for attr in ['name', 'color', 'nstd']:
  858. if attr in stats:
  859. cats_attr[attr] = stats[attr]
  860. if cats_attr:
  861. self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
  862. self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
  863. def SetStatistics(self, cat_id, attrs_dict):
  864. self.stats_data.statisticsSet.disconnect(self.SetCategory)
  865. self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
  866. self.stats_data.statisticsSet.connect(self.SetCategory)
  867. def AddStatistics(self, cat_id, name, color):
  868. self.stats_data.statisticsAdded.disconnect(self.AddCategory)
  869. self.stats_data.AddStatistics(cat_id, name, color)
  870. self.stats_data.statisticsAdded.connect(self.AddCategory)
  871. def DeleteStatistics(self, cat_id):
  872. self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
  873. self.stats_data.DeleteStatistics(cat_id)
  874. self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
  875. def GroupSet(self, group, subgroup):
  876. kwargs = {}
  877. if subgroup:
  878. kwargs['subgroup'] = subgroup
  879. res = RunCommand('i.group',
  880. flags='g',
  881. group=group,
  882. read=True, **kwargs).strip()
  883. if res.splitlines()[0]:
  884. bands = res.splitlines()
  885. self.scatt_mgr.SetBands(bands)