[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