[GRASS-SVN] r57604 - sandbox/turek/scatter_plot

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Sep 6 08:20:42 PDT 2013


Author: turek
Date: 2013-09-06 08:20:41 -0700 (Fri, 06 Sep 2013)
New Revision: 57604

Modified:
   sandbox/turek/scatter_plot/README.txt
   sandbox/turek/scatter_plot/testing_patch.diff
Log:
scatter plot bug fixing

Modified: sandbox/turek/scatter_plot/README.txt
===================================================================
--- sandbox/turek/scatter_plot/README.txt	2013-09-06 13:22:05 UTC (rev 57603)
+++ sandbox/turek/scatter_plot/README.txt	2013-09-06 15:20:41 UTC (rev 57604)
@@ -13,6 +13,18 @@
 
 -> should not crash if input is empty (above: * is missing from lsat pattern name_ pat="lsat7_2000_*")
 
+-----------------
+NEEDS TO BE DONE:
+
+- change layers order #DONE
+- ellipses managment
+- flip of digitized area on scatter plot
+- synch whith digitizer
+- statusbar showing information about computations and coordinates
+- scatter plot settings
+- check for range of data, if big show warning
+- better temp files clean up
+- comment code
 ------------------
 TODO suggestions:
 

Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff	2013-09-06 13:22:05 UTC (rev 57603)
+++ sandbox/turek/scatter_plot/testing_patch.diff	2013-09-06 15:20:41 UTC (rev 57604)
@@ -1,44 +1,3 @@
-Index: include/defs/vedit.h
-===================================================================
---- include/defs/vedit.h	(revision 57601)
-+++ include/defs/vedit.h	(working copy)
-@@ -33,6 +33,8 @@
- int Vedit_merge_lines(struct Map_info *, struct ilist *);
- 
- /* move.c */
-+int Vedit_move_areas(struct Map_info *, struct Map_info **, int,
-+		     		 struct ilist *, double, double, double, int, double);
- int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
- 		     struct ilist *, double, double, double, int, double);
- 
-Index: include/defs/imagery.h
-===================================================================
---- include/defs/imagery.h	(revision 57601)
-+++ include/defs/imagery.h	(working copy)
-@@ -110,6 +110,23 @@
- 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 *);
-+int I_sc_add_cat(struct scCats *);
-+int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
-+
-+void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
-+
-+int I_compute_scatts(struct Cell_head *, struct scCats *, const char **, 
-+	                 const char **, int, struct scCats *, 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 *);
-+
-+int I_id_scatt_to_bands(const int, const int, int *, int *);
-+int I_bands_to_id_scatt(const int, const int, const int, int *);
-+
- /* sig.c */
- int I_init_signatures(struct Signature *, int);
- int I_new_signature(struct Signature *);
 Index: include/imagery.h
 ===================================================================
 --- include/imagery.h	(revision 57601)
@@ -100,6 +59,47 @@
  #define SIGNATURE_TYPE_MIXED 1
  
  #define GROUPFILE "CURGROUP"
+Index: include/defs/vedit.h
+===================================================================
+--- include/defs/vedit.h	(revision 57601)
++++ include/defs/vedit.h	(working copy)
+@@ -33,6 +33,8 @@
+ int Vedit_merge_lines(struct Map_info *, struct ilist *);
+ 
+ /* move.c */
++int Vedit_move_areas(struct Map_info *, struct Map_info **, int,
++		     		 struct ilist *, double, double, double, int, double);
+ int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
+ 		     struct ilist *, double, double, double, int, double);
+ 
+Index: include/defs/imagery.h
+===================================================================
+--- include/defs/imagery.h	(revision 57601)
++++ include/defs/imagery.h	(working copy)
+@@ -110,6 +110,23 @@
+ 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 *);
++int I_sc_add_cat(struct scCats *);
++int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
++
++void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
++
++int I_compute_scatts(struct Cell_head *, struct scCats *, const char **, 
++	                 const char **, int, struct scCats *, 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 *);
++
++int I_id_scatt_to_bands(const int, const int, int *, int *);
++int I_bands_to_id_scatt(const int, const int, const int, int *);
++
+ /* sig.c */
+ int I_init_signatures(struct Signature *, int);
+ int I_new_signature(struct Signature *);
 Index: gui/icons/grass/polygon.png
 ===================================================================
 Cannot display: file marked as a binary type.
@@ -115,1714 +115,6 @@
 ## -0,0 +1 ##
 +application/octet-stream
 \ No newline at end of property
-Index: gui/wxpython/iclass/dialogs.py
-===================================================================
---- gui/wxpython/iclass/dialogs.py	(revision 57601)
-+++ gui/wxpython/iclass/dialogs.py	(working copy)
-@@ -333,13 +333,19 @@
-         toolbar.SetCategories(catNames = catNames, catIdx = cats)
-         if name in catNames:
-             toolbar.choice.SetStringSelection(name)
-+            cat = toolbar.GetSelectedCategoryIdx()
-         elif catNames:
-             toolbar.choice.SetSelection(0)
--            
-+            cat = toolbar.GetSelectedCategoryIdx()
-+        else:
-+            cat = None
-+
-         if toolbar.choice.IsEmpty():
-             toolbar.EnableControls(False)
-         else:
-             toolbar.EnableControls(True)
-+
-+        self.mapWindow.CategoryChanged(cat)
-         # don't forget to update maps, histo, ...
-         
-     def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
-Index: gui/wxpython/iclass/toolbars.py
-===================================================================
---- gui/wxpython/iclass/toolbars.py	(revision 57601)
-+++ gui/wxpython/iclass/toolbars.py	(working copy)
-@@ -46,9 +46,7 @@
-         'importAreas' : MetaIcon(img = 'layer-import',
-                             label = _('Import training areas from vector map')),
-         'addRgb' : MetaIcon(img = 'layer-rgb-add',
--                            label = _('Add RGB map layer')),
--        'scatt_plot'    : MetaIcon(img = 'layer-raster-analyze',
--                                   label = _('Open Scatter Plot Tool (EXPERIMENTAL GSoC 2013)')),
-+                            label = _('Add RGB map layer'))
-         }
-         
- class IClassMapToolbar(BaseToolbar):
-@@ -117,10 +115,7 @@
-                                      ("zoomBack", icons["zoomBack"],
-                                       self.parent.OnZoomBack),
-                                      ("zoomToMap", icons["zoomExtent"],
--                                      self.parent.OnZoomToMap),
--                                     (None, ),
--                                     ("scatt_plot", iClassIcons["scatt_plot"],
--                                      self.parent.OnScatterplot)
-+                                      self.parent.OnZoomToMap)
-                                     ))
- class IClassToolbar(BaseToolbar):
-     """!IClass toolbar
-@@ -156,7 +151,7 @@
-         """!Toolbar data"""
-         icons = iClassIcons
-         return self._getToolbarData((("selectGroup", icons['selectGroup'],
--                                      self.parent.OnAddBands),
-+                                      lambda event : self.parent.AddBands()),
-                                       (None, ),
-                                       ("classManager", icons['classManager'],
-                                       self.parent.OnCategoryManager),
-Index: gui/wxpython/iclass/frame.py
-===================================================================
---- gui/wxpython/iclass/frame.py	(revision 57601)
-+++ gui/wxpython/iclass/frame.py	(working copy)
-@@ -64,6 +64,8 @@
-                                IClassExportAreasDialog, IClassMapDialog
- from iclass.plots       import PlotPanel
- 
-+from grass.pydispatch.signal import Signal
-+
- class IClassMapFrame(DoubleMapFrame):
-     """! wxIClass main frame
-     
-@@ -114,6 +116,10 @@
-             lambda:
-             self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
-         self.SetSize(size)
-+
-+        self.groupSet = Signal("IClassMapFrame.groupSet")
-+        self.categoryChanged = Signal('IClassMapFrame.categoryChanged')
-+
-         #
-         # Add toolbars
-         #
-@@ -177,7 +183,7 @@
-         self.dialogs['category']   = None
-         
-         # PyPlot init
--        self.plotPanel = PlotPanel(self, stats_data = self.stats_data)
-+        self.plotPanel = PlotPanel(self, giface = self._giface, stats_data = self.stats_data)
-                                    
-         self._addPanes()
-         self._mgr.Update()
-@@ -237,7 +243,7 @@
-             return False
-         
-         return vectorName
--        
-+    
-     def RemoveTempVector(self):
-         """!Removes temporary vector map with training areas"""
-         ret = RunCommand(prog = 'g.remove',
-@@ -477,21 +483,47 @@
-         
-         self.Render(self.GetFirstWindow())
-         
--    def OnAddBands(self, event):
-+    def AddBands(self):
-         """!Add imagery group"""
-         dlg = IClassGroupDialog(self, group = self.group)
--        if dlg.ShowModal() == wx.ID_OK:
--            self.SetGroup(dlg.GetGroup())
-+        
-+        while True:
-+            if dlg.ShowModal() == wx.ID_OK:
-+                if self.SetGroup(dlg.GetGroup()):
-+                    break
-+            else: 
-+                break
-+        
-         dlg.Destroy()
-         
-     def SetGroup(self, name):
-         """!Set imagery group"""
-         group = grass.find_file(name = name, element = 'group')
-         if group['name']:
-+            if not self.GroupData(group['name']):
-+                GError(_("No data found in group <%s>.\n" \
-+                         "Please create subgroup with same name as group and add the data there.") \
-+                           % name, parent = self)
-+                return False
-             self.group = group['name']
-+            self.groupSet.emit(group = group['name'])
-         else:
-             GError(_("Group <%s> not found") % name, parent = self)
--    
-+            return False
-+
-+        return True
-+
-+    def GroupData(self, group):
-+        res = RunCommand('i.group',
-+                         flags = 'g',
-+                         group = group, subgroup = group,
-+                         read = True).strip()
-+        bands = None
-+        if res.split('\n')[0]:
-+            bands = res.split('\n')
-+            
-+        return bands
-+
-     def OnImportAreas(self, event):
-         """!Import training areas"""
-         # check if we have any changes
-@@ -768,17 +800,20 @@
-         
-         Updates number of stddev, histograms, layer in preview display. 
-         """
--        stat = self.stats_data.GetStatistics(currentCat)
--        nstd = stat.nstd
--        self.toolbars['iClass'].UpdateStddev(nstd)
--        
--        self.plotPanel.UpdateCategory(currentCat)
--        self.plotPanel.OnPlotTypeSelected(None)
-+        if currentCat:
-+          stat = self.stats_data.GetStatistics(currentCat)
-+          nstd = stat.nstd
-+          self.toolbars['iClass'].UpdateStddev(nstd)
-+          
-+          self.plotPanel.UpdateCategory(currentCat)
-+          self.plotPanel.OnPlotTypeSelected(None)
-                                    
--        name = stat.rasterName
--        name = self.previewMapManager.GetAlias(name)
--        if name:
--            self.previewMapManager.SelectLayer(name)
-+          name = stat.rasterName
-+          name = self.previewMapManager.GetAlias(name)
-+          if name:
-+              self.previewMapManager.SelectLayer(name)
-+
-+        self.categoryChanged.emit(cat = currentCat)
-         
-     def DeleteAreas(self, cats):
-         """!Removes all training areas of given categories
-@@ -1105,27 +1140,6 @@
-         self.GetFirstWindow().SetModePointer()
-         self.GetSecondWindow().SetModePointer()
- 
--    def OnScatterplot(self, event):
--        """!Init interactive scatterplot tools
--        """
--        if self.dialogs['scatt_plot']:
--            self.dialogs['scatt_plot'].Raise()
--            return
--
--        try:
--          from scatt_plot.dialogs import ScattPlotMainDialog
--        except:
--          GError(parent  = self, message = _("The Scatter Plot Tool is not installed."))
--          return
--
--        self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface, iclass_mapwin = self.GetFirstWindow())
--
--        scatt_mgr = self.dialogs['scatt_plot'].GetScattMgr()
--        scatt_mgr.DigitDataChanged(self.toolbars['vdigit'].mapLayer.GetName(), self.GetFirstWindow().GetDigit())
--
--        self.dialogs['scatt_plot'].CenterOnScreen()
--        self.dialogs['scatt_plot'].Show()
--
- class MapManager:
-     """! Class for managing map renderer.
-     
-Index: gui/wxpython/iclass/plots.py
-===================================================================
---- gui/wxpython/iclass/plots.py	(revision 57601)
-+++ gui/wxpython/iclass/plots.py	(working copy)
-@@ -19,6 +19,7 @@
- import wx.lib.plot as plot
- import wx.lib.scrolledpanel as scrolled
- from core.utils import _
-+from core.gcmd import GError
- 
- class PlotPanel(scrolled.ScrolledPanel):
-     """!Panel for drawing multiple plots.
-@@ -28,7 +29,7 @@
-     for each band and for one category. Coincidence plots show min max range
-     of classes for each band.
-     """
--    def __init__(self, parent, stats_data):
-+    def __init__(self, parent, giface, stats_data):
-         scrolled.ScrolledPanel.__init__(self, parent)
-         
-         self.SetupScrolling(scroll_x = False, scroll_y = True)
-@@ -38,26 +39,71 @@
-         self.stats_data = stats_data
-         self.currentCat = None
-         
-+        self._giface = giface
-+
-         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
--        
-+
-         self._createControlPanel()
--        
-+        self._createPlotPanel()
-+        self._createScatterPlotPanel()
-+
-         self.SetSizer(self.mainSizer)
-         self.mainSizer.Fit(self)
-         self.Layout()
--        
-+
-+    def _createPlotPanel(self):
-+
-+        self.canvasPanel = wx.Panel(parent=self)
-+        self.mainSizer.Add(item = self.canvasPanel, proportion = 1, flag = wx.EXPAND, border = 0)
-+        self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
-+        self.canvasPanel.SetSizer(self.canvasSizer)
-+
-     def _createControlPanel(self):
-         self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
-                                      choices = [_("Histograms"),
--                                                _("Coincident plots")])
-+                                                _("Coincident plots"),
-+                                                _("Scatter plots")])
-         self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
-         self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
--        
-+    
-+    def _createScatterPlotPanel(self):
-+        """!Init interactive scatterplot tools
-+        """
-+        try:
-+            from scatt_plot.frame import IClassScatterPlotsPanel
-+            self.scatt_plot_panel = IClassScatterPlotsPanel(parent=self, 
-+                                                            giface=self._giface, 
-+                                                            iclass_mapwin = self.parent.GetFirstWindow())
-+            self.mainSizer.Add(self.scatt_plot_panel, proportion = 1, flag = wx.EXPAND, border = 0)
-+            self.scatt_plot_panel.Hide()
-+        except ImportError as e:#TODO
-+            self.scatt_error = _("Scatter plot functionality is disabled. Reason:\n" \
-+                                 "Unable to import packages needed for scatter plot.\n%s" % e)
-+            GError(self.scatt_error)
-+            self.scatt_plot_panel = None
-+
-     def OnPlotTypeSelected(self, event):
-         """!Plot type selected"""
-+
-+        if self.plotSwitch.GetSelection() in [0, 1]:
-+            self.SetupScrolling(scroll_x = False, scroll_y = True)
-+            if self.scatt_plot_panel:
-+                self.scatt_plot_panel.Hide()
-+            self.canvasPanel.Show()
-+            self.Layout()
-+
-+        elif self.plotSwitch.GetSelection() == 2:
-+            self.SetupScrolling(scroll_x = False, scroll_y = False)
-+            if self.scatt_plot_panel:
-+                self.scatt_plot_panel.Show()
-+            else:
-+                GError(self.scatt_error)
-+            self.canvasPanel.Hide()
-+            self.Layout()
-+
-         if self.currentCat is None:
-             return
--        
-+
-         if self.plotSwitch.GetSelection() == 0:
-             stat = self.stats_data.GetStatistics(self.currentCat)
-             if not stat.IsReady():
-@@ -66,7 +112,10 @@
-             self.DrawHistograms(stat)
-         else:
-             self.DrawCoincidencePlots()
--            
-+
-+        self.Layout()
-+
-+
-     def StddevChanged(self):
-         """!Standard deviation multiplier changed, redraw histograms"""
-         if self.plotSwitch.GetSelection() == 0:
-@@ -89,7 +138,7 @@
-             panel.Destroy()
-             
-         self.canvasList = []
--            
-+
-     def ClearPlots(self):
-         """!Clears plot canvases"""
-         for bandIdx in range(len(self.bandList)):
-@@ -104,15 +153,15 @@
-     def CreatePlotCanvases(self):
-         """!Create plot canvases according to the number of bands"""
-         for band in self.bandList:
--            canvas = plot.PlotCanvas(self)
-+            canvas = plot.PlotCanvas(self.canvasPanel)
-             canvas.SetMinSize((-1, 140))
-             canvas.SetFontSizeTitle(10)
-             canvas.SetFontSizeAxis(8)
-             self.canvasList.append(canvas)
-             
--            self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
--
--        self.SetVirtualSize(self.GetBestVirtualSize()) 
-+            self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
-+        
-+        self.SetVirtualSize(self.GetBestVirtualSize())
-         self.Layout()
-         
-     def UpdatePlots(self, group, currentCat, stats_data):
-@@ -138,7 +187,7 @@
-         
-     def UpdateCategory(self, cat):
-         self.currentCat = cat
--        
-+    
-     def DrawCoincidencePlots(self):
-         """!Draw coincidence plots"""
-         for bandIdx in range(len(self.bandList)):
-Index: gui/wxpython/mapdisp/toolbars.py
-===================================================================
---- gui/wxpython/mapdisp/toolbars.py	(revision 57601)
-+++ gui/wxpython/mapdisp/toolbars.py	(working copy)
-@@ -239,7 +239,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 57601)
-+++ gui/wxpython/mapdisp/frame.py	(working copy)
-@@ -225,6 +225,7 @@
-         #
-         self.dialogs = {}
-         self.dialogs['attributes'] = None
-+        self.dialogs['scatt_plot'] = None
-         self.dialogs['category'] = None
-         self.dialogs['barscale'] = None
-         self.dialogs['legend'] = None
-@@ -1168,6 +1169,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: 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,134 @@
-+"""!
-+ 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
-+            
-+            #to
-+            #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(kwds=kwds,
-+                                  args=args, #TODO expand args to kwds
-+                                  ret=ret,
-+                                  exception=exception,
-+                                  userdata=vars()['userdata'],
-+                                  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,149 @@
-+"""!
-+ 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
-+from scatt_plot.scatt_core import idBandsToidScatt
-+
-+from core.gcmd import GError
-+
-+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.x_band = None
-+        self.y_band = None
-+
-+        self._createWidgets()
-+
-+    def _createWidgets(self):
-+
-+        self.labels = {}
-+        self.params = {}
-+
-+        self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("%s axis:" % "x"))
-+
-+        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 = _("%s axis:" % "y"))
-+
-+        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):
-+        """!
-+        """
-+        b_x = self.band_1_ch.GetSelection()
-+        b_y = self.band_2_ch.GetSelection()
-+
-+        err = True
-+
-+        if b_x < 0 or b_y < 0:
-+            GError(_("Select both x and y bands."))
-+        elif b_y == b_x:
-+            GError(_("Selected bands must be different."))
-+        else:
-+            err = False        
-+
-+        if err:
-+            self.band_y = None
-+            self.band_x = None
-+            return
-+
-+        self.band_y = b_y
-+        self.band_x = b_x
-+
-+        event.Skip()
-+
-+    def GetBands(self):
-+        return (self.band_x, self.band_y)
-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,216 @@
-+"""!
-+ 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
-+from core.gcmd import GException, GError, RunCommand
-+from scatt_plot.scatt_core import idBandsToidScatt
-+
-+
-+class MainToolbar(BaseToolbar):
-+    """!Main toolbar
-+    """
-+    def __init__(self, parent, scatt_mgr):
-+        BaseToolbar.__init__(self, parent)
-+        self.scatt_mgr = scatt_mgr
-+
-+        self.InitToolbar(self._toolbarData())
-+        
-+        # realize the toolbar
-+        self.Realize()
-+        self.scatt_mgr.modeSet.connect(self.ModeSet)
-+
-+    def _toolbarData(self):
-+
-+        icons = {
-+                'settings'   : BaseIcons['settings'].SetLabel( _('Ssettings')),
-+                'help'       : MetaIcon(img = 'help',
-+                                         label = _('Show manual')),
-+                'add_scatt_pl'  : MetaIcon(img = 'layer-raster-analyze',
-+                                            label = _('Add scatter plot')),
-+                'selCatPol'  : MetaIcon(img = 'polygon',
-+                                      label = _('Select area with polygon')),
-+                'pan'        : MetaIcon(img = 'pan',
-+                                         label = _('Pan mode for scatter plots')),
-+                'zoomIn'     : MetaIcon(img = 'zoom-in',
-+                                        label = _('Zoom mode for scatter plots (left mouse button, wheel)')),
-+                'zoomExtent' : MetaIcon(img = 'zoom-extent',
-+                                       label = _('Zoom to scatter plot data extend mode (click on scatter plot for zooming to extend)')),
-+                'cats_mgr' : MetaIcon(img = 'table-manager',
-+                                          label = _('Show/hide class manager'))
-+                }
-+
-+        return self._getToolbarData((
-+                                     ('add_scatt', icons["add_scatt_pl"],
-+                                     lambda event : self.scatt_mgr.AddScattPlot()),
-+                                     (None, ),
-+                                     ("cats_mgr", icons['cats_mgr'],
-+                                     lambda event: self.parent.ShowCategoryPanel(event.Checked()), wx.ITEM_CHECK),
-+                                     (None, ),
-+                                     ("pan", icons["pan"],
-+                                     lambda event: self.SetPloltsMode(event, 'pan'),
-+                                      wx.ITEM_CHECK),
-+                                     ("zoom", icons["zoomIn"],
-+                                     lambda event: self.SetPloltsMode(event, 'zoom'),
-+                                      wx.ITEM_CHECK),
-+                                     ("zoom_extend", icons["zoomExtent"],
-+                                     lambda event: self.SetPloltsMode(event, 'zoom_extend'),
-+                                      wx.ITEM_CHECK),
-+                                    (None, ),
-+                                     ('sel_pol_mode', icons['selCatPol'],
-+                                      self.ActivateSelectionPolygonMode,
-+                                     wx.ITEM_CHECK)
-+                                     #('settings', icon["settings"],
-+                                     # self.parent.OnSettings),  
-+                                     #('help', icons["help"],
-+                                     # self.OnHelp),                    
-+                                    ))
-+
-+    def GetToolId(self, toolName): #TODO can be useful in base
-+        return vars(self)[toolName]            
-+
-+    def SetPloltsMode(self, event, tool_name):
-+        self.scatt_mgr.modeSet.disconnect(self.ModeSet)
-+        if event.Checked()  == True:
-+            for i_tool_data in  self._data:
-+                i_tool_name = i_tool_data[0]
-+                if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
-+                    continue
-+                if i_tool_name == tool_name:
-+                    continue
-+                i_tool_id = vars(self)[i_tool_name]
-+                self.ToggleTool(i_tool_id, False)
-+
-+            self.scatt_mgr.SetPlotsMode(mode = tool_name)
-+        else:
-+            self.scatt_mgr.SetPlotsMode(mode = None)
-+        self.scatt_mgr.modeSet.connect(self.ModeSet)
-+
-+    def ActivateSelectionPolygonMode(self, event):
-+
-+        activated = self.scatt_mgr.ActivateSelectionPolygonMode(event.Checked())
-+        self.parent.ShowPlotEditingToolbar(activated)
-+
-+        i_tool_id = vars(self)['sel_pol_mode']
-+        self.ToggleTool(i_tool_id, activated)
-+
-+    def ModeSet(self, mode):
-+        self.UnsetMode()
-+
-+    def UnsetMode(self):
-+        for i_tool_data in  self._data:
-+                i_tool_name = i_tool_data[0]
-+                if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
-+                    continue
-+                i_tool_id = vars(self)[i_tool_name]
-+                self.ToggleTool(i_tool_id, False)
-+
-+class EditingToolbar(BaseToolbar):
-+    """!Main toolbar
-+    """
-+    def __init__(self, parent, scatt_mgr):
-+        BaseToolbar.__init__(self, parent)
-+        self.scatt_mgr = scatt_mgr
-+
-+        self.InitToolbar(self._toolbarData())
-+        
-+        # realize the toolbar
-+        self.Realize()
-+        self.scatt_mgr.modeSet.connect(self.ModeSet)
-+
-+    def _toolbarData(self):
-+        """!Toolbar data
-+        """
-+        self.icons = {
-+            'sel_add'         : MetaIcon(img = 'layer-add',
-+                                         label = _('Include selected area to class.'),
-+                                         desc = _('Include selected area to class.')),
-+            'sel_remove'      : MetaIcon(img = 'layer-remove',
-+                                         label = _('Exclude selected area from class.'),
-+                                         desc = _('Exclude selected area from class.')),
-+            'addVertex'       : MetaIcon(img = 'vertex-create',
-+                                         label = _('Add new vertex'),
-+                                         desc = _('Add new vertex to polygon boundary scatter plot')),
-+            'editLine'        : MetaIcon(img = 'polygon-create',
-+                                         label = _('Edit line/boundary'),
-+                                         desc = _('Add new vertex between last and first points of the boundary')),
-+            'moveVertex'      : MetaIcon(img = 'vertex-move',
-+                                         label = _('Move vertex'),
-+                                         desc = _('Move boundary vertex')),
-+            'removeVertex'    : MetaIcon(img = 'vertex-delete',
-+                                         label = _('Remove vertex'),
-+                                         desc = _('Remove boundary vertex.')),
-+            'delete'        : MetaIcon(img = 'polygon-delete',
-+                                         label = _('Edit line/boundary'),
-+                                         desc = _('Delete polygon')),
-+            }
-+
-+        return self._getToolbarData((
-+                                    ("sel_add", self.icons["sel_add"],
-+                                     lambda event: self.scatt_mgr.ProcessSelectionPolygons('add')),
-+                                     ("sel_remove", self.icons['sel_remove'],
-+                                     lambda event: self.scatt_mgr.ProcessSelectionPolygons('remove')),
-+                                     (None, ),
-+                                     ("add_vertex", self.icons["editLine"],
-+                                     lambda event: self.SetMode(event, 'add_vertex'),
-+                                     wx.ITEM_CHECK),
-+                                     ("add_boundary_vertex", self.icons['addVertex'],
-+                                     lambda event: self.SetMode(event, 'add_boundary_vertex'),
-+                                     wx.ITEM_CHECK),
-+                                     ("move_vertex", self.icons["moveVertex"],
-+                                     lambda event: self.SetMode(event, 'move_vertex'),
-+                                     wx.ITEM_CHECK),
-+                                     ('delete_vertex', self.icons['removeVertex'],
-+                                     lambda event: self.SetMode(event, 'delete_vertex'),
-+                                     wx.ITEM_CHECK),
-+                                     ('delete', self.icons['delete'],
-+                                     lambda event: self.SetMode(event, 'delete'),
-+                                     wx.ITEM_CHECK)
-+                                    ))
-+
-+    def SetMode(self, event, tool_name):
-+        self.scatt_mgr.modeSet.disconnect(self.ModeSet)
-+        if event.Checked() == True:
-+            for i_tool_data in  self._data:
-+                i_tool_name = i_tool_data[0]
-+                if not i_tool_name:
-+                    continue
-+                if i_tool_name == tool_name:
-+                    continue
-+                i_tool_id = vars(self)[i_tool_name]
-+                self.ToggleTool(i_tool_id, False)
-+            self.scatt_mgr.SetPlotsMode(tool_name)
-+        else:
-+            self.scatt_mgr.SetPlotsMode(None)
-+        self.scatt_mgr.modeSet.connect(self.ModeSet)
-+
-+    def ModeSet(self, mode):
-+
-+        if mode in ['zoom', 'pan', 'zoom_extend', None]:
-+            self.UnsetMode()
-+
-+    def UnsetMode(self):
-+        for i_tool_data in  self._data:
-+                i_tool_name = i_tool_data[0]
-+                if not i_tool_name:
-+                    continue
-+                i_tool_id = vars(self)[i_tool_name]
-+                self.ToggleTool(i_tool_id, False)
-+
-+    def GetToolId(self, toolName):
-+        return vars(self)[toolName]            
-Index: gui/wxpython/scatt_plot/scatt_core.py
-===================================================================
---- gui/wxpython/scatt_plot/scatt_core.py	(revision 0)
-+++ gui/wxpython/scatt_plot/scatt_core.py	(working copy)
-@@ -0,0 +1,779 @@
-+"""!
-+ 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 time
-+
-+import numpy as np
-+#TODO
-+from matplotlib.path import Path 
-+
-+from math import sqrt, ceil, floor
-+from copy import deepcopy
-+from scipy.signal import convolve2d
-+
-+from core.gcmd import GException, GError, RunCommand
-+
-+import grass.script as grass
-+
-+from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
-+                   SC_SCATT_DATA, SC_SCATT_CONDITIONS
-+
-+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, self.scatt_conds_dt;
-+
-+    def GetRegion(self):
-+        return self.an_data.GetRegion()
-+
-+    def GetCatRast(self, cat_id):
-+        return self.scatts_dt.GetCatRast(cat_id)
-+
-+    def AddScattPlot(self, scatt_id):
-+    
-+        self.scatts_dt.AddScattPlot(scatt_id = scatt_id)
-+
-+        cats_ids = self.scatts_dt.GetCategories()
-+        self.ComputeCatsScatts(cats_ids)
-+
-+
-+    def SetEditCatData(self, cat_id, scatt_id, bbox, value):
-+
-+        if cat_id not in self.scatts_dt.GetCategories():
-+            raise GException(_("Select category for editing."))
-+
-+        if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
-+            return None
-+
-+        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
-+        #arr.flush()
-+        #del arr
-+
-+        self.ComputeCatsScatts([cat_id])
-+        #print "time"
-+        #print time.clock() - start_time
-+        return cat_id
-+
-+    def ComputeCatsScatts(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 = self.scatts_dt.GetCatsRasts()
-+        cats_rasts_conds = self.scatts_dt.GetCatsRastsConds()
-+
-+        returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
-+                                           len(self.GetBands()), scatts, cats_rasts_conds, cats_rasts)
-+
-+        #print "ComputeScatts"
-+        #print returncode
-+        if returncode < 0:
-+            GException(_("Computing of scatter plots failed."))
-+        #self.scatts_dt.SetData(scatts)
-+
-+    def CatRastUpdater(self):
-+        return self.cat_rast_updater
-+    
-+    def UpdateCategoryWithPolygons(self, cat_id, scatts_pols, value):
-+        start_time = time.clock()
-+
-+        if cat_id not in self.scatts_dt.GetCategories():
-+            raise GException(_("Select category for editing."))
-+
-+        for scatt_id, coords in scatts_pols.iteritems():
-+
-+            if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
-+                return False
-+
-+            b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
-+
-+            b1_info = self.an_data.GetBandInfo(b1)
-+            b2_info = self.an_data.GetBandInfo(b2)
-+
-+            raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
-+
-+            raster_ind = np.where(raster_pol > 0) 
-+            arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
-+
-+            arr[raster_ind] = value
-+            #arr.flush()
-+        
-+        self.ComputeCatsScatts([cat_id])
-+        return cat_id
-+
-+    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 SetVectMap(self, vectMap):
-+        self.vectMap = vectMap
-+
-+    def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
-+        #TODO possible optimization - bbox only of vertex and its two neighbours
-+
-+        bboxs = old_bboxs + new_bboxs
-+        areas_cats = old_areas_cats + new_areas_cats 
-+
-+        updated_cats = []
-+
-+        for i in range(len(areas_cats)):
-+            self._updateCatRast(bboxs[i], areas_cats[i], updated_cats)
-+        
-+        return updated_cats
-+
-+    def _updateCatRast(self, bbox, 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)
-+
-+            grass_region = self._create_grass_region_env(bbox)
-+
-+            #TODO hack
-+            patch_rast = "temp_scatt_patch"
-+            self._rasterize(grass_region, layer, cat, patch_rast)
-+
-+            region = self.an_data.GetRegion()
-+            ret = UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastCond(cat))
-+            if ret < 0:
-+                GException(_("Patching category raster conditions file failed."))            
-+            RunCommand("g.remove",
-+                      rast = patch_rast)
-+
-+    def _rasterize(self, grass_region, layer, cat, out_rast):
-+
-+        #TODO different thread may be problem when user edits map
-+        environs = os.environ.copy()
-+        environs['GRASS_VECTOR_TEMPORARY'] = '1'
-+
-+        ret, text, msg = RunCommand("v.build",
-+                                      map = self.vectMap,
-+                                      getErrorMsg = True,
-+                                      read = True,
-+                                      env = environs)
-+
-+        if ret != 0:
-+            GException(_("v.build failed:\n%s" % msg))
-+
-+        environs = os.environ.copy()
-+        environs["GRASS_REGION"] = grass_region["GRASS_REGION"]
-+        environs['GRASS_VECTOR_TEMPORARY'] = '1'
-+
-+        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)
-+
-+        if ret != 0:
-+            GException(_("v.to.rast failed:\n%s" % msg))
-+
-+    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 = []
-+        self.bands_info = {}
-+
-+        self.region = None
-+
-+    def GetRegion(self):
-+        return self.region
-+
-+    def Create(self, bands):
-+
-+        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.bands_info = {}
-+        for b in bands:
-+            self.bands_info[b] = self._getRasterInfo(b)
-+            if self.bands_info[b]["datatype"] != "CELL":
-+                raise GException(_("Raster <%s> is not <CELL> type.") % (b))
-+            #TODO size of raster check
-+
-+        self.region = self._parseRegion(region)
-+
-+    def _getRasterInfo(self, rast):
-+        """
-+        """
-+        ret, out, msg = RunCommand("r.info",
-+                                    map = rast,
-+                                    flags = "rg",
-+                                    getErrorMsg = True,
-+                                    read = True)
-+
-+        if  ret != 0:
-+            raise GException("r.info failed:\n%s" % msg)
-+
-+        out = out.split("\n")
-+        raster_info = {} 
-+
-+        for b in out:
-+            if not b.strip():
-+                continue
-+            k, v = b.split("=")
-+            if k == "datatype":
-+                pass
-+            elif k in ['rows', 'cols', 'cells', 'min', 'max']:
-+                v = int(v)
-+            else:
-+                v = float(v)
-+
-+            raster_info[k] = v
-+
-+        raster_info['range'] = raster_info['max'] - raster_info['min'] + 1
-+        return raster_info
-+
-+    def GetBands(self):
-+        return self.bands
-+
-+    def GetBandInfo(self, band_id):
-+        band = self.bands[band_id]
-+        return self.bands_info[band]
-+
-+    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
-+
-+        #TODO
-+        self.max_n_cats = 10
-+    
-+        self.dtype = 'uint8'
-+        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 GetCategories(self):
-+        return self.cats.keys()
-+
-+    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
-+
-+        b_i = self.GetBandsInfo(scatt_id)
-+
-+        shape = (b_i['b2']['max'] - b_i['b2']['min'] + 1, b_i['b1']['max'] - b_i['b1']['min'] + 1)
-+
-+        np_vals = np.memmap(grass.tempfile(), dtype=self.dtype, mode='w+', shape = shape)
-+
-+        self.cats[cat_id][scatt_id] = {
-+                                        'np_vals' : np_vals,
-+                                      }
-+
-+        return 1
-+
-+    def GetBandsInfo(self, scatt_id):
-+        b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
-+
-+        b1_info = self.an_data.GetBandInfo(b1)
-+        b2_info = self.an_data.GetBandInfo(b2)
-+
-+        bands_info = {'b1' : b1_info,
-+                      'b2' : b2_info}
-+
-+        return bands_info
-+
-+    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'],
-+                                              'bands_info' : self.GetBandsInfo(scatt_id)}
-+                        
-+        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']
-+
-+    def GetScatt(self, scatt_id, cats_ids = None):
-+        scatts = {}
-+        for cat_id in self.cats.iterkeys():
-+            if cats_ids and cat_id not in cats_ids:
-+                continue
-+            if not self.cats[cat_id].has_key(scatt_id):
-+                continue
-+
-+            scatts[cat_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals'],
-+                              'bands_info' : self.GetBandsInfo(scatt_id)}
-+        return scatts
-+
-+                   
-+class ScattPlotsData(ScattPlotsCondsData):
-+
-+    def __init__(self, an_data):
-+
-+        self.cats_rasts = {}
-+        self.cats_rasts_conds = {}    
-+        self.scatts_ids = []    
-+
-+        ScattPlotsCondsData.__init__(self, an_data)
-+
-+        self.dtype = 'uint32'
-+
-+        #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_conds[cat_id] = None
-+            self.cats_rasts[cat_id] = None
-+        else:
-+            self.cats_rasts_conds[cat_id] = grass.tempfile()
-+            self.cats_rasts[cat_id] = "temp_cat_rast_%d" % cat_id
-+            region = self.an_data.GetRegion()
-+            CreateCatRast(region, self.cats_rasts_conds[cat_id])
-+
-+        return cat_id
-+
-+    def DeleteCategory(self, cat_id):
-+
-+        ScattPlotsCondsData.DeleteCategory(self, cat_id)
-+
-+        grass.try_remove(self.cats_rasts_conds[cat_id])
-+        del self.cats_rasts_conds[cat_id]
-+
-+        grass.try_remove(self.cats_rasts[cat_id])
-+        del self.cats_rasts[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)
-+                self.cats[cat_id][scatt_id]['ellipse'] = None
-+
-+        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 GetEllipses(self, scatt_id):
-+        if scatt_id not in self.scatts_ids:
-+            return False
-+
-+        scatts = {}
-+        for cat_id in self.cats.iterkeys():
-+            if cat_id == 0:
-+                continue
-+            scatts[cat_id] = self._getEllipse(cat_id, scatt_id)
-+
-+        return scatts
-+
-+    def _getEllipse(self, cat_id, scatt_id):
-+        # Joe Kington
-+        # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
-+
-+        nstd = 2
-+        data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
-+
-+        sel_pts = np.where(data > 0)
-+
-+        x = sel_pts[1]
-+        y = sel_pts[0]
-+
-+        flatten_data = data.reshape([-1])
-+        flatten_sel_pts = np.nonzero(flatten_data)
-+        weights = flatten_data[flatten_sel_pts]
-+        if len(weights) == 0:
-+            return None
-+
-+        x_avg = np.average(x, weights=weights)
-+        y_avg = np.average(y, weights=weights)
-+        pos = np.array([x_avg, y_avg])
-+
-+        x_diff = (x - x_avg)
-+        y_diff = (y - y_avg)
-+        
-+        x_diff = (x - x_avg) 
-+        y_diff = (y - y_avg) 
-+
-+        diffs = x_diff * y_diff.T
-+        cov = np.dot(diffs, weights) / (np.sum(weights) - 1)
-+
-+        diffs = x_diff * x_diff.T
-+        var_x = np.dot(diffs, weights) /  (np.sum(weights) - 1)
-+        
-+        diffs = y_diff * y_diff.T
-+        var_y = np.dot(diffs, weights) /  (np.sum(weights) - 1)
-+
-+        cov = np.array([[var_x, cov],[cov, var_y]])
-+
-+        def eigsorted(cov):
-+            vals, vecs = np.linalg.eigh(cov)
-+            order = vals.argsort()[::-1]
-+            return vals[order], vecs[:,order]
-+
-+        vals, vecs = eigsorted(cov)
-+        theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))
-+
-+        # Width and height are "full" widths, not radius
-+        width, height = 2 * nstd * np.sqrt(vals)
-+
-+        ellipse = {'pos' : pos, 
-+                   'width' : width,
-+                   'height' : height,
-+                   'theta' : theta}
-+
-+        del data
-+        del flatten_data
-+        del flatten_sel_pts
-+        del weights
-+        del sel_pts
-+        return ellipse
-+
-+    def CleanUp(self):
-+
-+        ScattPlotsCondsData.CleanUp(self)        
-+        for tmp in self.cats_rasts_conds:
-+            grass.try_remove(tmp) 
-+        for tmp in self.cats_rasts:
-+            grass.try_remove(tmp) 
-+
-+        self.cats_rasts = {}
-+        self.cats_rasts_conds = {}
-+
-+
-+    def GetCatRast(self, cat_id):
-+        return self.cats_rasts[cat_id]
-+
-+    def GetCatRastCond(self, cat_id):
-+        return self.cats_rasts_conds[cat_id]
-+
-+    def GetCatsRastsConds(self):
-+        max_cat_id = max(self.cats_rasts_conds.keys())
-+
-+        cats_rasts_conds = [''] * (max_cat_id + 1)
-+        for i_cat_id, i_rast in self.cats_rasts_conds.iteritems():
-+            cats_rasts_conds[i_cat_id] = i_rast
-+
-+        return cats_rasts_conds
-+
-+    def GetCatsRasts(self):
-+        max_cat_id = max(self.cats_rasts.keys())
-+
-+        cats_rasts = [''] * (max_cat_id + 1)
-+        for i_cat_id, i_rast in self.cats_rasts.iteritems():
-+            cats_rasts[i_cat_id] = i_rast
-+
-+        return cats_rasts
-+
-+
-+def RasterizePolygon(pol, height, width):
-+
-+    # Joe Kington
-+    # http://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask
-+
-+    #poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)]
-+
-+    nx = width + 1
-+    ny = height + 1
-+
-+    x, y = np.meshgrid(np.arange(nx), np.arange(ny))
-+    x, y = x.flatten(), y.flatten()
-+
-+    points = np.vstack((x,y)).T
-+
-+    p = Path(pol)
-+    grid = p.contains_points(points)
-+    grid = grid.reshape((ny,nx))
-+
-+    raster = np.zeros((height, width), dtype=np.uint8)#TODO bool
-+
-+    #TODO shift by 0.5
-+    B = np.ones((2,2))/4
-+    raster = convolve2d(grid, B, 'valid')
-+
-+
-+    #TODO this part is very inefficient, replace it with better solution
-+    #for (y, x), value in np.ndenumerate(grid):
-+
-+    #    if x >= width - 1: continue 
-+    #    if y >= height - 1: continue
-+
-+    #    if grid[y, x]:
-+    #        raster[y, x] = 1
-+    #        raster[y + 1, x] = 1
-+    #        raster[y, x + 1] = 1
-+    #        raster[y + 1, x + 1] = 1
-+
-+    return raster
-+
-+#TODO move to utils?
-+def idScattToidBands(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 idBandsToidScatt(band_1_id, band_2_id, n_bands):
-+
-+    if band_2_id <  band_1_id:
-+        tmp = band_1
-+        band_1_id = band_2_id
-+        band_2_id = tmp
-+
-+    n_b1 = n_bands - 1
-+
-+    scatt_id = (band_1_id * (2 * n_b1 + 1) - band_1_id * band_1_id) / 2 + band_2_id - band_1_id - 1
-+
-+    return scatt_id
-+
 Index: gui/wxpython/scatt_plot/core_c.py
 ===================================================================
 --- gui/wxpython/scatt_plot/core_c.py	(revision 0)
@@ -2002,7 +294,7 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/frame.py	(revision 0)
 +++ gui/wxpython/scatt_plot/frame.py	(working copy)
-@@ -0,0 +1,567 @@
+@@ -0,0 +1,582 @@
 +"""!
 + at package scatt_plot.dialogs
 +
@@ -2386,8 +678,10 @@
 +            self.rightClickedItemIdx = item
 +
 +        # generate popup-menu
++        cat_idx = self.rightClickedItemIdx
 +
-+        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++        cats = self.cats_mgr.GetCategories()
++        cat_id = cats[cat_idx]
 +        showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
 +        
 +        menu = wx.Menu()
@@ -2399,22 +693,23 @@
 +
 +        item_id = wx.NewId()
 +        menu.Append(item_id, text = text)
-+        self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id, 
++        self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
 +                                                                attrs={'show' : not showed}), 
-+                  id=item_id) 
-+
++                                                                id=item_id) 
 +        menu.AppendSeparator()
-+        """
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move item up"))
-+        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++        
++        if cat_idx != 0:
++            item_id = wx.NewId()
++            menu.Append(item_id, text=_("Move category up"))
++            self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
 +
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move item down"))
-+        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++        if cat_idx != len(cats) - 1:
++            item_id = wx.NewId()
++            menu.Append(item_id, text=_("Move category down"))
++            self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
 +
 +        menu.AppendSeparator()
-+        """
++        
 +        item_id = wx.NewId()
 +        menu.Append(item_id, text=_("Change opacity level"))
 +        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
@@ -2422,6 +717,18 @@
 +        self.PopupMenu(menu)
 +        menu.Destroy()
 +
++    def OnMoveUp(self, event):
++        cat_idx = self.rightClickedItemIdx
++        cat_id = self.cats_mgr.GetCategories()[cat_idx]
++        self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++    def OnMoveDown(self, event):
++        cat_idx = self.rightClickedItemIdx
++        cat_id = self.cats_mgr.GetCategories()[cat_idx]
++        self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
 +    def OnPopupOpacityLevel(self, event):
 +        """!Popup opacity level indicator"""
 +
@@ -2590,7 +897,7 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/plots.py	(revision 0)
 +++ gui/wxpython/scatt_plot/plots.py	(working copy)
-@@ -0,0 +1,824 @@
+@@ -0,0 +1,851 @@
 +"""!
 + at package scatt_plot.dialogs
 +
@@ -2784,7 +1091,7 @@
 +        self.SetSizer(self.main_sizer)
 +        self.main_sizer.Fit(self)
 +    
-+    def Plot(self, scatts, ellipses, styles):
++    def Plot(self, cats_order, scatts, ellipses, styles):
 +        """ Redraws the figure
 +        """
 +
@@ -2799,7 +1106,7 @@
 +            c = None
 +
 +        q = Queue()
-+        p = Process(target=MergeImg, args=(scatts, styles, self.transpose, q))
++        p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
 +        p.start()
 +        merged_img, self.full_extend = q.get()
 +        p.join()
@@ -2815,9 +1122,15 @@
 +        callafter_list.append([self.axes.draw_artist, [img]])
 +        callafter_list.append([grass.try_remove, [merged_img.filename]])
 +
-+        for cat_id, e in ellipses.iteritems():
-+            if cat_id == 0 or not e:
++        for cat_id in cats_order:
++            if cat_id == 0:
 +                continue
++            if not ellipses.has_key(cat_id):
++                continue
++                
++            e = ellipses[cat_id]
++            if not e:
++                continue
 +
 +            colors = styles[cat_id]['color'].split(":")
 +            if self.transpose:
@@ -2901,6 +1214,9 @@
 +        x2 = deepcopy(self.zoom_rect_coords['x'])
 +        y2 = deepcopy(self.zoom_rect_coords['y'])
 +
++        if x1 == x2 or y1 == y2:
++            return
++
 +        self.axes.set_xlim(x1, x2)#, auto = True)
 +        self.axes.set_ylim(y1, y2)#, auto = True)
 +        self.canvas.draw()
@@ -3344,11 +1660,21 @@
 +
 +    return im
 +
-+def MergeImg(scatts, styles, transpose, output_queue):
++def MergeImg(cats_order, scatts, styles, transpose, output_queue):
 +
++        start_time = time.clock()
++
++
++        start_time = time.clock()
++        #cmap_time = 0
++
++
 +        init = True
 +        merge_tmp = grass.tempfile()
-+        for cat_id, scatt in scatts.iteritems():
++        for cat_id in cats_order:
++            if not scatts.has_key(cat_id):
++                continue
++            scatt = scatts[cat_id]
 +            #print "color map %d" % cat_id
 +            #TODO make more general
 +            if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
@@ -3389,7 +1715,10 @@
 +            vmax = np.amax(masked_cat)
 +            masked_cat = masked_cat / float(vmax)
 +
++            #tmp = time.clock()
 +            colored_cat = np.uint8(cmap(masked_cat) * 255)
++            #cmap_time += time.clock() + tmp
++
 +            del masked_cat
 +            del cmap
 +            
@@ -3413,6 +1742,11 @@
 +                del c_img_a
 +            
 +            del colored_cat
++
++        #end_time =  time.clock() - start_time
++        #print "all time:%f" % (end_time)
++        #print "cmap_time:%f" % (end_time / cmap_time * 100.0 )
++
 +        output_queue.put((merged_img, full_extend))
 +        #return merged_img, full_extend 
 \ No newline at end of file
@@ -3420,7 +1754,7 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/controllers.py	(revision 0)
 +++ gui/wxpython/scatt_plot/controllers.py	(working copy)
-@@ -0,0 +1,685 @@
+@@ -0,0 +1,708 @@
 +"""!
 + at package scatt_plot.controllers
 +
@@ -3648,7 +1982,6 @@
 +
 +    def RenderScattPlts(self, scatt_ids = None):
 +        if len(self.tasks_pids['render']) > 1:
-+            print "skip"
 +            return 
 +
 +        self.tasks_pids['render'].append(self.thread.GetId())
@@ -3656,6 +1989,9 @@
 +
 +    def _renderscattplts(self, scatt_ids):
 +        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++        cats = self.cats_mgr.GetCategories()[:]
++        cats.reverse()
++        cats.insert(0, 0)
 +        for i_scatt_id, scatt in self.plots.items():
 +            if scatt_ids is not None and i_scatt_id not in scatt_ids:
 +                continue
@@ -3664,11 +2000,11 @@
 +            ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id)
 +
 +            if self.pol_sel_mode[0]:
-+                self._getSelectedAreas(i_scatt_id, scatt_dt, cats_attrs)
++                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
 +
-+            scatt.Plot(scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++            scatt.Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
 +
-+    def _getSelectedAreas(self, scatt_id, scatt_dt, cats_attrs):
++    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
 +
 +        cat_id = self.cats_mgr.GetSelectedCat()
 +        if not cat_id:
@@ -3679,6 +2015,8 @@
 +        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
 +        if not s:
 +            return
++
++        cats_order.append(sel_a_cat_id)
 +        cats_attrs[sel_a_cat_id] = {'color' : "255:255:0",
 +                                    'opacity' : 0.7,
 +                                    'show' : True}
@@ -3851,6 +2189,25 @@
 +            for cat_id in self.cats_ids:
 +                self.core.AddCategory(cat_id)
 +
++    def ChangePosition(self, cat_id, new_pos):
++        if new_pos >= len(self.cats_ids):
++            return False
++
++        try:
++            pos = self.cats_ids.index(cat_id)
++        except:
++            return False
++
++        if pos > new_pos:
++            pos -= 1
++
++        self.cats_ids.remove(cat_id)
++
++        self.cats_ids.insert(new_pos, cat_id)
++
++        self.scatt_mgr.RenderScattPlts()
++        return True
++
 +    def AddCategory(self, cat_id = None, name = None, color = None):
 +
 +        if cat_id is None:
@@ -3873,7 +2230,7 @@
 +                                'show' : True
 +                            }
 +
-+        self.cats_ids.append(cat_id)
++        self.cats_ids.insert(0, cat_id)
 +
 +        if name is not None:
 +            self.cats[cat_id]["name"] = name
@@ -4107,30 +2464,1305 @@
 +            bands = res.split('\n')
 +            self.scatt_mgr.SetData(bands)
 \ No newline at end of file
-Index: gui/wxpython/Makefile
+Index: gui/wxpython/scatt_plot/gthreading.py
 ===================================================================
---- gui/wxpython/Makefile	(revision 57601)
-+++ 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 mapwin/*.py mapdisp/*.py \
- 	mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* 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,8 +21,9 @@
- 
- PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
- 	gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
--	mapswipe vdigit wxplot web_services rlisetup vnet timeline)
-+	mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
- 
+--- gui/wxpython/scatt_plot/gthreading.py	(revision 0)
++++ gui/wxpython/scatt_plot/gthreading.py	(working copy)
+@@ -0,0 +1,134 @@
++"""!
++ at package scatt_plot.gthreading
 +
- DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
- 
- default: $(DSTFILES)
++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
++            
++            #to
++            #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(kwds=kwds,
++                                  args=args, #TODO expand args to kwds
++                                  ret=ret,
++                                  exception=exception,
++                                  userdata=vars()['userdata'],
++                                  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,149 @@
++"""!
++ 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
++from scatt_plot.scatt_core import idBandsToidScatt
++
++from core.gcmd import GError
++
++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.x_band = None
++        self.y_band = None
++
++        self._createWidgets()
++
++    def _createWidgets(self):
++
++        self.labels = {}
++        self.params = {}
++
++        self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("%s axis:" % "x"))
++
++        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 = _("%s axis:" % "y"))
++
++        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):
++        """!
++        """
++        b_x = self.band_1_ch.GetSelection()
++        b_y = self.band_2_ch.GetSelection()
++
++        err = True
++
++        if b_x < 0 or b_y < 0:
++            GError(_("Select both x and y bands."))
++        elif b_y == b_x:
++            GError(_("Selected bands must be different."))
++        else:
++            err = False        
++
++        if err:
++            self.band_y = None
++            self.band_x = None
++            return
++
++        self.band_y = b_y
++        self.band_x = b_x
++
++        event.Skip()
++
++    def GetBands(self):
++        return (self.band_x, self.band_y)
+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,216 @@
++"""!
++ 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
++from core.gcmd import GException, GError, RunCommand
++from scatt_plot.scatt_core import idBandsToidScatt
++
++
++class MainToolbar(BaseToolbar):
++    """!Main toolbar
++    """
++    def __init__(self, parent, scatt_mgr):
++        BaseToolbar.__init__(self, parent)
++        self.scatt_mgr = scatt_mgr
++
++        self.InitToolbar(self._toolbarData())
++        
++        # realize the toolbar
++        self.Realize()
++        self.scatt_mgr.modeSet.connect(self.ModeSet)
++
++    def _toolbarData(self):
++
++        icons = {
++                'settings'   : BaseIcons['settings'].SetLabel( _('Ssettings')),
++                'help'       : MetaIcon(img = 'help',
++                                         label = _('Show manual')),
++                'add_scatt_pl'  : MetaIcon(img = 'layer-raster-analyze',
++                                            label = _('Add scatter plot')),
++                'selCatPol'  : MetaIcon(img = 'polygon',
++                                      label = _('Select area with polygon')),
++                'pan'        : MetaIcon(img = 'pan',
++                                         label = _('Pan mode for scatter plots')),
++                'zoomIn'     : MetaIcon(img = 'zoom-in',
++                                        label = _('Zoom mode for scatter plots (left mouse button, wheel)')),
++                'zoomExtent' : MetaIcon(img = 'zoom-extent',
++                                       label = _('Zoom to scatter plot data extend mode (click on scatter plot for zooming to extend)')),
++                'cats_mgr' : MetaIcon(img = 'table-manager',
++                                          label = _('Show/hide class manager'))
++                }
++
++        return self._getToolbarData((
++                                     ('add_scatt', icons["add_scatt_pl"],
++                                     lambda event : self.scatt_mgr.AddScattPlot()),
++                                     (None, ),
++                                     ("cats_mgr", icons['cats_mgr'],
++                                     lambda event: self.parent.ShowCategoryPanel(event.Checked()), wx.ITEM_CHECK),
++                                     (None, ),
++                                     ("pan", icons["pan"],
++                                     lambda event: self.SetPloltsMode(event, 'pan'),
++                                      wx.ITEM_CHECK),
++                                     ("zoom", icons["zoomIn"],
++                                     lambda event: self.SetPloltsMode(event, 'zoom'),
++                                      wx.ITEM_CHECK),
++                                     ("zoom_extend", icons["zoomExtent"],
++                                     lambda event: self.SetPloltsMode(event, 'zoom_extend'),
++                                      wx.ITEM_CHECK),
++                                    (None, ),
++                                     ('sel_pol_mode', icons['selCatPol'],
++                                      self.ActivateSelectionPolygonMode,
++                                     wx.ITEM_CHECK)
++                                     #('settings', icon["settings"],
++                                     # self.parent.OnSettings),  
++                                     #('help', icons["help"],
++                                     # self.OnHelp),                    
++                                    ))
++
++    def GetToolId(self, toolName): #TODO can be useful in base
++        return vars(self)[toolName]            
++
++    def SetPloltsMode(self, event, tool_name):
++        self.scatt_mgr.modeSet.disconnect(self.ModeSet)
++        if event.Checked()  == True:
++            for i_tool_data in  self._data:
++                i_tool_name = i_tool_data[0]
++                if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
++                    continue
++                if i_tool_name == tool_name:
++                    continue
++                i_tool_id = vars(self)[i_tool_name]
++                self.ToggleTool(i_tool_id, False)
++
++            self.scatt_mgr.SetPlotsMode(mode = tool_name)
++        else:
++            self.scatt_mgr.SetPlotsMode(mode = None)
++        self.scatt_mgr.modeSet.connect(self.ModeSet)
++
++    def ActivateSelectionPolygonMode(self, event):
++
++        activated = self.scatt_mgr.ActivateSelectionPolygonMode(event.Checked())
++        self.parent.ShowPlotEditingToolbar(activated)
++
++        i_tool_id = vars(self)['sel_pol_mode']
++        self.ToggleTool(i_tool_id, activated)
++
++    def ModeSet(self, mode):
++        self.UnsetMode()
++
++    def UnsetMode(self):
++        for i_tool_data in  self._data:
++                i_tool_name = i_tool_data[0]
++                if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
++                    continue
++                i_tool_id = vars(self)[i_tool_name]
++                self.ToggleTool(i_tool_id, False)
++
++class EditingToolbar(BaseToolbar):
++    """!Main toolbar
++    """
++    def __init__(self, parent, scatt_mgr):
++        BaseToolbar.__init__(self, parent)
++        self.scatt_mgr = scatt_mgr
++
++        self.InitToolbar(self._toolbarData())
++        
++        # realize the toolbar
++        self.Realize()
++        self.scatt_mgr.modeSet.connect(self.ModeSet)
++
++    def _toolbarData(self):
++        """!Toolbar data
++        """
++        self.icons = {
++            'sel_add'         : MetaIcon(img = 'layer-add',
++                                         label = _('Include selected area to class.'),
++                                         desc = _('Include selected area to class.')),
++            'sel_remove'      : MetaIcon(img = 'layer-remove',
++                                         label = _('Exclude selected area from class.'),
++                                         desc = _('Exclude selected area from class.')),
++            'addVertex'       : MetaIcon(img = 'vertex-create',
++                                         label = _('Add new vertex'),
++                                         desc = _('Add new vertex to polygon boundary scatter plot')),
++            'editLine'        : MetaIcon(img = 'polygon-create',
++                                         label = _('Edit line/boundary'),
++                                         desc = _('Add new vertex between last and first points of the boundary')),
++            'moveVertex'      : MetaIcon(img = 'vertex-move',
++                                         label = _('Move vertex'),
++                                         desc = _('Move boundary vertex')),
++            'removeVertex'    : MetaIcon(img = 'vertex-delete',
++                                         label = _('Remove vertex'),
++                                         desc = _('Remove boundary vertex.')),
++            'delete'        : MetaIcon(img = 'polygon-delete',
++                                         label = _('Edit line/boundary'),
++                                         desc = _('Delete polygon')),
++            }
++
++        return self._getToolbarData((
++                                    ("sel_add", self.icons["sel_add"],
++                                     lambda event: self.scatt_mgr.ProcessSelectionPolygons('add')),
++                                     ("sel_remove", self.icons['sel_remove'],
++                                     lambda event: self.scatt_mgr.ProcessSelectionPolygons('remove')),
++                                     (None, ),
++                                     ("add_vertex", self.icons["editLine"],
++                                     lambda event: self.SetMode(event, 'add_vertex'),
++                                     wx.ITEM_CHECK),
++                                     ("add_boundary_vertex", self.icons['addVertex'],
++                                     lambda event: self.SetMode(event, 'add_boundary_vertex'),
++                                     wx.ITEM_CHECK),
++                                     ("move_vertex", self.icons["moveVertex"],
++                                     lambda event: self.SetMode(event, 'move_vertex'),
++                                     wx.ITEM_CHECK),
++                                     ('delete_vertex', self.icons['removeVertex'],
++                                     lambda event: self.SetMode(event, 'delete_vertex'),
++                                     wx.ITEM_CHECK),
++                                     ('delete', self.icons['delete'],
++                                     lambda event: self.SetMode(event, 'delete'),
++                                     wx.ITEM_CHECK)
++                                    ))
++
++    def SetMode(self, event, tool_name):
++        self.scatt_mgr.modeSet.disconnect(self.ModeSet)
++        if event.Checked() == True:
++            for i_tool_data in  self._data:
++                i_tool_name = i_tool_data[0]
++                if not i_tool_name:
++                    continue
++                if i_tool_name == tool_name:
++                    continue
++                i_tool_id = vars(self)[i_tool_name]
++                self.ToggleTool(i_tool_id, False)
++            self.scatt_mgr.SetPlotsMode(tool_name)
++        else:
++            self.scatt_mgr.SetPlotsMode(None)
++        self.scatt_mgr.modeSet.connect(self.ModeSet)
++
++    def ModeSet(self, mode):
++
++        if mode in ['zoom', 'pan', 'zoom_extend', None]:
++            self.UnsetMode()
++
++    def UnsetMode(self):
++        for i_tool_data in  self._data:
++                i_tool_name = i_tool_data[0]
++                if not i_tool_name:
++                    continue
++                i_tool_id = vars(self)[i_tool_name]
++                self.ToggleTool(i_tool_id, False)
++
++    def GetToolId(self, toolName):
++        return vars(self)[toolName]            
+Index: gui/wxpython/scatt_plot/scatt_core.py
+===================================================================
+--- gui/wxpython/scatt_plot/scatt_core.py	(revision 0)
++++ gui/wxpython/scatt_plot/scatt_core.py	(working copy)
+@@ -0,0 +1,779 @@
++"""!
++ 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 time
++
++import numpy as np
++#TODO
++from matplotlib.path import Path 
++
++from math import sqrt, ceil, floor
++from copy import deepcopy
++from scipy.signal import convolve2d
++
++from core.gcmd import GException, GError, RunCommand
++
++import grass.script as grass
++
++from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
++                   SC_SCATT_DATA, SC_SCATT_CONDITIONS
++
++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, self.scatt_conds_dt;
++
++    def GetRegion(self):
++        return self.an_data.GetRegion()
++
++    def GetCatRast(self, cat_id):
++        return self.scatts_dt.GetCatRast(cat_id)
++
++    def AddScattPlot(self, scatt_id):
++    
++        self.scatts_dt.AddScattPlot(scatt_id = scatt_id)
++
++        cats_ids = self.scatts_dt.GetCategories()
++        self.ComputeCatsScatts(cats_ids)
++
++
++    def SetEditCatData(self, cat_id, scatt_id, bbox, value):
++
++        if cat_id not in self.scatts_dt.GetCategories():
++            raise GException(_("Select category for editing."))
++
++        if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
++            return None
++
++        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
++        #arr.flush()
++        #del arr
++
++        self.ComputeCatsScatts([cat_id])
++        #print "time"
++        #print time.clock() - start_time
++        return cat_id
++
++    def ComputeCatsScatts(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 = self.scatts_dt.GetCatsRasts()
++        cats_rasts_conds = self.scatts_dt.GetCatsRastsConds()
++
++        returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
++                                           len(self.GetBands()), scatts, cats_rasts_conds, cats_rasts)
++
++        #print "ComputeScatts"
++        #print returncode
++        if returncode < 0:
++            GException(_("Computing of scatter plots failed."))
++        #self.scatts_dt.SetData(scatts)
++
++    def CatRastUpdater(self):
++        return self.cat_rast_updater
++    
++    def UpdateCategoryWithPolygons(self, cat_id, scatts_pols, value):
++        start_time = time.clock()
++
++        if cat_id not in self.scatts_dt.GetCategories():
++            raise GException(_("Select category for editing."))
++
++        for scatt_id, coords in scatts_pols.iteritems():
++
++            if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
++                return False
++
++            b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
++
++            b1_info = self.an_data.GetBandInfo(b1)
++            b2_info = self.an_data.GetBandInfo(b2)
++
++            raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
++
++            raster_ind = np.where(raster_pol > 0) 
++            arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
++
++            arr[raster_ind] = value
++            #arr.flush()
++        
++        self.ComputeCatsScatts([cat_id])
++        return cat_id
++
++    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 SetVectMap(self, vectMap):
++        self.vectMap = vectMap
++
++    def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
++        #TODO possible optimization - bbox only of vertex and its two neighbours
++
++        bboxs = old_bboxs + new_bboxs
++        areas_cats = old_areas_cats + new_areas_cats 
++
++        updated_cats = []
++
++        for i in range(len(areas_cats)):
++            self._updateCatRast(bboxs[i], areas_cats[i], updated_cats)
++        
++        return updated_cats
++
++    def _updateCatRast(self, bbox, 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)
++
++            grass_region = self._create_grass_region_env(bbox)
++
++            #TODO hack
++            patch_rast = "temp_scatt_patch"
++            self._rasterize(grass_region, layer, cat, patch_rast)
++
++            region = self.an_data.GetRegion()
++            ret = UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastCond(cat))
++            if ret < 0:
++                GException(_("Patching category raster conditions file failed."))            
++            RunCommand("g.remove",
++                      rast = patch_rast)
++
++    def _rasterize(self, grass_region, layer, cat, out_rast):
++
++        #TODO different thread may be problem when user edits map
++        environs = os.environ.copy()
++        environs['GRASS_VECTOR_TEMPORARY'] = '1'
++
++        ret, text, msg = RunCommand("v.build",
++                                      map = self.vectMap,
++                                      getErrorMsg = True,
++                                      read = True,
++                                      env = environs)
++
++        if ret != 0:
++            GException(_("v.build failed:\n%s" % msg))
++
++        environs = os.environ.copy()
++        environs["GRASS_REGION"] = grass_region["GRASS_REGION"]
++        environs['GRASS_VECTOR_TEMPORARY'] = '1'
++
++        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)
++
++        if ret != 0:
++            GException(_("v.to.rast failed:\n%s" % msg))
++
++    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 = []
++        self.bands_info = {}
++
++        self.region = None
++
++    def GetRegion(self):
++        return self.region
++
++    def Create(self, bands):
++
++        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.bands_info = {}
++        for b in bands:
++            self.bands_info[b] = self._getRasterInfo(b)
++            if self.bands_info[b]["datatype"] != "CELL":
++                raise GException(_("Raster <%s> is not <CELL> type.") % (b))
++            #TODO size of raster check
++
++        self.region = self._parseRegion(region)
++
++    def _getRasterInfo(self, rast):
++        """
++        """
++        ret, out, msg = RunCommand("r.info",
++                                    map = rast,
++                                    flags = "rg",
++                                    getErrorMsg = True,
++                                    read = True)
++
++        if  ret != 0:
++            raise GException("r.info failed:\n%s" % msg)
++
++        out = out.split("\n")
++        raster_info = {} 
++
++        for b in out:
++            if not b.strip():
++                continue
++            k, v = b.split("=")
++            if k == "datatype":
++                pass
++            elif k in ['rows', 'cols', 'cells', 'min', 'max']:
++                v = int(v)
++            else:
++                v = float(v)
++
++            raster_info[k] = v
++
++        raster_info['range'] = raster_info['max'] - raster_info['min'] + 1
++        return raster_info
++
++    def GetBands(self):
++        return self.bands
++
++    def GetBandInfo(self, band_id):
++        band = self.bands[band_id]
++        return self.bands_info[band]
++
++    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
++
++        #TODO
++        self.max_n_cats = 10
++    
++        self.dtype = 'uint8'
++        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 GetCategories(self):
++        return self.cats.keys()
++
++    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
++
++        b_i = self.GetBandsInfo(scatt_id)
++
++        shape = (b_i['b2']['max'] - b_i['b2']['min'] + 1, b_i['b1']['max'] - b_i['b1']['min'] + 1)
++
++        np_vals = np.memmap(grass.tempfile(), dtype=self.dtype, mode='w+', shape = shape)
++
++        self.cats[cat_id][scatt_id] = {
++                                        'np_vals' : np_vals,
++                                      }
++
++        return 1
++
++    def GetBandsInfo(self, scatt_id):
++        b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
++
++        b1_info = self.an_data.GetBandInfo(b1)
++        b2_info = self.an_data.GetBandInfo(b2)
++
++        bands_info = {'b1' : b1_info,
++                      'b2' : b2_info}
++
++        return bands_info
++
++    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'],
++                                              'bands_info' : self.GetBandsInfo(scatt_id)}
++                        
++        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']
++
++    def GetScatt(self, scatt_id, cats_ids = None):
++        scatts = {}
++        for cat_id in self.cats.iterkeys():
++            if cats_ids and cat_id not in cats_ids:
++                continue
++            if not self.cats[cat_id].has_key(scatt_id):
++                continue
++
++            scatts[cat_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals'],
++                              'bands_info' : self.GetBandsInfo(scatt_id)}
++        return scatts
++
++                   
++class ScattPlotsData(ScattPlotsCondsData):
++
++    def __init__(self, an_data):
++
++        self.cats_rasts = {}
++        self.cats_rasts_conds = {}    
++        self.scatts_ids = []    
++
++        ScattPlotsCondsData.__init__(self, an_data)
++
++        self.dtype = 'uint32'
++
++        #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_conds[cat_id] = None
++            self.cats_rasts[cat_id] = None
++        else:
++            self.cats_rasts_conds[cat_id] = grass.tempfile()
++            self.cats_rasts[cat_id] = "temp_cat_rast_%d" % cat_id
++            region = self.an_data.GetRegion()
++            CreateCatRast(region, self.cats_rasts_conds[cat_id])
++
++        return cat_id
++
++    def DeleteCategory(self, cat_id):
++
++        ScattPlotsCondsData.DeleteCategory(self, cat_id)
++
++        grass.try_remove(self.cats_rasts_conds[cat_id])
++        del self.cats_rasts_conds[cat_id]
++
++        grass.try_remove(self.cats_rasts[cat_id])
++        del self.cats_rasts[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)
++                self.cats[cat_id][scatt_id]['ellipse'] = None
++
++        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 GetEllipses(self, scatt_id):
++        if scatt_id not in self.scatts_ids:
++            return False
++
++        scatts = {}
++        for cat_id in self.cats.iterkeys():
++            if cat_id == 0:
++                continue
++            scatts[cat_id] = self._getEllipse(cat_id, scatt_id)
++
++        return scatts
++
++    def _getEllipse(self, cat_id, scatt_id):
++        # Joe Kington
++        # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
++
++        nstd = 2
++        data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
++
++        sel_pts = np.where(data > 0)
++
++        x = sel_pts[1]
++        y = sel_pts[0]
++
++        flatten_data = data.reshape([-1])
++        flatten_sel_pts = np.nonzero(flatten_data)
++        weights = flatten_data[flatten_sel_pts]
++        if len(weights) == 0:
++            return None
++
++        x_avg = np.average(x, weights=weights)
++        y_avg = np.average(y, weights=weights)
++        pos = np.array([x_avg, y_avg])
++
++        x_diff = (x - x_avg)
++        y_diff = (y - y_avg)
++        
++        x_diff = (x - x_avg) 
++        y_diff = (y - y_avg) 
++
++        diffs = x_diff * y_diff.T
++        cov = np.dot(diffs, weights) / (np.sum(weights) - 1)
++
++        diffs = x_diff * x_diff.T
++        var_x = np.dot(diffs, weights) /  (np.sum(weights) - 1)
++        
++        diffs = y_diff * y_diff.T
++        var_y = np.dot(diffs, weights) /  (np.sum(weights) - 1)
++
++        cov = np.array([[var_x, cov],[cov, var_y]])
++
++        def eigsorted(cov):
++            vals, vecs = np.linalg.eigh(cov)
++            order = vals.argsort()[::-1]
++            return vals[order], vecs[:,order]
++
++        vals, vecs = eigsorted(cov)
++        theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))
++
++        # Width and height are "full" widths, not radius
++        width, height = 2 * nstd * np.sqrt(vals)
++
++        ellipse = {'pos' : pos, 
++                   'width' : width,
++                   'height' : height,
++                   'theta' : theta}
++
++        del data
++        del flatten_data
++        del flatten_sel_pts
++        del weights
++        del sel_pts
++        return ellipse
++
++    def CleanUp(self):
++
++        ScattPlotsCondsData.CleanUp(self)        
++        for tmp in self.cats_rasts_conds:
++            grass.try_remove(tmp) 
++        for tmp in self.cats_rasts:
++            grass.try_remove(tmp) 
++
++        self.cats_rasts = {}
++        self.cats_rasts_conds = {}
++
++
++    def GetCatRast(self, cat_id):
++        return self.cats_rasts[cat_id]
++
++    def GetCatRastCond(self, cat_id):
++        return self.cats_rasts_conds[cat_id]
++
++    def GetCatsRastsConds(self):
++        max_cat_id = max(self.cats_rasts_conds.keys())
++
++        cats_rasts_conds = [''] * (max_cat_id + 1)
++        for i_cat_id, i_rast in self.cats_rasts_conds.iteritems():
++            cats_rasts_conds[i_cat_id] = i_rast
++
++        return cats_rasts_conds
++
++    def GetCatsRasts(self):
++        max_cat_id = max(self.cats_rasts.keys())
++
++        cats_rasts = [''] * (max_cat_id + 1)
++        for i_cat_id, i_rast in self.cats_rasts.iteritems():
++            cats_rasts[i_cat_id] = i_rast
++
++        return cats_rasts
++
++
++def RasterizePolygon(pol, height, width):
++
++    # Joe Kington
++    # http://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask
++
++    #poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)]
++
++    nx = width + 1
++    ny = height + 1
++
++    x, y = np.meshgrid(np.arange(nx), np.arange(ny))
++    x, y = x.flatten(), y.flatten()
++
++    points = np.vstack((x,y)).T
++
++    p = Path(pol)
++    grid = p.contains_points(points)
++    grid = grid.reshape((ny,nx))
++
++    raster = np.zeros((height, width), dtype=np.uint8)#TODO bool
++
++    #TODO shift by 0.5
++    B = np.ones((2,2))/4
++    raster = convolve2d(grid, B, 'valid')
++
++
++    #TODO this part is very inefficient, replace it with better solution
++    #for (y, x), value in np.ndenumerate(grid):
++
++    #    if x >= width - 1: continue 
++    #    if y >= height - 1: continue
++
++    #    if grid[y, x]:
++    #        raster[y, x] = 1
++    #        raster[y + 1, x] = 1
++    #        raster[y, x + 1] = 1
++    #        raster[y + 1, x + 1] = 1
++
++    return raster
++
++#TODO move to utils?
++def idScattToidBands(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 idBandsToidScatt(band_1_id, band_2_id, n_bands):
++
++    if band_2_id <  band_1_id:
++        tmp = band_1
++        band_1_id = band_2_id
++        band_2_id = tmp
++
++    n_b1 = n_bands - 1
++
++    scatt_id = (band_1_id * (2 * n_b1 + 1) - band_1_id * band_1_id) / 2 + band_2_id - band_1_id - 1
++
++    return scatt_id
++
 Index: gui/wxpython/vdigit/wxdigit.py
 ===================================================================
 --- gui/wxpython/vdigit/wxdigit.py	(revision 57601)
@@ -4680,21 +4312,439 @@
          return True
  
      def StopEditing(self):
-Index: lib/vector/Vlib/open.c
+Index: gui/wxpython/Makefile
 ===================================================================
---- lib/vector/Vlib/open.c	(revision 57601)
-+++ lib/vector/Vlib/open.c	(working copy)
-@@ -240,7 +240,9 @@
+--- gui/wxpython/Makefile	(revision 57601)
++++ 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 mapwin/*.py mapdisp/*.py \
+ 	mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* 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,8 +21,9 @@
+ 
+ PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
+ 	gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
+-	mapswipe vdigit wxplot web_services rlisetup vnet timeline)
++	mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
+ 
++
+ DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
+ 
+ default: $(DSTFILES)
+Index: gui/wxpython/mapdisp/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py	(revision 57601)
++++ gui/wxpython/mapdisp/frame.py	(working copy)
+@@ -225,6 +225,7 @@
+         #
+         self.dialogs = {}
+         self.dialogs['attributes'] = None
++        self.dialogs['scatt_plot'] = None
+         self.dialogs['category'] = None
+         self.dialogs['barscale'] = None
+         self.dialogs['legend'] = None
+@@ -1168,6 +1169,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: gui/wxpython/mapdisp/toolbars.py
+===================================================================
+--- gui/wxpython/mapdisp/toolbars.py	(revision 57601)
++++ gui/wxpython/mapdisp/toolbars.py	(working copy)
+@@ -239,7 +239,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/iclass/frame.py
+===================================================================
+--- gui/wxpython/iclass/frame.py	(revision 57601)
++++ gui/wxpython/iclass/frame.py	(working copy)
+@@ -64,6 +64,8 @@
+                                IClassExportAreasDialog, IClassMapDialog
+ from iclass.plots       import PlotPanel
+ 
++from grass.pydispatch.signal import Signal
++
+ class IClassMapFrame(DoubleMapFrame):
+     """! wxIClass main frame
+     
+@@ -114,6 +116,10 @@
+             lambda:
+             self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
+         self.SetSize(size)
++
++        self.groupSet = Signal("IClassMapFrame.groupSet")
++        self.categoryChanged = Signal('IClassMapFrame.categoryChanged')
++
+         #
+         # Add toolbars
+         #
+@@ -177,7 +183,7 @@
+         self.dialogs['category']   = None
+         
+         # PyPlot init
+-        self.plotPanel = PlotPanel(self, stats_data = self.stats_data)
++        self.plotPanel = PlotPanel(self, giface = self._giface, stats_data = self.stats_data)
+                                    
+         self._addPanes()
+         self._mgr.Update()
+@@ -237,7 +243,7 @@
+             return False
+         
+         return vectorName
+-        
++    
+     def RemoveTempVector(self):
+         """!Removes temporary vector map with training areas"""
+         ret = RunCommand(prog = 'g.remove',
+@@ -477,21 +483,47 @@
+         
+         self.Render(self.GetFirstWindow())
+         
+-    def OnAddBands(self, event):
++    def AddBands(self):
+         """!Add imagery group"""
+         dlg = IClassGroupDialog(self, group = self.group)
+-        if dlg.ShowModal() == wx.ID_OK:
+-            self.SetGroup(dlg.GetGroup())
++        
++        while True:
++            if dlg.ShowModal() == wx.ID_OK:
++                if self.SetGroup(dlg.GetGroup()):
++                    break
++            else: 
++                break
++        
+         dlg.Destroy()
+         
+     def SetGroup(self, name):
+         """!Set imagery group"""
+         group = grass.find_file(name = name, element = 'group')
+         if group['name']:
++            if not self.GroupData(group['name']):
++                GError(_("No data found in group <%s>.\n" \
++                         "Please create subgroup with same name as group and add the data there.") \
++                           % name, parent = self)
++                return False
+             self.group = group['name']
++            self.groupSet.emit(group = group['name'])
+         else:
+             GError(_("Group <%s> not found") % name, parent = self)
+-    
++            return False
++
++        return True
++
++    def GroupData(self, group):
++        res = RunCommand('i.group',
++                         flags = 'g',
++                         group = group, subgroup = group,
++                         read = True).strip()
++        bands = None
++        if res.split('\n')[0]:
++            bands = res.split('\n')
++            
++        return bands
++
+     def OnImportAreas(self, event):
+         """!Import training areas"""
+         # check if we have any changes
+@@ -768,17 +800,20 @@
+         
+         Updates number of stddev, histograms, layer in preview display. 
+         """
+-        stat = self.stats_data.GetStatistics(currentCat)
+-        nstd = stat.nstd
+-        self.toolbars['iClass'].UpdateStddev(nstd)
+-        
+-        self.plotPanel.UpdateCategory(currentCat)
+-        self.plotPanel.OnPlotTypeSelected(None)
++        if currentCat:
++          stat = self.stats_data.GetStatistics(currentCat)
++          nstd = stat.nstd
++          self.toolbars['iClass'].UpdateStddev(nstd)
++          
++          self.plotPanel.UpdateCategory(currentCat)
++          self.plotPanel.OnPlotTypeSelected(None)
+                                    
+-        name = stat.rasterName
+-        name = self.previewMapManager.GetAlias(name)
+-        if name:
+-            self.previewMapManager.SelectLayer(name)
++          name = stat.rasterName
++          name = self.previewMapManager.GetAlias(name)
++          if name:
++              self.previewMapManager.SelectLayer(name)
++
++        self.categoryChanged.emit(cat = currentCat)
+         
+     def DeleteAreas(self, cats):
+         """!Removes all training areas of given categories
+@@ -1105,27 +1140,6 @@
+         self.GetFirstWindow().SetModePointer()
+         self.GetSecondWindow().SetModePointer()
+ 
+-    def OnScatterplot(self, event):
+-        """!Init interactive scatterplot tools
+-        """
+-        if self.dialogs['scatt_plot']:
+-            self.dialogs['scatt_plot'].Raise()
+-            return
+-
+-        try:
+-          from scatt_plot.dialogs import ScattPlotMainDialog
+-        except:
+-          GError(parent  = self, message = _("The Scatter Plot Tool is not installed."))
+-          return
+-
+-        self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface, iclass_mapwin = self.GetFirstWindow())
+-
+-        scatt_mgr = self.dialogs['scatt_plot'].GetScattMgr()
+-        scatt_mgr.DigitDataChanged(self.toolbars['vdigit'].mapLayer.GetName(), self.GetFirstWindow().GetDigit())
+-
+-        self.dialogs['scatt_plot'].CenterOnScreen()
+-        self.dialogs['scatt_plot'].Show()
+-
+ class MapManager:
+     """! Class for managing map renderer.
+     
+Index: gui/wxpython/iclass/plots.py
+===================================================================
+--- gui/wxpython/iclass/plots.py	(revision 57601)
++++ gui/wxpython/iclass/plots.py	(working copy)
+@@ -19,6 +19,7 @@
+ import wx.lib.plot as plot
+ import wx.lib.scrolledpanel as scrolled
+ from core.utils import _
++from core.gcmd import GError
+ 
+ class PlotPanel(scrolled.ScrolledPanel):
+     """!Panel for drawing multiple plots.
+@@ -28,7 +29,7 @@
+     for each band and for one category. Coincidence plots show min max range
+     of classes for each band.
+     """
+-    def __init__(self, parent, stats_data):
++    def __init__(self, parent, giface, stats_data):
+         scrolled.ScrolledPanel.__init__(self, parent)
+         
+         self.SetupScrolling(scroll_x = False, scroll_y = True)
+@@ -38,26 +39,71 @@
+         self.stats_data = stats_data
+         self.currentCat = None
+         
++        self._giface = giface
++
+         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
+-        
++
+         self._createControlPanel()
+-        
++        self._createPlotPanel()
++        self._createScatterPlotPanel()
++
+         self.SetSizer(self.mainSizer)
+         self.mainSizer.Fit(self)
+         self.Layout()
+-        
++
++    def _createPlotPanel(self):
++
++        self.canvasPanel = wx.Panel(parent=self)
++        self.mainSizer.Add(item = self.canvasPanel, proportion = 1, flag = wx.EXPAND, border = 0)
++        self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
++        self.canvasPanel.SetSizer(self.canvasSizer)
++
+     def _createControlPanel(self):
+         self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
+                                      choices = [_("Histograms"),
+-                                                _("Coincident plots")])
++                                                _("Coincident plots"),
++                                                _("Scatter plots")])
+         self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+         self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
+-        
++    
++    def _createScatterPlotPanel(self):
++        """!Init interactive scatterplot tools
++        """
++        try:
++            from scatt_plot.frame import IClassScatterPlotsPanel
++            self.scatt_plot_panel = IClassScatterPlotsPanel(parent=self, 
++                                                            giface=self._giface, 
++                                                            iclass_mapwin = self.parent.GetFirstWindow())
++            self.mainSizer.Add(self.scatt_plot_panel, proportion = 1, flag = wx.EXPAND, border = 0)
++            self.scatt_plot_panel.Hide()
++        except ImportError as e:#TODO
++            self.scatt_error = _("Scatter plot functionality is disabled. Reason:\n" \
++                                 "Unable to import packages needed for scatter plot.\n%s" % e)
++            GError(self.scatt_error)
++            self.scatt_plot_panel = None
++
+     def OnPlotTypeSelected(self, event):
+         """!Plot type selected"""
++
++        if self.plotSwitch.GetSelection() in [0, 1]:
++            self.SetupScrolling(scroll_x = False, scroll_y = True)
++            if self.scatt_plot_panel:
++                self.scatt_plot_panel.Hide()
++            self.canvasPanel.Show()
++            self.Layout()
++
++        elif self.plotSwitch.GetSelection() == 2:
++            self.SetupScrolling(scroll_x = False, scroll_y = False)
++            if self.scatt_plot_panel:
++                self.scatt_plot_panel.Show()
++            else:
++                GError(self.scatt_error)
++            self.canvasPanel.Hide()
++            self.Layout()
++
+         if self.currentCat is None:
+             return
+-        
++
+         if self.plotSwitch.GetSelection() == 0:
+             stat = self.stats_data.GetStatistics(self.currentCat)
+             if not stat.IsReady():
+@@ -66,7 +112,10 @@
+             self.DrawHistograms(stat)
+         else:
+             self.DrawCoincidencePlots()
+-            
++
++        self.Layout()
++
++
+     def StddevChanged(self):
+         """!Standard deviation multiplier changed, redraw histograms"""
+         if self.plotSwitch.GetSelection() == 0:
+@@ -89,7 +138,7 @@
+             panel.Destroy()
+             
+         self.canvasList = []
+-            
++
+     def ClearPlots(self):
+         """!Clears plot canvases"""
+         for bandIdx in range(len(self.bandList)):
+@@ -104,15 +153,15 @@
+     def CreatePlotCanvases(self):
+         """!Create plot canvases according to the number of bands"""
+         for band in self.bandList:
+-            canvas = plot.PlotCanvas(self)
++            canvas = plot.PlotCanvas(self.canvasPanel)
+             canvas.SetMinSize((-1, 140))
+             canvas.SetFontSizeTitle(10)
+             canvas.SetFontSizeAxis(8)
+             self.canvasList.append(canvas)
+             
+-            self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+-
+-        self.SetVirtualSize(self.GetBestVirtualSize()) 
++            self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
++        
++        self.SetVirtualSize(self.GetBestVirtualSize())
+         self.Layout()
+         
+     def UpdatePlots(self, group, currentCat, stats_data):
+@@ -138,7 +187,7 @@
+         
+     def UpdateCategory(self, cat):
+         self.currentCat = cat
+-        
++    
+     def DrawCoincidencePlots(self):
+         """!Draw coincidence plots"""
+         for bandIdx in range(len(self.bandList)):
+Index: gui/wxpython/iclass/dialogs.py
+===================================================================
+--- gui/wxpython/iclass/dialogs.py	(revision 57601)
++++ gui/wxpython/iclass/dialogs.py	(working copy)
+@@ -333,13 +333,19 @@
+         toolbar.SetCategories(catNames = catNames, catIdx = cats)
+         if name in catNames:
+             toolbar.choice.SetStringSelection(name)
++            cat = toolbar.GetSelectedCategoryIdx()
+         elif catNames:
+             toolbar.choice.SetSelection(0)
+-            
++            cat = toolbar.GetSelectedCategoryIdx()
++        else:
++            cat = None
++
+         if toolbar.choice.IsEmpty():
+             toolbar.EnableControls(False)
+         else:
+             toolbar.EnableControls(True)
++
++        self.mapWindow.CategoryChanged(cat)
+         # don't forget to update maps, histo, ...
+         
+     def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
+Index: gui/wxpython/iclass/toolbars.py
+===================================================================
+--- gui/wxpython/iclass/toolbars.py	(revision 57601)
++++ gui/wxpython/iclass/toolbars.py	(working copy)
+@@ -46,9 +46,7 @@
+         'importAreas' : MetaIcon(img = 'layer-import',
+                             label = _('Import training areas from vector map')),
+         'addRgb' : MetaIcon(img = 'layer-rgb-add',
+-                            label = _('Add RGB map layer')),
+-        'scatt_plot'    : MetaIcon(img = 'layer-raster-analyze',
+-                                   label = _('Open Scatter Plot Tool (EXPERIMENTAL GSoC 2013)')),
++                            label = _('Add RGB map layer'))
          }
-         else {
-             char file_path[GPATH_MAX];
--            
-+            /* reduce to current mapset if search path was set */
-+            if(strcmp(Map->mapset, "") == 0)
-+                Map->mapset = G_store(G_mapset());
-             /* temporary map: reduce to current mapset if search path
-              * was set */
-             if (strcmp(Map->mapset, "") == 0)
+         
+ class IClassMapToolbar(BaseToolbar):
+@@ -117,10 +115,7 @@
+                                      ("zoomBack", icons["zoomBack"],
+                                       self.parent.OnZoomBack),
+                                      ("zoomToMap", icons["zoomExtent"],
+-                                      self.parent.OnZoomToMap),
+-                                     (None, ),
+-                                     ("scatt_plot", iClassIcons["scatt_plot"],
+-                                      self.parent.OnScatterplot)
++                                      self.parent.OnZoomToMap)
+                                     ))
+ class IClassToolbar(BaseToolbar):
+     """!IClass toolbar
+@@ -156,7 +151,7 @@
+         """!Toolbar data"""
+         icons = iClassIcons
+         return self._getToolbarData((("selectGroup", icons['selectGroup'],
+-                                      self.parent.OnAddBands),
++                                      lambda event : self.parent.AddBands()),
+                                       (None, ),
+                                       ("classManager", icons['classManager'],
+                                       self.parent.OnCategoryManager),
 Index: lib/imagery/scatt_sccats.c
 ===================================================================
 --- lib/imagery/scatt_sccats.c	(revision 0)
@@ -5857,3 +5907,18 @@
 +    return 0;    
 +}
 \ No newline at end of file
+Index: lib/vector/Vlib/open.c
+===================================================================
+--- lib/vector/Vlib/open.c	(revision 57601)
++++ lib/vector/Vlib/open.c	(working copy)
+@@ -240,7 +240,9 @@
+         }
+         else {
+             char file_path[GPATH_MAX];
+-            
++            /* reduce to current mapset if search path was set */
++            if(strcmp(Map->mapset, "") == 0)
++                Map->mapset = G_store(G_mapset());
+             /* temporary map: reduce to current mapset if search path
+              * was set */
+             if (strcmp(Map->mapset, "") == 0)



More information about the grass-commit mailing list