[GRASS-SVN] r57286 - in sandbox/turek/scatter_plot: . gui gui/wxpython gui/wxpython/scatt_plot include include/defs lib/imagery
svn_grass at osgeo.org
svn_grass at osgeo.org
Fri Jul 26 18:33:02 PDT 2013
Author: turek
Date: 2013-07-26 18:33:01 -0700 (Fri, 26 Jul 2013)
New Revision: 57286
Added:
sandbox/turek/scatter_plot/gui/
sandbox/turek/scatter_plot/gui/wxpython/
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/gthreading.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/sc_pl_core.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
sandbox/turek/scatter_plot/testing_pach.diff
Modified:
sandbox/turek/scatter_plot/include/defs/imagery.h
sandbox/turek/scatter_plot/include/imagery.h
sandbox/turek/scatter_plot/lib/imagery/scatt.c
sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
Log:
scatter plot: wx.iclass alpha version
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,8 @@
+all = [
+ 'dialogs',
+ 'gthreading',
+ 'plots',
+ 'sc_pl_core',
+ 'toolbars',
+ 'core_c',
+ ]
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,513 @@
+"""!
+ at package scatt_plot.controllers
+
+ at brief Controller layer for scatter plot tool.
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+#TODO
+import wx
+
+from core.gcmd import GException, GError, RunCommand
+
+from scatt_plot.sc_pl_core import Core, BandsToidScatt
+
+from scatt_plot.gthreading import gThread
+from core.gconsole import EVT_CMD_DONE
+
+from grass.pydispatch.signal import Signal
+
+class ScattsManager(wx.EvtHandler):
+ def __init__(self, guiparent, giface, iclass_mapwin):
+ #TODO remove iclass parameter
+
+ wx.EvtHandler.__init__(self)
+ self.giface = giface
+ self.mapDisp = giface.GetMapDisplay()
+ self.mapWin = giface.GetMapWindow()
+
+ if iclass_mapwin:
+ self.mapWin = iclass_mapwin
+
+ self.guiparent = guiparent
+
+ self.core = Core()
+ self.scatts_dt = self.core.GetScattsData()
+
+ self.cats_mgr = CategoriesManager(self, self.core)
+
+ self.thread = gThread(self);
+
+ self.scatt_plts = {}
+ self.added_cats_rasts = {}
+
+ self.cats_to_update = []
+
+ self.edit_cat_type = None
+
+ self.data_set = False
+
+ if iclass_mapwin:
+ self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
+ self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
+ else:
+ self.mapWin_conn = None
+ self.iclass_conn = None
+
+ self.tasks_pids = {
+ 'add_scatt' : -1,
+ 'set_data' : -1,
+ 'set_edit_cat_data' : -1,
+ 'mapwin_conn' : [],
+ 'render_plots' : -1
+ }
+
+ self.Bind(EVT_CMD_DONE, self.OnThreadDone)
+
+ def SetData(self, bands):
+ self.data_set = False
+ self.tasks_pids['set_data'] = self.thread.GetId()
+ self.thread.Run(callable = self.core.SetData, bands = bands)
+
+ def SetDataDone(self, event):
+ self.data_set = True
+ self.cats_mgr.InitCoreCats()
+
+ def OnOutput(self, event):
+ """!Print thread output according to debug level.
+ """
+ print event.text
+
+ def GetBands(self):
+ return self.core.GetBands()
+
+ def AddScattPlot(self, scatt_id):
+
+ self.tasks_pids['add_scatt'] = self.thread.GetId()
+ self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
+
+ def RenderScattPlts(self):
+
+ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
+ for scatt_id, scatt in self.scatt_plts.iteritems():
+ scatt_dt = self.scatts_dt.GetScatt(scatt_id)
+ scatt.Plot(scatt_dt, cats_attrs)
+
+
+ def AddScattPlotDone(self, event):
+
+ scatt_id = event.kwds['scatt_id']
+
+ #TODO guiparent - not very good
+ self.scatt_plts[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
+
+ if self.edit_cat_type:
+ self.scatt_plts[scatt_id].StartCategoryEdit()
+
+ scatt_dt = self.scatts_dt.GetScatt(scatt_id)
+
+ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
+ self.scatt_plts[scatt_id].Plot(scatt_dt, cats_attrs)
+
+ self.scatt_plts[scatt_id].GetParent().Show()
+
+
+ def CleanUp(self):
+ self.core.CleanUp()
+
+ for scatt_id, scatt in self.scatt_plts.items():
+ scatt.CleanUp()
+ del self.scatt_plts[scatt_id]
+
+ def OnThreadDone(self, event):
+
+ if event.exception:
+ GError(str(event.exception))
+ return
+
+ if event.pid in self.tasks_pids['mapwin_conn']:
+ self.tasks_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.tasks_pids['mapwin_conn']:
+ self.tasks_pids['render_plots'] = self.thread.GetId()
+ self.thread.Run(callable = self.core.ComputeCatScatts,
+ cats_ids = self.cats_to_update[:])
+ del self.cats_to_update[:]
+
+ return
+
+ if self.tasks_pids['render_plots'] == event.pid:
+ #TODO how to put it to the thread - kils gui
+ self.RenderScattPlts()
+ return
+
+ if self.tasks_pids['add_scatt'] == event.pid:
+ self.AddScattPlotDone(event)
+ return
+
+ if self.tasks_pids['set_data'] == event.pid:
+ self.SetDataDone(event)
+ return
+
+ if self.tasks_pids['set_edit_cat_data'] == event.pid:
+ self.SetEditCatDataDone(event)
+ return
+
+ def EditCat(self, edit_type):
+
+ self.edit_cat_type = edit_type
+
+ if self.edit_cat_type:
+ for scatt in self.scatt_plts.itervalues():
+ scatt.StopCategoryEdit()
+ scatt.StartCategoryEdit()
+
+ else:
+ for scatt in self.scatt_plts.itervalues():
+ scatt.StopCategoryEdit()
+
+ def SetEditCatData(self, scatt_id, bbox):
+
+ value = 1
+ if self.edit_cat_type == 'remove':
+ value = 0
+
+ self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
+
+ sel_cat_id = self.cats_mgr.GetSelectedCat()
+
+ self.thread.Run(callable = self.core.SetEditCatData,
+ scatt_id = scatt_id,
+ cat_id = sel_cat_id,
+ bbox = bbox,
+ value = value)
+
+ def SetEditCatDataDone(self, event):
+
+ self.RenderScattPlts()
+
+ cat_id = event.kwds["cat_id"]
+
+ cat_rast = self.core.GetCatRastOut(cat_id)
+
+
+ if cat_rast not in self.added_cats_rasts.values():
+
+ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+ #TODO use standard raster lib
+ name = "cat_%d" % cat_id
+ ret, err_msg = RunCommand('r.external',
+ output = name,
+ getErrorMsg = True,
+ input = cat_rast,
+ flags = "o",
+ overwrite = True)
+
+ if ret != 0:
+ GError(_("r.external failed\n%s" % err_msg))
+
+ region = self.core.GetRegion()
+ ret, err_msg = RunCommand('r.region',
+ map = name,
+ getErrorMsg = True,
+ n = "%f" % region['n'],
+ s = "%f" % region['s'],
+ e = "%f" % region['e'],
+ w = "%f" % region['w'],
+ )
+
+ region = self.core.GetRegion()
+ ret, err_msg = RunCommand('r.colors',
+ map = name,
+ rules = "-",
+ stdin = "1 %s" % cats_attrs["color"],
+ getErrorMsg = True)
+
+ if ret != 0:
+ GError(_("r.region failed\n%s" % err_msg))
+
+ self.mapWin.Map.AddLayer(ltype = "raster", name = "cat_%d" % cat_id, render = True,
+ command = ["d.rast", "map=%s" % name, "values=1"])
+
+ #elif self.giface.GetLayerTree():
+ # self.giface.GetLayerTree().AddLayer("raster", lname = "cat_%d" % cat_id, lchecked = True,
+ # lcmd = ["d.rast", "map=%s" % name, "values=1"])
+
+ if ret != 0:
+ GError(_("r.region failed\n%s" % err_msg))
+
+ self.added_cats_rasts[cat_id] = cat_rast
+
+ self.giface.updateMap.emit()
+
+ def DigitDataChanged(self, vectMap, digit):
+
+ if self.mapWin_conn:
+ self.mapWin_conn.DigitDataChanged(vectMap, digit)
+ return 1
+ else:
+ return 0
+
+ def GetCategoriesManager(self):
+ return self.cats_mgr
+
+class CategoriesManager:
+
+ def __init__(self, scatt_mgr, core):
+
+ self.core = core
+ self.scatt_mgr = scatt_mgr
+
+ self.cats = {}
+ self.cats_ids = []
+
+ self.sel_cat_id = -1
+
+ self.initialized = Signal('CategoriesManager.initialized')
+ self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
+ self.deletedCategory = Signal('CategoriesManager.deletedCategory')
+ self.addedCategory = Signal('CategoriesManager.addedCategory')
+
+ def Clear(self):
+
+ self.cats.clear()
+ del self.cats_ids[:]
+
+ self.sel_cat_id = -1
+
+ def InitCoreCats(self):
+ if self.scatt_mgr.data_set:
+ for cat_id in self.cats_ids:
+ self.core.AddCategory(cat_id)
+
+ def AddCategory(self, cat_id = None, name = None, color = 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:
+ ret = self.core.AddCategory(cat_id)
+ if ret < 0: #TODO
+ return -1;
+
+ self.cats[cat_id] = {
+ 'name' : _('Category %s' % cat_id ),
+ 'color' : "0:0:0"
+ }
+ self.cats_ids.append(cat_id)
+
+ if name is not None:
+ self.cats[cat_id]["name"] = name
+
+ if color is not None:
+ self.cats[cat_id]["color"] = color
+
+ 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):
+ for k, v in attrs_dict.iteritems():
+ self.cats[cat_id][k] = v
+
+ self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
+
+ def DeleteCategory(self, cat_id):
+
+ if self.scatt_mgr.data_set:
+ self.core.DeleteCategory(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
+
+ 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[:]
+
+class MapWinConnection:
+ 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.thread = self.scatt_mgr.thread
+
+ #TODO
+ self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
+
+ #def ChangeMap(self, vectMap, layers_cats):
+ # self.vectMap = vectMap
+ # self.layers_cats = layers_cats
+
+ #ret, region, msg = RunCommand("v.to.rast",
+ # flags = "gp",
+ # getErrorMsg = True,
+ # read = True)
+
+ 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.ChangeVertex)
+ self.digit.vertexRemoved.connect(self.ChangeVertex)
+ self.digit.lineEdited.connect(self.ChangeVertex)
+ self.digit.featuresMoved.connect(self.MoveFeatures)
+
+ def AddFeature(self, new_geom, cat):
+ if not self.scatt_mgr.data_set:
+ return
+
+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.AddedFeature,
+ new_geom = new_geom,
+ cat = cat)
+
+ def DeleteAreas(self, old_geoms, old_areas_cats):
+ if not self.scatt_mgr.data_set:
+ return
+
+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.DeletedAreas,
+ old_geoms = old_geoms,
+ old_areas_cats = old_areas_cats)
+
+ def ChangeVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
+ if not self.scatt_mgr.data_set:
+ return
+
+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.ChangedVertex,
+ new_geom = new_geom,
+ old_geom = old_geom,
+ old_areas_cats = old_areas_cats,
+ new_areas_cats = new_areas_cats)
+
+ def MoveFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
+ if not self.scatt_mgr.data_set:
+ return
+
+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.MovedFeatures,
+ move = move,
+ old_geoms = old_geoms,
+ old_areas_cats = old_areas_cats,
+ new_areas_cats = new_areas_cats)
+
+ def DigitDataChanged(self, vectMap, digit):
+
+ self.digit = digit
+ self.vectMap = vectMap
+
+ self.scatt_rast_updater.SetVectMap(vectMap)
+
+ self._connectSignals()
+
+class IClassConnection:
+ 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.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.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
+ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
+ self.cats_mgr.addedCategory.connect(self.AddStatistics)
+
+ self.SyncCats()
+
+ def SyncCats(self):
+ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
+ cats = self.stats_data.GetCategories()
+ for c in cats:
+ stats = self.stats_data.GetStatistics(c)
+ self.cats_mgr.AddCategory(c, stats.name, stats.color)
+ self.cats_mgr.addedCategory.connect(self.AddStatistics)
+
+ def AddCategory(self, cat, name, color):
+ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
+ self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
+ 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']:
+ 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)
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,211 @@
+"""!
+ at package scatt_plot.scatt_plot
+
+ at brief Functions which work with scatter plot c code.
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+
+from multiprocessing import Process, Queue
+
+from ctypes import *
+try:
+ from grass.lib.imagery import *
+ from grass.lib.gis import Cell_head, G_get_window
+
+except ImportError, e:
+ sys.stderr.write(_("Loading imagery lib failed"))
+
+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out):
+
+ #return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out)
+
+ # Queue object for interprocess communication
+ q = Queue()
+
+ # The separate render process
+ p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
+ n_bands, scatts, cats_rasts_in, cats_rasts_out, q))
+ p.start()
+ ret = q.get()
+ p.join()
+
+ return ret[0], ret[1]
+
+def UpdateCatRast(patch_rast, region, cat_rast):
+
+ #return ComputeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts)
+ # Queue object for interprocess communication
+
+ #_updateCatRastProcess(patch_rast, region, cat_rast, None)
+ #return
+ q = Queue()
+ # The separate render process
+ p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
+ p.start()
+ #ret = q.get()
+ p.join()
+
+ #return ret[0], ret[1]
+
+def CreateCatRast(region, cat_rast):
+
+ cell_head = _regionToCellHead(region)
+ I_create_cat_rast(pointer(cell_head), cat_rast)
+
+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, output_queue):
+
+ #TODO names for types not 0 and 1?
+ sccats_c, vals_dt = _getComputationStruct(scatts, 0, n_bands)
+ scatt_conds_c, vals_dt2 = _getComputationStruct(scatt_conds, 1, n_bands)
+
+ char_bands = _stringListToCharArr(bands)
+ char_cats_rasts_out = _stringListToCharArr(cats_rasts_out)
+ char_cats_rasts_in = _stringListToCharArr(cats_rasts_in)
+
+ cell_head = _regionToCellHead(region)
+
+ ret = I_compute_scatts(pointer(cell_head),
+ pointer(scatt_conds_c),
+ pointer(char_bands),
+ n_bands,
+ pointer(sccats_c),
+ pointer(char_cats_rasts_in),
+ pointer(char_cats_rasts_out))
+
+ I_sc_free_cats(pointer(sccats_c))
+ I_sc_free_cats(pointer(scatt_conds_c))
+
+ output_queue.put((ret, scatts))
+ #return (ret, scatts)
+
+def _regionToCellHead(region):
+ cell_head = struct_Cell_head()
+ G_get_window(pointer(cell_head))
+
+ convert_dict = {'n' : 'north', 'e' : 'east',
+ 'w' : 'west', 's' : 'south',
+ 'nsres' : 'ns_res',
+ 'ewres' : 'ew_res'}
+
+ for k, v in region.iteritems():
+ if k in ["rows", "cols", "cells"]:
+ v = int(v)
+ else:
+ v = float(v)
+
+ if convert_dict.has_key(k):
+ k = convert_dict[k]
+
+ setattr(cell_head, k, v)
+
+ return cell_head
+
+def _stringListToCharArr(str_list):
+
+ arr = c_char_p * len(str_list)
+ char_arr = arr()
+ for i, st in enumerate(str_list):
+ if st:
+ char_arr[i] = st
+ else:
+ char_arr[i] = None
+
+ return char_arr
+
+def _getComputationStruct(cats, cats_type, n_bands):
+
+ sccats = struct_scCats()
+ I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
+
+ #for i in range(sccats.n_a_cats):
+ # cat_id = sccats.cats_ids[i]
+ # I_sc_delete_cat(pointer(sccats), cat_id)
+ # i -= 1
+
+ vals_dt = []
+ for cat_id, scatt_ids in cats.iteritems():
+ I_sc_add_cat(pointer(sccats), cat_id)
+
+ for scatt_id, dt in scatt_ids.iteritems():
+ # if key is missing condition is always True (full scatter plor is computed)
+ if cats[cat_id].has_key(scatt_id):
+
+ vals = dt["np_vals"]
+ #TODO hack
+ vals.shape = (256 * 256)
+
+ c_uint_p = ctypes.POINTER(ctypes.c_uint)
+ scatt_vals = scdScattData()
+ if cats_type == 0:
+ vals[:] = 0
+ scatt_vals.scatt_vals_arr = vals.ctypes.data_as(c_uint_p)
+
+ else:
+ #TODO solve proble with short integer
+ scatt_vals.b_conds_arr = vals.ctypes.data_as(c_uint_p)
+
+ vals_dt.append(scatt_vals)
+
+ #vals_dt.append(data_p)
+ vals.shape = (256, 256)
+ I_sc_insert_scatt_data(pointer(sccats),
+ pointer(scatt_vals),
+ cat_id, scatt_id)
+
+ return sccats, vals_dt
+
+def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
+
+ #TODO names for types not 0 and 1?
+
+ cell_head = _regionToCellHead(region)
+
+
+ cat_rast
+ I_insert_patch_to_cat_rast(patch_rast,
+ pointer(cell_head),
+ cat_rast)
+
+ #print re
+ #output_queue.put((ret, scatts))
+ #return (ret, scatts)
+
+"""
+class A:
+ def __init__(self):
+ self.i = 0
+
+ def incr(self):
+ self.i += 1
+ return self.i
+
+def worker(input, output):
+ print "ahoj"
+ for func, args in iter(input.get, 'STOP'):
+ print "jedu"
+ result = func.incr
+ output.put(result)
+
+def test():
+ # Create queues
+ task_queue = Queue()
+ done_queue = Queue()
+
+ # Start worker processes
+ a = A()
+
+ prc = Process(target=worker, args=(task_queue, done_queue)).start()
+ task_queue.put((a,1))
+
+ data = done_queue.get()
+ print data
+ task_queue.put('STOP')
+"""
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,612 @@
+"""!
+ at package scatt_plot.dialogs
+
+ at brief GUI.
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+import wx.lib.flatnotebook as FN
+import wx.aui
+
+from core import globalvar
+from core.gcmd import GException, GError, RunCommand
+
+from gui_core.gselect import Select
+from gui_core.widgets import GNotebook
+
+from scatt_plot.controllers import ScattsManager
+from scatt_plot.toolbars import MainToolbar, CategoriesToolbar
+from scatt_plot.sc_pl_core import Core, BandsToidScatt
+from scatt_plot.plots import ScatterPlotWidget
+
+
+class ScattPlotMainDialog(wx.Dialog):
+ def __init__(self, parent, giface, iclass_mapwin = None,
+ id = wx.ID_ANY, title = _("GRASS GIS Interactive Scatter Plot Tool"),
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+
+ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+ #TODO remove iclass parameter
+ self.scatt_mgr = ScattsManager(guiparent = self, giface = giface, iclass_mapwin = iclass_mapwin)
+
+ # toobars
+ self.toolbars = {}
+ self.toolbars['mainToolbar'] = MainToolbar(parent = self)
+
+ self.mainPanel = wx.Panel(parent=self)
+ self.notebook = GNotebook(parent = self.mainPanel,
+ style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
+ FN.FNB_NO_X_BUTTON)
+
+ # Fancy gui
+ self._mgr = wx.aui.AuiManager(self)
+ self._addPanes()
+ self._mgr.Update()
+
+ self._createCategoryPage()
+
+ self.edit_cat_tools = {
+ 'editCatAdd' : {
+ 'toggle' : False,
+ 'type' : 'add'
+ },
+ 'editCatRemove' : {
+ 'toggle' : False,
+ 'type' : 'remove'
+ }
+ }
+ self._doLayout()
+ self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
+
+ dlgSize = (220, 400)
+ self.SetMinSize(dlgSize)
+ self.SetInitialSize(dlgSize)
+
+ #fix goutput's pane size (required for Mac OSX)
+ #if self.gwindow:
+ # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
+
+ def _doLayout(self):
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ sizer.Add(item = self.notebook, proportion = 1,
+ flag = wx.EXPAND)
+
+ self.mainPanel.SetSizer(sizer)
+
+ sizer.Fit(self)
+ self.Layout()
+
+ #TODO split to panel?
+ def _createCategoryPage(self):
+ catsPanel = wx.Panel(parent = self)
+
+
+ self.notebook.AddPage(page = catsPanel,
+ text=_('Categories'),
+ name = 'categories')
+
+ self.cats_list = CategoryListCtrl(parent = catsPanel,
+ cats_mgr = self.scatt_mgr.GetCategoriesManager())
+ self.toolbars['catsList'] = CategoriesToolbar(parent = catsPanel,
+ cats_list = self.cats_list,
+ scatts_dlg = self)
+
+ AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
+
+ catsSizer = wx.BoxSizer(wx.VERTICAL)
+
+ catsSizer.Add(item = self.toolbars['catsList'], proportion = 0)
+ catsSizer.Add(item = self.cats_list, proportion = 1, flag = wx.EXPAND)
+
+ catsPanel.SetSizer(catsSizer)
+
+ def _addPanes(self):
+ """!Adds toolbar pane and pane with tabs"""
+ self._mgr.AddPane(self.toolbars['mainToolbar'],
+ wx.aui.AuiPaneInfo().
+ Name("pointlisttools").Caption(_("Point cats_list toolbar")).
+ ToolbarPane().Top().Row(0).
+ Dockable(False).
+ CloseButton(False).Layer(0))
+
+ self._mgr.AddPane(self.mainPanel,
+ wx.aui.AuiPaneInfo().
+ Name("tabs").CaptionVisible(visible = False).
+ Center().
+ Dockable(False).
+ CloseButton(False).Layer(0))
+
+ def OnCloseDialog(self, event):
+ """!Close dialog"""
+ self.scatt_mgr.CleanUp()
+ self.Destroy()
+
+ def OnSettings(self, event):
+ pass
+
+ def OnSetData(self, event):
+
+ dlg = SetDataDialog(parent=self)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ bands = dlg.GetBands()
+ self.scatt_mgr.SetData(bands)
+
+ dlg.Destroy()
+
+
+ def NewScatterPlot(self, scatt_id):
+ #TODO needs to be resolved (should be in this class)
+ scatt_dlg = ScatterPlotDialog(parent = self, scatt_mgr = self.scatt_mgr, scatt_id = scatt_id)
+ return scatt_dlg.GetPlot()
+
+ def OnSettings(self, event):
+ pass
+
+ def OnAddScattPl(self, event):
+
+ bands = self.scatt_mgr.GetBands()
+ if not bands:
+ GError(_('No data set for scatter plot.'))
+ return
+
+ dlg = AddScattPlotDialog(parent = self, bands = bands)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ self.scatt_mgr.AddScattPlot(dlg.GetScattId())
+
+ dlg.Destroy()
+
+ def EditCatAdd(self):
+ self._setEditCat(tool_name = 'editCatAdd')
+
+ def EditCatRemove(self):
+ self._setEditCat(tool_name = 'editCatRemove')
+
+ def _setEditCat(self, tool_name):
+
+ if self.edit_cat_tools[tool_name]['toggle'] == False:
+
+ for i_tool_name in self.edit_cat_tools.iterkeys():
+ if i_tool_name == tool_name:
+ continue
+
+ self.edit_cat_tools[i_tool_name]['toggle'] = False
+
+ toolbar = self.toolbars['catsList']
+ toolbar.ToggleTool(vars(toolbar)[i_tool_name], False)
+
+ self.edit_cat_tools[tool_name]['toggle'] = True
+ self.scatt_mgr.EditCat(edit_type = self.edit_cat_tools[tool_name]['type'])
+ return
+
+ self.scatt_mgr.EditCat(edit_type = None)
+ self.edit_cat_tools[tool_name]['toggle'] = False
+
+ def GetScattMgr(self):
+ return self.scatt_mgr
+
+
+class SetDataDialog(wx.Dialog):
+
+ def __init__(self, parent, id = wx.ID_ANY):
+
+ wx.Dialog.__init__(self, parent, title = ("Select imagery group "), id = id)
+
+ self.bands = []
+
+
+ self._createWidgets()
+ self.group.SetValue("testovaci_1")
+
+ def _createWidgets(self):
+
+ self.labels = {}
+ self.params = {}
+
+ self.group_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name of raster group for input data:"))
+
+ self.group = Select(parent = self, type = 'group',
+ size = globalvar.DIALOG_GSELECT_SIZE)
+
+ # buttons
+ self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
+
+ self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
+
+ self._layout()
+
+ def _layout(self):
+
+ border = wx.BoxSizer(wx.VERTICAL)
+ dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+ regionSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ dialogSizer.Add(item = self._addSelectSizer(title = self.group_label,
+ sel = self.group))
+
+ # buttons
+ self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+
+ self.btnsizer.Add(item = self.btn_close, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ self.btnsizer.Add(item = self.btn_ok, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ dialogSizer.Add(item = self.btnsizer, proportion = 0,
+ flag = wx.ALIGN_CENTER)
+
+ border.Add(item = dialogSizer, proportion = 0,
+ flag = wx.ALL, border = 5)
+
+ self.SetSizer(border)
+ self.Layout()
+ self.Fit()
+
+ # bindings
+ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+ self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+
+ def _addSelectSizer(self, title, sel):
+ """!Helper layout function.
+ """
+ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
+ selTitleSizer.Add(item = title, proportion = 1,
+ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+
+ selSizer.Add(item = selTitleSizer, proportion = 0,
+ flag = wx.EXPAND)
+
+ selSizer.Add(item = sel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+
+ return selSizer
+
+ def GetBands(self):
+
+ return self.bands
+
+ def OnClose(self, event):
+ """!Close dialog
+ """
+ if not self.IsModal():
+ self.Destroy()
+ event.Skip()
+
+ def OnOk(self, event):
+ """!
+ """
+ ret, stdout, err_msg = RunCommand("i.group",
+ getErrorMsg = True,
+ read = True,
+ quiet = True,
+ group = self.group.GetValue().strip(),
+ flags = 'g')
+
+ if ret != 0:
+ GError("%s module failed:\n%s" % ("<i.group>", err_msg))
+ return
+
+ self.bands = stdout.split('\n')
+
+ for band in self.bands[:]:
+ if not band:
+ self.bands.remove(band)
+
+ event.Skip()
+
+ #TODO catch event or...
+ def GetSelectedCatId(self):
+
+ return self.cats_list.GetSelectedCatId()
+
+class AddScattPlotDialog(wx.Dialog):
+
+ def __init__(self, parent, bands, id = wx.ID_ANY):
+
+ wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
+
+ self.bands = bands
+
+ self.scatt_id = None
+
+ self._createWidgets()
+
+ def _createWidgets(self):
+
+ self.labels = {}
+ self.params = {}
+
+ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 1:"))
+
+ self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = self.bands,
+ style = wx.CB_READONLY, size = (350, 30))
+
+ self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 2:"))
+
+ self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = self.bands,
+ style = wx.CB_READONLY, size = (350, 30))
+
+ # buttons
+ self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
+
+ self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
+
+ self._layout()
+
+ def _layout(self):
+
+ border = wx.BoxSizer(wx.VERTICAL)
+ dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+ regionSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label,
+ sel = self.band_1_ch))
+
+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label,
+ sel = self.band_2_ch))
+
+ # buttons
+ self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+
+ self.btnsizer.Add(item = self.btn_close, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ self.btnsizer.Add(item = self.btn_ok, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ dialogSizer.Add(item = self.btnsizer, proportion = 0,
+ flag = wx.ALIGN_CENTER)
+
+ border.Add(item = dialogSizer, proportion = 0,
+ flag = wx.ALL, border = 5)
+
+ self.SetSizer(border)
+ self.Layout()
+ self.Fit()
+
+ # bindings
+ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+ self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+
+ def _addSelectSizer(self, title, sel):
+ """!Helper layout function.
+ """
+ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
+ selTitleSizer.Add(item = title, proportion = 1,
+ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+
+ selSizer.Add(item = selTitleSizer, proportion = 0,
+ flag = wx.EXPAND)
+
+ selSizer.Add(item = sel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+
+ return selSizer
+
+ def OnClose(self, event):
+ """!Close dialog
+ """
+ if not self.IsModal():
+ self.Destroy()
+ event.Skip()
+
+ def OnOk(self, event):
+ """!
+ """
+ band_1 = self.band_1_ch.GetSelection()
+ band_2 = self.band_2_ch.GetSelection()
+
+
+ if band_1 == band_2:
+ GError(_("Selected bands must be different."))
+ return
+
+ #TODO axes selection
+ if band_1 > band_2:
+ tmp_band = band_2
+ band_2 = band_1
+ band_1 = band_2
+
+ self.scatt_id = BandsToidScatt(band_1, band_2, len(self.bands))
+
+ event.Skip()
+
+ def GetScattId(self):
+ return self.scatt_id
+
+class ScatterPlotDialog(wx.Dialog):
+ def __init__(self, parent, scatt_mgr, scatt_id,
+ id = wx.ID_ANY, title = _("Test plot"),
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+
+ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
+ self.scatt = ScatterPlotWidget(parent = self,
+ scatt_mgr = scatt_mgr,
+ scatt_id = scatt_id)
+
+ def GetPlot(self):
+ return self.scatt
+
+ def _doLayout(self):
+ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.main_sizer.Add(self.self)
+
+ self.panel.SetSizer(self.main_sizer)
+ self.main_sizer.Fit(self)
+
+ def OnPlotClosed(self, scatt_id):
+ self.Destroy()
+
+class CategoryListCtrl(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin,
+ listmix.TextEditMixin):
+
+ def __init__(self, parent, cats_mgr, id = wx.ID_ANY):
+
+ wx.ListCtrl.__init__(self, parent, id,
+ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
+ self.columns = ((_('Category name'), 'name'),
+ (_('Color'), 'color'))
+ self.Populate(columns = self.columns)
+
+ self.cats_mgr = cats_mgr
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ self.rightClickedItemIdx = wx.NOT_FOUND
+
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+ listmix.TextEditMixin.__init__(self)
+
+ self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
+
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
+ self.cats_mgr.deletedCategory.connect(self.Update)
+ self.cats_mgr.addedCategory.connect(self.Update)
+
+ def Update(self, **kwargs):
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def InitCoreCats(self):
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def SetVirtualData(self, row, column, text):
+ attr = self.columns[column][1]
+ if attr == 'name':
+ try:
+ text.encode('ascii')
+ except UnicodeEncodeError:
+ GMessage(parent = self, message = _("Please use only ASCII characters."))
+ return
+
+ cat_id = self.cats_mgr.GetCategories()[row]
+
+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+ self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
+
+ self.Select(row)
+
+ def Populate(self, columns):
+ for i, col in enumerate(columns):
+ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+
+ self.SetColumnWidth(0, 100)
+ self.SetColumnWidth(1, 100)
+
+ def AddCategory(self):
+
+ self.cats_mgr.addedCategory.disconnect(self.Update)
+ cat_id = self.cats_mgr.AddCategory()
+ self.cats_mgr.addedCategory.connect(self.Update)
+
+ if cat_id < 0:
+ GError(_("Maximum limit of categories number was reached."))
+ return
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ def DeleteCategory(self):
+ indexList = sorted(self.GetSelectedIndices(), reverse = True)
+ cats = []
+ for i in indexList:
+ # remove temporary raster
+ cat_id = self.cats_mgr.GetCategories()[i]
+
+ cats.append(cat_id)
+
+ self.cats_mgr.deletedCategory.disconnect(self.Update)
+ self.cats_mgr.DeleteCategory(cat_id)
+ self.cats_mgr.deletedCategory.connect(self.Update)
+
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ def OnSel(self, event):
+ self.cats_mgr.SetSelectedCat(event.GetIndex() + 1)
+
+ event.Skip()
+
+ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
+ indices = []
+ lastFound = -1
+ while True:
+ index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
+ if index == -1:
+ break
+ else:
+ lastFound = index
+ indices.append(index)
+ return indices
+
+ def OnEdit(self, event):
+ currentItem = event.m_itemIndex
+ currentCol = event.m_col
+
+ if currentCol == 1:
+ dlg = wx.ColourDialog(self)
+ dlg.GetColourData().SetChooseFull(True)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ color = dlg.GetColourData().GetColour().Get()
+ color = ':'.join(map(str, color))
+ self.SetVirtualData(currentItem, currentCol, color)
+ dlg.Destroy()
+ wx.CallAfter(self.SetFocus)
+
+ event.Skip()
+
+
+ def DeselectAll(self):
+ """!Deselect all items"""
+ indexList = self.GetSelectedIndices()
+ for i in indexList:
+ self.Select(i, on = 0)
+
+ # no highlight
+ self.OnCategorySelected(None)
+
+ def OnGetItemText(self, item, col):
+ attr = self.columns[col][1]
+ cat_id = self.cats_mgr.GetCategories()[item]
+
+ return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
+
+ def OnGetItemImage(self, item):
+ return -1
+
+ def OnGetItemAttr(self, item):
+ return None
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/gthreading.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/gthreading.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/gthreading.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,133 @@
+"""!
+ at package scatt_plot.gthreading
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+
+import os
+import sys
+import time
+import threading
+import Queue
+
+import wx
+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
+
+class gThread(threading.Thread):
+ """!Thread for GRASS commands"""
+ requestId = 0
+
+ def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
+ """!
+ @param receiver event receiver (used in PostEvent)
+ """
+ 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.receiver = receiver
+ self._want_abort_all = False
+
+ 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):
+ os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
+ while True:
+ requestId, args, kwds = self.requestQ.get()
+ for key in ('callable', 'onDone', 'onPrepare', 'userData'):
+ if key in kwds:
+ vars()[key] = kwds[key]
+ del kwds[key]
+ else:
+ vars()[key] = None
+
+ requestTime = time.time()
+
+ #if self._want_abort_all and self.requestCmd is not None:
+ # self.requestCmd.abort()
+ # if self.requestQ.empty():
+ # self._want_abort_all = False
+
+ # prepare
+ if self.receiver:
+ event = wxCmdPrepare(type = 'method',
+ time=requestTime,
+ pid=requestId)
+
+ wx.PostEvent(self.receiver, event)
+
+ # run command
+ event = wxCmdRun(type = 'method',
+ pid=requestId)
+
+ wx.PostEvent(self.receiver, event)
+
+ time.sleep(.1)
+
+ ret = None
+ exception = None
+ #try:
+ ret = vars()['callable'](*args, **kwds)
+ #except Exception as e:
+ # exception = e;
+
+ self.resultQ.put((requestId, ret))
+
+ time.sleep(.1)
+
+
+ if self.receiver:
+ event = wxCmdDone(type = 'cmd',
+ kwds = kwds,
+ args = args, #TODO expand args to kwds
+ ret=ret,
+ exception=exception,
+ pid=requestId)
+
+ # send event
+ wx.PostEvent(self.receiver, event)
+
+ def abort(self, abortall=True):
+ """!Abort command(s)"""
+ if abortall:
+ self._want_abort_all = True
+ if self.requestQ.empty():
+ self._want_abort_all = False
\ No newline at end of file
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,185 @@
+"""!
+ at package scatt_plot.dialogs
+
+ at brief Ploting widgets.
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import wx
+import numpy as np
+import random
+try:
+ haveMatPlot = True
+ import matplotlib
+ matplotlib.use('WXAgg')
+ from matplotlib.figure import Figure
+ from matplotlib.backends.backend_wxagg import \
+ FigureCanvasWxAgg as FigCanvas, \
+ NavigationToolbar2WxAgg as NavigationToolbar
+except ImportError:
+ haveMatPlot = False
+
+class ScatterPlotWidget(wx.Panel):
+ def __init__(self, parent, scatt_id, scatt_mgr,
+ id = wx.ID_ANY):
+
+ wx.Panel.__init__(self, parent, id)
+
+ self.parent = parent
+
+ self._createWidgets()
+ self._doLayout()
+ self.scatt_id = scatt_id
+ self.scatt_mgr = scatt_mgr
+ self.press_coords = None
+
+ self.cidpress = None
+ self.cidrelease = None
+
+ def StartCategoryEdit(self):
+ 'connect to all the events we need'
+ self.cidpress = self.canvas.mpl_connect(
+ 'button_press_event', self.OnPress)
+ self.cidrelease = self.canvas.mpl_connect(
+ 'button_release_event', self.OnRelease)
+ #self.cidmotion = self.canvas.mpl_connect(
+ # 'motion_notify_event', self.OnRelease)
+
+ def OnPress(self, event):
+ 'on button press we will see if the mouse is over us and store some data'
+
+ if event.xdata and event.ydata:
+ self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
+ else:
+ self.press_coords = None
+
+ def OnRelease(self, event):
+ 'on release we reset the press data'
+
+ if event.xdata and event.ydata and self.press_coords:
+
+ bbox = {}
+ if event.ydata > self.press_coords['y']:
+ bbox['up_y'] = event.ydata
+ bbox['btm_y'] = self.press_coords['y']
+ else:
+ bbox['up_y'] = self.press_coords['y']
+ bbox['btm_y'] = event.ydata
+
+ if event.xdata > self.press_coords['x']:
+ bbox['up_x'] = event.xdata
+ bbox['btm_x'] = self.press_coords['x']
+ else:
+ bbox['up_x'] = self.press_coords['x']
+ bbox['btm_x'] = event.xdata
+
+ self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
+
+ def StopCategoryEdit(self):
+ 'disconnect all the stored connection ids'
+
+ if self.cidpress:
+ self.canvas.mpl_disconnect(self.cidpress)
+ if self.cidrelease:
+ self.canvas.mpl_disconnect(self.cidrelease)
+ #self.canvas.mpl_disconnect(self.cidmotion)
+
+ def _createWidgets(self):
+
+ # Create the mpl Figure and FigCanvas objects.
+ # 5x4 inches, 100 dots-per-inch
+ #
+ self.dpi = 100
+ self.fig = Figure((5.0, 4.0), dpi=self.dpi)
+ self.canvas = FigCanvas(self, -1, self.fig)
+
+ # Since we have only one plot, we can use add_axes
+ # instead of add_subplot, but then the subplot
+ # configuration tool in the navigation toolbar wouldn't
+ # work.
+ #
+ self.axes = self.fig.add_subplot(111)
+
+ # Bind the 'pick' event for clicking on one of the bars
+ #
+ self.canvas.mpl_connect('button_press_event', self.on_pick)
+
+
+ # Create the navigation toolbar, tied to the canvas
+ #
+ self.toolbar = NavigationToolbar(self.canvas)
+
+ def _doLayout(self):
+
+ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
+ self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
+ self.main_sizer.AddSpacer(10)
+
+ self.SetSizer(self.main_sizer)
+ self.main_sizer.Fit(self)
+
+ def Plot(self, scatts, styles):
+ """ Redraws the figure
+ """
+
+ self.axes.clear()
+ for cat_id, cat in scatts.iteritems():
+ if cat_id == 0:
+ cmap = matplotlib.cm.jet
+ cmap.set_bad('w',1.)
+ cmap._init()
+ cmap._lut[len(cmap._lut) - 1, -1] = 0
+ else:
+ colors = styles[cat_id]['color'].split(":")
+
+ cmap = matplotlib.cm.jet
+ cmap.set_bad('w',1.)
+ cmap._init()
+ cmap._lut[len(cmap._lut) - 1, -1] = 0
+ cmap._lut[:, 0] = int(colors[0])/255.0
+ cmap._lut[:, 1] = int(colors[1])/255.0
+ cmap._lut[:, 2] = int(colors[2])/255.0
+
+ masked_cat = np.ma.masked_less_equal(cat, 0)
+
+ self.axes.imshow(masked_cat, cmap = cmap, interpolation='nearest')
+
+ self.canvas.draw()
+
+ def on_pick(self, event):
+ pass
+ # The event received here is of the type
+ # matplotlib.backend_bases.PickEvent
+ #
+ # It carries lots of information, of which we're using
+ # only a small amount here.
+ #
+ #box_points = event.x
+
+ #msg = "You've clicked on a bar with coords:\n"
+
+ #dlg = wx.MessageDialog(
+ # self,
+ # msg,
+ # "Click!",
+ # wx.OK | wx.ICON_INFORMATION)
+
+ #dlg.ShowModal()
+ #dlg.Destroy()
+
+ def on_exit(self, event):
+
+ self.CleanUp()
+
+ def CleanUp(self):
+
+ self.parent.OnPlotClosed(self.scatt_id)
+ self.Destroy()
\ No newline at end of file
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/sc_pl_core.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/sc_pl_core.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/sc_pl_core.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,631 @@
+"""!
+ at package scatt_plot.scatt_plot
+
+ at brief Non GUI functions.
+
+Classes:
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+import numpy as np
+
+from math import sqrt, ceil, floor
+from copy import deepcopy
+
+from grass.script import core as grass
+from core.gcmd import GException, GError, RunCommand
+
+import grass.script as grass
+
+from core_c import CreateCatRast, ComputeScatts, UpdateCatRast
+
+class Core:
+ def __init__(self):
+
+ self.an_data = AnalyzedData()
+
+ self.scatts_dt = ScattPlotsData(self.an_data)
+ self.scatt_conds_dt = ScattPlotsCondsData(self.an_data)
+
+ self.cat_rast_updater = CatRastUpdater(self.scatts_dt, self.an_data, self)
+
+ def SetData(self, bands):
+ self.an_data.Create(bands)
+
+ n_bands = len(self.GetBands())
+
+ self.scatts_dt.Create(n_bands)
+ self.scatt_conds_dt.Create(n_bands)
+
+ def AddCategory(self, cat_id):
+ self.scatts_dt.AddCategory(cat_id)
+ return self.scatt_conds_dt.AddCategory(cat_id)
+
+ def DeleteCategory(self, cat_id):
+ self.scatts_dt.DeleteCategory(cat_id)
+ self.scatt_conds_dt.DeleteCategory(cat_id)
+
+ def CleanUp(self):
+ self.scatts_dt.CleanUp()
+ self.scatt_conds_dt.CleanUp()
+
+ def GetBands(self):
+ return self.an_data.GetBands()
+
+ def GetScattsData(self):
+ return self.scatts_dt;
+
+ def GetRegion(self):
+ return self.an_data.GetRegion()
+
+ def GetCatRastOut(self, cat_id):
+ return self.scatts_dt.GetCatRastOut(cat_id)
+
+ def AddScattPlot(self, scatt_id):
+
+ self.scatts_dt.AddScattPlot(scatt_id = scatt_id)
+
+ scatt_conds = self.scatt_conds_dt.GetData({0 : []})
+ scatts = self.scatts_dt.GetData({0 : [scatt_id]})
+
+ bands = self.an_data.GetBands()
+ cats_rasts_out = self.scatts_dt.GetCatsRastsOut()
+ cats_rasts_in = self.scatts_dt.GetCatsRastsIn()
+
+ returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
+ len(self.GetBands()), scatts, cats_rasts_in, cats_rasts_out)
+ self.scatts_dt.SetData(scatts)
+
+ def SetEditCatData(self, cat_id, scatt_id, bbox, value):
+
+ if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
+ return False
+
+ arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+
+ for k, v in bbox.iteritems():
+ bbox[k] = self._validExtend(v)
+
+ arr[bbox['btm_y'] : bbox['up_y'], bbox['btm_x'] : bbox['up_x']] = value
+
+ self.ComputeCatScatts([cat_id])
+
+ return True
+
+ def ComputeCatScatts(self, cats_ids):
+
+ requested_dt = {}
+ requested_dt_conds = {}
+
+ for c in cats_ids:
+ requested_dt_conds[c] = self.scatt_conds_dt.GetCatScatts(c)
+ requested_dt[c] = self.scatts_dt.GetCatScatts(c)
+
+ scatt_conds = self.scatt_conds_dt.GetData(requested_dt_conds)
+ scatts = self.scatts_dt.GetData(requested_dt)
+
+ bands = self.an_data.GetBands()
+
+ cats_rasts_out = self.scatts_dt.GetCatsRastsOut()
+ cats_rasts_in = self.scatts_dt.GetCatsRastsIn()
+
+ returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
+ len(self.GetBands()), scatts, cats_rasts_in, cats_rasts_out)
+
+ self.scatts_dt.SetData(scatts)
+
+ def CatRastUpdater(self):
+ return self.cat_rast_updater
+
+ def _validExtend(self, val):
+ #TODO do it general
+ if val > 255:
+ val = 255
+ elif val < 0:
+ val = 0
+
+ return val
+
+class CatRastUpdater:
+
+ def __init__(self, scatts_dt, an_data, core):
+ self.scatts_dt = scatts_dt
+ self.an_data = an_data # TODO may be confusing
+ self.core = core
+ self.vectMap = None
+
+ def _getBbox(self, points):
+ bbox = { "maxx" : None,
+ "maxy" : None,
+ "minx" : None,
+ "miny" : None,
+ }
+
+ for pt in points:
+ if not bbox['maxy']:
+ bbox['maxy'] = pt[1]
+ bbox['miny'] = pt[1]
+ bbox['maxx'] = pt[0]
+ bbox['minx'] = pt[0]
+ continue
+
+ if bbox['maxy'] < pt[1]:
+ bbox['maxy'] = pt[1]
+ elif bbox['miny'] > pt[1]:
+ bbox['miny'] = pt[1]
+
+ if bbox['maxx'] < pt[0]:
+ bbox['maxx'] = pt[0]
+ elif bbox['minx'] > pt[0]:
+ bbox['minx'] = pt[0]
+
+ return bbox
+
+ def SetVectMap(self, vectMap):
+ self.vectMap = vectMap
+
+ def AddedFeature(self, new_geom, cat):
+
+ layer = cat.keys()[0]
+ cat = cat.values()[0][0]
+
+ bbox = self._getBbox(new_geom)
+ grass_region = self._create_grass_region_env(bbox)
+
+ #TODO temp rast
+ patch_rast = "temp_scatt_plt_patch at class"
+ self._rasterize(grass_region, layer, cat, patch_rast)
+
+ region = self.an_data.GetRegion()
+
+ UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastIn(cat))
+
+ return [cat]
+
+ def DeletedAreas(self, old_geoms, old_areas_cats):
+
+ updated_cats = []
+ for i in range(len(old_geoms)):
+ self._updateCatRast(old_geoms[i], old_areas_cats[i], updated_cats)
+
+ return updated_cats
+
+ def ChangedVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
+ #TODO possible optimization - bbox only of vertex and its two neighbours
+
+ updated_cats = []
+ self._updateCatRast(old_geom, old_areas_cats, updated_cats)
+ self._updateCatRast(new_geom, new_areas_cats, updated_cats)
+
+ return updated_cats
+
+ def findLineDiff(geom1, geom2):
+
+ pass
+ #for pt1 in geom1:
+
+ def MovedFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
+ #TODO possible optimization - bbox only of vertex and its two neighbours
+
+ updated_cats = []
+ for i in range(len(old_geoms)):
+ self._updateCatRast(old_geoms[i], old_areas_cats[i], updated_cats)
+
+ new_geom = []
+ for pt in old_geoms[i]:
+ new_geom_pt = (pt[0] + move[0], pt[1] + move[1])
+ new_geom.append(new_geom_pt)
+
+ self._updateCatRast(new_geom, new_areas_cats[i], updated_cats)
+
+ new_areas_cats = old_geoms[i]
+
+ return updated_cats
+
+ def _updateCatRast(self, geom, areas_cats, updated_cats):
+
+ rasterized_cats = []
+ for c in range(len(areas_cats)):
+
+ if not areas_cats[c]:
+ continue
+
+ layer = areas_cats[c].keys()[0]
+ cat = areas_cats[c][layer][0]
+
+ if cat in rasterized_cats:
+ continue
+
+ rasterized_cats.append(cat)
+ updated_cats.append(cat)
+
+ bbox = self._getBbox(geom)
+ grass_region = self._create_grass_region_env(bbox)
+
+ #TODO hack
+ patch_rast = "pokus at class"
+ self._rasterize(grass_region, layer, cat, patch_rast)
+
+ region = self.an_data.GetRegion()
+ UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastIn(cat))
+
+
+ def _rasterize(self, grass_region, layer, cat, out_rast):
+
+ #TODO different thread may be problem when user edits map
+ ret, text, msg = RunCommand("v.build",
+ map = self.vectMap,
+ getErrorMsg = True,
+ read = True)#,
+
+ #TODO thread problem with env variable remove it!!!!
+ environs = os.environ.copy()
+ environs["GRASS_REGION"] = grass_region["GRASS_REGION"]
+
+ ret, text, msg = RunCommand("v.to.rast",
+ input = self.vectMap,
+ use = "cat",
+ layer = str(layer),
+ cat = str(cat),
+ output = out_rast,
+ getErrorMsg = True,
+ read = True,
+ overwrite = True,
+ env = environs)
+ def _create_grass_region_env(self, bbox):
+
+ region = self.an_data.GetRegion()
+ new_region = {}
+
+ if bbox["maxy"] <= region["s"]:
+ return 0
+ elif bbox["maxy"] >= region["n"]:
+ new_region["n"] = bbox["maxy"]
+ else:
+ new_region["n"] = ceil((bbox["maxy"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
+
+ if bbox["miny"] >= region["n"]:
+ return 0
+ elif bbox["miny"] <= region["s"]:
+ new_region["s"] = bbox["miny"]
+ else:
+ new_region["s"] = floor((bbox["miny"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
+
+ if bbox["maxx"] <= region["w"]:
+ return 0
+ elif bbox["maxx"] >= region["e"]:
+ new_region["e"] = bbox["maxx"]
+ else:
+ new_region["e"] = ceil((bbox["maxx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
+
+ if bbox["minx"] >= region["e"]:
+ return 0
+ elif bbox["minx"] <= region["w"]:
+ new_region["w"] = bbox["minx"]
+ else:
+ new_region["w"] = floor((bbox["minx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
+
+ #TODO check regions resolutin
+ new_region["nsres"] = region["nsres"]
+ new_region["ewres"] = region["ewres"]
+
+ return {"GRASS_REGION" : grass.region_env(**new_region)}
+
+class AnalyzedData:
+
+ def __init__(self):
+
+ self.bands = None
+ self.bin_bands_f = []
+
+ self.region = {}
+
+ def GetRegion(self):
+ return self.region
+
+ def Create(self, bands):
+
+ self.bands = None
+ self.bands = bands
+ self.region = None
+
+ ret, region, msg = RunCommand("g.region",
+ flags = "gp",
+ getErrorMsg = True,
+ read = True)
+
+ if ret != 0:
+ raise GException("g.region failed:\n%s" % msg)
+
+ self.region = self._parseRegion(region)
+
+ def GetBands(self):
+ return self.bands
+
+ def _parseRegion(self, region_str):
+
+ region = {}
+ region_str = region_str.splitlines()
+
+ for param in region_str:
+ k, v = param.split("=")
+ if k in ["rows", "cols", "cells"]:
+ v = int(v)
+ else:
+ v = float(v)
+ region[k] = v
+
+ return region
+
+class ScattPlotsCondsData:
+
+ def __init__(self, an_data):
+
+ self.an_data = an_data
+
+ self.max_n_cats = 10
+
+ self.vals = 256 * 256
+
+ self.type = 1;
+ self.CleanUp()
+
+ def CleanUp(self):
+
+ self.cats = {}
+
+ self.n_scatts = -1
+ self.n_bands = -1
+
+ for cat_id in self.cats.keys():
+ self.DeleteCategory(cat_id)
+
+ def Create(self, n_bands):
+
+ self.CleanUp()
+
+ self.n_scatts = (n_bands - 1) * n_bands / 2;
+ self.n_bands = n_bands
+
+ self.AddCategory(cat_id = 0)
+
+ def AddCategory(self, cat_id):
+
+ if cat_id not in self.cats.keys():
+ self.cats[cat_id] = {}
+ return cat_id
+ return -1
+
+ def DeleteCategory(self, cat_id):
+
+ if cat_id not in self.cats.keys():
+ return False
+
+ for scatt in self.cats[cat_id].itervalues():
+ del scatt['np_vals']
+
+ del self.cats[cat_id]
+
+ return True
+
+ def GetCatScatts(self, cat_id):
+
+ if not self.cats.has_key(cat_id):
+ return False
+
+ return self.cats[cat_id].keys()
+
+
+ def AddScattPlot(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return -1
+
+ if self.cats[cat_id].has_key(scatt_id):
+ return 0
+
+ if self.type == 0:
+ np_vals = np.zeros(self.vals, dtype = 'uint32')
+ elif self.type == 1:
+ #TODO solve proble with short
+ np_vals = np.zeros(self.vals, dtype = 'uint32')
+ else:
+ return -1
+
+ np_vals.shape = (256, 256)#TODO hack do it general
+
+ self.cats[cat_id][scatt_id] = {
+ 'np_vals' : np_vals
+ }
+
+ return 1
+
+ def DeleScattPlot(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return False
+
+ if not self.cats[cat_id].has_key(scatt_id):
+ return False
+
+ del self.cats[cat_id][scatt_id]
+ return True
+
+ def GetValuesArr(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return None
+
+ if not self.cats[cat_id].has_key(scatt_id):
+ return None
+
+ return self.cats[cat_id][scatt_id]['np_vals']
+
+ def GetData(self, requested_dt):
+
+ cats = {}
+ for cat_id, scatt_ids in requested_dt.iteritems():
+ if not cats.has_key(cat_id):
+ cats[cat_id] = {}
+ for scatt_id in scatt_ids:
+ # if key is missing condition is always True (full scatter plor is computed)
+ if self.cats[cat_id].has_key(scatt_id):
+ cats[cat_id][scatt_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals']}
+
+ return cats
+
+ def SetData(self, cats):
+
+ for cat_id, scatt_ids in cats.iteritems():
+ for scatt_id in scatt_ids:
+ # if key is missing condition is always True (full scatter plor is computed)
+ if self.cats[cat_id].has_key(scatt_id):
+ self.cats[cat_id][scatt_id]['np_vals'] = cats[cat_id][scatt_id]['np_vals']
+
+class ScattPlotsData(ScattPlotsCondsData):
+
+ def __init__(self, an_data):
+
+ self.cats_rasts_out = {}
+ self.cats_rasts_in = {}
+ self.scatts_ids = []
+
+ ScattPlotsCondsData.__init__(self, an_data)
+
+ #TODO
+ self.type = 0
+
+ def AddCategory(self, cat_id):
+ cat_id = ScattPlotsCondsData.AddCategory(self, cat_id)
+ if cat_id < 0:
+ return cat_id
+
+ for scatt_id in self.scatts_ids:
+ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
+
+ if cat_id == 0:
+ self.cats_rasts_in[cat_id] = None
+ self.cats_rasts_out[cat_id] = None
+ else:
+ self.cats_rasts_in[cat_id] = grass.tempfile()
+ self.cats_rasts_out[cat_id] = grass.tempfile()
+
+ region = self.an_data.GetRegion()
+ CreateCatRast(region, self.cats_rasts_in[cat_id])
+
+ return cat_id
+
+ def DeleteCategory(self, cat_id):
+
+ ScattPlotsCondsData.DeleteCategory(self, cat_id)
+
+ grass.try_remove(self.cats_rasts_in[cat_id])
+ del self.cats_rasts_in[cat_id]
+
+ grass.try_remove(self.cats_rasts_out[cat_id])
+ del self.cats_rasts_out[cat_id]
+
+ return True
+
+ def AddScattPlot(self, scatt_id):
+
+ if scatt_id in self.scatts_ids:
+ return False
+
+ self.scatts_ids.append(scatt_id)
+ for cat_id in self.cats.iterkeys():
+ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
+
+ return True
+
+ def DeleteScatterPlot(self, scatt_id):
+
+ if scatt_id not in self.scatts_ids:
+ return False
+
+ self.scatts_ids.remove(scatt_id)
+
+ for cat_id in self.cats.iterkeys():
+ ScattPlotsCondsData.DeleteScattPlot(self, cat_id, scatt_id)
+
+ return True
+
+ def GetScatt(self, scatt_id):
+ if scatt_id not in self.scatts_ids:
+ return False
+
+ scatts = {}
+ for cat_id in self.cats.iterkeys():
+ scatts[cat_id] = self.cats[cat_id][scatt_id]['np_vals']
+
+ return scatts
+
+ def CleanUp(self):
+
+ ScattPlotsCondsData.CleanUp(self)
+ for tmp in self.cats_rasts_in:
+ grass.try_remove(tmp)
+ for tmp in self.cats_rasts_out:
+ grass.try_remove(tmp)
+
+ self.cats_rasts_out = {}
+ self.cats_rasts_in = {}
+
+ def GetCatRastIn(self, cat_id):
+ return self.cats_rasts_in[cat_id]
+
+ def GetCatRastOut(self, cat_id):
+ return self.cats_rasts_out[cat_id]
+
+ def GetCatsRastsIn(self):
+ max_cat_id = max(self.cats_rasts_in.keys())
+
+ cats_rasts_in = [''] * (max_cat_id + 1)
+ for i_cat_id, i_rast in self.cats_rasts_in.iteritems():
+ cats_rasts_in[i_cat_id] = i_rast
+
+ return cats_rasts_in
+
+ def GetCatsRastsOut(self):
+ max_cat_id = max(self.cats_rasts_out.keys())
+
+ cats_rasts_out = [''] * (max_cat_id + 1)
+ for i_cat_id, i_rast in self.cats_rasts_out.iteritems():
+ cats_rasts_out[i_cat_id] = i_rast
+
+ return cats_rasts_out
+
+#TODO move to utils?
+def idScattToBands(scatt_id, n_bands):
+
+ n_b1 = n_bands - 1
+
+ band_1 = (int) ((2 * n_b1 + 1 - sqrt(((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2)
+
+ band_2 = scatt_id - (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_1 + 1
+
+ return band_1, band_2
+
+
+def BandsToidScatt(band_1, band_2, n_bands):
+
+ if band_2 < band_1:
+ tmp = band_1
+ band_1 = band_2
+ band_2 = tmp
+
+
+ n_b1 = n_bands - 1
+
+ scatt_id = (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_2 - band_1 - 1
+
+ return scatt_id
+
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,106 @@
+"""!
+ at package scatt_plot.toolbars
+
+ at brief Scatter plot - toolbars
+
+Classes:
+ - toolbars::MainToolbar
+
+(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.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+
+
+import wx
+
+from icons.icon import MetaIcon
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from core.gcmd import RunCommand
+
+class MainToolbar(BaseToolbar):
+ """!Main toolbar
+ """
+ def __init__(self, parent):
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+
+ # realize the toolbar
+ self.Realize()
+
+ def _toolbarData(self):
+
+ icons = {
+ 'set_data' : MetaIcon(img = 'layer-raster-add',
+ label = _('Set input raster data for plots')),
+ 'settings' : BaseIcons['settings'].SetLabel( _('Ssettings')),
+ 'help' : MetaIcon(img = 'help',
+ label = _('Show manual')),
+ 'add_scatt_pl' : MetaIcon(img = 'layer-raster-analyze',
+ label = _('Add scatter plot'))
+ }
+
+ return self._getToolbarData((
+ ('sat_data', icons["set_data"],
+ self.parent.OnSetData),
+ (None, ),
+ ('add_scatt', icons["add_scatt_pl"],
+ self.parent.OnAddScattPl),
+ #('settings', icons["settings"],
+ # self.parent.OnSettings),
+ #('help', icons["help"],
+ # self.OnHelp),
+ ("quit", BaseIcons['quit'],
+ self.parent.OnCloseDialog)
+ ))
+
+ def OnHelp(self, event) :
+ RunCommand('g.manual',
+ entry = 'wxGUI.scatt_plot')
+
+class CategoriesToolbar(BaseToolbar):
+
+ def __init__(self, parent, cats_list, scatts_dlg):
+ BaseToolbar.__init__(self, parent)
+ self.cats_list = cats_list
+ self.scatts_dlg = scatts_dlg
+
+ self.InitToolbar(self._toolbarData())
+
+
+ # realize the toolbar
+ self.Realize()
+
+
+ def _toolbarData(self):
+
+ icons = {
+ 'editCatAdd' : MetaIcon(img = 'polygon-create',
+ label = _('Add region to category mode')),
+ 'editCatRemove' : MetaIcon(img = 'polygon-delete',
+ label = _('Remove region to category mode')),
+ 'addCat' : MetaIcon(img = 'layer-add',
+ label = _('Add category')),
+ 'deleteCat' : MetaIcon(img = 'layer-remove',
+ label = _('Delete category'))
+ }
+
+ return self._getToolbarData((('editCatAdd', icons['editCatAdd'],
+ lambda event: self.scatts_dlg.EditCatAdd(),
+ wx.ITEM_CHECK),
+ ('editCatRemove', icons['editCatRemove'],
+ lambda event: self.scatts_dlg.EditCatRemove(),
+ wx.ITEM_CHECK),
+ (None, ),
+ ('addCat', icons["addCat"],
+ lambda event: self.cats_list.AddCategory()),
+ ('deleteCat', icons["deleteCat"],
+ lambda event: self.cats_list.DeleteCategory())))
+
+ def GetToolId(self, toolName): #TODO can be useful in base
+
+ return vars(self)[toolName]
\ No newline at end of file
Modified: sandbox/turek/scatter_plot/include/defs/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/defs/imagery.h 2013-07-27 00:06:18 UTC (rev 57285)
+++ sandbox/turek/scatter_plot/include/defs/imagery.h 2013-07-27 01:33:01 UTC (rev 57286)
@@ -127,8 +127,12 @@
int I_scd_set_value(struct scdScattData *, unsigned int, unsigned int);
int I_compute_scatts(struct Cell_head *, struct scCats *, const char **,
- int, struct scCats *, const char **);
+ int, struct scCats *, const char **, const char **);
+int I_create_cat_rast(struct Cell_head *, const char *);
+int I_insert_patch_to_cat_rast(const char *, struct Cell_head *, const char *);
+
+
/* sig.c */
int I_init_signatures(struct Signature *, int);
int I_new_signature(struct Signature *);
Modified: sandbox/turek/scatter_plot/include/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/imagery.h 2013-07-27 00:06:18 UTC (rev 57285)
+++ sandbox/turek/scatter_plot/include/imagery.h 2013-07-27 01:33:01 UTC (rev 57286)
@@ -165,7 +165,7 @@
struct scdScattData
{
int n_vals;
-
+ //TODO void pointer???
unsigned int * b_conds_arr;
unsigned int * scatt_vals_arr;
};
Modified: sandbox/turek/scatter_plot/lib/imagery/scatt.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt.c 2013-07-27 00:06:18 UTC (rev 57285)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt.c 2013-07-27 01:33:01 UTC (rev 57286)
@@ -134,15 +134,6 @@
B_bounds->east = ceil((intersec.east - B->west - ew_res * 0.5) / ew_res);
B_bounds->west = ceil((intersec.west - B->west - ew_res * 0.5) / ew_res);
- print_reg(A, "A");
- print_reg(B, "B");
-
- print_reg(&intersec, "intersec");
-
- print_reg(A_bounds, "A_bounds");
-
- print_reg(B_bounds, "B_bounds");
-
return 0;
}
@@ -160,7 +151,7 @@
char * null_chunk;
- //TODO free mapset (aslo in compute scatts)
+ //TODO G_free mapset (aslo in compute scatts)
const char *mapset;
struct Cell_head patch_lines, cat_rast_lines;
@@ -190,15 +181,12 @@
null_chunk = Rast_allocate_null_buf();
- print_reg(&cat_rast_bounds, "cat_rast_bounds");
- print_reg(&patch_bounds, "patch_bounds");
-
if(get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds) == -1) return -1;
ncols = cat_rast_bounds.east - cat_rast_bounds.west;
nrows = cat_rast_bounds.south - cat_rast_bounds.north;
- patch_data = (unsigned char *) malloc(ncols * sizeof(unsigned char));
+ patch_data = (unsigned char *) G_malloc(ncols * sizeof(unsigned char));
init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
@@ -209,12 +197,6 @@
step_shift = cat_rast_region->cols - ncols;
-
- G_message("init_shift %d", init_shift);
- G_message("step_shift %d", step_shift);
- G_message("ncols %d", ncols);
- G_message("nrows %d", nrows);
-
for(i_row = 0; i_row < nrows; i_row++) {
Rast_get_null_value_row (fd_patch_rast, null_chunk, i_row + patch_bounds.north);
@@ -247,28 +229,33 @@
return 0;
}
-
-static inline void update_cat_scatt_plt(CELL ** chunks, int chunk_size, unsigned short * belongs_pix, struct scScatts * scatts)
+static inline void update_cat_scatt_plt(CELL ** chunks, char ** null_chunks, int chunk_size, unsigned short * belongs_pix, struct scScatts * scatts)
{
int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunks_pix;
int r_bits = 256;
CELL * band_1_chunks;
CELL * band_2_chunks;
+ char * band_1_null_chunks,* band_2_null_chunks;
int * scatts_bands = scatts->scatts_bands;
-
for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
{
band_1_chunks = chunks[scatts_bands[i_scatt * 2]];
band_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
+ band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
+ band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
+
for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
{
- if(!belongs_pix[i_chunks_pix])
+ if(!belongs_pix[i_chunks_pix] ||
+ band_1_null_chunks[i_chunks_pix] == 1 ||
+ band_2_null_chunks[i_chunks_pix] == 1)
continue;
array_idx = band_1_chunks[i_chunks_pix] + band_2_chunks[i_chunks_pix] * r_bits;
+
++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
}
}
@@ -296,8 +283,8 @@
CELL * band_2_chunks;
unsigned int * i_scatt_conds;
- unsigned short * belongs_pix = (unsigned short *) malloc(chunk_size * sizeof(unsigned short));
- unsigned char * rast_pixs = (unsigned char *) malloc(chunk_size * sizeof(unsigned char));
+ unsigned short * belongs_pix = (unsigned short *) G_malloc(chunk_size * sizeof(unsigned short));
+ unsigned char * rast_pixs = (unsigned char *) G_malloc(chunk_size * sizeof(unsigned char));
for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
{
@@ -309,39 +296,39 @@
if(scatt_plts_cat_idx < 0)
continue;
- scatts_bands = scatts_conds->scatts_bands;
-
scatt_plts_scatts = scatt_plts->cats_arr[scatt_plts_cat_idx];
- // if no condition for cats_arr is defined, all pixels are taken
- memset(belongs_pix, 0, chunk_size * sizeof(unsigned short));
- if(!scatts_conds->n_a_scatts) {
+ G_zero(belongs_pix, chunk_size * sizeof(unsigned short));
+ if(!scatts_conds->n_a_scatts && !f_cats_rasts_in[i_cat]) {
for(i_scatt = 0; i_scatt < scatt_plts_scatts->n_a_scatts; i_scatt++)
- {
- band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
- band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
-
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++){
- if(band_1_null_chunks[i_chunks_pix] != 1 &&
- band_2_null_chunks[i_chunks_pix] != 1)
- belongs_pix[i_chunks_pix] = 1;
- }
+ {
+ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ belongs_pix[i_chunks_pix] = 1;
}
}
else
{
- fread(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_in[i_cat]);
- if (ferror(f_cats_rasts_in[i_cat]))
- {
- free(rast_pixs);
- free(belongs_pix);
- G_message("Unable to read from file.");
- return -1;
- }
+ scatts_bands = scatts_conds->scatts_bands;
+ if(f_cats_rasts_in[i_cat])
+
+ fread(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_in[i_cat]);
+ if (ferror(f_cats_rasts_in[i_cat]))
+ {
+ G_free(rast_pixs);
+ G_free(belongs_pix);
+ G_message("Unable to read from file.");
+ return -1;
+ }
+ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ {
+ if(rast_pixs[i_chunks_pix] != 0 & 255)
+ belongs_pix[i_chunks_pix] = 1;
+ }
+
// test every defined conditions in scatter plots
- for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
+ for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
{
band_1_chunks = chunks[scatts_bands[i_scatt * 2]];
band_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
@@ -357,10 +344,6 @@
band_1_null_chunks[i_chunks_pix] == 1 ||
band_2_null_chunks[i_chunks_pix] == 1)
continue;
- if(rast_pixs[i_chunks_pix] != 0 & 255) {
- belongs_pix[i_chunks_pix] = 1;
- continue;
- }
if(i_scatt_conds[band_1_chunks[i_chunks_pix] + band_2_chunks[i_chunks_pix] * r_bits])
belongs_pix[i_chunks_pix] = 1;
@@ -368,23 +351,26 @@
}
}
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
- rast_pixs[i_chunks_pix] = belongs_pix[i_chunks_pix] & 255;
+ if(f_cats_rasts_out[i_cat]) {
+ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ rast_pixs[i_chunks_pix] = belongs_pix[i_chunks_pix] & 255;
- fwrite(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_out[i_cat]);
- // TODO when code will be paralelized put it out of the loop
- if (ferror(f_cats_rasts_out[i_cat]))
- {
- free(rast_pixs);
- free(belongs_pix);
- G_debug(3, "Unable to write into file.");
- return -1;
+ fwrite(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_out[i_cat]);
+ // TODO when code will be paralelized put it out of the loop
+ if (ferror(f_cats_rasts_out[i_cat]))
+ {
+ G_free(rast_pixs);
+ G_free(belongs_pix);
+ G_debug(3, "Unable to write into file.");
+ return -1;
+ }
}
- update_cat_scatt_plt(chunks, chunk_size, belongs_pix, scatt_plts_scatts);
+
+ update_cat_scatt_plt(chunks, null_chunks, chunk_size, belongs_pix, scatt_plts_scatts);
}
- free(rast_pixs);
- free(belongs_pix);
+ G_free(rast_pixs);
+ G_free(belongs_pix);
return 0;
}
@@ -424,11 +410,13 @@
if(f_cats_rasts_in)
for(i = 0; i < n_a_cats; i++)
- fclose(f_cats_rasts_in[i]);
+ if(f_cats_rasts_in[i])
+ fclose(f_cats_rasts_in[i]);
if(f_cats_rasts_out)
for(i = 0; i < n_a_cats; i++)
- fclose(f_cats_rasts_out[i]);
+ if(f_cats_rasts_out[i])
+ fclose(f_cats_rasts_out[i]);
}
@@ -440,8 +428,13 @@
for(i_cat = 0; i_cat < n_a_cats; i_cat++)
{
id_cat = cats_ids[i_cat];
- f_cats_rasts[i_cat] = fopen(cats_rasts[id_cat], mode);
- if(!f_cats_rasts[i_cat]) return -1;
+
+ if(cats_rasts[id_cat]) {
+ f_cats_rasts[i_cat] = fopen(cats_rasts[id_cat], mode);
+ if(!f_cats_rasts[i_cat]) return -1;
+ }
+ else
+ f_cats_rasts[i_cat] = NULL;
}
return 0;
@@ -456,6 +449,9 @@
for(i_cat = 0; i_cat < n_a_cats; i_cat++)
{
+ if(!f_cats_rasts[i_cat])
+ continue;
+
fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cats_rasts[i_cat]);
if (ferror(f_cats_rasts[i_cat])) return -1;
}
@@ -464,8 +460,6 @@
}
-
-
//TODO change name: I_UpdateScattData???
int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** bands,
int n_bands, struct scCats * scatt_plts, const char ** cats_rasts_in, const char ** cats_rasts_out)
@@ -484,7 +478,6 @@
RASTER_MAP_TYPE data_type;
- int max_chunk_size = 11234;
int nrows, i_band, id_band, n_a_bands, band_id,
chunk_size, i, i_chunk, i_row, head_nchars, i_cat;
int fd_bands[n_bands];
@@ -537,7 +530,7 @@
++n_a_bands;
}
}
-
+
if (open_cats_rasts_files(cats_rasts_out, scatt_conds->n_a_cats, scatt_conds->cats_ids, "wb", f_cats_rasts_out) != 0) {
free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
&bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
@@ -558,8 +551,9 @@
}
for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
- fseek(f_cats_rasts_in[i_cat] , head_nchars, SEEK_SET);
-
+ if(f_cats_rasts_in[i_cat])
+ fseek(f_cats_rasts_in[i_cat] , head_nchars, SEEK_SET);
+
i_chunk = 0;
nrows = Rast_window_rows();
Modified: sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c 2013-07-27 00:06:18 UTC (rev 57285)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c 2013-07-27 01:33:01 UTC (rev 57286)
@@ -111,7 +111,6 @@
if(cats->cats_idxs[cat_id] >= 0)
return -1;
- G_message("Adding category %d", cat_id);
cats->cats_ids[n_a_cats] = cat_id;
cats->cats_idxs[cat_id] = n_a_cats;
@@ -130,7 +129,6 @@
cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++cats->n_a_cats;
- G_message("nacats %d ", cats->n_a_cats);
return 0;
}
@@ -201,8 +199,6 @@
scatts->scatts_arr[n_a_scatts] = scatt_vals;
++scatts->n_a_scatts;
- G_message("I_sc_insert_scatt_data cat_id %d, scatt_id %d, scatt_idx", cat_id, scatt_id, n_a_scatts);
-
return 0;
}
@@ -240,8 +236,6 @@
scatt_vals = scatts->scatts_arr[scatt_id];
scatts->n_a_scatts--;
- G_message("I_sc_remove_scatt_data %d, %d", cat_id, scatt_id);
-
return 0;
}
Added: sandbox/turek/scatter_plot/testing_pach.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_pach.diff (rev 0)
+++ sandbox/turek/scatter_plot/testing_pach.diff 2013-07-27 01:33:01 UTC (rev 57286)
@@ -0,0 +1,4189 @@
+Index: vector/v.edit/main.c
+===================================================================
+--- vector/v.edit/main.c (revision 57285)
++++ vector/v.edit/main.c (working copy)
+@@ -299,7 +299,7 @@
+ move_z = atof(params.move->answers[2]);
+ G_verbose_message(_("Threshold value for snapping is %.2f"),
+ thresh[THRESH_SNAP]);
+- ret = Vedit_move_lines(&Map, BgMap, nbgmaps, List, move_x, move_y, move_z, snap, thresh[THRESH_SNAP]);
++ ret = Vedit_move_lines(&Map, BgMap, nbgmaps, List, move_x, move_y, move_z, snap, thresh[THRESH_SNAP], NULL);
+ G_message(_("%d features moved"), ret);
+ break;
+ case MODE_VERTEX_MOVE:
+@@ -308,15 +308,15 @@
+ move_z = atof(params.move->answers[2]);
+ G_verbose_message(_("Threshold value for snapping is %.2f"),
+ thresh[THRESH_SNAP]);
+- ret = Vedit_move_vertex(&Map, BgMap, nbgmaps, List, coord, thresh[THRESH_COORDS], thresh[THRESH_SNAP], move_x, move_y, move_z, move_first, snap);
++ ret = Vedit_move_vertex(&Map, BgMap, nbgmaps, List, coord, thresh[THRESH_COORDS], thresh[THRESH_SNAP], move_x, move_y, move_z, move_first, snap, NULL);
+ G_message(_("%d vertices moved"), ret);
+ break;
+ case MODE_VERTEX_ADD:
+- ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]);
++ ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS], NULL);
+ G_message(_("%d vertices added"), ret);
+ break;
+ case MODE_VERTEX_DELETE:
+- ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]);
++ ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS], NULL);
+ G_message(_("%d vertices removed"), ret);
+ break;
+ case MODE_BREAK:
+Index: include/imagery.h
+===================================================================
+--- include/imagery.h (revision 57285)
++++ include/imagery.h (working copy)
+@@ -135,6 +135,41 @@
+
+ } IClass_statistics;
+
++/* Scatter Plot backend */
++struct scCats
++{
++ int type; //TODO do not identify it with number
++
++ int n_cats;
++
++ int n_bands;
++ int n_scatts;
++
++ int n_a_cats;
++ int * cats_ids;
++ int * cats_idxs;
++
++ struct scScatts ** cats_arr;
++};
++
++struct scScatts
++{
++ int n_a_scatts;
++
++ int * scatts_bands;
++ int * scatt_idxs;
++
++ struct scdScattData ** scatts_arr;
++};
++
++struct scdScattData
++{
++ int n_vals;
++ //TODO void pointer???
++ unsigned int * b_conds_arr;
++ unsigned int * scatt_vals_arr;
++};
++
+ #define SIGNATURE_TYPE_MIXED 1
+
+ #define GROUPFILE "CURGROUP"
+Index: include/defs/vedit.h
+===================================================================
+--- include/defs/vedit.h (revision 57285)
++++ include/defs/vedit.h (working copy)
+@@ -34,7 +34,7 @@
+
+ /* move.c */
+ int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
+- struct ilist *, double, double, double, int, double);
++ struct ilist *, double, double, double, int, double, struct ilist *);
+
+ /* render.c */
+ struct robject_list *Vedit_render_map(struct Map_info *, struct bound_box *, int,
+@@ -56,11 +56,11 @@
+ int Vedit_move_vertex(struct Map_info *, struct Map_info **, int,
+ struct ilist *,
+ struct line_pnts *, double, double,
+- double, double, double, int, int);
++ double, double, double, int, int, struct ilist *);
+ int Vedit_add_vertex(struct Map_info *Map, struct ilist *,
+- struct line_pnts *, double);
++ struct line_pnts *, double, struct ilist *);
+ int Vedit_remove_vertex(struct Map_info *, struct ilist *,
+- struct line_pnts *, double);
++ struct line_pnts *, double, struct ilist *);
+
+ /* zbulk.c */
+ int Vedit_bulk_labeling(struct Map_info *, struct ilist *,
+Index: include/defs/imagery.h
+===================================================================
+--- include/defs/imagery.h (revision 57285)
++++ include/defs/imagery.h (working copy)
+@@ -110,6 +110,29 @@
+ FILE *I_fopen_subgroup_ref_new(const char *, const char *);
+ FILE *I_fopen_subgroup_ref_old(const char *, const char *);
+
++/* scatt_plt.c */
++void I_sc_init_cats(struct scCats *, int, int);
++void I_sc_free_cats(struct scCats *);
++void I_sc_get_active_categories(int *, int *, struct scCats *);
++int I_sc_add_cat(struct scCats *, int);
++int I_sc_delete_cat(struct scCats *, int);
++int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
++int I_sc_remove_scatt_data(struct scCats *, struct scdScattData *, int, int);
++int I_sc_set_value(struct scCats *, int, int, int, int);
++
++void I_scd_init_scatt_data(struct scdScattData *, int, int);
++void I_scd_free_scatt_data(struct scdScattData *);
++int I_scd_get_data_size(struct scdScattData *);
++void * I_scd_get_data_ptr(struct scdScattData *);
++int I_scd_set_value(struct scdScattData *, unsigned int, unsigned int);
++
++int I_compute_scatts(struct Cell_head *, struct scCats *, const char **,
++ int, struct scCats *, const char **, const char **);
++
++int I_create_cat_rast(struct Cell_head *, const char *);
++int I_insert_patch_to_cat_rast(const char *, struct Cell_head *, const char *);
++
++
+ /* sig.c */
+ int I_init_signatures(struct Signature *, int);
+ int I_new_signature(struct Signature *);
+Index: gui/wxpython/scatt_plot/sc_pl_core.py
+===================================================================
+--- gui/wxpython/scatt_plot/sc_pl_core.py (revision 0)
++++ gui/wxpython/scatt_plot/sc_pl_core.py (working copy)
+@@ -0,0 +1,631 @@
++"""!
++ at package scatt_plot.scatt_plot
++
++ at brief Non GUI functions.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++import numpy as np
++
++from math import sqrt, ceil, floor
++from copy import deepcopy
++
++from grass.script import core as grass
++from core.gcmd import GException, GError, RunCommand
++
++import grass.script as grass
++
++from core_c import CreateCatRast, ComputeScatts, UpdateCatRast
++
++class Core:
++ def __init__(self):
++
++ self.an_data = AnalyzedData()
++
++ self.scatts_dt = ScattPlotsData(self.an_data)
++ self.scatt_conds_dt = ScattPlotsCondsData(self.an_data)
++
++ self.cat_rast_updater = CatRastUpdater(self.scatts_dt, self.an_data, self)
++
++ def SetData(self, bands):
++ self.an_data.Create(bands)
++
++ n_bands = len(self.GetBands())
++
++ self.scatts_dt.Create(n_bands)
++ self.scatt_conds_dt.Create(n_bands)
++
++ def AddCategory(self, cat_id):
++ self.scatts_dt.AddCategory(cat_id)
++ return self.scatt_conds_dt.AddCategory(cat_id)
++
++ def DeleteCategory(self, cat_id):
++ self.scatts_dt.DeleteCategory(cat_id)
++ self.scatt_conds_dt.DeleteCategory(cat_id)
++
++ def CleanUp(self):
++ self.scatts_dt.CleanUp()
++ self.scatt_conds_dt.CleanUp()
++
++ def GetBands(self):
++ return self.an_data.GetBands()
++
++ def GetScattsData(self):
++ return self.scatts_dt;
++
++ def GetRegion(self):
++ return self.an_data.GetRegion()
++
++ def GetCatRastOut(self, cat_id):
++ return self.scatts_dt.GetCatRastOut(cat_id)
++
++ def AddScattPlot(self, scatt_id):
++
++ self.scatts_dt.AddScattPlot(scatt_id = scatt_id)
++
++ scatt_conds = self.scatt_conds_dt.GetData({0 : []})
++ scatts = self.scatts_dt.GetData({0 : [scatt_id]})
++
++ bands = self.an_data.GetBands()
++ cats_rasts_out = self.scatts_dt.GetCatsRastsOut()
++ cats_rasts_in = self.scatts_dt.GetCatsRastsIn()
++
++ returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
++ len(self.GetBands()), scatts, cats_rasts_in, cats_rasts_out)
++ self.scatts_dt.SetData(scatts)
++
++ def SetEditCatData(self, cat_id, scatt_id, bbox, value):
++
++ if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
++ return False
++
++ arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
++
++ for k, v in bbox.iteritems():
++ bbox[k] = self._validExtend(v)
++
++ arr[bbox['btm_y'] : bbox['up_y'], bbox['btm_x'] : bbox['up_x']] = value
++
++ self.ComputeCatScatts([cat_id])
++
++ return True
++
++ def ComputeCatScatts(self, cats_ids):
++
++ requested_dt = {}
++ requested_dt_conds = {}
++
++ for c in cats_ids:
++ requested_dt_conds[c] = self.scatt_conds_dt.GetCatScatts(c)
++ requested_dt[c] = self.scatts_dt.GetCatScatts(c)
++
++ scatt_conds = self.scatt_conds_dt.GetData(requested_dt_conds)
++ scatts = self.scatts_dt.GetData(requested_dt)
++
++ bands = self.an_data.GetBands()
++
++ cats_rasts_out = self.scatts_dt.GetCatsRastsOut()
++ cats_rasts_in = self.scatts_dt.GetCatsRastsIn()
++
++ returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
++ len(self.GetBands()), scatts, cats_rasts_in, cats_rasts_out)
++
++ self.scatts_dt.SetData(scatts)
++
++ def CatRastUpdater(self):
++ return self.cat_rast_updater
++
++ def _validExtend(self, val):
++ #TODO do it general
++ if val > 255:
++ val = 255
++ elif val < 0:
++ val = 0
++
++ return val
++
++class CatRastUpdater:
++
++ def __init__(self, scatts_dt, an_data, core):
++ self.scatts_dt = scatts_dt
++ self.an_data = an_data # TODO may be confusing
++ self.core = core
++ self.vectMap = None
++
++ def _getBbox(self, points):
++ bbox = { "maxx" : None,
++ "maxy" : None,
++ "minx" : None,
++ "miny" : None,
++ }
++
++ for pt in points:
++ if not bbox['maxy']:
++ bbox['maxy'] = pt[1]
++ bbox['miny'] = pt[1]
++ bbox['maxx'] = pt[0]
++ bbox['minx'] = pt[0]
++ continue
++
++ if bbox['maxy'] < pt[1]:
++ bbox['maxy'] = pt[1]
++ elif bbox['miny'] > pt[1]:
++ bbox['miny'] = pt[1]
++
++ if bbox['maxx'] < pt[0]:
++ bbox['maxx'] = pt[0]
++ elif bbox['minx'] > pt[0]:
++ bbox['minx'] = pt[0]
++
++ return bbox
++
++ def SetVectMap(self, vectMap):
++ self.vectMap = vectMap
++
++ def AddedFeature(self, new_geom, cat):
++
++ layer = cat.keys()[0]
++ cat = cat.values()[0][0]
++
++ bbox = self._getBbox(new_geom)
++ grass_region = self._create_grass_region_env(bbox)
++
++ #TODO temp rast
++ patch_rast = "temp_scatt_plt_patch at class"
++ self._rasterize(grass_region, layer, cat, patch_rast)
++
++ region = self.an_data.GetRegion()
++
++ UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastIn(cat))
++
++ return [cat]
++
++ def DeletedAreas(self, old_geoms, old_areas_cats):
++
++ updated_cats = []
++ for i in range(len(old_geoms)):
++ self._updateCatRast(old_geoms[i], old_areas_cats[i], updated_cats)
++
++ return updated_cats
++
++ def ChangedVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
++ #TODO possible optimization - bbox only of vertex and its two neighbours
++
++ updated_cats = []
++ self._updateCatRast(old_geom, old_areas_cats, updated_cats)
++ self._updateCatRast(new_geom, new_areas_cats, updated_cats)
++
++ return updated_cats
++
++ def findLineDiff(geom1, geom2):
++
++ pass
++ #for pt1 in geom1:
++
++ def MovedFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
++ #TODO possible optimization - bbox only of vertex and its two neighbours
++
++ updated_cats = []
++ for i in range(len(old_geoms)):
++ self._updateCatRast(old_geoms[i], old_areas_cats[i], updated_cats)
++
++ new_geom = []
++ for pt in old_geoms[i]:
++ new_geom_pt = (pt[0] + move[0], pt[1] + move[1])
++ new_geom.append(new_geom_pt)
++
++ self._updateCatRast(new_geom, new_areas_cats[i], updated_cats)
++
++ new_areas_cats = old_geoms[i]
++
++ return updated_cats
++
++ def _updateCatRast(self, geom, areas_cats, updated_cats):
++
++ rasterized_cats = []
++ for c in range(len(areas_cats)):
++
++ if not areas_cats[c]:
++ continue
++
++ layer = areas_cats[c].keys()[0]
++ cat = areas_cats[c][layer][0]
++
++ if cat in rasterized_cats:
++ continue
++
++ rasterized_cats.append(cat)
++ updated_cats.append(cat)
++
++ bbox = self._getBbox(geom)
++ grass_region = self._create_grass_region_env(bbox)
++
++ #TODO hack
++ patch_rast = "pokus at class"
++ self._rasterize(grass_region, layer, cat, patch_rast)
++
++ region = self.an_data.GetRegion()
++ UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastIn(cat))
++
++
++ def _rasterize(self, grass_region, layer, cat, out_rast):
++
++ #TODO different thread may be problem when user edits map
++ ret, text, msg = RunCommand("v.build",
++ map = self.vectMap,
++ getErrorMsg = True,
++ read = True)#,
++
++ #TODO thread problem with env variable remove it!!!!
++ environs = os.environ.copy()
++ environs["GRASS_REGION"] = grass_region["GRASS_REGION"]
++
++ ret, text, msg = RunCommand("v.to.rast",
++ input = self.vectMap,
++ use = "cat",
++ layer = str(layer),
++ cat = str(cat),
++ output = out_rast,
++ getErrorMsg = True,
++ read = True,
++ overwrite = True,
++ env = environs)
++ def _create_grass_region_env(self, bbox):
++
++ region = self.an_data.GetRegion()
++ new_region = {}
++
++ if bbox["maxy"] <= region["s"]:
++ return 0
++ elif bbox["maxy"] >= region["n"]:
++ new_region["n"] = bbox["maxy"]
++ else:
++ new_region["n"] = ceil((bbox["maxy"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
++
++ if bbox["miny"] >= region["n"]:
++ return 0
++ elif bbox["miny"] <= region["s"]:
++ new_region["s"] = bbox["miny"]
++ else:
++ new_region["s"] = floor((bbox["miny"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
++
++ if bbox["maxx"] <= region["w"]:
++ return 0
++ elif bbox["maxx"] >= region["e"]:
++ new_region["e"] = bbox["maxx"]
++ else:
++ new_region["e"] = ceil((bbox["maxx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
++
++ if bbox["minx"] >= region["e"]:
++ return 0
++ elif bbox["minx"] <= region["w"]:
++ new_region["w"] = bbox["minx"]
++ else:
++ new_region["w"] = floor((bbox["minx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
++
++ #TODO check regions resolutin
++ new_region["nsres"] = region["nsres"]
++ new_region["ewres"] = region["ewres"]
++
++ return {"GRASS_REGION" : grass.region_env(**new_region)}
++
++class AnalyzedData:
++
++ def __init__(self):
++
++ self.bands = None
++ self.bin_bands_f = []
++
++ self.region = {}
++
++ def GetRegion(self):
++ return self.region
++
++ def Create(self, bands):
++
++ self.bands = None
++ self.bands = bands
++ self.region = None
++
++ ret, region, msg = RunCommand("g.region",
++ flags = "gp",
++ getErrorMsg = True,
++ read = True)
++
++ if ret != 0:
++ raise GException("g.region failed:\n%s" % msg)
++
++ self.region = self._parseRegion(region)
++
++ def GetBands(self):
++ return self.bands
++
++ def _parseRegion(self, region_str):
++
++ region = {}
++ region_str = region_str.splitlines()
++
++ for param in region_str:
++ k, v = param.split("=")
++ if k in ["rows", "cols", "cells"]:
++ v = int(v)
++ else:
++ v = float(v)
++ region[k] = v
++
++ return region
++
++class ScattPlotsCondsData:
++
++ def __init__(self, an_data):
++
++ self.an_data = an_data
++
++ self.max_n_cats = 10
++
++ self.vals = 256 * 256
++
++ self.type = 1;
++ self.CleanUp()
++
++ def CleanUp(self):
++
++ self.cats = {}
++
++ self.n_scatts = -1
++ self.n_bands = -1
++
++ for cat_id in self.cats.keys():
++ self.DeleteCategory(cat_id)
++
++ def Create(self, n_bands):
++
++ self.CleanUp()
++
++ self.n_scatts = (n_bands - 1) * n_bands / 2;
++ self.n_bands = n_bands
++
++ self.AddCategory(cat_id = 0)
++
++ def AddCategory(self, cat_id):
++
++ if cat_id not in self.cats.keys():
++ self.cats[cat_id] = {}
++ return cat_id
++ return -1
++
++ def DeleteCategory(self, cat_id):
++
++ if cat_id not in self.cats.keys():
++ return False
++
++ for scatt in self.cats[cat_id].itervalues():
++ del scatt['np_vals']
++
++ del self.cats[cat_id]
++
++ return True
++
++ def GetCatScatts(self, cat_id):
++
++ if not self.cats.has_key(cat_id):
++ return False
++
++ return self.cats[cat_id].keys()
++
++
++ def AddScattPlot(self, cat_id, scatt_id):
++
++ if not self.cats.has_key(cat_id):
++ return -1
++
++ if self.cats[cat_id].has_key(scatt_id):
++ return 0
++
++ if self.type == 0:
++ np_vals = np.zeros(self.vals, dtype = 'uint32')
++ elif self.type == 1:
++ #TODO solve proble with short
++ np_vals = np.zeros(self.vals, dtype = 'uint32')
++ else:
++ return -1
++
++ np_vals.shape = (256, 256)#TODO hack do it general
++
++ self.cats[cat_id][scatt_id] = {
++ 'np_vals' : np_vals
++ }
++
++ return 1
++
++ def DeleScattPlot(self, cat_id, scatt_id):
++
++ if not self.cats.has_key(cat_id):
++ return False
++
++ if not self.cats[cat_id].has_key(scatt_id):
++ return False
++
++ del self.cats[cat_id][scatt_id]
++ return True
++
++ def GetValuesArr(self, cat_id, scatt_id):
++
++ if not self.cats.has_key(cat_id):
++ return None
++
++ if not self.cats[cat_id].has_key(scatt_id):
++ return None
++
++ return self.cats[cat_id][scatt_id]['np_vals']
++
++ def GetData(self, requested_dt):
++
++ cats = {}
++ for cat_id, scatt_ids in requested_dt.iteritems():
++ if not cats.has_key(cat_id):
++ cats[cat_id] = {}
++ for scatt_id in scatt_ids:
++ # if key is missing condition is always True (full scatter plor is computed)
++ if self.cats[cat_id].has_key(scatt_id):
++ cats[cat_id][scatt_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals']}
++
++ return cats
++
++ def SetData(self, cats):
++
++ for cat_id, scatt_ids in cats.iteritems():
++ for scatt_id in scatt_ids:
++ # if key is missing condition is always True (full scatter plor is computed)
++ if self.cats[cat_id].has_key(scatt_id):
++ self.cats[cat_id][scatt_id]['np_vals'] = cats[cat_id][scatt_id]['np_vals']
++
++class ScattPlotsData(ScattPlotsCondsData):
++
++ def __init__(self, an_data):
++
++ self.cats_rasts_out = {}
++ self.cats_rasts_in = {}
++ self.scatts_ids = []
++
++ ScattPlotsCondsData.__init__(self, an_data)
++
++ #TODO
++ self.type = 0
++
++ def AddCategory(self, cat_id):
++ cat_id = ScattPlotsCondsData.AddCategory(self, cat_id)
++ if cat_id < 0:
++ return cat_id
++
++ for scatt_id in self.scatts_ids:
++ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
++
++ if cat_id == 0:
++ self.cats_rasts_in[cat_id] = None
++ self.cats_rasts_out[cat_id] = None
++ else:
++ self.cats_rasts_in[cat_id] = grass.tempfile()
++ self.cats_rasts_out[cat_id] = grass.tempfile()
++
++ region = self.an_data.GetRegion()
++ CreateCatRast(region, self.cats_rasts_in[cat_id])
++
++ return cat_id
++
++ def DeleteCategory(self, cat_id):
++
++ ScattPlotsCondsData.DeleteCategory(self, cat_id)
++
++ grass.try_remove(self.cats_rasts_in[cat_id])
++ del self.cats_rasts_in[cat_id]
++
++ grass.try_remove(self.cats_rasts_out[cat_id])
++ del self.cats_rasts_out[cat_id]
++
++ return True
++
++ def AddScattPlot(self, scatt_id):
++
++ if scatt_id in self.scatts_ids:
++ return False
++
++ self.scatts_ids.append(scatt_id)
++ for cat_id in self.cats.iterkeys():
++ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
++
++ return True
++
++ def DeleteScatterPlot(self, scatt_id):
++
++ if scatt_id not in self.scatts_ids:
++ return False
++
++ self.scatts_ids.remove(scatt_id)
++
++ for cat_id in self.cats.iterkeys():
++ ScattPlotsCondsData.DeleteScattPlot(self, cat_id, scatt_id)
++
++ return True
++
++ def GetScatt(self, scatt_id):
++ if scatt_id not in self.scatts_ids:
++ return False
++
++ scatts = {}
++ for cat_id in self.cats.iterkeys():
++ scatts[cat_id] = self.cats[cat_id][scatt_id]['np_vals']
++
++ return scatts
++
++ def CleanUp(self):
++
++ ScattPlotsCondsData.CleanUp(self)
++ for tmp in self.cats_rasts_in:
++ grass.try_remove(tmp)
++ for tmp in self.cats_rasts_out:
++ grass.try_remove(tmp)
++
++ self.cats_rasts_out = {}
++ self.cats_rasts_in = {}
++
++ def GetCatRastIn(self, cat_id):
++ return self.cats_rasts_in[cat_id]
++
++ def GetCatRastOut(self, cat_id):
++ return self.cats_rasts_out[cat_id]
++
++ def GetCatsRastsIn(self):
++ max_cat_id = max(self.cats_rasts_in.keys())
++
++ cats_rasts_in = [''] * (max_cat_id + 1)
++ for i_cat_id, i_rast in self.cats_rasts_in.iteritems():
++ cats_rasts_in[i_cat_id] = i_rast
++
++ return cats_rasts_in
++
++ def GetCatsRastsOut(self):
++ max_cat_id = max(self.cats_rasts_out.keys())
++
++ cats_rasts_out = [''] * (max_cat_id + 1)
++ for i_cat_id, i_rast in self.cats_rasts_out.iteritems():
++ cats_rasts_out[i_cat_id] = i_rast
++
++ return cats_rasts_out
++
++#TODO move to utils?
++def idScattToBands(scatt_id, n_bands):
++
++ n_b1 = n_bands - 1
++
++ band_1 = (int) ((2 * n_b1 + 1 - sqrt(((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2)
++
++ band_2 = scatt_id - (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_1 + 1
++
++ return band_1, band_2
++
++
++def BandsToidScatt(band_1, band_2, n_bands):
++
++ if band_2 < band_1:
++ tmp = band_1
++ band_1 = band_2
++ band_2 = tmp
++
++
++ n_b1 = n_bands - 1
++
++ scatt_id = (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_2 - band_1 - 1
++
++ return scatt_id
++
+Index: gui/wxpython/scatt_plot/gthreading.py
+===================================================================
+--- gui/wxpython/scatt_plot/gthreading.py (revision 0)
++++ gui/wxpython/scatt_plot/gthreading.py (working copy)
+@@ -0,0 +1,133 @@
++"""!
++ at package scatt_plot.gthreading
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++
++import os
++import sys
++import time
++import threading
++import Queue
++
++import wx
++from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
++
++class gThread(threading.Thread):
++ """!Thread for GRASS commands"""
++ requestId = 0
++
++ def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
++ """!
++ @param receiver event receiver (used in PostEvent)
++ """
++ 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.receiver = receiver
++ self._want_abort_all = False
++
++ 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):
++ os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
++ while True:
++ requestId, args, kwds = self.requestQ.get()
++ for key in ('callable', 'onDone', 'onPrepare', 'userData'):
++ if key in kwds:
++ vars()[key] = kwds[key]
++ del kwds[key]
++ else:
++ vars()[key] = None
++
++ requestTime = time.time()
++
++ #if self._want_abort_all and self.requestCmd is not None:
++ # self.requestCmd.abort()
++ # if self.requestQ.empty():
++ # self._want_abort_all = False
++
++ # prepare
++ if self.receiver:
++ event = wxCmdPrepare(type = 'method',
++ time=requestTime,
++ pid=requestId)
++
++ wx.PostEvent(self.receiver, event)
++
++ # run command
++ event = wxCmdRun(type = 'method',
++ pid=requestId)
++
++ wx.PostEvent(self.receiver, event)
++
++ time.sleep(.1)
++
++ ret = None
++ exception = None
++ #try:
++ ret = vars()['callable'](*args, **kwds)
++ #except Exception as e:
++ # exception = e;
++
++ self.resultQ.put((requestId, ret))
++
++ time.sleep(.1)
++
++
++ if self.receiver:
++ event = wxCmdDone(type = 'cmd',
++ kwds = kwds,
++ args = args, #TODO expand args to kwds
++ ret=ret,
++ exception=exception,
++ pid=requestId)
++
++ # send event
++ wx.PostEvent(self.receiver, event)
++
++ def abort(self, abortall=True):
++ """!Abort command(s)"""
++ if abortall:
++ self._want_abort_all = True
++ if self.requestQ.empty():
++ self._want_abort_all = False
+\ No newline at end of file
+Index: gui/wxpython/scatt_plot/dialogs.py
+===================================================================
+--- gui/wxpython/scatt_plot/dialogs.py (revision 0)
++++ gui/wxpython/scatt_plot/dialogs.py (working copy)
+@@ -0,0 +1,612 @@
++"""!
++ at package scatt_plot.dialogs
++
++ at brief GUI.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++import wx
++import wx.lib.mixins.listctrl as listmix
++import wx.lib.flatnotebook as FN
++import wx.aui
++
++from core import globalvar
++from core.gcmd import GException, GError, RunCommand
++
++from gui_core.gselect import Select
++from gui_core.widgets import GNotebook
++
++from scatt_plot.controllers import ScattsManager
++from scatt_plot.toolbars import MainToolbar, CategoriesToolbar
++from scatt_plot.sc_pl_core import Core, BandsToidScatt
++from scatt_plot.plots import ScatterPlotWidget
++
++
++class ScattPlotMainDialog(wx.Dialog):
++ def __init__(self, parent, giface, iclass_mapwin = None,
++ id = wx.ID_ANY, title = _("GRASS GIS Interactive Scatter Plot Tool"),
++ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
++
++ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
++ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
++
++ #TODO remove iclass parameter
++ self.scatt_mgr = ScattsManager(guiparent = self, giface = giface, iclass_mapwin = iclass_mapwin)
++
++ # toobars
++ self.toolbars = {}
++ self.toolbars['mainToolbar'] = MainToolbar(parent = self)
++
++ self.mainPanel = wx.Panel(parent=self)
++ self.notebook = GNotebook(parent = self.mainPanel,
++ style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
++ FN.FNB_NO_X_BUTTON)
++
++ # Fancy gui
++ self._mgr = wx.aui.AuiManager(self)
++ self._addPanes()
++ self._mgr.Update()
++
++ self._createCategoryPage()
++
++ self.edit_cat_tools = {
++ 'editCatAdd' : {
++ 'toggle' : False,
++ 'type' : 'add'
++ },
++ 'editCatRemove' : {
++ 'toggle' : False,
++ 'type' : 'remove'
++ }
++ }
++ self._doLayout()
++ self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
++
++ dlgSize = (220, 400)
++ self.SetMinSize(dlgSize)
++ self.SetInitialSize(dlgSize)
++
++ #fix goutput's pane size (required for Mac OSX)
++ #if self.gwindow:
++ # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
++
++ def _doLayout(self):
++
++ sizer = wx.BoxSizer(wx.VERTICAL)
++
++ sizer.Add(item = self.notebook, proportion = 1,
++ flag = wx.EXPAND)
++
++ self.mainPanel.SetSizer(sizer)
++
++ sizer.Fit(self)
++ self.Layout()
++
++ #TODO split to panel?
++ def _createCategoryPage(self):
++ catsPanel = wx.Panel(parent = self)
++
++
++ self.notebook.AddPage(page = catsPanel,
++ text=_('Categories'),
++ name = 'categories')
++
++ self.cats_list = CategoryListCtrl(parent = catsPanel,
++ cats_mgr = self.scatt_mgr.GetCategoriesManager())
++ self.toolbars['catsList'] = CategoriesToolbar(parent = catsPanel,
++ cats_list = self.cats_list,
++ scatts_dlg = self)
++
++ AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
++
++ catsSizer = wx.BoxSizer(wx.VERTICAL)
++
++ catsSizer.Add(item = self.toolbars['catsList'], proportion = 0)
++ catsSizer.Add(item = self.cats_list, proportion = 1, flag = wx.EXPAND)
++
++ catsPanel.SetSizer(catsSizer)
++
++ def _addPanes(self):
++ """!Adds toolbar pane and pane with tabs"""
++ self._mgr.AddPane(self.toolbars['mainToolbar'],
++ wx.aui.AuiPaneInfo().
++ Name("pointlisttools").Caption(_("Point cats_list toolbar")).
++ ToolbarPane().Top().Row(0).
++ Dockable(False).
++ CloseButton(False).Layer(0))
++
++ self._mgr.AddPane(self.mainPanel,
++ wx.aui.AuiPaneInfo().
++ Name("tabs").CaptionVisible(visible = False).
++ Center().
++ Dockable(False).
++ CloseButton(False).Layer(0))
++
++ def OnCloseDialog(self, event):
++ """!Close dialog"""
++ self.scatt_mgr.CleanUp()
++ self.Destroy()
++
++ def OnSettings(self, event):
++ pass
++
++ def OnSetData(self, event):
++
++ dlg = SetDataDialog(parent=self)
++
++ if dlg.ShowModal() == wx.ID_OK:
++ bands = dlg.GetBands()
++ self.scatt_mgr.SetData(bands)
++
++ dlg.Destroy()
++
++
++ def NewScatterPlot(self, scatt_id):
++ #TODO needs to be resolved (should be in this class)
++ scatt_dlg = ScatterPlotDialog(parent = self, scatt_mgr = self.scatt_mgr, scatt_id = scatt_id)
++ return scatt_dlg.GetPlot()
++
++ def OnSettings(self, event):
++ pass
++
++ def OnAddScattPl(self, event):
++
++ bands = self.scatt_mgr.GetBands()
++ if not bands:
++ GError(_('No data set for scatter plot.'))
++ return
++
++ dlg = AddScattPlotDialog(parent = self, bands = bands)
++
++ if dlg.ShowModal() == wx.ID_OK:
++ self.scatt_mgr.AddScattPlot(dlg.GetScattId())
++
++ dlg.Destroy()
++
++ def EditCatAdd(self):
++ self._setEditCat(tool_name = 'editCatAdd')
++
++ def EditCatRemove(self):
++ self._setEditCat(tool_name = 'editCatRemove')
++
++ def _setEditCat(self, tool_name):
++
++ if self.edit_cat_tools[tool_name]['toggle'] == False:
++
++ for i_tool_name in self.edit_cat_tools.iterkeys():
++ if i_tool_name == tool_name:
++ continue
++
++ self.edit_cat_tools[i_tool_name]['toggle'] = False
++
++ toolbar = self.toolbars['catsList']
++ toolbar.ToggleTool(vars(toolbar)[i_tool_name], False)
++
++ self.edit_cat_tools[tool_name]['toggle'] = True
++ self.scatt_mgr.EditCat(edit_type = self.edit_cat_tools[tool_name]['type'])
++ return
++
++ self.scatt_mgr.EditCat(edit_type = None)
++ self.edit_cat_tools[tool_name]['toggle'] = False
++
++ def GetScattMgr(self):
++ return self.scatt_mgr
++
++
++class SetDataDialog(wx.Dialog):
++
++ def __init__(self, parent, id = wx.ID_ANY):
++
++ wx.Dialog.__init__(self, parent, title = ("Select imagery group "), id = id)
++
++ self.bands = []
++
++
++ self._createWidgets()
++ self.group.SetValue("testovaci_1")
++
++ def _createWidgets(self):
++
++ self.labels = {}
++ self.params = {}
++
++ self.group_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name of raster group for input data:"))
++
++ self.group = Select(parent = self, type = 'group',
++ size = globalvar.DIALOG_GSELECT_SIZE)
++
++ # buttons
++ self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
++
++ self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
++
++ self._layout()
++
++ def _layout(self):
++
++ border = wx.BoxSizer(wx.VERTICAL)
++ dialogSizer = wx.BoxSizer(wx.VERTICAL)
++
++ regionSizer = wx.BoxSizer(wx.HORIZONTAL)
++
++ dialogSizer.Add(item = self._addSelectSizer(title = self.group_label,
++ sel = self.group))
++
++ # buttons
++ self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
++
++ self.btnsizer.Add(item = self.btn_close, proportion = 0,
++ flag = wx.ALL | wx.ALIGN_CENTER,
++ border = 10)
++
++ self.btnsizer.Add(item = self.btn_ok, proportion = 0,
++ flag = wx.ALL | wx.ALIGN_CENTER,
++ border = 10)
++
++ dialogSizer.Add(item = self.btnsizer, proportion = 0,
++ flag = wx.ALIGN_CENTER)
++
++ border.Add(item = dialogSizer, proportion = 0,
++ flag = wx.ALL, border = 5)
++
++ self.SetSizer(border)
++ self.Layout()
++ self.Fit()
++
++ # bindings
++ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
++ self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
++
++ def _addSelectSizer(self, title, sel):
++ """!Helper layout function.
++ """
++ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
++
++ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
++ selTitleSizer.Add(item = title, proportion = 1,
++ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
++
++ selSizer.Add(item = selTitleSizer, proportion = 0,
++ flag = wx.EXPAND)
++
++ selSizer.Add(item = sel, proportion = 1,
++ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
++ border = 5)
++
++ return selSizer
++
++ def GetBands(self):
++
++ return self.bands
++
++ def OnClose(self, event):
++ """!Close dialog
++ """
++ if not self.IsModal():
++ self.Destroy()
++ event.Skip()
++
++ def OnOk(self, event):
++ """!
++ """
++ ret, stdout, err_msg = RunCommand("i.group",
++ getErrorMsg = True,
++ read = True,
++ quiet = True,
++ group = self.group.GetValue().strip(),
++ flags = 'g')
++
++ if ret != 0:
++ GError("%s module failed:\n%s" % ("<i.group>", err_msg))
++ return
++
++ self.bands = stdout.split('\n')
++
++ for band in self.bands[:]:
++ if not band:
++ self.bands.remove(band)
++
++ event.Skip()
++
++ #TODO catch event or...
++ def GetSelectedCatId(self):
++
++ return self.cats_list.GetSelectedCatId()
++
++class AddScattPlotDialog(wx.Dialog):
++
++ def __init__(self, parent, bands, id = wx.ID_ANY):
++
++ wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
++
++ self.bands = bands
++
++ self.scatt_id = None
++
++ self._createWidgets()
++
++ def _createWidgets(self):
++
++ self.labels = {}
++ self.params = {}
++
++ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 1:"))
++
++ self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
++ choices = self.bands,
++ style = wx.CB_READONLY, size = (350, 30))
++
++ self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 2:"))
++
++ self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
++ choices = self.bands,
++ style = wx.CB_READONLY, size = (350, 30))
++
++ # buttons
++ self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
++
++ self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
++
++ self._layout()
++
++ def _layout(self):
++
++ border = wx.BoxSizer(wx.VERTICAL)
++ dialogSizer = wx.BoxSizer(wx.VERTICAL)
++
++ regionSizer = wx.BoxSizer(wx.HORIZONTAL)
++
++ dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label,
++ sel = self.band_1_ch))
++
++ dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label,
++ sel = self.band_2_ch))
++
++ # buttons
++ self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
++
++ self.btnsizer.Add(item = self.btn_close, proportion = 0,
++ flag = wx.ALL | wx.ALIGN_CENTER,
++ border = 10)
++
++ self.btnsizer.Add(item = self.btn_ok, proportion = 0,
++ flag = wx.ALL | wx.ALIGN_CENTER,
++ border = 10)
++
++ dialogSizer.Add(item = self.btnsizer, proportion = 0,
++ flag = wx.ALIGN_CENTER)
++
++ border.Add(item = dialogSizer, proportion = 0,
++ flag = wx.ALL, border = 5)
++
++ self.SetSizer(border)
++ self.Layout()
++ self.Fit()
++
++ # bindings
++ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
++ self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
++
++ def _addSelectSizer(self, title, sel):
++ """!Helper layout function.
++ """
++ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
++
++ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
++ selTitleSizer.Add(item = title, proportion = 1,
++ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
++
++ selSizer.Add(item = selTitleSizer, proportion = 0,
++ flag = wx.EXPAND)
++
++ selSizer.Add(item = sel, proportion = 1,
++ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
++ border = 5)
++
++ return selSizer
++
++ def OnClose(self, event):
++ """!Close dialog
++ """
++ if not self.IsModal():
++ self.Destroy()
++ event.Skip()
++
++ def OnOk(self, event):
++ """!
++ """
++ band_1 = self.band_1_ch.GetSelection()
++ band_2 = self.band_2_ch.GetSelection()
++
++
++ if band_1 == band_2:
++ GError(_("Selected bands must be different."))
++ return
++
++ #TODO axes selection
++ if band_1 > band_2:
++ tmp_band = band_2
++ band_2 = band_1
++ band_1 = band_2
++
++ self.scatt_id = BandsToidScatt(band_1, band_2, len(self.bands))
++
++ event.Skip()
++
++ def GetScattId(self):
++ return self.scatt_id
++
++class ScatterPlotDialog(wx.Dialog):
++ def __init__(self, parent, scatt_mgr, scatt_id,
++ id = wx.ID_ANY, title = _("Test plot"),
++ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
++
++ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
++ self.scatt = ScatterPlotWidget(parent = self,
++ scatt_mgr = scatt_mgr,
++ scatt_id = scatt_id)
++
++ def GetPlot(self):
++ return self.scatt
++
++ def _doLayout(self):
++ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
++ self.main_sizer.Add(self.self)
++
++ self.panel.SetSizer(self.main_sizer)
++ self.main_sizer.Fit(self)
++
++ def OnPlotClosed(self, scatt_id):
++ self.Destroy()
++
++class CategoryListCtrl(wx.ListCtrl,
++ listmix.ListCtrlAutoWidthMixin,
++ listmix.TextEditMixin):
++
++ def __init__(self, parent, cats_mgr, id = wx.ID_ANY):
++
++ wx.ListCtrl.__init__(self, parent, id,
++ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
++ self.columns = ((_('Category name'), 'name'),
++ (_('Color'), 'color'))
++ self.Populate(columns = self.columns)
++
++ self.cats_mgr = cats_mgr
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++ self.rightClickedItemIdx = wx.NOT_FOUND
++
++ listmix.ListCtrlAutoWidthMixin.__init__(self)
++
++ listmix.TextEditMixin.__init__(self)
++
++ self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
++ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
++
++ self.cats_mgr.setCategoryAttrs.connect(self.Update)
++ self.cats_mgr.deletedCategory.connect(self.Update)
++ self.cats_mgr.addedCategory.connect(self.Update)
++
++ def Update(self, **kwargs):
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def InitCoreCats(self):
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def SetVirtualData(self, row, column, text):
++ attr = self.columns[column][1]
++ if attr == 'name':
++ try:
++ text.encode('ascii')
++ except UnicodeEncodeError:
++ GMessage(parent = self, message = _("Please use only ASCII characters."))
++ return
++
++ cat_id = self.cats_mgr.GetCategories()[row]
++
++ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
++ self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
++ self.cats_mgr.setCategoryAttrs.connect(self.Update)
++
++ self.Select(row)
++
++ def Populate(self, columns):
++ for i, col in enumerate(columns):
++ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
++
++ self.SetColumnWidth(0, 100)
++ self.SetColumnWidth(1, 100)
++
++ def AddCategory(self):
++
++ self.cats_mgr.addedCategory.disconnect(self.Update)
++ cat_id = self.cats_mgr.AddCategory()
++ self.cats_mgr.addedCategory.connect(self.Update)
++
++ if cat_id < 0:
++ GError(_("Maximum limit of categories number was reached."))
++ return
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++ def DeleteCategory(self):
++ indexList = sorted(self.GetSelectedIndices(), reverse = True)
++ cats = []
++ for i in indexList:
++ # remove temporary raster
++ cat_id = self.cats_mgr.GetCategories()[i]
++
++ cats.append(cat_id)
++
++ self.cats_mgr.deletedCategory.disconnect(self.Update)
++ self.cats_mgr.DeleteCategory(cat_id)
++ self.cats_mgr.deletedCategory.connect(self.Update)
++
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++ def OnSel(self, event):
++ self.cats_mgr.SetSelectedCat(event.GetIndex() + 1)
++
++ event.Skip()
++
++ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
++ indices = []
++ lastFound = -1
++ while True:
++ index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
++ if index == -1:
++ break
++ else:
++ lastFound = index
++ indices.append(index)
++ return indices
++
++ def OnEdit(self, event):
++ currentItem = event.m_itemIndex
++ currentCol = event.m_col
++
++ if currentCol == 1:
++ dlg = wx.ColourDialog(self)
++ dlg.GetColourData().SetChooseFull(True)
++
++ if dlg.ShowModal() == wx.ID_OK:
++ color = dlg.GetColourData().GetColour().Get()
++ color = ':'.join(map(str, color))
++ self.SetVirtualData(currentItem, currentCol, color)
++ dlg.Destroy()
++ wx.CallAfter(self.SetFocus)
++
++ event.Skip()
++
++
++ def DeselectAll(self):
++ """!Deselect all items"""
++ indexList = self.GetSelectedIndices()
++ for i in indexList:
++ self.Select(i, on = 0)
++
++ # no highlight
++ self.OnCategorySelected(None)
++
++ def OnGetItemText(self, item, col):
++ attr = self.columns[col][1]
++ cat_id = self.cats_mgr.GetCategories()[item]
++
++ return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
++
++ def OnGetItemImage(self, item):
++ return -1
++
++ def OnGetItemAttr(self, item):
++ return None
+Index: gui/wxpython/scatt_plot/toolbars.py
+===================================================================
+--- gui/wxpython/scatt_plot/toolbars.py (revision 0)
++++ gui/wxpython/scatt_plot/toolbars.py (working copy)
+@@ -0,0 +1,106 @@
++"""!
++ at package scatt_plot.toolbars
++
++ at brief Scatter plot - toolbars
++
++Classes:
++ - toolbars::MainToolbar
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++
++
++import wx
++
++from icons.icon import MetaIcon
++from gui_core.toolbars import BaseToolbar, BaseIcons
++from core.gcmd import RunCommand
++
++class MainToolbar(BaseToolbar):
++ """!Main toolbar
++ """
++ def __init__(self, parent):
++ BaseToolbar.__init__(self, parent)
++
++ self.InitToolbar(self._toolbarData())
++
++ # realize the toolbar
++ self.Realize()
++
++ def _toolbarData(self):
++
++ icons = {
++ 'set_data' : MetaIcon(img = 'layer-raster-add',
++ label = _('Set input raster data for plots')),
++ 'settings' : BaseIcons['settings'].SetLabel( _('Ssettings')),
++ 'help' : MetaIcon(img = 'help',
++ label = _('Show manual')),
++ 'add_scatt_pl' : MetaIcon(img = 'layer-raster-analyze',
++ label = _('Add scatter plot'))
++ }
++
++ return self._getToolbarData((
++ ('sat_data', icons["set_data"],
++ self.parent.OnSetData),
++ (None, ),
++ ('add_scatt', icons["add_scatt_pl"],
++ self.parent.OnAddScattPl),
++ #('settings', icons["settings"],
++ # self.parent.OnSettings),
++ #('help', icons["help"],
++ # self.OnHelp),
++ ("quit", BaseIcons['quit'],
++ self.parent.OnCloseDialog)
++ ))
++
++ def OnHelp(self, event) :
++ RunCommand('g.manual',
++ entry = 'wxGUI.scatt_plot')
++
++class CategoriesToolbar(BaseToolbar):
++
++ def __init__(self, parent, cats_list, scatts_dlg):
++ BaseToolbar.__init__(self, parent)
++ self.cats_list = cats_list
++ self.scatts_dlg = scatts_dlg
++
++ self.InitToolbar(self._toolbarData())
++
++
++ # realize the toolbar
++ self.Realize()
++
++
++ def _toolbarData(self):
++
++ icons = {
++ 'editCatAdd' : MetaIcon(img = 'polygon-create',
++ label = _('Add region to category mode')),
++ 'editCatRemove' : MetaIcon(img = 'polygon-delete',
++ label = _('Remove region to category mode')),
++ 'addCat' : MetaIcon(img = 'layer-add',
++ label = _('Add category')),
++ 'deleteCat' : MetaIcon(img = 'layer-remove',
++ label = _('Delete category'))
++ }
++
++ return self._getToolbarData((('editCatAdd', icons['editCatAdd'],
++ lambda event: self.scatts_dlg.EditCatAdd(),
++ wx.ITEM_CHECK),
++ ('editCatRemove', icons['editCatRemove'],
++ lambda event: self.scatts_dlg.EditCatRemove(),
++ wx.ITEM_CHECK),
++ (None, ),
++ ('addCat', icons["addCat"],
++ lambda event: self.cats_list.AddCategory()),
++ ('deleteCat', icons["deleteCat"],
++ lambda event: self.cats_list.DeleteCategory())))
++
++ def GetToolId(self, toolName): #TODO can be useful in base
++
++ return vars(self)[toolName]
+\ No newline at end of file
+Index: gui/wxpython/scatt_plot/core_c.py
+===================================================================
+--- gui/wxpython/scatt_plot/core_c.py (revision 0)
++++ gui/wxpython/scatt_plot/core_c.py (working copy)
+@@ -0,0 +1,211 @@
++"""!
++ at package scatt_plot.scatt_plot
++
++ at brief Functions which work with scatter plot c code.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++
++from multiprocessing import Process, Queue
++
++from ctypes import *
++try:
++ from grass.lib.imagery import *
++ from grass.lib.gis import Cell_head, G_get_window
++
++except ImportError, e:
++ sys.stderr.write(_("Loading imagery lib failed"))
++
++def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out):
++
++ #return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out)
++
++ # Queue object for interprocess communication
++ q = Queue()
++
++ # The separate render process
++ p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
++ n_bands, scatts, cats_rasts_in, cats_rasts_out, q))
++ p.start()
++ ret = q.get()
++ p.join()
++
++ return ret[0], ret[1]
++
++def UpdateCatRast(patch_rast, region, cat_rast):
++
++ #return ComputeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts)
++ # Queue object for interprocess communication
++
++ #_updateCatRastProcess(patch_rast, region, cat_rast, None)
++ #return
++ q = Queue()
++ # The separate render process
++ p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
++ p.start()
++ #ret = q.get()
++ p.join()
++
++ #return ret[0], ret[1]
++
++def CreateCatRast(region, cat_rast):
++
++ cell_head = _regionToCellHead(region)
++ I_create_cat_rast(pointer(cell_head), cat_rast)
++
++def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, output_queue):
++
++ #TODO names for types not 0 and 1?
++ sccats_c, vals_dt = _getComputationStruct(scatts, 0, n_bands)
++ scatt_conds_c, vals_dt2 = _getComputationStruct(scatt_conds, 1, n_bands)
++
++ char_bands = _stringListToCharArr(bands)
++ char_cats_rasts_out = _stringListToCharArr(cats_rasts_out)
++ char_cats_rasts_in = _stringListToCharArr(cats_rasts_in)
++
++ cell_head = _regionToCellHead(region)
++
++ ret = I_compute_scatts(pointer(cell_head),
++ pointer(scatt_conds_c),
++ pointer(char_bands),
++ n_bands,
++ pointer(sccats_c),
++ pointer(char_cats_rasts_in),
++ pointer(char_cats_rasts_out))
++
++ I_sc_free_cats(pointer(sccats_c))
++ I_sc_free_cats(pointer(scatt_conds_c))
++
++ output_queue.put((ret, scatts))
++ #return (ret, scatts)
++
++def _regionToCellHead(region):
++ cell_head = struct_Cell_head()
++ G_get_window(pointer(cell_head))
++
++ convert_dict = {'n' : 'north', 'e' : 'east',
++ 'w' : 'west', 's' : 'south',
++ 'nsres' : 'ns_res',
++ 'ewres' : 'ew_res'}
++
++ for k, v in region.iteritems():
++ if k in ["rows", "cols", "cells"]:
++ v = int(v)
++ else:
++ v = float(v)
++
++ if convert_dict.has_key(k):
++ k = convert_dict[k]
++
++ setattr(cell_head, k, v)
++
++ return cell_head
++
++def _stringListToCharArr(str_list):
++
++ arr = c_char_p * len(str_list)
++ char_arr = arr()
++ for i, st in enumerate(str_list):
++ if st:
++ char_arr[i] = st
++ else:
++ char_arr[i] = None
++
++ return char_arr
++
++def _getComputationStruct(cats, cats_type, n_bands):
++
++ sccats = struct_scCats()
++ I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
++
++ #for i in range(sccats.n_a_cats):
++ # cat_id = sccats.cats_ids[i]
++ # I_sc_delete_cat(pointer(sccats), cat_id)
++ # i -= 1
++
++ vals_dt = []
++ for cat_id, scatt_ids in cats.iteritems():
++ I_sc_add_cat(pointer(sccats), cat_id)
++
++ for scatt_id, dt in scatt_ids.iteritems():
++ # if key is missing condition is always True (full scatter plor is computed)
++ if cats[cat_id].has_key(scatt_id):
++
++ vals = dt["np_vals"]
++ #TODO hack
++ vals.shape = (256 * 256)
++
++ c_uint_p = ctypes.POINTER(ctypes.c_uint)
++ scatt_vals = scdScattData()
++ if cats_type == 0:
++ vals[:] = 0
++ scatt_vals.scatt_vals_arr = vals.ctypes.data_as(c_uint_p)
++
++ else:
++ #TODO solve proble with short integer
++ scatt_vals.b_conds_arr = vals.ctypes.data_as(c_uint_p)
++
++ vals_dt.append(scatt_vals)
++
++ #vals_dt.append(data_p)
++ vals.shape = (256, 256)
++ I_sc_insert_scatt_data(pointer(sccats),
++ pointer(scatt_vals),
++ cat_id, scatt_id)
++
++ return sccats, vals_dt
++
++def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
++
++ #TODO names for types not 0 and 1?
++
++ cell_head = _regionToCellHead(region)
++
++
++ cat_rast
++ I_insert_patch_to_cat_rast(patch_rast,
++ pointer(cell_head),
++ cat_rast)
++
++ #print re
++ #output_queue.put((ret, scatts))
++ #return (ret, scatts)
++
++"""
++class A:
++ def __init__(self):
++ self.i = 0
++
++ def incr(self):
++ self.i += 1
++ return self.i
++
++def worker(input, output):
++ print "ahoj"
++ for func, args in iter(input.get, 'STOP'):
++ print "jedu"
++ result = func.incr
++ output.put(result)
++
++def test():
++ # Create queues
++ task_queue = Queue()
++ done_queue = Queue()
++
++ # Start worker processes
++ a = A()
++
++ prc = Process(target=worker, args=(task_queue, done_queue)).start()
++ task_queue.put((a,1))
++
++ data = done_queue.get()
++ print data
++ task_queue.put('STOP')
++"""
+Index: gui/wxpython/scatt_plot/__init__.py
+===================================================================
+--- gui/wxpython/scatt_plot/__init__.py (revision 0)
++++ gui/wxpython/scatt_plot/__init__.py (working copy)
+@@ -0,0 +1,8 @@
++all = [
++ 'dialogs',
++ 'gthreading',
++ 'plots',
++ 'sc_pl_core',
++ 'toolbars',
++ 'core_c',
++ ]
+Index: gui/wxpython/scatt_plot/plots.py
+===================================================================
+--- gui/wxpython/scatt_plot/plots.py (revision 0)
++++ gui/wxpython/scatt_plot/plots.py (working copy)
+@@ -0,0 +1,185 @@
++"""!
++ at package scatt_plot.dialogs
++
++ at brief Ploting widgets.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import wx
++import numpy as np
++import random
++try:
++ haveMatPlot = True
++ import matplotlib
++ matplotlib.use('WXAgg')
++ from matplotlib.figure import Figure
++ from matplotlib.backends.backend_wxagg import \
++ FigureCanvasWxAgg as FigCanvas, \
++ NavigationToolbar2WxAgg as NavigationToolbar
++except ImportError:
++ haveMatPlot = False
++
++class ScatterPlotWidget(wx.Panel):
++ def __init__(self, parent, scatt_id, scatt_mgr,
++ id = wx.ID_ANY):
++
++ wx.Panel.__init__(self, parent, id)
++
++ self.parent = parent
++
++ self._createWidgets()
++ self._doLayout()
++ self.scatt_id = scatt_id
++ self.scatt_mgr = scatt_mgr
++ self.press_coords = None
++
++ self.cidpress = None
++ self.cidrelease = None
++
++ def StartCategoryEdit(self):
++ 'connect to all the events we need'
++ self.cidpress = self.canvas.mpl_connect(
++ 'button_press_event', self.OnPress)
++ self.cidrelease = self.canvas.mpl_connect(
++ 'button_release_event', self.OnRelease)
++ #self.cidmotion = self.canvas.mpl_connect(
++ # 'motion_notify_event', self.OnRelease)
++
++ def OnPress(self, event):
++ 'on button press we will see if the mouse is over us and store some data'
++
++ if event.xdata and event.ydata:
++ self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
++ else:
++ self.press_coords = None
++
++ def OnRelease(self, event):
++ 'on release we reset the press data'
++
++ if event.xdata and event.ydata and self.press_coords:
++
++ bbox = {}
++ if event.ydata > self.press_coords['y']:
++ bbox['up_y'] = event.ydata
++ bbox['btm_y'] = self.press_coords['y']
++ else:
++ bbox['up_y'] = self.press_coords['y']
++ bbox['btm_y'] = event.ydata
++
++ if event.xdata > self.press_coords['x']:
++ bbox['up_x'] = event.xdata
++ bbox['btm_x'] = self.press_coords['x']
++ else:
++ bbox['up_x'] = self.press_coords['x']
++ bbox['btm_x'] = event.xdata
++
++ self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
++
++ def StopCategoryEdit(self):
++ 'disconnect all the stored connection ids'
++
++ if self.cidpress:
++ self.canvas.mpl_disconnect(self.cidpress)
++ if self.cidrelease:
++ self.canvas.mpl_disconnect(self.cidrelease)
++ #self.canvas.mpl_disconnect(self.cidmotion)
++
++ def _createWidgets(self):
++
++ # Create the mpl Figure and FigCanvas objects.
++ # 5x4 inches, 100 dots-per-inch
++ #
++ self.dpi = 100
++ self.fig = Figure((5.0, 4.0), dpi=self.dpi)
++ self.canvas = FigCanvas(self, -1, self.fig)
++
++ # Since we have only one plot, we can use add_axes
++ # instead of add_subplot, but then the subplot
++ # configuration tool in the navigation toolbar wouldn't
++ # work.
++ #
++ self.axes = self.fig.add_subplot(111)
++
++ # Bind the 'pick' event for clicking on one of the bars
++ #
++ self.canvas.mpl_connect('button_press_event', self.on_pick)
++
++
++ # Create the navigation toolbar, tied to the canvas
++ #
++ self.toolbar = NavigationToolbar(self.canvas)
++
++ def _doLayout(self):
++
++ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
++ self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
++ self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
++ self.main_sizer.AddSpacer(10)
++
++ self.SetSizer(self.main_sizer)
++ self.main_sizer.Fit(self)
++
++ def Plot(self, scatts, styles):
++ """ Redraws the figure
++ """
++
++ self.axes.clear()
++ for cat_id, cat in scatts.iteritems():
++ if cat_id == 0:
++ cmap = matplotlib.cm.jet
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ else:
++ colors = styles[cat_id]['color'].split(":")
++
++ cmap = matplotlib.cm.jet
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ cmap._lut[:, 0] = int(colors[0])/255.0
++ cmap._lut[:, 1] = int(colors[1])/255.0
++ cmap._lut[:, 2] = int(colors[2])/255.0
++
++ masked_cat = np.ma.masked_less_equal(cat, 0)
++
++ self.axes.imshow(masked_cat, cmap = cmap, interpolation='nearest')
++
++ self.canvas.draw()
++
++ def on_pick(self, event):
++ pass
++ # The event received here is of the type
++ # matplotlib.backend_bases.PickEvent
++ #
++ # It carries lots of information, of which we're using
++ # only a small amount here.
++ #
++ #box_points = event.x
++
++ #msg = "You've clicked on a bar with coords:\n"
++
++ #dlg = wx.MessageDialog(
++ # self,
++ # msg,
++ # "Click!",
++ # wx.OK | wx.ICON_INFORMATION)
++
++ #dlg.ShowModal()
++ #dlg.Destroy()
++
++ def on_exit(self, event):
++
++ self.CleanUp()
++
++ def CleanUp(self):
++
++ self.parent.OnPlotClosed(self.scatt_id)
++ self.Destroy()
+\ No newline at end of file
+Index: gui/wxpython/scatt_plot/controllers.py
+===================================================================
+--- gui/wxpython/scatt_plot/controllers.py (revision 0)
++++ gui/wxpython/scatt_plot/controllers.py (working copy)
+@@ -0,0 +1,513 @@
++"""!
++ at package scatt_plot.controllers
++
++ at brief Controller layer for scatter plot tool.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++#TODO
++import wx
++
++from core.gcmd import GException, GError, RunCommand
++
++from scatt_plot.sc_pl_core import Core, BandsToidScatt
++
++from scatt_plot.gthreading import gThread
++from core.gconsole import EVT_CMD_DONE
++
++from grass.pydispatch.signal import Signal
++
++class ScattsManager(wx.EvtHandler):
++ def __init__(self, guiparent, giface, iclass_mapwin):
++ #TODO remove iclass parameter
++
++ wx.EvtHandler.__init__(self)
++ self.giface = giface
++ self.mapDisp = giface.GetMapDisplay()
++ self.mapWin = giface.GetMapWindow()
++
++ if iclass_mapwin:
++ self.mapWin = iclass_mapwin
++
++ self.guiparent = guiparent
++
++ self.core = Core()
++ self.scatts_dt = self.core.GetScattsData()
++
++ self.cats_mgr = CategoriesManager(self, self.core)
++
++ self.thread = gThread(self);
++
++ self.scatt_plts = {}
++ self.added_cats_rasts = {}
++
++ self.cats_to_update = []
++
++ self.edit_cat_type = None
++
++ self.data_set = False
++
++ if iclass_mapwin:
++ self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
++ self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
++ else:
++ self.mapWin_conn = None
++ self.iclass_conn = None
++
++ self.tasks_pids = {
++ 'add_scatt' : -1,
++ 'set_data' : -1,
++ 'set_edit_cat_data' : -1,
++ 'mapwin_conn' : [],
++ 'render_plots' : -1
++ }
++
++ self.Bind(EVT_CMD_DONE, self.OnThreadDone)
++
++ def SetData(self, bands):
++ self.data_set = False
++ self.tasks_pids['set_data'] = self.thread.GetId()
++ self.thread.Run(callable = self.core.SetData, bands = bands)
++
++ def SetDataDone(self, event):
++ self.data_set = True
++ self.cats_mgr.InitCoreCats()
++
++ def OnOutput(self, event):
++ """!Print thread output according to debug level.
++ """
++ print event.text
++
++ def GetBands(self):
++ return self.core.GetBands()
++
++ def AddScattPlot(self, scatt_id):
++
++ self.tasks_pids['add_scatt'] = self.thread.GetId()
++ self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
++
++ def RenderScattPlts(self):
++
++ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++ for scatt_id, scatt in self.scatt_plts.iteritems():
++ scatt_dt = self.scatts_dt.GetScatt(scatt_id)
++ scatt.Plot(scatt_dt, cats_attrs)
++
++
++ def AddScattPlotDone(self, event):
++
++ scatt_id = event.kwds['scatt_id']
++
++ #TODO guiparent - not very good
++ self.scatt_plts[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
++
++ if self.edit_cat_type:
++ self.scatt_plts[scatt_id].StartCategoryEdit()
++
++ scatt_dt = self.scatts_dt.GetScatt(scatt_id)
++
++ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++ self.scatt_plts[scatt_id].Plot(scatt_dt, cats_attrs)
++
++ self.scatt_plts[scatt_id].GetParent().Show()
++
++
++ def CleanUp(self):
++ self.core.CleanUp()
++
++ for scatt_id, scatt in self.scatt_plts.items():
++ scatt.CleanUp()
++ del self.scatt_plts[scatt_id]
++
++ def OnThreadDone(self, event):
++
++ if event.exception:
++ GError(str(event.exception))
++ return
++
++ if event.pid in self.tasks_pids['mapwin_conn']:
++ self.tasks_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.tasks_pids['mapwin_conn']:
++ self.tasks_pids['render_plots'] = self.thread.GetId()
++ self.thread.Run(callable = self.core.ComputeCatScatts,
++ cats_ids = self.cats_to_update[:])
++ del self.cats_to_update[:]
++
++ return
++
++ if self.tasks_pids['render_plots'] == event.pid:
++ #TODO how to put it to the thread - kils gui
++ self.RenderScattPlts()
++ return
++
++ if self.tasks_pids['add_scatt'] == event.pid:
++ self.AddScattPlotDone(event)
++ return
++
++ if self.tasks_pids['set_data'] == event.pid:
++ self.SetDataDone(event)
++ return
++
++ if self.tasks_pids['set_edit_cat_data'] == event.pid:
++ self.SetEditCatDataDone(event)
++ return
++
++ def EditCat(self, edit_type):
++
++ self.edit_cat_type = edit_type
++
++ if self.edit_cat_type:
++ for scatt in self.scatt_plts.itervalues():
++ scatt.StopCategoryEdit()
++ scatt.StartCategoryEdit()
++
++ else:
++ for scatt in self.scatt_plts.itervalues():
++ scatt.StopCategoryEdit()
++
++ def SetEditCatData(self, scatt_id, bbox):
++
++ value = 1
++ if self.edit_cat_type == 'remove':
++ value = 0
++
++ self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
++
++ sel_cat_id = self.cats_mgr.GetSelectedCat()
++
++ self.thread.Run(callable = self.core.SetEditCatData,
++ scatt_id = scatt_id,
++ cat_id = sel_cat_id,
++ bbox = bbox,
++ value = value)
++
++ def SetEditCatDataDone(self, event):
++
++ self.RenderScattPlts()
++
++ cat_id = event.kwds["cat_id"]
++
++ cat_rast = self.core.GetCatRastOut(cat_id)
++
++
++ if cat_rast not in self.added_cats_rasts.values():
++
++ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++ #TODO use standard raster lib
++ name = "cat_%d" % cat_id
++ ret, err_msg = RunCommand('r.external',
++ output = name,
++ getErrorMsg = True,
++ input = cat_rast,
++ flags = "o",
++ overwrite = True)
++
++ if ret != 0:
++ GError(_("r.external failed\n%s" % err_msg))
++
++ region = self.core.GetRegion()
++ ret, err_msg = RunCommand('r.region',
++ map = name,
++ getErrorMsg = True,
++ n = "%f" % region['n'],
++ s = "%f" % region['s'],
++ e = "%f" % region['e'],
++ w = "%f" % region['w'],
++ )
++
++ region = self.core.GetRegion()
++ ret, err_msg = RunCommand('r.colors',
++ map = name,
++ rules = "-",
++ stdin = "1 %s" % cats_attrs["color"],
++ getErrorMsg = True)
++
++ if ret != 0:
++ GError(_("r.region failed\n%s" % err_msg))
++
++ self.mapWin.Map.AddLayer(ltype = "raster", name = "cat_%d" % cat_id, render = True,
++ command = ["d.rast", "map=%s" % name, "values=1"])
++
++ #elif self.giface.GetLayerTree():
++ # self.giface.GetLayerTree().AddLayer("raster", lname = "cat_%d" % cat_id, lchecked = True,
++ # lcmd = ["d.rast", "map=%s" % name, "values=1"])
++
++ if ret != 0:
++ GError(_("r.region failed\n%s" % err_msg))
++
++ self.added_cats_rasts[cat_id] = cat_rast
++
++ self.giface.updateMap.emit()
++
++ def DigitDataChanged(self, vectMap, digit):
++
++ if self.mapWin_conn:
++ self.mapWin_conn.DigitDataChanged(vectMap, digit)
++ return 1
++ else:
++ return 0
++
++ def GetCategoriesManager(self):
++ return self.cats_mgr
++
++class CategoriesManager:
++
++ def __init__(self, scatt_mgr, core):
++
++ self.core = core
++ self.scatt_mgr = scatt_mgr
++
++ self.cats = {}
++ self.cats_ids = []
++
++ self.sel_cat_id = -1
++
++ self.initialized = Signal('CategoriesManager.initialized')
++ self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
++ self.deletedCategory = Signal('CategoriesManager.deletedCategory')
++ self.addedCategory = Signal('CategoriesManager.addedCategory')
++
++ def Clear(self):
++
++ self.cats.clear()
++ del self.cats_ids[:]
++
++ self.sel_cat_id = -1
++
++ def InitCoreCats(self):
++ if self.scatt_mgr.data_set:
++ for cat_id in self.cats_ids:
++ self.core.AddCategory(cat_id)
++
++ def AddCategory(self, cat_id = None, name = None, color = 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:
++ ret = self.core.AddCategory(cat_id)
++ if ret < 0: #TODO
++ return -1;
++
++ self.cats[cat_id] = {
++ 'name' : _('Category %s' % cat_id ),
++ 'color' : "0:0:0"
++ }
++ self.cats_ids.append(cat_id)
++
++ if name is not None:
++ self.cats[cat_id]["name"] = name
++
++ if color is not None:
++ self.cats[cat_id]["color"] = color
++
++ 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):
++ for k, v in attrs_dict.iteritems():
++ self.cats[cat_id][k] = v
++
++ self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
++
++ def DeleteCategory(self, cat_id):
++
++ if self.scatt_mgr.data_set:
++ self.core.DeleteCategory(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
++
++ 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[:]
++
++class MapWinConnection:
++ 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.thread = self.scatt_mgr.thread
++
++ #TODO
++ self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
++
++ #def ChangeMap(self, vectMap, layers_cats):
++ # self.vectMap = vectMap
++ # self.layers_cats = layers_cats
++
++ #ret, region, msg = RunCommand("v.to.rast",
++ # flags = "gp",
++ # getErrorMsg = True,
++ # read = True)
++
++ 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.ChangeVertex)
++ self.digit.vertexRemoved.connect(self.ChangeVertex)
++ self.digit.lineEdited.connect(self.ChangeVertex)
++ self.digit.featuresMoved.connect(self.MoveFeatures)
++
++ def AddFeature(self, new_geom, cat):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.AddedFeature,
++ new_geom = new_geom,
++ cat = cat)
++
++ def DeleteAreas(self, old_geoms, old_areas_cats):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.DeletedAreas,
++ old_geoms = old_geoms,
++ old_areas_cats = old_areas_cats)
++
++ def ChangeVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.ChangedVertex,
++ new_geom = new_geom,
++ old_geom = old_geom,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
++ def MoveFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.MovedFeatures,
++ move = move,
++ old_geoms = old_geoms,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
++ def DigitDataChanged(self, vectMap, digit):
++
++ self.digit = digit
++ self.vectMap = vectMap
++
++ self.scatt_rast_updater.SetVectMap(vectMap)
++
++ self._connectSignals()
++
++class IClassConnection:
++ 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.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.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
++ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++ self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++ self.SyncCats()
++
++ def SyncCats(self):
++ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++ cats = self.stats_data.GetCategories()
++ for c in cats:
++ stats = self.stats_data.GetStatistics(c)
++ self.cats_mgr.AddCategory(c, stats.name, stats.color)
++ self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++ def AddCategory(self, cat, name, color):
++ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++ self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
++ 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']:
++ 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)
+Index: gui/wxpython/vdigit/wxdigit.py
+===================================================================
+--- gui/wxpython/vdigit/wxdigit.py (revision 57285)
++++ gui/wxpython/vdigit/wxdigit.py (working copy)
+@@ -17,7 +17,7 @@
+ (and NumPy would be an excellent candidate for acceleration via
+ e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+
+-(C) 2007-2011 by the GRASS Development Team
++(C) 2007-2011, 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.
+@@ -27,6 +27,8 @@
+
+ import grass.script.core as grass
+
++from grass.pydispatch.signal import Signal
++
+ from core.gcmd import GError
+ from core.debug import Debug
+ from core.settings import UserSettings
+@@ -176,7 +178,17 @@
+
+ if self.poMapInfo:
+ self.InitCats()
+-
++
++ #TODO signal for errors?
++ self.featureAdded = Signal('IVDigit.featureAdded')
++ self.areasDeleted = Signal('IVDigit.areasDeleted')
++ self.vertexMoved = Signal('IVDigit.vertexMoved')
++ self.vertexAdded = Signal('IVDigit.vertexAdded')
++ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
++ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
++ self.featuresMoved = Signal('IVDigit.featuresMoved')
++ self.lineEdited = Signal('IVDigit.lineEdited')
++
+ def __del__(self):
+ Debug.msg(1, "IVDigit.__del__()")
+ Vect_destroy_line_struct(self.poPoints)
+@@ -394,7 +406,6 @@
+
+ @return tuple (number of added features, feature ids)
+ """
+-
+ layer = self._getNewFeaturesLayer()
+ cat = self._getNewFeaturesCat()
+
+@@ -419,10 +430,14 @@
+ return (-1, None)
+
+ self.toolbar.EnableUndo()
+-
+- return self._addFeature(vtype, points, layer, cat,
+- self._getSnapMode(), self._display.GetThreshold())
+-
++
++ ret = self._addFeature(vtype, points, layer, cat,
++ self._getSnapMode(), self._display.GetThreshold())
++ if ret[0] > -1:
++ self.featureAdded.emit(new_geom = points, cat = {layer : [cat]})
++
++ return ret
++
+ def DeleteSelectedLines(self):
+ """!Delete selected features
+
+@@ -434,16 +449,25 @@
+ # colect categories for delete if requested
+ deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
+ catDict = dict()
++
++ old_geoms = []
++ old_areas_cats = []
+ if deleteRec:
+ for i in self._display.selected['ids']:
++
+ if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
+ self._error.ReadLine(i)
+
++ old_geoms.append(self._getGeom(i))
++ old_areas_cats.append(self._getLineAreasCategories(i))
++
+ cats = self.poCats.contents
+- for j in range(cats.n_cats):
+- if cats.field[j] not in catDict.keys():
+- catDict[cats.field[j]] = list()
+- catDict[cats.field[j]].append(cats.cat[j])
++
++ # catDict was not used -> put into comment
++ #for j in range(cats.n_cats):
++ # if cats.field[j] not in catDict.keys():
++ # catDict[cats.field[j]] = list()
++ # catDict[cats.field[j]].append(cats.cat[j])
+
+ poList = self._display.GetSelectedIList()
+ nlines = Vedit_delete_lines(self.poMapInfo, poList)
+@@ -456,7 +480,8 @@
+ self._deleteRecords(cats)
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++ self.featuresDeleted.emit(old_geoms = old_geoms, old_areas_cats = old_areas_cats)
++
+ return nlines
+
+ def _deleteRecords(self, cats):
+@@ -512,22 +537,123 @@
+
+ @return number of deleted
+ """
++ if len(self._display.selected['ids']) < 1:
++ return 0
++
+ poList = self._display.GetSelectedIList()
+ cList = poList.contents
+
+ nareas = 0
++ old_geoms = []
++ old_areas_cats = []
++
+ for i in range(cList.n_values):
++
+ if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
+ continue
+-
++
++ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
++ if area > 0:
++ geoms, cats = self._getaAreaGeomsCats(area)
++ old_geoms += geoms
++ old_areas_cats += cats
++
+ nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
+
+ if nareas > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
++ self.areasDeleted.emit(old_geoms = old_geoms, old_areas_cats = old_areas_cats)
++
++ return nareas
++
++
++ def _getaAreaGeomsCats(self, area):
++
++ po_b_list = Vect_new_list()
++ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
++ b_list = po_b_list.contents
++
++ geoms = []
++ areas_cats = []
++
++ if b_list.n_values > 0:
++ for i_line in range(b_list.n_values):
++
++ line = b_list.value[i_line];
++
++ geoms.append(self._getGeom(abs(line)))
++ areas_cats.append(self._getLineAreasCategories(abs(line)))
+
+- return nareas
++ Vect_destroy_list(po_b_list);
++
++ return geoms, areas_cats
++
++ def _getLineAreasCategories(self, ln_id):
++
++ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
++ if ltype != GV_BOUNDARY:
++ return []
++
++ cats = [None, None]
++
++ left = c_int()
++ right = c_int()
++
++ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
++ areas = [left.value, right.value]
++
++ for i, a in enumerate(areas):
++ if a > 0:
++ centroid = Vect_get_area_centroid(self.poMapInfo, a)
++ if centroid <= 0:
++ continue
++ c = self._getCategories(centroid)
++ if c:
++ cats[i] = c
++
++ return cats
++
++ def _getCategories(self, ln_id):
++
++ poCats = Vect_new_cats_struct()
++ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
++ Vect_destroy_cats_struct(poCats)
++ return None
++
++ cCats = poCats.contents
++
++ cats = {}
++ for j in range(cCats.n_cats):
++ if cats.has_key(cCats.field[j]):
++ cats[cCats.field[j]].append(cCats.cat[j])
++ else:
++ cats[cCats.field[j]] = [cCats.cat[j]]
+
++ Vect_destroy_cats_struct(poCats)
++ return cats
++
++ def _getGeom(self, ln_id):
++
++ poPoints = Vect_new_line_struct()
++ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
++ Vect_destroy_line_struct(poPoints)
++ return None
++
++ geom = self._convertGeom(poPoints)
++ Vect_destroy_line_struct(poPoints)
++ return geom
++
++ def _convertGeom(self, poPoints):
++
++ Points = poPoints.contents
++
++ pts_geom = []
++ for j in range(Points.n_points):
++ pts_geom.append((Points.x[j], Points.y[j]))
++
++ return pts_geom
++
+ def MoveSelectedLines(self, move):
+ """!Move selected features
+
+@@ -536,16 +662,39 @@
+ if not self._checkMap():
+ return -1
+
++ nsel = len(self._display.selected['ids'])
++ if nsel < 1:
++ return -1
++
+ thresh = self._display.GetThreshold()
+ snap = self._getSnapMode()
+
+ poList = self._display.GetSelectedIList()
++
++ old_geoms = []
++ old_areas_cats = []
++ for sel_id in self._display.selected['ids']:
++ old_geoms.append(self._getGeom(sel_id))
++ old_areas_cats.append(self._getLineAreasCategories(sel_id))
++
++ poNewIds = Vect_new_list()
+ nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList,
+ move[0], move[1], 0,
+- snap, thresh)
++ snap, thresh, poNewIds)
++
+ Vect_destroy_list(poList)
+
++ cList = poNewIds.contents
++
++ if nlines > 0:
++ new_areas_cats = []
++ for i in range(cList.n_values):
++ new_id = cList.value[i]
++ new_areas_cats.append(self._getLineAreasCategories(new_id))
++
++ Vect_destroy_list(poNewIds)
++
+ if nlines > 0 and self._settings['breakLines']:
+ for i in range(1, nlines):
+ self._breakLineAtIntersection(nlines + i, None, changeset)
+@@ -553,7 +702,11 @@
+ if nlines > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ self.featuresMoved.emit(move = move,
++ old_geoms = old_geoms,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
+ return nlines
+
+ def MoveSelectedVertex(self, point, move):
+@@ -571,20 +724,33 @@
+
+ if len(self._display.selected['ids']) != 1:
+ return -1
+-
++
++ # move only first found vertex in bbox
++ poList = self._display.GetSelectedIList()
++
++ cList = poList.contents
++ old_geom = self._getGeom(cList.value[0])
++ old_areas_cats = self._getLineAreasCategories(cList.value[0])
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, point[0], point[1], 0.0)
+-
+- # move only first found vertex in bbox
+- poList = self._display.GetSelectedIList()
++
++ poNewIds = Vect_new_list()
+ moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList, self.poPoints,
+ self._display.GetThreshold(type = 'selectThresh'),
+ self._display.GetThreshold(),
+ move[0], move[1], 0.0,
+- 1, self._getSnapMode())
++ 1, self._getSnapMode(), poNewIds)
+ Vect_destroy_list(poList)
+-
++
++ new_id = poNewIds.contents.value[0]
++ Vect_destroy_list(poNewIds)
++
++ if new_id > -1:
++ new_geom = self._getGeom(new_id)
++ new_areas_cats = self._getLineAreasCategories(new_id)
++
+ if moved > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+@@ -592,7 +758,13 @@
+ if moved > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if new_id > -1:
++ self.vertexMoved.emit(new_geom = new_geom,
++ new_areas_cats = new_areas_cats,
++ old_areas_cats = old_areas_cats,
++ old_geom = old_geom)
++
+ return moved
+
+ def AddVertex(self, coords):
+@@ -681,6 +853,9 @@
+ self._error.ReadLine(line)
+ return -1
+
++ old_geom = self._getGeom(line)
++ old_areas_cats = self._getLineAreasCategories(line)
++
+ # build feature geometry
+ Vect_reset_line(self.poPoints)
+ for p in coords:
+@@ -703,7 +878,15 @@
+ if newline > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ new_geom = self._getGeom(newline)
++ new_areas_cats = self._getLineAreasCategories(newline)
++
++ self.lineEdited.emit(old_geom = old_geom,
++ old_areas_cats = old_areas_cats,
++ new_geom = new_geom,
++ new_areas_cats = new_areas_cats)
++
+ return newline
+
+ def FlipLine(self):
+@@ -1510,26 +1693,49 @@
+ return 0
+
+ poList = self._display.GetSelectedIList()
++ cList = poList.contents
++
++ old_geom = self._getGeom(cList.value[0])
++ old_areas_cats = self._getLineAreasCategories(cList.value[0])
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
+
+ thresh = self._display.GetThreshold(type = 'selectThresh')
+
++ poNewIds = Vect_new_list()
++
+ if add:
+ ret = Vedit_add_vertex(self.poMapInfo, poList,
+- self.poPoints, thresh)
++ self.poPoints, thresh, poNewIds)
+ else:
+ ret = Vedit_remove_vertex(self.poMapInfo, poList,
+- self.poPoints, thresh)
++ self.poPoints, thresh, poNewIds)
++
++ new_id = poNewIds.contents.value[0]
++ Vect_destroy_list(poNewIds)
+ Vect_destroy_list(poList)
++
++ if new_id > -1:
++ new_geom = self._getGeom(new_id)
++ new_areas_cats = self._getLineAreasCategories(new_id)
+
+ if not add and ret > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+-
++
+ if ret > 0:
+ self._addChangeset()
+-
++
++ if new_id > -1:
++ if add:
++ self.vertexAdded.emit(old_geom = old_geom, new_geom = new_geom)
++ else:
++ self.vertexRemoved.emit(old_geom = old_geom,
++ new_geom = new_geom,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
+ return 1
+
+ def GetLineCats(self, line):
+Index: gui/wxpython/vdigit/toolbars.py
+===================================================================
+--- gui/wxpython/vdigit/toolbars.py (revision 57285)
++++ gui/wxpython/vdigit/toolbars.py (working copy)
+@@ -17,6 +17,7 @@
+ import wx
+
+ from grass.script import core as grass
++from grass.pydispatch.signal import Signal
+
+ from gui_core.toolbars import BaseToolbar, BaseIcons
+ from gui_core.dialogs import CreateNewVector
+@@ -42,6 +43,8 @@
+ self.digit = None
+ self._giface = giface
+
++ self.editingStarted = Signal("VDigitToolbar.editingStarted")
++
+ # currently selected map layer for editing (reference to MapLayer instance)
+ self.mapLayer = None
+ # list of vector layers from Layer Manager (only in the current mapset)
+@@ -820,6 +823,7 @@
+ alpha = int(opacity * 255)
+ self.digit.GetDisplay().UpdateSettings(alpha = alpha)
+
++ self.editingStarted.emit(vectMap = mapLayer.GetName())
+ return True
+
+ def StopEditing(self):
+Index: gui/wxpython/Makefile
+===================================================================
+--- gui/wxpython/Makefile (revision 57285)
++++ gui/wxpython/Makefile (working copy)
+@@ -13,7 +13,7 @@
+ $(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
+ gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapdisp/*.py \
+ mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* vdigit/* \
+- vnet/*.py web_services/*.py wxplot/*.py) \
++ vnet/*.py web_services/*.py wxplot/*.py scatt_plot/*.py) \
+ gis_set.py gis_set_error.py wxgui.py README
+
+ DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) \
+@@ -21,7 +21,7 @@
+
+ PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
+ gui_core iclass lmgr location_wizard mapdisp modules nviz psmap \
+- mapswipe vdigit wxplot web_services rlisetup vnet)
++ mapswipe vdigit wxplot web_services rlisetup vnet scatt_plot)
+
+ DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
+
+Index: gui/wxpython/mapdisp/toolbars.py
+===================================================================
+--- gui/wxpython/mapdisp/toolbars.py (revision 57285)
++++ gui/wxpython/mapdisp/toolbars.py (working copy)
+@@ -243,7 +243,8 @@
+ (MapIcons["scatter"], self.parent.OnScatterplot),
+ (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
+ (BaseIcons["histogramD"], self.parent.OnHistogram),
+- (MapIcons["vnet"], self.parent.OnVNet)))
++ (MapIcons["vnet"], self.parent.OnVNet),
++ (MapIcons["scatter"], self.parent.OnScatterplot2)))
+
+ def OnDecoration(self, event):
+ """!Decorations overlay menu
+Index: gui/wxpython/mapdisp/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py (revision 57285)
++++ gui/wxpython/mapdisp/frame.py (working copy)
+@@ -207,6 +207,7 @@
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
++ self.dialogs['scatt_plot'] = None
+ self.dialogs['category'] = None
+ self.dialogs['barscale'] = None
+ self.dialogs['legend'] = None
+@@ -1301,6 +1302,19 @@
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['map']
+
++ def OnScatterplot2(self, event):
++ """!Init interactive scatterplot tools
++ """
++ if self.dialogs['scatt_plot']:
++ self.dialogs['scatt_plot'].Raise()
++ return
++
++ from scatt_plot.dialogs import ScattPlotMainDialog
++ self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface)
++
++ self.dialogs['scatt_plot'].CenterOnScreen()
++ self.dialogs['scatt_plot'].Show()
++
+ def OnVNet(self, event):
+ """!Dialog for v.net* modules
+ """
+Index: lib/vector/vedit/move.c
+===================================================================
+--- lib/vector/vedit/move.c (revision 57285)
++++ lib/vector/vedit/move.c (working copy)
+@@ -22,13 +22,16 @@
+ \param List list of primitives to be moved
+ \param move_x,move_y,move_z direction (move_z used only if map is 3D)
+ \param snap enable snapping (see globals.h)
+-
++ \param new_ids list of newly assigned ids for features, which vertexes were moved,
++ new_id corresponds to same position of id in List
++ null pointer can be passed
++
+ \return number of modified primitives
+ \return -1 on error
+ */
+ int Vedit_move_lines(struct Map_info *Map, struct Map_info **BgMap,
+ int nbgmaps, struct ilist *List, double move_x,
+- double move_y, double move_z, int snap, double thresh)
++ double move_y, double move_z, int snap, double thresh, struct ilist *new_ids)
+ {
+ struct line_pnts *Points;
+ struct line_cats *Cats;
+@@ -84,6 +87,8 @@
+ if (newline < 0) {
+ return -1;
+ }
++ if(new_ids)
++ Vect_list_append(new_ids, newline);
+
+ nlines_moved++;
+ }
+Index: lib/vector/vedit/vertex.c
+===================================================================
+--- lib/vector/vedit/vertex.c (revision 57285)
++++ lib/vector/vedit/vertex.c (working copy)
+@@ -26,7 +26,10 @@
+ \param move_x,move_y,move_z direction (move_z is used when map is 3D)
+ \param move_first move only first vertex found in the bounding box
+ \param snap snapping mode (see vedit.h)
+-
++ \param new_ids list of newly assigned ids for features, which vertexes were moved,
++ new_id corresponds to same position of id in List
++ if feature was not rewritten new id is -1
++ null pointer can be passed
+ \return number of moved verteces
+ \return -1 on error
+ */
+@@ -34,11 +37,11 @@
+ int nbgmaps, struct ilist *List,
+ struct line_pnts *coord, double thresh_coords,
+ double thresh_snap, double move_x, double move_y,
+- double move_z, int move_first, int snap)
++ double move_z, int move_first, int snap, struct ilist *new_ids)
+ {
+ int nvertices_moved, nlines_modified, nvertices_snapped;
+
+- int i, j, k;
++ int i, j, k, newline;
+ int line, type, rewrite;
+ int npoints;
+ double east, north, dist;
+@@ -159,12 +162,17 @@
+ } /* for each coord */
+
+ if (rewrite) {
+- if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
++ newline = Vect_rewrite_line(Map, line, type, Points, Cats);
++ if (newline < 0) {
+ return -1;
+ }
++ if(new_ids)
++ Vect_list_append(new_ids, newline);
+
+ nlines_modified++;
+ }
++ else if(new_ids)
++ Vect_list_append(new_ids, -1);
+ } /* for each selected line */
+
+ /* destroy structures */
+@@ -187,12 +195,15 @@
+ \param List list of lines
+ \param coord points location
+ \param thresh find line in given threshold
+-
++ \param new_ids list of newly assigned ids for features, which vertexes were moved,
++ new_id corresponds to same position of id in List
++ if feature was not rewritten new id is -1
++ null pointer can be passed
+ \return number of add verteces
+ \return -1 on error
+ */
+ int Vedit_add_vertex(struct Map_info *Map, struct ilist *List,
+- struct line_pnts *coord, double thresh)
++ struct line_pnts *coord, double thresh, struct ilist *new_ids)
+ {
+ int i, j;
+ int type, line, seg;
+@@ -200,6 +211,7 @@
+ double east, north, dist;
+ double *x, *y, *z;
+ double px, py;
++ int newline;
+
+ struct line_pnts *Points;
+ struct line_cats *Cats;
+@@ -253,12 +265,17 @@
+ /* rewrite the line */
+ if (rewrite) {
+ Vect_line_prune(Points);
+- if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
++ newline = Vect_rewrite_line(Map, line, type, Points, Cats);
++ if (newline < 0) {
+ return -1;
+ }
++ if(new_ids)
++ Vect_list_append(new_ids, newline);
+
+ nlines_modified++;
+ }
++ else if(new_ids)
++ Vect_list_append(new_ids, -1);
+ } /* for each line */
+
+ /* destroy structures */
+@@ -277,12 +294,15 @@
+ \param List list of selected lines
+ \param coord points location
+ \param thresh threshold value to find a line
+-
++ \param new_ids list of newly assigned ids for features, which vertexes were moved,
++ new_id corresponds to same position of id in List
++ if feature was not rewritten new id is -1
++ null pointer can be passed
+ \return number of removed vertices
+ \return -1 on error
+ */
+ int Vedit_remove_vertex(struct Map_info *Map, struct ilist *List,
+- struct line_pnts *coord, double thresh)
++ struct line_pnts *coord, double thresh, struct ilist *new_ids)
+ {
+ int i, j, k;
+ int type, line;
+@@ -290,6 +310,7 @@
+ double east, north;
+ double dist;
+ double *x, *y, *z;
++ int newline;
+
+ struct line_pnts *Points;
+ struct line_cats *Cats;
+@@ -336,12 +357,18 @@
+
+ if (rewrite) {
+ /* rewrite the line */
+- if (Vect_rewrite_line(Map, line, type, Points, Cats) < 0) {
++ newline = Vect_rewrite_line(Map, line, type, Points, Cats);
++ if (newline < 0) {
+ return -1;
+ }
++ if(new_ids)
++ Vect_list_append(new_ids, newline);
+
+ nlines_modified++;
+ }
++ else if(new_ids)
++ Vect_list_append(new_ids, -1);
++
+ } /* for each line */
+
+ /* destroy structures */
+Index: lib/imagery/scatt_sccats.c
+===================================================================
+--- lib/imagery/scatt_sccats.c (revision 0)
++++ lib/imagery/scatt_sccats.c (working copy)
+@@ -0,0 +1,321 @@
++/*!
++ \file lib/imagery/scatt_cat_rast.c
++
++ \brief Imagery library - functions for wx Scatter Plot Tool.
++
++ Low level functions used by wx Scatter Plot Tool.
++
++ Copyright (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 at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++//TODO split formulas to be more understandable
++static void id_scatt_to_bands(const int scatt_id, const int n_bands, int * band_1, int * band_2)
++{
++ int n_b1 = n_bands - 1;
++
++ * band_1 = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
++
++ * band_2 = scatt_id - ((* band_1) * (2 * n_b1 + 1) - (* band_1) * (* band_1)) / 2 + (* band_1) + 1;
++
++ return;
++}
++
++static void bands_to_id_scatt(const int band_1, const int band_2, const int n_bands, int * scatt_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * scatt_id = (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_2 - band_1 - 1;
++
++ return;
++}
++
++void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
++{
++ int i_cat;
++
++ cats->type = type;
++
++ cats->n_cats = 10;
++ cats->n_a_cats = 0;
++
++ cats->n_bands = n_bands;
++ cats->n_scatts = (n_bands - 1) * n_bands / 2;
++
++ cats->cats_arr = (struct scScatts **) malloc(cats->n_cats * sizeof(struct scScatts *));
++ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
++
++ cats->cats_ids = (int *) malloc(cats->n_cats * sizeof(int));
++ cats->cats_idxs =(int *) malloc(cats->n_cats * sizeof(int));
++
++ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
++ cats->cats_idxs[i_cat] = -1;
++
++ return;
++}
++
++void I_sc_free_cats(struct scCats * cats)
++{
++ int i_cat;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ if(cats->cats_arr[i_cat])
++ {
++ free(cats->cats_arr[i_cat]->scatt_idxs);
++ free(cats->cats_arr[i_cat]->scatts_bands);
++ free(cats->cats_arr[i_cat]->scatts_arr);
++ free(cats->cats_arr[i_cat]);
++ }
++ }
++
++ free(cats->cats_ids);
++ free(cats->cats_idxs);
++ free(cats->cats_arr);
++
++ cats->n_cats = 0;
++ cats->n_a_cats = 0;
++ cats->n_bands = 0;
++ cats->n_scatts = 0;
++ cats->type = -1;
++
++ return;
++}
++
++void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
++{
++ a_cats_ids = cats->cats_ids;
++ * n_a_cats = cats->n_a_cats;
++}
++
++int I_sc_add_cat(struct scCats * cats, int cat_id)
++{
++ int i_scatt;
++ int n_a_cats = cats->n_a_cats;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ if(cats->cats_idxs[cat_id] >= 0)
++ return -1;
++
++ cats->cats_ids[n_a_cats] = cat_id;
++ cats->cats_idxs[cat_id] = n_a_cats;
++
++ cats->cats_arr[n_a_cats] = (struct scScatts *) malloc(sizeof(struct scScatts));
++
++ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) malloc(cats->n_scatts * sizeof(struct scdScattData *));
++ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
++
++ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
++
++ cats->cats_arr[n_a_cats]->scatts_bands = (int *) malloc(cats->n_scatts * 2 * sizeof(int));
++
++ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) malloc(cats->n_scatts * sizeof(int));
++ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
++ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++
++ ++cats->n_a_cats;
++
++ return 0;
++}
++
++int I_sc_delete_cat(struct scCats * cats, int cat_id)
++{
++ int cat_idx, i_cat;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ free(cats->cats_arr[cat_idx]->scatt_idxs);
++ free(cats->cats_arr[cat_idx]->scatts_bands);
++ free(cats->cats_arr[cat_idx]->scatts_arr);
++ free(cats->cats_arr[cat_idx]);
++
++ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
++ {
++ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
++ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
++ }
++ cats->cats_idxs[cat_id] = -1;
++
++ --cats->n_a_cats;
++
++ return 0;
++}
++
++
++int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_vals, int cat_id, int scatt_id)
++{
++ int band_1, band_2, cat_idx, n_a_scatts;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] >= 0)
++ return -1;
++
++ if(!scatt_vals->b_conds_arr && cats->type == 1)
++ return -1;
++
++ if(!scatt_vals->scatt_vals_arr && cats->type == 0)
++ return -1;
++
++ n_a_scatts = scatts->n_a_scatts;
++
++ scatts->scatt_idxs[scatt_id] = n_a_scatts;
++
++ id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
++
++ scatts->scatts_bands[n_a_scatts * 2] = band_1;
++ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
++
++ scatts->scatts_arr[n_a_scatts] = scatt_vals;
++ ++scatts->n_a_scatts;
++
++ return 0;
++}
++
++int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_vals, int cat_id, int scatt_id)
++{
++ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 && cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ scatt_vals = scatts->scatts_arr[scatt_idx];
++
++ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
++ {
++ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
++ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
++ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
++ }
++ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
++
++ scatts->scatt_idxs[scatt_id] = -1;
++
++ scatt_vals = scatts->scatts_arr[scatt_id];
++ scatts->n_a_scatts--;
++
++ return 0;
++}
++
++int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
++{
++ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
++ int cat_idx, scatt_idx, ret;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
++
++ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
++
++ return 0;
++}
++
++void I_scd_init_scatt_data(struct scdScattData * scatt_vals, int n_vals, int type)
++{
++ scatt_vals->n_vals = n_vals;
++
++ if(type == 0)
++ {
++ scatt_vals->scatt_vals_arr = (unsigned int *) malloc(n_vals * sizeof(unsigned int));
++ memset(scatt_vals->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
++ scatt_vals->b_conds_arr = NULL;
++ }
++ else if(type == 1)
++ {
++ scatt_vals->b_conds_arr = (unsigned short *) malloc(n_vals * sizeof(unsigned short));
++ memset(scatt_vals->b_conds_arr, 0, n_vals * sizeof(unsigned short));
++ scatt_vals->scatt_vals_arr = NULL;
++ }
++
++ return;
++}
++
++void I_scd_free_scatt_data(struct scdScattData * scatt_vals)
++{
++
++ free(scatt_vals->b_conds_arr);
++ free(scatt_vals->scatt_vals_arr);
++
++ scatt_vals = NULL;
++
++ return;
++}
++
++int I_scd_get_data_size(struct scdScattData * scatt_vals)
++{
++ return scatt_vals->n_vals;
++}
++
++void * I_scd_get_data_ptr(struct scdScattData * scatt_vals)
++{
++ if(!scatt_vals->b_conds_arr)
++ return scatt_vals->b_conds_arr;
++ else if(!scatt_vals->scatt_vals_arr)
++ return scatt_vals->scatt_vals_arr;
++
++ return NULL;
++}
++
++int I_scd_set_value(struct scdScattData * scatt_vals, unsigned int val_idx, unsigned int val)
++{
++ if(val_idx < 0 && val_idx > scatt_vals->n_vals)
++ return -1;
++
++ if(scatt_vals->b_conds_arr)
++ scatt_vals->b_conds_arr[val_idx] = val;
++ else if(scatt_vals->scatt_vals_arr)
++ scatt_vals->scatt_vals_arr[val_idx] = val;
++ else
++ return -1;
++
++ return 0;
++}
+Index: lib/imagery/scatt.c
+===================================================================
+--- lib/imagery/scatt.c (revision 0)
++++ lib/imagery/scatt.c (working copy)
+@@ -0,0 +1,582 @@
++/*!
++ \file lib/imagery/scatt.c
++
++ \brief Imagery library - functions for wx Scatter Plot Tool.
++
++ Low level functions used by wx Scatter Plot Tool.
++
++ Copyright (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 at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++
++static const int CAT_RAST_SCATT_SEL = 2;
++static const int CAT_RAST_RAST_SEL = 1;
++static const int CAT_RAST_NULL = 0;
++
++static int get_cat_rast_header(struct Cell_head * region, char * header){
++ return sprintf(header, "P5\n%d\n%d\n1\n", region->cols, region->rows);
++}
++
++int I_create_cat_rast(struct Cell_head * cat_rast_region, const char * cat_rast)
++{
++ FILE * f_cat_rast;
++ char cat_rast_header[1024];//TODO magic number
++ int i_row, i_col;
++ int head_nchars;
++
++ unsigned char * row_data;
++
++ f_cat_rast = fopen(cat_rast, "wb");
++ if(!f_cat_rast)
++ return -1;
++
++ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
++
++ fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cat_rast);
++ if (ferror(f_cat_rast)){
++ fclose(f_cat_rast);
++ return -1;
++ }
++
++ row_data = (unsigned char *) G_malloc(cat_rast_region->cols * sizeof(unsigned char));
++ for(i_col = 0; i_col < cat_rast_region->cols; i_col++)
++ row_data[i_col] = 0 & 255;
++
++ for(i_row = 0; i_row < cat_rast_region->rows; i_row++) {
++ fwrite(row_data, sizeof(unsigned char), (cat_rast_region->cols)/sizeof(unsigned char), f_cat_rast);
++ // TODO when code will be paralelized put it out of the loop
++ if (ferror(f_cat_rast))
++ {
++ fclose(f_cat_rast);
++ G_debug(3, "Unable to write into file.");
++ return -1;
++ }
++ }
++
++ fclose(f_cat_rast);
++ return 0;
++}
++
++static int print_reg(struct Cell_head * intersec, const char * pref)
++{
++ G_message("%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south,
++ intersec->east, intersec->west, intersec->ns_res, intersec->ew_res);
++}
++
++static int regions_intersecion(struct Cell_head * A, struct Cell_head * B, struct Cell_head * intersec)
++{
++
++ if(B->north < A->south) return -1;
++ else if(B->north > A->north) intersec->north = A->north;
++ else intersec->north = B->north;
++
++ if(B->south > A->north) return -1;
++ else if(B->south < A->south) intersec->south = A->south;
++ else intersec->south = B->south;
++
++ if(B->east < A->west) return -1;
++ else if(B->east > A->east) intersec->east = A->east;
++ else intersec->east = B->east;
++
++ if(B->west > A->east) return -1;
++ else if(B->west < A->west) intersec->west = A->west;
++ else intersec->west = B->west;
++
++ if(intersec->north == intersec->south) return -1;
++
++ if(intersec->east == intersec->west) return -1;
++
++ return 0;
++
++}
++
++static int get_rows_and_cols_bounds(struct Cell_head * A, struct Cell_head * B, struct Cell_head * A_bounds, struct Cell_head * B_bounds)
++{
++ float ns_res, ew_res;
++
++ struct Cell_head intersec;
++
++ if(A->ns_res != B->ns_res)
++ return -1;
++
++ if(A->ew_res != B->ew_res)
++ return -1;
++
++ ns_res = A->ns_res;
++ ew_res = A->ew_res;
++
++ if(regions_intersecion(A, B, &intersec) == -1)
++ return -1;
++
++ A_bounds->north = ceil((A->north - intersec.north - ns_res * 0.5) / ns_res);
++ A_bounds->south = ceil((A->north - intersec.south - ns_res * 0.5) / ns_res);
++
++ A_bounds->east = ceil((intersec.east - A->west - ew_res * 0.5) / ew_res);
++ A_bounds->west = ceil((intersec.west - A->west - ew_res * 0.5) / ew_res);
++
++ B_bounds->north = ceil((B->north - intersec.north - ns_res * 0.5) / ns_res);
++ B_bounds->south = ceil((B->north - intersec.south - ns_res * 0.5) / ns_res);
++
++ B_bounds->east = ceil((intersec.east - B->west - ew_res * 0.5) / ew_res);
++ B_bounds->west = ceil((intersec.west - B->west - ew_res * 0.5) / ew_res);
++
++ return 0;
++}
++
++
++int I_insert_patch_to_cat_rast(const char * patch_rast, struct Cell_head * cat_rast_region, const char * cat_rast)
++{
++
++ FILE * f_cat_rast;
++ struct Cell_head patch_region, patch_bounds, cat_rast_bounds;
++ char cat_rast_header[1024];//TODO magic number
++ int i_row, i_col, ncols, nrows, cat_rast_col, patch_col, val;
++ int head_nchars;
++ int fd_patch_rast, init_shift, step_shift;
++ unsigned char * patch_data;
++
++ char * null_chunk;
++
++ //TODO G_free mapset (aslo in compute scatts)
++ const char *mapset;
++
++ struct Cell_head patch_lines, cat_rast_lines;
++
++ unsigned char * row_data;
++
++ f_cat_rast = fopen(cat_rast, "rb+");
++ if(!f_cat_rast)
++ return -10;
++
++
++ /* TODO */
++ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
++
++ if ((mapset = G_find_raster2(patch_rast,"")) == NULL) {
++ fclose(f_cat_rast);
++ return -2;
++ }
++
++ Rast_get_cellhd(patch_rast, mapset, &patch_region);
++ Rast_set_window(&patch_region);
++
++ if ((fd_patch_rast = Rast_open_old(patch_rast, mapset)) < 0) {
++ fclose(f_cat_rast);
++ return -3;
++ }
++
++ null_chunk = Rast_allocate_null_buf();
++
++ if(get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds) == -1) return -1;
++
++ ncols = cat_rast_bounds.east - cat_rast_bounds.west;
++ nrows = cat_rast_bounds.south - cat_rast_bounds.north;
++
++ patch_data = (unsigned char *) G_malloc(ncols * sizeof(unsigned char));
++
++ init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
++
++ if(fseek(f_cat_rast, init_shift, SEEK_SET) != 0) {
++ G_message("seek failed");
++ return -4;
++ }
++
++ step_shift = cat_rast_region->cols - ncols;
++
++ for(i_row = 0; i_row < nrows; i_row++) {
++ Rast_get_null_value_row (fd_patch_rast, null_chunk, i_row + patch_bounds.north);
++
++ for(i_col = 0; i_col < ncols; i_col++) {
++ patch_col = patch_bounds.west + i_col;
++
++ if(null_chunk[patch_col] != 1)
++ patch_data[i_col] = 1 & 255;
++ else {
++ patch_data[i_col] = 0 & 255;
++ }
++ }
++
++ fwrite(patch_data, sizeof(unsigned char), (ncols)/sizeof(unsigned char), f_cat_rast);
++ if (ferror(f_cat_rast))
++ {
++ G_free(null_chunk);
++ fclose(f_cat_rast);
++ return -5;
++ }
++ if(fseek(f_cat_rast, step_shift, SEEK_CUR) != 0) {
++ G_message("seek failed");
++ return -6;
++ }
++ }
++
++ Rast_close(fd_patch_rast);
++ G_free(null_chunk);
++ fclose(f_cat_rast);
++ return 0;
++}
++
++static inline void update_cat_scatt_plt(CELL ** chunks, char ** null_chunks, int chunk_size, unsigned short * belongs_pix, struct scScatts * scatts)
++{
++ int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunks_pix;
++ int r_bits = 256;
++
++ CELL * band_1_chunks;
++ CELL * band_2_chunks;
++ char * band_1_null_chunks,* band_2_null_chunks;
++
++ int * scatts_bands = scatts->scatts_bands;
++ for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
++ {
++ band_1_chunks = chunks[scatts_bands[i_scatt * 2]];
++ band_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
++
++ band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
++ band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
++
++ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
++ {
++ if(!belongs_pix[i_chunks_pix] ||
++ band_1_null_chunks[i_chunks_pix] == 1 ||
++ band_2_null_chunks[i_chunks_pix] == 1)
++ continue;
++
++ array_idx = band_1_chunks[i_chunks_pix] + band_2_chunks[i_chunks_pix] * r_bits;
++
++ ++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
++ }
++ }
++}
++
++static inline int compute_scatts_from_chunk(struct scCats * scatt_plts, struct scCats * scatt_conds,
++ CELL ** chunks, char ** null_chunks, int chunk_size, FILE ** f_cats_rasts_in,
++ FILE ** f_cats_rasts_out, struct Cell_head *region, int i_chunk)
++{
++
++ int i_chunks_pix, i_cat, i_scatt, n_a_scatts, i_cond;
++ int cat_id, scatt_plts_cat_idx, array_idx;
++ char * band_1_null_chunks,* band_2_null_chunks;
++
++ int r_bits = 256;
++
++ struct scScatts * scatts_conds;
++ struct scScatts * scatt_plts_scatts;
++ struct scdScattData * conds;
++
++ int * scatts_bands;
++ struct scdScattData ** scatts_arr;
++
++ CELL * band_1_chunks;
++ CELL * band_2_chunks;
++ unsigned int * i_scatt_conds;
++
++ unsigned short * belongs_pix = (unsigned short *) G_malloc(chunk_size * sizeof(unsigned short));
++ unsigned char * rast_pixs = (unsigned char *) G_malloc(chunk_size * sizeof(unsigned char));
++
++ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
++ {
++ scatts_conds = scatt_conds->cats_arr[i_cat];
++
++ cat_id = scatt_conds->cats_ids[i_cat];
++
++ scatt_plts_cat_idx = scatt_plts->cats_idxs[cat_id];
++ if(scatt_plts_cat_idx < 0)
++ continue;
++
++ scatt_plts_scatts = scatt_plts->cats_arr[scatt_plts_cat_idx];
++
++ G_zero(belongs_pix, chunk_size * sizeof(unsigned short));
++
++ if(!scatts_conds->n_a_scatts && !f_cats_rasts_in[i_cat]) {
++ for(i_scatt = 0; i_scatt < scatt_plts_scatts->n_a_scatts; i_scatt++)
++ {
++ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
++ belongs_pix[i_chunks_pix] = 1;
++ }
++ }
++ else
++ {
++ scatts_bands = scatts_conds->scatts_bands;
++
++ if(f_cats_rasts_in[i_cat])
++
++ fread(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_in[i_cat]);
++ if (ferror(f_cats_rasts_in[i_cat]))
++ {
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++ G_message("Unable to read from file.");
++ return -1;
++ }
++ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
++ {
++ if(rast_pixs[i_chunks_pix] != 0 & 255)
++ belongs_pix[i_chunks_pix] = 1;
++ }
++
++ // test every defined conditions in scatter plots
++ for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
++ {
++ band_1_chunks = chunks[scatts_bands[i_scatt * 2]];
++ band_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
++
++ band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
++ band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
++
++ i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
++
++ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
++ {
++ if(belongs_pix[i_chunks_pix] ||
++ band_1_null_chunks[i_chunks_pix] == 1 ||
++ band_2_null_chunks[i_chunks_pix] == 1)
++ continue;
++
++ if(i_scatt_conds[band_1_chunks[i_chunks_pix] + band_2_chunks[i_chunks_pix] * r_bits])
++ belongs_pix[i_chunks_pix] = 1;
++ }
++ }
++ }
++
++ if(f_cats_rasts_out[i_cat]) {
++ for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
++ rast_pixs[i_chunks_pix] = belongs_pix[i_chunks_pix] & 255;
++
++ fwrite(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_out[i_cat]);
++ // TODO when code will be paralelized put it out of the loop
++ if (ferror(f_cats_rasts_out[i_cat]))
++ {
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++ G_debug(3, "Unable to write into file.");
++ return -1;
++ }
++ }
++
++ update_cat_scatt_plt(chunks, null_chunks, chunk_size, belongs_pix, scatt_plts_scatts);
++ }
++
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++
++ return 0;
++}
++
++static void get_needed_bands(struct scCats * cats, int * b_needed_bands)
++{
++ // results in b_needed_bands - array of bools - if item has value 1, band (defined by item index) is needed to be opened
++ int i_cat, i_scatt, cat_id;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ for(i_scatt = 0; i_scatt < cats->cats_arr[i_cat]->n_a_scatts; i_scatt++)
++ {
++ G_debug(3, "Active scatt %d in catt %d", i_scatt, i_cat);
++
++ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2]] = 1;
++ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2 + 1]] = 1;
++ }
++ }
++ return;
++}
++
++static void free_compute_scatts_data(int * fd_bands, CELL ** chunks, char ** null_chunks, int * n_a_bands, int * bands_ids,
++ FILE ** f_cats_rasts_in, FILE ** f_cats_rasts_out, int n_a_cats)
++{
++ int i, band_id;
++
++ for(i = 0; i < (* n_a_bands); i++)
++ {
++ band_id = bands_ids[i];
++ if(band_id >= 0) {
++ Rast_close(fd_bands[i]);
++ G_free(chunks[band_id]);
++ G_free(null_chunks[band_id]);
++ }
++ }
++
++ if(f_cats_rasts_in)
++ for(i = 0; i < n_a_cats; i++)
++ if(f_cats_rasts_in[i])
++ fclose(f_cats_rasts_in[i]);
++
++ if(f_cats_rasts_out)
++ for(i = 0; i < n_a_cats; i++)
++ if(f_cats_rasts_out[i])
++ fclose(f_cats_rasts_out[i]);
++
++}
++
++
++static int open_cats_rasts_files(const char ** cats_rasts, int n_a_cats, int * cats_ids, const char * mode, FILE ** f_cats_rasts)
++{
++ int i_cat, id_cat;
++
++ for(i_cat = 0; i_cat < n_a_cats; i_cat++)
++ {
++ id_cat = cats_ids[i_cat];
++
++ if(cats_rasts[id_cat]) {
++ f_cats_rasts[i_cat] = fopen(cats_rasts[id_cat], mode);
++ if(!f_cats_rasts[i_cat]) return -1;
++ }
++ else
++ f_cats_rasts[i_cat] = NULL;
++ }
++
++ return 0;
++}
++
++static int cats_rasts_write_header(int n_a_cats, struct Cell_head * region, FILE ** f_cats_rasts)
++{
++ int i_cat, id_cat, head_nchars;
++ char cat_rast_header[1024];//TODO magic number
++
++ head_nchars = get_cat_rast_header(region, cat_rast_header);
++
++ for(i_cat = 0; i_cat < n_a_cats; i_cat++)
++ {
++ if(!f_cats_rasts[i_cat])
++ continue;
++
++ fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cats_rasts[i_cat]);
++ if (ferror(f_cats_rasts[i_cat])) return -1;
++ }
++
++ return head_nchars;
++
++}
++
++//TODO change name: I_UpdateScattData???
++int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** bands,
++ int n_bands, struct scCats * scatt_plts, const char ** cats_rasts_in, const char ** cats_rasts_out)
++{/*scatt_plts must be zeros! */
++ if (n_bands != scatt_plts->n_bands ||
++ n_bands != scatt_conds->n_bands)
++ return -1;
++
++ const char *mapset;
++
++ FILE * f_cats_rasts_out[scatt_conds->n_a_cats];
++ FILE * f_cats_rasts_in[scatt_conds->n_a_cats];
++
++ CELL * chunks[n_bands];
++ char * null_chunks[n_bands];
++
++ RASTER_MAP_TYPE data_type;
++
++ int nrows, i_band, id_band, n_a_bands, band_id,
++ chunk_size, i, i_chunk, i_row, head_nchars, i_cat;
++ int fd_bands[n_bands];
++ for(i_band = 0; i_band < n_bands; i_band++)
++ fd_bands[i_band] = -1;
++
++ int bands_ids[n_bands];
++ for(i_band = 0; i_band < n_bands; i_band++)
++ bands_ids[i_band] = -1;
++
++ int b_needed_bands[n_bands];
++ memset(b_needed_bands, 0, (size_t)n_bands * sizeof(int));
++
++ get_needed_bands(scatt_conds, &b_needed_bands[0]);
++ get_needed_bands(scatt_plts, &b_needed_bands[0]);
++
++ Rast_set_window(region);
++
++ n_a_bands = 0;/*TODO realy needed?*/
++ for(id_band = 0; id_band < n_bands; id_band++)
++ {
++ if(b_needed_bands[id_band])
++ {
++ G_debug(3, "Opening raster no. %d with name: %s", id_band, bands[id_band]);
++
++ /* TODO solve returns*/
++ if ((mapset = G_find_raster2(bands[id_band],"")) == NULL) {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ if ((fd_bands[n_a_bands] = Rast_open_old(bands[id_band], mapset)) < 0) {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ //TODO check data type, minimum and maximum value
++ data_type = Rast_get_map_type(fd_bands[n_a_bands]);
++ //if(data_type != CELL)
++ // return -1;
++
++ //What happens if it is not CELL tyoe
++ chunks[id_band] = Rast_allocate_c_buf();
++ null_chunks[id_band] = Rast_allocate_null_buf();
++
++ bands_ids[n_a_bands] = id_band;
++
++ ++n_a_bands;
++ }
++ }
++
++ if (open_cats_rasts_files(cats_rasts_out, scatt_conds->n_a_cats, scatt_conds->cats_ids, "wb", f_cats_rasts_out) != 0) {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ head_nchars = cats_rasts_write_header(scatt_conds->n_a_cats, region, f_cats_rasts_out);
++ if (head_nchars < 0) {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ if (open_cats_rasts_files(cats_rasts_in, scatt_conds->n_a_cats, scatt_conds->cats_ids, "rb", f_cats_rasts_in) != 0) {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
++ if(f_cats_rasts_in[i_cat])
++ fseek(f_cats_rasts_in[i_cat] , head_nchars, SEEK_SET);
++
++ i_chunk = 0;
++
++ nrows = Rast_window_rows();
++ chunk_size = Rast_window_cols();
++
++ for (i_row = 0; i_row < nrows; i_row++)
++ {
++ for(i_band = 0; i_band < n_a_bands; i_band++)
++ {
++ band_id = bands_ids[i_band];
++ G_debug(3, "Reading data for band %d", band_id);
++ Rast_get_c_row(fd_bands[i_band], chunks[band_id], i_row);
++ Rast_get_null_value_row (fd_bands[i_band], null_chunks[band_id], i_row);
++ }
++ if(compute_scatts_from_chunk(scatt_plts, scatt_conds, chunks, null_chunks, chunk_size, &f_cats_rasts_in[0], &f_cats_rasts_out[0], region, i_chunk) == -1)
++ {
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ }
++ free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
++ &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
++ return 0;
++}
+\ No newline at end of file
More information about the grass-commit
mailing list