|
@@ -0,0 +1,808 @@
|
|
|
|
+"""!
|
|
|
|
+@package iclass::frame
|
|
|
|
+
|
|
|
|
+@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.
|
|
|
|
+
|
|
|
|
+@author Vaclav Petras <wenzeslaus gmail.com>
|
|
|
|
+@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()
|
|
|
|
+
|