[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