123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206 |
- """!
- @package iscatt.controllers
- @brief Controller layer wx.iscatt.
- Classes:
- - controllers::ScattsManager
- - controllers::PlotsRenderingManager
- - controllers::CategoriesManager
- - controllers::IMapWinDigitConnection
- - controllers::IClassDigitConnection
- - controllers::IMapDispConnection
- - controllers::IClassConnection
- - controllers::gThread
- (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 time
- import threading
- import Queue
- from core.gconsole import EVT_CMD_DONE
- from core.gcmd import GException, GError, GMessage, RunCommand, GWarning
- from core.settings import UserSettings
- from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
- 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 bellow 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 self.plots.has_key(scatt_id):
- 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" % self.all_bands[b_1])
- 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) bellow 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 self.plots.itervalues():
- 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 self.plots.itervalues():
- 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 self.plots.iteritems():
- 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 self.plots.itervalues():
- 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 occured 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 scatt_dt.iterkeys():
- 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 attrs_dict.iteritems():
- 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 self.added_cats_rasts.iterkeys():
- 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 stats.has_key(attr):
- 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.split('\n')[0]:
- bands = res.split('\n')
- self.scatt_mgr.SetBands(bands)
- class gThread(threading.Thread, wx.EvtHandler):
- """!Thread for scatter plot backend"""
- requestId = 0
- def __init__(self, requestQ=None, resultQ=None, **kwds):
- wx.EvtHandler.__init__(self)
- self.terminate = False
- threading.Thread.__init__(self, **kwds)
- if requestQ is None:
- self.requestQ = Queue.Queue()
- else:
- self.requestQ = requestQ
- if resultQ is None:
- self.resultQ = Queue.Queue()
- else:
- self.resultQ = resultQ
- #self.setDaemon(True)
- self.Bind(EVT_CMD_DONE, self.OnDone)
- self.start()
- def Run(self, *args, **kwds):
- """!Run command in queue
- @param args unnamed command arguments
- @param kwds named command arguments
- @return request id in queue
- """
- gThread.requestId += 1
- self.requestQ.put((gThread.requestId, args, kwds))
- return gThread.requestId
- def GetId(self):
- """!Get id for next command"""
- return gThread.requestId + 1
- def SetId(self, id):
- """!Set starting id"""
- gThread.requestId = id
- def run(self):
- while True:
- requestId, args, kwds = self.requestQ.get()
- for key in ('callable', 'ondone', 'userdata'):
- if key in kwds:
- vars()[key] = kwds[key]
- del kwds[key]
- else:
- vars()[key] = None
- requestTime = time.time()
- ret = None
- exception = None
- time.sleep(.1)
- if self.terminate:
- return
- ret = vars()['callable'](*args, **kwds)
- if self.terminate:
- return
- #except Exception as e:
- # exception = e;
- self.resultQ.put((requestId, ret))
- event = wxCmdDone(ondone=vars()['ondone'],
- kwds=kwds,
- args=args, #TODO expand args to kwds
- ret=ret,
- exception=exception,
- userdata=vars()['userdata'],
- pid=requestId)
- # send event
- wx.PostEvent(self, event)
- def OnDone(self, event):
- if event.ondone:
- event.ondone(event)
- def Terminate(self):
- """!Abort command(s)"""
- self.terminate = True
|