[GRASS-SVN] r49815 - in grass/trunk/gui: icons/grass wxpython wxpython/docs wxpython/iclass wxpython/vdigit wxpython/xml

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Dec 18 14:22:54 EST 2011


Author: annakrat
Date: 2011-12-18 11:22:53 -0800 (Sun, 18 Dec 2011)
New Revision: 49815

Added:
   grass/trunk/gui/icons/grass/layer-opacity.png
   grass/trunk/gui/icons/grass/table-manager.png
   grass/trunk/gui/wxpython/docs/wxGUI.IClass.html
   grass/trunk/gui/wxpython/iclass/
   grass/trunk/gui/wxpython/iclass/dialogs.py
   grass/trunk/gui/wxpython/iclass/digit.py
   grass/trunk/gui/wxpython/iclass/frame.py
   grass/trunk/gui/wxpython/iclass/plots.py
   grass/trunk/gui/wxpython/iclass/statistics.py
   grass/trunk/gui/wxpython/iclass/toolbars.py
Modified:
   grass/trunk/gui/wxpython/Makefile
   grass/trunk/gui/wxpython/vdigit/mapwindow.py
   grass/trunk/gui/wxpython/vdigit/toolbars.py
   grass/trunk/gui/wxpython/vdigit/wxdigit.py
   grass/trunk/gui/wxpython/wxpythonlib.dox
   grass/trunk/gui/wxpython/xml/menudata.xml
Log:
wxIClass: initial commit

Added: grass/trunk/gui/icons/grass/layer-opacity.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/layer-opacity.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: grass/trunk/gui/icons/grass/table-manager.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/table-manager.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/Makefile	2011-12-18 19:22:53 UTC (rev 49815)
@@ -10,12 +10,12 @@
 ETCDIR = $(ETC)/gui/wxpython
 
 SRCFILES := $(wildcard icons/*.* scripts/* xml/*) \
-	$(wildcard core/* dbmgr/* gcp/* gmodeler/* gui_core/* lmgr/* location_wizard/* \
+	$(wildcard core/* dbmgr/* gcp/* gmodeler/* gui_core/* iclass/* lmgr/* location_wizard/* \
 	mapdisp/* modules/* nviz/* psmap/* vdigit/* wxplot/* ) \
 	gis_set.py gis_set_error.py wxgui.py README
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
-PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,core dbmgr gcp gmodeler gui_core lmgr location_wizard \
+PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,core dbmgr gcp gmodeler gui_core iclass lmgr location_wizard \
 	mapdisp modules nviz psmap vdigit wxplot)
 DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
 

Added: grass/trunk/gui/wxpython/docs/wxGUI.IClass.html
===================================================================
--- grass/trunk/gui/wxpython/docs/wxGUI.IClass.html	                        (rev 0)
+++ grass/trunk/gui/wxpython/docs/wxGUI.IClass.html	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,90 @@
+<!-- meta page description: wxGUI wxIClass -->
+<!-- meta page index: wxGUI -->
+<h2>DESCRIPTION</h2>
+
+<p>
+<b>WxIClass</b> is
+a <em><a href="wxGUI.html">wxGUI</a></em> compoment which allows the
+user to create training areas and generate spectral signatures.
+The resulting signature file can be used as input for
+<em><a href="i.maxlik.html">i.maxlik</a></em>
+or as a seed signature file for
+<em><a href="i.cluster.html">i.cluster</a></em>.
+The modeler can be launched
+from the menu <tt>Imagery -> Classify image -> Interactive input for supervised classification</tt>.
+
+<p>
+<em>wxIClass</em> currently allows to:
+
+<ul>
+  <li>create training areas (using vector digitizer)</li>
+  <li>show histograms for each band and class (category)</li>
+  <li>show coincidence plots for each band</li>
+  <li>show raster cells that match training areas
+   (within the number of standard deviations specified)</li>
+  <li>specify color of class</li>
+  <li>write signature file</li>
+
+</ul>
+
+<p>
+<em>wxIClass</em> performs the first pass in the GRASS two-pass supervised image
+classification process; the GRASS program 
+<em><a href="i.maxlik.html">i.maxlik</a></em> executes the second pass.
+Both programs must be run to generate a classified map in GRASS
+raster format.
+
+<p>
+<em>wxIClass</em> is an interactive program that allows the user to create
+multiple training areas for multiple classes and calculate the spectral
+signatures based on the cells that are within the training areas.
+During this process the user will be
+shown histograms for each image band.
+The user can also display the cells of the image bands which fall within
+a user-specified number of standard deviations from the means in the spectral signature.
+By doing this, the user can see how much of the image
+is likely to be put into the class associated with the signature.
+
+<p>
+The spectral signatures are composed of region means and covariance matrices.
+These region means and covariance matrices are used in
+the second pass (<em><a href="i.maxlik.html">i.maxlik</a></em>)
+to classify the image.
+
+<p>
+Alternatively, the spectral signatures generated by <em>wxIClass</em> can be
+used for seed means for the clusters in
+<em><a href="i.cluster.html">i.cluster</a></em>.
+
+<h2>SEE ALSO</h2>
+
+<em>
+<a href="wxGUI.html">wxGUI</a>
+</em>
+
+<p>
+Other wxGUI components:<br>
+<em>
+<a href="wxGUI.Vector_Digitizer.html">Vector Digitizer</a><br>
+<a href="wxGUI.Attribute_Table_Manager.html">Attribute Table Manager</a><br>
+<a href="wxGUI.Nviz.html">3D Viewer</a><br>
+<a href="wxGUI.GCP_Manager.html">Manage Ground Control Points</a><br>
+<a href="wxGUI.PsMap.html">Cartographic Composer</a><br>
+<a href="wxGUI.Modeler.html">Graphical Modeler</a><br>
+</em>
+
+<p>
+See also
+user <a href="http://grass.osgeo.org/wiki/WxIClass">wiki</a> page
+and <a href="http://trac.osgeo.org/grass/wiki/wxGUIDevelopment/wxIClass">development</a> page.
+
+<h2>AUTHOR</h2>
+
+Anna Kratochvilova,
+ <a href="http://www.cvut.cz">Czech Technical University in Prague</a>, Czech Republic
+ 
+Vaclav Petras,
+ <a href="http://www.cvut.cz">Czech Technical University in Prague</a>, Czech Republic
+
+<p>
+<i>$Date: 2011-10-14 19:52:31 +0200 (Fri, 14 Oct 2011) $</i>

Added: grass/trunk/gui/wxpython/iclass/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/dialogs.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/dialogs.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,336 @@
+"""!
+ at package iclass::toolbars
+
+ at brief wxIClass dialogs
+
+Classes:
+ - dialogs::IClassGroupDialog
+ - dialogs::IClassMapDialog
+ - dialogs::IClassCategoryManagerDialog
+ - dialogs::CategoryListCtrl
+
+(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.mixins.listctrl as listmix
+
+from core               import globalvar
+from gui_core.dialogs   import ElementDialog, GroupDialog
+from gui_core           import gselect
+from iclass.statistics  import Statistics, BandStatistics
+
+import grass.script as grass
+
+class IClassGroupDialog(ElementDialog):
+    """!Dialog for imagery group selection"""
+    def __init__(self, parent, group = None, title = _("Select imagery group"), id = wx.ID_ANY):
+        """!
+        Does post init and layout.
+        
+        @param gui parent
+        @param title dialog window title
+        @param id wx id
+        """
+        ElementDialog.__init__(self, parent, title, label = _("Name of imagery group:"))
+        
+        self.element = gselect.Select(parent = self.panel, type = 'group',
+                                          mapsets = [grass.gisenv()['MAPSET']],
+                                          size = globalvar.DIALOG_GSELECT_SIZE)
+        if group:
+            self.element.SetValue(group)
+        self.editGroup = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                   label = _("Create/edit group..."))
+        self.editGroup.Bind(wx.EVT_BUTTON, self.OnEditGroup)
+        self.PostInit()
+        
+        self.__Layout()
+        self.SetMinSize(self.GetSize())
+
+    def __Layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        self.dataSizer.Add(self.editGroup, proportion = 0,
+                      flag = wx.ALL, border = 5)
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def GetGroup(self):
+        """!Returns selected group"""
+        return self.GetElement()
+        
+    def OnEditGroup(self, event):
+        """!Launch edit group dialog"""
+        dlg = GroupDialog(parent = self, defaultGroup = self.element.GetValue())
+
+        dlg.ShowModal()
+        gr = dlg.GetSelectedGroup()
+        if gr in dlg.GetExistGroups():
+            self.element.SetValue(gr)
+        dlg.Destroy()
+        
+class IClassMapDialog(ElementDialog):
+    """!Dialog for adding raster map"""
+    def __init__(self, parent, title = _("Add raster map"), id = wx.ID_ANY):
+        """!
+        Does post init and layout.
+        
+        @param gui parent
+        @param title dialog window title
+        @param id wx id
+        """
+        ElementDialog.__init__(self, parent, title, label = _("Name of raster map:"))
+        
+        self.element = gselect.Select(parent = self.panel, type = 'raster',
+                                      size = globalvar.DIALOG_GSELECT_SIZE)
+        #self.firstMapCheck = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                         #label = _("Add raster map to Training display"))
+        #self.secondMapCheck = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                         #label = _("Add raster map to Preview display"))
+        # in user settings
+        #self.firstMapCheck.SetValue(True)
+        #self.secondMapCheck.SetValue(True)
+        
+        self.PostInit()
+        
+        self.__Layout()
+        self.SetMinSize(self.GetSize())
+
+    def __Layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        #self.dataSizer.Add(self.firstMapCheck, proportion = 0,
+                      #flag = wx.ALL, border = 5)
+        #self.dataSizer.Add(self.secondMapCheck, proportion = 0,
+                      #flag = wx.ALL, border = 5)
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def GetRasterMap(self):
+        """!Returns selected raster map"""
+        return self.GetElement()
+        
+    #def IfAddToFirstMap(self):
+        #return self.firstMapCheck.IsChecked()
+        
+    #def IfAddToSecondMap(self):
+        #return self.secondMapCheck.IsChecked()
+        
+        
+class IClassCategoryManagerDialog(wx.Dialog):
+    """!Dialog for managing categories (classes).
+    
+    Alows adding, deleting class and changing its name and color.
+    """
+    def __init__(self, parent, title = _("Class manager"), id = wx.ID_ANY):
+        """!
+        Does post init and layout.
+        
+        @param gui parent
+        @param title dialog window title
+        @param id wx id
+        """
+        wx.Dialog.__init__(self, parent = parent, title = title, id = id)
+        
+        panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox(panel, id = wx.ID_ANY,
+                           label = " %s " % _("Classes"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridSizer.AddGrowableCol(0)
+        gridSizer.AddGrowableRow(2)
+        self.catList = CategoryListCtrl(panel, mapwindow = parent, statistics = parent.statisticsDict,
+                                         statisticsList = parent.statisticsList)
+        addButton = wx.Button(panel, id = wx.ID_ADD)
+        deleteButton = wx.Button(panel, id = wx.ID_DELETE)
+        
+        gridSizer.Add(item = self.catList, pos = (0, 0), span = (3, 1), flag = wx.EXPAND)
+        gridSizer.Add(item = addButton, pos = (0, 1), flag = wx.EXPAND)
+        gridSizer.Add(item = deleteButton, pos = (1, 1), flag = wx.EXPAND)
+        
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = sizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        closeButton = wx.Button(panel, id = wx.ID_CLOSE)
+        btnSizer.Add(item = wx.Size(-1, -1), proportion = 1, flag = wx.EXPAND)
+        btnSizer.Add(item = closeButton, proportion = 0, flag = wx.ALIGN_RIGHT)
+        mainSizer.Add(item = btnSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+        
+        addButton.Bind(wx.EVT_BUTTON, self.OnAddCategory)
+        deleteButton.Bind(wx.EVT_BUTTON, self.OnDeleteCategory)
+        closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        
+        panel.SetSizer(mainSizer)
+        mainSizer.Fit(panel)
+
+        self.SetSize((-1, 250))
+
+        self.Layout()
+
+    def OnAddCategory(self, event):
+        self.catList.AddCategory()
+        
+    def OnDeleteCategory(self, event):
+        self.catList.DeleteCategory()
+        
+    def OnClose(self, event):
+        self.catList.UpdateChoice()
+        if not isinstance(event, wx.CloseEvent):
+            self.Destroy()
+            
+        event.Skip()
+        
+class CategoryListCtrl(wx.ListCtrl,
+                       listmix.ListCtrlAutoWidthMixin,
+                       listmix.TextEditMixin):
+    """! Widget for controling list of classes (categories).
+    
+    CategoryListCtrl updates choice in mapwindow and removes raster map
+    when deleting class (category).
+    It uses virtual data in the terms of @c wx.ListCtrl.
+    
+    @todo statistics and categories are managed here directly,
+    it could be better to use some interface
+    @todo delete vector features after deleting class
+    """
+    def __init__(self, parent, mapwindow, statistics, statisticsList, id = wx.ID_ANY):
+        """!
+        @param parent gui parent
+        @param mapwindow mapwindow instance with iclass toolbar and remove raster method
+        @param statistics dictionary of statistics (defined in statistics.py)
+        @param statisticsList list of statistics
+        @param id wx id
+        """
+        wx.ListCtrl.__init__(self, parent, id,
+                             style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
+        self.columns = ((_('Class name'), 'name'),
+                        (_('Color'), 'color'))
+        self.Populate(columns = self.columns)
+        self.mapWindow = mapwindow
+        self.statisticsDict = statistics
+        self.statisticsList = statisticsList
+        self.SetItemCount(len(statisticsList))
+        
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        listmix.TextEditMixin.__init__(self)
+        
+        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
+        
+    def SetVirtualData(self, row, column, text):
+        attr = self.columns[column][1]
+        setattr(self.statisticsDict[self.statisticsList[row]], attr, text)
+        
+        self.UpdateChoice()
+        toolbar = self.mapWindow.toolbars['iClass']
+        toolbar.choice.SetSelection(row)
+        
+        if attr == 'name':
+            self.mapWindow.UpdateRasterName(text, toolbar.GetSelectedCategoryIdx())
+            
+    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):
+        st = Statistics()
+        if self.statisticsList:
+            cat = max(self.statisticsList) + 1
+        else:
+            cat = 1
+        defaultName = 'class' + '_' + str(cat) # intentionally not translatable
+        defaultColor = '0:0:0'
+        st.SetBaseStatistics(cat = cat, name = defaultName, color = defaultColor)
+        self.statisticsDict[cat] = st
+        self.statisticsList.append(cat)
+        self.SetItemCount(len(self.statisticsList))
+        
+        self.UpdateChoice()
+                
+    def DeleteCategory(self):
+        indexList = sorted(self.GetSelectedIndices(), reverse = True)
+        for i in indexList:
+            # remove temporary raster
+            name = self.statisticsDict[self.statisticsList[i]].rasterName
+            self.mapWindow.RemoveTempRaster(name)
+            
+            del self.statisticsDict[self.statisticsList[i]]
+            del self.statisticsList[i]
+        self.SetItemCount(len(self.statisticsList))
+        
+        self.UpdateChoice()
+        
+        
+        # delete vector items!
+    
+    def UpdateChoice(self):
+        toolbar = self.mapWindow.toolbars['iClass']
+        name = toolbar.GetSelectedCategoryName()
+        catNames = []
+        for cat in self.statisticsList:
+            catNames.append(self.statisticsDict[cat].name)
+        toolbar.SetCategories(catNames = catNames, catIdx = self.statisticsList)
+        if name in catNames:
+            toolbar.choice.SetStringSelection(name)
+        elif catNames:
+            toolbar.choice.SetSelection(0)
+            
+        if toolbar.choice.IsEmpty():
+            toolbar.EnableControls(False)
+        else:
+            toolbar.EnableControls(True)
+        # don't forget to update maps, histo, ...
+        
+    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
+        indices = []
+        lastFound = -1
+        while True:
+            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
+            if index == -1:
+                break
+            else:
+                lastFound = index
+                indices.append(index)
+        return indices
+        
+    def OnEdit(self, event):
+        currentItem = event.m_itemIndex
+        currentCol = event.m_col
+        if currentCol == 1:
+            dlg = wx.ColourDialog(self)
+            dlg.GetColourData().SetChooseFull(True)
+
+            if dlg.ShowModal() == wx.ID_OK:
+                color = dlg.GetColourData().GetColour().Get()
+                color = ':'.join(map(str, color))
+                self.SetVirtualData(currentItem, currentCol, color)
+            dlg.Destroy()
+            
+        event.Skip()
+        
+    def OnGetItemText(self, item, col):
+        cat = self.statisticsList[item]
+        return getattr(self.statisticsDict[cat], self.columns[col][1]) 
+
+    def OnGetItemImage(self, item):
+        return -1
+
+    def OnGetItemAttr(self, item):
+        return None

Added: grass/trunk/gui/wxpython/iclass/digit.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/digit.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/digit.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,81 @@
+"""!
+ at package iclass::digit
+
+ at brief wxIClass digitizer classes
+
+Classes:
+ - digit::IClassVDigit
+ - digit::IClassVDigitWindow
+
+(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
+
+from vdigit.mapwindow import VDigitWindow
+from vdigit.wxdigit import IVDigit
+
+class IClassVDigitWindow(VDigitWindow):
+    """! Class similar to VDigitWindow but specialized for wxIClass."""
+    def __init__(self, parent, map):
+        """!
+        
+        @a parent should has toolbar providing current class (category).
+        
+        @param parent gui parent
+        @param map map renderer instance
+        """
+        VDigitWindow.__init__(self, parent = parent, Map = map)
+        
+    def _onLeftDown(self, event):
+        action = self.toolbar.GetAction()
+        if not action:
+            return
+            
+        cat = self.GetCurrentCategory()
+        
+        if cat is None and action == "addLine":
+            dlg = wx.MessageDialog(parent = self.parent,
+                      message = _("In order to create a training area, "
+                                  "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.parent.OnCategoryManager(None)
+                
+            dlg.Destroy()
+            event.Skip()
+            return
+        
+        super(IClassVDigitWindow, self)._onLeftDown(event)
+        
+    def _addRecord(self):
+        return False
+        
+    def GetCurrentCategory(self):
+        """!Returns current category (class).
+        
+        Category should be assigned to new features (areas).
+        It is taken from parent's toolbar.
+        """
+        return self.parent.GetToolbar("iClass").GetSelectedCategoryIdx()
+        
+class IClassIVDigit(IVDigit):
+    """! Class similar to IVDigit but specialized for wxIClass."""
+    def __init__(self, mapwindow):
+        IVDigit.__init__(self, mapwindow)
+        
+    def _getNewFeaturesLayer(self):
+        return 1
+        
+    def _getNewFeaturesCat(self):
+        cat = self.mapWindow.GetCurrentCategory()
+        return cat

Added: grass/trunk/gui/wxpython/iclass/frame.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/frame.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/frame.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,808 @@
+"""!
+ at package iclass::frame
+
+ at brief wxIClass frame with toolbar for digitizing training areas and
+for spectral signature analysis.
+
+Classes:
+ - frame::IClassMapFrame
+ - frame::MapManager
+
+(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 os
+import sys
+
+#if __name__ == "__main__":
+sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+import copy
+import wx
+
+from ctypes import *
+
+from core import globalvar
+try:
+    from grass.lib.imagery import *
+    from grass.lib.vector import *
+except ImportError, e:
+    sys.stderr.write(_("Loading imagery lib failed"))
+
+
+#sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+import grass.script as grass
+
+from mapdisp            import statusbar as sb
+from mapdisp.mapwindow  import BufferedWindow
+from vdigit.toolbars    import VDigitToolbar
+from gui_core.mapdisp   import DoubleMapFrame
+from core.render        import Map, MapLayer
+from core.gcmd          import RunCommand, GMessage
+from gui_core.dialogs   import SetOpacityDialog
+import grass.script as grass
+
+from iclass.digit       import IClassVDigitWindow, IClassIVDigit
+from iclass.toolbars    import IClassMapToolbar, IClassMiscToolbar,\
+                               IClassToolbar, IClassMapManagerToolbar
+from iclass.statistics  import Statistics, BandStatistics
+from iclass.dialogs     import CategoryListCtrl, IClassCategoryManagerDialog,\
+                               IClassGroupDialog, IClassMapDialog
+from iclass.plots       import PlotPanel
+
+        
+class IClassMapFrame(DoubleMapFrame):
+    """! wxIClass main frame
+    
+    It has two map windows one for digitizing training areas and one for
+    result preview.
+    It generates histograms, raster maps and signature files using
+    @c I_iclass_* functions from C imagery library.
+    
+    It is wxGUI counterpart of old i.class module.
+    """
+    def __init__(self, parent = None, title = _("Supervised Classification Tool"),
+                 toolbars = ["iClassMap", "iClassMisc", "vdigit", "iClass"],
+                 size = (800, 600), name = 'IClassWindow', **kwargs):
+        """!
+        @param parent (no parent is expected)
+        @param title window title
+        @param toolbars dictionary of active toolbars (defalult value represents all toolbars)
+        @param size default size
+        """
+        
+        DoubleMapFrame.__init__(self, parent = parent, title = title,
+                                name = name,
+                                firstMap = Map(), secondMap = Map(),
+                                **kwargs)
+        
+        self.firstMapWindow = IClassVDigitWindow(self, map = self.firstMap)
+        self.secondMapWindow = BufferedWindow(self, Map = self.secondMap)
+        self.MapWindow = self.firstMapWindow # current by default
+        
+        self._bindWindowsActivation()
+        
+        self.SetSize(size)
+        #
+        # Add toolbars
+        #
+        
+        toolbarsCopy = toolbars[:]
+        self.AddToolbar(toolbarsCopy.pop(0))
+        if sys.platform == 'win32':
+            toolbarsCopy.reverse()
+        for toolb in toolbarsCopy:
+            self.AddToolbar(toolb)
+        self.firstMapWindow.SetToolbar(self.toolbars['vdigit'])
+        
+        self.GetMapToolbar().GetActiveMapTool().Bind(wx.EVT_CHOICE, self.OnUpdateActive)
+        
+        #
+        # 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))
+        self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
+        
+        self.statusbarManager.Update()
+        
+        self.trainingMapManager = MapManager(self, mapWindow = self.GetFirstWindow(),
+                                             Map = self.GetFirstMap())
+        self.previewMapManager = MapManager(self, mapWindow = self.GetSecondWindow(),
+                                            Map = self.GetSecondMap())
+                                           
+        self.InitStatistics()
+        #self.InitTestStatistics()
+        
+        # PyPlot init
+        self.plotPanel = PlotPanel(self, statDict = self.statisticsDict,
+                                   statList = self.statisticsList)
+                                   
+        self._addPanes()
+        self._mgr.Update()
+        
+        self.trainingMapManager.SetToolbar(self.toolbars['iClassTrainingMapManager'])
+        self.previewMapManager.SetToolbar(self.toolbars['iClassPreviewMapManager'])
+        
+        # default action
+        self.OnPan(event = None)
+        
+        wx.CallAfter(self.AddTrainingAreaMap)
+        
+        #self.dialogs['category'] = None
+        
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        
+    def OnCloseWindow(self, event):
+        self.Destroy()
+        
+    def __del__(self):
+        """! Frees C structs and removes vector map and all raster maps."""
+        I_free_signatures(self.signatures)
+        I_free_group_ref(self.refer)
+        for st in self.cStatisticsDict.values():
+            I_iclass_free_statistics(st)
+            
+        self.RemoveTempVector()
+        for i in self.statisticsList:
+            self.RemoveTempRaster(self.statisticsDict[i].rasterName)
+            
+    def OnHelp(self, event):
+        """!Show help page"""
+        grass.run_command('g.manual',
+                          entry = 'wxGUI.IClass')
+        
+    def CreateTempVector(self):
+        """!Create temporary vector map for training areas"""
+        vectorPath = grass.tempfile(create = False)
+        vectorName = 'trAreas' + os.path.basename(vectorPath).replace('.','')
+
+        cmd = ('v.edit', {'tool': 'create',
+                          'map': vectorName})
+        
+        ret = RunCommand(prog = cmd[0],
+                         parent = self,
+                         overwrite = True,
+                         **cmd[1])
+        if ret != 0:
+            return False
+            
+        return vectorName
+        
+    def RemoveTempVector(self):
+        """!Removes temporary vector map with training areas"""
+        ret = RunCommand(prog = 'g.remove',
+                         parent = self,
+                         vect = self.trainingAreaVector)
+        if ret != 0:
+            return False
+        return True
+        
+    def RemoveTempRaster(self, raster):
+        """!Removes temporary raster maps"""
+        ret = RunCommand(prog = 'g.remove',
+                         parent = self,
+                         rast = raster)
+        if ret != 0:
+            return False
+        return True
+        
+    def AddToolbar(self, name):
+        """!Add defined toolbar to the window
+        
+        Currently known toolbars are:
+         - 'iClassMap'          - basic map toolbar
+         - 'iClass'             - iclass tools
+         - 'iClassMisc'         - miscellaneous (help)
+         - 'vdigit'             - digitizer toolbar (areas)
+         
+         Toolbars 'iClassPreviewMapManager' are added in _addPanes().
+        """
+        if name == "iClassMap":
+            self.toolbars[name] = IClassMapToolbar(self)
+            
+            self._mgr.AddPane(self.toolbars[name],
+                              wx.aui.AuiPaneInfo().
+                              Name(name).Caption(_("Map Toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).Row(1).
+                              BestSize((self.toolbars[name].GetBestSize())))
+                              
+        if name == "iClass":
+            self.toolbars[name] = IClassToolbar(self)
+            
+            self._mgr.AddPane(self.toolbars[name],
+                              wx.aui.AuiPaneInfo().
+                              Name(name).Caption(_("IClass Toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).Row(2).
+                              BestSize((self.toolbars[name].GetBestSize())))
+                              
+        if name == "iClassMisc":
+            self.toolbars[name] = IClassMiscToolbar(self)
+            
+            self._mgr.AddPane(self.toolbars[name],
+                              wx.aui.AuiPaneInfo().
+                              Name(name).Caption(_("IClass Misc Toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).Row(2).
+                              BestSize((self.toolbars[name].GetBestSize())))
+
+        if name == "vdigit":
+            self.toolbars[name] = VDigitToolbar(self, MapWindow = self.GetFirstWindow(),
+                                                tools = ['addArea', 'moveVertex', 'addVertex',
+                                                         'removeVertex', 'editLine',
+                                                         'moveLine', 'deleteLine'])
+            
+            self._mgr.AddPane(self.toolbars[name],
+                              wx.aui.AuiPaneInfo().
+                              Name(name).Caption(_("Digitization Toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).Row(2).
+                              BestSize((self.toolbars[name].GetBestSize())))
+                              
+    def _addPanes(self):
+        """!Add mapwindows and toolbars to aui manager"""
+        name = 'iClassPreviewMapManager'
+        self.toolbars[name] = IClassMapManagerToolbar(self, self.previewMapManager)
+        self._mgr.AddPane(self.toolbars[name],
+                          wx.aui.AuiPaneInfo().ToolbarPane().Movable().
+                          Name(name).
+                          CloseButton(False).Center().Layer(0).
+                          BestSize((self.toolbars[name].GetBestSize())))
+                          
+        self._mgr.AddPane(self.GetSecondWindow(), wx.aui.AuiPaneInfo().
+                  Name("preview").Caption(_("Preview Display")).
+                  Dockable(False).Floatable(False).CloseButton(False).
+                  Center().Layer(0))
+                  
+        name = 'iClassTrainingMapManager'
+        self.toolbars[name] = IClassMapManagerToolbar(self, self.trainingMapManager)
+        self._mgr.AddPane(self.toolbars[name],
+                          wx.aui.AuiPaneInfo().ToolbarPane().
+                          Name(name).
+                          CloseButton(False).Center().Layer(0).
+                          BestSize((self.toolbars[name].GetBestSize())))
+                          
+        self._mgr.AddPane(self.GetFirstWindow(), wx.aui.AuiPaneInfo().
+                  Name("training").Caption(_("Training Areas Display")).
+                  Dockable(False).Floatable(False).CloseButton(False).
+                  Center().Layer(0))
+                  
+        self._mgr.AddPane(self.plotPanel, wx.aui.AuiPaneInfo().
+                  Name("plots").Caption(_("Plots")).
+                  Dockable(False).Floatable(False).CloseButton(False).
+                  Left().Layer(1).BestSize((400, -1)))
+                  
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        return True
+    
+    def OnUpdateActive(self, event):
+        """!
+        @todo move to DoubleMapFrame?
+        """
+        if self.GetMapToolbar().GetActiveMap() == 0:
+            self.MapWindow = self.firstMapWindow
+            self.Map = self.firstMap
+        else:
+            self.MapWindow = self.secondMapWindow
+            self.Map = self.secondMap
+
+        self.UpdateActive(self.MapWindow)
+        # for wingrass
+        if os.name == 'nt':
+            self.MapWindow.SetFocus()
+
+    def UpdateActive(self, win):
+        """!
+        @todo move to DoubleMapFrame?
+        """
+        mapTb = self.GetMapToolbar()
+        # optionally disable tool zoomback tool
+        mapTb.Enable('zoomback', enable = (len(self.MapWindow.zoomhistory) > 1))
+
+        if mapTb.GetActiveMap() != (win == self.secondMapWindow):
+            mapTb.SetActiveMap((win == self.secondMapWindow))
+        self.StatusbarUpdate() 
+        
+    def GetMapToolbar(self):
+        """!Returns toolbar with zooming tools"""
+        return self.toolbars['iClassMap']
+    
+        
+    def OnZoomMenu(self, event):
+        """!Popup Zoom menu """
+        zoommenu = wx.Menu()
+        # Add items to the menu
+
+        zoomsource = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust Training Area Display to Preview Display'))
+        zoommenu.AppendItem(zoomsource)
+        self.Bind(wx.EVT_MENU, self.OnZoomToPreview, zoomsource)
+
+        zoomtarget = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust Preview display to Training Area Display'))
+        zoommenu.AppendItem(zoomtarget)
+        self.Bind(wx.EVT_MENU, self.OnZoomToTraining, zoomtarget)
+
+        # Popup the menu. If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(zoommenu)
+        zoommenu.Destroy()
+        
+    def OnZoomToTraining(self, event):
+        """!Set preview display to match extents of training display """
+
+        if not self.MapWindow == self.GetSecondWindow():
+            self.MapWindow = self.GetSecondWindow()
+            self.Map = self.GetSecondMap()
+            self.UpdateActive(self.GetSecondWindow())
+
+        newreg = self.firstMap.GetCurrentRegion()
+        self.GetSecondMap().region = copy.copy(newreg)
+        
+        self.Render(self.GetSecondWindow())
+        
+    def OnZoomToPreview(self, event):
+        """!Set preview display to match extents of training display """
+
+        if not self.MapWindow == self.GetFirstWindow():
+            self.MapWindow = self.GetFirstWindow()
+            self.Map = self.GetFirstMap()
+            self.UpdateActive(self.GetFirstWindow())
+
+        newreg = self.GetSecondMap().GetCurrentRegion()
+        self.GetFirstMap().region = copy.copy(newreg)
+        
+        self.Render(self.GetFirstWindow())
+        
+    def OnAddBands(self, event):
+        """!Add imagery group"""
+        dlg = IClassGroupDialog(self, group = self.group)
+        if dlg.ShowModal() == wx.ID_OK:
+            group = grass.find_file(name = dlg.GetGroup(), element = 'group')
+            if group['fullname']:
+                self.group = group['fullname']
+                
+        dlg.Destroy()
+        
+    def OnCategoryManager(self, event):
+        """!Show category management dialog"""
+        dlg = IClassCategoryManagerDialog(self)
+        dlg.Show()
+        
+    def CategoryChanged(self, currentCat):
+        """!Updates everything which depends on current category.
+        
+        Updates number of stddev, histograms, layer in preview display. 
+        """
+        nstd = self.statisticsDict[currentCat].nstd
+        self.toolbars['iClass'].UpdateStddev(nstd)
+        
+        self.plotPanel.UpdateCategory(currentCat)
+        self.plotPanel.OnPlotTypeSelected(None)
+                                   
+        name = self.statisticsDict[currentCat].rasterName
+        name = self.previewMapManager.GetAlias(name)
+        if name:
+            self.previewMapManager.SelectLayer(name)
+        
+    def UpdateRasterName(self, newName, cat):
+        """!Update alias of raster map when category name is changed"""
+        origName = self.statisticsDict[cat].rasterName
+        self.previewMapManager.SetAlias(origName, newName)
+        
+    def StddevChanged(self, cat, nstd):
+        """!Standard deviation multiplier changed, rerender map, histograms"""
+        stat = self.statisticsDict[cat]
+        stat.nstd = nstd
+        raster = stat.rasterName
+        
+        cstat = self.cStatisticsDict[cat]
+        I_iclass_statistics_set_nstd(cstat, nstd)
+        
+        I_iclass_create_raster(cstat, self.refer, raster)
+        self.Render(self.GetSecondWindow())
+    
+        stat.SetBandStatistics(cstat)
+        self.plotPanel.StddevChanged()
+        
+    def AddRasterMap(self, name, firstMap = True, secondMap = True):
+        """!Add raster map to Map"""
+        cmdlist = ['d.rast', 'map=%s' % name]
+        if firstMap:
+            self.GetFirstMap().AddLayer(type='raster', command=cmdlist, l_active=True,
+                                        name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+            self.Render(self.GetFirstWindow())
+        if secondMap:
+            self.GetSecondMap().AddLayer(type='raster', command=cmdlist, l_active=True,
+                                        name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+            self.Render(self.GetSecondWindow())
+           
+    def AddTrainingAreaMap(self):
+        """!Add vector map with training areas to Map (training
+        sub-display)"""
+        vname = self.CreateTempVector()
+        if vname:
+            self.trainingAreaVector = vname
+        else:
+            GMessage(parent = self, message = _("Failed to create temporary vector map."))
+            return
+            
+        mapLayer = self.GetFirstMap().AddLayer(type = 'vector',
+                                               command = ['d.vect', 'map=%s' % vname],
+                                               name = vname, l_active = False)
+        
+        self.toolbars['vdigit'].StartEditing(mapLayer)
+        self.Render(self.GetFirstWindow())
+        
+    def OnRunAnalysis(self, event):
+        """!Run analysis and update plots"""
+        if self.RunAnalysis():
+            currentCat = self.GetCurrentCategoryIdx()
+            self.plotPanel.UpdatePlots(group = self.group, currentCat = currentCat,
+                                       statDict = self.statisticsDict,
+                                       statList = self.statisticsList)
+        
+    def RunAnalysis(self):
+        """!Run analysis
+        
+        Calls C functions to compute all statistics and creates raster maps.
+        Signatures are created but signature file is not.
+        """
+        #self.firstMapWindow.digit.CloseMap()
+        self.poMapInfo = self.firstMapWindow.digit.GetDisplay().poMapInfo
+        if not self.CheckInput(group = self.group, vector = self.trainingAreaVector):
+            return
+            
+        for statistic in self.cStatisticsDict.values():
+            I_iclass_free_statistics(statistic)
+        self.cStatisticsDict = {}
+        
+        # init Ref struct with the files in group */
+        I_free_group_ref(self.refer)
+        if (not I_iclass_init_group(self.group, self.refer)):
+            return False
+        
+        I_free_signatures(self.signatures)
+        I_iclass_init_signatures(self.signatures, self.refer)
+        
+        cats = self.statisticsList[:]
+        for i in cats:
+            stats = self.statisticsDict[i]
+            
+            statistics_obj = IClass_statistics()
+            statistics = pointer(statistics_obj)
+            
+            I_iclass_init_statistics(statistics, 
+                                     stats.category,
+                                     stats.name,
+                                     stats.color,
+                                     stats.nstd)
+                                     
+            if I_iclass_analysis(statistics, self.refer, self.poMapInfo, "1",
+                                 self.group, stats.rasterName):
+                # tests
+                self.cStatisticsDict[i] = statistics
+                
+                stats.SetStatistics(statistics)
+                stats.SetReady()
+                self.statisticsDict[stats.category] = stats
+                
+                #self.ConvertToNull(name = stats.rasterName)
+                self.previewMapManager.AddLayer(name = stats.rasterName, resultsLayer = True)
+                # write statistics
+                I_iclass_add_signature(self.signatures, statistics)
+            else:
+                I_iclass_free_statistics(statistics)
+        
+        return True
+        
+    def OnSaveSigFile(self, event):
+        """!Asks for signature file name and saves it."""
+        if not self.group:
+            GMessage(parent = self, message = _("No imagery group selected."))
+            return
+            
+        dlg = wx.TextEntryDialog(self, message = _("Enter name of signature file:"),
+                                 caption = _("Save signature file"))
+
+        if self.sigFile:
+            dlg.SetValue(self.sigFile)
+        if dlg.ShowModal() == wx.ID_OK:
+            file = dlg.GetValue()
+            
+            self.WriteSignatures(self.signatures, self.group, file)
+            
+        dlg.Destroy()
+        
+    def InitStatistics(self):
+        """!Initialize variables and c structures neccessary for
+        computing statistics.
+        """
+        self.group = None
+        self.sigFile = None
+        
+        self.statisticsDict = {}
+        self.statisticsList = []
+        
+        self.cStatisticsDict = {}
+        
+        self.signatures_obj = Signature()
+        self.signatures = pointer(self.signatures_obj)
+        I_init_signatures(self.signatures, 0) # must be freed on exit
+        
+        refer_obj = Ref()
+        self.refer = pointer(refer_obj)
+        I_init_group_ref(self.refer) # must be freed on exit
+        
+    def WriteSignatures(self, signatures, group, filename):
+        """!Writes current signatures to signature file
+        
+        @param signatures signature (c structure)
+        @param group imagery group
+        @param filename signature file name
+        """
+        I_iclass_write_signatures(signatures, group, group, filename)
+                                        
+    def CheckInput(self, group, vector):
+        """!Check if input is valid"""
+        # check if group is ok
+        if not group:
+            GMessage(parent = self,
+                     message = _("No imagery group selected. "
+                                 "Operation cancelled."))
+            return False
+            
+        groupLayers = self.GetGroupLayers(group)
+            
+        nLayers = len(groupLayers)
+        if nLayers <= 1:
+            GMessage(parent = self,
+                     message = _("Group <%s> does not have enough files "
+                                 "(it has %d files). Operation cancelled.") % (group, nLayers))
+            return False
+        
+        #check if vector has any areas
+        vectorInfo = grass.vector_info(vector)
+        numAreas = Vect_get_num_areas(self.poMapInfo)
+        
+        if numAreas <= 0:
+            GMessage(parent = self,
+            message = _("No areas given. "
+                        "Operation cancelled."))
+            return False
+            
+        # check if vector is inside raster
+        rasterInfo = grass.raster_info(groupLayers[0])
+        
+        if vectorInfo['north'] > rasterInfo['north'] or \
+           vectorInfo['south'] < rasterInfo['south'] or \
+           vectorInfo['east'] > rasterInfo['east'] or \
+           vectorInfo['west'] < rasterInfo['west']:
+           GMessage(parent = self,
+                    message = _("Vector features are outside raster layers. "
+                                "Operation cancelled."))
+           return False
+            
+        return True
+        
+    def GetGroupLayers(self, group):
+        """! Get layers in group
+    
+        @todo consider moving this function to core module for convenient
+        """
+        res = RunCommand('i.group',
+                         flags = 'g',
+                         group = group,
+                         read = True).strip()
+        if res.split('\n')[0]:
+            return res.split('\n')
+        return []
+    
+    def ConvertToNull(self, name):
+        """! Sets value which represents null values for given raster map.
+        
+        @param name raster map name
+        """
+        RunCommand('r.null',
+                   map = name,
+                   setnull = 0)
+                     
+    def GetCurrentCategoryIdx(self):
+        """!Returns current category number"""
+        return self.toolbars['iClass'].GetSelectedCategoryIdx()
+        
+    def OnZoomIn(self, event):
+        """!Enable zooming for plots"""
+        super(IClassMapFrame, self).OnZoomIn(event)
+        self.plotPanel.EnableZoom(type = 1)
+        
+    def OnZoomOut(self, event):
+        """!Enable zooming for plots"""
+        super(IClassMapFrame, self).OnZoomOut(event)
+        self.plotPanel.EnableZoom(type = -1)
+        
+    def OnPan(self, event):
+        """!Enable paanning for plots"""
+        super(IClassMapFrame, self).OnPan(event)
+        self.plotPanel.EnablePan()
+        
+
+class MapManager:
+    """! Class for managing map renderer.
+    
+    It is connected with iClassMapManagerToolbar.
+    """
+    def __init__(self, frame, mapWindow, Map):
+        """!
+        
+        It is expected that \a mapWindow is conected with \a Map.
+        
+        @param frame application main window
+        @param mapWindow map window instance
+        @param Map map renderer instance
+        """
+        self.map = Map
+        self.frame = frame
+        self.mapWindow = mapWindow
+        self.toolbar = None
+        
+        self.layerName = {}
+        
+        
+    def SetToolbar(self, toolbar):
+        self.toolbar = toolbar
+        
+    def AddLayer(self, name, resultsLayer = False):
+        """!Adds layer to Map and update toolbar 
+        
+        @param name layer (raster) name
+        @param resultsLayer True if layer is temp. raster showing the results of computation
+        """
+        if (resultsLayer and
+            name in [l.GetName() for l in self.map.GetListOfLayers(l_name = name)]):
+            self.frame.Render(self.mapWindow)
+            return
+            
+        cmdlist = ['d.rast', 'map=%s' % name]
+        self.map.AddLayer(type = 'raster', command = cmdlist, l_active = True,
+                          name = name, l_hidden = False, l_opacity = 1.0, l_render = True)
+        self.frame.Render(self.mapWindow)
+        
+        if resultsLayer:
+            alias = self._addSuffix(name.rsplit("_", 1)[0])
+            self.layerName[alias] = name
+            name = alias
+        else:
+            self.layerName[name] = name
+            
+        self.toolbar.choice.Insert(name, 0)
+        self.toolbar.choice.SetSelection(0)
+        
+    def RemoveLayer(self, name, idx):
+        """!Removes layer from Map and update toolbar"""
+        self.map.GetListOfLayers(l_type = 'raster')
+        name = self.layerName[name]
+        self.map.RemoveLayer(name = name)
+        del self.layerName[name]
+        self.toolbar.choice.Delete(idx)
+        if not self.toolbar.choice.IsEmpty():
+            self.toolbar.choice.SetSelection(0)
+            
+        self.frame.Render(self.mapWindow)
+            
+    def SelectLayer(self, name):
+        """!Moves selected layer to top"""
+        layers = self.map.GetListOfLayers(l_type = 'raster')
+        idx = None
+        for i, layer in enumerate(layers):
+            if self.layerName[name] == layer.GetName():
+                idx = i
+                break
+                
+        if idx is not None: # should not happen
+            layers.append(layers.pop(idx))
+            
+            choice = self.toolbar.choice
+            idx = choice.FindString(name)
+            choice.Delete(idx)
+            choice.Insert(name, 0)
+            choice.SetSelection(0)
+            
+            #layers.reverse()
+            self.map.ReorderLayers(layers)
+            self.frame.Render(self.mapWindow)
+        
+    def SetOpacity(self, name):
+        """!Sets opacity of layers."""
+        name = self.layerName[name]
+        layers = self.map.GetListOfLayers(l_name = name)
+        if not layers:
+            return
+            
+        # works for first layer only
+        oldOpacity = layers[0].GetOpacity()
+        dlg = SetOpacityDialog(self.frame, opacity = oldOpacity)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            self.map.ChangeOpacity(layer = layers[0], l_opacity = dlg.GetOpacity())
+            
+        dlg.Destroy()
+        
+        self.frame.Render(self.mapWindow)
+        
+    def _addSuffix(self, name):
+        suffix = _('results')
+        return '_'.join((name, suffix))
+        
+    def GetAlias(self, name):
+        """!Returns alias for layer"""
+        name =  [k for k, v in self.layerName.iteritems() if v == name]
+        if name:
+            return name[0]
+        return None
+        
+    def SetAlias(self, original, alias):
+        name = self.GetAlias(original)
+        if name:
+            self.layerName[self._addSuffix(alias)] = original
+            del self.layerName[name]
+            idx = self.toolbar.choice.FindString(name)
+            if idx != wx.NOT_FOUND:
+                self.toolbar.choice.SetString(idx, self._addSuffix(alias))
+
+def test():
+    import gettext
+    import core.render as render
+    
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    app = wx.PySimpleApp()
+    wx.InitAllImageHandlers()
+    
+    frame = IClassMapFrame()
+    frame.Show()
+    app.MainLoop()
+
+if __name__ == "__main__":
+    test()
+    

Added: grass/trunk/gui/wxpython/iclass/plots.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/plots.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/plots.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,253 @@
+"""!
+ 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
+
+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, statDict, statList):
+        scrolled.ScrolledPanel.__init__(self, parent)
+        
+        self.SetupScrolling(scroll_x = False, scroll_y = True)
+        self.parent = parent
+        self.canvasList = []
+        self.bandList = []
+        self.statDict = statDict
+        self.statList = statList
+        self.currentCat = None
+        
+        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self._createControlPanel()
+        
+        self.SetSizer(self.mainSizer)
+        self.mainSizer.Fit(self)
+        self.Layout()
+        
+    def _createControlPanel(self):
+        self.graphSwitch = wx.Choice(self, id = wx.ID_ANY,
+                                     choices = [_("Histograms"),
+                                                _("Coincident plots")])
+        self.mainSizer.Add(self.graphSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+        self.graphSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
+        
+    def OnPlotTypeSelected(self, event):
+        """!Graph type selected"""
+        if self.graphSwitch.GetSelection() == 0:
+            if not self.statDict[self.currentCat].IsReady():
+                return
+            self.DrawHistograms(self.statDict[self.currentCat])
+        else:
+            self.DrawCoincidencePlots()
+            
+    def StddevChanged(self):
+        """!Standard deviation multiplier changed, redraw histograms"""
+        if self.graphSwitch.GetSelection() == 0:
+            self.UpdateRanges(self.statDict[self.currentCat])
+        
+    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 CreatePlotCanvases(self):
+        """!Create plot canvases according to the number of bands"""
+        for band in self.bandList:
+            canvas = plot.PlotCanvas(self)
+            canvas.SetMinSize((-1, 140))
+            canvas.SetFontSizeTitle(10)
+            canvas.SetFontSizeAxis(8)
+            self.canvasList.append(canvas)
+            
+            self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+
+        self.SetVirtualSize(self.GetBestVirtualSize()) 
+        self.Layout()
+        
+    def UpdatePlots(self, group, currentCat, statDict, statList):
+        """!Update plots after new analysis
+        
+        @param group imagery group
+        @param currentCat currently selected category (class)
+        @param statDict dictionary with Statistics
+        @param statList list of currently used categories
+        """
+        self.statDict = statDict
+        self.statList = statList
+        self.currentCat = currentCat
+        self.bandList = self.parent.GetGroupLayers(group)
+        
+        graphType = self.graphSwitch.GetSelection()
+        
+        if not statDict[currentCat].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))
+            for i, cat in enumerate(self.statList):
+                if not self.statDict[cat].IsReady():
+                    continue
+                color = self.statDict[cat].color
+                level = i + 1
+                line = self.DrawCoincidenceLine(level, color, self.statDict[cat].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.Color(*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: grass/trunk/gui/wxpython/iclass/statistics.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/statistics.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/statistics.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,151 @@
+"""!
+ at package iclass::statistics
+
+ at brief wxIClass classes for storing statistics about cells in training areas.
+
+Classes:
+ - statistics::Statistics
+ - statistics::BandStatistics
+
+(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 os
+from ctypes import *
+
+import grass.script as grass
+
+try:
+    from grass.lib.imagery import *
+except ImportError, e:
+    sys.stderr.write(_("Loading imagery lib failed"))
+
+class Statistics:
+    """! Statistis conected to one class (category).
+    
+    It is Python counterpart of similar C structure.
+    But it adds some attributes or features used in wxIClass.
+    It is not interface to C structure (it copies values).
+    """
+    def __init__(self):
+        self.category = -1
+        self.name = ""
+        self.rasterName = ""
+        self.color = "0:0:0"
+        self.nbands = 0
+        self.ncells = 0
+        self.nstd = 1.5
+        self.bands = []
+        self.ready = False
+        
+        
+    def SetReady(self, ready = True):
+        self.ready = ready
+        
+    def IsReady(self):
+        return self.ready
+        
+    def SetBaseStatistics(self, cat, name, color):
+        """! Sets basic (non-statistical) values.
+        
+        @todo Later self.name is changed but self.rasterName is not.
+        self.rasterName should not be set by user. It can remains the same.
+        But it should be done more explicitly.
+        Currently it looks like unintentional feature or bug.
+        """
+        self.category = cat
+        self.name = name
+        self.color = color
+        
+        rasterPath = grass.tempfile(create = False)
+        self.rasterName = name + '_' + os.path.basename(rasterPath)
+        
+    def SetStatistics(self, cStatistics):
+        """! Sets all statistical values.
+        
+        Copies all statistic values from \a cStattistics.
+        
+        @param cStatistics pointer to C statistics structure
+        """
+        cat = c_int()
+        I_iclass_statistics_get_cat(cStatistics, byref(cat))
+        self.category = cat.value
+        
+        name = c_char_p()
+        I_iclass_statistics_get_name(cStatistics, byref(name))
+        self.name = name.value
+        
+        color = c_char_p()
+        I_iclass_statistics_get_color(cStatistics, byref(color))
+        self.color = color.value
+        
+        nbands = c_int()
+        I_iclass_statistics_get_nbands(cStatistics, byref(nbands))
+        self.nbands = nbands.value
+        
+        ncells = c_int()
+        I_iclass_statistics_get_ncells(cStatistics, byref(ncells))
+        self.ncells = ncells.value
+        
+        nstd = c_float()
+        I_iclass_statistics_get_nstd(cStatistics, byref(nstd))
+        self.nstd = nstd.value
+        
+        self.SetBandStatistics(cStatistics)
+        
+    def SetBandStatistics(self, cStatistics):
+        """! Sets all band statistics.
+        
+        @param cStatistics pointer to C statistics structure
+        """
+        self.bands = []
+        for i in range(self.nbands):
+            band = BandStatistics()
+            band.SetStatistics(cStatistics, index = i)
+            self.bands.append(band)
+        
+class BandStatistics:
+    """! Statistis conected to one band within class (category).
+    
+    @see Statistics
+    """
+    def __init__(self):
+        self.min = self.max = None
+        self.rangeMin = self.rangeMax = None
+        self.mean = None
+        self.stddev = None
+        self.histo = [0] * 256 # max categories
+        
+        
+    def SetStatistics(self, cStatistics, index):
+        """! Sets statistics for one band by given index.
+        
+        @param cStatistics pointer to C statistics structure
+        @param index index of band in C statistics structure
+        """
+        min, max = c_int(), c_int()
+        I_iclass_statistics_get_min(cStatistics, index, byref(min))
+        I_iclass_statistics_get_max(cStatistics, index, byref(max))
+        self.min, self.max = min.value, max.value
+        
+        rangeMin, rangeMax = c_int(), c_int()
+        I_iclass_statistics_get_range_min(cStatistics, index, byref(rangeMin))
+        I_iclass_statistics_get_range_max(cStatistics, index, byref(rangeMax))
+        self.rangeMin, self.rangeMax = rangeMin.value, rangeMax.value
+        
+        mean, stddev = c_float(), c_float()
+        I_iclass_statistics_get_mean(cStatistics, index, byref(mean))
+        I_iclass_statistics_get_stddev(cStatistics, index, byref(stddev))
+        self.mean, self.stddev = mean.value, stddev.value
+        
+        histo = c_int()
+        for i in range(len(self.histo)):
+            I_iclass_statistics_get_histo(cStatistics, index, i, byref(histo))
+            self.histo[i] = histo.value
+        

Added: grass/trunk/gui/wxpython/iclass/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/iclass/toolbars.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/iclass/toolbars.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -0,0 +1,303 @@
+"""!
+ at package iclass::toolbars
+
+ at brief wxIClass toolbars and icons.
+
+Classes:
+ - toolbars::IClassMapToolbar
+ - toolbars::IClassToolbar
+ - toolbars::IClassMapManagerToolbar
+ - toolbars::IClassMiscToolbar
+
+(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
+
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon import MetaIcon
+from iclass.dialogs import IClassMapDialog
+
+import grass.script as grass
+
+iClassIcons = {
+        'opacity' : MetaIcon(img = 'layer-opacity',
+                             label = _('Set opacity level')),
+        'classManager' : MetaIcon(img = 'table-manager',
+                                  label = _('Class manager')),
+        'selectGroup' : MetaIcon(img = 'layer-group-add',
+                                 label = _('Select imagery group')),
+        'run' : MetaIcon(img = 'execute',
+                         label = _('Run analysis')),
+        'sigFile' : MetaIcon(img = 'script-save',
+                             label = _('Save signature file')),
+        'delCmd' : MetaIcon(img = 'layer-remove',
+                            label = _('Delete selected map layer')),
+        }
+        
+class IClassMapToolbar(BaseToolbar):
+    """!IClass Map toolbar
+    """
+    def __init__(self, parent):
+        """!IClass Map toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # add tool to toggle active map window
+        self.togglemapid = wx.NewId()
+        self.togglemap = wx.Choice(parent = self, id = self.togglemapid,
+                                   choices = [_('Training'), _('Preview')],
+                                   style = wx.CB_READONLY)
+
+        self.InsertControl(9, self.togglemap)
+
+        self.SetToolShortHelp(self.togglemapid, '%s %s %s' % (_('Set map canvas for '),
+                                                              BaseIcons["zoomBack"].GetLabel(),
+                                                              _('/ Zoom to map')))
+
+        # realize the toolbar
+        self.Realize()
+        
+        self.action = { 'id' : self.pan }
+        self.defaultAction = { 'id' : self.pan,
+                               'bind' : self.parent.OnPan }
+        
+        self.OnTool(None)
+        
+        self.EnableTool(self.zoomback, False)
+        
+    def GetActiveMapTool(self):
+        """!Return widget for selecting active maps"""
+        return self.togglemap
+        
+    def GetActiveMap(self):
+        """!Get currently selected map"""
+        return self.togglemap.GetSelection()
+        
+    def SetActiveMap(self, index):
+        """!Set currently selected map"""
+        return self.togglemap.SetSelection(index)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = BaseIcons
+        return self._getToolbarData((("displaymap", icons["display"],
+                                      self.parent.OnDraw),
+                                     ("rendermap", icons["render"],
+                                      self.parent.OnRender),
+                                     ("erase", icons["erase"],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ("pan", icons["pan"],
+                                      self.parent.OnPan,
+                                      wx.ITEM_CHECK),
+                                     ("zoomin", icons["zoomIn"],
+                                      self.parent.OnZoomIn,
+                                      wx.ITEM_CHECK),
+                                     ("zoomout", icons["zoomOut"],
+                                      self.parent.OnZoomOut,
+                                      wx.ITEM_CHECK),
+                                     ("zoommenu", icons["zoomMenu"],
+                                      self.parent.OnZoomMenu),
+                                     (None, ),
+                                     ("zoomback", icons["zoomBack"],
+                                      self.parent.OnZoomBack),
+                                     ("zoomtomap", icons["zoomExtent"],
+                                      self.parent.OnZoomToMap),
+                                     
+                                     #('settings', Icons["georectify"]["settings"],
+                                      #self.parent.OnSettings),
+                                     #('help', Icons["misc"]["help"],
+                                      #self.parent.OnHelp),
+                                     #(None, ),
+                                     #('quit', Icons["georectify"]["quit"],
+                                      #self.parent.OnQuit))
+                                    ))
+class IClassToolbar(BaseToolbar):
+    """!IClass toolbar
+    """
+    def __init__(self, parent):
+        """!IClass toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        self.InitToolbar(self._toolbarData())
+        
+        self.choice = wx.Choice(parent = self, id = wx.ID_ANY, size = (110, -1))
+        choiceid = self.InsertControl(3, self.choice)
+        
+        self.choice.Bind(wx.EVT_CHOICE, self.OnSelectCategory)
+        
+        # stupid workaround to insert small space between controls
+        self.InsertControl(4, wx.StaticText(self, id = wx.ID_ANY, label = ' '))
+        
+        self.combo = wx.ComboBox(self, id = wx.ID_ANY, size = (130, -1), 
+                                 style = wx.TE_PROCESS_ENTER)
+        self.InitStddev()
+        comboid = self.InsertControl(5, self.combo)
+        
+        self.EnableControls(False)
+        
+        self.combo.Bind(wx.EVT_COMBOBOX, self.OnStdChangeSelection)
+        self.combo.Bind(wx.EVT_TEXT_ENTER, self.OnStdChangeText)
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = iClassIcons
+        return self._getToolbarData((("selectGroup", icons['selectGroup'],
+                                      self.parent.OnAddBands),
+                                      (None, ),
+                                      ("classManager", icons['classManager'],
+                                      self.parent.OnCategoryManager),
+                                      (None, ),
+                                      ("runAnalysis", icons['run'],
+                                      self.parent.OnRunAnalysis),
+                                      ("sigFile", icons['sigFile'],
+                                      self.parent.OnSaveSigFile),
+                                    ))
+    def OnSelectCategory(self, event):
+        idx = self.choice.GetSelection()
+        cat = self.choice.GetClientData(idx)
+        
+        self.parent.CategoryChanged(currentCat = cat)
+        
+    def SetCategories(self, catNames, catIdx):
+        self.choice.Clear()
+        for name, idx in zip(catNames, catIdx):
+            self.choice.Append(name, idx)
+        
+    def GetSelectedCategoryName(self):
+        return self.choice.GetStringSelection()
+        
+    def GetSelectedCategoryIdx(self):
+        idx = self.choice.GetSelection()
+        if idx != wx.NOT_FOUND:
+            return self.choice.GetClientData(idx)
+        
+        return None
+        
+    def OnStdChangeSelection(self, event):
+        idx = self.combo.GetSelection()
+        nstd = self.combo.GetClientData(idx)
+        
+        self.StddevChanged(nstd)
+        
+    def OnStdChangeText(self, event):
+        val = self.combo.GetValue().strip()
+        try:
+            nstd = float(val)
+        except ValueError:
+            try:
+                nstd = float(val.split()[0])
+            except ValueError:
+                nstd = None
+                
+        if nstd is not None:
+            self.StddevChanged(nstd)
+            
+    def StddevChanged(self, nstd):
+        idx = self.GetSelectedCategoryIdx()
+        if not idx:
+            return
+            
+        self.parent.StddevChanged(cat = idx, nstd = nstd)
+        
+    def UpdateStddev(self, nstd):
+        self.combo.SetValue(' '.join(("%.2f" % nstd, _('std dev'))))
+        
+    def InitStddev(self):
+        for nstd in range(50, 250, 25):
+            nstd /= 100.
+            self.combo.Append(item = ' '.join(("%.2f" % nstd, _('std dev'))), clientData = nstd)
+        self.combo.SetSelection(4) # 1.5
+        
+    def EnableControls(self, enable = True):
+        self.combo.Enable(enable)
+        self.choice.Enable(enable)
+        
+class IClassMapManagerToolbar(BaseToolbar):
+    """!IClass toolbar
+    """
+    def __init__(self, parent, mapManager):
+        """!IClass toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        self.choice = wx.Choice(parent = self, id = wx.ID_ANY, size = (300, -1))
+        
+        self.choiceid = self.AddControl(self.choice)
+        
+        self.choice.Bind(wx.EVT_CHOICE, self.OnSelectLayer)
+        
+        self.mapManager = mapManager
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        return self._getToolbarData((("addRast", BaseIcons['addRast'],
+                                      self.OnAddRast),
+                                      ("delRast", iClassIcons['delCmd'],
+                                      self.OnDelRast),
+                                      ("setOpacity", iClassIcons['opacity'],
+                                      self.OnSetOpacity),
+                                    ))
+                                    
+    def OnSelectLayer(self, event):
+        layer = self.choice.GetStringSelection()
+        self.mapManager.SelectLayer(name = layer)
+        
+    def OnAddRast(self, event):
+        dlg = IClassMapDialog(self)
+        if dlg.ShowModal() == wx.ID_OK:
+            raster = grass.find_file(name = dlg.GetRasterMap(), element = 'cell')
+            if raster['fullname']:
+                self.mapManager.AddLayer(name = raster['fullname'])
+                
+        dlg.Destroy()
+        
+    def OnDelRast(self, event):
+        layer = self.choice.GetStringSelection()
+        idx = self.choice.GetSelection()
+        if layer:
+            self.mapManager.RemoveLayer(name = layer, idx = idx)
+            
+    def OnSetOpacity(self, event):
+        layer = self.choice.GetStringSelection()
+        idx = self.choice.GetSelection()
+        if idx == wx.NOT_FOUND:
+            return
+            
+        self.mapManager.SetOpacity(name = layer)
+
+class IClassMiscToolbar(BaseToolbar):
+    """!IClass toolbar
+    """
+    def __init__(self, parent):
+        """!IClass toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = BaseIcons
+        return self._getToolbarData((("help", icons['help'],
+                                      self.parent.OnHelp),
+                                     #("quit", icons['quit'],
+                                      #self.parent.OnCloseWindow),
+                                    ))

Modified: grass/trunk/gui/wxpython/vdigit/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/mapwindow.py	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/vdigit/mapwindow.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -875,9 +875,7 @@
                 self.Refresh()
                 
                 # add new record into atribute table
-                if UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled') and \
-                        (line is True or \
-                             (not line and nfeat > 0)):
+                if self._addRecord() and (line is True or (not line and nfeat > 0)):
                     posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
                                                      position[1] + self.dialogOffset))
                         
@@ -1074,3 +1072,5 @@
         for id in self.moveInfo['id']:
             self.pdcTmp.RemoveId(id)
         
+    def _addRecord(self):
+        return UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled')

Modified: grass/trunk/gui/wxpython/vdigit/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/toolbars.py	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/vdigit/toolbars.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -21,6 +21,7 @@
 from gui_core.dialogs   import CreateNewVector
 from vdigit.preferences import VDigitSettingsDialog
 from vdigit.main        import VDigit
+from iclass.digit       import IClassIVDigit
 from core.debug         import Debug
 from core.settings      import UserSettings
 from core.gcmd          import GError
@@ -210,12 +211,8 @@
     
     def OnTool(self, event):
         """!Tool selected -> disable selected tool in map toolbar"""
-        if self.parent.GetName() == 'IClassWindow':
-            toolbarName = 'iClassMap'
-        else:
-            toolbarName = 'map'
-        aId = self.parent.toolbars[toolbarName].GetAction(type = 'id')
-        self.parent.toolbars[toolbarName].ToggleTool(aId, False)
+        aId = self.parent.GetMapToolbar().GetAction(type = 'id')
+        self.parent.GetMapToolbar().ToggleTool(aId, False)
                 
         # set cursor
         cursor = self.parent.cursors["cross"]
@@ -696,7 +693,10 @@
                                         0)
         
         self.MapWindow.pdcVector = wx.PseudoDC()
-        self.digit = self.MapWindow.digit = VDigit(mapwindow = self.MapWindow)
+        if self.parent.GetName() == 'IClassWindow':
+            self.digit = self.MapWindow.digit = IClassIVDigit(mapwindow = self.MapWindow)
+        else:
+            self.digit = self.MapWindow.digit = VDigit(mapwindow = self.MapWindow)
         
         self.mapLayer = mapLayer
         # open vector map

Modified: grass/trunk/gui/wxpython/vdigit/wxdigit.py
===================================================================
--- grass/trunk/gui/wxpython/vdigit/wxdigit.py	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/vdigit/wxdigit.py	2011-12-18 19:22:53 UTC (rev 49815)
@@ -246,6 +246,24 @@
         else:
             return NO_SNAP
     
+    def _getNewFeaturesLayer(self):
+        """!Returns layer of new feature (from settings)"""
+        if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
+            layer = -1 # -> no category
+        else:
+            layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
+        
+        return layer
+        
+    def _getNewFeaturesCat(self):
+        """!Returns category of new feature (from settings)"""
+        if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
+            cat   = -1
+        else:
+            cat   = self.SetCategory()
+        
+        return cat
+        
     def _breakLineAtIntersection(self, line, pointsLine, changeset):
         """!Break given line at intersection
 
@@ -446,13 +464,10 @@
         
         @return tuple (number of added features, feature ids)
         """
-        if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
-            layer = -1 # -> no category
-            cat   = -1
-        else:
-            layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
-            cat   = self.SetCategory()
         
+        layer = self._getNewFeaturesLayer()
+        cat = self._getNewFeaturesCat()
+        
         if ftype == 'point':
             vtype = GV_POINT
         elif ftype == 'line':
@@ -498,7 +513,7 @@
                 if Vect_read_line(self.poMapInfo, None, poCats, i) < 0:
                     Vect_destroy_cats_struct(poCatsDel)
                     self._error.ReadLine(i)
-                    return -1
+                    
                 
                 cats = poCats.contents
                 for j in range(cats.n_cats):

Modified: grass/trunk/gui/wxpython/wxpythonlib.dox
===================================================================
--- grass/trunk/gui/wxpython/wxpythonlib.dox	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/wxpythonlib.dox	2011-12-18 19:22:53 UTC (rev 49815)
@@ -439,6 +439,29 @@
  - scatter::ScatterFrame
  - scatter::ScatterToolbar
 
+\subsection wxIClass wxIClass
+- iclass::dialogs
+ - dialogs::IClassGroupDialog
+ - dialogs::IClassMapDialog
+ - dialogs::IClassCategoryManagerDialog
+ - dialogs::CategoryListCtrl
+- iclass::digit
+ - digit::IClassVDigit
+ - digit::IClassVDigitWindow
+- iclass::frame
+ - frame::IClassMapFrame
+ - frame::MapManager
+- iclass::plots
+ - plots::PlotPanel
+- iclass::statistics
+ - statistics::Statistics
+ - statistics::BandStatistics
+- iclass::toolbars
+ - toolbars::IClassMapToolbar
+ - toolbars::IClassToolbar
+ - toolbars::IClassMapManagerToolbar
+ - toolbars::IClassMiscToolbar
+ 
 \subsection other Other GUI modules
 
 - modules::colorrules

Modified: grass/trunk/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/trunk/gui/wxpython/xml/menudata.xml	2011-12-18 19:12:56 UTC (rev 49814)
+++ grass/trunk/gui/wxpython/xml/menudata.xml	2011-12-18 19:22:53 UTC (rev 49815)
@@ -2664,6 +2664,13 @@
 	      <handler>OnMenuCmd</handler>
 	      <command>i.gensigset</command>
 	    </menuitem>
+	    <separator />
+	    <menuitem>
+	      <label>Interactive input for supervised classification</label>
+	      <help>Generates spectral signatures by allowing the user to outline training areas.</help>
+	      <keywords>imagery,classification,supervised</keywords>
+	      <handler>OnIClass</handler>
+	    </menuitem>
 	  </items>
 	</menu>
 	<menu>



More information about the grass-commit mailing list