1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195 |
- """
- @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)
- """
- from copy import deepcopy
- import wx
- import six
- from core.gcmd import 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 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 cat_id not 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 cat_id not 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)
|