controllers.py 38 KB

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