12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151 |
- """
- @package iscatt.controllers
- @brief Controller layer wx.iscatt.
- Classes:
- - controllers::ScattsManager
- - controllers::PlotsRenderingManager
- - controllers::CategoriesManager
- - controllers::IMapWinDigitConnection
- - controllers::IClassDigitConnection
- - controllers::IMapDispConnection
- - controllers::IClassConnection
- (C) 2013 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
- """
- import os
- import sys
- from copy import deepcopy
- import wx
- import six
- from core.gcmd import GException, GError, GMessage, RunCommand, GWarning
- from core.settings import UserSettings
- from core.gthread import gThread
- from iscatt.iscatt_core import Core, idBandsToidScatt, GetRasterInfo, GetRegion, \
- MAX_SCATT_SIZE, WARN_SCATT_SIZE, MAX_NCELLS, WARN_NCELLS
- from iscatt.dialogs import AddScattPlotDialog, ExportCategoryRaster
- from iclass.dialogs import IClassGroupDialog
- import grass.script as grass
- from grass.pydispatch.signal import Signal
- class ScattsManager:
- """Main controller
- """
- def __init__(self, guiparent, giface, iclass_mapwin=None):
- self.giface = giface
- self.mapDisp = giface.GetMapDisplay()
- if iclass_mapwin:
- self.mapWin = iclass_mapwin
- else:
- self.mapWin = giface.GetMapWindow()
- self.guiparent = guiparent
- self.show_add_scatt_plot = False
- self.core = Core()
- self.cats_mgr = CategoriesManager(self, self.core)
- self.render_mgr = PlotsRenderingManager(scatt_mgr=self,
- cats_mgr=self.cats_mgr,
- core=self.core)
- self.thread = gThread()
- self.plots = {}
- self.plot_mode = None
- self.pol_sel_mode = [False, None]
- self.data_set = False
- self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
- self.renderingStarted = self.render_mgr.renderingStarted
- self.renderingFinished = self.render_mgr.renderingFinished
- self.computingStarted = Signal("ScattsManager.computingStarted")
- if iclass_mapwin:
- self.digit_conn = IClassDigitConnection(self,
- self.mapWin,
- self.core.CatRastUpdater())
- self.iclass_conn = IClassConnection(self,
- iclass_mapwin.parent,
- self.cats_mgr)
- else:
- self.digit_conn = IMapWinDigitConnection()
- self.iclass_conn = IMapDispConnection(scatt_mgr=self,
- cats_mgr=self.cats_mgr,
- giface=self.giface)
- self._initSettings()
- self.modeSet = Signal("ScattsManager.mondeSet")
- def CleanUp(self):
- self.thread.Terminate()
- # there should be better way hot to clean up the thread
- # than calling the clean up function outside the thread,
- # which still may running
- self.core.CleanUp()
- def CleanUpDone(self):
- for scatt_id, scatt in self.plots.items():
- if scatt['scatt']:
- scatt['scatt'].CleanUp()
- self.plots.clear()
- def _initSettings(self):
- """Initialization of settings (if not already defined)
- """
- # initializes default settings
- initSettings = [
- ['selection', 'sel_pol', (255, 255, 0)],
- ['selection', 'sel_pol_vertex', (255, 0, 0)],
- ['selection', 'sel_area', (0, 255, 19)],
- ['selection', "snap_tresh", 10],
- ['selection', 'sel_area_opacty', 50],
- ['ellipses', 'show_ellips', True],
- ]
- for init in initSettings:
- UserSettings.ReadSettingsFile()
- UserSettings.Append(dict=UserSettings.userSettings,
- group='scatt',
- key=init[0],
- subkey=init[1],
- value=init[2],
- overwrite=False)
- def SetData(self):
- self.iclass_conn.SetData()
- self.digit_conn.SetData()
- def SetBands(self, bands):
- self.busy = wx.BusyInfo(_("Loading data..."))
- self.data_set = False
- self.thread.Run(callable=self.core.CleanUp,
- ondone=lambda event: self.CleanUpDone())
- if self.show_add_scatt_plot:
- show_add = True
- else:
- show_add = False
- self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands)))
- self.all_bands = bands
- self.region = GetRegion()
- ncells = self.region["rows"] * self.region["cols"]
- if ncells > MAX_NCELLS:
- del self.busy
- self.data_set = True
- return
- self.bands = bands[:]
- self.bands_info = {}
- valid_bands = []
- for b in self.bands[:]:
- i = GetRasterInfo(b)
- self.bands_info[b] = i
- if i is not None:
- valid_bands.append(b)
- for i, b in enumerate(valid_bands):
- # name : index in core bands -
- # if not in core bands (not CELL type) -> index = -1
- self.all_bands_to_bands[b] = i
- self.thread.Run(callable=self.core.SetData,
- bands=valid_bands,
- ondone=self.SetDataDone,
- userdata={"show_add": show_add})
- def SetDataDone(self, event):
- del self.busy
- self.data_set = True
- todo = event.ret
- self.bad_bands = event.ret
- bands = self.core.GetBands()
- self.bad_rasts = event.ret
- self.cats_mgr.SetData()
- if event.userdata['show_add']:
- self.AddScattPlot()
- def GetBands(self):
- return self.core.GetBands()
- def AddScattPlot(self):
- if not self.data_set and self.iclass_conn:
- self.show_add_scatt_plot = True
- self.iclass_conn.SetData()
- self.show_add_scatt_plot = False
- return
- if not self.data_set:
- GError(_('No data set.'))
- return
- self.computingStarted.emit()
- bands = self.core.GetBands()
- #added_bands_ids = []
- # for scatt_id in self.plots):
- # added_bands_ids.append[idBandsToidScatt(scatt_id)]
- self.digit_conn.Update()
- ncells = self.region["rows"] * self.region["cols"]
- if ncells > MAX_NCELLS:
- GError(
- _(
- parent=self.guiparent, mmessage=_(
- "Interactive Scatter Plot Tool can not be used.\n"
- "Number of cells (rows*cols) <%d> in current region"
- "is higher than maximum limit <%d>.\n\n"
- "You can reduce number of cells in current region using <g.region> command." %
- (ncells, MAX_NCELLS))))
- return
- elif ncells > WARN_NCELLS:
- dlg = wx.MessageDialog(
- parent=self.guiparent,
- message=_("Number of cells (rows*cols) <%d> in current region is "
- "higher than recommended threshold <%d>.\n"
- "It is strongly advised to reduce number of cells "
- "in current region below recommend threshold.\n "
- "It can be done by <g.region> command.\n\n"
- "Do you want to continue using "
- "Interactive Scatter Plot Tool with this region?"
- % (ncells, WARN_NCELLS)),
- style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
- ret = dlg.ShowModal()
- if ret != wx.ID_YES:
- return
- dlg = AddScattPlotDialog(parent=self.guiparent,
- bands=self.all_bands,
- check_bands_callback=self.CheckBands)
- if dlg.ShowModal() == wx.ID_OK:
- scatt_ids = []
- sel_bands = dlg.GetBands()
- for b_1, b_2 in sel_bands:
- transpose = False
- if b_1 > b_2:
- transpose = True
- tmp_band = b_2
- b_2 = b_1
- b_1 = tmp_band
- b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
- b_2_id = self.all_bands_to_bands[self.all_bands[b_2]]
- scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
- if scatt_id in self.plots:
- continue
- self.plots[scatt_id] = {'transpose': transpose,
- 'scatt': None}
- scatt_ids.append(scatt_id)
- self._addScattPlot(scatt_ids)
- dlg.Destroy()
- def CheckBands(self, b_1, b_2):
- bands = self.core.GetBands()
- added_scatts_ids = self.plots.keys()
- b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
- b_2_id = self.all_bands_to_bands[self.all_bands[b_1]]
- scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
- if scatt_id in added_scatts_ids:
- GWarning(
- parent=self.guiparent, message=_(
- "Scatter plot with same band combination (regardless x y order) "
- "is already displayed."))
- return False
- b_1_name = self.all_bands[b_1]
- b_2_name = self.all_bands[b_2]
- b_1_i = self.bands_info[b_1_name]
- b_2_i = self.bands_info[b_2_name]
- err = ""
- for b in [b_1_name, b_2_name]:
- if self.bands_info[b] is None:
- err += _("Band <%s> is not CELL (integer) type.\n" % b)
- if err:
- GMessage(parent=self.guiparent,
- message=_("Scatter plot cannot be added.\n" + err))
- return False
- mrange = b_1_i['range'] * b_2_i['range']
- if mrange > MAX_SCATT_SIZE:
- GWarning(parent=self.guiparent,
- message=_("Scatter plot cannot be added.\n"
- "Multiple of bands ranges <%s:%d * %s:%d = %d> "
- "is higher than maximum limit <%d>.\n"
- % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'],
- mrange, MAX_SCATT_SIZE)))
- return False
- elif mrange > WARN_SCATT_SIZE:
- dlg = wx.MessageDialog(
- parent=self.guiparent,
- message=_(
- "Multiple of bands ranges <%s:%d * %s:%d = %d> "
- "is higher than recommended limit <%d>.\n"
- "It is strongly advised to reduce range extend of bands"
- "(e. g. using r.rescale) below recommended threshold.\n\n"
- "Do you really want to add this scatter plot?" %
- (b_1_name, b_1_i['range'],
- b_1_name, b_2_i['range'],
- mrange, WARN_SCATT_SIZE)),
- style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
- ret = dlg.ShowModal()
- if ret != wx.ID_YES:
- return False
- return True
- def _addScattPlot(self, scatt_ids):
- self.render_mgr.NewRunningProcess()
- self.thread.Run(callable=self.core.AddScattPlots,
- scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
- def AddScattPlotDone(self, event):
- if not self.data_set:
- return
- scatt_ids = event.kwds['scatt_ids']
- for s_id in scatt_ids:
- trans = self.plots[s_id]['transpose']
- self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(
- scatt_id=s_id, transpose=trans)
- self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
- self.plots[s_id]['scatt'].cursorMove.connect(
- lambda x, y, scatt_id:
- self.cursorPlotMove.emit(x=x, y=y,
- scatt_id=scatt_id))
- if self.plot_mode:
- self.plots[s_id]['scatt'].SetMode(self.plot_mode)
- self.plots[s_id]['scatt'].ZoomToExtend()
- self.render_mgr.RunningProcessDone()
- def PlotClosed(self, scatt_id):
- del self.plots[scatt_id]
- def SetPlotsMode(self, mode):
- self.plot_mode = mode
- for scatt in six.itervalues(self.plots):
- if scatt['scatt']:
- scatt['scatt'].SetMode(mode)
- self.modeSet.emit(mode=mode)
- def ActivateSelectionPolygonMode(self, activate):
- self.pol_sel_mode[0] = activate
- for scatt in six.itervalues(self.plots):
- if not scatt['scatt']:
- continue
- scatt['scatt'].SetSelectionPolygonMode(activate)
- if not activate and self.plot_mode not in [
- 'zoom', 'pan', 'zoom_extend']:
- self.SetPlotsMode(None)
- self.render_mgr.RunningProcessDone()
- return activate
- def ProcessSelectionPolygons(self, process_mode):
- scatts_polygons = {}
- for scatt_id, scatt in six.iteritems(self.plots):
- if not scatt['scatt']:
- continue
- coords = scatt['scatt'].GetCoords()
- if coords is not None:
- scatts_polygons[scatt_id] = coords
- if not scatts_polygons:
- return
- value = 1
- if process_mode == 'remove':
- value = 0
- sel_cat_id = self.cats_mgr.GetSelectedCat()
- if not sel_cat_id:
- dlg = wx.MessageDialog(
- parent=self.guiparent,
- message=_(
- "In order to select arrea in scatter plot, "
- "you have to select class first.\n\n"
- "There is no class yet, "
- "do you want to create one?"),
- caption=_("No class selected"),
- style=wx.YES_NO)
- if dlg.ShowModal() == wx.ID_YES:
- self.iclass_conn.EmptyCategories()
- sel_cat_id = self.cats_mgr.GetSelectedCat()
- if not sel_cat_id:
- return
- for scatt in six.itervalues(self.plots):
- if scatt['scatt']:
- scatt['scatt'].SetEmpty()
- self.computingStarted.emit()
- self.render_mgr.NewRunningProcess()
- self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
- self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
- self.thread.Run(callable=self.core.UpdateCategoryWithPolygons,
- cat_id=sel_cat_id,
- scatts_pols=scatts_polygons,
- value=value, ondone=self.SetEditCatDataDone)
- def SetEditCatDataDone(self, event):
- if not self.data_set:
- return
- self.render_mgr.RunningProcessDone()
- if event.exception:
- GError(
- _("Error occurred during computation of scatter plot category:\n%s"),
- parent=self.guiparent,
- showTraceback=False)
- cat_id = event.ret
- self.iclass_conn.RenderCatRast(cat_id)
- def SettingsUpdated(self, chanaged_setts):
- self.render_mgr.RenderRequest()
- #['ellipses', 'show_ellips']
- def GetCategoriesManager(self):
- return self.cats_mgr
- class PlotsRenderingManager:
- """Manages rendering of scatter plot.
- .. todo::
- still space for optimalization
- """
- def __init__(self, scatt_mgr, cats_mgr, core):
- self.scatt_mgr = scatt_mgr
- self.cats_mgr = cats_mgr
- self.core = core
- self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
- self.runningProcesses = 0
- self.data_to_render = {}
- self.render_queue = []
- self.cat_ids = []
- self.cat_cond_ids = []
- self.renderingStarted = Signal("ScattsManager.renderingStarted")
- self.renderingFinished = Signal("ScattsManager.renderingFinished")
- def AddRenderRequest(self, scatts):
- for scatt_id, cat_ids in scatts:
- if not self.data_to_render.has_key[scatt_id]:
- self.data_to_render = cat_ids
- else:
- for c in cat_ids:
- if c not in self.data_to_render[scatt_id]:
- self.data_to_render[scatt_id].append(c)
- def NewRunningProcess(self):
- self.runningProcesses += 1
- def RunningProcessDone(self):
- self.runningProcesses -= 1
- if self.runningProcesses <= 1:
- self.RenderScattPlts()
- def RenderRequest(self):
- if self.runningProcesses <= 1:
- self.RenderScattPlts()
- def CategoryChanged(self, cat_ids):
- for c in cat_ids:
- if c not in self.cat_ids:
- self.cat_ids.append(c)
- def CategoryCondsChanged(self, cat_ids):
- for c in cat_ids:
- if c not in self.cat_cond_ids:
- self.cat_cond_ids.append(c)
- def RenderScattPlts(self, scatt_ids=None):
- if len(self.render_queue) > 1:
- return
- self.renderingStarted.emit()
- self.render_queue.append(self.scatt_mgr.thread.GetId())
- cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
- cats = self.cats_mgr.GetCategories()[:]
- self.scatt_mgr.thread.Run(
- callable=self._renderscattplts,
- scatt_ids=scatt_ids,
- cats=cats,
- cats_attrs=cats_attrs,
- ondone=self.RenderingDone)
- def _renderscattplts(self, scatt_ids, cats, cats_attrs):
- cats.reverse()
- cats.insert(0, 0)
- for i_scatt_id, scatt in self.scatt_mgr.plots.items():
- if scatt_ids is not None and \
- i_scatt_id not in scatt_ids:
- continue
- if not scatt['scatt']:
- continue
- scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
- if self._showConfEllipses():
- ellipses_dt = self.scatts_dt.GetEllipses(
- i_scatt_id, cats_attrs)
- else:
- ellipses_dt = {}
- for c in six.iterkeys(scatt_dt):
- try:
- self.cat_ids.remove(c)
- scatt_dt[c]['render'] = True
- except:
- scatt_dt[c]['render'] = False
- if self.scatt_mgr.pol_sel_mode[0]:
- self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
- scatt['scatt'].Plot(cats_order=cats,
- scatts=scatt_dt,
- ellipses=ellipses_dt,
- styles=cats_attrs)
- def RenderingDone(self, event):
- self.render_queue.remove(event.pid)
- if not self.render_queue:
- self.renderingFinished.emit()
- def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
- cat_id = self.cats_mgr.GetSelectedCat()
- if not cat_id:
- return
- sel_a_cat_id = -1
- s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
- if not s:
- return
- cats_order.append(sel_a_cat_id)
- col = UserSettings.Get(group='scatt',
- key='selection',
- subkey='sel_area')
- col = ":".join(map(str, col))
- opac = UserSettings.Get(group='scatt',
- key='selection',
- subkey='sel_area_opacty') / 100.0
- cats_attrs[sel_a_cat_id] = {'color': col,
- 'opacity': opac,
- 'show': True}
- scatt_dt[sel_a_cat_id] = s[cat_id]
- scatt_dt[sel_a_cat_id]['render'] = False
- if cat_id in self.cat_cond_ids:
- scatt_dt[sel_a_cat_id]['render'] = True
- self.cat_cond_ids.remove(cat_id)
- def _showConfEllipses(self):
- return UserSettings.Get(group='scatt',
- key="ellipses",
- subkey="show_ellips")
- class CategoriesManager:
- """Manages categories list of scatter plot.
- """
- def __init__(self, scatt_mgr, core):
- self.core = core
- self.scatt_mgr = scatt_mgr
- self.cats = {}
- self.cats_ids = []
- self.sel_cat_id = None
- self.exportRaster = None
- self.initialized = Signal('CategoriesManager.initialized')
- self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
- self.deletedCategory = Signal('CategoriesManager.deletedCategory')
- self.addedCategory = Signal('CategoriesManager.addedCategory')
- def ChangePosition(self, cat_id, new_pos):
- if new_pos >= len(self.cats_ids):
- return False
- try:
- pos = self.cats_ids.index(cat_id)
- except:
- return False
- if pos > new_pos:
- pos -= 1
- self.cats_ids.remove(cat_id)
- self.cats_ids.insert(new_pos, cat_id)
- self.scatt_mgr.render_mgr.RenderRequest()
- return True
- def _addCategory(self, cat_id):
- self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
- cat_id=cat_id)
- def SetData(self):
- if not self.scatt_mgr.data_set:
- return
- for cat_id in self.cats_ids:
- self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
- cat_id=cat_id)
- def AddCategory(self, cat_id=None, name=None, color=None, nstd=None):
- if cat_id is None:
- if self.cats_ids:
- cat_id = max(self.cats_ids) + 1
- else:
- cat_id = 1
- if self.scatt_mgr.data_set:
- self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
- cat_id=cat_id)
- # TODO check number of cats
- # if ret < 0: #TODO
- # return -1;
- self.cats[cat_id] = {
- 'name': 'class_%d' % cat_id,
- 'color': "0:0:0",
- 'opacity': 1.0,
- 'show': True,
- 'nstd': 1.0,
- }
- self.cats_ids.insert(0, cat_id)
- if name is not None:
- self.cats[cat_id]["name"] = name
- if color is not None:
- self.cats[cat_id]["color"] = color
- if nstd is not None:
- self.cats[cat_id]["nstd"] = nstd
- self.addedCategory.emit(cat_id=cat_id,
- name=self.cats[cat_id]["name"],
- color=self.cats[cat_id]["color"])
- return cat_id
- def SetCategoryAttrs(self, cat_id, attrs_dict):
- render = False
- update_cat_rast = []
- for k, v in six.iteritems(attrs_dict):
- if not render and k in ['color', 'opacity', 'show', 'nstd']:
- render = True
- if k in ['color', 'name']:
- update_cat_rast.append(k)
- self.cats[cat_id][k] = v
- if render:
- self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
- self.scatt_mgr.render_mgr.RenderRequest()
- if update_cat_rast:
- self.scatt_mgr.iclass_conn.UpdateCategoryRaster(
- cat_id, update_cat_rast)
- self.setCategoryAttrs.emit(cat_id=cat_id, attrs_dict=attrs_dict)
- def DeleteCategory(self, cat_id):
- if self.scatt_mgr.data_set:
- self.scatt_mgr.thread.Run(callable=self.core.DeleteCategory,
- cat_id=cat_id)
- del self.cats[cat_id]
- self.cats_ids.remove(cat_id)
- self.deletedCategory.emit(cat_id=cat_id)
- # TODO emit event?
- def SetSelectedCat(self, cat_id):
- self.sel_cat_id = cat_id
- if self.scatt_mgr.pol_sel_mode[0]:
- self.scatt_mgr.render_mgr.RenderRequest()
- def GetSelectedCat(self):
- return self.sel_cat_id
- def GetCategoryAttrs(self, cat_id):
- #TODO is mutable
- return self.cats[cat_id]
- def GetCategoriesAttrs(self):
- #TODO is mutable
- return self.cats
- def GetCategories(self):
- return self.cats_ids[:]
- def SetCategoryPosition(self):
- if newindex > oldindex:
- newindex -= 1
- self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
- def ExportCatRast(self, cat_id):
- cat_attrs = self.GetCategoryAttrs(cat_id)
- dlg = ExportCategoryRaster(
- parent=self.scatt_mgr.guiparent,
- rasterName=self.exportRaster,
- title=_("Export scatter plot raster of class <%s>") %
- cat_attrs['name'])
- if dlg.ShowModal() == wx.ID_OK:
- self.exportCatRast = dlg.GetRasterName()
- dlg.Destroy()
- self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
- userdata={'name': cat_attrs['name']},
- cat_id=cat_id,
- rast_name=self.exportCatRast,
- ondone=self.OnExportCatRastDone)
- def OnExportCatRastDone(self, event):
- ret, err = event.ret
- if ret == 0:
- cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
- GMessage(
- _("Scatter plot raster of class <%s> exported to raster map <%s>.") %
- (event.userdata['name'], event.kwds['rast_name']))
- else:
- GMessage(
- _("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") %
- (event.userdata['name'], event.kwds['rast_name'], err))
- class IMapWinDigitConnection:
- """Manage communication of the scatter plot with digitizer in
- mapwindow (does not work).
- """
- def Update(self):
- pass
- def SetData(self):
- pass
- class IClassDigitConnection:
- """Manages communication of the scatter plot with digitizer in
- wx.iclass.
- """
- def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
- self.mapWin = mapWin
- self.vectMap = None
- self.scatt_rast_updater = scatt_rast_updater
- self.scatt_mgr = scatt_mgr
- self.cats_mgr = scatt_mgr.cats_mgr
- self.cats_to_update = []
- self.pids = {'mapwin_conn': []}
- self.thread = self.scatt_mgr.thread
- # TODO
- self.mapWin.parent.toolbars[
- "vdigit"].editingStarted.connect(self.DigitDataChanged)
- def Update(self):
- self.thread.Run(callable=self.scatt_rast_updater.SyncWithMap)
- def SetData(self):
- self.cats_to_update = []
- self.pids = {'mapwin_conn': []}
- def _connectSignals(self):
- self.digit.featureAdded.connect(self.AddFeature)
- self.digit.areasDeleted.connect(self.DeleteAreas)
- self.digit.featuresDeleted.connect(self.DeleteAreas)
- self.digit.vertexMoved.connect(self.EditedFeature)
- self.digit.vertexRemoved.connect(self.EditedFeature)
- self.digit.lineEdited.connect(self.EditedFeature)
- self.digit.featuresMoved.connect(self.EditedFeature)
- def AddFeature(self, new_bboxs, new_areas_cats):
- if not self.scatt_mgr.data_set:
- return
- self.scatt_mgr.computingStarted.emit()
- self.pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
- new_bboxs=new_bboxs,
- old_bboxs=[],
- old_areas_cats=[],
- new_areas_cats=new_areas_cats,
- ondone=self.OnDone)
- def DeleteAreas(self, old_bboxs, old_areas_cats):
- if not self.scatt_mgr.data_set:
- return
- self.scatt_mgr.computingStarted.emit()
- self.pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
- new_bboxs=[],
- old_bboxs=old_bboxs,
- old_areas_cats=old_areas_cats,
- new_areas_cats=[],
- ondone=self.OnDone)
- def EditedFeature(self, new_bboxs, new_areas_cats,
- old_bboxs, old_areas_cats):
- if not self.scatt_mgr.data_set:
- return
- self.scatt_mgr.computingStarted.emit()
- self.pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable=self.scatt_rast_updater.EditedFeature,
- new_bboxs=new_bboxs,
- old_bboxs=old_bboxs,
- old_areas_cats=old_areas_cats,
- new_areas_cats=new_areas_cats,
- ondone=self.OnDone)
- def DigitDataChanged(self, vectMap, digit):
- self.digit = digit
- self.vectMap = vectMap
- self.digit.EmitSignals(emit=True)
- self.scatt_rast_updater.SetVectMap(vectMap)
- self._connectSignals()
- def OnDone(self, event):
- if not self.scatt_mgr.data_set:
- return
- self.pids['mapwin_conn'].remove(event.pid)
- updated_cats = event.ret
- for cat in updated_cats:
- if cat not in self.cats_to_update:
- self.cats_to_update.append(cat)
- if not self.pids['mapwin_conn']:
- self.thread.Run(
- callable=self.scatt_mgr.core.ComputeCatsScatts,
- cats_ids=self.cats_to_update[:],
- ondone=self.Render)
- del self.cats_to_update[:]
- def Render(self, event):
- self.scatt_mgr.render_mgr.RenderScattPlts()
- class IMapDispConnection:
- """Manage comunication of the scatter plot with mapdisplay in mapwindow.
- """
- def __init__(self, scatt_mgr, cats_mgr, giface):
- self.scatt_mgr = scatt_mgr
- self.cats_mgr = cats_mgr
- self.set_g = {'group': None, 'subg': None}
- self.giface = giface
- self.added_cats_rasts = {}
- def SetData(self):
- dlg = IClassGroupDialog(self.scatt_mgr.guiparent,
- group=self.set_g['group'],
- subgroup=self.set_g['subg'])
- bands = []
- while True:
- if dlg.ShowModal() == wx.ID_OK:
- bands = dlg.GetGroupBandsErr(parent=self.scatt_mgr.guiparent)
- if bands:
- name, s = dlg.GetData()
- group = grass.find_file(name=name, element='group')
- self.set_g['group'] = group['name']
- self.set_g['subg'] = s
- break
- else:
- break
- dlg.Destroy()
- self.added_cats_rasts = {}
- if bands:
- self.scatt_mgr.SetBands(bands)
- def EmptyCategories(self):
- return None
- def UpdateCategoryRaster(self, cat_id, attrs, render=True):
- cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
- if not grass.find_file(cat_rast, element='cell', mapset='.')['file']:
- return
- cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
- if "color" in attrs:
- ret, err_msg = RunCommand('r.colors',
- map=cat_rast,
- rules="-",
- stdin="1 %s" % cats_attrs["color"],
- getErrorMsg=True)
- if ret != 0:
- GError("r.colors failed\n%s" % err_msg)
- if render:
- self.giface.updateMap.emit()
- if "name" in attrs:
- # TODO hack
- self.giface.GetLayerList()._tree.SetItemText(
- self.added_cats_rasts[cat_id], cats_attrs['name'])
- cats_attrs["name"]
- def RenderCatRast(self, cat_id):
- if not cat_id in six.iterkeys(self.added_cats_rasts):
- cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
- cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
- self.UpdateCategoryRaster(cat_id, ['color'], render=False)
- cmd = ['d.rast', 'map=%s' % cat_rast]
- # TODO HACK
- layer = self.giface.GetLayerList()._tree.AddLayer(ltype="raster",
- lname=cat_name,
- lcmd=cmd,
- lchecked=True)
- self.added_cats_rasts[cat_id] = layer
- else: # TODO settings
- self.giface.updateMap.emit()
- class IClassConnection:
- """Manage comunication of the scatter plot with mapdisplay in wx.iclass.
- """
- def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
- self.iclass_frame = iclass_frame
- self.stats_data = self.iclass_frame.stats_data
- self.cats_mgr = cats_mgr
- self.scatt_mgr = scatt_mgr
- self.added_cats_rasts = []
- self.stats_data.statisticsAdded.connect(self.AddCategory)
- self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
- self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
- self.stats_data.statisticsSet.connect(self.SetCategory)
- self.iclass_frame.groupSet.connect(self.GroupSet)
- self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
- self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
- self.cats_mgr.addedCategory.connect(self.AddStatistics)
- self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
- self.SyncCats()
- def UpdateCategoryRaster(self, cat_id, attrs, render=True):
- if not self.scatt_mgr.data_set:
- return
- cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
- if not cat_rast:
- return
- if not grass.find_file(cat_rast, element='cell', mapset='.')['file']:
- return
- cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
- train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
- if "color" in attrs:
- ret, err_msg = RunCommand('r.colors',
- map=cat_rast,
- rules="-",
- stdin="1 %s" % cats_attrs["color"],
- getErrorMsg=True)
- if ret != 0:
- GError("r.colors failed\n%s" % err_msg)
- if render:
- train_mgr.Render()
- if "name" in attrs:
- cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
- train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
- cats_attrs["name"]
- def RenderCatRast(self, cat_id):
- train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
- if not cat_id in self.added_cats_rasts:
- cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
- cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
- self.UpdateCategoryRaster(cat_id, ['color'], render=False)
- train_mgr.AddLayer(cat_rast, alias=cat_name)
- self.added_cats_rasts.append(cat_id)
- else: # TODO settings
- train_mgr.Render()
- def SetData(self):
- self.iclass_frame.AddBands()
- self.added_cats_rasts = []
- def EmptyCategories(self):
- self.iclass_frame.OnCategoryManager(None)
- def SyncCats(self, cats_ids=None):
- self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
- cats = self.stats_data.GetCategories()
- for c in cats:
- if cats_ids and c not in cats_ids:
- continue
- stats = self.stats_data.GetStatistics(c)
- self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
- self.cats_mgr.addedCategory.connect(self.AddStatistics)
- def CategoryChanged(self, cat):
- self.cats_mgr.SetSelectedCat(cat)
- def AddCategory(self, cat, name, color):
- self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
- stats = self.stats_data.GetStatistics(cat)
- self.cats_mgr.AddCategory(
- cat_id=cat,
- name=name,
- color=color,
- nstd=stats.nstd)
- self.cats_mgr.addedCategory.connect(self.AddStatistics)
- def DeleteCategory(self, cat):
- self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
- self.cats_mgr.DeleteCategory(cat)
- self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
- def DeletAllCategories(self):
- self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
- cats = self.stats_data.GetCategories()
- for c in cats:
- self.cats_mgr.DeleteCategory(c)
- self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
- def SetCategory(self, cat, stats):
- self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
- cats_attr = {}
- for attr in ['name', 'color', 'nstd']:
- if attr in stats:
- cats_attr[attr] = stats[attr]
- if cats_attr:
- self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
- self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
- def SetStatistics(self, cat_id, attrs_dict):
- self.stats_data.statisticsSet.disconnect(self.SetCategory)
- self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
- self.stats_data.statisticsSet.connect(self.SetCategory)
- def AddStatistics(self, cat_id, name, color):
- self.stats_data.statisticsAdded.disconnect(self.AddCategory)
- self.stats_data.AddStatistics(cat_id, name, color)
- self.stats_data.statisticsAdded.connect(self.AddCategory)
- def DeleteStatistics(self, cat_id):
- self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
- self.stats_data.DeleteStatistics(cat_id)
- self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
- def GroupSet(self, group, subgroup):
- kwargs = {}
- if subgroup:
- kwargs['subgroup'] = subgroup
- res = RunCommand('i.group',
- flags='g',
- group=group,
- read=True, **kwargs).strip()
- if res.splitlines()[0]:
- bands = res.splitlines()
- self.scatt_mgr.SetBands(bands)
|