controllers.py 36 KB

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