[GRASS-SVN] r57823 - in sandbox/turek/scatter_plot: . gui/wxpython gui/wxpython/docs gui/wxpython/iclass gui/wxpython/mapdisp gui/wxpython/scatt_plot include include/defs lib/imagery

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Sep 23 09:22:37 PDT 2013


Author: turek
Date: 2013-09-23 09:22:36 -0700 (Mon, 23 Sep 2013)
New Revision: 57823

Added:
   sandbox/turek/scatter_plot/gui/wxpython/docs/
   sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html
   sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
   sandbox/turek/scatter_plot/gui/wxpython/iclass/
   sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py
   sandbox/turek/scatter_plot/gui/wxpython/mapdisp/
   sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py
   sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py
Modified:
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
   sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
   sandbox/turek/scatter_plot/include/defs/imagery.h
   sandbox/turek/scatter_plot/include/imagery.h
   sandbox/turek/scatter_plot/lib/imagery/scatt.c
   sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
   sandbox/turek/scatter_plot/testing_patch.diff
Log:
scattter plot: gsoc final source

Added: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,85 @@
+<!-- meta page description: wxGUI Interactive Scatter Plot Tool-->
+<!-- meta page index: wxGUI -->
+
+<h2>DESCRIPTION</h2>
+
+<em>Interactive Scatter Plot Tool</em> allows to analyze group of raster maps. 
+    The tool is integrated into <em><a href="wxGUI.iclass.html">
+    Supervised Classification Tool (wx.iclass) </a></em> (see the screen shot bellow). 
+    Also it is possible to launch it from Map Display Window 
+    (Analyze map → Interactive Scatter Plot Tool). 
+
+The main idea of the tool is that everything is linked together 
+(scatter plots together and mapwindow with the scatter plots).
+
+The main feature of the tool is the interactivity, which allows user to: 
+<ul>
+  <li> work with multiple plots, which represents multiple raster bands combinations.
+  </li>
+  <li> highlight plotted points in open scatter plots according to currently 
+       chosen pixels for classes by it's training areas (supported only in wx.iclass)
+  </li>
+  <li> be able to define areas in plots and the tool will highlight pixels in 
+       map display window and corresponding points in other plots
+  </li>
+  <li> plot of confidence ellipses
+  </li>
+</ul>
+
+<h2>TOOL CONTROLS LAYOUT</h2>
+
+<center>
+<br>
+<img src="wxGUI_iscatt.png" border="1" alt="Interactive Scatter Plot Tool">
+<br><br>
+</center>
+
+If editing mode is activated (the green polygon tool in toolbar), the areas which were selected 
+in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6 B_7 inside the ellipse. 
+Opacity and color of the selected area can be set in settings. The area corresponds to the active class (in this case clouds). 
+Selected areas are subset of areas, which belongs to the category.
+<p>
+In the editing mode it is possible to select certain area by the polygon, which can be created and edited by tools in editing toolbar. 
+After the area is selected, we can include or exclude it into active category by clicking on the first (plus) respectively 
+second (minus) tool in the editing toolbar. In mapwindow corresponding pixels are shown by the class raster representing 
+selected areas in scatter plots. In this case we can see clouds class raster (blue), forest class raster (green) and 
+water class raster (red).
+
+<h2>NOTES</h2>
+
+The tool can analyze only integer (CELL) rasters.
+
+It works with 8 bit range smoothly. 
+The tool is capable of processing data up to 12 bit range, however it is not recommended open many scatter plots in 12/11 bit mode.
+It could require significant amount of memory and plot rendering time can be longer.
+Analyzing of raster data with higher range is not recommended.   
+The raster range can be lowered using <em><a href="r.rescale.html">r.rescale</a></em>.
+
+<p>
+
+<h2>KNOWN ISSUES</h2>
+
+Selection of areas in mapwindow is currently supported only if the tool was launched from 
+<em><a href="wxGUI.iclass.html">Supervised Classification Tool (wx.iclass) </a></em>
+
+<h2>SEE ALSO</h2>
+
+<em>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.components.html">wxGUI components</a>
+  <a href="r.rescale.html">r.rescale</a><br>
+</em>
+
+<p>
+See also the
+user <a href="http://grasswiki.osgeo.org/wiki/WxGUI_Interactive_Scatter_Plot_Tool">wiki</a>
+page.
+
+<h2>AUTHOR</h2>
+
+Stepan
+Turek, <a href="http://grasswiki.osgeo.org/wiki/GRASS_GSoC_2013_GRASS_GIS_Interactive_Scatter_Plot_Tool">Google
+Summer of Code 2013</a> (mentor: Martin Landa)
+
+<p>
+<i>$Date: 2013-07-04 09:55:17 +0200 (Thu, 04 Jul 2013) $</i>

Added: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
===================================================================
(Binary files differ)


Property changes on: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,320 @@
+"""!
+ at package iclass.plots
+
+ at brief wxIClass plots (histograms, coincidence plots).
+
+Classes:
+ - plots::PlotPanel
+
+(C) 2006-2011 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 Vaclav Petras <wenzeslaus gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+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.
+    
+    There are two types of plots: histograms and coincidence plots.
+    Histograms show frequency of cell category values in training areas
+    for each band and for one category. Coincidence plots show min max range
+    of classes for each band.
+    """
+    def __init__(self, parent, giface, stats_data):
+        scrolled.ScrolledPanel.__init__(self, parent)
+        
+        self.SetupScrolling(scroll_x = False, scroll_y = True)
+        self._giface = giface
+        self.parent = parent
+        self.canvasList = []
+        self.bandList = []
+        self.stats_data = stats_data
+        self.currentCat = None
+        
+        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"),
+                                                _("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 IClassIScattPanel
+            self.scatt_plot_panel = IClassIScattPanel(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():
+                self.ClearPlots()
+                return
+            self.DrawHistograms(stat)
+        else:
+            self.DrawCoincidencePlots()
+
+        self.Layout()
+
+
+    def StddevChanged(self):
+        """!Standard deviation multiplier changed, redraw histograms"""
+        if self.plotSwitch.GetSelection() == 0:
+            stat = self.stats_data.GetStatistics(self.currentCat)
+            self.UpdateRanges(stat)
+        
+    def EnableZoom(self, type, enable = True):
+        for canvas in self.canvasList:
+            canvas.SetEnableZoom(enable)
+            
+        #canvas.zoom = type
+        
+    def EnablePan(self, enable = True):
+        for canvas in self.canvasList:
+            canvas.SetEnableDrag(enable)
+            
+    def DestroyPlots(self):
+        """!Destroy all plot canvases"""
+        for panel in self.canvasList:
+            panel.Destroy()
+            
+        self.canvasList = []
+
+    def ClearPlots(self):
+        """!Clears plot canvases"""
+        for bandIdx in range(len(self.bandList)):
+            self.canvasList[bandIdx].Clear()
+            
+    def Reset(self):
+        """!Reset plots (when new map imported)"""
+        self.currentCat = None
+        self.ClearPlots()
+        # bands are still the same
+        
+    def CreatePlotCanvases(self):
+        """!Create plot canvases according to the number of bands"""
+        for band in self.bandList:
+            canvas = plot.PlotCanvas(self.canvasPanel)
+            canvas.SetMinSize((-1, 140))
+            canvas.SetFontSizeTitle(10)
+            canvas.SetFontSizeAxis(8)
+            self.canvasList.append(canvas)
+            
+            self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+        
+        self.SetVirtualSize(self.GetBestVirtualSize())
+        self.Layout()
+        
+    def UpdatePlots(self, group, subgroup, currentCat, stats_data):
+        """!Update plots after new analysis
+        
+        @param group imagery group
+        @param subgroup imagery group
+        @param currentCat currently selected category (class)
+        @param stats_data StatisticsData instance (defined in statistics.py)
+        """
+        self.stats_data = stats_data
+        self.currentCat = currentCat
+        self.bandList = self.parent.GetGroupLayers(group, subgroup)
+        
+        graphType = self.plotSwitch.GetSelection()
+
+        stat = self.stats_data.GetStatistics(currentCat)
+        if not stat.IsReady() and graphType == 0:
+            return
+            
+        self.DestroyPlots()
+        self.CreatePlotCanvases()
+        self.OnPlotTypeSelected(None)
+        
+    def UpdateCategory(self, cat):
+        self.currentCat = cat
+    
+    def DrawCoincidencePlots(self):
+        """!Draw coincidence plots"""
+        for bandIdx in range(len(self.bandList)):
+            self.canvasList[bandIdx].SetYSpec(type = 'none')
+            lines = []
+            level = 0.5
+            lines.append(self.DrawInvisibleLine(level))
+
+            cats = self.stats_data.GetCategories()
+            for i, cat in enumerate(cats):
+                stat = self.stats_data.GetStatistics(cat)
+                if not stat.IsReady():
+                    continue
+                color = stat.color
+                level = i + 1
+                line = self.DrawCoincidenceLine(level, color, stat.bands[bandIdx])
+                lines.append(line)
+            
+            # invisible 
+            level += 0.5
+            lines.append(self.DrawInvisibleLine(level))
+            
+            plotGraph = plot.PlotGraphics(lines, title = self.bandList[bandIdx])
+            self.canvasList[bandIdx].Draw(plotGraph)
+        
+    def DrawCoincidenceLine(self, level, color, bandValues):
+        """!Draw line between band min and max values
+        
+        @param level y coordinate of line
+        @param color class color
+        @param bandValues BandStatistics instance
+        """
+        minim = bandValues.min
+        maxim = bandValues.max
+        points = [(minim, level), (maxim, level)]
+        color = wx.Colour(*map(int, color.split(':')))
+        return plot.PolyLine(points, colour = color, width = 4)
+        
+    def DrawInvisibleLine(self, level):
+        """!Draw white line to achieve better margins"""
+        points = [(100, level), (101, level)]
+        return plot.PolyLine(points, colour = wx.WHITE, width = 1)
+        
+    def DrawHistograms(self, statistics):
+        """!Draw histograms for one class
+        
+        @param statistics statistics for one class
+        """
+        self.histogramLines = []
+        for bandIdx in range(len(self.bandList)):
+            self.canvasList[bandIdx].Clear()
+            self.canvasList[bandIdx].SetYSpec(type = 'auto')
+            histgramLine = self.CreateHistogramLine(bandValues = statistics.bands[bandIdx])
+                                                
+            meanLine = self.CreateMean(bandValues = statistics.bands[bandIdx])
+            
+            minLine = self.CreateMin(bandValues = statistics.bands[bandIdx])
+            
+            maxLine = self.CreateMax(bandValues = statistics.bands[bandIdx])
+            
+            self.histogramLines.append([histgramLine, meanLine, minLine, maxLine])
+            
+            maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
+            minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
+            
+            plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
+                                          title = self.bandList[bandIdx])
+            self.canvasList[bandIdx].Draw(plotGraph)
+                            
+    def CreateMinRange(self, bandValues):
+        maxVal = max(bandValues.histo)
+        rMin = bandValues.rangeMin
+        
+        points = [(rMin, 0), (rMin, maxVal)]
+        
+        return plot.PolyLine(points, colour = wx.RED, width = 1)
+        
+    def CreateMaxRange(self, bandValues):
+        maxVal = max(bandValues.histo)
+        rMax = bandValues.rangeMax
+        points = [(rMax, 0), (rMax, maxVal)]
+        
+        return plot.PolyLine(points, colour = wx.RED, width = 1)
+        
+    def CreateMean(self, bandValues):
+        maxVal = max(bandValues.histo)
+        mean = bandValues.mean
+        points = [(mean, 0), (mean, maxVal)]
+        
+        return plot.PolyLine(points, colour = wx.BLUE, width = 1)
+        
+    def CreateMin(self, bandValues):
+        maxVal = max(bandValues.histo)
+        minim = bandValues.min
+        points = [(minim, 0), (minim, maxVal)]
+        
+        return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
+        
+    def CreateMax(self, bandValues):
+        maxVal = max(bandValues.histo)
+        maxim = bandValues.max
+        points = [(maxim, 0), (maxim, maxVal)]
+        
+        return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
+        
+    def CreateHistogramLine(self, bandValues):
+        points = []
+        for cellCat, count in enumerate(bandValues.histo):
+            if cellCat < bandValues.min - 5:
+                continue
+            if cellCat > bandValues.max + 5:
+                break
+            points.append((cellCat, count))
+            
+        return plot.PolyLine(points, colour = wx.BLACK, width = 1)
+         
+    def UpdateRanges(self, statistics):
+        """!Redraw ranges lines in histograms when std dev multiplier changes
+        
+        @param statistics python Statistics instance
+        """
+        for bandIdx in range(len(self.bandList)):
+            self.canvasList[bandIdx].Clear()
+            maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
+            minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
+            
+            plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
+                                          title = self.bandList[bandIdx])
+            self.canvasList[bandIdx].Draw(plotGraph)
+        

Added: sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,1234 @@
+"""!
+ at package mapdisp.frame
+
+ at brief Map display with toolbar for various display management
+functions, and additional toolbars (vector digitizer, 3d view).
+
+Can be used either from Layer Manager or as d.mon backend.
+
+Classes:
+ - mapdisp::MapFrame
+
+(C) 2006-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 Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (SingleMapFrame, handlers support)
+ at author Anna Kratochvilova <kratochanna gmail.com> (SingleMapFrame)
+ at author Stepan Turek <stepan.turek seznam.cz> (handlers support)
+"""
+
+import os
+import sys
+import copy
+
+from core import globalvar
+import wx
+import wx.aui
+
+if os.path.join(globalvar.ETCWXDIR, "icons") not in sys.path:
+    sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+if os.path.join(globalvar.ETCDIR, "python") not in sys.path:
+    sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
+
+from core               import globalvar
+from core.render        import Map
+from vdigit.toolbars    import VDigitToolbar
+from mapdisp.toolbars   import MapToolbar, NvizIcons
+from mapdisp.gprint     import PrintOptions
+from core.gcmd          import GError, GMessage
+from dbmgr.dialogs      import DisplayAttributesDialog
+from core.utils         import ListOfCatsToRange, GetLayerNameFromCmd, _
+from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
+from core.debug         import Debug
+from core.settings      import UserSettings
+from gui_core.mapdisp   import SingleMapFrame
+from mapwin.base import MapWindowProperties
+from gui_core.query     import QueryDialog, PrepareQueryResults
+from mapwin.buffered import BufferedMapWindow
+from mapwin.decorations import TextLayerDialog, \
+    LegendController, BarscaleController, ArrowController
+from modules.histogram  import HistogramFrame
+from wxplot.histogram   import HistogramPlotFrame
+from wxplot.profile     import ProfileFrame
+from wxplot.scatter     import ScatterFrame
+from mapwin.analysis import ProfileController, MeasureDistanceController
+from gui_core.forms import GUI
+
+from mapdisp import statusbar as sb
+
+import grass.script as grass
+
+from grass.pydispatch.signal import Signal
+
+
+class MapFrame(SingleMapFrame):
+    """!Main frame for map display window. Drawing takes place in
+    child double buffered drawing window.
+    """
+    def __init__(self, parent, giface, title = _("GRASS GIS - Map display"),
+                 toolbars = ["map"], tree = None, notebook = None, lmgr = None,
+                 page = None, Map = Map(), auimgr = None, name = 'MapWindow', **kwargs):
+        """!Main map display window with toolbars, statusbar and
+        2D map window, 3D map window and digitizer.
+        
+        @param toolbars array of activated toolbars, e.g. ['map', 'digit']
+        @param tree reference to layer tree
+        @param notebook control book ID in Layer Manager
+        @param lmgr Layer Manager
+        @param page notebook page with layer tree
+        @param Map instance of render.Map
+        @param auimgs AUI manager
+        @param name frame name
+        @param kwargs wx.Frame attributes
+        """
+        SingleMapFrame.__init__(self, parent = parent, title = title,
+                              Map = Map, auimgr = auimgr, name = name, **kwargs)
+        
+        self._giface = giface
+        # Layer Manager object
+        # need by GLWindow (a lot), VDigitWindow (a little bit)
+        self._layerManager = lmgr
+        # Layer Manager layer tree object
+        # used for VDigit toolbar and window and GLWindow
+        self.tree = tree
+        # Notebook page holding the layer tree
+        # used only in OnCloseWindow
+        self.page = page
+        # Layer Manager layer tree notebook
+        # used only in OnCloseWindow
+        self.layerbook = notebook
+
+        # Emitted when starting (switching to) 3D mode.
+        # Parameter firstTime specifies if 3D was already actived.
+        self.starting3dMode = Signal("MapFrame.starting3dMode")
+
+        # Emitted when ending (switching from) 3D mode.
+        self.ending3dMode = Signal("MapFrame.ending3dMode")
+
+        # properties are shared in other objects, so defining here
+        self.mapWindowProperties = MapWindowProperties()
+        self.mapWindowProperties.setValuesFromUserSettings()
+
+        #
+        # Add toolbars
+        #
+        for toolb in toolbars:
+            self.AddToolbar(toolb)
+
+        #
+        # Add statusbar
+        #
+        
+        # items for choice
+        self.statusbarItems = [sb.SbCoordinates,
+                               sb.SbRegionExtent,
+                               sb.SbCompRegionExtent,
+                               sb.SbShowRegion,
+                               sb.SbAlignExtent,
+                               sb.SbResolution,
+                               sb.SbDisplayGeometry,
+                               sb.SbMapScale,
+                               sb.SbGoTo,
+                               sb.SbProjection]
+                            
+        self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
+                                           sb.SbDisplayGeometry,
+                                           sb.SbShowRegion,
+                                           sb.SbResolution,
+                                           sb.SbMapScale)
+        
+        # create statusbar and its manager
+        statusbar = self.CreateStatusBar(number = 4, style = 0)
+        statusbar.SetStatusWidths([-5, -2, -1, -1])
+        self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
+        
+        # fill statusbar manager
+        self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
+        self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
+        sbRender = sb.SbRender(self, statusbar = statusbar, position = 3)
+        self.statusbarManager.AddStatusbarItem(sbRender)
+        
+        self.statusbarManager.Update()
+        
+        #
+        self.Map.updateProgress.connect(self.statusbarManager.SetProgress)
+
+        # init decoration objects
+        self.decorations = {}
+        self.legend = LegendController(self.Map, self._giface)
+        self.barscale = BarscaleController(self.Map, self._giface)
+        self.arrow = ArrowController(self.Map, self._giface)
+        self.decorations[self.legend.id] = self.legend
+        self.decorations[self.barscale.id] = self.barscale
+        self.decorations[self.arrow.id] = self.arrow
+
+        self.mapWindowProperties.autoRenderChanged.connect(
+            lambda value:
+            self.OnRender(None) if value else None)
+
+        #
+        # Init map display (buffered DC & set default cursor)
+        #
+        self.MapWindow2D = BufferedMapWindow(self, giface = self._giface,
+                                             Map=self.Map,
+                                             properties=self.mapWindowProperties,
+                                             overlays=self.decorations)
+        self.MapWindow2D.mapQueried.connect(self.Query)
+        self.MapWindow2D.overlayActivated.connect(self._activateOverlay)
+        self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
+        self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
+        for overlay in (self.legend, self.barscale, self.arrow):
+            overlay.overlayChanged.connect(lambda: self.MapWindow2D.UpdateMap(render=False, renderVector=False))
+        self._setUpMapWindow(self.MapWindow2D)
+
+        self.MapWindow2D.mouseHandlerUnregistered.connect(self.ResetPointer)
+
+        self.MapWindow2D.InitZoomHistory()
+        self.MapWindow2D.zoomChanged.connect(self.StatusbarUpdate)
+
+        self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
+        # default is 2D display mode
+        self.MapWindow = self.MapWindow2D
+        self.MapWindow.SetNamedCursor('default')
+        # used by vector digitizer
+        self.MapWindowVDigit = None
+        # used by Nviz (3D display mode)
+        self.MapWindow3D = None 
+
+        #
+        # initialize region values
+        #
+        self._initMap(Map = self.Map) 
+
+        self.toolbars['map'].SelectDefault()
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Bind(wx.EVT_SIZE,     self.OnSize)
+        
+        #
+        # Update fancy gui style
+        #
+        self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).Name('2d').
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
+        self._mgr.Update()
+
+        #
+        # Init print module and classes
+        #
+        self.printopt = PrintOptions(self, self.MapWindow)
+
+        #
+        # Re-use dialogs
+        #
+        self.dialogs = {}
+        self.dialogs['attributes'] = None
+        self.dialogs['scatt_plot'] = None
+        self.dialogs['category'] = None
+        self.dialogs['vnet'] = None
+        self.dialogs['query'] = None
+
+        self.measureDistController = None
+
+    def GetMapWindow(self):
+        return self.MapWindow
+    
+    def _addToolbarVDigit(self):
+        """!Add vector digitizer toolbar
+        """
+        from vdigit.main import haveVDigit, VDigit
+        
+        if not haveVDigit:
+            from vdigit import errorMsg
+            
+            self.toolbars['map'].combo.SetValue(_("2D view"))
+            
+            GError(_("Unable to start wxGUI vector digitizer.\n"
+                     "Details: %s") % errorMsg, parent = self)
+            return
+
+        if not self.MapWindowVDigit:
+            from vdigit.mapwindow import VDigitWindow
+            self.MapWindowVDigit = VDigitWindow(parent=self, giface=self._giface,
+                                                properties=self.mapWindowProperties,
+                                                Map=self.Map, tree=self.tree,
+                                                lmgr=self._layerManager,
+                                                overlays=self.decorations)
+            self._setUpMapWindow(self.MapWindowVDigit)
+            self.MapWindowVDigit.digitizingInfo.connect(
+                lambda text:
+                self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(text))
+            self.MapWindowVDigit.digitizingInfoUnavailable.connect(
+                lambda:
+                self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
+            self.MapWindowVDigit.Show()
+            self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).Name('vdigit').
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
+        
+        self.MapWindow = self.MapWindowVDigit
+        
+        if self._mgr.GetPane('2d').IsShown():
+            self._mgr.GetPane('2d').Hide()
+        elif self._mgr.GetPane('3d').IsShown():
+            self._mgr.GetPane('3d').Hide()
+        self._mgr.GetPane('vdigit').Show()
+        self.toolbars['vdigit'] = VDigitToolbar(parent=self, toolSwitcher=self._toolSwitcher,
+                                                MapWindow = self.MapWindow,
+                                                digitClass=VDigit, giface=self._giface,
+                                                layerTree=self.tree)
+        self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
+        
+        self._mgr.AddPane(self.toolbars['vdigit'],
+                          wx.aui.AuiPaneInfo().
+                          Name("vdigittoolbar").Caption(_("Vector Digitizer Toolbar")).
+                          ToolbarPane().Top().Row(1).
+                          LeftDockable(False).RightDockable(False).
+                          BottomDockable(False).TopDockable(True).
+                          CloseButton(False).Layer(2).
+                          BestSize((self.toolbars['vdigit'].GetBestSize())))
+        # change mouse to draw digitized line
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype     = 0
+        self.MapWindow.pen          = wx.Pen(colour = 'red',   width = 2, style = wx.SOLID)
+        self.MapWindow.polypen      = wx.Pen(colour = 'green', width = 2, style = wx.SOLID)
+
+    def AddNviz(self):
+        """!Add 3D view mode window
+        """
+        from nviz.main import haveNviz, GLWindow, errorMsg
+        
+        # check for GLCanvas and OpenGL
+        if not haveNviz:
+            self.toolbars['map'].combo.SetValue(_("2D view"))
+            GError(parent = self,
+                   message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
+                               "was not found or loaded properly.\n"
+                               "Switching back to 2D display mode.\n\nDetails: %s" % errorMsg))
+            return
+
+        # here was disabling 3D for other displays, now done on starting3dMode
+
+        self.toolbars['map'].Enable2D(False)
+        # add rotate tool to map toolbar
+        self.toolbars['map'].InsertTool((('rotate', NvizIcons['rotate'],
+                                          self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
+        self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
+                                          tool=self.toolbars['map'].rotate)
+        self.toolbars['map'].InsertTool((('flyThrough', NvizIcons['flyThrough'],
+                                          self.OnFlyThrough, wx.ITEM_CHECK, 8),))
+        self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
+                                          tool=self.toolbars['map'].flyThrough)
+        self.toolbars['map'].ChangeToolsDesc(mode2d = False)
+        # update status bar
+        
+        self.statusbarManager.HideStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+        self.statusbarManager.SetMode(0)
+        
+        # erase map window
+        self.MapWindow.EraseMap()
+        
+        self._giface.WriteCmdLog(_("Starting 3D view mode..."))
+        self.SetStatusText(_("Please wait, loading data..."), 0)
+        
+        # create GL window
+        if not self.MapWindow3D:
+            self.MapWindow3D = GLWindow(self, giface = self._giface, id = wx.ID_ANY, frame = self,
+                                        Map = self.Map, tree = self.tree, lmgr = self._layerManager)
+            self._setUpMapWindow(self.MapWindow3D)
+            self.MapWindow3D.mapQueried.connect(self.Query)
+            self.MapWindow = self.MapWindow3D
+            self.MapWindow.SetNamedCursor('default')
+
+            # here was AddNvizTools in lmgr
+            self.starting3dMode.emit(firstTime=True)
+
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
+                              Dockable(False).BestSize((-1,-1)).Name('3d').
+                              CloseButton(False).DestroyOnClose(True).
+                              Layer(0))
+            
+            self.MapWindow3D.Show()
+            self.MapWindow3D.ResetViewHistory()            
+            self.MapWindow3D.UpdateView(None)
+            self.MapWindow3D.overlayActivated.connect(self._activateOverlay)
+            self.MapWindow3D.overlayHidden.connect(self._hideOverlay)
+            self.legend.overlayChanged.connect(lambda: self.MapWindow3D.UpdateOverlays())
+        else:
+            self.MapWindow = self.MapWindow3D
+            os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True, windres3 = True)
+            self.MapWindow3D.GetDisplay().Init()
+            del os.environ['GRASS_REGION']
+            
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.GetPane('3d').Show()
+
+            # here was AddNvizTools in lmgr and updating of pages
+            self.starting3dMode.emit(firstTime=False)
+
+            self.MapWindow3D.ResetViewHistory()
+
+        self._giface.updateMap.disconnect(self.MapWindow2D.UpdateMap)
+        self._giface.updateMap.connect(self.MapWindow3D.UpdateMap)
+        self.MapWindow3D.overlays = self.MapWindow2D.overlays
+        self.MapWindow3D.textdict = self.MapWindow2D.textdict
+        # update overlays needs to be called after because getClientSize
+        # is called during update and it must give reasonable values
+        wx.CallAfter(self.MapWindow3D.UpdateOverlays)
+        
+        self.SetStatusText("", 0)
+        self._mgr.Update()
+
+    def Disable3dMode(self):
+        """Disables 3D mode (NVIZ) in user interface."""
+        # TODO: this is broken since item is removed but switch is drived by index
+        if '3D' in self.toolbars['map'].combo.GetString(1):
+            self.toolbars['map'].combo.Delete(1)
+
+    def RemoveNviz(self):
+        """!Restore 2D view"""
+        try:
+            self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
+            self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
+        except AttributeError:
+            pass
+        
+        # update status bar
+        self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+        self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
+                                                       key = 'statusbarMode',
+                                                       subkey = 'selection'))
+        self.SetStatusText(_("Please wait, unloading data..."), 0)
+        self._giface.WriteCmdLog(_("Switching back to 2D view mode..."))
+        if self.MapWindow3D:
+            self.MapWindow3D.OnClose(event = None)
+        # switch from MapWindowGL to MapWindow
+        self._mgr.GetPane('2d').Show()
+        self._mgr.GetPane('3d').Hide()
+
+        self.MapWindow = self.MapWindow2D
+        # here was RemoveNvizTools form lmgr
+        self.ending3dMode.emit()
+        try:
+            self.MapWindow2D.overlays = self.MapWindow3D.overlays
+            self.MapWindow2D.textdict = self.MapWindow3D.textdict
+        except AttributeError:
+            pass
+        # TODO: here we end because self.MapWindow3D is None for a while
+        self._giface.updateMap.disconnect(self.MapWindow3D.UpdateMap)
+        self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
+
+        self.MapWindow.UpdateMap()
+        self._mgr.Update()
+        self.GetMapToolbar().SelectDefault()
+        
+    def AddToolbar(self, name, fixed = False):
+        """!Add defined toolbar to the window
+
+        Currently recognized toolbars are:
+         - 'map'     - basic map toolbar
+         - 'vdigit'  - vector digitizer
+
+        @param name toolbar to add
+        @param fixed fixed toolbar
+        """
+        # default toolbar
+        if name == "map":
+            self.toolbars['map'] = MapToolbar(self, toolSwitcher=self._toolSwitcher)
+            
+            self._mgr.AddPane(self.toolbars['map'],
+                              wx.aui.AuiPaneInfo().
+                              Name("maptoolbar").Caption(_("Map Toolbar")).
+                              ToolbarPane().Top().Name('mapToolbar').
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).
+                              BestSize((self.toolbars['map'].GetBestSize())))
+            
+        # vector digitizer
+        elif name == "vdigit":
+            self.toolbars['map'].combo.SetValue(_("Digitize"))
+            self._addToolbarVDigit()
+        
+        if fixed:
+            self.toolbars['map'].combo.Disable()
+         
+        self._mgr.Update()
+        
+    def RemoveToolbar (self, name):
+        """!Removes defined toolbar from the window
+
+        @todo Only hide, activate by calling AddToolbar()
+        """
+        # cannot hide main toolbar
+        if name == "map":
+            return
+        
+        self._mgr.DetachPane(self.toolbars[name])
+        self._toolSwitcher.RemoveToolbarFromGroup('mouseUse', self.toolbars[name])
+        self.toolbars[name].Destroy()
+        self.toolbars.pop(name)
+        
+        if name == 'vdigit':
+            self._mgr.GetPane('vdigit').Hide()
+            self._mgr.GetPane('2d').Show()
+            self.MapWindow = self.MapWindow2D
+            
+        self.toolbars['map'].combo.SetValue(_("2D view"))
+        self.toolbars['map'].Enable2D(True)
+        
+        self._mgr.Update()
+    
+    def IsPaneShown(self, name):
+        """!Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
+        if self._mgr.GetPane(name).IsOk():
+            return self._mgr.GetPane(name).IsShown()
+        return False
+
+    def RemoveQueryLayer(self):
+        """!Removes temporary map layers (queries)"""
+        qlayer = self.GetMap().GetListOfLayers(name = globalvar.QUERYLAYER)
+        for layer in qlayer:
+            self.GetMap().DeleteLayer(layer)
+
+    def OnRender(self, event):
+        """!Re-render map composition (each map layer)
+        """
+        self.RemoveQueryLayer()
+        
+        # deselect features in vdigit
+        if self.GetToolbar('vdigit'):
+            if self.MapWindow.digit:
+                self.MapWindow.digit.GetDisplay().SetSelected([])
+            self.MapWindow.UpdateMap(render = True, renderVector = True)
+        else:
+            self.MapWindow.UpdateMap(render = True)
+        
+        # update statusbar
+        self.StatusbarUpdate()
+
+    def OnPointer(self, event):
+        """!Pointer button clicked
+        """        
+        self.MapWindow.SetModePointer()
+
+        if self.GetToolbar('vdigit'):
+            self.toolbars['vdigit'].action['id'] = -1
+            self.toolbars['vdigit'].action['desc']=''
+
+    def OnRotate(self, event):
+        """!Rotate 3D view
+        """
+        self.MapWindow.mouse['use'] = "rotate"
+        
+        # change the cursor
+        self.MapWindow.SetNamedCursor('hand')
+
+    def OnFlyThrough(self, event):
+        """!Fly-through mode
+        """
+        self.MapWindow.mouse['use'] = "fly"
+        
+        # change the cursor
+        self.MapWindow.SetNamedCursor('hand')
+        self.MapWindow.SetFocus()
+
+    def SaveToFile(self, event):
+        """!Save map to image
+        """
+        if self.IsPaneShown('3d'):
+            filetype = "TIF file (*.tif)|*.tif|PPM file (*.ppm)|*.ppm"
+            ltype = [{ 'ext' : 'tif', 'type' : 'tif' },
+                     { 'ext' : 'ppm', 'type' : 'ppm' }]
+        else:
+            img = self.MapWindow.img
+            if not img:
+                GMessage(parent = self,
+                         message = _("Nothing to render (empty map). Operation canceled."))
+                return
+            filetype, ltype = GetImageHandlers(img)
+        
+        # get size
+        dlg = ImageSizeDialog(self)
+        dlg.CentreOnParent()
+        if dlg.ShowModal() != wx.ID_OK:
+            dlg.Destroy()
+            return
+        width, height = dlg.GetValues()
+        dlg.Destroy()
+        
+        # get filename
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to save the image "
+                                        "(no need to add extension)"),
+                            wildcard = filetype,
+                            style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            self.MapWindow.SaveToFile(path, fileType,
+                                      width, height)
+            
+        dlg.Destroy()
+
+    def PrintMenu(self, event):
+        """
+        Print options and output menu for map display
+        """
+        printmenu = wx.Menu()
+        # Add items to the menu
+        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+        printmenu.AppendItem(setup)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
+
+        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+        printmenu.AppendItem(preview)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+
+        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
+        printmenu.AppendItem(doprint)
+        self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(printmenu)
+        printmenu.Destroy()
+
+    def OnCloseWindow(self, event):
+        """!Window closed.
+        Also close associated layer tree page
+        """
+        Debug.msg(2, "MapFrame.OnCloseWindow(): function starts")
+        pgnum = None
+        self.Map.Clean()
+        
+        # close edited map and 3D tools properly
+        if self.GetToolbar('vdigit'):
+            maplayer = self.toolbars['vdigit'].GetLayer()
+            if maplayer:
+                self.toolbars['vdigit'].OnExit()
+        if self.IsPaneShown('3d'):
+            self.RemoveNviz()
+        
+        if not self._layerManager:
+            self.Destroy()
+        elif self.page:
+            pgnum = self.layerbook.GetPageIndex(self.page)
+            if pgnum > -1:
+                self.layerbook.DeletePage(pgnum)
+        Debug.msg(2, "MapFrame.OnCloseWindow(): function ends")
+
+    def Query(self, x, y):
+        """!Query selected layers. 
+
+        @param x,y coordinates
+        @param layers selected tree item layers
+        """
+        layers = self._giface.GetLayerList().GetSelectedLayers(checkedOnly=False)
+        rast = []
+        vect = []
+        for layer in layers:
+            name, found = GetLayerNameFromCmd(layer.cmd)
+            if not found:
+                continue
+            ltype = layer.maplayer.GetType()
+            if ltype == 'raster':
+                rast.append(name)
+            elif ltype in ('rgb', 'his'):
+                for iname in name.split('\n'):
+                    rast.append(iname)
+            elif ltype in ('vector', 'thememap', 'themechart'):
+                vect.append(name)
+        if vect:
+            # check for vector maps open to be edited
+            digitToolbar = self.GetToolbar('vdigit')
+            if digitToolbar:
+                lmap = digitToolbar.GetLayer().GetName()
+                for name in vect:
+                    if lmap == name:
+                        self._giface.WriteWarning(_("Vector map <%s> "
+                                                                  "opened for editing - skipped.") % lmap)
+                        vect.remove(name)
+
+        if not (rast + vect):
+            GMessage(parent = self,
+                     message = _('No raster or vector map layer selected for querying.'))
+            return
+
+        # set query snap distance for v.what at map unit equivalent of 10 pixels
+        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
+
+        # TODO: replace returning None by exception or so
+        try:
+            east, north = self.MapWindow.Pixel2Cell((x, y))
+        except TypeError:
+            return
+
+        if not self.IsPaneShown('3d'):
+            self.QueryMap(east, north, qdist, rast, vect)
+        else:
+            if rast:
+                self.MapWindow.QuerySurface(x, y)
+            if vect:
+                self.QueryMap(east, north, qdist, rast = [], vect = vect)
+
+    def QueryMap(self, east, north, qdist, rast, vect):
+        """!Query raster or vector map layers by r/v.what
+        
+        @param east,north coordinates
+        @param qdist query distance
+        @param rast raster map names
+        @param vect vector map names
+        """
+        Debug.msg(1, "QueryMap(): raster=%s vector=%s" % (','.join(rast),
+                                                          ','.join(vect)))
+
+        # use display region settings instead of computation region settings
+        self.tmpreg = os.getenv("GRASS_REGION")
+        os.environ["GRASS_REGION"] = self.Map.SetRegion(windres = False)
+
+        rastQuery = []
+        vectQuery = []
+        if rast:
+            rastQuery = grass.raster_what(map=rast, coord=(east, north))
+        if vect:
+            vectQuery = grass.vector_what(map=vect, coord=(east, north), distance=qdist)
+        self._QueryMapDone()
+        if 'Id' in vectQuery:
+            self._queryHighlight(vectQuery)
+
+        result = rastQuery + vectQuery
+        result = PrepareQueryResults(coordinates = (east, north), result = result)
+        if self.dialogs['query']:
+            self.dialogs['query'].Raise()
+            self.dialogs['query'].SetData(result)
+        else:
+            self.dialogs['query'] = QueryDialog(parent = self, data = result)
+            self.dialogs['query'].Bind(wx.EVT_CLOSE, self._oncloseQueryDialog)
+            self.dialogs['query'].Show()
+
+    def _oncloseQueryDialog(self, event):
+        self.dialogs['query'] = None
+        event.Skip()
+
+    def _queryHighlight(self, vectQuery):
+        """!Highlight category from query."""
+        cats = name = None
+        for res in vectQuery:
+            cats = {res['Layer']: [res['Category']]}
+            name = res['Map']
+        try:
+            qlayer = self.Map.GetListOfLayers(name = globalvar.QUERYLAYER)[0]
+        except IndexError:
+            qlayer = None
+
+        if not (cats and name):
+            if qlayer:
+                self.Map.DeleteLayer(qlayer)
+                self.MapWindow.UpdateMap(render = False, renderVector = False)
+            return
+
+        if not self.IsPaneShown('3d') and self.IsAutoRendered():
+            # highlight feature & re-draw map
+            if qlayer:
+                qlayer.SetCmd(self.AddTmpVectorMapLayer(name, cats,
+                                                        useId = False,
+                                                        addLayer = False))
+            else:
+                qlayer = self.AddTmpVectorMapLayer(name, cats, useId = False)
+            
+            # set opacity based on queried layer
+            # TODO fix
+            # opacity = layer.maplayer.GetOpacity(float = True)
+            # qlayer.SetOpacity(opacity)
+            
+            self.MapWindow.UpdateMap(render = False, renderVector = False)
+
+    def _QueryMapDone(self):
+        """!Restore settings after querying (restore GRASS_REGION)
+        """
+        if hasattr(self, "tmpreg"):
+            if self.tmpreg:
+                os.environ["GRASS_REGION"] = self.tmpreg
+            elif 'GRASS_REGION' in os.environ:
+                del os.environ["GRASS_REGION"]
+        elif 'GRASS_REGION' in os.environ:
+            del os.environ["GRASS_REGION"]
+        
+        if hasattr(self, "tmpreg"):
+            del self.tmpreg
+        
+    def OnQuery(self, event):
+        """!Query tools menu"""
+        self.MapWindow.mouse['use'] = "query"
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype = 0
+        
+        # change the cursor
+        self.MapWindow.SetNamedCursor('cross')
+        
+    def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
+        """!Add temporal vector map layer to map composition
+
+        @param name name of map layer
+        @param useId use feature id instead of category 
+        """
+        # color settings from ATM
+        color = UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'color')
+        colorStr = str(color[0]) + ":" + \
+            str(color[1]) + ":" + \
+            str(color[2])
+
+        # icon used in vector display and its size
+        icon = ''
+        size = 0
+        # here we know that there is one selected layer and it is vector
+        vparam = self._giface.GetLayerList().GetSelectedLayers()[0].cmd
+        for p in vparam:
+            if '=' in p:
+                parg,pval = p.split('=', 1)
+                if parg == 'icon': icon = pval
+                elif parg == 'size': size = float(pval)
+
+        pattern = ["d.vect",
+                   "map=%s" % name,
+                   "color=%s" % colorStr,
+                   "fcolor=%s" % colorStr,
+                   "width=%d"  % UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'width')]
+        if icon != '':
+            pattern.append('icon=%s' % icon)
+        if size > 0:
+            pattern.append('size=%i' % size)
+        
+        if useId:
+            cmd = pattern
+            cmd.append('-i')
+            cmd.append('cats=%s' % str(cats))
+        else:
+            cmd = []
+            for layer in cats.keys():
+                cmd.append(copy.copy(pattern))
+                lcats = cats[layer]
+                cmd[-1].append("layer=%d" % layer)
+                cmd[-1].append("cats=%s" % ListOfCatsToRange(lcats))
+
+        if addLayer:
+            if useId:
+                return self.Map.AddLayer(ltype = 'vector', name = globalvar.QUERYLAYER, command = cmd,
+                                         active = True, hidden = True, opacity = 1.0)
+            else:
+                return self.Map.AddLayer(ltype = 'command', name = globalvar.QUERYLAYER, command = cmd,
+                                         active = True, hidden = True, opacity = 1.0)
+        else:
+            return cmd
+
+    def OnMeasure(self, event):
+        if not self.measureDistController:
+            self.measureDistController = MeasureDistanceController(self._giface,
+                                                                   mapWindow=self.GetMapWindow())
+        self.measureDistController.Start()
+
+    def OnProfile(self, event):
+        """!Launch profile tool
+        """
+        rasters = []
+        layers = self._giface.GetLayerList().GetSelectedLayers()
+        for layer in layers:
+            if layer.type == 'raster':
+                rasters.append(layer.maplayer.name)
+        self.Profile(rasters=rasters)
+
+    def Profile(self, rasters=None):
+        """!Launch profile tool"""
+        self.profileController = ProfileController(self._giface,
+                                                   mapWindow=self.GetMapWindow())
+        win = ProfileFrame(parent=self, rasterList=rasters,
+                           units=self.Map.projinfo['units'],
+                           controller=self.profileController)
+        win.Show()
+        # Open raster select dialog to make sure that a raster (and
+        # the desired raster) is selected to be profiled
+        win.OnSelectRaster(None)
+
+    def OnHistogramPyPlot(self, event):
+        """!Init PyPlot histogram display canvas and tools
+        """
+        raster = []
+
+        for layer in self._giface.GetLayerList().GetSelectedLayers():
+            if layer.maplayer.GetType() == 'raster':
+                raster.append(layer.maplayer.GetName())
+
+        win = HistogramPlotFrame(parent = self, rasterList = raster)
+        win.CentreOnParent()
+        win.Show()
+        
+    def OnScatterplot(self, event):
+        """!Init PyPlot scatterplot display canvas and tools
+        """
+        raster = []
+
+        for layer in self._giface.GetLayerList().GetSelectedLayers():
+            if layer.maplayer.GetType() == 'raster':
+                raster.append(layer.maplayer.GetName())
+
+        win = ScatterFrame(parent = self, rasterList = raster)
+        
+        win.CentreOnParent()
+        win.Show()
+        # Open raster select dialog to make sure that at least 2 rasters (and the desired rasters)
+        # are selected to be plotted
+        win.OnSelectRaster(None)
+
+    def OnHistogram(self, event):
+        """!Init histogram display canvas and tools
+        """
+        win = HistogramFrame(self, giface=self._giface)
+        
+        win.CentreOnParent()
+        win.Show()
+        win.Refresh()
+        win.Update()
+
+    def _activateOverlay(self, overlayId):
+        """!Launch decoration dialog according to overlay id.
+
+        @param overlayId id of overlay        
+        """
+        if overlayId > 100:
+            self.OnAddText(None)
+        elif overlayId == 0:
+            self.AddLegend(showDialog=True)
+        elif overlayId == 1:
+            self.AddBarscale(showDialog=True)
+        elif overlayId == 2:
+            self.AddArrow(showDialog=True)
+
+    def _hideOverlay(self, overlayId):
+        """!Hide overlay.
+
+        @param overlayId id of overlay        
+        """
+        self.decorations[overlayId].Hide()
+
+    def AddBarscale(self, cmd=None, showDialog=None):
+        """!Handler for scale bar map decoration menu selection."""
+        if self.IsPaneShown('3d'):
+            self.MapWindow3D.SetDrawScalebar((70, 70))
+            return
+
+        if self.barscale.IsShown() and showDialog is None:
+            self.barscale.Hide()
+            return
+
+        if cmd:
+            self.barscale.cmd = cmd
+
+        if not showDialog:
+            self.barscale.Show()
+            return
+
+        # Decoration overlay control dialog
+        if self.barscale.dialog:
+            if self.barscale.dialog.IsShown():
+                self.barscale.dialog.SetFocus()
+                self.barscale.dialog.Raise()
+            else:
+                self.barscale.dialog.Show()
+        else:
+            # If location is latlon, only display north arrow (scale won't work)
+            #        proj = self.Map.projinfo['proj']
+            #        if proj == 'll':
+            #            barcmd = 'd.barscale -n'
+            #        else:
+            #            barcmd = 'd.barscale'
+
+            # decoration overlay control dialog
+            GUI(parent=self, giface=self._giface, show=True,
+                modal=False).ParseCommand(self.barscale.cmd,
+                                          completed=(self.barscale.GetOptData, None, None))
+
+        self.MapWindow.mouse['use'] = 'pointer'
+
+    def AddLegend(self, cmd=None, showDialog=None):
+        """!Handler for legend map decoration menu selection."""
+        if self.legend.IsShown() and showDialog is None:
+            self.legend.Hide()
+            return
+        if cmd:
+            self.legend.cmd = cmd
+        else:
+            layers = self._giface.GetLayerList().GetSelectedLayers()
+            for layer in layers:
+                if layer.type == 'raster':
+                    self.legend.cmd.append('map=%s' % layer.maplayer.name)
+                    break
+
+        if not showDialog and self.legend.CmdIsValid():
+            self.legend.Show()
+            return
+
+        # Decoration overlay control dialog
+        if self.legend.dialog:
+            if self.legend.dialog.IsShown():
+                self.legend.dialog.SetFocus()
+                self.legend.dialog.Raise()
+            else:
+                self.legend.dialog.Show()
+        else:
+            GUI(parent=self, giface=self._giface, show=True,
+                modal=False).ParseCommand(self.legend.cmd,
+                                          completed=(self.legend.GetOptData, None, None))
+
+        self.MapWindow.mouse['use'] = 'pointer'
+
+    def AddArrow(self, cmd=None, showDialog=None):
+        """!Handler for north arrow menu selection."""
+        if self.IsPaneShown('3d'):
+            # here was opening of appearance page of nviz notebook
+            # but now moved to MapWindow3D where are other problematic nviz calls
+            self.MapWindow3D.SetDrawArrow((70, 70))
+            return
+
+        if self.arrow.IsShown() and showDialog is None:
+            self.arrow.Hide()
+            return
+        if cmd:
+            self.arrow.cmd = cmd
+
+        if not showDialog:
+            self.arrow.Show()
+            return
+
+        # Decoration overlay control dialog
+        if self.arrow.dialog:
+            if self.arrow.dialog.IsShown():
+                self.arrow.dialog.SetFocus()
+                self.arrow.dialog.Raise()
+            else:
+                self.arrow.dialog.Show()
+        else:
+            GUI(parent=self, giface=self._giface, show=True,
+                modal=False).ParseCommand(self.arrow.cmd,
+                                          completed=(self.arrow.GetOptData, None, None))
+
+        self.MapWindow.mouse['use'] = 'pointer'
+
+    def OnAddText(self, event):
+        """!Handler for text decoration menu selection.
+        """
+        if self.MapWindow.dragid > -1:
+            id = self.MapWindow.dragid
+            self.MapWindow.dragid = -1
+        else:
+            # index for overlay layer in render
+            if len(self.MapWindow.textdict.keys()) > 0:
+                id = max(self.MapWindow.textdict.keys()) + 1
+            else:
+                id = 101
+        
+        self.dialogs['text'] = TextLayerDialog(parent = self, ovlId = id, 
+                                               title = _('Add text layer'),
+                                               size = (400, 200))
+        self.dialogs['text'].CenterOnParent()
+
+        # If OK button pressed in decoration control dialog
+        if self.dialogs['text'].ShowModal() == wx.ID_OK:
+            text = self.dialogs['text'].GetValues()['text']
+            active = self.dialogs['text'].GetValues()['active']
+        
+            # delete object if it has no text or is not active
+            if text == '' or active == False:
+                try:
+                    self.MapWindow2D.pdc.ClearId(id)
+                    self.MapWindow2D.pdc.RemoveId(id)
+                    del self.MapWindow.textdict[id]
+                    if self.IsPaneShown('3d'):
+                        self.MapWindow3D.UpdateOverlays()
+                        self.MapWindow.UpdateMap()
+                    else:
+                        self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+                except:
+                    pass
+                return
+
+            
+            self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
+            
+            if self.IsPaneShown('3d'):
+                self.MapWindow3D.UpdateOverlays()
+                self.MapWindow3D.UpdateMap()
+            else:
+                self.MapWindow2D.pdc.ClearId(id)
+                self.MapWindow2D.pdc.SetId(id)
+                self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+            
+        self.MapWindow.mouse['use'] = 'pointer'
+        
+    def GetOptData(self, dcmd, type, params, propwin):
+        """!Callback method for decoration overlay command generated by
+        dialog created in menuform.py
+        """
+        # Reset comand and rendering options in render.Map. Always render decoration.
+        # Showing/hiding handled by PseudoDC
+        self.Map.ChangeOverlay(ovltype = type, type = 'overlay', name = '', command = dcmd,
+                               active = True, render = False)
+        self.params[type] = params
+        self.propwin[type] = propwin
+
+    def OnZoomToMap(self, event):
+        """!Set display extents to match selected raster (including
+        NULLs) or vector map.
+        """
+        Debug.msg(3, "MapFrame.OnZoomToMap()")
+        layers = None
+        if self.IsStandalone():
+            layers = self.MapWindow.GetMap().GetListOfLayers(active = False)
+        
+        self.MapWindow.ZoomToMap(layers = layers)
+
+    def OnZoomToRaster(self, event):
+        """!Set display extents to match selected raster map (ignore NULLs)
+        """
+        self.MapWindow.ZoomToMap(ignoreNulls = True)
+        
+    def OnZoomToSaved(self, event):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        self.MapWindow.ZoomToSaved()
+        
+    def OnDisplayToWind(self, event):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        self.MapWindow.DisplayToWind()
+ 
+    def OnSaveDisplayRegion(self, event):
+        """!Save display extents to named region file.
+        """
+        self.MapWindow.SaveRegion(display = True)
+
+    def OnSaveWindRegion(self, event):
+        """!Save computational region to named region file.
+        """
+        self.MapWindow.SaveRegion(display = False)
+        
+    def OnZoomMenu(self, event):
+        """!Popup Zoom menu
+        """
+        zoommenu = wx.Menu()
+        
+        for label, handler in ((_('Zoom to computational region'), self.OnZoomToWind),
+                               (_('Zoom to default region'), self.OnZoomToDefault),
+                               (_('Zoom to saved region'), self.OnZoomToSaved),
+                               (_('Set computational region from display extent'), self.OnDisplayToWind),
+                               (_('Save display geometry to named region'), self.OnSaveDisplayRegion),
+                               (_('Save computational region to named region'), self.OnSaveWindRegion)):
+            mid = wx.MenuItem(zoommenu, wx.ID_ANY, label)
+            zoommenu.AppendItem(mid)
+            self.Bind(wx.EVT_MENU, handler, mid)
+            
+        # Popup the menu. If an item is selected then its handler will
+        # be called before PopupMenu returns.
+        self.PopupMenu(zoommenu)
+        zoommenu.Destroy()
+
+    def SetProperties(self, render = False, mode = 0, showCompExtent = False,
+                      constrainRes = False, projection = False, alignExtent = True):
+        """!Set properies of map display window"""
+        self.mapWindowProperties.autoRender = render
+        self.statusbarManager.SetMode(mode)
+        self.StatusbarUpdate()
+        self.mapWindowProperties.showRegion = showCompExtent
+        self.mapWindowProperties.alignExtent = alignExtent
+        self.mapWindowProperties.resolution = constrainRes
+        self.SetProperty('projection', projection)
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone
+
+        @depreciated
+        """
+        # TODO: once it is removed from 2 places in vdigit it can be deleted
+        # here and also in base class and other classes in the tree (hopefully)
+        # and one place here still uses IsStandalone
+        Debug.msg(1, "MapFrame.IsStandalone(): Method IsStandalone is"
+                  "depreciated, use some general approach instead such as"
+                  " Signals or giface")
+        if self._layerManager:
+            return False
+        
+        return True
+    
+    def GetLayerManager(self):
+        """!Get reference to Layer Manager
+
+        @return window reference
+        @return None (if standalone)
+
+        @depreciated
+        """
+        Debug.msg(1, "MapFrame.GetLayerManager(): Method GetLayerManager is"
+                  "depreciated, use some general approach instead such as"
+                  " Signals or giface")
+        return self._layerManager
+    
+    def GetMapToolbar(self):
+        """!Returns toolbar with zooming tools"""
+        return self.toolbars['map']
+
+    def OnIScatt(self, event):
+        """!Init interactive scatterplot tools
+        """
+        if self.dialogs['scatt_plot']:
+            self.dialogs['scatt_plot'].Raise()
+            return
+
+        from scatt_plot.frame import IScattDialog
+        self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
+        
+        self.dialogs['scatt_plot'].CenterOnScreen()
+        self.dialogs['scatt_plot'].Show()
+
+    def OnVNet(self, event):
+        """!Dialog for v.net* modules 
+        """
+        if self.dialogs['vnet']:
+            self.dialogs['vnet'].Raise()
+            return
+        
+        from vnet.dialogs import VNETDialog
+        self.dialogs['vnet'] = VNETDialog(parent=self, giface=self._giface)
+        self.dialogs['vnet'].CenterOnScreen()
+        self.dialogs['vnet'].Show()
+
+    def ResetPointer(self):
+        """Sets pointer mode.
+
+        Sets pointer and toggles it (e.g. after unregistration of mouse
+        handler).
+        """
+        self.GetMapToolbar().SelectDefault()

Added: sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,269 @@
+"""!
+ at package mapdisp.toolbars
+
+ at brief Map display frame - toolbars
+
+Classes:
+ - toolbars::MapToolbar
+
+(C) 2007-2011 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 Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from nviz.main         import haveNviz
+from vdigit.main       import haveVDigit
+from icons.icon        import MetaIcon
+from core.utils import _
+
+MapIcons =  {
+    'query'      : MetaIcon(img = 'info',
+                            label = _('Query raster/vector map(s)'),
+                            desc = _('Query selected raster/vector map(s)')),
+    'addBarscale': MetaIcon(img = 'scalebar-add',
+                            label = _('Show/hide scale bar')),
+    'addLegend'  : MetaIcon(img = 'legend-add',
+                            label = _('Show/hide legend')),
+    'addNorthArrow': MetaIcon(img = 'north-arrow-add',
+                              label = _('Show/hide north arrow')),
+    'analyze'    : MetaIcon(img = 'layer-raster-analyze',
+                            label = _('Analyze map'),
+                            desc = _('Measuring, profiling, histogramming, ...')),
+    'measure'    : MetaIcon(img = 'measure-length',
+                            label = _('Measure distance')),
+    'profile'    : MetaIcon(img = 'layer-raster-profile',
+                            label = _('Profile surface map')),
+    'scatter'    : MetaIcon(img = 'layer-raster-profile',
+                            label = _("Create bivariate scatterplot of raster maps")),
+    'addText'    : MetaIcon(img = 'text-add',
+                            label = _('Add text layer')),
+    'histogram'  : MetaIcon(img = 'layer-raster-histogram',
+                            label = _('Create histogram of raster map')),
+    'vnet'       : MetaIcon(img = 'vector-tools',
+                            label = _('Vector network analysis tool')),
+    'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
+                            label = _("Interactive scatter plot tool")),
+    }
+
+NvizIcons = {
+    'rotate'    : MetaIcon(img = '3d-rotate',
+                           label = _('Rotate 3D scene'),
+                           desc = _('Drag with mouse to rotate 3D scene')), 
+    'flyThrough': MetaIcon(img = 'flythrough',
+                           label = _('Fly-through mode'),
+                           desc = _('Drag with mouse, hold Ctrl down for different mode'
+                                    ' or Shift to accelerate')),
+    'zoomIn'    : BaseIcons['zoomIn'].SetLabel(desc = _('Click mouse to zoom')),
+    'zoomOut'   : BaseIcons['zoomOut'].SetLabel(desc = _('Click mouse to unzoom'))
+    }
+
+class MapToolbar(BaseToolbar):
+    """!Map Display toolbar
+    """
+    def __init__(self, parent, toolSwitcher):
+        """!Map Display constructor
+
+        @param parent reference to MapFrame
+        """
+        BaseToolbar.__init__(self, parent=parent, toolSwitcher=toolSwitcher) # MapFrame
+        
+        self.InitToolbar(self._toolbarData())
+        self._default = self.pointer
+        
+        # optional tools
+        choices = [ _('2D view'), ]
+        self.toolId = { '2d' : 0 }
+        if self.parent.GetLayerManager():
+            log = self.parent.GetLayerManager().GetLogWindow()
+        
+        if haveNviz:
+            choices.append(_('3D view'))
+            self.toolId['3d'] = 1
+        else:
+            from nviz.main import errorMsg
+            if self.parent.GetLayerManager():
+              log.WriteCmdLog(_('3D view mode not available'))
+              log.WriteWarning(_('Reason: %s') % str(errorMsg))
+            
+            self.toolId['3d'] = -1
+
+        if haveVDigit:
+            choices.append(_('Digitize'))
+            if self.toolId['3d'] > -1:
+                self.toolId['vdigit'] = 2
+            else:
+                self.toolId['vdigit'] = 1
+        else:
+            from vdigit.main import errorMsg
+            if self.parent.GetLayerManager():
+              log.WriteCmdLog(_('Vector digitizer not available'))
+              log.WriteWarning(_('Reason: %s') % errorMsg)
+              log.WriteLog(_('Note that the wxGUI\'s vector digitizer is currently disabled '
+                             '(hopefully this will be fixed soon). '
+                             'Please keep an eye out for updated versions of GRASS. '
+                             'In the meantime you can use "v.digit" from the Develop Vector menu.'), wrap = 60)
+            
+            self.toolId['vdigit'] = -1
+        
+        self.combo = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                 choices = choices,
+                                 style = wx.CB_READONLY, size = (110, -1))
+        self.combo.SetSelection(0)
+        
+        self.comboid = self.AddControl(self.combo)
+        self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
+        
+        # realize the toolbar
+        self.Realize()
+        
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.combo.Hide()
+        self.combo.Show()
+        
+        for tool in (self.pointer, self.query, self.pan, self.zoomIn, self.zoomOut):
+            self.toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self, tool=tool)
+        
+        self.EnableTool(self.zoomBack, False)
+        
+        self.FixSize(width = 90)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        return self._getToolbarData((('displayMap', BaseIcons['display'],
+                                      self.parent.OnDraw),
+                                     ('renderMap', BaseIcons['render'],
+                                      self.parent.OnRender),
+                                     ('erase', BaseIcons['erase'],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ('pointer', BaseIcons['pointer'],
+                                      self.parent.OnPointer,
+                                      wx.ITEM_CHECK),
+                                     ('query', MapIcons['query'],
+                                      self.parent.OnQuery,
+                                      wx.ITEM_CHECK),
+                                     ('pan', BaseIcons['pan'],
+                                      self.parent.OnPan,
+                                      wx.ITEM_CHECK),
+                                     ('zoomIn', BaseIcons['zoomIn'],
+                                      self.parent.OnZoomIn,
+                                      wx.ITEM_CHECK),
+                                     ('zoomOut', BaseIcons['zoomOut'],
+                                      self.parent.OnZoomOut,
+                                      wx.ITEM_CHECK),
+                                     ('zoomExtent', BaseIcons['zoomExtent'],
+                                      self.parent.OnZoomToMap),
+                                     ('zoomBack', BaseIcons['zoomBack'],
+                                      self.parent.OnZoomBack),
+                                     ('zoomMenu', BaseIcons['zoomMenu'],
+                                      self.parent.OnZoomMenu),
+                                     (None, ),
+                                     ('analyze', MapIcons['analyze'],
+                                      self.OnAnalyze),
+                                     (None, ),
+                                     ('overlay', BaseIcons['overlay'],
+                                      self.OnDecoration),
+                                     (None, ),
+                                     ('saveFile', BaseIcons['saveFile'],
+                                      self.parent.SaveToFile),
+                                     ('printMap', BaseIcons['print'],
+                                      self.parent.PrintMenu),
+                                     (None, ))
+                                    )
+    def InsertTool(self, data):
+        """!Insert tool to toolbar
+        
+        @param data toolbar data"""
+        data = self._getToolbarData(data)
+        for tool in data:
+            self.CreateTool(*tool)
+        self.Realize()
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def RemoveTool(self, tool):
+        """!Remove tool from toolbar
+        
+        @param tool tool id"""
+        self.DeleteTool(tool)
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def ChangeToolsDesc(self, mode2d):
+        """!Change description of zoom tools for 2D/3D view"""
+        if mode2d:
+            icons = BaseIcons
+        else:
+            icons = NvizIcons
+        for i, data in enumerate(self._data):
+            for tool in (('zoomIn', 'zoomOut')):
+                if data[0] == tool:
+                    tmp = list(data)
+                    tmp[4] = icons[tool].GetDesc()
+                    self._data[i] = tuple(tmp)
+        
+    def OnSelectTool(self, event):
+        """!Select / enable tool available in tools list
+        """
+        tool =  event.GetSelection()
+        
+        if tool == self.toolId['2d']:
+            self.ExitToolbars()
+            self.Enable2D(True)
+            self.ChangeToolsDesc(mode2d = True)            
+        
+        elif tool == self.toolId['3d'] and \
+                not (self.parent.MapWindow3D and self.parent.IsPaneShown('3d')):
+            self.ExitToolbars()
+            self.parent.AddNviz()
+            
+        elif tool == self.toolId['vdigit'] and \
+                not self.parent.GetToolbar('vdigit'):
+            self.ExitToolbars()
+            self.parent.AddToolbar("vdigit")
+            self.parent.MapWindow.SetFocus()
+
+    def OnAnalyze(self, event):
+        """!Analysis tools menu
+        """
+        self._onMenu(((MapIcons["measure"],     self.parent.OnMeasure),
+                      (MapIcons["profile"],     self.parent.OnProfile),
+                      (MapIcons["scatter"],     self.parent.OnScatterplot),
+                      (MapIcons["histogram"],   self.parent.OnHistogramPyPlot),
+                      (BaseIcons["histogramD"], self.parent.OnHistogram),
+                      (MapIcons["vnet"],        self.parent.OnVNet),
+                      (MapIcons["interact_scatter"], 
+                       self.parent.OnIScatt)))
+        
+    def OnDecoration(self, event):
+        """!Decorations overlay menu
+        """
+        self._onMenu(((MapIcons["addLegend"], lambda evt: self.parent.AddLegend()),
+                      (MapIcons["addBarscale"], lambda evt: self.parent.AddBarscale()),
+                      (MapIcons["addNorthArrow"], lambda evt: self.parent.AddArrow()),
+                      (MapIcons["addText"], self.parent.OnAddText)))
+
+        
+    def ExitToolbars(self):
+        if self.parent.GetToolbar('vdigit'):
+            self.parent.toolbars['vdigit'].OnExit()
+        if self.parent.GetLayerManager() and \
+                self.parent.GetLayerManager().IsPaneShown('toolbarNviz'):
+            self.parent.RemoveNviz()
+        
+    def Enable2D(self, enabled):
+        """!Enable/Disable 2D display mode specific tools"""
+        for tool in (self.zoomMenu,
+                     self.analyze,
+                     self.printMap):
+            self.EnableTool(tool, enabled)

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,8 +1,11 @@
 all = [
     'dialogs',
+    'controllers',
+    'frame',
     'gthreading',
     'plots',
-    'sc_pl_core',
+    'scatt_core',
     'toolbars',
+    'scatt_core',
     'core_c',
     ]

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -4,6 +4,14 @@
 @brief Controller layer for scatter plot tool.
 
 Classes:
+ - controllers::ScattsManager
+ - controllers::PlotsRenderingManager
+ - controllers::CategoriesManager
+ - controllers::IMapWinDigitConnection
+ - controllers::IClassDigitConnection
+ - controllers::IMapDispConnection
+ - controllers::IClassConnection
+ - controllers::gThread
 
 (C) 2013 by the GRASS Development Team
 
@@ -14,78 +22,129 @@
 """
 import os
 import sys
+from copy import deepcopy
 
-#TODO
+import time
+import threading
+import Queue
+from core.gconsole import EVT_CMD_DONE
+
 import wx
 
-from core.gcmd     import GException, GError, RunCommand
+from core.gcmd import GException, GError, GMessage, RunCommand
+from core.settings import UserSettings
+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
+from scatt_plot.scatt_core import Core, idBandsToidScatt
+from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
 
-from scatt_plot.sc_pl_core import Core, idBandsToidScatt
+from iclass.dialogs import IClassGroupDialog
 
-from scatt_plot.gthreading import gThread
-from core.gconsole         import EVT_CMD_DONE
-
+import grass.script as grass
 from grass.pydispatch.signal import Signal
 
-class ScattsManager(wx.EvtHandler):
-    def __init__(self, guiparent, giface, iclass_mapwin):
+class ScattsManager:
+    """!Main controller."""
+    def __init__(self, guiparent, giface, iclass_mapwin = None):
         #TODO remove iclass parameter
 
-        wx.EvtHandler.__init__(self)
         self.giface = giface
         self.mapDisp  = giface.GetMapDisplay()
-        self.mapWin = giface.GetMapWindow()
 
         if iclass_mapwin:
             self.mapWin = iclass_mapwin
+        else:
+            self.mapWin = giface.GetMapWindow()
 
         self.guiparent = guiparent
 
+        self.show_add_scatt_plot = False
+
         self.core = Core()
-        self.scatts_dt = self.core.GetScattsData()
 
         self.cats_mgr = CategoriesManager(self, self.core)
+        self.render_mgr = PlotsRenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
 
-        self.thread = gThread(self);
+        self.thread = gThread();
         
-        self.scatt_plts = {}
-        self.added_cats_rasts = {}
+        self.plots = {}
 
-        self.cats_to_update = []
+        self.plot_mode =  None
+        self.pol_sel_mode = [False, None]
 
-        self.edit_cat_type =  None
-
         self.data_set = False
 
+        self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
+
+        self.renderingStarted = self.render_mgr.renderingStarted
+        self.renderingFinished = self.render_mgr.renderingFinished
+
+        self.computingStarted = Signal("ScattsManager.computingStarted")
+
         if iclass_mapwin: 
-            self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
+            self.digit_conn = IClassDigitConnection(self, self.mapWin, self.core.CatRastUpdater())
             self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
         else:
-            self.mapWin_conn = None
-            self.iclass_conn = None
+            self.digit_conn = IMapWinDigitConnection()
+            self.iclass_conn = IMapDispConnection(scatt_mgr=self, cats_mgr=self.cats_mgr, giface=self.giface)
 
-        self.tasks_pids = {
-                            'add_scatt' : [],
-                            'set_data' : -1,
-                            'set_edit_cat_data' : -1,
-                            'mapwin_conn' : [],
-                            'render_plots' : -1
-                           }
+        self._initSettings()
 
-        self.Bind(EVT_CMD_DONE, self.OnThreadDone)
+        self.modeSet = Signal("ScattsManager.mondeSet")
 
-    def SetData(self, bands):
+    def CleanUp(self):
+        self.thread.Run(callable=self.core.CleanUp)
+        self.added_cats_rasts = []
+
+        self.cats_mgr.CleanUp()
+        for scatt_id, scatt in self.plots.items():
+            if scatt['scatt']:
+                scatt['scatt'].CleanUp()
+
+    def _initSettings(self):
+        """!Initialization of settings (if not already defined)"""
+        # initializes default settings
+        initSettings = [
+                        ['selection', 'sel_pol', (255,255,0)],
+                        ['selection', 'sel_pol_vertex', (255,0,0)],
+                        ['selection', 'sel_area', (0,255,19)],
+                        ['selection', "snap_tresh", 10],
+                        ['selection', 'sel_area_opacty', 50],
+                        ['ellipses', 'show_ellips', True],
+                       ]
+
+        for init in initSettings: 
+            UserSettings.ReadSettingsFile()
+            UserSettings.Append(dict = UserSettings.userSettings, 
+                                group ='scatt',
+                                key = init[0],
+                                subkey =init[1],
+                                value = init[2],
+                                overwrite = False)
+
+    def SetData(self):
+        self.iclass_conn.SetData()
+
+    def SetBands(self, bands):
+        self.busy = wx.BusyInfo(_("Loading data..."))
+        self.CleanUp()
         self.data_set = False
-        self.tasks_pids['set_data'] = self.thread.GetId()
-        self.thread.Run(callable = self.core.SetData, bands = bands)
+        
+        if self.show_add_scatt_plot:
+            show_add=True
+        else:
+            show_add=False
 
-        #for i in range(1):
-        #    self.AddScattPlot(i)
+        self.thread.Run(callable=self.core.SetData, bands=bands, 
+                        ondone=self.SetDataDone, userdata={"show_add" : show_add})
 
     def SetDataDone(self, event):
+        del self.busy
         self.data_set = True
-        self.cats_mgr.InitCoreCats()
 
+        self.iclass_conn.SyncCats()
+        if event.userdata['show_add']:
+          self.AddScattPlot()
+
     def OnOutput(self, event):
         """!Print thread output according to debug level.
         """
@@ -94,186 +153,310 @@
     def GetBands(self):
         return self.core.GetBands()
 
-    def AddScattPlot(self, scatt_id):
+    def AddScattPlot(self):
+        if not self.data_set and self.iclass_conn:
+            self.show_add_scatt_plot = True
+            self.iclass_conn.SetData()
+            self.show_add_scatt_plot = False
+            return
+        if not self.data_set:
+            GError(_('No data set.'))
+            return
 
-        self.tasks_pids['add_scatt'].append(self.thread.GetId())
-        self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
+        self.computingStarted.emit()
 
-    def RenderScattPlts(self):
+        bands = self.core.GetBands()
 
-        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
-        for scatt_id, scatt in self.scatt_plts.iteritems():
-            scatt_dt = self.scatts_dt.GetScatt(scatt_id)
-            scatt.Plot(scatt_dt, cats_attrs)
+        #added_bands_ids = []
+        #for scatt_id in self.plots):
+        #    added_bands_ids.append[idBandsToidScatt(scatt_id)]
 
+        self.digit_conn.Update()
+        dlg = AddScattPlotDialog(parent = self.guiparent, 
+                                 bands = bands,
+                                 added_scatts_ids = self.plots.keys())
+        if dlg.ShowModal() == wx.ID_OK:
 
-    def AddScattPlotDone(self, event):
-        
-        scatt_id = event.kwds['scatt_id']
+            scatt_ids = []
+            sel_bands = dlg.GetBands()
 
-        #TODO guiparent - not very good
-        self.scatt_plts[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
+            for b_1, b_2 in sel_bands:
+           
+                transpose = False
+                if b_1 > b_2:
+                    transpose = True
+                    tmp_band = b_2
+                    b_2 = b_1
+                    b_1 = tmp_band
 
-        if self.edit_cat_type:
-            self.scatt_plts[scatt_id].StartCategoryEdit()
+                scatt_id = idBandsToidScatt(b_1, b_2, len(bands))
+                if self.plots.has_key(scatt_id):
+                    continue
 
-        scatt_dt = self.scatts_dt.GetScatt(scatt_id)
+                self.plots[scatt_id] = {'transpose' : transpose,
+                                        'scatt' : None}
+                scatt_ids.append(scatt_id)
         
-        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
+            self._addScattPlot(scatt_ids)
+            
+        dlg.Destroy()
+         
+    def _addScattPlot(self, scatt_ids):
+        self.render_mgr.NewRunningProcess()
+        self.thread.Run(callable=self.core.AddScattPlots,
+                        scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
 
-        self.scatt_plts[scatt_id].Plot(scatt_dt, cats_attrs)
-        #for scatt in scatt_dt.itervalues():
-        #    del scatt 
-        self.scatt_plts[scatt_id].GetParent().Show()
-        print "ok %d" % scatt_id
+    def AddScattPlotDone(self, event):
 
-    def CleanUp(self):
-        self.core.CleanUp()
+        scatt_ids = event.kwds['scatt_ids']
+        for s_id in scatt_ids:
+            trans = self.plots[s_id]['transpose']
 
-        for scatt_id, scatt in self.scatt_plts.items():
-            scatt.CleanUp()
-            del self.scatt_plts[scatt_id]
+            self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(scatt_id=s_id, 
+                                                                      transpose=trans)
 
-    def OnThreadDone(self, event):
-        
-        if event.exception:
-            GError(str(event.exception))
-            return
+            self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
+            self.plots[s_id]['scatt'].cursorMove.connect(lambda x, y, scatt_id: 
+                                                         self.cursorPlotMove.emit(x=x, y=y, 
+                                                                                  scatt_id=scatt_id))
 
-        if event.pid in self.tasks_pids['mapwin_conn']:
-            self.tasks_pids['mapwin_conn'].remove(event.pid)
-            updated_cats = event.ret
+            if self.plot_mode:
+                self.plots[s_id]['scatt'].SetMode(self.plot_mode)
+                self.plots[s_id]['scatt'].ZoomToExtend()
 
-            for cat in updated_cats:
-                if cat not in  self.cats_to_update:
-                    self.cats_to_update.append(cat)
+        self.render_mgr.RunningProcessDone()
 
-            if not self.tasks_pids['mapwin_conn']:
-                self.tasks_pids['render_plots'] = self.thread.GetId()
-                self.thread.Run(callable = self.core.ComputeCatScatts, 
-                                cats_ids = self.cats_to_update[:])
-                del self.cats_to_update[:]
- 
-            return
+    def PlotClosed(self, scatt_id):
+        del self.plots[scatt_id]
 
-        if self.tasks_pids['render_plots'] == event.pid:
-            #TODO how to put it to the thread - kils gui
-            self.RenderScattPlts()
-            return
-            
-        if event.pid in self.tasks_pids['add_scatt']:
-            self.AddScattPlotDone(event)
-            return
+    def SetPlotsMode(self, mode):
 
-        if self.tasks_pids['set_data'] == event.pid:
-            self.SetDataDone(event)
-            return
-        
-        if self.tasks_pids['set_edit_cat_data'] == event.pid:
-            self.SetEditCatDataDone(event)
-            return
+        self.plot_mode = mode
+        for scatt in self.plots.itervalues():
+            if scatt['scatt']:
+                scatt['scatt'].SetMode(mode)
 
-    def EditCat(self, edit_type):
+        self.modeSet.emit(mode = mode)
 
-        self.edit_cat_type = edit_type
+    def ActivateSelectionPolygonMode(self, activate):
+        self.pol_sel_mode[0] = activate
+        for scatt in self.plots.itervalues():
+            if not scatt['scatt']:
+                continue
+            scatt['scatt'].SetSelectionPolygonMode(activate)
+            if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
+                self.SetPlotsMode(None)
 
-        if self.edit_cat_type:
-            for scatt in self.scatt_plts.itervalues():
-                scatt.StopCategoryEdit()
-                scatt.StartCategoryEdit()
+        self.render_mgr.RunningProcessDone()
+        return activate
 
-        else:
-            for scatt in self.scatt_plts.itervalues():
-                scatt.StopCategoryEdit()
+    def ProcessSelectionPolygons(self, process_mode):        
+        scatts_polygons = {}        
+        for scatt_id, scatt in self.plots.iteritems():
+            if not scatt['scatt']:
+                continue
+            coords = scatt['scatt'].GetCoords()
+            if coords is not None:
+                scatts_polygons[scatt_id] = coords
 
-    def SetEditCatData(self, scatt_id, bbox):
-        
+        if not scatts_polygons:
+            return
+
         value = 1
-        if self.edit_cat_type == 'remove':
+        if process_mode == 'remove':
             value = 0
 
-        self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
+        sel_cat_id = self.cats_mgr.GetSelectedCat()
+        if not sel_cat_id:
+            dlg = wx.MessageDialog(parent = self.guiparent,
+                      message = _("In order to select arrea in scatter plot, "
+                                  "you have to select class first.\n\n"
+                                  "There is no class yet, "
+                                  "do you want to create one?"),
+                      caption = _("No class selected"),
+                      style = wx.YES_NO)
+            if dlg.ShowModal() == wx.ID_YES:
+                self.iclass_conn.EmptyCategories()
 
         sel_cat_id = self.cats_mgr.GetSelectedCat()
-        
-        self.thread.Run(callable = self.core.SetEditCatData, 
-                        scatt_id = scatt_id,
+        if not sel_cat_id:
+            return
+
+        for scatt in self.plots.itervalues():
+            if scatt['scatt']:
+                scatt['scatt'].SetEmpty()
+
+        self.computingStarted.emit()
+
+        self.render_mgr.NewRunningProcess()
+        self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
+        self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
+
+        self.thread.Run(callable = self.core.UpdateCategoryWithPolygons, 
                         cat_id = sel_cat_id,
-                        bbox = bbox,
-                        value = value)
-    
+                        scatts_pols = scatts_polygons,
+                        value = value, ondone=self.SetEditCatDataDone)
+
     def SetEditCatDataDone(self, event):
+        self.render_mgr.RunningProcessDone()
+        if event.exception:
+            GError(_("Error occured during computation of scatter plot category:\n%s"), 
+                      parent = self.guiparent, showTraceback = False)
 
-        self.RenderScattPlts()
+        cat_id = event.ret
+        self.iclass_conn.RenderCatRast(cat_id)
+            
+    def SettingsUpdated(self, chanaged_setts):
+        self.render_mgr.RenderRequest()
 
-        cat_id = event.kwds["cat_id"]
+        #['ellipses', 'show_ellips']
+    def GetCategoriesManager(self):
+        return self.cats_mgr
 
-        cat_rast = self.core.GetCatRastOut(cat_id)
+class PlotsRenderingManager:
+    """!Manages rendering of scatter plot.
 
+    @todo still space for optimalization
+    """
+    def __init__(self, scatt_mgr, cats_mgr, core):
+        self.scatt_mgr = scatt_mgr
+        self.cats_mgr = cats_mgr
+        self.core = core
+        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
 
-        if cat_rast not in self.added_cats_rasts.values():
+        self.runningProcesses = 0
 
-            cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+        self.data_to_render = {}
+        self.render_queue = []
 
-            #TODO use standard raster lib
-            name = "cat_%d" % cat_id
-            ret, err_msg = RunCommand('r.external',
-                                      output = name,
-                                      getErrorMsg = True,
-                                      input = cat_rast,
-                                      flags = "o",
-                                      overwrite = True)
+        self.cat_ids = []
+        self.cat_cond_ids = []
 
-            if ret != 0:
-                GError(_("r.external failed\n%s" % err_msg))
+        self.renderingStarted = Signal("ScattsManager.renderingStarted")
+        self.renderingFinished = Signal("ScattsManager.renderingFinished")
 
-            region = self.core.GetRegion()
-            ret, err_msg = RunCommand('r.region',
-                                      map = name,
-                                      getErrorMsg = True,
-                                      n = "%f" % region['n'],
-                                      s = "%f" % region['s'],
-                                      e = "%f" % region['e'],
-                                      w = "%f" % region['w'],
-                                      )
+    def AddRenderRequest(self, scatts):
+        for scatt_id, cat_ids in scatts:
+            if not self.data_to_render.has_key[scatt_id]:
+                self.data_to_render = cat_ids
+            else:
+                for c in cat_ids:
+                    if c not in self.data_to_render[scatt_id]:
+                         self.data_to_render[scatt_id].append(c) 
 
-            region = self.core.GetRegion()
-            ret, err_msg = RunCommand('r.colors',
-                                      map = name,
-                                      rules = "-",
-                                      stdin = "1 %s" % cats_attrs["color"],
-                                      getErrorMsg = True)
+    def NewRunningProcess(self):
+        self.runningProcesses += 1 
 
-            if ret != 0:
-                GError(_("r.region failed\n%s" % err_msg))
+    def RunningProcessDone(self):
+        self.runningProcesses -= 1
+        if self.runningProcesses <= 1:
+            self.RenderScattPlts()
 
-            self.mapWin.Map.AddLayer(ltype = "raster", name =  "cat_%d" % cat_id, render = True,
-                                     command = ["d.rast", "map=%s" % name, "values=1"])                
+    def RenderRequest(self):
+        if self.runningProcesses <= 1:
+            self.RenderScattPlts()
 
-            #elif self.giface.GetLayerTree():
-            #    self.giface.GetLayerTree().AddLayer("raster", lname =  "cat_%d" % cat_id, lchecked = True,
-            #                    lcmd = ["d.rast", "map=%s" % name, "values=1"])
+    def CategoryChanged(self, cat_ids):
+        for c in cat_ids:
+            if c not in self.cat_ids:
+                self.cat_ids.append(c)
 
-            if ret != 0:
-                GError(_("r.region failed\n%s" % err_msg))
+    def CategoryCondsChanged(self, cat_ids):
+        for c in cat_ids:
+            if c not in self.cat_cond_ids:
+                self.cat_cond_ids.append(c)
 
-            self.added_cats_rasts[cat_id] = cat_rast
+    def RenderScattPlts(self, scatt_ids = None):
+        if len(self.render_queue) > 1:
+            return 
+        
+        self.renderingStarted.emit()
+        self.render_queue.append(self.scatt_mgr.thread.GetId())
 
-        self.giface.updateMap.emit()
+        cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
+        cats = self.cats_mgr.GetCategories()[:]
+        self.scatt_mgr.thread.Run(callable=self._renderscattplts, scatt_ids=scatt_ids,
+                                  cats=cats, cats_attrs=cats_attrs,
+                                  ondone=self.RenderingDone)
 
-    def DigitDataChanged(self, vectMap, digit):
+    def _renderscattplts(self, scatt_ids, cats, cats_attrs):
+        cats.reverse()
+        cats.insert(0, 0)
+        for i_scatt_id, scatt in self.scatt_mgr.plots.items():
+            if scatt_ids is not None and \
+               i_scatt_id not in scatt_ids:
+                continue
+            if not scatt['scatt']:
+                continue
+
+            scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
+            if self._showConfEllipses():
+                ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id, cats_attrs)
+            else:
+                ellipses_dt = {}
+
+            for c in scatt_dt.iterkeys():
+                try:
+                    self.cat_ids.remove(c)
+                    scatt_dt[c]['render']=True
+                except:
+                    scatt_dt[c]['render']=False
+
+            if self.scatt_mgr.pol_sel_mode[0]:
+                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
+
+            scatt['scatt'].Plot(cats_order=cats,
+                                scatts=scatt_dt, 
+                                ellipses=ellipses_dt, 
+                                styles=cats_attrs)
+
+    def RenderingDone(self, event):
+        self.render_queue.remove(event.pid)
+        if not self.render_queue:    
+            self.renderingFinished.emit()
+
+    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
+
+        cat_id = self.cats_mgr.GetSelectedCat()
+        if not cat_id:
+            return
+
+        sel_a_cat_id = -1        
+
+        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
+        if not s:
+            return
+
+        cats_order.append(sel_a_cat_id)
+
+        col = UserSettings.Get(group='scatt', 
+                                 key='selection', 
+                                 subkey='sel_area')
+
+        col = ":".join(map(str, col))
+        opac = UserSettings.Get(group='scatt', 
+                                key='selection', 
+                                subkey='sel_area_opacty') / 100.0
+
+        cats_attrs[sel_a_cat_id] = {'color' : col,
+                                    'opacity' : opac,
+                                    'show' : True}
+
+        scatt_dt[sel_a_cat_id] = s[cat_id]
         
-        if self.mapWin_conn:
-            self.mapWin_conn.DigitDataChanged(vectMap, digit)
-            return 1
-        else:
-            return 0
+        scatt_dt[sel_a_cat_id]['render'] = False
+        if cat_id in self.cat_cond_ids:
+            scatt_dt[sel_a_cat_id]['render'] = True
+            self.cat_cond_ids.remove(cat_id)
 
-    def GetCategoriesManager(self):
-        return self.cats_mgr
+    def _showConfEllipses(self):
+        return UserSettings.Get(group='scatt', 
+                                key="ellipses", 
+                                subkey="show_ellips")
 
 class CategoriesManager:
-
+    """!Manages categories list of scatter plot.
+    """
     def __init__(self, scatt_mgr, core):
 
         self.core = core
@@ -282,27 +465,48 @@
         self.cats = {}
         self.cats_ids = []
 
-        self.sel_cat_id = -1
+        self.sel_cat_id = None
 
+        self.exportRaster = None
+
         self.initialized = Signal('CategoriesManager.initialized')
+        self.cleanuped = Signal('CategoriesManager.cleanuped')
         self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
         self.deletedCategory = Signal('CategoriesManager.deletedCategory')
         self.addedCategory = Signal('CategoriesManager.addedCategory')
 
-    def Clear(self):
-
+    def CleanUp(self):
+        
         self.cats.clear()
         del self.cats_ids[:]
 
-        self.sel_cat_id = -1   
+        self.sel_cat_id = None  
 
-    def InitCoreCats(self):
-        if self.scatt_mgr.data_set:
-            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
 
-    def AddCategory(self, cat_id = None, name = None, color = None):
+        try:
+            pos = self.cats_ids.index(cat_id)
+        except:
+            return False
 
+        if pos > new_pos:
+            pos -= 1
+
+        self.cats_ids.remove(cat_id)
+
+        self.cats_ids.insert(new_pos, cat_id)
+
+        self.scatt_mgr.render_mgr.RenderRequest()
+        return True
+
+    def _addCategory(self, cat_id):
+        self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
+                                  cat_id = cat_id)
+
+    def AddCategory(self, cat_id = None, name = None, color = None, nstd = None):
+
         if cat_id is None:
             if self.cats_ids:
                 cat_id = max(self.cats_ids) + 1
@@ -310,37 +514,62 @@
                 cat_id = 1
 
         if self.scatt_mgr.data_set:
-            ret = self.core.AddCategory(cat_id)
-            if ret < 0: #TODO
-                return -1;
+            self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
+                                      cat_id = cat_id)
+            #TODO check number of cats
+            #if ret < 0: #TODO
+            #    return -1;
 
         self.cats[cat_id] = {
-                                'name' : _('Category %s' % cat_id ),
-                                'color' : "0:0:0"
+                                'name' : 'class_%d' % cat_id,
+                                'color' : "0:0:0",
+                                'opacity' : 1.0,
+                                'show' : True,
+                                'nstd' : 1.0,
                             }
-        self.cats_ids.append(cat_id)
 
+        self.cats_ids.insert(0, cat_id)
+
         if name is not None:
             self.cats[cat_id]["name"] = name
    
         if color is not None:
             self.cats[cat_id]["color"] = color
 
+        if nstd is not None:
+            self.cats[cat_id]["nstd"] = nstd
+
         self.addedCategory.emit(cat_id = cat_id,
                                 name = self.cats[cat_id]["name"], 
                                 color = self.cats[cat_id]["color"] )
         return cat_id
 
     def SetCategoryAttrs(self, cat_id, attrs_dict):
+        render = False
+        update_cat_rast = []
+
         for k, v in attrs_dict.iteritems():
+            if not render and k in ['color', 'opacity', 'show', 'nstd']:
+                render = True
+            if k in ['color', 'name']:
+                update_cat_rast.append(k)
+
             self.cats[cat_id][k] = v
 
+        if render:
+            self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
+            self.scatt_mgr.render_mgr.RenderRequest()
+        
+        if update_cat_rast:
+            self.scatt_mgr.iclass_conn.UpdateCategoryRaster(cat_id, update_cat_rast)
+
         self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
 
     def DeleteCategory(self, cat_id):
 
         if self.scatt_mgr.data_set:
-            self.core.DeleteCategory(cat_id)
+            self.scatt_mgr.thread.Run(callable = self.core.DeleteCategory, 
+                                      cat_id = cat_id)
         del self.cats[cat_id]
         self.cats_ids.remove(cat_id)
 
@@ -349,6 +578,8 @@
     #TODO emit event?
     def SetSelectedCat(self, cat_id):
         self.sel_cat_id = cat_id
+        if self.scatt_mgr.pol_sel_mode[0]:
+            self.scatt_mgr.render_mgr.RenderRequest()
 
     def GetSelectedCat(self):
         return self.sel_cat_id
@@ -364,7 +595,50 @@
     def GetCategories(self):
         return self.cats_ids[:]
 
-class MapWinConnection:
+    def SetCategoryPosition(self):
+        if newindex > oldindex:
+            newindex -= 1
+        
+        self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
+
+    def ExportCatRast(self, cat_id):
+
+        cat_attrs = self.GetCategoryAttrs(cat_id)
+
+        dlg = ExportCategoryRaster(parent=self.scatt_mgr.guiparent, 
+                                   rasterName=self.exportRaster, 
+                                   title=_("Export scatter plot raster of class <%s>") % cat_attrs['name'])
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            self.exportCatRast = dlg.GetRasterName()
+            dlg.Destroy()
+
+            self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
+                                      userdata={'name' : cat_attrs['name']},
+                                      cat_id=cat_id,
+                                      rast_name=self.exportCatRast, 
+                                      ondone=self.OnExportCatRastDone)
+
+    def OnExportCatRastDone(self, event):
+        ret, err = event.ret
+        if ret == 0:
+            cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
+            GMessage(_("Scatter plot raster of class <%s> exported to raster map <%s>.") % 
+                      (event.userdata['name'], event.kwds['rast_name']))
+        else:
+            GMessage(_("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") % 
+                      (event.userdata['name'], event.kwds['rast_name'], err))
+
+
+class IMapWinDigitConnection:
+    """!Manage comunication of the scatter plot with digitizer in mapwindow (does not work).
+    """
+    def Update(self):
+        pass
+
+class IClassDigitConnection:
+    """!Manages comunication of the scatter plot with digitizer in wx.iclass.
+    """
     def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
         self.mapWin = mapWin
         self.vectMap = None
@@ -372,107 +646,269 @@
         self.scatt_mgr = scatt_mgr
         self.cats_mgr = scatt_mgr.cats_mgr
 
+        self.cats_to_update = []
+        self.tasks_pids = {'mapwin_conn' : []}
+
         self.thread = self.scatt_mgr.thread
 
         #TODO
         self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
 
-        #def ChangeMap(self, vectMap, layers_cats):
-        #     self.vectMap = vectMap
-        #     self.layers_cats = layers_cats
+    def Update(self):
+        self.thread.Run(callable=self.scatt_rast_updater.SyncWithMap)
 
-        #ret, region, msg = RunCommand("v.to.rast",
-        #                              flags = "gp",
-        #                              getErrorMsg = True,
-        #                              read = True)
-
     def _connectSignals(self):
         self.digit.featureAdded.connect(self.AddFeature)
         self.digit.areasDeleted.connect(self.DeleteAreas)
         self.digit.featuresDeleted.connect(self.DeleteAreas)
-        self.digit.vertexMoved.connect(self.ChangeVertex)
-        self.digit.vertexRemoved.connect(self.ChangeVertex)
-        self.digit.lineEdited.connect(self.ChangeVertex)
-        self.digit.featuresMoved.connect(self.MoveFeatures)
+        self.digit.vertexMoved.connect(self.EditedFeature)
+        self.digit.vertexRemoved.connect(self.EditedFeature)
+        self.digit.lineEdited.connect(self.EditedFeature)
+        self.digit.featuresMoved.connect(self.EditedFeature)
 
-    def AddFeature(self, new_geom, cat):
+    def AddFeature(self, new_bboxs, new_areas_cats):
         if not self.scatt_mgr.data_set:
             return
+        self.scatt_mgr.computingStarted.emit()
 
-        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-        self.thread.Run(callable = self.scatt_rast_updater.AddedFeature, 
-                        new_geom = new_geom, 
-                        cat = cat)
+        self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
+                        new_bboxs = new_bboxs, 
+                        old_bboxs = [], 
+                        old_areas_cats = [],
+                        new_areas_cats = new_areas_cats,
+                        ondone=self.OnDone)
 
-    def DeleteAreas(self, old_geoms, old_areas_cats):
+    def DeleteAreas(self, old_bboxs, old_areas_cats):
         if not self.scatt_mgr.data_set:
             return
+        self.scatt_mgr.computingStarted.emit()
 
-        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-        self.thread.Run(callable = self.scatt_rast_updater.DeletedAreas, 
-                        old_geoms = old_geoms, 
-                        old_areas_cats = old_areas_cats)
-
-    def ChangeVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
-        if not self.scatt_mgr.data_set:
-            return
-
-        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-        self.thread.Run(callable = self.scatt_rast_updater.ChangedVertex, 
-                        new_geom = new_geom, 
-                        old_geom = old_geom, 
+        self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
+                        new_bboxs = [], 
+                        old_bboxs = old_bboxs, 
                         old_areas_cats = old_areas_cats,
-                        new_areas_cats = new_areas_cats)
+                        new_areas_cats = [],
+                        ondone=self.OnDone)
 
-    def MoveFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
+    def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
         if not self.scatt_mgr.data_set:
             return
+        self.scatt_mgr.computingStarted.emit()
 
-        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-        self.thread.Run(callable = self.scatt_rast_updater.MovedFeatures, 
-                        move = move, 
-                        old_geoms = old_geoms, 
+        self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
+                        new_bboxs = new_bboxs, 
+                        old_bboxs = old_bboxs, 
                         old_areas_cats = old_areas_cats,
-                        new_areas_cats = new_areas_cats)
+                        new_areas_cats = new_areas_cats,
+                        ondone=self.OnDone)
 
     def DigitDataChanged(self, vectMap, digit):
 
         self.digit = digit
         self.vectMap = vectMap
 
+        self.digit.EmitSignals(emit = True)
+
         self.scatt_rast_updater.SetVectMap(vectMap)
 
         self._connectSignals()
 
+    def OnDone(self, event):
+        self.tasks_pids['mapwin_conn'].remove(event.pid)
+        updated_cats = event.ret
+        for cat in updated_cats:
+            if cat not in  self.cats_to_update:
+                self.cats_to_update.append(cat)
+
+        if not self.tasks_pids['mapwin_conn']:
+            self.tasks_pids['render_plots'] = self.thread.GetId()
+            self.thread.Run(callable = self.scatt_mgr.core.ComputeCatsScatts, 
+                            cats_ids = self.cats_to_update[:], ondone=self.Render)
+            del self.cats_to_update[:]
+
+    def Render(self, event):
+        self.scatt_mgr.render_mgr.RenderScattPlts()
+
+class IMapDispConnection:
+    """!Manage comunication of the scatter plot with mapdisplay in mapwindow.
+    """
+    def __init__(self, scatt_mgr, cats_mgr, giface):
+        self.scatt_mgr = scatt_mgr
+        self.cats_mgr = cats_mgr
+        self.set_g = {'group' :  None, 'subg' :  None}
+        self.giface = giface
+        self.added_cats_rasts = {}
+
+    def SetData(self):
+        dlg = IClassGroupDialog(self.scatt_mgr.guiparent, 
+                                group=self.set_g['group'],
+                                subgroup=self.set_g['subg'])
+        
+        bands = []
+        while True:
+            if dlg.ShowModal() == wx.ID_OK:
+                
+                bands = dlg.GetGroupBandsErr(parent=self.scatt_mgr.guiparent)
+                if bands:
+                    name, s = dlg.GetData()
+                    group = grass.find_file(name = name, element = 'group')
+                    self.set_g['group'] = group['name']
+                    self.set_g['subg'] = s
+
+                    break
+            else: 
+                break
+        
+        dlg.Destroy()
+        
+        if bands:
+            self.scatt_mgr.SetBands(bands)
+
+    def EmptyCategories(self):
+        return None
+
+    def SyncCats(self, cats_ids = None):
+        pass
+
+    def UpdateCategoryRaster(self, cat_id, attrs, render = True):
+
+        cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+        if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
+            return
+        cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+        if "color" in attrs:
+            ret, err_msg = RunCommand('r.colors',
+                                      map=cat_rast,
+                                      rules="-",
+                                      stdin="1 %s" % cats_attrs["color"],
+                                      getErrorMsg=True)
+
+            if ret != 0:
+                GError("r.colors failed\n%s" % err_msg)
+            if render:
+                self.giface.updateMap.emit()
+
+        if "name" in attrs:
+            #TODO hack
+            self.giface.GetLayerList()._tree.SetItemText(self.added_cats_rasts[cat_id], 
+                                                         cats_attrs['name'])
+            cats_attrs["name"]
+
+    def RenderCatRast(self, cat_id):
+
+        if not cat_id in self.added_cats_rasts.iterkeys():
+            cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+            cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
+            self.UpdateCategoryRaster(cat_id, ['color'], render = False)
+
+            cmd = ['d.rast', 'map=%s' % cat_rast]
+            #HACK
+            layer = self.giface.GetLayerList()._tree.AddLayer(ltype="raster",
+                                                         lname=cat_name,
+                                                         lcmd=cmd,
+                                                         lchecked=True)
+            self.added_cats_rasts[cat_id] = layer
+        else: #TODO settings
+            self.giface.updateMap.emit()
+
 class IClassConnection:
+    """!Manage comunication of the scatter plot with mapdisplay in wx.iclass.
+    """
     def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
         self.iclass_frame = iclass_frame
-        self.stats_data   = self.iclass_frame.stats_data
-        self.cats_mgr     = cats_mgr
-        self.scatt_mgr    = scatt_mgr
+        self.stats_data = self.iclass_frame.stats_data
+        self.cats_mgr = cats_mgr
+        self.scatt_mgr= scatt_mgr
+        self.added_cats_rasts = []
 
         self.stats_data.statisticsAdded.connect(self.AddCategory)
         self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
         self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
         self.stats_data.statisticsSet.connect(self.SetCategory)
 
+        self.iclass_frame.groupSet.connect(self.GroupSet)
+
         self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
         self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
         self.cats_mgr.addedCategory.connect(self.AddStatistics)
 
+        self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
+
         self.SyncCats()
 
-    def SyncCats(self):
+    def UpdateCategoryRaster(self, cat_id, attrs, render = True):
+        if not self.scatt_mgr.data_set:
+            return
+
+        cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+        if not cat_rast:
+            return
+
+        if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
+            return
+        cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+        train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
+
+        if "color" in attrs:
+            ret, err_msg = RunCommand('r.colors',
+                                      map=cat_rast,
+                                      rules="-",
+                                      stdin="1 %s" % cats_attrs["color"],
+                                      getErrorMsg=True)
+
+            if ret != 0:
+                GError("r.colors failed\n%s" % err_msg)
+            if render:
+                train_mgr.Render()
+
+        if "name" in attrs:
+            cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+            train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
+            cats_attrs["name"]
+
+    def RenderCatRast(self, cat_id):
+
+        train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
+        if not cat_id in self.added_cats_rasts:
+            cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+            cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
+            self.UpdateCategoryRaster(cat_id, ['color'], render = False)
+            train_mgr.AddLayer(cat_rast, alias = cat_name)
+
+            self.added_cats_rasts.append(cat_id)
+        else: #TODO settings
+            train_mgr.Render()
+
+    def SetData(self):
+        self.iclass_frame.AddBands()
+
+    def EmptyCategories(self):
+        self.iclass_frame.OnCategoryManager(None)
+
+    def SyncCats(self, cats_ids = None):
         self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
         cats = self.stats_data.GetCategories()
         for c in cats:
+            if cats_ids and c not in cats_ids:
+                continue
             stats = self.stats_data.GetStatistics(c)
-            self.cats_mgr.AddCategory(c, stats.name, stats.color)
+            self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
         self.cats_mgr.addedCategory.connect(self.AddStatistics)
 
+    def CategoryChanged(self, cat):
+        self.cats_mgr.SetSelectedCat(cat) 
+
     def AddCategory(self, cat, name, color):
         self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
-        self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
+        stats = self.stats_data.GetStatistics(cat)
+        self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color, nstd = stats.nstd)
         self.cats_mgr.addedCategory.connect(self.AddStatistics)
 
     def DeleteCategory(self, cat):
@@ -480,7 +916,6 @@
         self.cats_mgr.DeleteCategory(cat)
         self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
 
-
     def DeletAllCategories(self):
 
         self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
@@ -493,7 +928,8 @@
 
         self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
         cats_attr = {}
-        for attr in ['name', 'color']:
+
+        for attr in ['name', 'color', 'nstd']:
             if stats.has_key(attr):
                 cats_attr[attr] = stats[attr]
 
@@ -516,3 +952,117 @@
         self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
         self.stats_data.DeleteStatistics(cat_id)
         self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
+
+    def GroupSet(self, group, subgroup):
+        kwargs = {}
+        if subgroup:
+            kwargs['subgroup'] = subgroup
+
+        res = RunCommand('i.group',
+                         flags = 'g',
+                         group = group,
+                         read = True, **kwargs).strip()
+
+        if res.split('\n')[0]:
+            bands = res.split('\n')
+            self.scatt_mgr.SetBands(bands)
+
+class gThread(threading.Thread, wx.EvtHandler):
+    """!Thread for scatter plot backend"""
+    requestId = 0
+
+    def __init__(self, requestQ=None, resultQ=None, **kwds):
+        wx.EvtHandler.__init__(self)
+
+        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._want_abort_all = False
+
+        self.Bind(EVT_CMD_DONE, self.OnDone)
+        self.start()
+
+    def Run(self, *args, **kwds):
+        """!Run command in queue
+
+        @param args unnamed command arguments
+        @param kwds named command arguments
+
+        @return request id in queue
+        """
+        gThread.requestId += 1
+        self.requestQ.put((gThread.requestId, args, kwds))
+
+        return gThread.requestId
+
+    def GetId(self):
+         """!Get id for next command"""
+         return gThread.requestId + 1
+
+    def SetId(self, id):
+        """!Set starting id"""
+        gThread.requestId = id
+
+    def run(self):
+        os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
+        while True:
+            requestId, args, kwds = self.requestQ.get()
+            for key in ('callable', 'ondone', 'userdata'):
+                if key in kwds:
+                    vars()[key] = kwds[key]
+                    del kwds[key]
+                else:
+                    vars()[key] = None
+
+            requestTime = time.time()
+
+            #if self._want_abort_all and self.requestCmd is not None:
+            #    self.requestCmd.abort()
+            #    if self.requestQ.empty():
+            #        self._want_abort_all = False
+
+            # prepare
+            ret = None
+            exception = None
+            time.sleep(.1)
+
+            #to
+            #try:
+            ret = vars()['callable'](*args, **kwds)
+            #except Exception as e:
+            #    exception  = e;
+
+            self.resultQ.put((requestId, ret))
+
+            event = wxCmdDone(ondone=vars()['ondone'],
+                              kwds=kwds,
+                              args=args, #TODO expand args to kwds
+                              ret=ret,
+                              exception=exception,
+                              userdata=vars()['userdata'],
+                              pid=requestId)
+
+            # send event
+            wx.PostEvent(self, event)
+
+    def OnDone(self, event):
+        if event.ondone:
+            event.ondone(event)
+
+    def abort(self, abortall=True):
+        """!Abort command(s)"""
+        if abortall:
+            self._want_abort_all = True
+        if self.requestQ.empty():
+            self._want_abort_all = False

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,10 +1,8 @@
 """!
 @package scatt_plot.scatt_plot
 
- at brief Functions which work with scatter plot c code.
+ at brief Wrappers for scatter plot C backend.
 
-Classes:
-
 (C) 2013 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
@@ -13,80 +11,128 @@
 @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
 """
 
+import sys
+import numpy as np
 from multiprocessing import Process, Queue
 
 from ctypes import *
 try:
     from grass.lib.imagery import *
     from grass.lib.gis import Cell_head, G_get_window
-    #from grass.lib.raster import struct_Range
+except ImportError, e:
+    sys.stderr.write(_("Loading ctypes libs failed"))
 
+from core.gcmd import GException
 
-except ImportError, e:
-    sys.stderr.write(_("Loading imagery lib failed"))
+def Rasterize(polygon, rast, region, value):
+    
+    pol_size = len(polygon) * 2
+    pol = np.array(polygon, dtype=float)
 
-def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out):
+    c_uint8_p = POINTER(c_uint8)
+    c_double_p = POINTER(c_double)
 
-    return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, None)
+    rows, cols = rast.shape 
 
-    # Queue object for interprocess communication
+    #TODO creating of region is on many places
+    region['rows'] = rows
+    region['cols'] = cols
+
+    region['nsres'] = 1.0
+    region['ewres'] = 1.0
+
+    pol_p = pol.ctypes.data_as(c_double_p)
+    rast_p = rast.ctypes.data_as(c_uint8_p)
+
+    cell_h = _regionToCellHead(region)
+    I_rasterize(pol_p, 
+                len(polygon), 
+                value, 
+                pointer(cell_h), rast_p)
+
+def ApplyColormap(vals, vals_mask, colmap, out_vals):
+    
+    c_uint8_p = POINTER(c_uint8)
+
+    vals_p = vals.ctypes.data_as(c_uint8_p)
+
+
+    if hasattr(vals_mask, "ctypes"):
+        vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
+    else: #vals mask is empty (all data are selected)
+        vals_mask_p = None
+    colmap_p = colmap.ctypes.data_as(c_uint8_p)
+    out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
+
+    vals_size = vals.reshape((-1)).shape[0]
+    I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
+
+def MergeArrays(merged_arr, overlay_arr, alpha):
+    if merged_arr.shape != overlay_arr.shape:
+        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
+
+    c_uint8_p = POINTER(c_uint8)
+    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
+    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
+
+    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
+
+def MergeArrays(merged_arr, overlay_arr, alpha):
+    if merged_arr.shape != overlay_arr.shape:
+        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
+
+    c_uint8_p = POINTER(c_uint8)
+    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
+    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
+
+    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
+
+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
+
     q = Queue()
-
-    # The separate render process
     p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands, 
-                                                    n_bands, scatts, cats_rasts_in, cats_rasts_out, q))
+                                                    n_bands, scatts, cats_rasts_conds, cats_rasts, q))
     p.start()
     ret = q.get()
     p.join()
-
+    
     return ret[0], ret[1]
 
 def UpdateCatRast(patch_rast, region, cat_rast):
-
-    #return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts, None)
-    # Queue object for interprocess communication
-
-    #_updateCatRastProcess(patch_rast, region, cat_rast, None)
-    #return 
     q = Queue()
-    # The separate render process
     p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
     p.start()
-    #ret = q.get()
+    ret = q.get()
     p.join()
 
-    #return ret[0], ret[1]
+    return ret
 
 def CreateCatRast(region, cat_rast):
-
     cell_head = _regionToCellHead(region)
     I_create_cat_rast(pointer(cell_head), cat_rast)   
 
-def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, output_queue):
+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
 
     #TODO names for types not 0 and 1?
-    sccats_c, vals_dt = _getComputationStruct(scatts, 0, n_bands)
-    scatt_conds_c, vals_dt2 = _getComputationStruct(scatt_conds, 1, n_bands)
+    sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
+    scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
 
     char_bands = _stringListToCharArr(bands)
-    char_cats_rasts_out = _stringListToCharArr(cats_rasts_out)
-    char_cats_rasts_in = _stringListToCharArr(cats_rasts_in)
-
+   
     cell_head = _regionToCellHead(region)
 
     ret = I_compute_scatts(pointer(cell_head),
                            pointer(scatt_conds_c),
+                           pointer(cats_rasts_conds_c),
                            pointer(char_bands),
                            n_bands,
                            pointer(sccats_c),
-                           pointer(char_cats_rasts_in),
-                           pointer(char_cats_rasts_out))
+                           pointer(cats_rasts_c))
 
     I_sc_free_cats(pointer(sccats_c))
     I_sc_free_cats(pointer(scatt_conds_c))
 
-    #output_queue.put((ret, scatts))
-    return (ret, scatts)
+    output_queue.put((ret, scatts))
 
 def _getBandcRange( band_info):
     band_c_range = struct_Range()
@@ -96,7 +142,6 @@
 
     return band_c_range
 
-
 def _regionToCellHead(region):
     cell_head = struct_Cell_head()
     G_get_window(pointer(cell_head))
@@ -131,27 +176,21 @@
 
     return char_arr
 
-def _getComputationStruct(cats, cats_type, n_bands):
+def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
 
     sccats = struct_scCats()
     I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
-        
-    #for i in range(sccats.n_a_cats):
-    #    cat_id = sccats.cats_ids[i]
-    #    I_sc_delete_cat(pointer(sccats), cat_id)
-    #    i -= 1
 
-    vals_dt = []
+    refs = []        
+    cats_rasts_core = []
+    
     for cat_id, scatt_ids in cats.iteritems():
-        I_sc_add_cat(pointer(sccats), cat_id)
+        cat_c_id = I_sc_add_cat(pointer(sccats))
+        cats_rasts_core.append(cats_rasts[cat_id])
 
         for scatt_id, dt in scatt_ids.iteritems():
             # if key is missing condition is always True (full scatter plor is computed)
-            if cats[cat_id].has_key(scatt_id):
-
                 vals = dt['np_vals']
-                b1_info = dt['bands_info']['b1']
-                b2_info = dt['bands_info']['b2']
 
                 scatt_vals = scdScattData()
 
@@ -163,65 +202,27 @@
                     pass
                 else:
                     return None
-
-                b1_c_info = _getBandcRange(b1_info)
-                b2_c_info =_getBandcRange(b2_info)
                 data_p = vals.ctypes.data_as(c_void_p)
-                I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p, 
-                                      b1_c_info, b2_c_info)
+                I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
 
-                vals_dt.append(scatt_vals)
+                refs.append(scatt_vals)
 
                 I_sc_insert_scatt_data(pointer(sccats),  
                                        pointer(scatt_vals),
-                                       cat_id, scatt_id)
+                                       cat_c_id, scatt_id)
 
-    return sccats, vals_dt
+    cats_rasts_c = _stringListToCharArr(cats_rasts_core)
 
+    return sccats, cats_rasts_c, refs
+
 def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
-
-    #TODO names for types not 0 and 1?
-
     cell_head = _regionToCellHead(region)
     
     
-    cat_rast
-    I_insert_patch_to_cat_rast(patch_rast, 
+    ret = I_insert_patch_to_cat_rast(patch_rast, 
                                      pointer(cell_head), 
                                      cat_rast)
 
-    #print re
-    #output_queue.put((ret, scatts))
-    #return (ret, scatts)
+    output_queue.put(ret)
 
-"""
-class A:
-    def __init__(self):        
-        self.i = 0        
 
-    def incr(self):
-        self.i += 1
-        return self.i
-
-def worker(input, output):
-    print "ahoj"
-    for func, args in iter(input.get, 'STOP'):
-        print "jedu"
-        result = func.incr
-        output.put(result)
-
-def test():
-    # Create queues
-    task_queue = Queue()
-    done_queue = Queue()
-
-    # Start worker processes
-    a = A()
-
-    prc = Process(target=worker, args=(task_queue, done_queue)).start()
-    task_queue.put((a,1))
-
-    data = done_queue.get()
-    print data
-    task_queue.put('STOP')
-"""

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,9 +1,14 @@
 """!
 @package scatt_plot.dialogs
 
- at brief GUI.
+ at brief Dialogs widgets.
 
 Classes:
+ - dialogs::AddScattPlotDialog
+ - dialogs::ExportCategoryRaster
+ - dialogs::SettingsDialog
+ - dialogs::ManageBusyCursorMixin
+ - dialogs::RenameClassDialog
 
 (C) 2013 by the GRASS Development Team
 
@@ -16,219 +21,58 @@
 import sys
 
 import wx
-import wx.lib.mixins.listctrl as listmix
-import wx.lib.flatnotebook  as FN
-import wx.aui
-
-from core          import globalvar
-from core.gcmd     import GException, GError, RunCommand
-
+from scatt_plot.scatt_core import idBandsToidScatt
 from gui_core.gselect import Select
-from gui_core.widgets import GNotebook
+import wx.lib.colourselect as csel
 
-from scatt_plot.controllers import ScattsManager
-from scatt_plot.toolbars    import MainToolbar, CategoriesToolbar
-from scatt_plot.sc_pl_core  import Core, idBandsToidScatt
-from scatt_plot.plots       import ScatterPlotWidget
+import grass.script as grass
 
+from core import globalvar
+from core.gcmd import GMessage
+from core.settings import UserSettings
+from gui_core.dialogs import SimpleDialog
 
-class ScattPlotMainDialog(wx.Dialog):
-    def __init__(self, parent, giface, iclass_mapwin = None,
-                 id = wx.ID_ANY, title = _("GRASS GIS Interactive Scatter Plot Tool"),
-                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-    
-        wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+class AddScattPlotDialog(wx.Dialog):
 
-        #TODO remove iclass parameter
-        self.scatt_mgr = ScattsManager(guiparent = self, giface = giface, iclass_mapwin = iclass_mapwin)
+    def __init__(self, parent, bands, added_scatts_ids, id  = wx.ID_ANY):
+        wx.Dialog.__init__(self, parent, title = ("Add scatter plots"), id = id)
 
-        # toobars
-        self.toolbars = {}
-        self.toolbars['mainToolbar'] = MainToolbar(parent = self)
-        
-        self.mainPanel = wx.Panel(parent=self)        
-        self.notebook = GNotebook(parent = self.mainPanel,
-                                  style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
-                                          FN.FNB_NO_X_BUTTON)
+        self.added_scatts_ids = added_scatts_ids
+        self.bands = bands
 
-        # Fancy gui
-        self._mgr = wx.aui.AuiManager(self)
-        self._addPanes()
-        self._mgr.Update()
+        self.x_band = None
+        self.y_band = None
 
-        self._createCategoryPage()
-       
-        self.edit_cat_tools = {
-                                'editCatAdd' : {
-                                                'toggle' : False,
-                                                'type' : 'add'
-                                                },
-                                'editCatRemove' : {
-                                                   'toggle' : False,
-                                                   'type' : 'remove'
-                                                  }
-                              }
-        self._doLayout()
-        self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
-
-        dlgSize = (220, 400)
-        self.SetMinSize(dlgSize)
-        self.SetInitialSize(dlgSize)
-
-        #fix goutput's pane size (required for Mac OSX)
-        #if self.gwindow:         
-        #    self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
-
-    def _doLayout(self):
-
-        sizer = wx.BoxSizer(wx.VERTICAL)
-
-        sizer.Add(item = self.notebook, proportion = 1,
-                  flag = wx.EXPAND)
-
-        self.mainPanel.SetSizer(sizer)
-
-        sizer.Fit(self)  
-        self.Layout()
-
-    #TODO split to panel?
-    def _createCategoryPage(self):
-        catsPanel = wx.Panel(parent = self)
-
-
-        self.notebook.AddPage(page = catsPanel, 
-                              text=_('Categories'), 
-                              name = 'categories')
-
-        self.cats_list = CategoryListCtrl(parent = catsPanel, 
-                                          cats_mgr = self.scatt_mgr.GetCategoriesManager())
-        self.toolbars['catsList'] = CategoriesToolbar(parent = catsPanel, 
-                                                      cats_list = self.cats_list,
-                                                      scatts_dlg = self)
-
-        AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
-
-        catsSizer = wx.BoxSizer(wx.VERTICAL)
-
-        catsSizer.Add(item = self.toolbars['catsList'], proportion = 0)
-        catsSizer.Add(item = self.cats_list, proportion = 1, flag = wx.EXPAND)
-
-        catsPanel.SetSizer(catsSizer)
-
-    def _addPanes(self):
-        """!Adds toolbar pane and pane with tabs"""
-        self._mgr.AddPane(self.toolbars['mainToolbar'],
-                              wx.aui.AuiPaneInfo().
-                              Name("pointlisttools").Caption(_("Point cats_list toolbar")).
-                              ToolbarPane().Top().Row(0).
-                              Dockable(False).
-                              CloseButton(False).Layer(0))
-
-        self._mgr.AddPane(self.mainPanel,
-                              wx.aui.AuiPaneInfo().
-                              Name("tabs").CaptionVisible(visible = False).
-                              Center().
-                              Dockable(False).
-                              CloseButton(False).Layer(0))
-
-    def OnCloseDialog(self, event):
-        """!Close dialog"""
-        self.scatt_mgr.CleanUp()
-        self.Destroy()
-
-    def OnSettings(self, event):
-        pass
-
-    def OnSetData(self, event):
-
-        dlg = SetDataDialog(parent=self)        
-
-        if dlg.ShowModal() == wx.ID_OK:
-            bands = dlg.GetBands()
-            self.scatt_mgr.SetData(bands)
-        
-        dlg.Destroy()
-
-
-    def NewScatterPlot(self, scatt_id):
-        #TODO needs to be resolved (should be in this class)
-        scatt_dlg = ScatterPlotDialog(parent = self, scatt_mgr = self.scatt_mgr, scatt_id = scatt_id)
-        return scatt_dlg.GetPlot()
-
-    def OnSettings(self, event):
-        pass
-
-    def OnAddScattPl(self, event):
-
-        bands = self.scatt_mgr.GetBands()
-        if not bands:
-            GError(_('No data set for scatter plot.'))
-            return
-
-        dlg = AddScattPlotDialog(parent = self, bands = bands)
-        
-        if dlg.ShowModal() == wx.ID_OK:
-            self.scatt_mgr.AddScattPlot(dlg.GetScattId())
-        
-        dlg.Destroy()
-
-    def EditCatAdd(self):
-        self._setEditCat(tool_name = 'editCatAdd')
-
-    def EditCatRemove(self):
-        self._setEditCat(tool_name = 'editCatRemove')
-
-    def _setEditCat(self, tool_name):
-        
-        if self.edit_cat_tools[tool_name]['toggle'] == False:
-            
-            for i_tool_name in self.edit_cat_tools.iterkeys():
-                if i_tool_name == tool_name:
-                    continue
-
-                self.edit_cat_tools[i_tool_name]['toggle'] = False
-
-                toolbar = self.toolbars['catsList']
-                toolbar.ToggleTool(vars(toolbar)[i_tool_name], False)
-            
-            self.edit_cat_tools[tool_name]['toggle'] = True
-            self.scatt_mgr.EditCat(edit_type = self.edit_cat_tools[tool_name]['type'])
-            return
-
-        self.scatt_mgr.EditCat(edit_type = None)
-        self.edit_cat_tools[tool_name]['toggle'] = False
-
-    def GetScattMgr(self):
-        return  self.scatt_mgr
-
-
-class SetDataDialog(wx.Dialog):
-
-    def __init__(self, parent, id  = wx.ID_ANY):
-        
-        wx.Dialog.__init__(self, parent, title = ("Select imagery group "), id = id)
-
-        self.bands = []
-
-
+        self.added_bands_ids = []
+        self.sel_bands_ids = []
         self._createWidgets()
-        self.group.SetValue("B_sk")
 
     def _createWidgets(self):
 
         self.labels = {}
         self.params = {}
 
-        self.group_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name of raster group for input data:"))
+        self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("x axis:"))
 
-        self.group = Select(parent = self, type = 'group',
-                            size = globalvar.DIALOG_GSELECT_SIZE)
+        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 = _("y axis:"))
+
+        self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                     choices = self.bands,
+                                     style = wx.CB_READONLY, size = (350, 30))
+
+        self.scattsBox = wx.ListBox(parent = self,  id = wx.ID_ANY, size = (-1, 150),
+                                    style = wx.LB_MULTIPLE | wx.LB_NEEDED_SB)
+
         # buttons
-        self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
+        self.btn_add = wx.Button(parent=self, id=wx.ID_ANY, label = _("Add"))
+        self.btn_remove = wx.Button(parent=self, id=wx.ID_ANY, label = _("Remove"))
         
-        self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
+        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()
 
@@ -239,25 +83,40 @@
 
         regionSizer = wx.BoxSizer(wx.HORIZONTAL)
 
-        dialogSizer.Add(item = self._addSelectSizer(title = self.group_label, 
-                                                    sel = self.group))
+        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))
+
+
+        dialogSizer.Add(item=self.btn_add, proportion=0,  flag = wx.TOP, border = 5)
+
+        box = wx.StaticBox(self, id = wx.ID_ANY,
+                           label = " %s " % _("Bands of scatter plots to be added (x y):"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        sizer.Add(item=self.scattsBox, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
+        sizer.Add(item=self.btn_remove, proportion=0, flag=wx.TOP, border = 5)
+
+        dialogSizer.Add(item=sizer, proportion=1,  flag = wx.EXPAND | wx.TOP, border = 5)
+
         # buttons
         self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
 
         self.btnsizer.Add(item = self.btn_close, proportion = 0,
-                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          flag = wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
                           border = 10)
         
         self.btnsizer.Add(item = self.btn_ok, proportion = 0,
-                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          flag = wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
                           border = 10)
 
         dialogSizer.Add(item = self.btnsizer, proportion = 0,
-                        flag = wx.ALIGN_CENTER)
+                        flag = wx.ALIGN_CENTER | wx.TOP, border = 5)
 
         border.Add(item = dialogSizer, proportion = 0,
-                   flag = wx.ALL, border = 5)
+                   flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 10)
 
         self.SetSizer(border)
         self.Layout()
@@ -266,7 +125,17 @@
         # bindings
         self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
         self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+        self.btn_add.Bind(wx.EVT_BUTTON, self.OnAdd)
+        self.btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
 
+    def OnOk(self, event):
+        
+        if not self.GetBands():
+            GMessage(parent=self, message=_("No scatter plots selected."))
+            return
+
+        event.Skip()
+
     def _addSelectSizer(self, title, sel): 
         """!Helper layout function.
         """
@@ -274,21 +143,21 @@
 
         selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
         selTitleSizer.Add(item = title, proportion = 1,
-                          flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+                          flag = 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,
+                     flag = wx.EXPAND | wx.TOP| wx.ALIGN_CENTER_VERTICAL,
                      border = 5)
 
         return selSizer
 
     def GetBands(self):
+        """!Get layers"""
+        return self.sel_bands_ids
 
-        return self.bands
-
     def OnClose(self, event):
         """!Close dialog
         """
@@ -296,317 +165,353 @@
             self.Destroy()
         event.Skip()
 
-    def OnOk(self, event):
+    def OnRemoveLayer(self, event):
+        """!Remove layer from listbox"""
+        while self.scattsBox.GetSelections():
+            sel = self.scattsBox.GetSelections()[0]
+            self.scattsBox.Delete(sel)
+            self.sel_bands_ids.pop(sel)
+
+    def OnAdd(self, event):
         """!
         """
-        ret, stdout, err_msg = RunCommand("i.group",
-                                      getErrorMsg = True,
-                                      read = True,
-                                      quiet = True,
-                                      group = self.group.GetValue().strip(),
-                                      flags = 'g')
+        b_x = self.band_1_ch.GetSelection()
+        b_y = self.band_2_ch.GetSelection()
 
-        if ret != 0:
-            GError("%s module failed:\n%s" % ("<i.group>", err_msg))
+        if b_x < 0 or b_y < 0:
+            GMessage(parent=self, message=_("Select both x and y bands."))
             return
+        if b_y == b_x:
+            GMessage(parent=self, message=_("Selected bands must be different."))
+            return
 
-        self.bands = stdout.split('\n')
+        scatt_id = idBandsToidScatt(b_x, b_y, len(self.bands))
+        if scatt_id in self.added_scatts_ids:
+            GMessage(parent=self, 
+                     message=_("Scatter plot with same band combination (regardless x y order) " 
+                               "is already displayed."))
+            return
 
-        for band in self.bands[:]:
-            if not band:
-                self.bands.remove(band)
-    
-        event.Skip()
+        if [b_x, b_y] in self.sel_bands_ids or [b_y, b_x] in self.sel_bands_ids:
+            GMessage(parent=self, 
+                     message=_("Scatter plot with same bands combination (regardless x y order) " 
+                               "has been already added into the list."))
+            return
 
-    #TODO catch event or...
-    def GetSelectedCatId(self):
-        
-        return self.cats_list.GetSelectedCatId()
+        self.sel_bands_ids.append([b_x, b_y])
 
-class AddScattPlotDialog(wx.Dialog):
+        b_x_str = self.band_1_ch.GetStringSelection()
+        b_y_str = self.band_2_ch.GetStringSelection()
 
-    def __init__(self, parent, bands, id  = wx.ID_ANY):
-        
-        wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
+        text = b_x_str + " " + b_y_str
 
-        self.bands = bands
+        self.scattsBox.Append(text)
+        event.Skip()
 
-        self.scatt_id = None
+class ExportCategoryRaster(wx.Dialog):
+    def __init__(self, parent, title, rasterName = None, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 **kwargs):
+        """!Dialog for export of category raster.
+        
+        @param parent window
+        @param rasterName name of vector layer for export
+        @param title window title
+        """
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        
+        self.rasterName = rasterName
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        self.btnOK     = wx.Button(parent = self.panel, id = wx.ID_OK)
+        self.btnOK.SetDefault()
+        self.btnOK.Enable(False)
+        self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
+        
+        self.__layout()
+        
+        self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
+        self.OnTextChanged(None)
+        wx.CallAfter(self.vectorNameCtrl.SetFocus)
 
-        self._createWidgets()
+    def OnTextChanged(self, event):
+        """!Name of new vector map given.
+        
+        Enable/diable OK button.
+        """
+        file = self.vectorNameCtrl.GetValue()
+        if len(file) > 0:
+            self.btnOK.Enable(True)
+        else:
+            self.btnOK.Enable(False)
+        
+    def __layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        dataSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                           label = _("Enter name of new vector map:")),
+                      proportion = 0, flag = wx.ALL, border = 3)
+        self.vectorNameCtrl = Select(parent = self.panel, type = 'raster',
+                                     mapsets = [grass.gisenv()['MAPSET']],
+                                     size = globalvar.DIALOG_GSELECT_SIZE)
+        if self.rasterName:
+            self.vectorNameCtrl.SetValue(self.rasterName)
+        dataSizer.Add(item = self.vectorNameCtrl,
+                      proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+                      
+        # buttons
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOK)
+        btnSizer.Realize()
+        
+        sizer.Add(item = dataSizer, proportion = 1,
+                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        sizer.Add(item = btnSizer, proportion = 0,
+                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self)
+        
+        self.SetMinSize(self.GetSize())
+        
+    def GetRasterName(self):
+        """!Returns vector name"""
+        return self.vectorNameCtrl.GetValue()
 
-    def _createWidgets(self):
+    def OnOK(self, event):
+        """!Checks if map exists and can be overwritten."""
+        overwrite = UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled')
+        rast_name = self.GetRasterName()
+        res = grass.find_file(rast_name, element = 'cell')
+        if res['fullname'] and overwrite is False:
+            qdlg = wx.MessageDialog(parent = self,
+                                        message = _("Raster map <%s> already exists."
+                                                    " Do you want to overwrite it?" % rast_name) ,
+                                        caption = _("Raster <%s> exists" % rast_name),
+                                        style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+            if qdlg.ShowModal() == wx.ID_YES:
+                event.Skip()
+            qdlg.Destroy()
+        else:
+            event.Skip()
 
-        self.labels = {}
-        self.params = {}
+class SettingsDialog(wx.Dialog):
+    def __init__(self, parent, id, title, scatt_mgr, pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.DEFAULT_DIALOG_STYLE):
+        """!Settings dialog"""
+        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
 
-        self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 1:"))
+        self.scatt_mgr = scatt_mgr
 
-        self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
-                                     choices = self.bands,
-                                     style = wx.CB_READONLY, size = (350, 30))
+        maxValue = 1e8
+        self.parent = parent
+        self.settings = {}
 
-        self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 2:"))
+        settsLabels = {} 
 
-        self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
-                                     choices = self.bands,
-                                     style = wx.CB_READONLY, size = (350, 30))
+        self.settings["show_ellips"] = wx.CheckBox(parent = self, id=wx.ID_ANY,
+                                                   label = _('Show confidence ellipses'))
+        show_ellips = UserSettings.Get(group ='scatt', key = "ellipses", subkey = "show_ellips")
+        self.settings["show_ellips"].SetValue(show_ellips)
 
-        # 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()
+        self.colorsSetts = {
+                            "sel_pol" : ["selection", _("Selection polygon color:")],
+                            "sel_pol_vertex" : ["selection", _("Color of selection polygon vertex:")], 
+                            "sel_area" : ["selection", _("Selected area color:")]
+                           }
 
-    def _layout(self):
+        for settKey, sett in self.colorsSetts.iteritems():
+            settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+            col = UserSettings.Get(group ='scatt', key = sett[0], subkey = settKey)
+            self.settings[settKey] = csel.ColourSelect(parent = self, id = wx.ID_ANY,
+                                            colour = wx.Colour(col[0],
+                                                               col[1],
+                                                               col[2], 
+                                                               255))
 
-        border = wx.BoxSizer(wx.VERTICAL) 
-        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+        self.sizeSetts = {
+                          "snap_tresh" : ["selection", _("Snapping treshold in pixels:")],
+                          "sel_area_opacty" : ["selection", _("Selected area opacity:")]
+                         }
 
-        regionSizer = wx.BoxSizer(wx.HORIZONTAL)
+        for settKey, sett in self.sizeSetts.iteritems():
+            settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+            self.settings[settKey] = wx.SpinCtrl(parent = self, id = wx.ID_ANY, min=0, max = 100)
+            size = int(UserSettings.Get(group = 'scatt', key = sett[0], subkey = settKey))
+            self.settings[settKey].SetValue(size)
 
-        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.btnSave = wx.Button(self, wx.ID_SAVE)
+        self.btnApply = wx.Button(self, wx.ID_APPLY)
+        self.btnClose = wx.Button(self, wx.ID_CLOSE)
+        self.btnApply.SetDefault()
 
-        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)
+        # bindings
+        self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        self.btnApply.SetToolTipString(_("Apply changes for the current session"))
+        self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        self.btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.btnClose.SetToolTipString(_("Close dialog"))
 
-        dialogSizer.Add(item = self.btnsizer, proportion = 0,
-                        flag = wx.ALIGN_CENTER)
+        #Layout
 
-        border.Add(item = dialogSizer, proportion = 0,
-                   flag = wx.ALL, border = 5)
+        # Analysis result style layout
+        self.SetMinSize(self.GetBestSize())
 
-        self.SetSizer(border)
-        self.Layout()
-        self.Fit()
+        sizer = wx.BoxSizer(wx.VERTICAL)
 
-        # bindings
-        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
-        self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+        sel_pol_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                   label =" %s " % _("Selection style:"))
+        selPolBoxSizer = wx.StaticBoxSizer(sel_pol_box, wx.VERTICAL)
 
-    def _addSelectSizer(self, title, sel): 
-        """!Helper layout function.
-        """
-        selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
 
-        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
-        selTitleSizer.Add(item = title, proportion = 1,
-                          flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+        row = 0
+        setts = dict(self.colorsSetts.items() + self.sizeSetts.items())
 
-        selSizer.Add(item = selTitleSizer, proportion = 0,
-                     flag = wx.EXPAND)
+        settsOrder = ["sel_pol", "sel_pol_vertex", "sel_area",
+                      "sel_area_opacty", "snap_tresh"]
+        for settKey in settsOrder:
+            sett = setts[settKey]
+            gridSizer.Add(item = settsLabels[settKey], flag = wx.ALIGN_CENTER_VERTICAL, pos =(row, 0))
+            gridSizer.Add(item = self.settings[settKey],
+                          flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+                          pos =(row, 1))  
+            row += 1
 
-        selSizer.Add(item = sel, proportion = 1,
-                     flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
-                     border = 5)
+        gridSizer.AddGrowableCol(1)
+        selPolBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
 
-        return selSizer
+        ell_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                               label =" %s " % _("Ellipses settings:"))
+        ellPolBoxSizer = wx.StaticBoxSizer(ell_box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
 
-    def OnClose(self, event):
-        """!Close dialog
-        """
-        if not self.IsModal():
-            self.Destroy()
-        event.Skip()
+        sett = setts[settKey]
 
-    def OnOk(self, event):
-        """!
-        """
-        band_1 = self.band_1_ch.GetSelection()
-        band_2 = self.band_2_ch.GetSelection()
+        row = 0
+        gridSizer.Add(item=self.settings["show_ellips"], 
+                      flag=wx.ALIGN_CENTER_VERTICAL, 
+                      pos=(row, 0))
 
+        gridSizer.AddGrowableCol(1)
+        ellPolBoxSizer.Add(item=gridSizer, flag=wx.EXPAND)
 
-        if band_1 == band_2:
-            GError(_("Selected bands must be different."))
-            return
-        
-        #TODO axes selection
-        if band_1 > band_2:
-            tmp_band = band_2
-            band_2 = band_1
-            band_1 = band_2
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(self.btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(self.btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(self.btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
 
-        self.scatt_id = idBandsToidScatt(band_1, band_2, len(self.bands))
+        sizer.Add(item=selPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(item=ellPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(item=btnSizer, flag=wx.EXPAND | wx.ALL, border=5, proportion=0)    
 
-        event.Skip()
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+     
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        self.UpdateSettings()
 
-    def GetScattId(self):
-        return self.scatt_id
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings=fileSettings)
+        fileSettings['scatt'] = UserSettings.Get(group='scatt')
+        UserSettings.SaveToFile(fileSettings)
 
-class ScatterPlotDialog(wx.Dialog):
-    def __init__(self, parent, scatt_mgr, scatt_id,
-                 id = wx.ID_ANY, title = _("Test plot"),
-                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-    
-        wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
-        self.scatt = ScatterPlotWidget(parent = self, 
-                                       scatt_mgr = scatt_mgr, 
-                                       scatt_id = scatt_id)
+        self.Close()
 
-    def GetPlot(self):
-        return self.scatt
+    def UpdateSettings(self):
 
-    def _doLayout(self):
-        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
-        self.main_sizer.Add(self.self)
+        chanaged_setts = [];
+        for settKey, sett in self.colorsSetts.iteritems():
+            col = tuple(self.settings[settKey].GetColour())
+            col_s = UserSettings.Get(group='scatt', key=sett[0], subkey=settKey)
+            if col_s != col:
+                UserSettings.Set(group='scatt', 
+                                 key=sett[0], 
+                                 subkey=settKey,
+                                 value=col)
+                chanaged_setts.append([settKey, sett[0]])
 
-        self.panel.SetSizer(self.main_sizer)
-        self.main_sizer.Fit(self)
-    
-    def OnPlotClosed(self, scatt_id):
-        self.Destroy()
+        for settKey, sett in self.sizeSetts.iteritems():
+            val = self.settings[settKey].GetValue()
+            val_s = UserSettings.Get(group ='scatt', key = sett[0], subkey = settKey)
 
-class CategoryListCtrl(wx.ListCtrl,
-                       listmix.ListCtrlAutoWidthMixin,
-                       listmix.TextEditMixin):
+            if val_s != val:
+                UserSettings.Set(group = 'scatt', key = sett[0], subkey = settKey, 
+                                 value = val)
+                chanaged_setts.append([settKey, sett[0]])
 
-    def __init__(self, parent, cats_mgr, id = wx.ID_ANY):
+        val = self.settings['show_ellips'].IsChecked()
+        val_s = UserSettings.Get(group ='scatt', key='ellipses', subkey='show_ellips')
 
-        wx.ListCtrl.__init__(self, parent, id,
-                             style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
-        self.columns = ((_('Category name'), 'name'),
-                        (_('Color'), 'color'))
-        self.Populate(columns = self.columns)
-        
-        self.cats_mgr = cats_mgr
-        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+        if val != val_s:
+            UserSettings.Set(group='scatt', key='ellipses', subkey='show_ellips', 
+                             value=val)
+            chanaged_setts.append(['ellipses', 'show_ellips'])
 
-        self.rightClickedItemIdx = wx.NOT_FOUND
-        
-        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        if chanaged_setts: 
+            self.scatt_mgr.SettingsUpdated(chanaged_setts)
 
-        listmix.TextEditMixin.__init__(self)
-        
-        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
-        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
-             
-        self.cats_mgr.setCategoryAttrs.connect(self.Update)
-        self.cats_mgr.deletedCategory.connect(self.Update)
-        self.cats_mgr.addedCategory.connect(self.Update)
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        self.UpdateSettings()
+        #self.Close()
 
-    def Update(self, **kwargs):
-        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+    def OnClose(self, event):
+        """!Button 'Cancel' pressed"""
+        self.Close()
 
-    def InitCoreCats(self):
-        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+class ManageBusyCursorMixin:
+    def __init__(self, window):
+        self.win = window
 
-    def SetVirtualData(self, row, column, text):
-        attr = self.columns[column][1]
-        if attr == 'name':
-            try:
-                text.encode('ascii')
-            except UnicodeEncodeError:
-                GMessage(parent = self, message = _("Please use only ASCII characters."))
-                return
+        self.is_busy = False
+        self.cur_inside = False
 
-        cat_id = self.cats_mgr.GetCategories()[row]
+        self.win.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
+        self.win.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
 
-        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
-        self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
-        self.cats_mgr.setCategoryAttrs.connect(self.Update)
-        
-        self.Select(row)
-        
-    def Populate(self, columns):
-        for i, col in enumerate(columns):
-            self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+    def OnLeave(self, event):   
+        self.cur_inside = False     
+        self.busy_cur = None
 
-        self.SetColumnWidth(0, 100)
-        self.SetColumnWidth(1, 100)
-        
-    def AddCategory(self):
+    def OnEnter(self, event):
+        self.cur_inside = True
+        if self.is_busy:
+            self.busy_cur = wx.BusyCursor()
 
-        self.cats_mgr.addedCategory.disconnect(self.Update)
-        cat_id = self.cats_mgr.AddCategory()
-        self.cats_mgr.addedCategory.connect(self.Update)
-
-        if cat_id < 0:
-            GError(_("Maximum limit of categories number was reached."))
+    def UpdateCur(self, busy):
+        self.is_busy = busy
+        if self.cur_inside and self.is_busy:
+            self.busy_cur = wx.BusyCursor()
             return
-        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-                        
-    def DeleteCategory(self):
-        indexList = sorted(self.GetSelectedIndices(), reverse = True)
-        cats = []
-        for i in indexList:
-            # remove temporary raster
-            cat_id = self.cats_mgr.GetCategories()[i]
-            
-            cats.append(cat_id)
 
-            self.cats_mgr.deletedCategory.disconnect(self.Update)
-            self.cats_mgr.DeleteCategory(cat_id)
-            self.cats_mgr.deletedCategory.connect(self.Update)
-            
-        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-        
-    def OnSel(self, event): 
-        self.cats_mgr.SetSelectedCat(event.GetIndex() + 1)
+        self.busy_cur = None
 
-        event.Skip()
+class RenameClassDialog(SimpleDialog):
+    def __init__(self, parent, old_name, title=("Change class name")):
+        SimpleDialog.__init__(self, parent, title)
 
-    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
-        indices = []
-        lastFound = -1
-        while True:
-            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
-            if index == -1:
-                break
-            else:
-                lastFound = index
-                indices.append(index)
-        return indices        
+        self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY) 
+        self.name.SetValue(old_name)
 
-    def OnEdit(self, event):
-        currentItem = event.m_itemIndex
-        currentCol = event.m_col
+        self.dataSizer.Add(self.name, proportion = 0,
+                           flag = wx.EXPAND | wx.ALL, border = 1)
 
-        if currentCol == 1:
-            dlg = wx.ColourDialog(self)
-            dlg.GetColourData().SetChooseFull(True)
+        self.panel.SetSizer(self.sizer)
+        self.name.SetMinSize((200, -1))
+        self.sizer.Fit(self)
 
-            if dlg.ShowModal() == wx.ID_OK:
-                color = dlg.GetColourData().GetColour().Get()
-                color = ':'.join(map(str, color))
-                self.SetVirtualData(currentItem, currentCol, color)
-            dlg.Destroy()
-            wx.CallAfter(self.SetFocus)
-        
-        event.Skip()
-        
-        
-    def DeselectAll(self):
-        """!Deselect all items"""
-        indexList = self.GetSelectedIndices()
-        for i in indexList:
-            self.Select(i, on = 0)
-         
-        # no highlight
-        self.OnCategorySelected(None)
-        
-    def OnGetItemText(self, item, col):
-        attr = self.columns[col][1]
-        cat_id = self.cats_mgr.GetCategories()[item]
-
-        return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
-
-    def OnGetItemImage(self, item):
-        return -1
-
-    def OnGetItemAttr(self, item):
-        return None
+    def GetNewName(self):
+        return self.name.GetValue()

Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,694 @@
+"""!
+ at package scatt_plot.dialogs
+
+ at brief Main scatter plot widgets.
+
+Classes:
+ - frame::IClassIScattPanel
+ - frame::IScattDialog
+ - frame::MapDispIScattPanel
+ - frame::ScatterPlotsPanel
+ - frame::CategoryListCtrl
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+import wx.lib.mixins.listctrl as listmix
+
+from core import globalvar
+from core.gcmd import GException, GError, RunCommand
+
+from gui_core.gselect import Select
+from gui_core.dialogs import SetOpacityDialog
+from scatt_plot.controllers import ScattsManager
+from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
+from scatt_plot.scatt_core import idScattToidBands
+from scatt_plot.dialogs import ManageBusyCursorMixin, RenameClassDialog
+from scatt_plot.plots import ScatterPlotWidget
+from iclass.dialogs import ContrastColor
+
+try:
+    from agw import aui
+except ImportError:
+    import wx.lib.agw.aui as aui
+
+class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
+    def __init__(self, parent, giface, iclass_mapwin = None,
+                 id = wx.ID_ANY):
+
+        #wx.SplitterWindow.__init__(self, parent = parent, id = id,
+        #                           style = wx.SP_LIVE_UPDATE)
+        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+        ManageBusyCursorMixin.__init__(self, window=self)
+
+        self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface, 
+                                              iclass_mapwin=iclass_mapwin)
+
+        # toobars
+        self.toolbars = {}
+        self.toolbars['mainToolbar'] = self._createMainToolbar()
+        self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
+        
+        self._createCategoryPanel(self)
+
+        self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
+
+        self.mainsizer = wx.BoxSizer(wx.VERTICAL)
+        self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
+        self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
+        self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
+        self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
+
+        self.catsPanel.Hide()
+        self.toolbars['editingToolbar'].Hide()
+
+        self.SetSizer(self.mainsizer)
+        
+        self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
+        self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
+        self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
+
+        #self.SetSashGravity(0.5)
+        #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
+        self.Layout()
+
+    def UpdateCur(self, busy):
+        self.plot_panel.SetBusy(busy)
+        ManageBusyCursorMixin.UpdateCur(self, busy)
+
+    def _selCatInIScatt(self):
+         return False
+
+    def _createMainToolbar(self):
+         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
+
+    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
+        return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
+
+
+    def NewScatterPlot(self, scatt_id, transpose):
+        return self.plot_panel.NewScatterPlot(scatt_id, transpose)
+
+    def ShowPlotEditingToolbar(self, show):
+        self.toolbars["editingToolbar"].Show(show)
+        self.Layout()
+
+    def ShowCategoryPanel(self, show):
+        self.catsPanel.Show(show)
+        
+        #if show:
+        #    self.SetSashSize(5) 
+        #else:
+        #    self.SetSashSize(0)
+        self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
+        self.Layout()
+
+    def _createCategoryPanel(self, parent):
+        self.catsPanel = wx.Panel(parent=parent)
+        self.cats_list = CategoryListCtrl(parent=self.catsPanel, 
+                                          cats_mgr=self.scatt_mgr.GetCategoriesManager(),
+                                          sel_cats_in_iscatt=self._selCatInIScatt())
+
+        self.catsPanel.SetMinSize((-1, 100))
+        self.catsPanel.SetInitialSize((-1, 150))
+
+        box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
+                             label=' %s ' % _("Classes"),)
+        catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
+
+        self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
+
+        catsSizer.Add(item=self.cats_list, proportion=1,  flag=wx.EXPAND | wx.TOP, border = 5)
+        if self.toolbars['categoryToolbar']:
+            catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
+
+        self.catsPanel.SetSizer(catsSizer)
+
+    def CleanUp(self):
+        pass
+
+    def _createCategoryToolbar(self, parent):
+        return CategoryToolbar(parent=parent,
+                               scatt_mgr=self.scatt_mgr, 
+                               cats_list=self.cats_list)
+
+class IScattDialog(wx.Dialog):
+    def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
+                 id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
+        wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+        self.iscatt_panel = MapDispIScattPanel(self, giface)
+
+        mainsizer = wx.BoxSizer(wx.VERTICAL)
+        mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
+
+        self.SetSizer(mainsizer)
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+        self.SetMinSize((300, 300))
+
+    def OnCloseWindow(self, event):
+        event.Skip()
+        #self.
+
+class MapDispIScattPanel(IClassIScattPanel):
+    def __init__(self, parent, giface,
+                 id=wx.ID_ANY, **kwargs):
+        IClassIScattPanel.__init__(self, parent=parent, giface=giface,
+                                         id=id, **kwargs)
+
+    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
+        return ScattsManager(guiparent = self, giface = giface)
+
+    def _createMainToolbar(self):
+         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
+
+    def _selCatInIScatt(self):
+         return True
+
+class ScatterPlotsPanel(scrolled.ScrolledPanel):
+    def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
+    
+        scrolled.ScrolledPanel.__init__(self, parent)
+        self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
+
+        self.scatt_mgr = scatt_mgr
+
+        self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        #self._createCategoryPanel()
+        # Fancy gui
+        self._mgr = aui.AuiManager(self.mainPanel)
+        #self._mgr.SetManagedWindow(self)
+
+        self._mgr.Update()
+
+        self._doLayout()
+        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
+        self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
+
+        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
+
+        dlgSize = (-1, 400)
+        #self.SetBestSize(dlgSize)
+        #self.SetInitialSize(dlgSize)
+        self.SetAutoLayout(1)
+        #fix goutput's pane size (required for Mac OSX)
+        #if self.gwindow:         
+        #    self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
+        self.ignore_scroll = 0
+        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
+
+        self.scatt_i = 1
+        self.scatt_id_scatt_i = {}
+        self.transpose = {}
+        self.scatts = {}
+
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        
+        self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
+
+    def SetBusy(self, busy):
+        for scatt in self.scatts.itervalues():
+            scatt.UpdateCur(busy)
+
+    def CursorPlotMove(self, x, y, scatt_id):
+
+        try:
+            x = int(round(x))
+            y = int(round(y))
+            coords = True
+        except:
+            coords = False
+
+        pane = self._getPane(scatt_id)
+        caption = self._creteCaption(scatt_id)
+        if coords:
+            caption += "   %d, %d" % (x, y)
+
+        pane.Caption(caption)
+        self._mgr.RefreshCaptions()
+
+    def _getPane(self, scatt_id):
+        scatt_i = self.scatt_id_scatt_i[scatt_id]
+        name = self._getScatterPlotName(scatt_i)
+        return self._mgr.GetPane(name)
+
+    def ScatterPlotClosed(self, scatt_id):
+
+        scatt_i = self.scatt_id_scatt_i[scatt_id]
+
+        name = self._getScatterPlotName(scatt_i)
+        pane = self._mgr.GetPane(name)
+
+        del self.scatt_id_scatt_i[scatt_id]
+        del self.scatts[scatt_id]
+
+        if pane.IsOk(): 
+          self._mgr.ClosePane(pane) 
+        self._mgr.Update() 
+
+    def OnMouseWheel(self, event):
+        #TODO very ugly find some better solution        
+        self.ignore_scroll = 3
+        event.Skip()
+
+    def ScrollChildIntoView(self, child):
+        #For aui manager it does not work and returns position always to the top -> deactivated.
+        pass
+
+    def OnPlotPaneClosed(self, event):
+        if isinstance(event.pane.window, ScatterPlotWidget):
+            event.pane.window.CleanUp()
+
+    def OnScrollChanged(self, event):
+        wx.CallAfter(self.Layout)
+
+    def OnScroll(self, event):
+        if self.ignore_scroll > 0:
+            self.ignore_scroll -= 1
+        else:
+            event.Skip()
+
+        #wx.CallAfter(self._mgr.Update)
+        #wx.CallAfter(self.Layout)
+
+    def _doLayout(self):
+
+        mainsizer = wx.BoxSizer(wx.VERTICAL)
+        mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
+        self.SetSizer(mainsizer)
+
+        self.Layout()
+        self.SetupScrolling()
+
+    def OnClose(self, event):
+        """!Close dialog"""
+        #TODO
+        print "closed"
+        self.scatt_mgr.CleanUp()
+        self.Destroy()
+
+    def OnSettings(self, event):
+        pass
+
+    def _newScatterPlotName(self, scatt_id):
+        name = self._getScatterPlotName(self.scatt_i) 
+        self.scatt_id_scatt_i[scatt_id] = self.scatt_i
+        self.scatt_i += 1
+        return name
+
+    def _getScatterPlotName(self, i):
+        name = "scatter plot %d" % i
+        return name
+
+    def NewScatterPlot(self, scatt_id, transpose):
+        #TODO needs to be resolved (should be in this class)
+
+        scatt = ScatterPlotWidget(parent = self.mainPanel, 
+                                  scatt_mgr = self.scatt_mgr, 
+                                  scatt_id = scatt_id, 
+                                  transpose = transpose)
+        scatt.plotClosed.connect(self.ScatterPlotClosed)
+        self.transpose[scatt_id] = transpose
+        
+        caption = self._creteCaption(scatt_id)
+        self._mgr.AddPane(scatt,
+                           aui.AuiPaneInfo().Dockable(True).Floatable(True).
+                           Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
+                           Caption(caption).
+                           Center().Position(1).MaximizeButton(True).
+                           MinimizeButton(True).CaptionVisible(True).
+                           CloseButton(True).Layer(0))
+
+        self._mgr.Update()
+  
+        self.SetVirtualSize(self.GetBestVirtualSize())
+        self.Layout()
+
+        self.scatts[scatt_id] = scatt
+
+        return scatt
+
+    def _creteCaption(self, scatt_id):
+
+        transpose = self.transpose[scatt_id]
+        bands = self.scatt_mgr.GetBands()
+
+        #TODO too low level
+        b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
+
+        x_b =  bands[b1_id].split('@')[0]
+        y_b = bands[b2_id].split('@')[0]
+
+        if transpose:
+            tmp = x_b
+            x_b = y_b
+            y_b = tmp
+
+        return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
+
+    def GetScattMgr(self):
+        return  self.scatt_mgr
+
+class CategoryListCtrl(wx.ListCtrl,
+                       listmix.ListCtrlAutoWidthMixin):
+                       #listmix.TextEditMixin):
+
+    def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
+
+        wx.ListCtrl.__init__(self, parent, id,
+                             style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
+                                     wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
+        self.columns = ((_('Class name'), 'name'), )
+                        #(_('Color'), 'color'))
+
+        self.sel_cats_in_iscatt = sel_cats_in_iscatt
+
+        self.Populate(columns = self.columns)
+        
+        self.cats_mgr = cats_mgr
+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+        self.rightClickedItemIdx = wx.NOT_FOUND
+        
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        #listmix.TextEditMixin.__init__(self)
+      
+        self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
+        self.Bind(wx.EVT_RIGHT_UP,            self.OnCategoryRightUp) #wxGTK
+
+        #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
+             
+        self.cats_mgr.setCategoryAttrs.connect(self.Update)
+        self.cats_mgr.deletedCategory.connect(self.Update)
+        self.cats_mgr.addedCategory.connect(self.Update)
+        self.cats_mgr.cleanuped.connect(self.Update)
+
+    def Update(self, **kwargs):
+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+    def InitCoreCats(self):
+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+    def SetVirtualData(self, row, column, text):
+        attr = self.columns[column][1]
+        if attr == 'name':
+            try:
+                text.encode('ascii')
+            except UnicodeEncodeError:
+                GMessage(parent = self, message = _("Please use only ASCII characters."))
+                return
+
+        cat_id = self.cats_mgr.GetCategories()[row]
+
+        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+        self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
+        self.cats_mgr.setCategoryAttrs.connect(self.Update)
+        
+        self.Select(row)
+        
+    def Populate(self, columns):
+        for i, col in enumerate(columns):
+            self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+
+        #self.SetColumnWidth(0, 100)
+        #self.SetColumnWidth(1, 100)
+        
+    def AddCategory(self):
+
+        self.cats_mgr.addedCategory.disconnect(self.Update)
+        cat_id = self.cats_mgr.AddCategory()
+        self.cats_mgr.addedCategory.connect(self.Update)
+
+        if cat_id < 0:
+            GError(_("Maximum limit of categories number was reached."))
+            return
+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+                        
+    def DeleteCategory(self):
+        indexList = sorted(self.GetSelectedIndices(), reverse = True)
+        cats = []
+        for i in indexList:
+            # remove temporary raster
+            cat_id = self.cats_mgr.GetCategories()[i]
+            
+            cats.append(cat_id)
+
+            self.cats_mgr.deletedCategory.disconnect(self.Update)
+            self.cats_mgr.DeleteCategory(cat_id)
+            self.cats_mgr.deletedCategory.connect(self.Update)
+            
+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
+        
+    def OnSel(self, event):
+        if self.sel_cats_in_iscatt:
+            indexList = self.GetSelectedIndices()
+            sel_cats = []
+            cats = self.cats_mgr.GetCategories()
+            for i in indexList:
+                sel_cats.append(cats[i])       
+
+            if sel_cats:
+                self.cats_mgr.SetSelectedCat(sel_cats[0])
+        event.Skip()
+
+    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
+        indices = []
+        lastFound = -1
+        while True:
+            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
+            if index == -1:
+                break
+            else:
+                lastFound = index
+                indices.append(index)
+        return indices        
+
+    def DeselectAll(self):
+        """!Deselect all items"""
+        indexList = self.GetSelectedIndices()
+        for i in indexList:
+            self.Select(i, on = 0)
+         
+        # no highlight
+        self.OnCategorySelected(None)
+        
+    def OnGetItemText(self, item, col):
+        attr = self.columns[col][1]
+        cat_id = self.cats_mgr.GetCategories()[item]
+
+        return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
+
+    def OnGetItemImage(self, item):
+        return -1
+
+    def OnGetItemAttr(self, item):
+        cat_id = self.cats_mgr.GetCategories()[item]
+
+        cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
+        
+        if cattr['show']:
+            c = cattr['color']
+            
+            back_c = wx.Colour(*map(int, c.split(':')))
+            text_c = wx.Colour(*ContrastColor(back_c))
+        else:
+            back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
+            text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
+
+        # if it is in scope of the method, gui falls, using self solved it 
+        self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
+        return self.l
+
+    def OnCategoryRightUp(self, event):
+        """!Show context menu on right click"""
+        item, flags = self.HitTest((event.GetX(), event.GetY()))
+        if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
+            self.rightClickedItemIdx = item
+
+        # generate popup-menu
+        cat_idx = self.rightClickedItemIdx
+
+        cats = self.cats_mgr.GetCategories()
+        cat_id = cats[cat_idx]
+        showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
+        
+        menu = wx.Menu()
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Rename class"))
+        self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Set color"))
+        self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Change opacity level"))
+        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
+
+        if showed:
+            text = _("Hide")
+        else:
+            text = _("Show")
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text = text)
+        self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
+                                                                attrs={'show' : not showed}), 
+                                                                id=item_id) 
+        
+        menu.AppendSeparator()
+        
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Move to top"))
+        self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
+        if cat_idx == 0:
+            menu.Enable(item_id, False)
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Move to bottom"))
+        self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
+        if cat_idx == len(cats) - 1:
+            menu.Enable(item_id, False)
+
+        menu.AppendSeparator()
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Move category up"))
+        self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
+        if cat_idx == 0:
+            menu.Enable(item_id, False)
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Move category down"))
+        self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
+        if cat_idx == len(cats) - 1:
+            menu.Enable(item_id, False)
+
+        menu.AppendSeparator()
+
+        item_id = wx.NewId()
+        menu.Append(item_id, text=_("Export class raster"))
+        self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnExportCatRast(self, event):
+        """!Export training areas"""
+        #TODO
+        #   GMessage(parent=self, message=_("No class raster to export."))
+        #    return
+
+        cat_idx = self.rightClickedItemIdx
+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
+
+        self.cats_mgr.ExportCatRast(cat_id)
+
+    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 OnMoveTop(self, event):
+        cat_idx = self.rightClickedItemIdx
+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
+        self.cats_mgr.ChangePosition(cat_id, 0)
+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+    def OnMoveBottom(self, event):
+        cat_idx = self.rightClickedItemIdx
+        cats = self.cats_mgr.GetCategories()
+        cat_id = cats[cat_idx]
+        self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+    def OnSetColor(self, event):
+        """!Popup opacity level indicator"""
+        cat_idx = self.rightClickedItemIdx
+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
+
+        col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
+        col = map(int, col.split(':'))
+
+        col_data =  wx.ColourData()
+        col_data.SetColour(wx.Colour(*col))
+
+        dlg = wx.ColourDialog(self, col_data)
+        dlg.GetColourData().SetChooseFull(True)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            color = dlg.GetColourData().GetColour().Get()
+            color = ':'.join(map(str, color))
+            self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
+
+        dlg.Destroy()
+
+    def OnPopupOpacityLevel(self, event):
+        """!Popup opacity level indicator"""
+
+        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
+        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+        value = cat_attrs['opacity'] * 100
+        name = cat_attrs['name']
+
+        dlg = SetOpacityDialog(self, opacity = value,
+                               title = _("Change opacity of class <%s>" % name))
+
+        dlg.applyOpacity.connect(lambda value:
+                                 self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
+        dlg.CentreOnParent()
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
+        
+        dlg.Destroy()
+
+    def OnRename(self, event):
+        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
+        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+        dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
+        dlg.CentreOnParent()
+
+        while True:
+            if dlg.ShowModal() == wx.ID_OK:
+                name = dlg.GetNewName().strip()
+                if not name:
+                    GMessage(parent=self, message=_("Empty name was inserted."))
+                else:
+                    self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
+                    break
+            else:
+                break
+
+        dlg.Destroy()
+
+    def _setCatAttrs(self, cat_id, attrs):
+        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+        self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
+        self.cats_mgr.setCategoryAttrs.connect(self.Update)

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -14,82 +14,83 @@
 """
 import wx
 import numpy as np
-import random
+from math import ceil
+#TODO testing
+import time
+from multiprocessing import Process, Queue
+
+from copy import deepcopy
+from scatt_plot.core_c import MergeArrays, ApplyColormap
+from scatt_plot.dialogs import ManageBusyCursorMixin
+from core.settings import UserSettings
+
 try:
-    haveMatPlot = True
     import matplotlib
     matplotlib.use('WXAgg')
     from matplotlib.figure import Figure
     from matplotlib.backends.backend_wxagg import \
-    FigureCanvasWxAgg as FigCanvas, \
-    NavigationToolbar2WxAgg as NavigationToolbar
-except ImportError:
-    haveMatPlot = False
+    FigureCanvasWxAgg as FigCanvas
+    from matplotlib.lines import Line2D
+    from matplotlib.artist import Artist
+    from matplotlib.mlab import dist_point_to_segment
+    from matplotlib.patches import Polygon, Ellipse, Rectangle
+    import matplotlib.image as mi
+    import matplotlib.colors as mcolors
+    import matplotlib.cbook as cbook
+except ImportError as e:
+    raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
 
-class ScatterPlotWidget(wx.Panel):
-    def __init__(self, parent, scatt_id, scatt_mgr,
+import grass.script as grass
+from grass.pydispatch.signal import Signal
+
+class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):
+    def __init__(self, parent, scatt_id, scatt_mgr, transpose,
                  id = wx.ID_ANY):
-    
+        #TODO should not be transpose and scatt_id but x, y
         wx.Panel.__init__(self, parent, id)
+        # bacause of aui (if floatable it can not take cursor from parent)        
+        ManageBusyCursorMixin.__init__(self, window=self)
 
         self.parent = parent
+        self.full_extend = None
+        self.mode = None
 
         self._createWidgets()
         self._doLayout()
         self.scatt_id = scatt_id
         self.scatt_mgr = scatt_mgr
-        self.press_coords = None
 
         self.cidpress = None
         self.cidrelease = None
 
-    def StartCategoryEdit(self):
-        'connect to all the events we need'
-        self.cidpress = self.canvas.mpl_connect(
-            'button_press_event', self.OnPress)
-        self.cidrelease = self.canvas.mpl_connect(
-            'button_release_event', self.OnRelease)
-        #self.cidmotion = self.canvas.mpl_connect(
-        #    'motion_notify_event', self.OnRelease)
+        self.rend_dt = {}
 
-    def OnPress(self, event):
-        'on button press we will see if the mouse is over us and store some data'
+        self.transpose = transpose
 
-        if event.xdata and event.ydata:
-            self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
-        else:
-            self.press_coords = None
+        self.inverse = False
 
-    def OnRelease(self, event):
-        'on release we reset the press data'
+        self.SetSize((200, 100))
+        self.Layout()
 
-        if event.xdata and event.ydata and self.press_coords:
+        self.base_scale = 1.2
+        self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
 
-            bbox = {}
-            if event.ydata > self.press_coords['y']:
-                bbox['up_y'] = event.ydata
-                bbox['btm_y'] = self.press_coords['y']
-            else:
-                bbox['up_y'] = self.press_coords['y']
-                bbox['btm_y'] = event.ydata
+        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
+        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")
 
-            if event.xdata > self.press_coords['x']:
-                bbox['up_x'] = event.xdata
-                bbox['btm_x'] = self.press_coords['x']
-            else:
-                bbox['up_x'] = self.press_coords['x']
-                bbox['btm_x'] = event.xdata
+        self.contex_menu = ScatterPlotContextMenu(plot = self)
 
-            self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
+        self.ciddscroll = None
 
-    def StopCategoryEdit(self):
-        'disconnect all the stored connection ids'
+        self.canvas.mpl_connect('motion_notify_event', self.Motion)
+        self.canvas.mpl_connect('button_press_event', self.OnPress)
+        self.canvas.mpl_connect('button_release_event', self.OnRelease)
+        self.canvas.mpl_connect('draw_event', self.draw_callback)
+        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)
 
-        if self.cidpress:
-            self.canvas.mpl_disconnect(self.cidpress)
-        if self.cidrelease:
-            self.canvas.mpl_disconnect(self.cidrelease)
-        #self.canvas.mpl_disconnect(self.cidmotion)
+    def draw_callback(self, event):
+        self.polygon_drawer.draw_callback(event)
+        self.axes.draw_artist(self.zoom_rect)
 
     def _createWidgets(self):
 
@@ -97,103 +98,856 @@
         # 5x4 inches, 100 dots-per-inch
         #
         self.dpi = 100
-        self.fig = Figure((5.0, 4.0), dpi=self.dpi)
+        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
+        self.fig.autolayout = True
+
         self.canvas = FigCanvas(self, -1, self.fig)
         
-        # Since we have only one plot, we can use add_axes 
-        # instead of add_subplot, but then the subplot
-        # configuration tool in the navigation toolbar wouldn't
-        # work.
-        #
-        self.axes = self.fig.add_subplot(111)
-        
-        # Bind the 'pick' event for clicking on one of the bars
-        #
-        self.canvas.mpl_connect('button_press_event', self.on_pick)
-        
+        self.axes = self.fig.add_axes([0.0,0.0,1,1])
 
-        # Create the navigation toolbar, tied to the canvas
-        #
-        self.toolbar = NavigationToolbar(self.canvas)
-        
+        pol = Polygon(list(zip([0], [0])), animated=True)
+        self.axes.add_patch(pol)
+        self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
+
+        self.zoom_wheel_coords = None
+        self.zoom_rect_coords = None
+        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor = 'none')
+        self.zoom_rect.set_visible(False)
+        self.axes.add_patch(self.zoom_rect)
+
+    def ZoomToExtend(self):
+        if self.full_extend:
+            self.axes.axis(self.full_extend)
+            self.canvas.draw()
+
+    def SetMode(self, mode):
+        self._deactivateMode()
+        if mode == 'zoom':
+            self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.ZoomWheel)
+            self.mode = 'zoom'
+        elif mode == 'zoom_extend':
+            self.mode = 'zoom_extend'
+        elif mode == 'pan':
+            self.mode = 'pan'
+        elif mode:
+            self.polygon_drawer.SetMode(mode)
+
+    def SetSelectionPolygonMode(self, activate):
+        self.polygon_drawer.SetSelectionPolygonMode(activate)
+
+    def _deactivateMode(self):
+        self.mode  = None
+        self.polygon_drawer.SetMode(None)
+
+        if self.ciddscroll:
+            self.canvas.mpl_disconnect(self.ciddscroll)
+
+        self.zoom_rect.set_visible(False)
+        self._stopCategoryEdit()
+
+    def GetCoords(self):
+
+        coords = self.polygon_drawer.GetCoords()
+        if coords is None:
+            return
+
+        if self.transpose:
+            for c in coords:
+                tmp = c[0]
+                c[0] = c[1]
+                c[1] = tmp
+
+        return coords
+
+    def SetEmpty(self):
+        return self.polygon_drawer.SetEmpty()
+
+    def OnRelease(self, event):
+        if not self.mode == "zoom": return
+        self.zoom_rect.set_visible(False)
+        self.ZoomRectangle(event)
+        self.canvas.draw()
+    
+    def OnPress(self, event):
+        'on button press we will see if the mouse is over us and store some data'
+        if not event.inaxes:
+            return
+        if self.mode == "zoom_extend":
+            self.ZoomToExtend()
+
+        if event.xdata and event.ydata:
+            self.zoom_wheel_coords = { 'x' : event.xdata, 'y' : event.ydata}
+            self.zoom_rect_coords = { 'x' : event.xdata, 'y' : event.ydata}
+        else:
+            self.zoom_wheel_coords = None
+            self.zoom_rect_coords = None
+
+    def _stopCategoryEdit(self):
+        'disconnect all the stored connection ids'
+
+        if self.cidpress:
+            self.canvas.mpl_disconnect(self.cidpress)
+        if self.cidrelease:
+            self.canvas.mpl_disconnect(self.cidrelease)
+        #self.canvas.mpl_disconnect(self.cidmotion)
+
     def _doLayout(self):
         
         self.main_sizer = wx.BoxSizer(wx.VERTICAL)
         self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
-        self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
-        self.main_sizer.AddSpacer(10)
-        
         self.SetSizer(self.main_sizer)
         self.main_sizer.Fit(self)
     
-    def Plot(self, scatts, styles):
+    def Plot(self, cats_order, scatts, ellipses, styles):
         """ Redraws the figure
         """
-      
+
+        callafter_list = []
+
+        if self.full_extend:
+            cx = self.axes.get_xlim()
+            cy = self.axes.get_ylim()
+            c = cx + cy
+        else:
+            c = None
+
+        q = Queue()
+        p = Process(target=MergeImg, args=(cats_order, scatts, styles, 
+                                           self.rend_dt, q))
+        p.start()
+        merged_img, self.full_extend, self.rend_dt = q.get()
+        p.join()
+        
+        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
         self.axes.clear()
-        for cat_id, scatt in scatts.iteritems():
+        self.axes.axis('equal')
+
+        if self.transpose:
+            merged_img = np.transpose(merged_img, (1, 0, 2))
+
+        img = imshow(self.axes, merged_img,
+                     extent= [int(ceil(x)) for x in self.full_extend],
+                     origin='lower',
+                     interpolation='nearest',
+                     aspect="equal")
+
+        callafter_list.append([self.axes.draw_artist, [img]])
+        callafter_list.append([grass.try_remove, [merged_img.filename]])
+
+        for cat_id in cats_order:
             if cat_id == 0:
-                cmap = matplotlib.cm.jet
-                cmap.set_bad('w',1.)
-                cmap._init()
-                cmap._lut[len(cmap._lut) - 1, -1] = 0
-            else:
-                colors = styles[cat_id]['color'].split(":")
+                continue
+            if not ellipses.has_key(cat_id):
+                continue
+                
+            e = ellipses[cat_id]
+            if not e:
+                continue
 
-                cmap = matplotlib.cm.jet
-                cmap.set_bad('w',1.)
-                cmap._init()
-                cmap._lut[len(cmap._lut) - 1, -1] = 0
-                cmap._lut[:, 0] = int(colors[0])/255.0
-                cmap._lut[:, 1] = int(colors[1])/255.0
-                cmap._lut[:, 2] = int(colors[2])/255.0
+            colors = styles[cat_id]['color'].split(":")
+            if self.transpose:
+                e['theta'] = 360 - e['theta'] + 90
+                if e['theta'] >= 360:
+                    e['theta'] = abs(360 - e['theta']) 
+                
+                e['pos'] = [e['pos'][1], e['pos'][0]]
 
-            masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
+            ellip = Ellipse(xy=e['pos'], 
+                            width=e['width'], 
+                            height=e['height'], 
+                            angle=e['theta'], 
+                            edgecolor="w",
+                            linewidth=1.5, 
+                            facecolor='None')
+            self.axes.add_artist(ellip)
+            callafter_list.append([self.axes.draw_artist, [ellip]])
 
+            color = map(lambda v : int(v)/255.0, styles[cat_id]['color'].split(":"))
 
-            b1_i = scatt['bands_info']['b1']
-            b2_i = scatt['bands_info']['b2']
+            ellip = Ellipse(xy=e['pos'], 
+                            width=e['width'], 
+                            height=e['height'], 
+                            angle=e['theta'], 
+                            edgecolor=color,
+                            linewidth=1, 
+                            facecolor='None')
+
+            self.axes.add_artist(ellip)
+            callafter_list.append([self.axes.draw_artist, [ellip]])
             
-            #self.axes.set_xlim((0, 270))
-            #self.axes.set_ylim((0, 270))
-            np.savetxt("/home/ostepok/Desktop/data.txt", scatt['np_vals'], fmt = '%d') 
+            center = Line2D([e['pos'][0]], [e['pos'][1]], 
+                            marker='x',
+                            markeredgecolor='w',
+                            #markerfacecolor=color,
+                            markersize=2)
+            self.axes.add_artist(center)
+            callafter_list.append([self.axes.draw_artist, [center]])
 
+        callafter_list.append([self.fig.canvas.blit, []])
 
-            #TODO needs optimization
-            img = self.axes.imshow(masked_cat, cmap = cmap,
-                                               origin = 'lower',
-                                               extent = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5), 
-                                               interpolation='nearest')
-            
-            self.canvas.draw()
+        if c:
+            self.axes.axis(c)
+        wx.CallAfter(lambda : self.CallAfter(callafter_list))
+    
+    def CallAfter(self, funcs_list):
+        while funcs_list: 
+            fcn, args = funcs_list.pop(0) 
+            fcn(*args) 
 
-    
-    def on_pick(self, event):
-        pass
-        # The event received here is of the type
-        # matplotlib.backend_bases.PickEvent
-        #
-        # It carries lots of information, of which we're using
-        # only a small amount here.
-        # 
-        #box_points = event.x
+        self.canvas.draw()
+
+    def CleanUp(self):
+        self.plotClosed.emit(scatt_id = self.scatt_id)
+        self.Destroy()
+
+    def ZoomWheel(self, event):
+        # get the current x and y limits
+        if not event.inaxes:
+            return
+        # tcaswell
+        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
+        cur_xlim = self.axes.get_xlim()
+        cur_ylim = self.axes.get_ylim()
         
-        #msg = "You've clicked on a bar with coords:\n"
+        xdata = event.xdata
+        ydata = event.ydata 
+        if event.button == 'up':
+            scale_factor = 1/self.base_scale
+        elif event.button == 'down':
+            scale_factor = self.base_scale
+        else:
+            scale_factor = 1
+
+        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
+                  xdata + (cur_xlim[1] - xdata) * scale_factor, 
+                  ydata - (ydata - cur_ylim[0]) * scale_factor,
+                  ydata + (cur_ylim[1] - ydata) * scale_factor)
+
+        self.axes.axis(extend)
         
-        #dlg = wx.MessageDialog(
-        #    self, 
-        #    msg, 
-        #    "Click!",
-        #    wx.OK | wx.ICON_INFORMATION)
+        self.canvas.draw()
 
-        #dlg.ShowModal() 
-        #dlg.Destroy()        
+    def ZoomRectangle(self, event):
+        # get the current x and y limits
+        if not self.mode == "zoom": return
+        if event.inaxes is None: return
+        if event.button != 1: return
 
-    def on_exit(self, event):
+        cur_xlim = self.axes.get_xlim()
+        cur_ylim = self.axes.get_ylim()
+        
+        x1, y1 = event.xdata, event.ydata
+        x2 = deepcopy(self.zoom_rect_coords['x'])
+        y2 = deepcopy(self.zoom_rect_coords['y'])
+
+        if x1 == x2 or y1 == y2:
+            return
+
+        self.axes.axis((x1, x2, y1, y2))
+        #self.axes.set_xlim(x1, x2)#, auto = True)
+        #self.axes.set_ylim(y1, y2)#, auto = True)
+        self.canvas.draw()
+
+    def Motion(self, event):
+        self.PanMotion(event)
+        self.ZoomRectMotion(event)
+        
+        if event.inaxes is None: 
+            return
+        
+        self.cursorMove.emit(x=event.xdata, y=event.ydata, scatt_id=self.scatt_id)
+
+    def OnCanvasLeave(self, event):
+        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)
+
+    def PanMotion(self, event):
+        'on mouse movement'
+        if not self.mode == "pan": 
+            return
+        if event.inaxes is None: 
+            return
+        if event.button != 1: 
+            return
+
+        cur_xlim = self.axes.get_xlim()
+        cur_ylim = self.axes.get_ylim()
+
+        x,y = event.xdata, event.ydata
+        
+        mx = (x - self.zoom_wheel_coords['x']) * 0.6
+        my = (y - self.zoom_wheel_coords['y']) * 0.6
+
+        extend = (cur_xlim[0] - mx, cur_xlim[1] - mx, cur_ylim[0] - my, cur_ylim[1] - my)
+
+        self.zoom_wheel_coords['x'] = x
+        self.zoom_wheel_coords['y'] = y
+
+        self.axes.axis(extend)
+
+        #self.canvas.copy_from_bbox(self.axes.bbox)
+        #self.canvas.restore_region(self.background)
+        self.canvas.draw()
+        
+    def ZoomRectMotion(self, event):
+        if not self.mode == "zoom": return
+        if event.inaxes is None: return
+        if event.button != 1: return
+
+        x1, y1 = event.xdata, event.ydata
+        self.zoom_rect.set_visible(True)
+        x2 = self.zoom_rect_coords['x']
+        y2 = self.zoom_rect_coords['y']
+
+        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))
+
+        #self.axes.draw_artist(self.zoom_rect)
+        self.canvas.draw()
+
+def MergeImg(cats_order, scatts, styles, rend_dt, output_queue):
+
+        init = True
+        merged_img = None
+        merge_tmp = grass.tempfile()
+        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 \
+               not styles[cat_id]['show']):
+                if rend_dt.has_key(cat_id) and not rend_dt[cat_id]:
+                    del rend_dt[cat_id]
+                continue
+            if init:
+
+                b2_i = scatt['bands_info']['b1']
+                b1_i = scatt['bands_info']['b2']
+
+                full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5) 
             
-        self.CleanUp()
+            # if it does not need to be updated and was already rendered 
+            if not _renderCat(cat_id, rend_dt, scatt, styles):
+                # is empty - has only zeros
+                if rend_dt[cat_id] is None:
+                    continue
+            else:
+                masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
+                vmax = np.amax(masked_cat)
+                # totally empty -> no need to render
+                if vmax == 0:
+                    render_cat_ids[cat_id] = None
+                    continue
+
+                cmap = _getColorMap(cat_id, styles)
+                masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
+
+                cmap = np.uint8(cmap._lut * 255)
+                sh =masked_cat.shape
+
+                rend_dt[cat_id] = {}
+                if cat_id != 0:
+                    rend_dt[cat_id]['color'] = styles[cat_id]['color']
+                rend_dt[cat_id]['dt'] = np.memmap(grass.tempfile(), dtype='uint8', mode='w+', 
+                                                                    shape=(sh[0], sh[1], 4))
+                #colored_cat = np.zeros(dtype='uint8', )
+                ApplyColormap(masked_cat, masked_cat.mask, cmap, rend_dt[cat_id]['dt'])
+
+                #colored_cat = np.uint8(cmap(masked_cat) * 255)
+                del masked_cat
+                del cmap
+            
+            #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
+            if init:
+                merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', 
+                                       shape=rend_dt[cat_id]['dt'].shape)
+                merged_img[:] = rend_dt[cat_id]['dt']
+                init = False
+            else:
+                MergeArrays(merged_img, rend_dt[cat_id]['dt'], styles[cat_id]['opacity'])
+
+            """
+                #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape) 
+                c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
+
+                #TODO apply strides and there will be no need for loop
+                #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
+                
+                for i in range(3):
+                    merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
+                merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
+                
+                del c_img_a
+            """
+
+        output_queue.put((merged_img, full_extend, rend_dt))
+
+def _renderCat(cat_id, rend_dt, scatt, styles):
+    return True
+
+    if not rend_dt.has_key(cat_id):
+        return True
+    if not rend_dt[cat_id]:
+        return False
+    if scatt['render']:
+        return True
+    if cat_id != 0 and \
+       rend_dt[cat_id]['color'] != styles[cat_id]['color']:
+       return True
+    
+    return False
+
+def _getColorMap(cat_id, styles):
+    cmap = matplotlib.cm.jet
+    if cat_id == 0:
+        cmap.set_bad('w',1.)
+        cmap._init()
+        cmap._lut[len(cmap._lut) - 1, -1] = 0
+    else:
+        colors = styles[cat_id]['color'].split(":")
+
+        cmap.set_bad('w',1.)
+        cmap._init()
+        cmap._lut[len(cmap._lut) - 1, -1] = 0
+        cmap._lut[:, 0] = int(colors[0])/255.0
+        cmap._lut[:, 1] = int(colors[1])/255.0
+        cmap._lut[:, 2] = int(colors[2])/255.0
+
+    return cmap
+
+class ScatterPlotContextMenu:
+    def __init__(self, plot):
+
+        self.plot = plot
+        self.canvas = plot.canvas
+        self.cidpress = self.canvas.mpl_connect(
+            'button_press_event', self.ContexMenu)
+   
+    def ContexMenu(self, event):
+        if not event.inaxes:
+            return
+
+        if event.button == 3:
+            menu = wx.Menu()       
+            menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
+
+            for item in menu_items:
+                item_id = wx.ID_ANY
+                menu.Append(item_id, text = item[1])
+                menu.Bind(wx.EVT_MENU, item[2], id = item_id)
+
+            wx.CallAfter(self.ShowMenu, menu) 
+   
+    def ShowMenu(self, menu):
+        self.plot.PopupMenu(menu)
+        menu.Destroy()
+        self.plot.ReleaseMouse() 
+
+class PolygonDrawer:
+    """
+    An polygon editor.
+    """
+    def __init__(self, ax, pol, empty_pol):
+        if pol.figure is None:
+            raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
+        self.ax = ax
+        self.canvas = pol.figure.canvas
+
+        self.showverts = True
+
+        self.pol = pol
+        self.empty_pol = empty_pol
+
+        x, y = zip(*self.pol.xy)
+
+        style = self._getPolygonStyle()
+
+        self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
+        self.ax.add_line(self.line)
+        #self._update_line(pol)
+
+        cid = self.pol.add_callback(self.poly_changed)
+        self.moving_ver_idx = None # the active vert
+
+        self.mode = None
+
+        if self.empty_pol:
+            self._show(False)
+
+        #self.canvas.mpl_connect('draw_event', self.draw_callback)
+        self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
+        self.canvas.mpl_connect('button_release_event', self.button_release_callback)
+        self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
+    
+        self.it = 0
+
+    def _getPolygonStyle(self):
+        style = {}
+        style['sel_pol'] = UserSettings.Get(group='scatt', 
+                                            key='selection', 
+                                            subkey='sel_pol')
+        style['sel_pol_vertex'] = UserSettings.Get(group='scatt', 
+                                                   key='selection', 
+                                                   subkey='sel_pol_vertex')
+
+        style['sel_pol'] = [i / 255.0 for i in style['sel_pol']] 
+        style['sel_pol_vertex'] = [i / 255.0 for i in style['sel_pol_vertex']] 
+
+        return style
+
+    def _getSnapTresh(self):
+        return UserSettings.Get(group='scatt', 
+                                key='selection', 
+                                subkey='snap_tresh')
+
+    def SetMode(self, mode):
+        self.mode = mode
+
+    def SetSelectionPolygonMode(self, activate):
         
-    def CleanUp(self):
+        self.Show(activate)
+        if not activate and self.mode:
+            self.SetMode(None) 
+
+    def Show(self, show):
+        if show:
+            if not self.empty_pol:
+                self._show(True)
+        else:
+            self._show(False)
+
+    def GetCoords(self):
+        if self.empty_pol:
+            return None
+
+        coords = deepcopy(self.pol.xy)
+        return coords
+
+    def SetEmpty(self):
+        self._setEmptyPol(True)
+
+    def _setEmptyPol(self, empty_pol):
+        self.empty_pol = empty_pol
+        if self.empty_pol:
+            #TODO
+            self.pol.xy = np.array([[0, 0]])
+        self._show(not empty_pol)
+
+    def _show(self, show):
+
+        self.show = show
+
+        self.line.set_visible(self.show)
+        self.pol.set_visible(self.show)
+
+        self.Redraw()
+
+    def Redraw(self):
+        if self.show:
+            self.ax.draw_artist(self.pol)
+            self.ax.draw_artist(self.line)
+        self.canvas.blit(self.ax.bbox)
+        self.canvas.draw()
+
+    def draw_callback(self, event):
+
+        style=self._getPolygonStyle()
+        self.pol.set_facecolor(style['sel_pol'])
+        self.line.set_markerfacecolor(style['sel_pol_vertex'])
+
+        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
+        self.ax.draw_artist(self.pol)
+        self.ax.draw_artist(self.line)
+    
+    def poly_changed(self, pol):
+        'this method is called whenever the polygon object is called'
+        # only copy the artist props to the line (except visibility)
+        vis = self.line.get_visible()
+        Artist.update_from(self.line, pol)
+        self.line.set_visible(vis)  # don't use the pol visibility state
+
+    def get_ind_under_point(self, event):
+        'get the index of the vertex under point if within treshold'
+
+        # display coords
+        xy = np.asarray(self.pol.xy)
+        xyt = self.pol.get_transform().transform(xy)
+        xt, yt = xyt[:, 0], xyt[:, 1]
+        d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
+        indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
+        ind = indseq[0]
+
+        if d[ind]>=self._getSnapTresh():
+            ind = None
+
+        return ind
+
+    def OnButtonPressed(self, event):
+        if not event.inaxes:
+            return
+
+        if event.button in [2, 3]: 
+            return
+
+        if self.mode == "delete_vertex":
+            self._deleteVertex(event)
+        elif self.mode == "add_boundary_vertex":
+            self._addVertexOnBoundary(event)
+        elif self.mode == "add_vertex":
+            self._addVertex(event)
+        elif self.mode == "remove_polygon":
+            self.SetEmpty()
+        self.moving_ver_idx = self.get_ind_under_point(event)
+
+    def button_release_callback(self, event):
+        'whenever a mouse button is released'
+        if not self.showverts: return
+        if event.button != 1: return
+        self.moving_ver_idx = None
+
+    def ShowVertices(self, show):
+        self.showverts = show
+        self.line.set_visible(self.showverts)
+        if not self.showverts: self.moving_ver_idx = None
+
+    def _deleteVertex(self, event):
+        ind = self.get_ind_under_point(event)
+
+        if ind  is None or self.empty_pol:
+            return
+
+        if len(self.pol.xy) <= 2:
+            self.empty_pol = True
+            self._show(False)
+            return
+
+        coords = []
+        for i,tup in enumerate(self.pol.xy): 
+            if i == ind:
+                continue
+            elif i == 0 and ind == len(self.pol.xy) - 1:
+                continue
+            elif i == len(self.pol.xy) - 1 and ind == 0: 
+                continue
+
+            coords.append(tup)
+
+        self.pol.xy = coords
+        self.line.set_data(zip(*self.pol.xy))
+
+        self.Redraw()
+
+    def _addVertexOnBoundary(self, event):
+        if self.empty_pol:
+            return
+
+        xys = self.pol.get_transform().transform(self.pol.xy)
+        p = event.x, event.y # display coords
+        for i in range(len(xys)-1):
+            s0 = xys[i]
+            s1 = xys[i+1]
+            d = dist_point_to_segment(p, s0, s1)
+
+            if d<=self._getSnapTresh():
+                self.pol.xy = np.array(
+                    list(self.pol.xy[:i + 1]) +
+                    [(event.xdata, event.ydata)] +
+                    list(self.pol.xy[i + 1:]))
+                self.line.set_data(zip(*self.pol.xy))
+                break
+
+        self.Redraw()
+
+    def _addVertex(self, event):
+
+        if self.empty_pol:
+            pt = (event.xdata, event.ydata)
+            self.pol.xy = np.array([pt, pt])
+            self._show(True)
+            self.empty_pol = False
+        else:
+            self.pol.xy = np.array(
+                        [(event.xdata, event.ydata)] +
+                        list(self.pol.xy[1:]) +
+                        [(event.xdata, event.ydata)])
+
+        self.line.set_data(zip(*self.pol.xy))
         
-        self.parent.OnPlotClosed(self.scatt_id)
-        self.Destroy()
\ No newline at end of file
+        self.Redraw()
+
+    def motion_notify_callback(self, event):
+        'on mouse movement'
+        if not self.mode == "move_vertex": return
+        if not self.showverts: return
+        if self.empty_pol: return
+        if self.moving_ver_idx is None: return
+        if event.inaxes is None: return
+        if event.button != 1: return
+
+        self.it += 1
+
+        x,y = event.xdata, event.ydata
+
+        self.pol.xy[self.moving_ver_idx] = x,y
+        if self.moving_ver_idx == 0:
+            self.pol.xy[len(self.pol.xy) - 1] = x,y
+        elif self.moving_ver_idx == len(self.pol.xy) - 1:
+            self.pol.xy[0] = x,y
+
+        self.line.set_data(zip(*self.pol.xy))
+
+        self.canvas.restore_region(self.background)
+
+        self.Redraw()
+
+class ModestImage(mi.AxesImage):
+    """
+    Computationally modest image class.
+
+    ModestImage is an extension of the Matplotlib AxesImage class
+    better suited for the interactive display of larger images. Before
+    drawing, ModestImage resamples the data array based on the screen
+    resolution and view window. This has very little affect on the
+    appearance of the image, but can substantially cut down on
+    computation since calculations of unresolved or clipped pixels
+    are skipped.
+
+    The interface of ModestImage is the same as AxesImage. However, it
+    does not currently support setting the 'extent' property. There
+    may also be weird coordinate warping operations for images that
+    I'm not aware of. Don't expect those to work either.
+
+    Author: Chris Beaumont <beaumont at hawaii.edu>
+    """
+    def __init__(self, minx=0.0, miny=0.0, *args, **kwargs):
+        if 'extent' in kwargs and kwargs['extent'] is not None:
+            raise NotImplementedError("ModestImage does not support extents")
+
+        self._full_res = None
+        self._sx, self._sy = None, None
+        self._bounds = (None, None, None, None)
+        self.minx = minx
+        self.miny = miny
+
+        super(ModestImage, self).__init__(*args, **kwargs)
+
+    def set_data(self, A):
+        """
+        Set the image array
+
+        ACCEPTS: numpy/PIL Image A
+        """
+        self._full_res = A
+        self._A = A
+
+        if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
+                                                         np.float):
+            raise TypeError("Image data can not convert to float")
+
+        if (self._A.ndim not in (2, 3) or
+            (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
+            raise TypeError("Invalid dimensions for image data")
+
+        self._imcache =None
+        self._rgbacache = None
+        self._oldxslice = None
+        self._oldyslice = None
+        self._sx, self._sy = None, None
+
+    def get_array(self):
+        """Override to return the full-resolution array"""
+        return self._full_res
+
+    def _scale_to_res(self):
+        """ Change self._A and _extent to render an image whose
+        resolution is matched to the eventual rendering."""
+
+        ax = self.axes
+        ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
+        xlim, ylim = ax.get_xlim(), ax.get_ylim()
+        dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
+
+        y0 = max(self.miny, ylim[0] - 5)
+        y1 = min(self._full_res.shape[0] + self.miny, ylim[1] + 5)
+        x0 = max(self.minx, xlim[0] - 5)
+        x1 = min(self._full_res.shape[1] + self.minx, xlim[1] + 5)
+        y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
+
+        sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
+        sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
+
+        # have we already calculated what we need?
+        if sx == self._sx and sy == self._sy and \
+            x0 == self._bounds[0] and x1 == self._bounds[1] and \
+            y0 == self._bounds[2] and y1 == self._bounds[3]:
+            return
+
+        self._A = self._full_res[y0 - self.miny:y1 - self.miny:sy, 
+                                 x0 - self.minx:x1 - self.minx:sx]
+
+        x1 = x0 + self._A.shape[1] * sx
+        y1 = y0 + self._A.shape[0] * sy
+
+        self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
+        self._sx = sx
+        self._sy = sy
+        self._bounds = (x0, x1, y0, y1)
+        self.changed()
+
+    def draw(self, renderer, *args, **kwargs):
+        self._scale_to_res()
+        super(ModestImage, self).draw(renderer, *args, **kwargs)
+
+def imshow(axes, X, cmap=None, norm=None, aspect=None,
+           interpolation=None, alpha=None, vmin=None, vmax=None,
+           origin=None, extent=None, shape=None, filternorm=1,
+           filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
+    """Similar to matplotlib's imshow command, but produces a ModestImage
+
+    Unlike matplotlib version, must explicitly specify axes
+    Author: Chris Beaumont <beaumont at hawaii.edu>
+    """
+
+    if not axes._hold:
+        axes.cla()
+    if norm is not None:
+        assert(isinstance(norm, mcolors.Normalize))
+    if aspect is None:
+        aspect = rcParams['image.aspect']
+    axes.set_aspect(aspect)
+
+    if extent:
+        minx=extent[0]
+        miny=extent[2]
+    else:
+        minx=0.0
+        miny=0.0
+
+    im = ModestImage(minx, miny, axes, cmap, norm, interpolation, origin, extent,
+                     filternorm=filternorm,
+                     filterrad=filterrad, resample=resample, **kwargs)
+
+    im.set_data(X)
+    im.set_alpha(alpha)
+    axes._set_artist_props(im)
+
+    if im.get_clip_path() is None:
+        # image does not already have clipping set, clip to axes patch
+        im.set_clip_path(axes.patch)
+
+    #if norm is None and shape is None:
+    #    im.set_clim(vmin, vmax)
+    if vmin is not None or vmax is not None:
+        im.set_clim(vmin, vmax)
+    else:
+        im.autoscale_None()
+    im.set_url(url)
+
+    # update ax.dataLim, and, if autoscaling, set viewLim
+    # to tightly fit the image, regardless of dataLim.
+    im.set_extent(im.get_extent())
+
+    axes.images.append(im)
+    im._remove_method = lambda h: axes.images.remove(h)
+
+    return im

Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py	                        (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,835 @@
+"""!
+ at package scatt_plot.scatt_plot
+
+ at brief Non GUI functions.
+
+Classes:
+ - scatt_core::Core
+ - scatt_core::CatRastUpdater
+ - scatt_core::AnalyzedData
+ - scatt_core::ScattPlotsCondsData
+ - scatt_core::ScattPlotsData
+
+(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
+
+# used iclass perimeters algorithm instead of convolve2d
+#from matplotlib.path import Path 
+#from scipy.signal import convolve2d
+
+from math import sqrt, ceil, floor
+from copy import deepcopy
+
+from core.gcmd import GException, GError, RunCommand
+
+import grass.script as grass
+
+from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
+                   Rasterize, SC_SCATT_DATA, SC_SCATT_CONDITIONS
+
+class Core:
+    """!Represents scatter plot backend.
+    """
+    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):
+        """Set bands for analysis.
+        """
+        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 AddScattPlots(self, scatt_ids):
+    
+        for s_id in scatt_ids:
+            self.scatts_dt.AddScattPlot(scatt_id = s_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
+
+        self.ComputeCatsScatts([cat_id])
+        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()))    
+            b = self.scatts_dt.GetBandsInfo(scatt_id)
+
+            region = {}
+            region['s'] = b['b2']['min'] - 0.5
+            region['n'] = b['b2']['max'] + 0.5
+
+            region['w'] = b['b1']['min'] - 0.5
+            region['e'] = b['b1']['max'] + 0.5
+
+            arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+            Rasterize(polygon=coords, 
+                      rast=arr, 
+                      region=region, 
+                      value=value)
+
+            # previous way of rasterization / used scipy
+            #raster_pol = RasterizePolygon(coords, b['b1']['range'], b['b1']['min'], 
+            #                                      b['b2']['range'], b['b2']['min'])
+
+            #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 ExportCatRast(self, cat_id, rast_name):
+
+        cat_rast = self.scatts_dt.GetCatRast(cat_id);
+        if not cat_rast:
+            return 1
+        
+        return RunCommand("g.copy", 
+                          rast=cat_rast + ',' + rast_name,
+                          getErrorMsg=True,
+                          overwrite=True)
+
+    def _validExtend(self, val):
+        #TODO do it general
+        if  val > 255:
+            val = 255
+        elif val < 0:
+            val = 0
+
+        return val
+
+class CatRastUpdater:
+    """!Update backend data structures according to selected areas in mapwindow.
+    """
+    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 SyncWithMap(self):
+        #TODO possible optimization - bbox only of vertex and its two neighbours
+
+        region = self.an_data.GetRegion()
+
+        bbox = {}
+        bbox['maxx'] = region['e']
+        bbox['minx'] = region['w']
+        bbox['maxy'] = region['n']
+        bbox['miny'] = region['s']
+
+        updated_cats = []
+
+        for cat_id in self.scatts_dt.GetCategories():
+            if cat_id == 0:
+                continue
+            
+            cat = [{1 : [cat_id]}]
+            self._updateCatRast(bbox, cat, updated_cats)
+
+        return updated_cats
+
+    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.category",
+                                    input=self.vectMap,
+                                    getErrorMsg = True,
+                                    option='report',
+                                    read=True,
+                                    env=environs)
+
+        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):
+
+        r = self.an_data.GetRegion()
+        new_r = {}
+
+        if bbox["maxy"] <= r["s"]:
+            return 0
+        elif bbox["maxy"] >= r["n"]:
+            new_r["n"] = bbox["maxy"]
+        else:
+            new_r["n"] = ceil((bbox["maxy"] - r["s"]) / r["nsres"]) * r["nsres"] + r["s"]
+
+        if bbox["miny"] >= r["n"]:
+            return 0
+        elif bbox["miny"] <= r["s"]:
+            new_r["s"] = bbox["miny"]
+        else:
+            new_r["s"] = floor((bbox["miny"] - r["s"]) / r["nsres"]) * r["nsres"] + r["s"]
+
+        if bbox["maxx"] <= r["w"]:
+            return 0
+        elif bbox["maxx"] >= r["e"]:
+            new_r["e"] = bbox["maxx"]
+        else:
+            new_r["e"] = ceil((bbox["maxx"] - r["w"]) / r["ewres"]) * r["ewres"] + r["w"]
+
+        if bbox["minx"] >= r["e"]:
+            return 0
+        elif bbox["minx"] <= r["w"]:
+            new_r["w"] = bbox["minx"]
+        else:
+            new_r["w"] = floor((bbox["minx"] - r["w"]) / r["ewres"]) * r["ewres"] + r["w"]
+
+        #TODO check regions resolution
+        new_r["nsres"] = r["nsres"]
+        new_r["ewres"] = r["ewres"]
+
+        return {"GRASS_REGION" :  grass.region_env(**new_r)}
+
+class AnalyzedData:
+    """!Represents analyzed data (bands, region).
+    """
+    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:
+    """!Data structure for selected areas in scatter plot(condtions).
+    """
+    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):
+    """!Data structure for computed points (classes) in scatter plots.\
+    """
+    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]
+
+        RunCommand("g.remove",
+                   rast=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, styles):
+        if scatt_id not in self.scatts_ids:
+            return False
+
+        scatts = {}
+        for cat_id in self.cats.iterkeys():
+            if cat_id == 0:
+                continue
+            nstd = styles[cat_id]['nstd']
+            scatts[cat_id] = self._getEllipse(cat_id, scatt_id, nstd)
+
+        return scatts
+
+    def _getEllipse(self, cat_id, scatt_id, nstd):
+        # Joe Kington
+        # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
+
+        data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
+
+        b = self.GetBandsInfo(scatt_id)
+        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 + b['b1']['min'], y_avg + b['b2']['min']])
+
+        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.itervalues():
+            grass.try_remove(tmp) 
+        for tmp in self.cats_rasts.itervalues():
+            RunCommand("g.remove",
+                       rast=tmp,
+                       getErrorMsg=True)
+
+        self.cats_rasts = {}
+        self.cats_rasts_conds = {}
+
+    def GetCatRast(self, cat_id):
+        if self.cats_rasts.has_key(cat_id):
+            return self.cats_rasts[cat_id]
+        return None
+
+    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
+
+
+# not used,  using iclass_perimeter algorith instead of scipy convolve2d
+"""
+def RasterizePolygon(pol, height, min_h, width, min_w):
+
+    # 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
+    ny = height
+
+    x, y =  np.meshgrid(np.arange(-0.5 + min_w, nx + 0.5 + min_w, dtype=float), 
+                        np.arange(-0.5 + min_h, ny + 0.5 + min_h, dtype=float))
+    x, y = x.flatten(), y.flatten()
+
+    points = np.vstack((x,y)).T
+
+    p = Path(pol)
+    grid = p.contains_points(points)
+    grid = grid.reshape((ny + 1, nx + 1))
+    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')
+
+    return raster
+"""
+
+def idScattToidBands(scatt_id, n_bands):
+    """!Get bands ids from scatter plot id.""" 
+    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):
+    """!Get scatter plot id from band ids."""
+    if band_2_id <  band_1_id:
+        tmp = band_1_id
+        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
\ No newline at end of file

Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py	2013-09-23 16:22:36 UTC (rev 57823)
@@ -13,94 +13,255 @@
 
 @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
 """
-
-
 import wx
 
-from icons.icon        import MetaIcon
+from icons.icon import MetaIcon
 from gui_core.toolbars import BaseToolbar, BaseIcons
-from core.gcmd         import RunCommand
+from core.gcmd import RunCommand
+from core.gcmd import GException, GError, RunCommand
+from scatt_plot.scatt_core import idBandsToidScatt
+from scatt_plot.dialogs import SettingsDialog
 
 class MainToolbar(BaseToolbar):
     """!Main toolbar
     """
-    def __init__(self, parent):
+    def __init__(self, parent, scatt_mgr, opt_tools=None):
         BaseToolbar.__init__(self, parent)
-        
+        self.scatt_mgr = scatt_mgr
+        self.opt_tools = opt_tools
+
         self.InitToolbar(self._toolbarData())
         
         # realize the toolbar
         self.Realize()
+        self.scatt_mgr.modeSet.connect(self.ModeSet)
 
     def _toolbarData(self):
 
         icons = {
-                 'set_data'    : MetaIcon(img = 'layer-raster-add',
-                                         label = _('Set input raster data for plots')),
-                 'settings'   : BaseIcons['settings'].SetLabel( _('Ssettings')),
-                 'help'       : MetaIcon(img = 'help',
+                'selectGroup' : MetaIcon(img = 'layer-group-add',
+                                 label = _('Select imagery group')),
+                'settings'   : BaseIcons['settings'].SetLabel( _('Settings')),
+                'help'       : MetaIcon(img = 'help',
                                          label = _('Show manual')),
-                 'add_scatt_pl'  : MetaIcon(img = 'layer-raster-analyze',
-                                            label = _('Add scatter plot'))
+                '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'))
                 }
+            
+        tools = [
+                    ('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),
+                    (None, ),
+                    ('settings', icons["settings"],
+                    self.OnSettings),
+                    ('help', icons["help"],
+                     self.OnHelp)                    
+                ]
 
+        if self.opt_tools and "add_group" in self.opt_tools:
+            tools.insert(0, ("selectGroup", icons['selectGroup'],
+                             lambda event : self.scatt_mgr.SetData()))
+
+        return self._getToolbarData(tools)
+
+    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)
+
+    def OnSettings(self, event):
+        dlg = SettingsDialog(parent=self, id=wx.ID_ANY, 
+                             title=_('Settings'), scatt_mgr = self.scatt_mgr)
+        
+        dlg.ShowModal()
+        dlg.Destroy()
+
+    def OnHelp(self, event) :
+            RunCommand('g.manual',
+                       entry = 'wxGUI.iscatt')
+
+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 = _('Create selection polygon'),
+                                         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 = _("Remove polygon (click on scatter plot for removing it's polygon)")),
+            }
+
         return self._getToolbarData((
-                                     ('sat_data', icons["set_data"],
-                                      self.parent.OnSetData),
+                                    ("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_scatt', icons["add_scatt_pl"],
-                                      self.parent.OnAddScattPl),
-                                     #('settings', icons["settings"],
-                                     # self.parent.OnSettings),  
-                                     #('help', icons["help"],
-                                     # self.OnHelp),                    
-                                     ("quit", BaseIcons['quit'],
-                                      self.parent.OnCloseDialog)
+                                     ("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),
+                                     ('remove_polygon', self.icons['delete'],
+                                     lambda event: self.SetMode(event, 'remove_polygon'),
+                                     wx.ITEM_CHECK)
                                     ))
 
-    def OnHelp(self, event) :
-            RunCommand('g.manual',
-                       entry = 'wxGUI.scatt_plot')
+    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)
 
-class CategoriesToolbar(BaseToolbar):
+    def ModeSet(self, mode):
 
-    def __init__(self, parent, cats_list, scatts_dlg):
+        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]
+
+class CategoryToolbar(BaseToolbar):
+    """!Main toolbar
+    """
+    def __init__(self, parent, scatt_mgr, cats_list):
         BaseToolbar.__init__(self, parent)
+        self.scatt_mgr = scatt_mgr
+        self.cats_mgr = self.scatt_mgr.GetCategoriesManager()
         self.cats_list = cats_list
-        self.scatts_dlg = scatts_dlg
 
         self.InitToolbar(self._toolbarData())
         
-
         # realize the toolbar
         self.Realize()
 
-
     def _toolbarData(self):
-
-        icons = {
-            'editCatAdd'  : MetaIcon(img = 'polygon-create',
-                                     label = _('Add region to category mode')),
-            'editCatRemove'  : MetaIcon(img = 'polygon-delete',
-                                        label = _('Remove region to category mode')),
-            'addCat'     : MetaIcon(img = 'layer-add',
-                                    label = _('Add category')),
-            'deleteCat'  : MetaIcon(img = 'layer-remove',
-                                    label = _('Delete category'))
+        """!Toolbar data
+        """
+        self.icons = {
+            'add_class'     : MetaIcon(img = 'layer-add',
+                                       label = _('Add class')),
+            'remove_class'  : MetaIcon(img = 'layer-remove',
+                                       label = _('Remove selected class'))
             }
 
-        return  self._getToolbarData((('editCatAdd', icons['editCatAdd'],
-                                        lambda event: self.scatts_dlg.EditCatAdd(),
-                                        wx.ITEM_CHECK),
-                                      ('editCatRemove', icons['editCatRemove'],
-                                        lambda event: self.scatts_dlg.EditCatRemove(),
-                                        wx.ITEM_CHECK),
-                                     (None, ),
-                                     ('addCat', icons["addCat"],
-                                        lambda event: self.cats_list.AddCategory()),
-                                     ('deleteCat', icons["deleteCat"],
-                                        lambda event: self.cats_list.DeleteCategory())))
-                                    
-    def GetToolId(self, toolName): #TODO can be useful in base
-
-        return vars(self)[toolName]            
\ No newline at end of file
+        return self._getToolbarData((
+                                    ("add_class", self.icons["add_class"],
+                                     lambda event: self.cats_mgr.AddCategory()),
+                                     ("remove_class", self.icons['remove_class'],
+                                     lambda event: self.cats_list.DeleteCategory()),
+                                    ))
\ No newline at end of file

Modified: sandbox/turek/scatter_plot/include/defs/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/defs/imagery.h	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/include/defs/imagery.h	2013-09-23 16:22:36 UTC (rev 57823)
@@ -92,6 +92,7 @@
 int I_list_group_simple(const struct Ref *, FILE *);
 
 /* list_subgp.c */
+char ** I_list_subgroups(const char *, int *);
 int I_list_subgroup(const char *, const char *, const struct Ref *, FILE *);
 int I_list_subgroup_simple(const struct Ref *, FILE *);
 
@@ -110,29 +111,28 @@
 FILE *I_fopen_subgroup_ref_new(const char *, const char *);
 FILE *I_fopen_subgroup_ref_old(const char *, const char *);
 
-/* scatt_plt.c */
+/* scatt_sccats.c */
 void I_sc_init_cats(struct scCats *, int, int);
 void I_sc_free_cats(struct scCats *);
-void I_sc_get_active_categories(int *, int *, struct scCats *);
-int I_sc_add_cat(struct scCats *, int);
-int I_sc_delete_cat(struct scCats *, int);
+int I_sc_add_cat(struct scCats *);
 int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
-int I_sc_remove_scatt_data(struct scCats *, struct scdScattData *, int, int);
-int I_sc_set_value(struct scCats *, int, int, int, int);
 
-void I_scd_init_scatt_data(struct scdScattData *, int, int, void *, struct Range, struct Range);
-void I_scd_free_scatt_data(struct scdScattData *);
-void I_scd_get_range_min_max(struct scdScattData *, CELL *, CELL *, CELL *, CELL *);
-void * I_scd_get_data_ptr(struct scdScattData *);
-int I_scd_set_value(struct scdScattData *, unsigned int, unsigned int);
+void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
 
+/* scatt.c */
 int I_compute_scatts(struct Cell_head *, struct scCats *, const char **, 
-                     int, struct scCats *, const char **,  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 *);
 
+int I_merge_arrays(unsigned char *, unsigned char *, unsigned, unsigned, double);
+int I_apply_colormap(unsigned char *, unsigned char *, unsigned,  unsigned char *, unsigned char *);
+int I_rasterize(double *, int, unsigned char, struct Cell_head *, unsigned char *);
+
 /* sig.c */
 int I_init_signatures(struct Signature *, int);
 int I_new_signature(struct Signature *);

Modified: sandbox/turek/scatter_plot/include/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/imagery.h	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/include/imagery.h	2013-09-23 16:22:36 UTC (rev 57823)
@@ -136,47 +136,55 @@
 } IClass_statistics;
 
 /* Scatter Plot backend */
+
+#define SC_SCATT_DATA          0 
+#define SC_SCATT_CONDITIONS    1
+
+
+/*! Holds list of all catagories. 
+    It can contain selected areas for scatter plots (SC_SCATT_CONDITIONS type) or computed scatter plots (SC_SCATT_DATA type).
+*/
 struct scCats 
 {
-    int type; //TODO do not identify it with number
+    int type;        /*!< SC_SCATT_DATA -> computed scatter plots, SC_SCATT_CONDITIONS -> set conditions for scatter plots to be computed*/
 
-    int n_cats;
+    int n_cats;      /*!< number of alocated categories */
     
-    int n_bands;
-    int n_scatts;
+    int n_bands;     /*!< number of analyzed bands */
+    int n_scatts;    /*!< number of possible scattter plot which can be created from bands */
 
-    int   n_a_cats;
-    int * cats_ids;
-    int * cats_idxs;
+    int   n_a_cats;  /*!< number of used/active categories */
+    int * cats_ids;  /*!< (cat_idx->cat_id) array index is internal idx (position in cats_arr) and id is saved in it's position*/
+    int * cats_idxs; /*!< (cat_id->cat_idx) array index is id and internal idx is saved in it's position*/
 
-    struct scScatts ** cats_arr;
+    struct scScatts ** cats_arr; /*!< array of pointers to struct scScatts */
 };
 
+
+/*! Holds list of all scatter plots, which belongs to category. 
+*/
 struct scScatts
 {
-    int n_a_scatts;
+    int n_a_scatts;     /*!< number of used/active scatter plots*/
     
-    int * scatts_bands;
-    int * scatt_idxs;
+    int * scatts_bands; /*!< array of bands for which represents the scatter plots, 
+                             every scatter plot has assigned two bads (size of array is n_a_scatts * 2)*/
+    int * scatt_idxs;   /*!< (scatt_id->scatt_idx) internal idx of the scatter plot (position in scatts_arr)*/
 
-    struct scdScattData ** scatts_arr;
+    struct scdScattData ** scatts_arr; /*!< array of pointers to scdScattData */
 };
 
-#define SC_SCATT_DATA          0 
-#define SC_SCATT_CONDITIONS    1
-
+/*! Holds scatter plot data.
+*/
 struct scdScattData
 {
-    int n_vals; //TODO delete???
-    
-    struct Range band_1_range;
-    struct Range band_2_range;
+    int n_vals; /*!< Number of values in scatter plot. */
 
-
-    unsigned char  * b_conds_arr;
-    unsigned int  * scatt_vals_arr;
+    unsigned char  * b_conds_arr; /*!< array of selected areas (used for SC_SCATT_CONDITIONS type) otherwise NULL */
+    unsigned int  * scatt_vals_arr; /*!< array of computed areas (used for SC_SCATT_DATA type) otherwise NULL */
 };
 
+
 #define SIGNATURE_TYPE_MIXED 1
 
 #define GROUPFILE "CURGROUP"

Modified: sandbox/turek/scatter_plot/lib/imagery/scatt.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt.c	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt.c	2013-09-23 16:22:36 UTC (rev 57823)
@@ -12,25 +12,44 @@
 
    \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
  */
-
-#include <grass/raster.h>
-#include <grass/imagery.h>
-#include <grass/gis.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
 
+#include <grass/gis.h>
+#include <grass/vector.h>
+#include <grass/raster.h>
+#include <grass/imagery.h>
+#include <grass/glocale.h>
 
-static const int CAT_RAST_SCATT_SEL = 2;
-static const int CAT_RAST_RAST_SEL = 1;
-static const int  CAT_RAST_NULL = 0;
+#include "iclass_local_proto.h"
 
+struct rast_row
+{
+    CELL * row;
+    char * null_row;
+    struct Range rast_range; /*Range of whole raster.*/
+};
+
+/*!
+   \brief Create pgm header.
+
+   Scatter plot internally generates pgm files. These pgms have header in format created by this function.
+    
+   \param region region to be pgm header generated for 
+   \param [out] header header of pgm file
+ */
 static int get_cat_rast_header(struct Cell_head * region, char * header){
     return sprintf(header, "P5\n%d\n%d\n1\n", region->cols, region->rows);
 }
 
+/*!
+   \brief Create category raster conditions file.
+    
+   \param cat_rast_region region to be file generated for
+   \param cat_rast path of generated category raster file
+ */
 int I_create_cat_rast(struct Cell_head * cat_rast_region,  const char * cat_rast)
 {
     FILE * f_cat_rast;
@@ -41,14 +60,17 @@
     unsigned char * row_data;
 
     f_cat_rast = fopen(cat_rast, "wb");
-    if(!f_cat_rast)
+    if(!f_cat_rast) {
+        G_warning("Unable to create category raster condition file <%s>.", cat_rast);
         return -1;
+    }
 
     head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
 
     fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cat_rast);
     if (ferror(f_cat_rast)){
         fclose(f_cat_rast);
+        G_warning(_("Unable to write header into category raster condition file <%s>."), cat_rast);
         return -1;
     }
 
@@ -58,11 +80,10 @@
 
     for(i_row = 0; i_row < cat_rast_region->rows; i_row++) {
         fwrite(row_data, sizeof(unsigned char), (cat_rast_region->cols)/sizeof(unsigned char), f_cat_rast);
-        // TODO when code will be paralelized put it out of the loop
         if (ferror(f_cat_rast))
         {
             fclose(f_cat_rast);
-            G_debug(3, "Unable to write into file.");
+            G_warning(_("Unable to write into category raster condition file <%s>."), cat_rast);
             return -1;
         }
     }
@@ -71,12 +92,22 @@
     return 0;
 }
 
-static int print_reg(struct Cell_head * intersec, const char * pref)
+static int print_reg(struct Cell_head * intersec, const char * pref, int dbg_level)
 {
-    G_message("%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south, 
+    G_debug(dbg_level, "%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south, 
                intersec->east, intersec->west, intersec->ns_res, intersec->ew_res);
 }
 
+/*!
+   \brief Find intersection region of two regions.
+
+   \param A pointer to intersected region
+   \param B pointer to intersected region
+   \param [out] intersec pointer to intersection region of regions A B (relevant params of the region are: south, north, east, west)
+
+   \return  0 if interection exists
+   \return -1 if regions does not intersect
+ */
 static int regions_intersecion(struct Cell_head * A, struct Cell_head * B, struct Cell_head * intersec)
 {
 
@@ -104,17 +135,34 @@
 
 }
 
+/*!
+   \brief Get rows and cols numbers, which defines intersection of the regions.
+  
+   \param A pointer to intersected region
+   \param B pointer to intersected region (A and B must have same resolution)
+   \param [out] A_bounds rows and cols numbers of A stored in south, north, east, west, which defines intersection of A and B
+   \param [out] B_bounds rows and cols numbers of B stored in south, north, east, west, which defines intersection of A and B
+
+   \return  0 if interection exists
+   \return -1 if regions do not intersect
+   \return -2 resolution of regions is not same 
+*/
 static int get_rows_and_cols_bounds(struct Cell_head * A,  struct Cell_head * B, struct Cell_head * A_bounds,  struct Cell_head * B_bounds)
 {
     float ns_res, ew_res;
 
     struct Cell_head intersec;
 
-    if(A->ns_res != B->ns_res)
-        return -1;
+    /* TODO is it right check? */
+    if(abs(A->ns_res - B->ns_res) > GRASS_EPSILON) {
+        G_debug(0, "'get_rows_and_cols_bounds' ns_res does not fit, A->ns_res: %f B->ns_res: %f", A->ns_res, B->ns_res);
+        return -2;
+    }
 
-    if(A->ew_res != B->ew_res)
-        return -1;
+    if(abs(A->ew_res - B->ew_res) > GRASS_EPSILON) {
+        G_debug(0, "'get_rows_and_cols_bounds' ew_res does not fit, A->ew_res: %f B->ew_res: %f", A->ew_res, B->ew_res);
+        return -2;
+    }
 
     ns_res = A->ns_res;
     ew_res = A->ew_res;
@@ -137,7 +185,17 @@
     return 0;
 }
 
+/*!
+   \brief Insert raster map patch into pgm file.
+    Warning: calls Rast_set_window
 
+   \param patch_rast name of raster map
+   \param cat_rast_region region of category raster file
+   \param cat_rast path to category raster file
+
+   \return  0 on success
+   \return -1 on failure
+*/
 int I_insert_patch_to_cat_rast(const char * patch_rast, struct Cell_head * cat_rast_region,  const char * cat_rast)
 {
 
@@ -145,13 +203,12 @@
     struct Cell_head patch_region, patch_bounds, cat_rast_bounds;
     char cat_rast_header[1024];//TODO magic number 
     int i_row, i_col, ncols, nrows, cat_rast_col, patch_col, val;
-    int head_nchars;
+    int head_nchars, ret;
     int fd_patch_rast, init_shift, step_shift;
     unsigned char * patch_data;
 
-    char * null_chunk;
+    char * null_chunk_row;
 
-    //TODO G_free mapset (aslo in compute  scatts)
     const char *mapset;
 
     struct Cell_head patch_lines, cat_rast_lines;
@@ -159,16 +216,16 @@
     unsigned char * row_data;
 
     f_cat_rast = fopen(cat_rast, "rb+");
-    if(!f_cat_rast)
-        return -10;
+    if(!f_cat_rast) {
+        G_warning(_("Unable to open category raster condtions file <%s>."), cat_rast);
+        return -1;
+    }
 
-
-    /* TODO */
     head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
-
-    if ((mapset = G_find_raster2(patch_rast,"")) == NULL) {
+    if ((mapset = G_find_raster(patch_rast,"")) == NULL) {
         fclose(f_cat_rast);
-        return -2;
+        G_warning(_("Unable to find patch raster <%s>."), patch_rast);
+        return -1;
     }
 
     Rast_get_cellhd(patch_rast, mapset, &patch_region);
@@ -176,13 +233,26 @@
             
     if ((fd_patch_rast = Rast_open_old(patch_rast, mapset)) < 0) {
         fclose(f_cat_rast);
-        return -3;
+        return -1;
     }
 
-    null_chunk =  Rast_allocate_null_buf();
+    ret = get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds);
+    if(ret == -2) { 
+        G_warning(_("Resolutions of patch <%s> and patched file <%s> are not same."), patch_rast, cat_rast);
 
-    if(get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds) == -1) return -1;
+        Rast_close(fd_patch_rast);
+        fclose(f_cat_rast);
 
+        return -1;
+    }
+    else if (ret == -1){
+
+        Rast_close(fd_patch_rast);
+        fclose(f_cat_rast);
+
+        return 0;
+    }
+
     ncols = cat_rast_bounds.east - cat_rast_bounds.west;
     nrows = cat_rast_bounds.south - cat_rast_bounds.north;
 
@@ -191,19 +261,26 @@
     init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
 
     if(fseek(f_cat_rast, init_shift, SEEK_SET) != 0) {
-        G_message("seek failed");
-        return -4;
+        G_warning(_("Corrupted  category raster conditions file <%s> (fseek failed)"), cat_rast);
+
+        Rast_close(fd_patch_rast);
+        G_free(null_chunk_row);
+        fclose(f_cat_rast);
+
+        return -1;
     }
 
     step_shift = cat_rast_region->cols - ncols;
+
+    null_chunk_row =  Rast_allocate_null_buf();
     
     for(i_row = 0; i_row < nrows; i_row++) {
-        Rast_get_null_value_row (fd_patch_rast, null_chunk, i_row + patch_bounds.north);
+        Rast_get_null_value_row (fd_patch_rast, null_chunk_row, i_row + patch_bounds.north);
 
         for(i_col = 0; i_col < ncols; i_col++) {
             patch_col = patch_bounds.west + i_col;
 
-            if(null_chunk[patch_col] != 1) 
+            if(null_chunk_row[patch_col] != 1) 
                 patch_data[i_col] = 1 & 255;
             else {
                 patch_data[i_col] = 0 & 255;
@@ -213,78 +290,122 @@
         fwrite(patch_data, sizeof(unsigned char), (ncols)/sizeof(unsigned char), f_cat_rast);
         if (ferror(f_cat_rast))
         {
-            G_free(null_chunk);
+            G_warning(_("Unable to write into category raster conditions file <%s>"), cat_rast);
+            
+            Rast_close(fd_patch_rast);
+            G_free(null_chunk_row);
             fclose(f_cat_rast);
-            return -5;
+
+            return -1;
         }
         if(fseek(f_cat_rast, step_shift, SEEK_CUR) != 0) {
-            G_message("seek failed");
-            return -6;
+            G_warning(_("Corrupted  category raster conditions file <%s> (fseek failed)"), cat_rast);
+            
+            Rast_close(fd_patch_rast);
+            G_free(null_chunk_row);
+            fclose(f_cat_rast);
+            
+            return -1;
         }
     }
 
     Rast_close(fd_patch_rast);
-    G_free(null_chunk);
+    G_free(null_chunk_row);
     fclose(f_cat_rast);
     return 0;
 }
 
-static inline void update_cat_scatt_plt(CELL ** chunks, char ** null_chunks, int chunk_size, unsigned short * belongs_pix, struct scScatts * scatts)
+/*!
+   \brief Updates scatter plots data in category by pixels which meets category conditions.
+  
+   \param bands_rows data represents data describig one row from raster band
+   \param belongs_pix array which defines which pixels belongs to category (1 value) and which not (0 value)
+   \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, which are modified according to values in belongs_pix
+*/
+static inline void update_cat_scatt_plts(struct rast_row * bands_rows, unsigned short * belongs_pix, struct scScatts * scatts)
 {
-    int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunks_pix, max_arr_idx;
+    int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunk_rows_pix, max_arr_idx;
 
-    CELL * b_1_chunks;
-    CELL * b_2_chunks;
-    char * band_1_null_chunks,* band_2_null_chunks;
+    CELL * b_1_row;
+    CELL * b_2_row;
+    char * b_1_null_row,* b_2_null_row;
+    struct rast_row b_1_rast_row, b_2_rast_row;
 
     struct Range b_1_range, b_2_range;
     int b_1_range_size;
 
+    int row_size = Rast_window_cols();
+
     int * scatts_bands = scatts->scatts_bands;
 
     for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
     {   
-        b_1_chunks = chunks[scatts_bands[i_scatt * 2]];
-        b_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
+        b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
+        b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
 
-        band_1_null_chunks =  null_chunks[scatts_bands[i_scatt * 2]];
-        band_2_null_chunks =  null_chunks[scatts_bands[i_scatt * 2 + 1]];
+        b_1_row = b_1_rast_row.row;
+        b_2_row = b_2_rast_row.row;
 
-        b_1_range = scatts->scatts_arr[i_scatt]->band_1_range;
-        b_2_range = scatts->scatts_arr[i_scatt]->band_2_range;
+        b_1_null_row =  b_1_rast_row.null_row;
+        b_2_null_row =  b_2_rast_row.null_row;
 
+        b_1_range = b_1_rast_row.rast_range;
+        b_2_range = b_2_rast_row.rast_range;
+        
         b_1_range_size = b_1_range.max - b_1_range.min + 1;
         max_arr_idx = (b_1_range.max -  b_1_range.min + 1) * (b_2_range.max -  b_2_range.min + 1);
 
-        for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+        for(i_chunk_rows_pix = 0; i_chunk_rows_pix < row_size; i_chunk_rows_pix++)
         {
-            if(!belongs_pix[i_chunks_pix] || 
-                band_1_null_chunks[i_chunks_pix] == 1 || 
-                band_2_null_chunks[i_chunks_pix] == 1)                
+            /* pixel does not belongs to scatter plot or has null value in one of the bands */
+            if(!belongs_pix[i_chunk_rows_pix] || 
+                b_1_null_row[i_chunk_rows_pix] == 1 || 
+                b_2_null_row[i_chunk_rows_pix] == 1)                
                 continue;
 
-            array_idx = b_1_chunks[i_chunks_pix] - b_1_range.min + (b_2_chunks[i_chunks_pix] - b_2_range.min) * b_1_range_size;
+            /* index in scatter plot array */
+            array_idx = b_1_row[i_chunk_rows_pix] - b_1_range.min + (b_2_row[i_chunk_rows_pix] - b_2_range.min) * b_1_range_size;
 
             if(array_idx < 0 || array_idx >= max_arr_idx) {
-                G_warning ("pixel out of range");
+                        G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
                 continue;
             }
+
+            /* increment scatter plot value */
             ++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
         }
     }
 }
 
-static inline int compute_scatts_from_chunk(struct scCats * scatt_plts, struct scCats * scatt_conds, 
-                                            CELL ** chunks, char ** null_chunks, int chunk_size, FILE ** f_cats_rasts_in,
-                                            FILE ** f_cats_rasts_out, struct Cell_head *region, int i_chunk)
+/*!
+   \brief Computes scatter plots data from chunk_rows.
+
+   \param scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
+   \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are selected areas (condtitions)stored
+
+   \param chunk_rows data arrays of chunk_rows from analyzed raster bands (all data in chunk_rows, null_chunk_rows and belongs_pix arrays represents same region)
+   \param null_chunk_rows null data arrays of chunk_rows from analyzed raster bands
+   \param row_size size of data in chunk_rows, null_chunk_rows and belongs_pix arrays
+   \param f_cats_rasts_conds file which stores selected areas (conditions) from mapwindow see I_create_cat_rast() and I_pa
+   \param fd_cats_rasts array of openedraster maps which represents all selected pixels for category
+   \param region analysis region
+
+   \return  0 on success
+   \return -1 on failure
+*/
+static inline int compute_scatts_from_chunk_row(struct Cell_head *region, struct scCats * scatt_conds, 
+                                                FILE ** f_cats_rasts_conds, struct rast_row * bands_rows, 
+                                                struct scCats * scatts, int * fd_cats_rasts)
 {
 
-    int i_chunks_pix, i_cat, i_scatt, n_a_scatts, i_cond;
+    int i_rows_pix, i_cat, i_scatt, n_a_scatts, n_pixs;
     int cat_id, scatt_plts_cat_idx, array_idx, max_arr_idx;
-    char * band_1_null_chunks,* band_2_null_chunks;
-    
+    char * b_1_null_row,* b_2_null_row;
+    struct rast_row b_1_rast_row, b_2_rast_row;
+    CELL * cat_rast_row;
+
     struct scScatts * scatts_conds;
-    struct scScatts * scatt_plts_scatts;
+    struct scScatts * scatts_scatt_plts;
     struct scdScattData * conds;
 
     struct Range b_1_range, b_2_range;
@@ -293,116 +414,135 @@
     int * scatts_bands;
     struct scdScattData ** scatts_arr;
 
-    CELL * b_1_chunks;
-    CELL * b_2_chunks;
+    CELL * b_1_row;
+    CELL * b_2_row;
     unsigned char * i_scatt_conds;
 
-    unsigned short * belongs_pix = (unsigned short *) G_malloc(chunk_size * sizeof(unsigned short)); 
-    unsigned char * rast_pixs = (unsigned char *) G_malloc(chunk_size * sizeof(unsigned char));
+    int row_size = Rast_window_cols();
 
+    unsigned short * belongs_pix = (unsigned short *) G_malloc(row_size * sizeof(unsigned short)); 
+    unsigned char * rast_pixs = (unsigned char *) G_malloc(row_size * sizeof(unsigned char));
+    cat_rast_row =  Rast_allocate_c_buf();
+
+     
     for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
     {
         scatts_conds = scatt_conds->cats_arr[i_cat];
 
         cat_id = scatt_conds->cats_ids[i_cat];
 
-        scatt_plts_cat_idx = scatt_plts->cats_idxs[cat_id];
+        scatt_plts_cat_idx = scatts->cats_idxs[cat_id];
         if(scatt_plts_cat_idx < 0)
             continue;
+        scatts_scatt_plts = scatts->cats_arr[scatt_plts_cat_idx];
 
-        scatt_plts_scatts = scatt_plts->cats_arr[scatt_plts_cat_idx];
+        G_zero(belongs_pix, row_size * sizeof(unsigned short));
 
-        G_zero(belongs_pix, chunk_size * sizeof(unsigned short));
-
-        if(!scatts_conds->n_a_scatts && !f_cats_rasts_in[i_cat]) {
-            for(i_scatt = 0; i_scatt < scatt_plts_scatts->n_a_scatts; i_scatt++)
+        /* if category has no conditions defined, scatter plots without any constraint are computed (default scatter plots) */
+        if(!scatts_conds->n_a_scatts && !f_cats_rasts_conds[i_cat]) {
+            for(i_scatt = 0; i_scatt < scatts_scatt_plts->n_a_scatts; i_scatt++)
             {       
-                for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)                
-                    belongs_pix[i_chunks_pix] = 1;
+                /* all pixels belongs */
+                for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)                
+                    belongs_pix[i_rows_pix] = 1;
             }
         }
+        /* compute belonging pixels for defined conditions */
         else
         {
             scatts_bands = scatts_conds->scatts_bands;
 
-            if(f_cats_rasts_in[i_cat])
-         
-                fread(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_in[i_cat]);
-                if (ferror(f_cats_rasts_in[i_cat]))
+            /* check conditions from category raster condtitions file */
+            if(f_cats_rasts_conds[i_cat]) {
+                n_pixs = fread(rast_pixs, sizeof(unsigned char), (row_size)/sizeof(unsigned char), f_cats_rasts_conds[i_cat]);
+
+                if (ferror(f_cats_rasts_conds[i_cat]))
                 {
                     G_free(rast_pixs);
                     G_free(belongs_pix);
-                    G_message("Unable to read from file.");
+                    G_warning(_("Unable to read from category raster condtition file."));
                     return -1;
                 }
-                for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+                if (n_pixs != n_pixs) {
+                    G_free(rast_pixs);
+                    G_free(belongs_pix);
+                    G_warning(_("Invalid size of category raster conditions file."));
+                    return -1;
+
+                }
+
+                for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
                 {
-                    if(rast_pixs[i_chunks_pix] != 0 & 255)
-                        belongs_pix[i_chunks_pix] = 1;
+                    if(rast_pixs[i_rows_pix] != 0 & 255)
+                        belongs_pix[i_rows_pix] = 1;
                 }
+            }
 
-            // test every defined conditions in scatter plots
+            /* check condtions defined in scatter plots*/
             for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
             {   
-                b_1_chunks =  chunks[scatts_bands[i_scatt * 2]];
-                b_2_chunks =  chunks[scatts_bands[i_scatt * 2 + 1]];
+                b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
+                b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
 
-                band_1_null_chunks =  null_chunks[scatts_bands[i_scatt * 2]];
-                band_2_null_chunks =  null_chunks[scatts_bands[i_scatt * 2 + 1]];
+                b_1_row = b_1_rast_row.row;
+                b_2_row = b_2_rast_row.row;
 
-                i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
+                b_1_null_row =  b_1_rast_row.null_row;
+                b_2_null_row =  b_2_rast_row.null_row;
 
-                b_1_range = scatts_conds->scatts_arr[i_scatt]->band_1_range;
-                b_2_range = scatts_conds->scatts_arr[i_scatt]->band_2_range;
+                b_1_range = b_1_rast_row.rast_range;
+                b_2_range = b_2_rast_row.rast_range;
 
                 b_1_range_size = b_1_range.max - b_1_range.min + 1;
-
                 max_arr_idx = (b_1_range.max -  b_1_range.min + 1) * (b_2_range.max -  b_2_range.min + 1);
-                //G_message("max_arr_idx scatt_conds: %d", max_arr_idx);
 
-                for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+                i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
+
+                for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
                 {
-                    if(belongs_pix[i_chunks_pix] || 
-                       band_1_null_chunks[i_chunks_pix] == 1 || 
-                       band_2_null_chunks[i_chunks_pix] == 1)
+                    /* pixels already belongs to category from category raster conditions file or contains null value in one of the bands */
+                    if(belongs_pix[i_rows_pix] || 
+                       b_1_null_row[i_rows_pix] == 1 || 
+                       b_2_null_row[i_rows_pix] == 1)
                         continue;
 
-                    array_idx = b_1_chunks[i_chunks_pix] - b_1_range.min + (b_2_chunks[i_chunks_pix] - b_2_range.min) * b_1_range_size;
-                    /*TODO test value */
+                    array_idx = b_1_row[i_rows_pix] - b_1_range.min + (b_2_row[i_rows_pix] - b_2_range.min) * b_1_range_size;
                     if(array_idx < 0 || array_idx >= max_arr_idx) {
-                        G_warning ("pixel out of range");
+                        G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
                         continue;
                     }
+                    /* pixels meets condtion defined in scatter plot */
                     if(i_scatt_conds[array_idx])
-                        belongs_pix[i_chunks_pix] = 1;
+                        belongs_pix[i_rows_pix] = 1;
                 }                
             }
         }
 
-        if(f_cats_rasts_out[i_cat]) {
-            for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
-                rast_pixs[i_chunks_pix] = belongs_pix[i_chunks_pix]  & 255;
+        /* update category raster with belonging pixels */
+        if(fd_cats_rasts[i_cat] >= 0) {
+            Rast_set_null_value(cat_rast_row, Rast_window_cols(), CELL_TYPE); 
 
-            fwrite(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_out[i_cat]);
-            // TODO when code will be paralelized put it out of the loop
-            if (ferror(f_cats_rasts_out[i_cat]))
-            {
-                G_free(rast_pixs);
-                G_free(belongs_pix);
-                G_debug(3, "Unable to write into file.");
-                return -1;
-            }
+            for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
+                if(belongs_pix[i_rows_pix])
+                    cat_rast_row[i_rows_pix] = belongs_pix[i_rows_pix];
+
+            Rast_put_c_row (fd_cats_rasts[i_cat], cat_rast_row); 
         }
 
-        update_cat_scatt_plt(chunks, null_chunks, chunk_size, belongs_pix, scatt_plts_scatts);
+        /* update scatter plots with belonging pixels */
+        update_cat_scatt_plts(bands_rows, belongs_pix, scatts_scatt_plts);
     }
 
+    G_free(cat_rast_row);
     G_free(rast_pixs);
     G_free(belongs_pix);
 
     return 0;
 }
 
+/*!
+   \brief Get list if bands needed to be opened for analysis from scCats struct.
+*/
 static void get_needed_bands(struct scCats * cats, int * b_needed_bands)
 {
     // results in b_needed_bands - array of bools - if item has value 1, band (defined by item index) is needed to be opened
@@ -421,190 +561,338 @@
     return;
 }
 
-static void free_compute_scatts_data(int * fd_bands, CELL ** chunks, char ** null_chunks, int * n_a_bands, int * bands_ids, 
-                                     FILE ** f_cats_rasts_in, FILE ** f_cats_rasts_out, int n_a_cats)
+/*!
+   \brief Helper function for clean up.
+*/
+static void free_compute_scatts_data(int * fd_bands, struct rast_row * bands_rows, int n_a_bands, int * bands_ids, 
+                                     int * fd_cats_rasts, FILE ** f_cats_rasts_conds, int n_a_cats)
 {
     int i, band_id;
 
-    for(i = 0; i < (* n_a_bands); i++)
+    for(i = 0; i < n_a_bands; i++)
     {   
         band_id = bands_ids[i];
         if(band_id >= 0) {
             Rast_close(fd_bands[i]);
-            G_free(chunks[band_id]);
-            G_free(null_chunks[band_id]);
+            G_free(bands_rows[band_id].row);
+            G_free(bands_rows[band_id].null_row);
         }
     }
-
-    if(f_cats_rasts_in)
+     
+    if(f_cats_rasts_conds)
         for(i = 0; i < n_a_cats; i++)
-            if(f_cats_rasts_in[i])
-                fclose(f_cats_rasts_in[i]);
+            if(f_cats_rasts_conds[i])
+                fclose(f_cats_rasts_conds[i]);
 
-    if(f_cats_rasts_out)
+    if(fd_cats_rasts)
         for(i = 0; i < n_a_cats; i++)
-            if(f_cats_rasts_out[i])
-                fclose(f_cats_rasts_out[i]);
+            if(fd_cats_rasts[i] >= 0)
+                Rast_close(fd_cats_rasts[i]);
 
 }
 
+/*!
+   \brief Compute scatter plots data.
 
-static int open_cats_rasts_files(const char ** cats_rasts, int n_a_cats, int * cats_ids, const char * mode, FILE ** f_cats_rasts)
-{
-    int i_cat, id_cat;
+    If category has not defined no category raster condition file and no scatter plot with consdtion,
+    default scatter plot is computed.
+    Warning: calls Rast_set_window
 
-    for(i_cat = 0; i_cat < n_a_cats; i_cat++)
-    {
-        id_cat = cats_ids[i_cat];
+   \param region analysis region, beaware that all input data must be prepared for this region (bands (their ranges), cats_rasts_conds rasters...)
+   \param region function calls Rast_set_window for this region
+   \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are stored selected areas (conditions) in scatter plots
+   \param cats_rasts_conds paths to category raster conditions files representing selected areas (conditions) in rasters for every category 
+   \param cats_rasts_conds index in array represents corresponding category id
+   \param cats_rasts_conds for manupulation with category raster conditions file see also I_id_scatt_to_bands() and I_insert_patch_to_cat_rast()
+   \param bands names of analyzed bands, order of bands is defined by their id
+   \param n_bands number of bands
+   \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
+   \param [out] cats_rasts array of raster maps names where will be stored all selected pixels for every category
 
-        if(cats_rasts[id_cat]) {
-            f_cats_rasts[i_cat] = fopen(cats_rasts[id_cat], mode);
-            if(!f_cats_rasts[i_cat]) return -1;
-        }
-        else
-            f_cats_rasts[i_cat] = NULL;
-    }
-
-    return 0;
-}
-
-static int cats_rasts_write_header(int n_a_cats, struct Cell_head * region, FILE ** f_cats_rasts)
+   \return  0 on success
+   \return -1 on failure
+*/
+int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** cats_rasts_conds,
+                     const char ** bands, int n_bands, struct scCats * scatts, const char ** cats_rasts) 
 {
-    int i_cat, id_cat, head_nchars;
-    char cat_rast_header[1024];//TODO magic number 
-
-    head_nchars = get_cat_rast_header(region, cat_rast_header);
-
-    for(i_cat = 0; i_cat < n_a_cats; i_cat++)
-    {
-        if(!f_cats_rasts[i_cat])
-            continue;
-
-        fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cats_rasts[i_cat]);
-        if (ferror(f_cats_rasts[i_cat])) return -1;
-    }
-
-    return head_nchars;
-
-}
-
-//TODO change name: I_UpdateScattData???
-int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** bands, 
-                    int n_bands, struct scCats * scatt_plts, const char ** cats_rasts_in, const char ** cats_rasts_out) 
-{/*scatt_plts must be zeros! */
-    if (n_bands != scatt_plts->n_bands ||
-        n_bands != scatt_conds->n_bands)
-        return -1;
-
     const char *mapset;
+    char header[1024];
 
-    FILE * f_cats_rasts_out[scatt_conds->n_a_cats];
-    FILE * f_cats_rasts_in[scatt_conds->n_a_cats];
+    int fd_cats_rasts[scatt_conds->n_a_cats];
+    FILE * f_cats_rasts_conds[scatt_conds->n_a_cats];
 
-    CELL * chunks[n_bands];
-    char * null_chunks[n_bands];
+    struct rast_row bands_rows[n_bands];
 
     RASTER_MAP_TYPE data_type;
 
-    int nrows, i_band, id_band, n_a_bands, band_id, 
-        chunk_size, i, i_chunk, i_row, head_nchars, i_cat;
+    int nrows, i_band, n_a_bands, band_id, 
+        i, i_row, head_nchars, i_cat, id_cat;
+   
     int fd_bands[n_bands];
+    int bands_ids[n_bands];
+    int b_needed_bands[n_bands];  
+
+    Rast_set_window(region);
+
     for(i_band = 0; i_band < n_bands; i_band++)
         fd_bands[i_band] = -1;
     
-    int bands_ids[n_bands];
     for(i_band = 0; i_band < n_bands; i_band++)
         bands_ids[i_band] = -1;
 
-    int b_needed_bands[n_bands];  
+    if (n_bands != scatts->n_bands ||
+        n_bands != scatt_conds->n_bands)
+        return -1;
+
     memset(b_needed_bands, 0, (size_t)n_bands * sizeof(int));
 
     get_needed_bands(scatt_conds, &b_needed_bands[0]);
-    get_needed_bands(scatt_plts, &b_needed_bands[0]);
+    get_needed_bands(scatts, &b_needed_bands[0]);
 
-    Rast_set_window(region);
+    n_a_bands = 0;
 
-    n_a_bands = 0;/*TODO realy needed?*/
-    for(id_band = 0; id_band < n_bands; id_band++)
+    /* open band rasters, which are needed for computation */
+    for(band_id = 0; band_id < n_bands; band_id++)
     {
-        if(b_needed_bands[id_band])
+        if(b_needed_bands[band_id])
         {
-            G_debug(3, "Opening raster no. %d with name: %s", id_band, bands[id_band]);
+            G_debug(3, "Opening raster no. %d with name: %s", band_id, bands[band_id]);
 
-            /* TODO solve  returns*/
-            if ((mapset = G_find_raster2(bands[id_band],"")) == NULL) {
-                free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                                   &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
+            if ((mapset = G_find_raster2(bands[band_id],"")) == NULL) {
+                free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, 
+                                         bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+                G_warning(_("Unbale to read find raster <%s>"), bands[band_id]);
                 return -1;
             }
             
-            if ((fd_bands[n_a_bands] = Rast_open_old(bands[id_band], mapset)) < 0) {
-                free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                                   &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
+            if ((fd_bands[n_a_bands] = Rast_open_old(bands[band_id], mapset)) < 0) {
+                free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, 
+                                         bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+                G_warning(_("Unbale to open raster <%s>"), bands[band_id]);
                 return -1;
             }
 
-            //TODO check data type, minimum and maximum value
             data_type = Rast_get_map_type(fd_bands[n_a_bands]);
-            //if(data_type != CELL)
-            //    return -1;
+            if(data_type != CELL_TYPE) {
+                G_warning(_("Raster <%s> type is not <%s>"), bands[band_id], "CELL");
+                return -1;
+            }
 
-            //What happens if it is not CELL tyoe
-            chunks[id_band] =  Rast_allocate_c_buf();
-            null_chunks[id_band] =  Rast_allocate_null_buf();
+            bands_rows[band_id].row =  Rast_allocate_c_buf();
+            bands_rows[band_id].null_row =  Rast_allocate_null_buf();
 
-            bands_ids[n_a_bands] = id_band;
+            if(Rast_read_range(bands[band_id], mapset, &bands_rows[band_id].rast_range) != 1){
+                free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, 
+                                         bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+                G_warning(_("Unable to read range of raster <%s>"), bands[band_id]);
+                return -1;
+            }      
 
+            bands_ids[n_a_bands] = band_id;
             ++n_a_bands;
         }
     }
 
-    if (open_cats_rasts_files(cats_rasts_out, scatt_conds->n_a_cats, scatt_conds->cats_ids, "wb", f_cats_rasts_out) != 0) {
-        free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                                 &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
-        return -1;
-    }
+    /* open category rasters condition files and category rasters */
+    for(i_cat = 0; i_cat < scatts->n_a_cats; i_cat++)
+    {
+        id_cat = scatts->cats_ids[i_cat];
+        if(cats_rasts[id_cat]) {
+            fd_cats_rasts[i_cat] = Rast_open_new(cats_rasts[id_cat], CELL_TYPE);   
+        }
+        else
+            fd_cats_rasts[i_cat] = -1;
 
-    head_nchars = cats_rasts_write_header(scatt_conds->n_a_cats, region, f_cats_rasts_out);
-    if (head_nchars < 0) {
-        free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                                 &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
-        return -1;
+        if(cats_rasts_conds[id_cat]) {
+            f_cats_rasts_conds[i_cat] = fopen(cats_rasts_conds[id_cat], "r");
+            if(!f_cats_rasts_conds[i_cat]) {
+                free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, 
+                                         f_cats_rasts_conds, f_cats_rasts_conds, scatt_conds->n_a_cats);
+                G_warning(_("Unable to open category raster condtition file <%s>"), bands[band_id]);
+                return -1;
+            }
+        }
+        else
+            f_cats_rasts_conds[i_cat] = NULL;
     }
 
-    if (open_cats_rasts_files(cats_rasts_in, scatt_conds->n_a_cats, scatt_conds->cats_ids, "rb", f_cats_rasts_in) != 0) {
-        free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                                 &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
-        return -1;
-    }
-
+    head_nchars =  get_cat_rast_header(region, header);
     for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
-        if(f_cats_rasts_in[i_cat])
-            fseek(f_cats_rasts_in[i_cat] , head_nchars, SEEK_SET);
+        if(f_cats_rasts_conds[i_cat])
+            if( fseek(f_cats_rasts_conds[i_cat] , head_nchars, SEEK_SET) != 0) {
+                G_warning(_("Corrupted category raster conditions file (fseek failed)"));
+                return -1;
+            }
 
-    i_chunk = 0;
-
     nrows = Rast_window_rows();
-    chunk_size = Rast_window_cols();
 
+    /* analyze bands by rows */
     for (i_row = 0; i_row < nrows; i_row++)
     {
         for(i_band = 0; i_band < n_a_bands; i_band++)
         {   
             band_id = bands_ids[i_band];
-            G_debug(3, "Reading data for band %d", band_id);
-            Rast_get_c_row(fd_bands[i_band], chunks[band_id], i_row);
-            Rast_get_null_value_row (fd_bands[i_band], null_chunks[band_id], i_row);
+            Rast_get_c_row(fd_bands[i_band], bands_rows[band_id].row, i_row);
+            Rast_get_null_value_row (fd_bands[i_band], bands_rows[band_id].null_row, i_row);
         }
-        if(compute_scatts_from_chunk(scatt_plts, scatt_conds, chunks, null_chunks, chunk_size,  &f_cats_rasts_in[0], &f_cats_rasts_out[0], region, i_chunk) == -1) 
+        if(compute_scatts_from_chunk_row(region, scatt_conds, f_cats_rasts_conds, bands_rows, scatts, fd_cats_rasts) == -1) 
         {
-            free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                               &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
+            free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, fd_cats_rasts, 
+                                     f_cats_rasts_conds, scatt_conds->n_a_cats);
             return -1;
         }
  
     }
-    free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands, 
-                             &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats); 
+    free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, 
+                             fd_cats_rasts, f_cats_rasts_conds, scatt_conds->n_a_cats); 
     return 0;    
-}
\ No newline at end of file
+}
+
+/*!
+   \brief Merge arrays according to opacity.
+    Every pixel in array must be represented by 4 values (RGBA). 
+    
+    Implementd for speeding up of scatter plots rendering.
+
+   \param merged_arr array which will be overlayd with overlay_arr
+   \param overlay_arr array to be merged_arr overlayed with
+   \param rows number of rows for the both arrays
+   \param cols number of columns for the both arrays
+   \param alpha transparency of the overlay array for merging
+
+   \return  0
+*/
+int I_merge_arrays(unsigned char *  merged_arr, unsigned char *  overlay_arr, unsigned rows, unsigned cols, double alpha)
+{
+    unsigned int i_row, i_col, i_b;
+    unsigned int row_idx, col_idx, idx;
+    unsigned int c_a_i, c_a;
+
+    for(i_row = 0; i_row < rows; i_row++)
+    {
+        row_idx = i_row * cols;
+        for(i_col = 0; i_col < cols; i_col++)
+        {
+                col_idx = 4 * (row_idx + i_col);
+                idx = col_idx + 3;
+
+                c_a = overlay_arr[idx] * alpha;
+                c_a_i = 255 - c_a;
+
+                merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * 255) / 255;
+                
+                for(i_b = 0; i_b < 3; i_b++)
+                {
+                    idx = col_idx + i_b;
+                    merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * (int)overlay_arr[idx]) / 255;
+                }
+        }
+    }
+    return 0; 
+}
+
+/*!
+   \brief Apply colromap to the raster. 
+    
+    Implementd for speeding up of scatter plots rendering.
+
+   \param vals array of values for applying the colormap
+   \param vals_mask maks of vals array
+   \param nvals number of items of vals_mask and vals array
+   \param colmap coulour map to be applied
+   \param[out] col_vals output raster with applied color map (length is 4 * nvals (RGBA)) 
+
+   \return 0
+*/
+int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned nvals, unsigned char * colmap, unsigned char * col_vals){
+    unsigned int i_val;
+    int v, i, i_cm;
+
+    for(i_val = 0; i_val < nvals; i_val++){
+        i_cm = 4 * i_val;
+
+        v = vals[i_val];
+
+        if(vals_mask && vals_mask[i_val])
+            for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[258 * 4 + i];
+        else if(v > 255)
+            for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[257 * 4 + i];
+        else if(v < 0)
+            for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[256 * 4 + i];
+        else
+            for(i = 0; i < 4; i++){ col_vals[i_cm + i] = colmap[v * 4 + i];
+        }
+    }
+    return 0;
+}
+
+
+/*!
+   \brief Wrapper for using of iclass perimeter rasterization by scatter plot. 
+    Warning: calls Rast_set_window
+    
+   \param polygon array of polygon coordinates [x, y, x, y...]
+   \param pol_n_pts number of points in the polygon array
+   \param val value to be assigned to cells, which belong to plygon
+   \param rast_region region of raster
+   \param[out] rast raster to be pologyn rasterized in
+
+   \return 0 on success
+   \return 1 on failure
+*/
+
+int I_rasterize(double * polygon, int pol_n_pts, unsigned char val, struct Cell_head *rast_region, unsigned char * rast){
+    int i;
+    int x0, x1, y;
+    int row, row_idx, i_col;
+
+    IClass_perimeter perimeter;
+
+    struct line_pnts *pol; 
+    pol = Vect_new_line_struct();
+
+    for(i = 0; i < pol_n_pts; i++) {
+        Vect_append_point(pol,
+                          polygon[i*2], 
+                          polygon[i*2 + 1], 
+                          0.0);
+    }
+
+    Rast_set_window(rast_region);
+
+    make_perimeter(pol, &perimeter, rast_region);
+    for (i = 1; i < perimeter.npoints; i += 2) {
+        y = perimeter.points[i].y;
+        if (y != perimeter.points[i - 1].y) {
+            G_warning(_("prepare_signature: scan line %d has odd number of points."),
+                  (i + 1) / 2);
+            return 1;
+        }
+
+        x0 = perimeter.points[i - 1].x;
+        x1 = perimeter.points[i].x;
+
+        if (x0 > x1) {
+            G_warning(_("signature: perimeter points out of order."));
+            return 1;
+        }
+
+        row = (rast_region->rows - y);
+        if(row < 0 || row >= rast_region->rows) {
+            continue;
+        }
+
+        row_idx = rast_region->cols * row;
+
+        for(i_col = x0; i_col <= x1; i_col++) {
+            if(i_col < 0 || i_col >= rast_region->cols) {
+                continue;
+            }
+            rast[row_idx + i_col] = val;
+        }
+    }
+
+    Vect_destroy_line_struct(pol); 
+    G_free(perimeter.points);  
+    return 0;
+}

Modified: sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c	2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,10 +1,8 @@
 /*!
    \file lib/imagery/scatt_cat_rast.c
 
-   \brief Imagery library - functions for wx Scatter Plot Tool.
+   \brief Imagery library - functions for manipulation with scatter plot structs.
 
-   Low level functions used by wx Scatter Plot Tool.
-
    Copyright (C) 2013 by the GRASS Development Team
 
    This program is free software under the GNU General Public License
@@ -22,44 +20,84 @@
 #include <math.h>
 #include <string.h>
 
-//TODO split formulas to be more understandable
-static void id_scatt_to_bands(const int scatt_id, const int n_bands, int * band_1, int * band_2)
+/*!
+   \brief Compute band ids from scatter plot id.
+
+    Scatter plot id describes which bands defines the scatter plot.
+
+    Let say we have 3 bands, their ids are 0, 1 and 2.
+    Scatter plot with id 0 consists of band 1 (b_1_id) 0 and  band 2 (b_2_id) 1.
+    All scatter plots:
+    scatt_id b_1_id b_2_id
+    0        0      1
+    1        0      2
+    2        1      2
+
+   \param scatt_id scatter plot id
+   \param n_bands number of bands
+   \param [out] b_1_id id of band1
+   \param[out] b_2_id id of band2
+
+   \return 0
+ */
+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
 {   
     int n_b1 = n_bands - 1;
 
-    * band_1 = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
+    * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
 
-    * band_2 = scatt_id - ((* band_1) * (2 * n_b1 + 1) - (* band_1) * (* band_1)) / 2 + (* band_1) + 1;
+    * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
 
-    return;
+    return 0;
 }
 
-static void bands_to_id_scatt(const int band_1, const int band_2, const int n_bands, int * scatt_id)
+
+/*!
+   \brief Compute scatter plot id from band ids.
+
+    See also I_id_scatt_to_bands().
+
+   \param n_bands number of bands
+   \param b_1_id id of band1
+   \param b_1_id id of band2
+   \param [out] scatt_id scatter plot id
+
+   \return 0
+ */
+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
 {   
     int n_b1 = n_bands - 1;
 
-    * scatt_id = (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_2 - band_1 - 1;
+    * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
 
-    return;
+    return 0;
 }
 
+/*!
+   \brief Initialize structure for storing scatter plots data.
+
+   \param cats pointer to scCats struct 
+   \param n_bands number of bands
+   \param type SC_SCATT_DATA - stores scatter plots 
+   \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
+ */
 void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
 {
     int i_cat;
 
     cats->type = type;
 
-    cats->n_cats = 10;
+    cats->n_cats = 100;
     cats->n_a_cats = 0;
 
     cats->n_bands = n_bands;
     cats->n_scatts = (n_bands - 1) * n_bands / 2;
 
-    cats->cats_arr = (struct scScatts **) malloc(cats->n_cats * sizeof(struct scScatts *));
+    cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
     memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
 
-    cats->cats_ids = (int *)  malloc(cats->n_cats * sizeof(int));
-    cats->cats_idxs =(int *)  malloc(cats->n_cats * sizeof(int));
+    cats->cats_ids = (int *)  G_malloc(cats->n_cats * sizeof(int));
+    cats->cats_idxs =(int *)  G_malloc(cats->n_cats * sizeof(int));
 
     for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
         cats->cats_idxs[i_cat] = -1;
@@ -67,6 +105,11 @@
     return;
 }
 
+/*!
+   \brief Free data of struct scCats, the structure itself remains alocated.
+
+   \param cats pointer to existing scCats struct
+ */
 void I_sc_free_cats(struct scCats * cats)
 {
     int i_cat;
@@ -75,16 +118,16 @@
     {        
         if(cats->cats_arr[i_cat])
         {   
-            free(cats->cats_arr[i_cat]->scatt_idxs);
-            free(cats->cats_arr[i_cat]->scatts_bands);
-            free(cats->cats_arr[i_cat]->scatts_arr);
-            free(cats->cats_arr[i_cat]);
+            G_free(cats->cats_arr[i_cat]->scatt_idxs);
+            G_free(cats->cats_arr[i_cat]->scatts_bands);
+            G_free(cats->cats_arr[i_cat]->scatts_arr);
+            G_free(cats->cats_arr[i_cat]);
         }
     }
 
-    free(cats->cats_ids);
-    free(cats->cats_idxs);
-    free(cats->cats_arr);
+    G_free(cats->cats_ids);
+    G_free(cats->cats_idxs);
+    G_free(cats->cats_arr);
 
     cats->n_cats = 0;
     cats->n_a_cats = 0;
@@ -95,44 +138,60 @@
     return;
 }
 
+#if 0
 void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
 {
     a_cats_ids = cats->cats_ids;
     * n_a_cats = cats->n_a_cats;
 }
+#endif
 
-int I_sc_add_cat(struct scCats * cats, int cat_id)
+/*!
+   \brief Add category.
+    
+    Category represents group of scatter plots.
+
+   \param cats pointer to scCats struct
+
+   \return assigned category id (starts with 0)
+   \return -1 if maximum nuber of categories was reached
+ */
+int I_sc_add_cat(struct scCats * cats)
 {
-    int i_scatt;
+    int i_scatt, i_cat_id, cat_id;
     int n_a_cats = cats->n_a_cats;
 
-    if(cat_id < 0 || cat_id >= cats->n_cats)
+    if(cats->n_a_cats >= cats->n_cats)
         return -1;
 
-    if(cats->cats_idxs[cat_id] >= 0) 
-        return -1;
+    for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
+        if(cats->cats_idxs[i_cat_id] < 0) {
+            cat_id = i_cat_id;
+            break;
+        }
 
     cats->cats_ids[n_a_cats] = cat_id;
     cats->cats_idxs[cat_id] = n_a_cats;
     
-    cats->cats_arr[n_a_cats] = (struct scScatts *) malloc(sizeof(struct scScatts));
+    cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
 
-    cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) malloc(cats->n_scatts * sizeof(struct scdScattData *));
+    cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
     memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
     
     cats->cats_arr[n_a_cats]->n_a_scatts = 0;
 
-    cats->cats_arr[n_a_cats]->scatts_bands = (int *) malloc(cats->n_scatts * 2 * sizeof(int));
+    cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
      
-    cats->cats_arr[n_a_cats]->scatt_idxs = (int *) malloc(cats->n_scatts * sizeof(int));
+    cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
     for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
         cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
     
     ++cats->n_a_cats;
 
-    return 0;
+    return cat_id;
 }
 
+#if 0
 int I_sc_delete_cat(struct scCats * cats, int cat_id)
 {
     int cat_idx, i_cat;
@@ -144,10 +203,10 @@
     if(cat_idx < 0)
         return -1;
 
-    free(cats->cats_arr[cat_idx]->scatt_idxs);
-    free(cats->cats_arr[cat_idx]->scatts_bands);
-    free(cats->cats_arr[cat_idx]->scatts_arr);
-    free(cats->cats_arr[cat_idx]);
+    G_free(cats->cats_arr[cat_idx]->scatt_idxs);
+    G_free(cats->cats_arr[cat_idx]->scatts_bands);
+    G_free(cats->cats_arr[cat_idx]->scatts_arr);
+    G_free(cats->cats_arr[cat_idx]);
 
     for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
     {
@@ -160,8 +219,19 @@
     
     return 0;
 }
-    
+#endif
 
+/*!
+   \brief Insert scatter plot data .
+    Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
+
+   \param cats pointer to scCats struct
+   \param cat_id id number of category.
+   \param scatt_id id number of scatter plot.
+
+   \return  0 on success
+   \return -1 on failure
+ */
 int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
 {
     int band_1, band_2, cat_idx, n_a_scatts;
@@ -191,7 +261,7 @@
 
     scatts->scatt_idxs[scatt_id] = n_a_scatts;
 
-    id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
+    I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
 
     scatts->scatts_bands[n_a_scatts * 2] = band_1;
     scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
@@ -202,6 +272,7 @@
     return 0;
 }
 
+#if 0
 int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
 {
     int cat_idx, scatt_idx, n_init_scatts, i_scatt;
@@ -258,26 +329,26 @@
 
     return 0;
 }
+#endif
 
-void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data, 
-                           struct Range band_1_range, struct Range band_2_range)
+/*!
+   \brief Insert scatter plot data.
+    
+   \param scatt_data pointer to existing struct scdScattData
+   \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
+   \param n_vals number of data values
+   \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
+ */
+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
 {
     scatt_data->n_vals = n_vals;
 
-    scatt_data->band_1_range.min = band_1_range.min;
-    scatt_data->band_1_range.max = band_1_range.max;
-    scatt_data->band_1_range.first_time = 0;
-
-    scatt_data->band_2_range.min = band_2_range.min;
-    scatt_data->band_2_range.max = band_2_range.max;
-    scatt_data->band_2_range.first_time = 0;
-
     if(type == SC_SCATT_DATA)
     {   
         if(data)
             scatt_data->scatt_vals_arr = (unsigned int *) data;
         else {
-            scatt_data->scatt_vals_arr = (unsigned int *) malloc(n_vals * sizeof(unsigned int));
+            scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
             memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int)); 
         }
         scatt_data->b_conds_arr = NULL;
@@ -287,7 +358,7 @@
         if(data)
             scatt_data->b_conds_arr = (unsigned char *) data;
         else {
-            scatt_data->b_conds_arr = (unsigned char *) malloc(n_vals * sizeof(unsigned char));
+            scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
             memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
         }
         scatt_data->scatt_vals_arr = NULL;
@@ -296,17 +367,8 @@
     return;
 }
 
-void I_scd_free_scatt_data(struct scdScattData * scatt_data)
-{
 
-    free(scatt_data->b_conds_arr);
-    free(scatt_data->scatt_vals_arr);
-
-    scatt_data = NULL;
-
-    return;
-}
-
+#if 0
 void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
 {
 
@@ -315,7 +377,7 @@
 
     return;
 }
-
+s
 void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
 {
     if(!scatt_data->b_conds_arr)
@@ -340,3 +402,4 @@
 
     return 0;
 }
+#endif

Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff	2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/testing_patch.diff	2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,3 +1,413 @@
+Index: lib/imagery/scatt_sccats.c
+===================================================================
+--- lib/imagery/scatt_sccats.c	(revision 0)
++++ lib/imagery/scatt_sccats.c	(working copy)
+@@ -0,0 +1,405 @@
++/*!
++   \file lib/imagery/scatt_cat_rast.c
++
++   \brief Imagery library - functions for manipulation with scatter plot structs.
++
++   Copyright (C) 2013 by the GRASS Development Team
++
++   This program is free software under the GNU General Public License
++   (>=v2).  Read the file COPYING that comes with GRASS for details.
++
++   \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++/*!
++   \brief Compute band ids from scatter plot id.
++
++    Scatter plot id describes which bands defines the scatter plot.
++
++    Let say we have 3 bands, their ids are 0, 1 and 2.
++    Scatter plot with id 0 consists of band 1 (b_1_id) 0 and  band 2 (b_2_id) 1.
++    All scatter plots:
++    scatt_id b_1_id b_2_id
++    0        0      1
++    1        0      2
++    2        1      2
++
++   \param scatt_id scatter plot id
++   \param n_bands number of bands
++   \param [out] b_1_id id of band1
++   \param[out] b_2_id id of band2
++
++   \return 0
++ */
++int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
++{   
++    int n_b1 = n_bands - 1;
++
++    * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
++
++    * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
++
++    return 0;
++}
++
++
++/*!
++   \brief Compute scatter plot id from band ids.
++
++    See also I_id_scatt_to_bands().
++
++   \param n_bands number of bands
++   \param b_1_id id of band1
++   \param b_1_id id of band2
++   \param [out] scatt_id scatter plot id
++
++   \return 0
++ */
++int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
++{   
++    int n_b1 = n_bands - 1;
++
++    * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
++
++    return 0;
++}
++
++/*!
++   \brief Initialize structure for storing scatter plots data.
++
++   \param cats pointer to scCats struct 
++   \param n_bands number of bands
++   \param type SC_SCATT_DATA - stores scatter plots 
++   \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
++ */
++void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
++{
++    int i_cat;
++
++    cats->type = type;
++
++    cats->n_cats = 100;
++    cats->n_a_cats = 0;
++
++    cats->n_bands = n_bands;
++    cats->n_scatts = (n_bands - 1) * n_bands / 2;
++
++    cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
++    memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
++
++    cats->cats_ids = (int *)  G_malloc(cats->n_cats * sizeof(int));
++    cats->cats_idxs =(int *)  G_malloc(cats->n_cats * sizeof(int));
++
++    for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
++        cats->cats_idxs[i_cat] = -1;
++
++    return;
++}
++
++/*!
++   \brief Free data of struct scCats, the structure itself remains alocated.
++
++   \param cats pointer to existing scCats struct
++ */
++void I_sc_free_cats(struct scCats * cats)
++{
++    int i_cat;
++
++    for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++    {        
++        if(cats->cats_arr[i_cat])
++        {   
++            G_free(cats->cats_arr[i_cat]->scatt_idxs);
++            G_free(cats->cats_arr[i_cat]->scatts_bands);
++            G_free(cats->cats_arr[i_cat]->scatts_arr);
++            G_free(cats->cats_arr[i_cat]);
++        }
++    }
++
++    G_free(cats->cats_ids);
++    G_free(cats->cats_idxs);
++    G_free(cats->cats_arr);
++
++    cats->n_cats = 0;
++    cats->n_a_cats = 0;
++    cats->n_bands = 0;
++    cats->n_scatts = 0;
++    cats->type = -1;
++
++    return;
++}
++
++#if 0
++void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
++{
++    a_cats_ids = cats->cats_ids;
++    * n_a_cats = cats->n_a_cats;
++}
++#endif
++
++/*!
++   \brief Add category.
++    
++    Category represents group of scatter plots.
++
++   \param cats pointer to scCats struct
++
++   \return assigned category id (starts with 0)
++   \return -1 if maximum nuber of categories was reached
++ */
++int I_sc_add_cat(struct scCats * cats)
++{
++    int i_scatt, i_cat_id, cat_id;
++    int n_a_cats = cats->n_a_cats;
++
++    if(cats->n_a_cats >= cats->n_cats)
++        return -1;
++
++    for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
++        if(cats->cats_idxs[i_cat_id] < 0) {
++            cat_id = i_cat_id;
++            break;
++        }
++
++    cats->cats_ids[n_a_cats] = cat_id;
++    cats->cats_idxs[cat_id] = n_a_cats;
++    
++    cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
++
++    cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
++    memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
++    
++    cats->cats_arr[n_a_cats]->n_a_scatts = 0;
++
++    cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
++     
++    cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
++    for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
++        cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++    
++    ++cats->n_a_cats;
++
++    return cat_id;
++}
++
++#if 0
++int I_sc_delete_cat(struct scCats * cats, int cat_id)
++{
++    int cat_idx, i_cat;
++
++    if(cat_id < 0 || cat_id >= cats->n_cats)
++        return -1;
++
++    cat_idx = cats->cats_idxs[cat_id];
++    if(cat_idx < 0)
++        return -1;
++
++    G_free(cats->cats_arr[cat_idx]->scatt_idxs);
++    G_free(cats->cats_arr[cat_idx]->scatts_bands);
++    G_free(cats->cats_arr[cat_idx]->scatts_arr);
++    G_free(cats->cats_arr[cat_idx]);
++
++    for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
++    {
++        cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
++        cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
++    }
++    cats->cats_idxs[cat_id] = -1; 
++
++    --cats->n_a_cats;
++    
++    return 0;
++}
++#endif
++
++/*!
++   \brief Insert scatter plot data .
++    Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
++
++   \param cats pointer to scCats struct
++   \param cat_id id number of category.
++   \param scatt_id id number of scatter plot.
++
++   \return  0 on success
++   \return -1 on failure
++ */
++int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++    int band_1, band_2, cat_idx, n_a_scatts;
++    struct scScatts * scatts;
++
++    if(cat_id < 0 || cat_id >= cats->n_cats)
++        return -1;
++
++    cat_idx = cats->cats_idxs[cat_id];
++    if(cat_idx < 0)
++        return -1;
++
++    if(scatt_id < 0 && scatt_id >= cats->n_scatts)
++        return -1;
++
++    scatts = cats->cats_arr[cat_idx];
++    if(scatts->scatt_idxs[scatt_id] >= 0)
++        return -1;
++
++    if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
++        return -1;
++
++    if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
++        return -1;
++
++    n_a_scatts = scatts->n_a_scatts;
++
++    scatts->scatt_idxs[scatt_id] = n_a_scatts;
++
++    I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
++
++    scatts->scatts_bands[n_a_scatts * 2] = band_1;
++    scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
++
++    scatts->scatts_arr[n_a_scatts] = scatt_data;
++    ++scatts->n_a_scatts;
++
++    return 0;
++}
++
++#if 0
++int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++    int cat_idx, scatt_idx, n_init_scatts, i_scatt;
++    struct scScatts * scatts;
++
++    if(cat_id < 0 && cat_id >= cats->n_cats)
++        return -1;
++
++    cat_idx = cats->cats_idxs[cat_id];
++    if(cat_idx < 0)
++        return -1;
++
++    if(scatt_id < 0 || scatt_id >= cats->n_scatts)
++        return -1;
++
++    scatts = cats->cats_arr[cat_idx];
++    if(scatts->scatt_idxs[scatt_id] < 0)
++        return -1;
++
++    scatt_data = scatts->scatts_arr[scatt_idx];
++
++    for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
++    {
++        scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
++        scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
++        scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
++    }
++    scatts->scatts_arr[scatts->n_a_scatts] = NULL;
++
++    scatts->scatt_idxs[scatt_id] = -1;
++
++    scatt_data = scatts->scatts_arr[scatt_id];
++    scatts->n_a_scatts--;
++
++    return 0;
++}
++
++int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
++{
++    int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
++    int cat_idx, scatt_idx, ret;
++
++    cat_idx = cats->cats_idxs[cat_id];
++    if(cat_idx < 0)
++        return -1;
++    
++    if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
++        return -1;
++
++    cat_idx = cats->cats_idxs[cat_id];
++    scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
++
++    I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
++
++    return 0;
++}
++#endif
++
++/*!
++   \brief Insert scatter plot data.
++    
++   \param scatt_data pointer to existing struct scdScattData
++   \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
++   \param n_vals number of data values
++   \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
++ */
++void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
++{
++    scatt_data->n_vals = n_vals;
++
++    if(type == SC_SCATT_DATA)
++    {   
++        if(data)
++            scatt_data->scatt_vals_arr = (unsigned int *) data;
++        else {
++            scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
++            memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int)); 
++        }
++        scatt_data->b_conds_arr = NULL;
++    }
++    else if(type == SC_SCATT_CONDITIONS)
++    {
++        if(data)
++            scatt_data->b_conds_arr = (unsigned char *) data;
++        else {
++            scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
++            memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
++        }
++        scatt_data->scatt_vals_arr = NULL;
++    }
++
++    return;
++}
++
++
++#if 0
++void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
++{
++
++    Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
++    Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
++
++    return;
++}
++s
++void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
++{
++    if(!scatt_data->b_conds_arr)
++        return scatt_data->b_conds_arr;
++    else if(!scatt_data->scatt_vals_arr)
++        return scatt_data->scatt_vals_arr;
++
++    return NULL;
++}
++
++int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
++{
++    if(val_idx < 0 && val_idx >  scatt_data->n_vals)
++        return -1;
++
++    if(scatt_data->b_conds_arr)
++        scatt_data->b_conds_arr[val_idx] = val;
++    else if(scatt_data->scatt_vals_arr)
++        scatt_data->scatt_vals_arr[val_idx] = val;
++    else
++        return -1;
++
++    return 0;
++}
++#endif
 Index: lib/imagery/scatt.c
 ===================================================================
 --- lib/imagery/scatt.c	(revision 0)
@@ -2,3 +412,3 @@
 +++ lib/imagery/scatt.c	(working copy)
-@@ -0,0 +1,871 @@
+@@ -0,0 +1,898 @@
 +/*!
@@ -757,6 +1167,7 @@
 +   \brief Merge arrays according to opacity.
 +    Every pixel in array must be represented by 4 values (RGBA). 
 +    
++    Implementd for speeding up of scatter plots rendering.
 +
 +   \param merged_arr array which will be overlayd with overlay_arr
 +   \param overlay_arr array to be merged_arr overlayed with
@@ -795,12 +1206,24 @@
 +    return 0; 
 +}
 +
++/*!
++   \brief Apply colromap to the raster. 
++    
++    Implementd for speeding up of scatter plots rendering.
 +
-+int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned vals_size, unsigned char * colmap, unsigned char * col_vals){
++   \param vals array of values for applying the colormap
++   \param vals_mask maks of vals array
++   \param nvals number of items of vals_mask and vals array
++   \param colmap coulour map to be applied
++   \param[out] col_vals output raster with applied color map (length is 4 * nvals (RGBA)) 
++
++   \return 0
++*/
++int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned nvals, unsigned char * colmap, unsigned char * col_vals){
 +    unsigned int i_val;
 +    int v, i, i_cm;
 +
-+    for(i_val = 0; i_val < vals_size; i_val++){
++    for(i_val = 0; i_val < nvals; i_val++){
 +        i_cm = 4 * i_val;
 +
 +        v = vals[i_val];
@@ -819,7 +1242,21 @@
 +}
 +
 +
-+int I_rasterize(double * polygon, int pol_n_pts, unsigned char * rast, unsigned char val, struct Cell_head *rast_region){
++/*!
++   \brief Wrapper for using of iclass perimeter rasterization by scatter plot. 
++    Warning: calls Rast_set_window
++    
++   \param polygon array of polygon coordinates [x, y, x, y...]
++   \param pol_n_pts number of points in the polygon array
++   \param val value to be assigned to cells, which belong to plygon
++   \param rast_region region of raster
++   \param[out] rast raster to be pologyn rasterized in
++
++   \return 0 on success
++   \return 1 on failure
++*/
++
++int I_rasterize(double * polygon, int pol_n_pts, unsigned char val, struct Cell_head *rast_region, unsigned char * rast){
 +    int i;
 +    int x0, x1, y;
 +    int row, row_idx, i_col;
@@ -874,432 +1311,9 @@
 +    G_free(perimeter.points);  
 +    return 0;
 +}
-Index: lib/imagery/scatt_sccats.c
-===================================================================
---- lib/imagery/scatt_sccats.c	(revision 0)
-+++ lib/imagery/scatt_sccats.c	(working copy)
-@@ -0,0 +1,405 @@
-+/*!
-+   \file lib/imagery/scatt_cat_rast.c
-+
-+   \brief Imagery library - functions for manipulation with scatter plot structs.
-+
-+   Copyright (C) 2013 by the GRASS Development Team
-+
-+   This program is free software under the GNU General Public License
-+   (>=v2).  Read the file COPYING that comes with GRASS for details.
-+
-+   \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
-+ */
-+
-+#include <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/gis.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.h>
-+
-+/*!
-+   \brief Compute band ids from scatter plot id.
-+
-+    Scatter plot id describes which bands defines the scatter plot.
-+
-+    Let say we have 3 bands, their ids are 0, 1 and 2.
-+    Scatter plot with id 0 consists of band 1 (b_1_id) 0 and  band 2 (b_2_id) 1.
-+    All scatter plots:
-+    scatt_id b_1_id b_2_id
-+    0        0      1
-+    1        0      2
-+    2        1      2
-+
-+   \param scatt_id scatter plot id
-+   \param n_bands number of bands
-+   \param [out] b_1_id id of band1
-+   \param[out] b_2_id id of band2
-+
-+   \return 0
-+ */
-+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
-+{   
-+    int n_b1 = n_bands - 1;
-+
-+    * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
-+
-+    * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
-+
-+    return 0;
-+}
-+
-+
-+/*!
-+   \brief Compute scatter plot id from band ids.
-+
-+    See also I_id_scatt_to_bands().
-+
-+   \param n_bands number of bands
-+   \param b_1_id id of band1
-+   \param b_1_id id of band2
-+   \param [out] scatt_id scatter plot id
-+
-+   \return 0
-+ */
-+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
-+{   
-+    int n_b1 = n_bands - 1;
-+
-+    * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
-+
-+    return 0;
-+}
-+
-+/*!
-+   \brief Initialize structure for storing scatter plots data.
-+
-+   \param cats pointer to scCats struct 
-+   \param n_bands number of bands
-+   \param type SC_SCATT_DATA - stores scatter plots 
-+   \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
-+ */
-+void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
-+{
-+    int i_cat;
-+
-+    cats->type = type;
-+
-+    cats->n_cats = 100;
-+    cats->n_a_cats = 0;
-+
-+    cats->n_bands = n_bands;
-+    cats->n_scatts = (n_bands - 1) * n_bands / 2;
-+
-+    cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
-+    memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
-+
-+    cats->cats_ids = (int *)  G_malloc(cats->n_cats * sizeof(int));
-+    cats->cats_idxs =(int *)  G_malloc(cats->n_cats * sizeof(int));
-+
-+    for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
-+        cats->cats_idxs[i_cat] = -1;
-+
-+    return;
-+}
-+
-+/*!
-+   \brief Free data of struct scCats, the structure itself remains alocated.
-+
-+   \param cats pointer to existing scCats struct
-+ */
-+void I_sc_free_cats(struct scCats * cats)
-+{
-+    int i_cat;
-+
-+    for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
-+    {        
-+        if(cats->cats_arr[i_cat])
-+        {   
-+            G_free(cats->cats_arr[i_cat]->scatt_idxs);
-+            G_free(cats->cats_arr[i_cat]->scatts_bands);
-+            G_free(cats->cats_arr[i_cat]->scatts_arr);
-+            G_free(cats->cats_arr[i_cat]);
-+        }
-+    }
-+
-+    G_free(cats->cats_ids);
-+    G_free(cats->cats_idxs);
-+    G_free(cats->cats_arr);
-+
-+    cats->n_cats = 0;
-+    cats->n_a_cats = 0;
-+    cats->n_bands = 0;
-+    cats->n_scatts = 0;
-+    cats->type = -1;
-+
-+    return;
-+}
-+
-+#if 0
-+void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
-+{
-+    a_cats_ids = cats->cats_ids;
-+    * n_a_cats = cats->n_a_cats;
-+}
-+#endif
-+
-+/*!
-+   \brief Add category.
-+    
-+    Category represents group of scatter plots.
-+
-+   \param cats pointer to scCats struct
-+
-+   \return assigned category id (starts with 0)
-+   \return -1 if maximum nuber of categories was reached
-+ */
-+int I_sc_add_cat(struct scCats * cats)
-+{
-+    int i_scatt, i_cat_id, cat_id;
-+    int n_a_cats = cats->n_a_cats;
-+
-+    if(cats->n_a_cats >= cats->n_cats)
-+        return -1;
-+
-+    for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
-+        if(cats->cats_idxs[i_cat_id] < 0) {
-+            cat_id = i_cat_id;
-+            break;
-+        }
-+
-+    cats->cats_ids[n_a_cats] = cat_id;
-+    cats->cats_idxs[cat_id] = n_a_cats;
-+    
-+    cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
-+
-+    cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
-+    memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
-+    
-+    cats->cats_arr[n_a_cats]->n_a_scatts = 0;
-+
-+    cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
-+     
-+    cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
-+    for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
-+        cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
-+    
-+    ++cats->n_a_cats;
-+
-+    return cat_id;
-+}
-+
-+#if 0
-+int I_sc_delete_cat(struct scCats * cats, int cat_id)
-+{
-+    int cat_idx, i_cat;
-+
-+    if(cat_id < 0 || cat_id >= cats->n_cats)
-+        return -1;
-+
-+    cat_idx = cats->cats_idxs[cat_id];
-+    if(cat_idx < 0)
-+        return -1;
-+
-+    G_free(cats->cats_arr[cat_idx]->scatt_idxs);
-+    G_free(cats->cats_arr[cat_idx]->scatts_bands);
-+    G_free(cats->cats_arr[cat_idx]->scatts_arr);
-+    G_free(cats->cats_arr[cat_idx]);
-+
-+    for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
-+    {
-+        cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
-+        cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
-+    }
-+    cats->cats_idxs[cat_id] = -1; 
-+
-+    --cats->n_a_cats;
-+    
-+    return 0;
-+}
-+#endif
-+
-+/*!
-+   \brief Insert scatter plot data .
-+    Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
-+
-+   \param cats pointer to scCats struct
-+   \param cat_id id number of category.
-+   \param scatt_id id number of scatter plot.
-+
-+   \return  0 on success
-+   \return -1 on failure
-+ */
-+int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+    int band_1, band_2, cat_idx, n_a_scatts;
-+    struct scScatts * scatts;
-+
-+    if(cat_id < 0 || cat_id >= cats->n_cats)
-+        return -1;
-+
-+    cat_idx = cats->cats_idxs[cat_id];
-+    if(cat_idx < 0)
-+        return -1;
-+
-+    if(scatt_id < 0 && scatt_id >= cats->n_scatts)
-+        return -1;
-+
-+    scatts = cats->cats_arr[cat_idx];
-+    if(scatts->scatt_idxs[scatt_id] >= 0)
-+        return -1;
-+
-+    if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
-+        return -1;
-+
-+    if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
-+        return -1;
-+
-+    n_a_scatts = scatts->n_a_scatts;
-+
-+    scatts->scatt_idxs[scatt_id] = n_a_scatts;
-+
-+    I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
-+
-+    scatts->scatts_bands[n_a_scatts * 2] = band_1;
-+    scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
-+
-+    scatts->scatts_arr[n_a_scatts] = scatt_data;
-+    ++scatts->n_a_scatts;
-+
-+    return 0;
-+}
-+
-+#if 0
-+int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+    int cat_idx, scatt_idx, n_init_scatts, i_scatt;
-+    struct scScatts * scatts;
-+
-+    if(cat_id < 0 && cat_id >= cats->n_cats)
-+        return -1;
-+
-+    cat_idx = cats->cats_idxs[cat_id];
-+    if(cat_idx < 0)
-+        return -1;
-+
-+    if(scatt_id < 0 || scatt_id >= cats->n_scatts)
-+        return -1;
-+
-+    scatts = cats->cats_arr[cat_idx];
-+    if(scatts->scatt_idxs[scatt_id] < 0)
-+        return -1;
-+
-+    scatt_data = scatts->scatts_arr[scatt_idx];
-+
-+    for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
-+    {
-+        scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
-+        scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
-+        scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
-+    }
-+    scatts->scatts_arr[scatts->n_a_scatts] = NULL;
-+
-+    scatts->scatt_idxs[scatt_id] = -1;
-+
-+    scatt_data = scatts->scatts_arr[scatt_id];
-+    scatts->n_a_scatts--;
-+
-+    return 0;
-+}
-+
-+int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
-+{
-+    int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
-+    int cat_idx, scatt_idx, ret;
-+
-+    cat_idx = cats->cats_idxs[cat_id];
-+    if(cat_idx < 0)
-+        return -1;
-+    
-+    if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
-+        return -1;
-+
-+    cat_idx = cats->cats_idxs[cat_id];
-+    scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
-+
-+    I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
-+
-+    return 0;
-+}
-+#endif
-+
-+/*!
-+   \brief Insert scatter plot data.
-+    
-+   \param scatt_data pointer to existing struct scdScattData
-+   \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
-+   \param n_vals number of data values
-+   \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
-+ */
-+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
-+{
-+    scatt_data->n_vals = n_vals;
-+
-+    if(type == SC_SCATT_DATA)
-+    {   
-+        if(data)
-+            scatt_data->scatt_vals_arr = (unsigned int *) data;
-+        else {
-+            scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
-+            memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int)); 
-+        }
-+        scatt_data->b_conds_arr = NULL;
-+    }
-+    else if(type == SC_SCATT_CONDITIONS)
-+    {
-+        if(data)
-+            scatt_data->b_conds_arr = (unsigned char *) data;
-+        else {
-+            scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
-+            memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
-+        }
-+        scatt_data->scatt_vals_arr = NULL;
-+    }
-+
-+    return;
-+}
-+
-+
-+#if 0
-+void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
-+{
-+
-+    Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
-+    Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
-+
-+    return;
-+}
-+s
-+void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
-+{
-+    if(!scatt_data->b_conds_arr)
-+        return scatt_data->b_conds_arr;
-+    else if(!scatt_data->scatt_vals_arr)
-+        return scatt_data->scatt_vals_arr;
-+
-+    return NULL;
-+}
-+
-+int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
-+{
-+    if(val_idx < 0 && val_idx >  scatt_data->n_vals)
-+        return -1;
-+
-+    if(scatt_data->b_conds_arr)
-+        scatt_data->b_conds_arr[val_idx] = val;
-+    else if(scatt_data->scatt_vals_arr)
-+        scatt_data->scatt_vals_arr[val_idx] = val;
-+    else
-+        return -1;
-+
-+    return 0;
-+}
-+#endif
-Index: include/defs/vedit.h
-===================================================================
---- include/defs/vedit.h	(revision 57760)
-+++ 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 57760)
+--- include/defs/imagery.h	(revision 57820)
 +++ include/defs/imagery.h	(working copy)
 @@ -111,6 +111,28 @@
  FILE *I_fopen_subgroup_ref_new(const char *, const char *);
@@ -1325,14 +1339,14 @@
 +
 +int I_merge_arrays(unsigned char *, unsigned char *, unsigned, unsigned, double);
 +int I_apply_colormap(unsigned char *, unsigned char *, unsigned,  unsigned char *, unsigned char *);
-+int I_rasterize(double *, int, unsigned char *, unsigned char, struct Cell_head *);
++int I_rasterize(double *, int, unsigned char, struct Cell_head *, unsigned char *);
 +
  /* sig.c */
  int I_init_signatures(struct Signature *, int);
  int I_new_signature(struct Signature *);
 Index: include/imagery.h
 ===================================================================
---- include/imagery.h	(revision 57760)
+--- include/imagery.h	(revision 57820)
 +++ include/imagery.h	(working copy)
 @@ -135,6 +135,56 @@
      
@@ -1391,560 +1405,65 @@
  #define SIGNATURE_TYPE_MIXED 1
  
  #define GROUPFILE "CURGROUP"
-Index: gui/wxpython/vdigit/wxdigit.py
+Index: gui/wxpython/mapdisp/frame.py
 ===================================================================
---- gui/wxpython/vdigit/wxdigit.py	(revision 57760)
-+++ gui/wxpython/vdigit/wxdigit.py	(working copy)
-@@ -17,7 +17,7 @@
- (and NumPy would be an excellent candidate for acceleration via
- e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+--- gui/wxpython/mapdisp/frame.py	(revision 57820)
++++ gui/wxpython/mapdisp/frame.py	(working copy)
+@@ -231,6 +231,7 @@
+         #
+         self.dialogs = {}
+         self.dialogs['attributes'] = None
++        self.dialogs['scatt_plot'] = None
+         self.dialogs['category'] = None
+         self.dialogs['vnet'] = None
+         self.dialogs['query'] = None
+@@ -1199,6 +1200,19 @@
+         """!Returns toolbar with zooming tools"""
+         return self.toolbars['map']
  
--(C) 2007-2011 by the GRASS Development Team
-+(C) 2007-2011, 2013 by the GRASS Development Team
- 
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
-@@ -27,6 +27,8 @@
- 
- import grass.script.core as grass
- 
-+from grass.pydispatch.signal import Signal
-+
- from core.gcmd        import GError
- from core.debug       import Debug
- from core.settings    import UserSettings
-@@ -176,7 +178,21 @@
-         
-         if self.poMapInfo:
-             self.InitCats()
--        
-+
-+        self.emit_signals = False
-+
-+        # signals which describes features changes during digitization, 
-+        # activate them using EmitSignals method 
-+        #TODO handle errors?
-+        self.featureAdded = Signal('IVDigit.featureAdded')
-+        self.areasDeleted = Signal('IVDigit.areasDeleted')
-+        self.vertexMoved = Signal('IVDigit.vertexMoved')
-+        self.vertexAdded = Signal('IVDigit.vertexAdded')
-+        self.vertexRemoved = Signal('IVDigit.vertexRemoved')
-+        self.featuresDeleted = Signal('IVDigit.featuresDeleted')
-+        self.featuresMoved = Signal('IVDigit.featuresMoved')
-+        self.lineEdited = Signal('IVDigit.lineEdited')
-+
-     def __del__(self):
-         Debug.msg(1, "IVDigit.__del__()")
-         Vect_destroy_line_struct(self.poPoints)
-@@ -188,7 +204,12 @@
-             Vect_close(self.poBgMapInfo)
-             self.poBgMapInfo = self.popoBgMapInfo = None
-             del self.bgMapInfo
--        
-+     
-+    def EmitSignals(self, emit):
-+        """!Activate/deactivate signals which describes features changes during digitization.
++    def OnIScatt(self, event):
++        """!Init interactive scatterplot tools
 +        """
-+        self.emit_signals = emit
++        if self.dialogs['scatt_plot']:
++            self.dialogs['scatt_plot'].Raise()
++            return
 +
-     def CloseBackgroundMap(self):
-         """!Close background vector map"""
-         if not self.poBgMapInfo:
-@@ -394,7 +415,6 @@
-         
-         @return tuple (number of added features, feature ids)
-         """
--        
-         layer = self._getNewFeaturesLayer()
-         cat = self._getNewFeaturesCat()
-         
-@@ -419,10 +439,15 @@
-             return (-1, None)
-         
-         self.toolbar.EnableUndo()
--        
--        return self._addFeature(vtype, points, layer, cat,
--                                self._getSnapMode(), self._display.GetThreshold())
--    
-+
-+        ret = self._addFeature(vtype, points, layer, cat,
-+                               self._getSnapMode(), self._display.GetThreshold())
-+        if ret[0] > -1 and self.emit_signals:
-+            self.featureAdded.emit(new_bboxs=[self._createBbox(points)], 
-+                                   new_areas_cats=[[{layer : [cat]}, None]])
-+
-+        return ret
-+
-     def DeleteSelectedLines(self):
-         """!Delete selected features
- 
-@@ -434,16 +459,27 @@
-         # collect categories for deleting if requested
-         deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
-         catDict = dict()
-+
-+        old_bboxs = []
-+        old_areas_cats = []
-         if deleteRec:
-             for i in self._display.selected['ids']:
-+                
-                 if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
-                     self._error.ReadLine(i)
-                 
--                cats = self.poCats.contents
--                for j in range(cats.n_cats):
--                    if cats.field[j] not in catDict.keys():
--                        catDict[cats.field[j]] = list()
--                    catDict[cats.field[j]].append(cats.cat[j])
-+                if self.emit_signals:
-+                    ret = self._getLineAreaBboxCats(i)
-+                    if ret:
-+                        old_bboxs += ret[0]
-+                        old_areas_cats += ret[1]
-+                
-+                # catDict was not used -> put into comment
-+                #cats = self.poCats.contents
-+                #for j in range(cats.n_cats):
-+                #    if cats.field[j] not in catDict.keys():
-+                #        catDict[cats.field[j]] = list()
-+                #    catDict[cats.field[j]].append(cats.cat[j])
-         
-         poList = self._display.GetSelectedIList()
-         nlines = Vedit_delete_lines(self.poMapInfo, poList)
-@@ -456,7 +492,11 @@
-                 self._deleteRecords(catDict)
-             self._addChangeset()
-             self.toolbar.EnableUndo()
--        
-+
-+            if self.emit_signals:
-+                self.featuresDeleted.emit(old_bboxs=old_bboxs, 
-+                                          old_areas_cats=old_areas_cats)
-+
-         return nlines
-             
-     def _deleteRecords(self, cats):
-@@ -512,22 +552,173 @@
- 
-         @return number of deleted 
-         """
-+        if len(self._display.selected['ids']) < 1:
-+            return 0
++        from scatt_plot.frame import IScattDialog
++        self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
 +        
-         poList = self._display.GetSelectedIList()
-         cList  = poList.contents
-         
-         nareas = 0
-+        old_bboxs = []
-+        old_areas_cats = []
++        self.dialogs['scatt_plot'].CenterOnScreen()
++        self.dialogs['scatt_plot'].Show()
 +
-         for i in range(cList.n_values):
-+
-             if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
-                 continue
--            
-+
-+            if self.emit_signals:
-+                area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
-+                if area > 0: 
-+                    bbox, cats = self._getaAreaBboxCats(area)
-+                    old_bboxs += bbox
-+                    old_areas_cats += cats
-+
-             nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
-         
-         if nareas > 0:
-             self._addChangeset()
-             self.toolbar.EnableUndo()
-+            if self.emit_signals:
-+                self.areasDeleted.emit(old_bboxs=old_bboxs, 
-+                                       old_areas_cats=old_areas_cats)        
-+
-+        return nareas
-+   
-+    def _getLineAreaBboxCats(self, ln_id):
-+        ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+
-+        if ltype == GV_CENTROID:
-+            #TODO centroid opttimization, can be edited also its area -> it will appear two times in new_ lists
-+            return self._getCentroidAreaBboxCats(ln_id)
-+        else: 
-+            return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
-+
-+
-+    def _getCentroidAreaBboxCats(self, centroid):
-+        if not Vect_line_alive(self.poMapInfo, centroid):
-+            return None
-+
-+        area = Vect_get_centroid_area(self.poMapInfo, centroid)  
-+        if area > 0:
-+            return self._getaAreaBboxCats(area)
-+        else:
-+            return None
-+
-+    def _getaAreaBboxCats(self, area):
-+        po_b_list = Vect_new_list()
-+        Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
-+        b_list = po_b_list.contents
-+
-+        geoms = []
-+        areas_cats = []
-+
-+        if b_list.n_values > 0:
-+            for i_line in range(b_list.n_values):
-+
-+                line = b_list.value[i_line];
-+
-+                geoms.append(self._getBbox(abs(line)))
-+                areas_cats.append(self._getLineAreasCategories(abs(line)))
-         
--        return nareas
-+        Vect_destroy_list(po_b_list);
-+
-+        return geoms, areas_cats
-+
-+    def _getLineAreasCategories(self, ln_id):
-+        if not Vect_line_alive (self.poMapInfo, ln_id):
-+            return []
-+
-+        ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+        if ltype != GV_BOUNDARY:
-+            return []
-+
-+        cats = [None, None]
-+
-+        left = c_int()
-+        right = c_int()
-+
-+        if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
-+            areas = [left.value, right.value]
-+
-+            for i, a in enumerate(areas):
-+                if a > 0: 
-+                    centroid = Vect_get_area_centroid(self.poMapInfo, a)
-+                    if centroid <= 0:
-+                        continue
-+                    c = self._getCategories(centroid)
-+                    if c:
-+                        cats[i] = c
-+
-+        return cats
-+
-+    def _getCategories(self, ln_id):
-+        if not Vect_line_alive (self.poMapInfo, ln_id):
-+            return none
-+
-+        poCats = Vect_new_cats_struct()
-+        if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
-+            Vect_destroy_cats_struct(poCats)
-+            return None
-+
-+        cCats = poCats.contents
-+
-+        cats = {}
-+        for j in range(cCats.n_cats):
-+            if cats.has_key(cCats.field[j]):
-+                cats[cCats.field[j]].append(cCats.cat[j])
-+            else:
-+                cats[cCats.field[j]] = [cCats.cat[j]]
-     
-+        Vect_destroy_cats_struct(poCats)
-+        return cats
-+
-+    def _getBbox(self, ln_id):
-+        if not Vect_line_alive (self.poMapInfo, ln_id):
-+            return None
-+
-+        poPoints = Vect_new_line_struct()
-+        if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
-+            Vect_destroy_line_struct(poPoints)
-+            return []
-+
-+        geom = self._convertGeom(poPoints)
-+        bbox = self._createBbox(geom)
-+        Vect_destroy_line_struct(poPoints)
-+        return bbox
-+
-+    def _createBbox(self, points):
-+
-+        bbox = {}
-+        for pt in points:
-+            if not bbox.has_key('maxy'):
-+                bbox['maxy'] = pt[1]
-+                bbox['miny'] = pt[1]
-+                bbox['maxx'] = pt[0]
-+                bbox['minx'] = pt[0]
-+                continue
-+                
-+            if   bbox['maxy'] < pt[1]:
-+                bbox['maxy'] = pt[1]
-+            elif bbox['miny'] > pt[1]:
-+                bbox['miny'] = pt[1]
-+                
-+            if   bbox['maxx'] < pt[0]:
-+                bbox['maxx'] = pt[0]
-+            elif bbox['minx'] > pt[0]:
-+                bbox['minx'] = pt[0]
-+        return bbox
-+
-+    def _convertGeom(self, poPoints):
-+
-+        Points = poPoints.contents
-+
-+        pts_geom = []
-+        for j in range(Points.n_points):
-+            pts_geom.append((Points.x[j], Points.y[j]))
-+
-+        return pts_geom
-+
-     def MoveSelectedLines(self, move):
-         """!Move selected features
- 
-@@ -536,16 +727,45 @@
-         if not self._checkMap():
-             return -1
-         
-+        nsel = len(self._display.selected['ids'])
-+        if nsel < 1:
-+            return -1   
-+        
-         thresh = self._display.GetThreshold()
-         snap   = self._getSnapMode()
-         
-         poList = self._display.GetSelectedIList()
-+
-+        if self.emit_signals:
-+            old_bboxs = []
-+            old_areas_cats = []
-+            for sel_id in self._display.selected['ids']:
-+                ret = self._getLineAreaBboxCats(sel_id)
-+                if ret:
-+                    old_bboxs += ret[0]
-+                    old_areas_cats += ret[1]
-+        
-+            Vect_set_updated(self.poMapInfo, 1)
-+            n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+        
-         nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
-                                   poList,
-                                   move[0], move[1], 0,
-                                   snap, thresh)
-+
-         Vect_destroy_list(poList)
--        
-+
-+        if nlines > 0 and self.emit_signals:
-+            new_bboxs = []
-+            new_areas_cats = []
-+            n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+            for i in range(n_up_lines_old, n_up_lines):
-+                new_id = Vect_get_updated_line(self.poMapInfo, i)
-+                ret = self._getLineAreaBboxCats(new_id)
-+                if ret:
-+                    new_bboxs += ret[0]
-+                    new_areas_cats += ret[1]
-+
-         if nlines > 0 and self._settings['breakLines']:
-             for i in range(1, nlines):
-                 self._breakLineAtIntersection(nlines + i, None, changeset)
-@@ -553,7 +773,13 @@
-         if nlines > 0:
-             self._addChangeset()
-             self.toolbar.EnableUndo()
--        
-+            
-+            if self.emit_signals:
-+                self.featuresMoved.emit(new_bboxs=new_bboxs,
-+                                        old_bboxs=old_bboxs, 
-+                                        old_areas_cats=old_areas_cats, 
-+                                        new_areas_cats=new_areas_cats)
-+
-         return nlines
- 
-     def MoveSelectedVertex(self, point, move):
-@@ -571,12 +797,21 @@
-         
-         if len(self._display.selected['ids']) != 1:
-             return -1
--        
-+
-+        # move only first found vertex in bbox 
-+        poList = self._display.GetSelectedIList()
-+
-+        if self.emit_signals:
-+            cList = poList.contents
-+            old_bboxs = [self._getBbox(cList.value[0])]
-+            old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+            Vect_set_updated(self.poMapInfo, 1)
-+            n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
-         Vect_reset_line(self.poPoints)
-         Vect_append_point(self.poPoints, point[0], point[1], 0.0)
--        
--        # move only first found vertex in bbox 
--        poList = self._display.GetSelectedIList()
-+
-         moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
-                                   poList, self.poPoints,
-                                   self._display.GetThreshold(type = 'selectThresh'),
-@@ -584,7 +819,17 @@
-                                   move[0], move[1], 0.0,
-                                   1, self._getSnapMode())
-         Vect_destroy_list(poList)
--        
-+
-+        if moved > 0 and self.emit_signals:
-+            n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+
-+            new_bboxs = []
-+            new_areas_cats = []
-+            for i in range(n_up_lines_old, n_up_lines):
-+                new_id = Vect_get_updated_line(self.poMapInfo, i)
-+                new_bboxs.append(self._getBbox(new_id))
-+                new_areas_cats.append(self._getLineAreasCategories(new_id))
-+
-         if moved > 0 and self._settings['breakLines']:
-             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
-                                           None)
-@@ -592,7 +837,13 @@
-         if moved > 0:
-             self._addChangeset()
-             self.toolbar.EnableUndo()
--        
-+
-+            if self.emit_signals:
-+                self.vertexMoved.emit(new_bboxs=new_bboxs,  
-+                                      new_areas_cats=new_areas_cats, 
-+                                      old_areas_cats=old_areas_cats, 
-+                                      old_bboxs=old_bboxs)
-+
-         return moved
- 
-     def AddVertex(self, coords):
-@@ -681,6 +932,10 @@
-             self._error.ReadLine(line)
-             return -1
-         
-+        if self.emit_signals:
-+            old_bboxs = [self._getBbox(line)]
-+            old_areas_cats = [self._getLineAreasCategories(line)]
-+
-         # build feature geometry
-         Vect_reset_line(self.poPoints)
-         for p in coords:
-@@ -696,6 +951,9 @@
-         
-         newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
-                                     self.poPoints, self.poCats)
-+        if newline > 0 and self.emit_signals:
-+            new_geom = [self._getBbox(newline)]
-+            new_areas_cats = [self._getLineAreasCategories(newline)]
-         
-         if newline > 0 and self._settings['breakLines']:
-             self._breakLineAtIntersection(newline, None)
-@@ -703,7 +961,13 @@
-         if newline > 0:
-             self._addChangeset()
-             self.toolbar.EnableUndo()
--        
-+    
-+            if self.emit_signals:
-+                self.lineEdited.emit(old_bboxs=old_bboxs, 
-+                                     old_areas_cats=old_areas_cats, 
-+                                     new_bboxs=new_bboxs, 
-+                                     new_areas_cats=new_areas_cats)
-+
-         return newline
- 
-     def FlipLine(self):
-@@ -1514,6 +1778,16 @@
-             return 0
-         
-         poList  = self._display.GetSelectedIList()
-+
-+        if self.emit_signals:
-+            cList = poList.contents
-+            
-+            old_bboxs = [self._getBbox(cList.value[0])]
-+            old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+            Vect_set_updated(self.poMapInfo, 1)
-+            n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
-         Vect_reset_line(self.poPoints)
-         Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
-         
-@@ -1525,15 +1799,35 @@
-         else:
-             ret = Vedit_remove_vertex(self.poMapInfo, poList,
-                                       self.poPoints, thresh)
-+
-         Vect_destroy_list(poList)
-+
-+        if ret > 0 and self.emit_signals:
-+            new_bboxs = []
-+            new_areas_cats = []
-+
-+            n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+            for i in range(n_up_lines_old, n_up_lines):
-+                new_id = Vect_get_updated_line(self.poMapInfo, i)
-+                new_areas_cats.append(self._getLineAreasCategories(new_id))
-+                new_bboxs.append(self._getBbox(new_id))
-         
-         if not add and ret > 0 and self._settings['breakLines']:
-             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
-                                           None)
--        
-+
-         if ret > 0:
-             self._addChangeset()
--                
-+
-+        if ret > 0 and self.emit_signals:
-+            if add:
-+                self.vertexAdded.emit(old_bboxs=old_bboxs, new_bboxs=new_bboxs)
-+            else:
-+                self.vertexRemoved.emit(old_bboxs=old_bboxs, 
-+                                        new_bboxs=new_bboxs,
-+                                        old_areas_cats=old_areas_cats,
-+                                        new_areas_cats=new_areas_cats)
-+
-         return 1
-     
-     def GetLineCats(self, line):
-Index: gui/wxpython/vdigit/toolbars.py
+     def OnVNet(self, event):
+         """!Dialog for v.net* modules 
+         """
+Index: gui/wxpython/mapdisp/toolbars.py
 ===================================================================
---- gui/wxpython/vdigit/toolbars.py	(revision 57760)
-+++ gui/wxpython/vdigit/toolbars.py	(working copy)
-@@ -17,6 +17,7 @@
- import wx
+--- gui/wxpython/mapdisp/toolbars.py	(revision 57820)
++++ gui/wxpython/mapdisp/toolbars.py	(working copy)
+@@ -49,6 +49,8 @@
+                             label = _('Create histogram of raster map')),
+     'vnet'       : MetaIcon(img = 'vector-tools',
+                             label = _('Vector network analysis tool')),
++    'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
++                            label = _("Interactive scatter plot tool")),
+     }
  
- from grass.script import core as grass
-+from grass.pydispatch.signal import Signal
- 
- from gui_core.toolbars  import BaseToolbar, BaseIcons
- from gui_core.dialogs   import CreateNewVector
-@@ -42,6 +43,8 @@
-         self.digit         = None
-         self._giface       = giface
+ NvizIcons = {
+@@ -239,7 +241,9 @@
+                       (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["interact_scatter"], 
++                       self.parent.OnIScatt)))
          
-+        self.editingStarted = Signal("VDigitToolbar.editingStarted")
-+
-         # currently selected map layer for editing (reference to MapLayer instance)
-         self.mapLayer = None
-         # list of vector layers from Layer Manager (only in the current mapset)
-@@ -860,6 +863,7 @@
-             alpha = int(opacity * 255)
-             self.digit.GetDisplay().UpdateSettings(alpha = alpha)
-         
-+        self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
-         return True
- 
-     def StopEditing(self):
+     def OnDecoration(self, event):
+         """!Decorations overlay menu
 Index: gui/wxpython/iclass/plots.py
 ===================================================================
---- gui/wxpython/iclass/plots.py	(revision 57760)
+--- gui/wxpython/iclass/plots.py	(revision 57820)
 +++ gui/wxpython/iclass/plots.py	(working copy)
 @@ -19,6 +19,7 @@
  import wx.lib.plot as plot
@@ -2079,62 +1598,1043 @@
      def DrawCoincidencePlots(self):
          """!Draw coincidence plots"""
          for bandIdx in range(len(self.bandList)):
-Index: gui/wxpython/mapdisp/toolbars.py
+Index: gui/wxpython/docs/wxGUI_iscatt.png
 ===================================================================
---- gui/wxpython/mapdisp/toolbars.py	(revision 57760)
-+++ gui/wxpython/mapdisp/toolbars.py	(working copy)
-@@ -49,6 +49,8 @@
-                             label = _('Create histogram of raster map')),
-     'vnet'       : MetaIcon(img = 'vector-tools',
-                             label = _('Vector network analysis tool')),
-+    'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
-+                            label = _("Interactive scatter plot tool")),
-     }
- 
- NvizIcons = {
-@@ -239,7 +241,9 @@
-                       (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["interact_scatter"], 
-+                       self.parent.OnIScatt)))
-         
-     def OnDecoration(self, event):
-         """!Decorations overlay menu
-Index: gui/wxpython/mapdisp/frame.py
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: gui/wxpython/docs/wxGUI_iscatt.png
 ===================================================================
---- gui/wxpython/mapdisp/frame.py	(revision 57760)
-+++ gui/wxpython/mapdisp/frame.py	(working copy)
-@@ -231,6 +231,7 @@
-         #
-         self.dialogs = {}
-         self.dialogs['attributes'] = None
-+        self.dialogs['scatt_plot'] = None
-         self.dialogs['category'] = None
-         self.dialogs['vnet'] = None
-         self.dialogs['query'] = None
-@@ -1199,6 +1200,19 @@
-         """!Returns toolbar with zooming tools"""
-         return self.toolbars['map']
- 
-+    def OnIScatt(self, event):
-+        """!Init interactive scatterplot tools
-+        """
-+        if self.dialogs['scatt_plot']:
-+            self.dialogs['scatt_plot'].Raise()
+--- gui/wxpython/docs/wxGUI_iscatt.png	(revision 57820)
++++ gui/wxpython/docs/wxGUI_iscatt.png	(working copy)
+
+Property changes on: gui/wxpython/docs/wxGUI_iscatt.png
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++application/octet-stream
+\ No newline at end of property
+Index: gui/wxpython/docs/wxGUI.iscatt.html
+===================================================================
+--- gui/wxpython/docs/wxGUI.iscatt.html	(revision 0)
++++ gui/wxpython/docs/wxGUI.iscatt.html	(working copy)
+@@ -0,0 +1,85 @@
++<!-- meta page description: wxGUI Interactive Scatter Plot Tool-->
++<!-- meta page index: wxGUI -->
++
++<h2>DESCRIPTION</h2>
++
++<em>Interactive Scatter Plot Tool</em> allows to analyze group of raster maps. 
++    The tool is integrated into <em><a href="wxGUI.iclass.html">
++    Supervised Classification Tool (wx.iclass) </a></em> (see the screen shot bellow). 
++    Also it is possible to launch it from Map Display Window 
++    (Analyze map → Interactive Scatter Plot Tool). 
++
++The main idea of the tool is that everything is linked together 
++(scatter plots together and mapwindow with the scatter plots).
++
++The main feature of the tool is the interactivity, which allows user to: 
++<ul>
++  <li> work with multiple plots, which represents multiple raster bands combinations.
++  </li>
++  <li> highlight plotted points in open scatter plots according to currently 
++       chosen pixels for classes by it's training areas (supported only in wx.iclass)
++  </li>
++  <li> be able to define areas in plots and the tool will highlight pixels in 
++       map display window and corresponding points in other plots
++  </li>
++  <li> plot of confidence ellipses
++  </li>
++</ul>
++
++<h2>TOOL CONTROLS LAYOUT</h2>
++
++<center>
++<br>
++<img src="wxGUI_iscatt.png" border="1" alt="Interactive Scatter Plot Tool">
++<br><br>
++</center>
++
++If editing mode is activated (the green polygon tool in toolbar), the areas which were selected 
++in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6 B_7 inside the ellipse. 
++Opacity and color of the selected area can be set in settings. The area corresponds to the active class (in this case clouds). 
++Selected areas are subset of areas, which belongs to the category.
++<p>
++In the editing mode it is possible to select certain area by the polygon, which can be created and edited by tools in editing toolbar. 
++After the area is selected, we can include or exclude it into active category by clicking on the first (plus) respectively 
++second (minus) tool in the editing toolbar. In mapwindow corresponding pixels are shown by the class raster representing 
++selected areas in scatter plots. In this case we can see clouds class raster (blue), forest class raster (green) and 
++water class raster (red).
++
++<h2>NOTES</h2>
++
++The tool can analyze only integer (CELL) rasters.
++
++It works with 8 bit range smoothly. 
++The tool is capable of processing data up to 12 bit range, however it is not recommended open many scatter plots in 12/11 bit mode.
++It could require significant amount of memory and plot rendering time can be longer.
++Analyzing of raster data with higher range is not recommended.   
++The raster range can be lowered using <em><a href="r.rescale.html">r.rescale</a></em>.
++
++<p>
++
++<h2>KNOWN ISSUES</h2>
++
++Selection of areas in mapwindow is currently supported only if the tool was launched from 
++<em><a href="wxGUI.iclass.html">Supervised Classification Tool (wx.iclass) </a></em>
++
++<h2>SEE ALSO</h2>
++
++<em>
++  <a href="wxGUI.html">wxGUI</a><br>
++  <a href="wxGUI.components.html">wxGUI components</a>
++  <a href="r.rescale.html">r.rescale</a><br>
++</em>
++
++<p>
++See also the
++user <a href="http://grasswiki.osgeo.org/wiki/WxGUI_Interactive_Scatter_Plot_Tool">wiki</a>
++page.
++
++<h2>AUTHOR</h2>
++
++Stepan
++Turek, <a href="http://grasswiki.osgeo.org/wiki/GRASS_GSoC_2013_GRASS_GIS_Interactive_Scatter_Plot_Tool">Google
++Summer of Code 2013</a> (mentor: Martin Landa)
++
++<p>
++<i>$Date: 2013-07-04 09:55:17 +0200 (Thu, 04 Jul 2013) $</i>
+Index: gui/wxpython/scatt_plot/core_c.py
+===================================================================
+--- gui/wxpython/scatt_plot/core_c.py	(revision 0)
++++ gui/wxpython/scatt_plot/core_c.py	(working copy)
+@@ -0,0 +1,228 @@
++"""!
++ at package scatt_plot.scatt_plot
++
++ at brief Wrappers for scatter plot C backend.
++
++(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 sys
++import numpy as np
++from multiprocessing import Process, Queue
++
++from ctypes import *
++try:
++    from grass.lib.imagery import *
++    from grass.lib.gis import Cell_head, G_get_window
++except ImportError, e:
++    sys.stderr.write(_("Loading ctypes libs failed"))
++
++from core.gcmd import GException
++
++def Rasterize(polygon, rast, region, value):
++    
++    pol_size = len(polygon) * 2
++    pol = np.array(polygon, dtype=float)
++
++    c_uint8_p = POINTER(c_uint8)
++    c_double_p = POINTER(c_double)
++
++    rows, cols = rast.shape 
++
++    #TODO creating of region is on many places
++    region['rows'] = rows
++    region['cols'] = cols
++
++    region['nsres'] = 1.0
++    region['ewres'] = 1.0
++
++    pol_p = pol.ctypes.data_as(c_double_p)
++    rast_p = rast.ctypes.data_as(c_uint8_p)
++
++    cell_h = _regionToCellHead(region)
++    I_rasterize(pol_p, 
++                len(polygon), 
++                value, 
++                pointer(cell_h), rast_p)
++
++def ApplyColormap(vals, vals_mask, colmap, out_vals):
++    
++    c_uint8_p = POINTER(c_uint8)
++
++    vals_p = vals.ctypes.data_as(c_uint8_p)
++
++
++    if hasattr(vals_mask, "ctypes"):
++        vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
++    else: #vals mask is empty (all data are selected)
++        vals_mask_p = None
++    colmap_p = colmap.ctypes.data_as(c_uint8_p)
++    out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
++
++    vals_size = vals.reshape((-1)).shape[0]
++    I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
++
++def MergeArrays(merged_arr, overlay_arr, alpha):
++    if merged_arr.shape != overlay_arr.shape:
++        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
++
++    c_uint8_p = POINTER(c_uint8)
++    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
++    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
++
++    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
++
++def MergeArrays(merged_arr, overlay_arr, alpha):
++    if merged_arr.shape != overlay_arr.shape:
++        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
++
++    c_uint8_p = POINTER(c_uint8)
++    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
++    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
++
++    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
++
++def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
++
++    q = Queue()
++    p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands, 
++                                                    n_bands, scatts, cats_rasts_conds, cats_rasts, q))
++    p.start()
++    ret = q.get()
++    p.join()
++    
++    return ret[0], ret[1]
++
++def UpdateCatRast(patch_rast, region, cat_rast):
++    q = Queue()
++    p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
++    p.start()
++    ret = q.get()
++    p.join()
++
++    return ret
++
++def CreateCatRast(region, cat_rast):
++    cell_head = _regionToCellHead(region)
++    I_create_cat_rast(pointer(cell_head), cat_rast)   
++
++def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
++
++    #TODO names for types not 0 and 1?
++    sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
++    scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
++
++    char_bands = _stringListToCharArr(bands)
++   
++    cell_head = _regionToCellHead(region)
++
++    ret = I_compute_scatts(pointer(cell_head),
++                           pointer(scatt_conds_c),
++                           pointer(cats_rasts_conds_c),
++                           pointer(char_bands),
++                           n_bands,
++                           pointer(sccats_c),
++                           pointer(cats_rasts_c))
++
++    I_sc_free_cats(pointer(sccats_c))
++    I_sc_free_cats(pointer(scatt_conds_c))
++
++    output_queue.put((ret, scatts))
++
++def _getBandcRange( band_info):
++    band_c_range = struct_Range()
++
++    band_c_range.max = band_info['max']
++    band_c_range.min = band_info['min']
++
++    return band_c_range
++
++def _regionToCellHead(region):
++    cell_head = struct_Cell_head()
++    G_get_window(pointer(cell_head))
++
++    convert_dict = {'n' : 'north', 'e' : 'east', 
++                    'w' : 'west',  's' : 'south', 
++                    'nsres' : 'ns_res',
++                    'ewres' : 'ew_res'}
++
++    for k, v in region.iteritems():
++        if k in ["rows", "cols", "cells"]:
++            v = int(v)
++        else:
++            v = float(v)
++
++        if convert_dict.has_key(k):
++            k = convert_dict[k]
++           
++        setattr(cell_head, k, v)
++
++    return cell_head
++
++def _stringListToCharArr(str_list):
++
++    arr = c_char_p * len(str_list)
++    char_arr = arr()
++    for i, st in enumerate(str_list):
++        if st:
++            char_arr[i] = st
++        else:
++            char_arr[i] = None
++
++    return char_arr
++
++def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
++
++    sccats = struct_scCats()
++    I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
++
++    refs = []        
++    cats_rasts_core = []
++    
++    for cat_id, scatt_ids in cats.iteritems():
++        cat_c_id = I_sc_add_cat(pointer(sccats))
++        cats_rasts_core.append(cats_rasts[cat_id])
++
++        for scatt_id, dt in scatt_ids.iteritems():
++            # if key is missing condition is always True (full scatter plor is computed)
++                vals = dt['np_vals']
++
++                scatt_vals = scdScattData()
++
++                c_void_p = ctypes.POINTER(ctypes.c_void_p)
++
++                if cats_type == SC_SCATT_DATA:
++                    vals[:] = 0
++                elif cats_type == SC_SCATT_CONDITIONS:
++                    pass
++                else:
++                    return None
++                data_p = vals.ctypes.data_as(c_void_p)
++                I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
++
++                refs.append(scatt_vals)
++
++                I_sc_insert_scatt_data(pointer(sccats),  
++                                       pointer(scatt_vals),
++                                       cat_c_id, scatt_id)
++
++    cats_rasts_c = _stringListToCharArr(cats_rasts_core)
++
++    return sccats, cats_rasts_c, refs
++
++def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
++    cell_head = _regionToCellHead(region)
++    
++    
++    ret = I_insert_patch_to_cat_rast(patch_rast, 
++                                     pointer(cell_head), 
++                                     cat_rast)
++
++    output_queue.put(ret)
++
++
+Index: gui/wxpython/scatt_plot/frame.py
+===================================================================
+--- gui/wxpython/scatt_plot/frame.py	(revision 0)
++++ gui/wxpython/scatt_plot/frame.py	(working copy)
+@@ -0,0 +1,694 @@
++"""!
++ at package scatt_plot.dialogs
++
++ at brief Main scatter plot widgets.
++
++Classes:
++ - frame::IClassIScattPanel
++ - frame::IScattDialog
++ - frame::MapDispIScattPanel
++ - frame::ScatterPlotsPanel
++ - frame::CategoryListCtrl
++
++(C) 2013 by the GRASS Development Team
++
++This program is free software under the GNU General Public License
++(>=v2). Read the file COPYING that comes with GRASS for details.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++import wx
++import wx.lib.scrolledpanel as scrolled
++import wx.lib.mixins.listctrl as listmix
++
++from core import globalvar
++from core.gcmd import GException, GError, RunCommand
++
++from gui_core.gselect import Select
++from gui_core.dialogs import SetOpacityDialog
++from scatt_plot.controllers import ScattsManager
++from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
++from scatt_plot.scatt_core import idScattToidBands
++from scatt_plot.dialogs import ManageBusyCursorMixin, RenameClassDialog
++from scatt_plot.plots import ScatterPlotWidget
++from iclass.dialogs import ContrastColor
++
++try:
++    from agw import aui
++except ImportError:
++    import wx.lib.agw.aui as aui
++
++class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
++    def __init__(self, parent, giface, iclass_mapwin = None,
++                 id = wx.ID_ANY):
++
++        #wx.SplitterWindow.__init__(self, parent = parent, id = id,
++        #                           style = wx.SP_LIVE_UPDATE)
++        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
++        ManageBusyCursorMixin.__init__(self, window=self)
++
++        self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface, 
++                                              iclass_mapwin=iclass_mapwin)
++
++        # toobars
++        self.toolbars = {}
++        self.toolbars['mainToolbar'] = self._createMainToolbar()
++        self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
++        
++        self._createCategoryPanel(self)
++
++        self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
++
++        self.mainsizer = wx.BoxSizer(wx.VERTICAL)
++        self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
++        self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
++        self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
++        self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
++
++        self.catsPanel.Hide()
++        self.toolbars['editingToolbar'].Hide()
++
++        self.SetSizer(self.mainsizer)
++        
++        self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
++        self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
++        self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
++
++        #self.SetSashGravity(0.5)
++        #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
++        self.Layout()
++
++    def UpdateCur(self, busy):
++        self.plot_panel.SetBusy(busy)
++        ManageBusyCursorMixin.UpdateCur(self, busy)
++
++    def _selCatInIScatt(self):
++         return False
++
++    def _createMainToolbar(self):
++         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
++
++    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
++        return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
++
++
++    def NewScatterPlot(self, scatt_id, transpose):
++        return self.plot_panel.NewScatterPlot(scatt_id, transpose)
++
++    def ShowPlotEditingToolbar(self, show):
++        self.toolbars["editingToolbar"].Show(show)
++        self.Layout()
++
++    def ShowCategoryPanel(self, show):
++        self.catsPanel.Show(show)
++        
++        #if show:
++        #    self.SetSashSize(5) 
++        #else:
++        #    self.SetSashSize(0)
++        self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
++        self.Layout()
++
++    def _createCategoryPanel(self, parent):
++        self.catsPanel = wx.Panel(parent=parent)
++        self.cats_list = CategoryListCtrl(parent=self.catsPanel, 
++                                          cats_mgr=self.scatt_mgr.GetCategoriesManager(),
++                                          sel_cats_in_iscatt=self._selCatInIScatt())
++
++        self.catsPanel.SetMinSize((-1, 100))
++        self.catsPanel.SetInitialSize((-1, 150))
++
++        box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
++                             label=' %s ' % _("Classes"),)
++        catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
++
++        self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
++
++        catsSizer.Add(item=self.cats_list, proportion=1,  flag=wx.EXPAND | wx.TOP, border = 5)
++        if self.toolbars['categoryToolbar']:
++            catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
++
++        self.catsPanel.SetSizer(catsSizer)
++
++    def CleanUp(self):
++        pass
++
++    def _createCategoryToolbar(self, parent):
++        return CategoryToolbar(parent=parent,
++                               scatt_mgr=self.scatt_mgr, 
++                               cats_list=self.cats_list)
++
++class IScattDialog(wx.Dialog):
++    def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
++                 id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
++        wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
++        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
++
++        self.iscatt_panel = MapDispIScattPanel(self, giface)
++
++        mainsizer = wx.BoxSizer(wx.VERTICAL)
++        mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
++
++        self.SetSizer(mainsizer)
++
++        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
++
++        self.SetMinSize((300, 300))
++
++    def OnCloseWindow(self, event):
++        event.Skip()
++        #self.
++
++class MapDispIScattPanel(IClassIScattPanel):
++    def __init__(self, parent, giface,
++                 id=wx.ID_ANY, **kwargs):
++        IClassIScattPanel.__init__(self, parent=parent, giface=giface,
++                                         id=id, **kwargs)
++
++    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
++        return ScattsManager(guiparent = self, giface = giface)
++
++    def _createMainToolbar(self):
++         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
++
++    def _selCatInIScatt(self):
++         return True
++
++class ScatterPlotsPanel(scrolled.ScrolledPanel):
++    def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
++    
++        scrolled.ScrolledPanel.__init__(self, parent)
++        self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
++
++        self.scatt_mgr = scatt_mgr
++
++        self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
++
++        #self._createCategoryPanel()
++        # Fancy gui
++        self._mgr = aui.AuiManager(self.mainPanel)
++        #self._mgr.SetManagedWindow(self)
++
++        self._mgr.Update()
++
++        self._doLayout()
++        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
++        self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
++
++        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
++
++        dlgSize = (-1, 400)
++        #self.SetBestSize(dlgSize)
++        #self.SetInitialSize(dlgSize)
++        self.SetAutoLayout(1)
++        #fix goutput's pane size (required for Mac OSX)
++        #if self.gwindow:         
++        #    self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
++        self.ignore_scroll = 0
++        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
++
++        self.scatt_i = 1
++        self.scatt_id_scatt_i = {}
++        self.transpose = {}
++        self.scatts = {}
++
++        self.Bind(wx.EVT_CLOSE, self.OnClose)
++        
++        self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
++
++    def SetBusy(self, busy):
++        for scatt in self.scatts.itervalues():
++            scatt.UpdateCur(busy)
++
++    def CursorPlotMove(self, x, y, scatt_id):
++
++        try:
++            x = int(round(x))
++            y = int(round(y))
++            coords = True
++        except:
++            coords = False
++
++        pane = self._getPane(scatt_id)
++        caption = self._creteCaption(scatt_id)
++        if coords:
++            caption += "   %d, %d" % (x, y)
++
++        pane.Caption(caption)
++        self._mgr.RefreshCaptions()
++
++    def _getPane(self, scatt_id):
++        scatt_i = self.scatt_id_scatt_i[scatt_id]
++        name = self._getScatterPlotName(scatt_i)
++        return self._mgr.GetPane(name)
++
++    def ScatterPlotClosed(self, scatt_id):
++
++        scatt_i = self.scatt_id_scatt_i[scatt_id]
++
++        name = self._getScatterPlotName(scatt_i)
++        pane = self._mgr.GetPane(name)
++
++        del self.scatt_id_scatt_i[scatt_id]
++        del self.scatts[scatt_id]
++
++        if pane.IsOk(): 
++          self._mgr.ClosePane(pane) 
++        self._mgr.Update() 
++
++    def OnMouseWheel(self, event):
++        #TODO very ugly find some better solution        
++        self.ignore_scroll = 3
++        event.Skip()
++
++    def ScrollChildIntoView(self, child):
++        #For aui manager it does not work and returns position always to the top -> deactivated.
++        pass
++
++    def OnPlotPaneClosed(self, event):
++        if isinstance(event.pane.window, ScatterPlotWidget):
++            event.pane.window.CleanUp()
++
++    def OnScrollChanged(self, event):
++        wx.CallAfter(self.Layout)
++
++    def OnScroll(self, event):
++        if self.ignore_scroll > 0:
++            self.ignore_scroll -= 1
++        else:
++            event.Skip()
++
++        #wx.CallAfter(self._mgr.Update)
++        #wx.CallAfter(self.Layout)
++
++    def _doLayout(self):
++
++        mainsizer = wx.BoxSizer(wx.VERTICAL)
++        mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
++        self.SetSizer(mainsizer)
++
++        self.Layout()
++        self.SetupScrolling()
++
++    def OnClose(self, event):
++        """!Close dialog"""
++        #TODO
++        print "closed"
++        self.scatt_mgr.CleanUp()
++        self.Destroy()
++
++    def OnSettings(self, event):
++        pass
++
++    def _newScatterPlotName(self, scatt_id):
++        name = self._getScatterPlotName(self.scatt_i) 
++        self.scatt_id_scatt_i[scatt_id] = self.scatt_i
++        self.scatt_i += 1
++        return name
++
++    def _getScatterPlotName(self, i):
++        name = "scatter plot %d" % i
++        return name
++
++    def NewScatterPlot(self, scatt_id, transpose):
++        #TODO needs to be resolved (should be in this class)
++
++        scatt = ScatterPlotWidget(parent = self.mainPanel, 
++                                  scatt_mgr = self.scatt_mgr, 
++                                  scatt_id = scatt_id, 
++                                  transpose = transpose)
++        scatt.plotClosed.connect(self.ScatterPlotClosed)
++        self.transpose[scatt_id] = transpose
++        
++        caption = self._creteCaption(scatt_id)
++        self._mgr.AddPane(scatt,
++                           aui.AuiPaneInfo().Dockable(True).Floatable(True).
++                           Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
++                           Caption(caption).
++                           Center().Position(1).MaximizeButton(True).
++                           MinimizeButton(True).CaptionVisible(True).
++                           CloseButton(True).Layer(0))
++
++        self._mgr.Update()
++  
++        self.SetVirtualSize(self.GetBestVirtualSize())
++        self.Layout()
++
++        self.scatts[scatt_id] = scatt
++
++        return scatt
++
++    def _creteCaption(self, scatt_id):
++
++        transpose = self.transpose[scatt_id]
++        bands = self.scatt_mgr.GetBands()
++
++        #TODO too low level
++        b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
++
++        x_b =  bands[b1_id].split('@')[0]
++        y_b = bands[b2_id].split('@')[0]
++
++        if transpose:
++            tmp = x_b
++            x_b = y_b
++            y_b = tmp
++
++        return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
++
++    def GetScattMgr(self):
++        return  self.scatt_mgr
++
++class CategoryListCtrl(wx.ListCtrl,
++                       listmix.ListCtrlAutoWidthMixin):
++                       #listmix.TextEditMixin):
++
++    def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
++
++        wx.ListCtrl.__init__(self, parent, id,
++                             style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
++                                     wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
++        self.columns = ((_('Class name'), 'name'), )
++                        #(_('Color'), 'color'))
++
++        self.sel_cats_in_iscatt = sel_cats_in_iscatt
++
++        self.Populate(columns = self.columns)
++        
++        self.cats_mgr = cats_mgr
++        self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++        self.rightClickedItemIdx = wx.NOT_FOUND
++        
++        listmix.ListCtrlAutoWidthMixin.__init__(self)
++
++        #listmix.TextEditMixin.__init__(self)
++      
++        self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
++        self.Bind(wx.EVT_RIGHT_UP,            self.OnCategoryRightUp) #wxGTK
++
++        #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
++        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
++             
++        self.cats_mgr.setCategoryAttrs.connect(self.Update)
++        self.cats_mgr.deletedCategory.connect(self.Update)
++        self.cats_mgr.addedCategory.connect(self.Update)
++        self.cats_mgr.cleanuped.connect(self.Update)
++
++    def Update(self, **kwargs):
++        self.SetItemCount(len(self.cats_mgr.GetCategories()))
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++    def InitCoreCats(self):
++        self.SetItemCount(len(self.cats_mgr.GetCategories()))
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++    def SetVirtualData(self, row, column, text):
++        attr = self.columns[column][1]
++        if attr == 'name':
++            try:
++                text.encode('ascii')
++            except UnicodeEncodeError:
++                GMessage(parent = self, message = _("Please use only ASCII characters."))
++                return
++
++        cat_id = self.cats_mgr.GetCategories()[row]
++
++        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
++        self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
++        self.cats_mgr.setCategoryAttrs.connect(self.Update)
++        
++        self.Select(row)
++        
++    def Populate(self, columns):
++        for i, col in enumerate(columns):
++            self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
++
++        #self.SetColumnWidth(0, 100)
++        #self.SetColumnWidth(1, 100)
++        
++    def AddCategory(self):
++
++        self.cats_mgr.addedCategory.disconnect(self.Update)
++        cat_id = self.cats_mgr.AddCategory()
++        self.cats_mgr.addedCategory.connect(self.Update)
++
++        if cat_id < 0:
++            GError(_("Maximum limit of categories number was reached."))
 +            return
++        self.SetItemCount(len(self.cats_mgr.GetCategories()))
++                        
++    def DeleteCategory(self):
++        indexList = sorted(self.GetSelectedIndices(), reverse = True)
++        cats = []
++        for i in indexList:
++            # remove temporary raster
++            cat_id = self.cats_mgr.GetCategories()[i]
++            
++            cats.append(cat_id)
 +
-+        from scatt_plot.frame import IScattDialog
-+        self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
++            self.cats_mgr.deletedCategory.disconnect(self.Update)
++            self.cats_mgr.DeleteCategory(cat_id)
++            self.cats_mgr.deletedCategory.connect(self.Update)
++            
++        self.SetItemCount(len(self.cats_mgr.GetCategories()))
 +        
-+        self.dialogs['scatt_plot'].CenterOnScreen()
-+        self.dialogs['scatt_plot'].Show()
++    def OnSel(self, event):
++        if self.sel_cats_in_iscatt:
++            indexList = self.GetSelectedIndices()
++            sel_cats = []
++            cats = self.cats_mgr.GetCategories()
++            for i in indexList:
++                sel_cats.append(cats[i])       
 +
-     def OnVNet(self, event):
-         """!Dialog for v.net* modules 
-         """
++            if sel_cats:
++                self.cats_mgr.SetSelectedCat(sel_cats[0])
++        event.Skip()
++
++    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
++        indices = []
++        lastFound = -1
++        while True:
++            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
++            if index == -1:
++                break
++            else:
++                lastFound = index
++                indices.append(index)
++        return indices        
++
++    def DeselectAll(self):
++        """!Deselect all items"""
++        indexList = self.GetSelectedIndices()
++        for i in indexList:
++            self.Select(i, on = 0)
++         
++        # no highlight
++        self.OnCategorySelected(None)
++        
++    def OnGetItemText(self, item, col):
++        attr = self.columns[col][1]
++        cat_id = self.cats_mgr.GetCategories()[item]
++
++        return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
++
++    def OnGetItemImage(self, item):
++        return -1
++
++    def OnGetItemAttr(self, item):
++        cat_id = self.cats_mgr.GetCategories()[item]
++
++        cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
++        
++        if cattr['show']:
++            c = cattr['color']
++            
++            back_c = wx.Colour(*map(int, c.split(':')))
++            text_c = wx.Colour(*ContrastColor(back_c))
++        else:
++            back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
++            text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
++
++        # if it is in scope of the method, gui falls, using self solved it 
++        self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
++        return self.l
++
++    def OnCategoryRightUp(self, event):
++        """!Show context menu on right click"""
++        item, flags = self.HitTest((event.GetX(), event.GetY()))
++        if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
++            self.rightClickedItemIdx = item
++
++        # generate popup-menu
++        cat_idx = self.rightClickedItemIdx
++
++        cats = self.cats_mgr.GetCategories()
++        cat_id = cats[cat_idx]
++        showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
++        
++        menu = wx.Menu()
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Rename class"))
++        self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Set color"))
++        self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Change opacity level"))
++        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++
++        if showed:
++            text = _("Hide")
++        else:
++            text = _("Show")
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text = text)
++        self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
++                                                                attrs={'show' : not showed}), 
++                                                                id=item_id) 
++        
++        menu.AppendSeparator()
++        
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Move to top"))
++        self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
++        if cat_idx == 0:
++            menu.Enable(item_id, False)
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Move to bottom"))
++        self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
++        if cat_idx == len(cats) - 1:
++            menu.Enable(item_id, False)
++
++        menu.AppendSeparator()
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Move category up"))
++        self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
++        if cat_idx == 0:
++            menu.Enable(item_id, False)
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Move category down"))
++        self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
++        if cat_idx == len(cats) - 1:
++            menu.Enable(item_id, False)
++
++        menu.AppendSeparator()
++
++        item_id = wx.NewId()
++        menu.Append(item_id, text=_("Export class raster"))
++        self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
++
++        self.PopupMenu(menu)
++        menu.Destroy()
++
++    def OnExportCatRast(self, event):
++        """!Export training areas"""
++        #TODO
++        #   GMessage(parent=self, message=_("No class raster to export."))
++        #    return
++
++        cat_idx = self.rightClickedItemIdx
++        cat_id = self.cats_mgr.GetCategories()[cat_idx]
++
++        self.cats_mgr.ExportCatRast(cat_id)
++
++    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 OnMoveTop(self, event):
++        cat_idx = self.rightClickedItemIdx
++        cat_id = self.cats_mgr.GetCategories()[cat_idx]
++        self.cats_mgr.ChangePosition(cat_id, 0)
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++    def OnMoveBottom(self, event):
++        cat_idx = self.rightClickedItemIdx
++        cats = self.cats_mgr.GetCategories()
++        cat_id = cats[cat_idx]
++        self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
++        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++    def OnSetColor(self, event):
++        """!Popup opacity level indicator"""
++        cat_idx = self.rightClickedItemIdx
++        cat_id = self.cats_mgr.GetCategories()[cat_idx]
++
++        col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
++        col = map(int, col.split(':'))
++
++        col_data =  wx.ColourData()
++        col_data.SetColour(wx.Colour(*col))
++
++        dlg = wx.ColourDialog(self, col_data)
++        dlg.GetColourData().SetChooseFull(True)
++
++        if dlg.ShowModal() == wx.ID_OK:
++            color = dlg.GetColourData().GetColour().Get()
++            color = ':'.join(map(str, color))
++            self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
++
++        dlg.Destroy()
++
++    def OnPopupOpacityLevel(self, event):
++        """!Popup opacity level indicator"""
++
++        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++        value = cat_attrs['opacity'] * 100
++        name = cat_attrs['name']
++
++        dlg = SetOpacityDialog(self, opacity = value,
++                               title = _("Change opacity of class <%s>" % name))
++
++        dlg.applyOpacity.connect(lambda value:
++                                 self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
++        dlg.CentreOnParent()
++
++        if dlg.ShowModal() == wx.ID_OK:
++            self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
++        
++        dlg.Destroy()
++
++    def OnRename(self, event):
++        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++        dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
++        dlg.CentreOnParent()
++
++        while True:
++            if dlg.ShowModal() == wx.ID_OK:
++                name = dlg.GetNewName().strip()
++                if not name:
++                    GMessage(parent=self, message=_("Empty name was inserted."))
++                else:
++                    self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
++                    break
++            else:
++                break
++
++        dlg.Destroy()
++
++    def _setCatAttrs(self, cat_id, attrs):
++        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
++        self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
++        self.cats_mgr.setCategoryAttrs.connect(self.Update)
 Index: gui/wxpython/scatt_plot/__init__.py
 ===================================================================
 --- gui/wxpython/scatt_plot/__init__.py	(revision 0)
@@ -2155,7 +2655,7 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/plots.py	(revision 0)
 +++ gui/wxpython/scatt_plot/plots.py	(working copy)
-@@ -0,0 +1,950 @@
+@@ -0,0 +1,953 @@
 +"""!
 + at package scatt_plot.dialogs
 +
@@ -2221,6 +2721,8 @@
 +        self.cidpress = None
 +        self.cidrelease = None
 +
++        self.rend_dt = {}
++
 +        self.transpose = transpose
 +
 +        self.inverse = False
@@ -2242,7 +2744,7 @@
 +        self.canvas.mpl_connect('button_press_event', self.OnPress)
 +        self.canvas.mpl_connect('button_release_event', self.OnRelease)
 +        self.canvas.mpl_connect('draw_event', self.draw_callback)
-+        self.canvas.mpl_connect('figure_leave_event', self.OnLeave)
++        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)
 +
 +    def draw_callback(self, event):
 +        self.polygon_drawer.draw_callback(event)
@@ -2367,13 +2869,14 @@
 +        else:
 +            c = None
 +
-+        #q = Queue()
-+        #p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
-+        #p.start()
-+        #merged_img, self.full_extend = q.get()
-+        #p.join()
++        q = Queue()
++        p = Process(target=MergeImg, args=(cats_order, scatts, styles, 
++                                           self.rend_dt, q))
++        p.start()
++        merged_img, self.full_extend, self.rend_dt = q.get()
++        p.join()
 +        
-+        merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
++        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
 +        self.axes.clear()
 +        self.axes.axis('equal')
 +
@@ -2512,7 +3015,7 @@
 +        
 +        self.cursorMove.emit(x=event.xdata, y=event.ydata, scatt_id=self.scatt_id)
 +
-+    def OnLeave(self, event):
++    def OnCanvasLeave(self, event):
 +        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)
 +
 +    def PanMotion(self, event):
@@ -2558,6 +3061,118 @@
 +        #self.axes.draw_artist(self.zoom_rect)
 +        self.canvas.draw()
 +
++def MergeImg(cats_order, scatts, styles, rend_dt, output_queue):
++
++        init = True
++        merged_img = None
++        merge_tmp = grass.tempfile()
++        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 \
++               not styles[cat_id]['show']):
++                if rend_dt.has_key(cat_id) and not rend_dt[cat_id]:
++                    del rend_dt[cat_id]
++                continue
++            if init:
++
++                b2_i = scatt['bands_info']['b1']
++                b1_i = scatt['bands_info']['b2']
++
++                full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5) 
++            
++            # if it does not need to be updated and was already rendered 
++            if not _renderCat(cat_id, rend_dt, scatt, styles):
++                # is empty - has only zeros
++                if rend_dt[cat_id] is None:
++                    continue
++            else:
++                masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
++                vmax = np.amax(masked_cat)
++                # totally empty -> no need to render
++                if vmax == 0:
++                    render_cat_ids[cat_id] = None
++                    continue
++
++                cmap = _getColorMap(cat_id, styles)
++                masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
++
++                cmap = np.uint8(cmap._lut * 255)
++                sh =masked_cat.shape
++
++                rend_dt[cat_id] = {}
++                if cat_id != 0:
++                    rend_dt[cat_id]['color'] = styles[cat_id]['color']
++                rend_dt[cat_id]['dt'] = np.memmap(grass.tempfile(), dtype='uint8', mode='w+', 
++                                                                    shape=(sh[0], sh[1], 4))
++                #colored_cat = np.zeros(dtype='uint8', )
++                ApplyColormap(masked_cat, masked_cat.mask, cmap, rend_dt[cat_id]['dt'])
++
++                #colored_cat = np.uint8(cmap(masked_cat) * 255)
++                del masked_cat
++                del cmap
++            
++            #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
++            if init:
++                merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', 
++                                       shape=rend_dt[cat_id]['dt'].shape)
++                merged_img[:] = rend_dt[cat_id]['dt']
++                init = False
++            else:
++                MergeArrays(merged_img, rend_dt[cat_id]['dt'], styles[cat_id]['opacity'])
++
++            """
++                #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape) 
++                c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
++
++                #TODO apply strides and there will be no need for loop
++                #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
++                
++                for i in range(3):
++                    merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
++                merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
++                
++                del c_img_a
++            """
++
++        output_queue.put((merged_img, full_extend, rend_dt))
++
++def _renderCat(cat_id, rend_dt, scatt, styles):
++    return True
++
++    if not rend_dt.has_key(cat_id):
++        return True
++    if not rend_dt[cat_id]:
++        return False
++    if scatt['render']:
++        return True
++    if cat_id != 0 and \
++       rend_dt[cat_id]['color'] != styles[cat_id]['color']:
++       return True
++    
++    return False
++
++def _getColorMap(cat_id, styles):
++    cmap = matplotlib.cm.jet
++    if cat_id == 0:
++        cmap.set_bad('w',1.)
++        cmap._init()
++        cmap._lut[len(cmap._lut) - 1, -1] = 0
++    else:
++        colors = styles[cat_id]['color'].split(":")
++
++        cmap.set_bad('w',1.)
++        cmap._init()
++        cmap._lut[len(cmap._lut) - 1, -1] = 0
++        cmap._lut[:, 0] = int(colors[0])/255.0
++        cmap._lut[:, 1] = int(colors[1])/255.0
++        cmap._lut[:, 2] = int(colors[2])/255.0
++
++    return cmap
++
 +class ScatterPlotContextMenu:
 +    def __init__(self, plot):
 +
@@ -2671,6 +3286,9 @@
 +
 +    def _setEmptyPol(self, empty_pol):
 +        self.empty_pol = empty_pol
++        if self.empty_pol:
++            #TODO
++            self.pol.xy = np.array([[0, 0]])
 +        self._show(not empty_pol)
 +
 +    def _show(self, show):
@@ -2991,133 +3609,25 @@
 +    im._remove_method = lambda h: axes.images.remove(h)
 +
 +    return im
-+
-+def MergeImg(cats_order, scatts, styles, output_queue):
-+
-+        start_time = time.clock()
-+        cmap_time = 0
-+        merge_time = 0
-+        mask_time = 0
-+        norm_time = 0
-+        max_time = 0
-+
-+        init = True
-+        merge_tmp = grass.tempfile()
-+        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 \
-+               not styles[cat_id]['show']):
-+                continue
-+            if init:
-+
-+                b2_i = scatt['bands_info']['b1']
-+                b1_i = scatt['bands_info']['b2']
-+
-+                full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5) 
-+            
-+            if cat_id == 0:
-+                cmap = matplotlib.cm.jet
-+                cmap.set_bad('w',1.)
-+                cmap._init()
-+                cmap._lut[len(cmap._lut) - 1, -1] = 0
-+            else:
-+                colors = styles[cat_id]['color'].split(":")
-+
-+                cmap = matplotlib.cm.jet
-+                cmap.set_bad('w',1.)
-+                cmap._init()
-+                cmap._lut[len(cmap._lut) - 1, -1] = 0
-+                cmap._lut[:, 0] = int(colors[0])/255.0
-+                cmap._lut[:, 1] = int(colors[1])/255.0
-+                cmap._lut[:, 2] = int(colors[2])/255.0
-+
-+            #if init:
-+            tmp = time.clock()
-+
-+            masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
-+            mask_time += time.clock() - tmp
-+
-+            tmp = time.clock()
-+            vmax = np.amax(masked_cat)
-+            max_time += time.clock() - tmp
-+
-+            tmp = time.clock()
-+            masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
-+            norm_time += time.clock() - tmp
-+
-+            #print masked_cat
-+            #print masked_cat.shape
-+            #print masked_cat.mask.dtype
-+            #
-+            #print "colored_cat.shape"
-+            #print colored_cat.shape
-+            #merged_img = np.memmap(merge_tmp, dtype='uint8',, shape=colored_cat.shape)
-+            tmp = time.clock()
-+            cmap = np.uint8(cmap._lut * 255)
-+            sh =masked_cat.shape
-+
-+            colored_cat = np.zeros(dtype='uint8', shape=(sh[0], sh[1], 4))
-+            ApplyColormap(masked_cat, masked_cat.mask, cmap, colored_cat)
-+
-+            #colored_cat = np.uint8(cmap(masked_cat) * 255)
-+            cmap_time += time.clock() - tmp
-+
-+            del masked_cat
-+            del cmap
-+            
-+            #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
-+
-+            tmp = time.clock()
-+            if init:
-+                merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', shape=colored_cat.shape)
-+                merged_img[:] = colored_cat[:]
-+                init = False
-+            else:
-+                MergeArrays(merged_img, colored_cat, styles[cat_id]['opacity'])
-+
-+            """
-+                #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape) 
-+                c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
-+
-+                #TODO apply strides and there will be no need for loop
-+                #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
-+                
-+                for i in range(3):
-+                    merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
-+                merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
-+                
-+                del c_img_a
-+            """
-+            merge_time += time.clock() - tmp
-+
-+            del colored_cat
-+
-+        end_time =  time.clock() - start_time
-+        #print "all time:%f" % (end_time)
-+        #print "cmap_time:%f" % (cmap_time / end_time * 100.0 )
-+        #print "merge_time:%f" % (merge_time / end_time * 100.0 )
-+        #print "mask_time:%f" % (mask_time / end_time * 100.0 )
-+        #print "nax_time:%f" % (max_time / end_time * 100.0 )
-+        #print "norm_time:%f" % (norm_time / end_time * 100.0 )
-+        
-+        #output_queue.put((merged_img, full_extend))
-+        return merged_img, full_extend 
-\ No newline at end of file
 Index: gui/wxpython/scatt_plot/controllers.py
 ===================================================================
 --- gui/wxpython/scatt_plot/controllers.py	(revision 0)
 +++ gui/wxpython/scatt_plot/controllers.py	(working copy)
-@@ -0,0 +1,890 @@
+@@ -0,0 +1,1068 @@
 +"""!
 + at package scatt_plot.controllers
 +
 + at brief Controller layer for scatter plot tool.
 +
 +Classes:
++ - controllers::ScattsManager
++ - controllers::PlotsRenderingManager
++ - controllers::CategoriesManager
++ - controllers::IMapWinDigitConnection
++ - controllers::IClassDigitConnection
++ - controllers::IMapDispConnection
++ - controllers::IClassConnection
++ - controllers::gThread
 +
 +(C) 2013 by the GRASS Development Team
 +
@@ -3128,23 +3638,28 @@
 +"""
 +import os
 +import sys
++from copy import deepcopy
 +
-+#TODO just for testing
 +import time
++import threading
++import Queue
++from core.gconsole import EVT_CMD_DONE
 +
 +import wx
 +
 +from core.gcmd import GException, GError, GMessage, RunCommand
 +from core.settings import UserSettings
-+from scatt_plot.gthreading import gThread
++from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
 +from scatt_plot.scatt_core import Core, idBandsToidScatt
 +from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
++
 +from iclass.dialogs import IClassGroupDialog
 +
 +import grass.script as grass
 +from grass.pydispatch.signal import Signal
 +
 +class ScattsManager:
++    """!Main controller."""
 +    def __init__(self, guiparent, giface, iclass_mapwin = None):
 +        #TODO remove iclass parameter
 +
@@ -3163,7 +3678,7 @@
 +        self.core = Core()
 +
 +        self.cats_mgr = CategoriesManager(self, self.core)
-+        self.render_mgr = RenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
++        self.render_mgr = PlotsRenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
 +
 +        self.thread = gThread();
 +        
@@ -3226,6 +3741,7 @@
 +        self.iclass_conn.SetData()
 +
 +    def SetBands(self, bands):
++        self.busy = wx.BusyInfo(_("Loading data..."))
 +        self.CleanUp()
 +        self.data_set = False
 +        
@@ -3238,16 +3754,13 @@
 +                        ondone=self.SetDataDone, userdata={"show_add" : show_add})
 +
 +    def SetDataDone(self, event):
++        del self.busy
 +        self.data_set = True
 +
 +        self.iclass_conn.SyncCats()
 +        if event.userdata['show_add']:
-+          self.AddScattPlot()  
++          self.AddScattPlot()
 +
-+    def SettingsUpdated(self, event):
-+        #TODO optimization
-+        self.render_mgr.RenderScattPlts()
-+
 +    def OnOutput(self, event):
 +        """!Print thread output according to debug level.
 +        """
@@ -3305,6 +3818,7 @@
 +        dlg.Destroy()
 +         
 +    def _addScattPlot(self, scatt_ids):
++        self.render_mgr.NewRunningProcess()
 +        self.thread.Run(callable=self.core.AddScattPlots,
 +                        scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
 +
@@ -3326,7 +3840,7 @@
 +                self.plots[s_id]['scatt'].SetMode(self.plot_mode)
 +                self.plots[s_id]['scatt'].ZoomToExtend()
 +
-+        self.render_mgr.RenderScattPlts(scatt_ids = scatt_ids)
++        self.render_mgr.RunningProcessDone()
 +
 +    def PlotClosed(self, scatt_id):
 +        del self.plots[scatt_id]
@@ -3349,7 +3863,7 @@
 +            if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
 +                self.SetPlotsMode(None)
 +
-+        self.render_mgr.RenderScattPlts()
++        self.render_mgr.RunningProcessDone()
 +        return activate
 +
 +    def ProcessSelectionPolygons(self, process_mode):        
@@ -3385,62 +3899,103 @@
 +            return
 +
 +        for scatt in self.plots.itervalues():
-+            scatt['scatt'].SetEmpty()
++            if scatt['scatt']:
++                scatt['scatt'].SetEmpty()
 +
 +        self.computingStarted.emit()
 +
++        self.render_mgr.NewRunningProcess()
++        self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
++        self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
++
 +        self.thread.Run(callable = self.core.UpdateCategoryWithPolygons, 
 +                        cat_id = sel_cat_id,
 +                        scatts_pols = scatts_polygons,
 +                        value = value, ondone=self.SetEditCatDataDone)
 +
 +    def SetEditCatDataDone(self, event):
++        self.render_mgr.RunningProcessDone()
 +        if event.exception:
 +            GError(_("Error occured during computation of scatter plot category:\n%s"), 
 +                      parent = self.guiparent, showTraceback = False)
 +
 +        cat_id = event.ret
-+
-+        self.render_mgr.RenderScattPlts()
 +        self.iclass_conn.RenderCatRast(cat_id)
 +            
-+    def DigitDataChanged(self, vectMap, digit):
-+        
-+        if self.digit_conn:
-+            self.digit_conn.DigitDataChanged(vectMap, digit)
-+            return 1
-+        else:
-+            return 0
++    def SettingsUpdated(self, chanaged_setts):
++        self.render_mgr.RenderRequest()
 +
++        #['ellipses', 'show_ellips']
 +    def GetCategoriesManager(self):
 +        return self.cats_mgr
 +
-+class RenderingManager:
++class PlotsRenderingManager:
++    """!Manages rendering of scatter plot.
 +
++    @todo still space for optimalization
++    """
 +    def __init__(self, scatt_mgr, cats_mgr, core):
 +        self.scatt_mgr = scatt_mgr
 +        self.cats_mgr = cats_mgr
 +        self.core = core
 +        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
 +
-+        self.scatts_to_render = {}
++        self.runningProcesses = 0
++
++        self.data_to_render = {}
 +        self.render_queue = []
 +
++        self.cat_ids = []
++        self.cat_cond_ids = []
++
 +        self.renderingStarted = Signal("ScattsManager.renderingStarted")
 +        self.renderingFinished = Signal("ScattsManager.renderingFinished")
 +
++    def AddRenderRequest(self, scatts):
++        for scatt_id, cat_ids in scatts:
++            if not self.data_to_render.has_key[scatt_id]:
++                self.data_to_render = cat_ids
++            else:
++                for c in cat_ids:
++                    if c not in self.data_to_render[scatt_id]:
++                         self.data_to_render[scatt_id].append(c) 
++
++    def NewRunningProcess(self):
++        self.runningProcesses += 1 
++
++    def RunningProcessDone(self):
++        self.runningProcesses -= 1
++        if self.runningProcesses <= 1:
++            self.RenderScattPlts()
++
++    def RenderRequest(self):
++        if self.runningProcesses <= 1:
++            self.RenderScattPlts()
++
++    def CategoryChanged(self, cat_ids):
++        for c in cat_ids:
++            if c not in self.cat_ids:
++                self.cat_ids.append(c)
++
++    def CategoryCondsChanged(self, cat_ids):
++        for c in cat_ids:
++            if c not in self.cat_cond_ids:
++                self.cat_cond_ids.append(c)
++
 +    def RenderScattPlts(self, scatt_ids = None):
 +        if len(self.render_queue) > 1:
 +            return 
 +        
 +        self.renderingStarted.emit()
 +        self.render_queue.append(self.scatt_mgr.thread.GetId())
-+        self.scatt_mgr.thread.Run(callable = self._renderscattplts, scatt_ids = scatt_ids,
-+                                  ondone=self.RenderingDone)
 +
-+    def _renderscattplts(self, scatt_ids):
-+        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++        cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
 +        cats = self.cats_mgr.GetCategories()[:]
++        self.scatt_mgr.thread.Run(callable=self._renderscattplts, scatt_ids=scatt_ids,
++                                  cats=cats, cats_attrs=cats_attrs,
++                                  ondone=self.RenderingDone)
++
++    def _renderscattplts(self, scatt_ids, cats, cats_attrs):
 +        cats.reverse()
 +        cats.insert(0, 0)
 +        for i_scatt_id, scatt in self.scatt_mgr.plots.items():
@@ -3456,15 +4011,25 @@
 +            else:
 +                ellipses_dt = {}
 +
++            for c in scatt_dt.iterkeys():
++                try:
++                    self.cat_ids.remove(c)
++                    scatt_dt[c]['render']=True
++                except:
++                    scatt_dt[c]['render']=False
++
 +            if self.scatt_mgr.pol_sel_mode[0]:
 +                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
 +
-+            scatt['scatt'].Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++            scatt['scatt'].Plot(cats_order=cats,
++                                scatts=scatt_dt, 
++                                ellipses=ellipses_dt, 
++                                styles=cats_attrs)
 +
 +    def RenderingDone(self, event):
 +        self.render_queue.remove(event.pid)
 +        if not self.render_queue:    
-+                self.renderingFinished.emit()
++            self.renderingFinished.emit()
 +
 +    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
 +
@@ -3472,7 +4037,7 @@
 +        if not cat_id:
 +            return
 +
-+        sel_a_cat_id = max(cats_attrs.keys()) + 1        
++        sel_a_cat_id = -1        
 +
 +        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
 +        if not s:
@@ -3494,15 +4059,20 @@
 +                                    'show' : True}
 +
 +        scatt_dt[sel_a_cat_id] = s[cat_id]
++        
++        scatt_dt[sel_a_cat_id]['render'] = False
++        if cat_id in self.cat_cond_ids:
++            scatt_dt[sel_a_cat_id]['render'] = True
++            self.cat_cond_ids.remove(cat_id)
 +
-+
 +    def _showConfEllipses(self):
 +        return UserSettings.Get(group='scatt', 
 +                                key="ellipses", 
 +                                subkey="show_ellips")
 +
 +class CategoriesManager:
-+
++    """!Manages categories list of scatter plot.
++    """
 +    def __init__(self, scatt_mgr, core):
 +
 +        self.core = core
@@ -3516,6 +4086,7 @@
 +        self.exportRaster = None
 +
 +        self.initialized = Signal('CategoriesManager.initialized')
++        self.cleanuped = Signal('CategoriesManager.cleanuped')
 +        self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
 +        self.deletedCategory = Signal('CategoriesManager.deletedCategory')
 +        self.addedCategory = Signal('CategoriesManager.addedCategory')
@@ -3543,7 +4114,7 @@
 +
 +        self.cats_ids.insert(new_pos, cat_id)
 +
-+        self.scatt_mgr.render_mgr.RenderScattPlts()
++        self.scatt_mgr.render_mgr.RenderRequest()
 +        return True
 +
 +    def _addCategory(self, cat_id):
@@ -3566,7 +4137,7 @@
 +            #    return -1;
 +
 +        self.cats[cat_id] = {
-+                                'name' : _('Class %s' % cat_id ),
++                                'name' : 'class_%d' % cat_id,
 +                                'color' : "0:0:0",
 +                                'opacity' : 1.0,
 +                                'show' : True,
@@ -3602,7 +4173,8 @@
 +            self.cats[cat_id][k] = v
 +
 +        if render:
-+            self.scatt_mgr.render_mgr.RenderScattPlts()
++            self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
++            self.scatt_mgr.render_mgr.RenderRequest()
 +        
 +        if update_cat_rast:
 +            self.scatt_mgr.iclass_conn.UpdateCategoryRaster(cat_id, update_cat_rast)
@@ -3623,7 +4195,7 @@
 +    def SetSelectedCat(self, cat_id):
 +        self.sel_cat_id = cat_id
 +        if self.scatt_mgr.pol_sel_mode[0]:
-+            self.scatt_mgr.render_mgr.RenderScattPlts()
++            self.scatt_mgr.render_mgr.RenderRequest()
 +
 +    def GetSelectedCat(self):
 +        return self.sel_cat_id
@@ -3661,7 +4233,7 @@
 +                                      userdata={'name' : cat_attrs['name']},
 +                                      cat_id=cat_id,
 +                                      rast_name=self.exportCatRast, 
-+                                      ondone=self.cats_mgr.OnExportCatRastDone)
++                                      ondone=self.OnExportCatRastDone)
 +
 +    def OnExportCatRastDone(self, event):
 +        ret, err = event.ret
@@ -3675,10 +4247,14 @@
 +
 +
 +class IMapWinDigitConnection:
++    """!Manage comunication of the scatter plot with digitizer in mapwindow (does not work).
++    """
 +    def Update(self):
 +        pass
 +
 +class IClassDigitConnection:
++    """!Manages comunication of the scatter plot with digitizer in wx.iclass.
++    """
 +    def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
 +        self.mapWin = mapWin
 +        self.vectMap = None
@@ -3773,6 +4349,8 @@
 +        self.scatt_mgr.render_mgr.RenderScattPlts()
 +
 +class IMapDispConnection:
++    """!Manage comunication of the scatter plot with mapdisplay in mapwindow.
++    """
 +    def __init__(self, scatt_mgr, cats_mgr, giface):
 +        self.scatt_mgr = scatt_mgr
 +        self.cats_mgr = cats_mgr
@@ -3782,7 +4360,7 @@
 +
 +    def SetData(self):
 +        dlg = IClassGroupDialog(self.scatt_mgr.guiparent, 
-+                                group=self.set_g['g'], 
++                                group=self.set_g['group'],
 +                                subgroup=self.set_g['subg'])
 +        
 +        bands = []
@@ -3806,7 +4384,7 @@
 +            self.scatt_mgr.SetBands(bands)
 +
 +    def EmptyCategories(self):
-+        pass
++        return None
 +
 +    def SyncCats(self, cats_ids = None):
 +        pass
@@ -3855,6 +4433,8 @@
 +            self.giface.updateMap.emit()
 +
 +class IClassConnection:
++    """!Manage comunication of the scatter plot with mapdisplay in wx.iclass.
++    """
 +    def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
 +        self.iclass_frame = iclass_frame
 +        self.stats_data = self.iclass_frame.stats_data
@@ -4002,43 +4582,12 @@
 +        if res.split('\n')[0]:
 +            bands = res.split('\n')
 +            self.scatt_mgr.SetBands(bands)
-\ No newline at end of file
-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,125 @@
-+"""!
-+ 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
-+from core.gconsole import EVT_CMD_DONE
-+
-+import wx
-+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
-+
 +class gThread(threading.Thread, wx.EvtHandler):
-+    """!Thread for GRASS commands"""
++    """!Thread for scatter plot backend"""
 +    requestId = 0
 +
 +    def __init__(self, requestQ=None, resultQ=None, **kwds):
-+        """!
-+        @param receiver event receiver (used in PostEvent)
-+        """
 +        wx.EvtHandler.__init__(self)
 +
 +        threading.Thread.__init__(self, **kwds)
@@ -4133,18 +4682,22 @@
 +            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,494 @@
+@@ -0,0 +1,517 @@
 +"""!
 + at package scatt_plot.dialogs
 +
-+ at brief GUI.
++ at brief Dialogs widgets.
 +
 +Classes:
++ - dialogs::AddScattPlotDialog
++ - dialogs::ExportCategoryRaster
++ - dialogs::SettingsDialog
++ - dialogs::ManageBusyCursorMixin
++ - dialogs::RenameClassDialog
 +
 +(C) 2013 by the GRASS Development Team
 +
@@ -4166,6 +4719,7 @@
 +from core import globalvar
 +from core.gcmd import GMessage
 +from core.settings import UserSettings
++from gui_core.dialogs import SimpleDialog
 +
 +class AddScattPlotDialog(wx.Dialog):
 +
@@ -4452,7 +5006,7 @@
 +        settsLabels = {} 
 +
 +        self.settings["show_ellips"] = wx.CheckBox(parent = self, id=wx.ID_ANY,
-+                                                  label = _('Show confidence ellipses'))
++                                                   label = _('Show confidence ellipses'))
 +        show_ellips = UserSettings.Get(group ='scatt', key = "ellipses", subkey = "show_ellips")
 +        self.settings["show_ellips"].SetValue(show_ellips)
 +
@@ -4528,7 +5082,7 @@
 +        selPolBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
 +
 +        ell_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
-+                                   label =" %s " % _("Ellipses settings:"))
++                               label =" %s " % _("Ellipses settings:"))
 +        ellPolBoxSizer = wx.StaticBoxSizer(ell_box, wx.VERTICAL)
 +        gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
 +
@@ -4633,12 +5187,28 @@
 +            return
 +
 +        self.busy_cur = None
-\ No newline at end of file
++
++class RenameClassDialog(SimpleDialog):
++    def __init__(self, parent, old_name, title=("Change class name")):
++        SimpleDialog.__init__(self, parent, title)
++
++        self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY) 
++        self.name.SetValue(old_name)
++
++        self.dataSizer.Add(self.name, proportion = 0,
++                           flag = wx.EXPAND | wx.ALL, border = 1)
++
++        self.panel.SetSizer(self.sizer)
++        self.name.SetMinSize((200, -1))
++        self.sizer.Fit(self)
++
++    def GetNewName(self):
++        return self.name.GetValue()
 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,264 @@
+@@ -0,0 +1,267 @@
 +"""!
 + at package scatt_plot.toolbars
 +
@@ -4721,9 +5291,9 @@
 +                    wx.ITEM_CHECK),
 +                    (None, ),
 +                    ('settings', icons["settings"],
-+                    self.OnSettings)
-+                    #('help', icons["help"],
-+                    # self.OnHelp),                    
++                    self.OnSettings),
++                    ('help', icons["help"],
++                     self.OnHelp)                    
 +                ]
 +
 +        if self.opt_tools and "add_group" in self.opt_tools:
@@ -4778,6 +5348,10 @@
 +        dlg.ShowModal()
 +        dlg.Destroy()
 +
++    def OnHelp(self, event) :
++            RunCommand('g.manual',
++                       entry = 'wxGUI.iscatt')
++
 +class EditingToolbar(BaseToolbar):
 +    """!Main toolbar
 +    """
@@ -4796,26 +5370,25 @@
 +        """
 +        self.icons = {
 +            'sel_add'         : MetaIcon(img = 'layer-add',
-+                                         label = _('Include selected area to class.'),
-+                                         desc = _('Include selected area to class.')),
++                                         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.')),
++                                         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'),
++                                         label = _('Create selection polygon'),
 +                                         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.')),
++                                         desc = _('Remove boundary vertex')),
 +            'delete'        : MetaIcon(img = 'polygon-delete',
-+                                       label = _('Remove polygon.'),
-+                                       desc = _('Remove polygon.')),
++                                       label = _("Remove polygon (click on scatter plot for removing it's polygon)")),
 +            }
 +
 +        return self._getToolbarData((
@@ -4892,9 +5465,9 @@
 +        """
 +        self.icons = {
 +            'add_class'     : MetaIcon(img = 'layer-add',
-+                                       label = _('Add class.')),
++                                       label = _('Add class')),
 +            'remove_class'  : MetaIcon(img = 'layer-remove',
-+                                       label = _('Remove selected class.'))
++                                       label = _('Remove selected class'))
 +            }
 +
 +        return self._getToolbarData((
@@ -4908,13 +5481,18 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/scatt_core.py	(revision 0)
 +++ gui/wxpython/scatt_plot/scatt_core.py	(working copy)
-@@ -0,0 +1,811 @@
+@@ -0,0 +1,835 @@
 +"""!
 + at package scatt_plot.scatt_plot
 +
 + at brief Non GUI functions.
 +
 +Classes:
++ - scatt_core::Core
++ - scatt_core::CatRastUpdater
++ - scatt_core::AnalyzedData
++ - scatt_core::ScattPlotsCondsData
++ - scatt_core::ScattPlotsData
 +
 +(C) 2013 by the GRASS Development Team
 +
@@ -4929,12 +5507,13 @@
 +import time
 +
 +import numpy as np
-+#TODO
-+from matplotlib.path import Path 
 +
++# used iclass perimeters algorithm instead of convolve2d
++#from matplotlib.path import Path 
++#from scipy.signal import convolve2d
++
 +from math import sqrt, ceil, floor
 +from copy import deepcopy
-+#from scipy.signal import convolve2d
 +
 +from core.gcmd import GException, GError, RunCommand
 +
@@ -4944,6 +5523,8 @@
 +                   Rasterize, SC_SCATT_DATA, SC_SCATT_CONDITIONS
 +
 +class Core:
++    """!Represents scatter plot backend.
++    """
 +    def __init__(self):
 +        
 +        self.an_data = AnalyzedData()
@@ -4954,6 +5535,8 @@
 +        self.cat_rast_updater = CatRastUpdater(self.scatts_dt, self.an_data, self)
 +
 +    def SetData(self, bands):
++        """Set bands for analysis.
++        """
 +        self.an_data.Create(bands)
 +
 +        n_bands = len(self.GetBands())
@@ -5080,6 +5663,17 @@
 +        self.ComputeCatsScatts([cat_id])
 +        return cat_id
 +
++    def ExportCatRast(self, cat_id, rast_name):
++
++        cat_rast = self.scatts_dt.GetCatRast(cat_id);
++        if not cat_rast:
++            return 1
++        
++        return RunCommand("g.copy", 
++                          rast=cat_rast + ',' + rast_name,
++                          getErrorMsg=True,
++                          overwrite=True)
++
 +    def _validExtend(self, val):
 +        #TODO do it general
 +        if  val > 255:
@@ -5090,7 +5684,8 @@
 +        return val
 +
 +class CatRastUpdater:
-+
++    """!Update backend data structures according to selected areas in mapwindow.
++    """
 +    def __init__(self, scatts_dt, an_data, core):
 +        self.scatts_dt = scatts_dt
 +        self.an_data = an_data # TODO may be confusing
@@ -5245,7 +5840,8 @@
 +        return {"GRASS_REGION" :  grass.region_env(**new_r)}
 +
 +class AnalyzedData:
-+
++    """!Represents analyzed data (bands, region).
++    """
 +    def __init__(self):
 +        
 +        self.bands = []
@@ -5333,7 +5929,8 @@
 +        return region
 +
 +class ScattPlotsCondsData:
-+
++    """!Data structure for selected areas in scatter plot(condtions).
++    """
 +    def __init__(self, an_data):
 +
 +        self.an_data = an_data
@@ -5482,7 +6079,8 @@
 +
 +                   
 +class ScattPlotsData(ScattPlotsCondsData):
-+
++    """!Data structure for computed points (classes) in scatter plots.\
++    """
 +    def __init__(self, an_data):
 +
 +        self.cats_rasts = {}
@@ -5697,9 +6295,8 @@
 +    return raster
 +"""
 +
-+#TODO move to utils?
 +def idScattToidBands(scatt_id, n_bands):
-+ 
++    """!Get bands ids from scatter plot id.""" 
 +    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)
@@ -5709,7 +6306,7 @@
 +    return band_1, band_2
 +
 +def idBandsToidScatt(band_1_id, band_2_id, n_bands):
-+
++    """!Get scatter plot id from band ids."""
 +    if band_2_id <  band_1_id:
 +        tmp = band_1_id
 +        band_1_id = band_2_id
@@ -5721,1077 +6318,9 @@
 +
 +    return scatt_id
 \ No newline at end of file
-Index: gui/wxpython/scatt_plot/core_c.py
-===================================================================
---- gui/wxpython/scatt_plot/core_c.py	(revision 0)
-+++ gui/wxpython/scatt_plot/core_c.py	(working copy)
-@@ -0,0 +1,232 @@
-+"""!
-+ at package scatt_plot.scatt_plot
-+
-+ at brief Functions which work with scatter plot c code.
-+
-+Classes:
-+
-+(C) 2013 by the GRASS Development Team
-+
-+This program is free software under the GNU General Public License
-+(>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+
-+#TODO just for testing
-+import time
-+
-+import sys
-+import numpy as np
-+from multiprocessing import Process, Queue
-+
-+from ctypes import *
-+try:
-+    from grass.lib.imagery import *
-+    from grass.lib.gis import Cell_head, G_get_window
-+except ImportError, e:
-+    sys.stderr.write(_("Loading ctypes libs failed"))
-+
-+from core.gcmd import GException
-+
-+def Rasterize(polygon, rast, region, value):
-+    pol_size = len(polygon) * 2
-+    pol = np.array(polygon, dtype=float)
-+
-+    c_uint8_p = POINTER(c_uint8)
-+    c_double_p = POINTER(c_double)
-+
-+    rows, cols = rast.shape 
-+
-+    #TODO creating of region is on many places
-+    region['rows'] = rows
-+    region['cols'] = cols
-+
-+    region['nsres'] = 1.0
-+    region['ewres'] = 1.0
-+
-+    pol_p = pol.ctypes.data_as(c_double_p)
-+    rast_p = rast.ctypes.data_as(c_uint8_p)
-+
-+    cell_h = _regionToCellHead(region)
-+    I_rasterize(pol_p, 
-+                len(polygon), 
-+                rast_p, value, 
-+                pointer(cell_h))
-+
-+def ApplyColormap(vals, vals_mask, colmap, out_vals):
-+    
-+    c_uint8_p = POINTER(c_uint8)
-+
-+    vals_p = vals.ctypes.data_as(c_uint8_p)
-+
-+
-+    if hasattr(vals_mask, "ctypes"):
-+        vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
-+    else: #vals mask is empty (all data are selected)
-+        vals_mask_p = None
-+    colmap_p = colmap.ctypes.data_as(c_uint8_p)
-+    out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
-+
-+    vals_size = vals.reshape((-1)).shape[0]
-+    I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
-+
-+def MergeArrays(merged_arr, overlay_arr, alpha):
-+    if merged_arr.shape != overlay_arr.shape:
-+        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
-+
-+    c_uint8_p = POINTER(c_uint8)
-+    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
-+    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
-+
-+    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
-+
-+def MergeArrays(merged_arr, overlay_arr, alpha):
-+    if merged_arr.shape != overlay_arr.shape:
-+        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
-+
-+    c_uint8_p = POINTER(c_uint8)
-+    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
-+    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
-+
-+    I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
-+
-+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
-+
-+    q = Queue()
-+    p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands, 
-+                                                    n_bands, scatts, cats_rasts_conds, cats_rasts, q))
-+    p.start()
-+    ret = q.get()
-+    p.join()
-+    
-+    return ret[0], ret[1]
-+
-+def UpdateCatRast(patch_rast, region, cat_rast):
-+    q = Queue()
-+    p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
-+    p.start()
-+    ret = q.get()
-+    p.join()
-+
-+    return ret
-+
-+def CreateCatRast(region, cat_rast):
-+    cell_head = _regionToCellHead(region)
-+    I_create_cat_rast(pointer(cell_head), cat_rast)   
-+
-+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
-+
-+    #TODO names for types not 0 and 1?
-+    sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
-+    scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
-+
-+    char_bands = _stringListToCharArr(bands)
-+   
-+    cell_head = _regionToCellHead(region)
-+
-+    ret = I_compute_scatts(pointer(cell_head),
-+                           pointer(scatt_conds_c),
-+                           pointer(cats_rasts_conds_c),
-+                           pointer(char_bands),
-+                           n_bands,
-+                           pointer(sccats_c),
-+                           pointer(cats_rasts_c))
-+
-+    I_sc_free_cats(pointer(sccats_c))
-+    I_sc_free_cats(pointer(scatt_conds_c))
-+
-+    output_queue.put((ret, scatts))
-+
-+def _getBandcRange( band_info):
-+    band_c_range = struct_Range()
-+
-+    band_c_range.max = band_info['max']
-+    band_c_range.min = band_info['min']
-+
-+    return band_c_range
-+
-+def _regionToCellHead(region):
-+    cell_head = struct_Cell_head()
-+    G_get_window(pointer(cell_head))
-+
-+    convert_dict = {'n' : 'north', 'e' : 'east', 
-+                    'w' : 'west',  's' : 'south', 
-+                    'nsres' : 'ns_res',
-+                    'ewres' : 'ew_res'}
-+
-+    for k, v in region.iteritems():
-+        if k in ["rows", "cols", "cells"]:
-+            v = int(v)
-+        else:
-+            v = float(v)
-+
-+        if convert_dict.has_key(k):
-+            k = convert_dict[k]
-+           
-+        setattr(cell_head, k, v)
-+
-+    return cell_head
-+
-+def _stringListToCharArr(str_list):
-+
-+    arr = c_char_p * len(str_list)
-+    char_arr = arr()
-+    for i, st in enumerate(str_list):
-+        if st:
-+            char_arr[i] = st
-+        else:
-+            char_arr[i] = None
-+
-+    return char_arr
-+
-+def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
-+
-+    sccats = struct_scCats()
-+    I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
-+
-+    refs = []        
-+    cats_rasts_core = []
-+    
-+    for cat_id, scatt_ids in cats.iteritems():
-+        cat_c_id = I_sc_add_cat(pointer(sccats))
-+        cats_rasts_core.append(cats_rasts[cat_id])
-+
-+        for scatt_id, dt in scatt_ids.iteritems():
-+            # if key is missing condition is always True (full scatter plor is computed)
-+                vals = dt['np_vals']
-+
-+                scatt_vals = scdScattData()
-+
-+                c_void_p = ctypes.POINTER(ctypes.c_void_p)
-+
-+                if cats_type == SC_SCATT_DATA:
-+                    vals[:] = 0
-+                elif cats_type == SC_SCATT_CONDITIONS:
-+                    pass
-+                else:
-+                    return None
-+                data_p = vals.ctypes.data_as(c_void_p)
-+                I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
-+
-+                refs.append(scatt_vals)
-+
-+                I_sc_insert_scatt_data(pointer(sccats),  
-+                                       pointer(scatt_vals),
-+                                       cat_c_id, scatt_id)
-+
-+    cats_rasts_c = _stringListToCharArr(cats_rasts_core)
-+
-+    return sccats, cats_rasts_c, refs
-+
-+def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
-+    cell_head = _regionToCellHead(region)
-+    
-+    
-+    ret = I_insert_patch_to_cat_rast(patch_rast, 
-+                                     pointer(cell_head), 
-+                                     cat_rast)
-+
-+    output_queue.put(ret)
-+
-+
-Index: gui/wxpython/scatt_plot/frame.py
-===================================================================
---- gui/wxpython/scatt_plot/frame.py	(revision 0)
-+++ gui/wxpython/scatt_plot/frame.py	(working copy)
-@@ -0,0 +1,826 @@
-+"""!
-+ at package scatt_plot.dialogs
-+
-+ at brief GUI.
-+
-+Classes:
-+
-+(C) 2013 by the GRASS Development Team
-+
-+This program is free software under the GNU General Public License
-+(>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+import os
-+import sys
-+
-+import wx
-+import wx.lib.scrolledpanel as scrolled
-+import wx.lib.mixins.listctrl as listmix
-+
-+from core import globalvar
-+from core.gcmd import GException, GError, RunCommand
-+
-+from gui_core.gselect import Select
-+from gui_core.dialogs import SetOpacityDialog, SimpleDialog
-+
-+from scatt_plot.controllers import ScattsManager
-+from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
-+from scatt_plot.scatt_core import idScattToidBands
-+from scatt_plot.dialogs import ManageBusyCursorMixin
-+from scatt_plot.plots import ScatterPlotWidget
-+from iclass.dialogs import ContrastColor
-+
-+try:
-+    from agw import aui
-+except ImportError:
-+    import wx.lib.agw.aui as aui
-+
-+
-+class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
-+    def __init__(self, parent, giface, iclass_mapwin = None,
-+                 id = wx.ID_ANY):
-+
-+        #wx.SplitterWindow.__init__(self, parent = parent, id = id,
-+        #                           style = wx.SP_LIVE_UPDATE)
-+        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
-+        ManageBusyCursorMixin.__init__(self, window=self)
-+
-+        self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface, 
-+                                              iclass_mapwin=iclass_mapwin)
-+
-+        # toobars
-+        self.toolbars = {}
-+        self.toolbars['mainToolbar'] = self._createMainToolbar()
-+        self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
-+        
-+        self._createCategoryPanel(self)
-+
-+        self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
-+
-+        self.mainsizer = wx.BoxSizer(wx.VERTICAL)
-+        self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
-+        self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
-+        self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
-+        self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
-+
-+        self.catsPanel.Hide()
-+        self.toolbars['editingToolbar'].Hide()
-+
-+        self.SetSizer(self.mainsizer)
-+        
-+        self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
-+        self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
-+        self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
-+
-+        #self.SetSashGravity(0.5)
-+        #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
-+        self.Layout()
-+
-+    def UpdateCur(self, busy):
-+        self.plot_panel.SetBusy(busy)
-+        ManageBusyCursorMixin.UpdateCur(self, busy)
-+
-+    def _selCatInIScatt(self):
-+         return False
-+
-+    def _createMainToolbar(self):
-+         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
-+
-+    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
-+        return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
-+
-+
-+    def NewScatterPlot(self, scatt_id, transpose):
-+        return self.plot_panel.NewScatterPlot(scatt_id, transpose)
-+
-+    def ShowPlotEditingToolbar(self, show):
-+        self.toolbars["editingToolbar"].Show(show)
-+        self.Layout()
-+
-+    def ShowCategoryPanel(self, show):
-+        self.catsPanel.Show(show)
-+        
-+        #if show:
-+        #    self.SetSashSize(5) 
-+        #else:
-+        #    self.SetSashSize(0)
-+        self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
-+        self.Layout()
-+
-+    def _createCategoryPanel(self, parent):
-+        self.catsPanel = wx.Panel(parent=parent)
-+        self.cats_list = CategoryListCtrl(parent=self.catsPanel, 
-+                                          cats_mgr=self.scatt_mgr.GetCategoriesManager(),
-+                                          sel_cats_in_iscatt=self._selCatInIScatt())
-+
-+        self.catsPanel.SetMinSize((-1, 100))
-+        self.catsPanel.SetInitialSize((-1, 150))
-+
-+        box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
-+                             label=' %s ' % _("Classes"),)
-+        catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
-+
-+        self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
-+
-+        catsSizer.Add(item=self.cats_list, proportion=1,  flag=wx.EXPAND | wx.TOP, border = 5)
-+        if self.toolbars['categoryToolbar']:
-+            catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
-+
-+        self.catsPanel.SetSizer(catsSizer)
-+
-+    def CleanUp(self):
-+        pass
-+
-+    def _createCategoryToolbar(self, parent):
-+        return CategoryToolbar(parent=parent,
-+                               scatt_mgr=self.scatt_mgr, 
-+                               cats_list=self.cats_list)
-+
-+class IScattDialog(wx.Dialog):
-+    def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
-+                 id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
-+        wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
-+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-+
-+        self.iscatt_panel = MapDispIScattPanel(self, giface)
-+
-+        mainsizer = wx.BoxSizer(wx.VERTICAL)
-+        mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
-+
-+        self.SetSizer(mainsizer)
-+
-+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
-+
-+        self.SetMinSize((300, 300))
-+
-+    def OnCloseWindow(self, event):
-+        event.Skip()
-+        #self.
-+
-+class MapDispIScattPanel(IClassIScattPanel):
-+    def __init__(self, parent, giface,
-+                 id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-+        IClassIScattPanel.__init__(self, parent=parent, giface=giface,
-+                                         id=id, style=style, **kwargs)
-+
-+    def _createScattMgr(self, guiparent, giface, iclass_mapwin):
-+        return ScattsManager(guiparent = self, giface = giface)
-+
-+    def _createMainToolbar(self):
-+         return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
-+
-+    def _selCatInIScatt(self):
-+         return True
-+
-+class ScatterPlotsPanel(scrolled.ScrolledPanel):
-+    def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
-+    
-+        scrolled.ScrolledPanel.__init__(self, parent)
-+        self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
-+
-+        self.scatt_mgr = scatt_mgr
-+
-+        self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
-+
-+        #self._createCategoryPanel()
-+        # Fancy gui
-+        self._mgr = aui.AuiManager(self.mainPanel)
-+        #self._mgr.SetManagedWindow(self)
-+
-+        self._mgr.Update()
-+
-+        self._doLayout()
-+        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
-+        self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
-+
-+        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
-+
-+        dlgSize = (-1, 400)
-+        #self.SetBestSize(dlgSize)
-+        #self.SetInitialSize(dlgSize)
-+        self.SetAutoLayout(1)
-+        #fix goutput's pane size (required for Mac OSX)
-+        #if self.gwindow:         
-+        #    self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
-+        self.ignore_scroll = 0
-+        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
-+
-+        self.scatt_i = 1
-+        self.scatt_id_scatt_i = {}
-+        self.transpose = {}
-+        self.scatts = {}
-+
-+        self.Bind(wx.EVT_CLOSE, self.OnClose)
-+        
-+        self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
-+
-+    def SetBusy(self, busy):
-+        for scatt in self.scatts.itervalues():
-+            scatt.UpdateCur(busy)
-+
-+    def CursorPlotMove(self, x, y, scatt_id):
-+
-+        try:
-+            x = int(round(x))
-+            y = int(round(y))
-+            coords = True
-+        except:
-+            coords = False
-+
-+        pane = self._getPane(scatt_id)
-+        caption = self._creteCaption(scatt_id)
-+        if coords:
-+            caption += "   %d, %d" % (x, y)
-+
-+        pane.Caption(caption)
-+        self._mgr.RefreshCaptions()
-+
-+    def _getPane(self, scatt_id):
-+        scatt_i = self.scatt_id_scatt_i[scatt_id]
-+        name = self._getScatterPlotName(scatt_i)
-+        return self._mgr.GetPane(name)
-+
-+    def ScatterPlotClosed(self, scatt_id):
-+
-+        scatt_i = self.scatt_id_scatt_i[scatt_id]
-+
-+        name = self._getScatterPlotName(scatt_i)
-+        pane = self._mgr.GetPane(name)
-+
-+        del self.scatt_id_scatt_i[scatt_id]
-+        del self.scatts[scatt_id]
-+
-+        if pane.IsOk(): 
-+          self._mgr.ClosePane(pane) 
-+        self._mgr.Update() 
-+
-+    def OnMouseWheel(self, event):
-+        #TODO very ugly find some better solution        
-+        self.ignore_scroll = 3
-+        event.Skip()
-+
-+    def ScrollChildIntoView(self, child):
-+        #For aui manager it does not work and returns position always to the top -> deactivated.
-+        pass
-+
-+    def OnPlotPaneClosed(self, event):
-+        if isinstance(event.pane.window, ScatterPlotWidget):
-+            event.pane.window.CleanUp()
-+
-+    def OnScrollChanged(self, event):
-+        wx.CallAfter(self.Layout)
-+
-+    def OnScroll(self, event):
-+        if self.ignore_scroll > 0:
-+            self.ignore_scroll -= 1
-+        else:
-+            event.Skip()
-+
-+        #wx.CallAfter(self._mgr.Update)
-+        #wx.CallAfter(self.Layout)
-+
-+    def _doLayout(self):
-+
-+        mainsizer = wx.BoxSizer(wx.VERTICAL)
-+        mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
-+        self.SetSizer(mainsizer)
-+
-+        self.Layout()
-+        self.SetupScrolling()
-+
-+    def OnClose(self, event):
-+        """!Close dialog"""
-+        #TODO
-+        print "closed"
-+        self.scatt_mgr.CleanUp()
-+        self.Destroy()
-+
-+    def OnSettings(self, event):
-+        pass
-+
-+    def _newScatterPlotName(self, scatt_id):
-+        name = self._getScatterPlotName(self.scatt_i) 
-+        self.scatt_id_scatt_i[scatt_id] = self.scatt_i
-+        self.scatt_i += 1
-+        return name
-+
-+    def _getScatterPlotName(self, i):
-+        name = "scatter plot %d" % i
-+        return name
-+
-+    def NewScatterPlot(self, scatt_id, transpose):
-+        #TODO needs to be resolved (should be in this class)
-+
-+        scatt = ScatterPlotWidget(parent = self.mainPanel, 
-+                                  scatt_mgr = self.scatt_mgr, 
-+                                  scatt_id = scatt_id, 
-+                                  transpose = transpose)
-+        scatt.plotClosed.connect(self.ScatterPlotClosed)
-+        self.transpose[scatt_id] = transpose
-+        
-+        caption = self._creteCaption(scatt_id)
-+        self._mgr.AddPane(scatt,
-+                           aui.AuiPaneInfo().Dockable(True).Floatable(True).
-+                           Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
-+                           Caption(caption).
-+                           Center().Position(1).MaximizeButton(True).
-+                           MinimizeButton(True).CaptionVisible(True).
-+                           CloseButton(True).Layer(0))
-+
-+        self._mgr.Update()
-+  
-+        self.SetVirtualSize(self.GetBestVirtualSize())
-+        self.Layout()
-+
-+        self.scatts[scatt_id] = scatt
-+
-+        return scatt
-+
-+    def _creteCaption(self, scatt_id):
-+
-+        transpose = self.transpose[scatt_id]
-+        bands = self.scatt_mgr.GetBands()
-+
-+        #TODO too low level
-+        b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
-+
-+        x_b =  bands[b1_id].split('@')[0]
-+        y_b = bands[b2_id].split('@')[0]
-+
-+        if transpose:
-+            tmp = x_b
-+            x_b = y_b
-+            y_b = tmp
-+
-+        return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
-+
-+    def GetScattMgr(self):
-+        return  self.scatt_mgr
-+
-+class CategoryListCtrl(wx.ListCtrl,
-+                       listmix.ListCtrlAutoWidthMixin):
-+                       #listmix.TextEditMixin):
-+
-+    def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
-+
-+        wx.ListCtrl.__init__(self, parent, id,
-+                             style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
-+                                     wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
-+        self.columns = ((_('Class name'), 'name'), )
-+                        #(_('Color'), 'color'))
-+
-+        self.sel_cats_in_iscatt = sel_cats_in_iscatt
-+
-+        self.Populate(columns = self.columns)
-+        
-+        self.cats_mgr = cats_mgr
-+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+
-+        self.rightClickedItemIdx = wx.NOT_FOUND
-+        
-+        listmix.ListCtrlAutoWidthMixin.__init__(self)
-+
-+        #listmix.TextEditMixin.__init__(self)
-+      
-+        self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
-+        self.Bind(wx.EVT_RIGHT_UP,            self.OnCategoryRightUp) #wxGTK
-+
-+        #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
-+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
-+             
-+        self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+        self.cats_mgr.deletedCategory.connect(self.Update)
-+        self.cats_mgr.addedCategory.connect(self.Update)
-+
-+    def Update(self, **kwargs):
-+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+    def InitCoreCats(self):
-+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+    def SetVirtualData(self, row, column, text):
-+        attr = self.columns[column][1]
-+        if attr == 'name':
-+            try:
-+                text.encode('ascii')
-+            except UnicodeEncodeError:
-+                GMessage(parent = self, message = _("Please use only ASCII characters."))
-+                return
-+
-+        cat_id = self.cats_mgr.GetCategories()[row]
-+
-+        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
-+        self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
-+        self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+        
-+        self.Select(row)
-+        
-+    def Populate(self, columns):
-+        for i, col in enumerate(columns):
-+            self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
-+
-+        #self.SetColumnWidth(0, 100)
-+        #self.SetColumnWidth(1, 100)
-+        
-+    def AddCategory(self):
-+
-+        self.cats_mgr.addedCategory.disconnect(self.Update)
-+        cat_id = self.cats_mgr.AddCategory()
-+        self.cats_mgr.addedCategory.connect(self.Update)
-+
-+        if cat_id < 0:
-+            GError(_("Maximum limit of categories number was reached."))
-+            return
-+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+                        
-+    def DeleteCategory(self):
-+        indexList = sorted(self.GetSelectedIndices(), reverse = True)
-+        cats = []
-+        for i in indexList:
-+            # remove temporary raster
-+            cat_id = self.cats_mgr.GetCategories()[i]
-+            
-+            cats.append(cat_id)
-+
-+            self.cats_mgr.deletedCategory.disconnect(self.Update)
-+            self.cats_mgr.DeleteCategory(cat_id)
-+            self.cats_mgr.deletedCategory.connect(self.Update)
-+            
-+        self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+        
-+    def OnSel(self, event):
-+        if self.sel_cats_in_iscatt:
-+            indexList = self.GetSelectedIndices()
-+            sel_cats = []
-+            cats = self.cats_mgr.GetCategories()
-+            for i in indexList:
-+                sel_cats.append(cats[i])       
-+
-+            if sel_cats:
-+                self.cats_mgr.SetSelectedCat(sel_cats[0])
-+        event.Skip()
-+
-+    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
-+        indices = []
-+        lastFound = -1
-+        while True:
-+            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
-+            if index == -1:
-+                break
-+            else:
-+                lastFound = index
-+                indices.append(index)
-+        return indices        
-+
-+    def DeselectAll(self):
-+        """!Deselect all items"""
-+        indexList = self.GetSelectedIndices()
-+        for i in indexList:
-+            self.Select(i, on = 0)
-+         
-+        # no highlight
-+        self.OnCategorySelected(None)
-+        
-+    def OnGetItemText(self, item, col):
-+        attr = self.columns[col][1]
-+        cat_id = self.cats_mgr.GetCategories()[item]
-+
-+        return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
-+
-+    def OnGetItemImage(self, item):
-+        return -1
-+
-+    def OnGetItemAttr(self, item):
-+        cat_id = self.cats_mgr.GetCategories()[item]
-+        c = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
-+
-+        back_c = wx.Colour(*map(int, c.split(':')))
-+        text_c = wx.Colour(*ContrastColor(back_c))
-+
-+        # if it is in scope of the method, gui falls, using self solved it 
-+        self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
-+        return self.l
-+
-+    def OnCategoryRightUp(self, event):
-+        """!Show context menu on right click"""
-+        item, flags = self.HitTest((event.GetX(), event.GetY()))
-+        if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
-+            self.rightClickedItemIdx = item
-+
-+        # generate popup-menu
-+        cat_idx = self.rightClickedItemIdx
-+
-+        cats = self.cats_mgr.GetCategories()
-+        cat_id = cats[cat_idx]
-+        showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
-+        
-+        menu = wx.Menu()
-+
-+        
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move to top"))
-+        self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
-+        if cat_idx == 0:
-+            menu.Enable(item_id, False)
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move to bottom"))
-+        self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
-+        if cat_idx == len(cats) - 1:
-+            menu.Enable(item_id, False)
-+
-+        menu.AppendSeparator()
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move category up"))
-+        self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
-+        if cat_idx == 0:
-+            menu.Enable(item_id, False)
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Move category down"))
-+        self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
-+        if cat_idx == len(cats) - 1:
-+            menu.Enable(item_id, False)
-+
-+        menu.AppendSeparator()
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Export class raster"))
-+        self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
-+
-+        menu.AppendSeparator()
-+
-+        if showed:
-+            text = _("Hide")
-+        else:
-+            text = _("Show")
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text = text)
-+        self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
-+                                                                attrs={'show' : not showed}), 
-+                                                                id=item_id) 
-+        
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Rename class"))
-+        self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Set color"))
-+        self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
-+
-+        item_id = wx.NewId()
-+        menu.Append(item_id, text=_("Change opacity level"))
-+        self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
-+
-+
-+
-+        self.PopupMenu(menu)
-+        menu.Destroy()
-+
-+    def OnExportCatRast(self, event):
-+        """!Export training areas"""
-+        #TODO
-+        #   GMessage(parent=self, message=_("No class raster to export."))
-+        #    return
-+
-+        cat_idx = self.rightClickedItemIdx
-+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+
-+        self.cats_mgr.ExportCatRast(cat_id)
-+
-+    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 OnMoveTop(self, event):
-+        cat_idx = self.rightClickedItemIdx
-+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+        self.cats_mgr.ChangePosition(cat_id, 0)
-+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+    def OnMoveBottom(self, event):
-+        cat_idx = self.rightClickedItemIdx
-+        cats = self.cats_mgr.GetCategories()
-+        cat_id = cats[cat_idx]
-+        self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
-+        self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+    def OnSetColor(self, event):
-+        """!Popup opacity level indicator"""
-+        cat_idx = self.rightClickedItemIdx
-+        cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+
-+        col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
-+        col = map(int, col.split(':'))
-+
-+        col_data =  wx.ColourData()
-+        col_data.SetColour(wx.Colour(*col))
-+
-+        dlg = wx.ColourDialog(self, col_data)
-+        dlg.GetColourData().SetChooseFull(True)
-+
-+        if dlg.ShowModal() == wx.ID_OK:
-+            color = dlg.GetColourData().GetColour().Get()
-+            color = ':'.join(map(str, color))
-+            self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
-+
-+        dlg.Destroy()
-+
-+    def OnPopupOpacityLevel(self, event):
-+        """!Popup opacity level indicator"""
-+
-+        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
-+        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+        value = cat_attrs['opacity'] * 100
-+        name = cat_attrs['name']
-+
-+        dlg = SetOpacityDialog(self, opacity = value,
-+                               title = _("Change opacity of class <%s>" % name))
-+
-+        dlg.applyOpacity.connect(lambda value:
-+                                 self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
-+        dlg.CentreOnParent()
-+
-+        if dlg.ShowModal() == wx.ID_OK:
-+            self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
-+        
-+        dlg.Destroy()
-+
-+    def OnRename(self, event):
-+        cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
-+        cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+
-+        dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
-+        dlg.CentreOnParent()
-+
-+        while True:
-+            if dlg.ShowModal() == wx.ID_OK:
-+                name = dlg.GetNewName().strip()
-+                if not name:
-+                    GMessage(parent=self, message=_("Empty name was inserted."))
-+                else:
-+                    self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
-+                    break
-+            else:
-+                break
-+
-+        dlg.Destroy()
-+
-+    def _setCatAttrs(self, cat_id, attrs):
-+        self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
-+        self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
-+        self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+
-+class RenameClassDialog(SimpleDialog):
-+    def __init__(self, parent, old_name, title=("Change class name")):
-+        SimpleDialog.__init__(self, parent, title)
-+
-+        self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY) 
-+        self.name.SetValue(old_name)
-+
-+        self.dataSizer.Add(self.name, proportion = 0,
-+                           flag = wx.EXPAND | wx.ALL, border = 1)
-+
-+        self.panel.SetSizer(self.sizer)
-+        self.name.SetMinSize((200, -1))
-+        self.sizer.Fit(self)
-+
-+    def GetNewName(self):
-+        return self.name.GetValue()
-+
-+class AddScattPlotDialog(wx.Dialog):
-+
-+    def __init__(self, parent, bands, id  = wx.ID_ANY):
-+        
-+        wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
-+
-+        self.bands = bands
-+
-+        self.scatt_id = None
-+
-+        self._createWidgets()
-+
-+    def _createWidgets(self):
-+
-+        self.labels = {}
-+        self.params = {}
-+
-+        self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("x axis:"))
-+
-+        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 = _("y axis:"))
-+
-+        self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
-+                                     choices = self.bands,
-+                                     style = wx.CB_READONLY, size = (350, 30))
-+
-+        # buttons
-+        self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
-+        
-+        self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
-+
-+        self._layout()
-+
-+    def _layout(self):
-+
-+        border = wx.BoxSizer(wx.VERTICAL) 
-+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
-+
-+        regionSizer = wx.BoxSizer(wx.HORIZONTAL)
-+
-+        dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label, 
-+                                                    sel = self.band_1_ch))
-+
-+        dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label, 
-+                                                    sel = self.band_2_ch))
-+
-+        # buttons
-+        self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
-+
-+        self.btnsizer.Add(item = self.btn_close, proportion = 0,
-+                          flag = wx.ALL | wx.ALIGN_CENTER,
-+                          border = 10)
-+        
-+        self.btnsizer.Add(item = self.btn_ok, proportion = 0,
-+                          flag = wx.ALL | wx.ALIGN_CENTER,
-+                          border = 10)
-+
-+        dialogSizer.Add(item = self.btnsizer, proportion = 0,
-+                        flag = wx.ALIGN_CENTER)
-+
-+        border.Add(item = dialogSizer, proportion = 0,
-+                   flag = wx.ALL, border = 5)
-+
-+        self.SetSizer(border)
-+        self.Layout()
-+        self.Fit()
-+
-+        # bindings
-+        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
-+        self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
-+
-+    def _addSelectSizer(self, title, sel): 
-+        """!Helper layout function.
-+        """
-+        selSizer = wx.BoxSizer(orient = wx.VERTICAL)
-+
-+        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
-+        selTitleSizer.Add(item = title, proportion = 1,
-+                          flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
-+
-+        selSizer.Add(item = selTitleSizer, proportion = 0,
-+                     flag = wx.EXPAND)
-+
-+        selSizer.Add(item = sel, proportion = 1,
-+                     flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
-+                     border = 5)
-+
-+        return selSizer
-+
-+    def OnClose(self, event):
-+        """!Close dialog
-+        """
-+        if not self.IsModal():
-+            self.Destroy()
-+        event.Skip()
-+
-+    def OnOk(self, event):
-+        """!
-+        """
-+        band_1 = self.band_1_ch.GetSelection()
-+        band_2 = self.band_2_ch.GetSelection()
-+
-+        if band_1 == band_2:
-+            GError(_("Selected bands must be different."))
-+            return
-+        
-+        #TODO axes selection
-+        if band_1 > band_2:
-+            tmp_band = band_2
-+            band_2 = band_1
-+            band_1 = band_2
-+
-+        self.scatt_id = idBandsToidScatt(band_1, band_2, len(self.bands))
-+
-+        event.Skip()
-+
-+    def GetScattId(self):
-+        return self.scatt_id
-+
 Index: gui/wxpython/vnet/dialogs.py
 ===================================================================
---- gui/wxpython/vnet/dialogs.py	(revision 57760)
+--- gui/wxpython/vnet/dialogs.py	(revision 57820)
 +++ gui/wxpython/vnet/dialogs.py	(working copy)
 @@ -109,7 +109,7 @@
  
@@ -6825,7 +6354,7 @@
          self.statusItems = []
 Index: gui/wxpython/vnet/toolbars.py
 ===================================================================
---- gui/wxpython/vnet/toolbars.py	(revision 57760)
+--- gui/wxpython/vnet/toolbars.py	(revision 57820)
 +++ gui/wxpython/vnet/toolbars.py	(working copy)
 @@ -19,9 +19,9 @@
  
@@ -6841,7 +6370,7 @@
  class PointListToolbar(BaseToolbar):
 Index: gui/wxpython/Makefile
 ===================================================================
---- gui/wxpython/Makefile	(revision 57760)
+--- gui/wxpython/Makefile	(revision 57820)
 +++ gui/wxpython/Makefile	(working copy)
 @@ -13,7 +13,7 @@
  	$(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
@@ -6863,18 +6392,3 @@
  DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
  
  default: $(DSTFILES)
-Index: gui/icons/grass/polygon.png
-===================================================================
-Cannot display: file marked as a binary type.
-svn:mime-type = application/octet-stream
-Index: gui/icons/grass/polygon.png
-===================================================================
---- gui/icons/grass/polygon.png	(revision 57760)
-+++ gui/icons/grass/polygon.png	(working copy)
-
-Property changes on: gui/icons/grass/polygon.png
-___________________________________________________________________
-Added: svn:mime-type
-## -0,0 +1 ##
-+application/octet-stream
-\ No newline at end of property



More information about the grass-commit mailing list