[GRASS-SVN] r54515 - in grass/trunk/gui/wxpython: . docs lmgr mapdisp vnet xml
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Jan 3 09:46:18 PST 2013
Author: martinl
Date: 2013-01-03 09:46:18 -0800 (Thu, 03 Jan 2013)
New Revision: 54515
Added:
grass/trunk/gui/wxpython/docs/wxGUI.vnet.html
grass/trunk/gui/wxpython/vnet/
grass/trunk/gui/wxpython/vnet/dialogs.py
grass/trunk/gui/wxpython/vnet/toolbars.py
grass/trunk/gui/wxpython/vnet/widgets.py
Modified:
grass/trunk/gui/wxpython/Makefile
grass/trunk/gui/wxpython/lmgr/frame.py
grass/trunk/gui/wxpython/mapdisp/frame.py
grass/trunk/gui/wxpython/mapdisp/toolbars.py
grass/trunk/gui/wxpython/xml/menudata.xml
Log:
wxGUI: wx.vnet moved from addons to trunk
Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile 2013-01-03 16:59:02 UTC (rev 54514)
+++ grass/trunk/gui/wxpython/Makefile 2013-01-03 17:46:18 UTC (rev 54515)
@@ -11,12 +11,12 @@
SRCFILES := $(wildcard icons/*.* scripts/* xml/*) \
$(wildcard animation/* core/* dbmgr/* gcp/* gmodeler/* gui_core/* iclass/* lmgr/* location_wizard/* \
- mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* ogc_services/* rlisetup/*) \
+ mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* ogc_services/* rlisetup/* vnet/*) \
gis_set.py gis_set_error.py wxgui.py README
DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler gui_core iclass lmgr location_wizard \
- mapdisp modules nviz psmap mapswipe vdigit wxplot ogc_services rlisetup)
+ mapdisp modules nviz psmap mapswipe vdigit wxplot ogc_services rlisetup vnet)
DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
default: $(DSTFILES)
@@ -41,4 +41,4 @@
$(MKDIR) $@
#doxygen:
-DOXNAME=wxpython
+DOXNAME = wxpython
Added: grass/trunk/gui/wxpython/docs/wxGUI.vnet.html
===================================================================
--- grass/trunk/gui/wxpython/docs/wxGUI.vnet.html (rev 0)
+++ grass/trunk/gui/wxpython/docs/wxGUI.vnet.html 2013-01-03 17:46:18 UTC (rev 54515)
@@ -0,0 +1,100 @@
+<!-- meta page description: wxGUI Vector Network Analysis Tool -->
+<!-- meta page index: wxGUI -->
+
+<h2>DESCRIPTION</h2>
+
+<em>Vector Network Analysis Tool</em> is graphical front-end
+for <tt>v.net*</tt> modules. It allows perform network analysis
+directly in <em><a href="wxGUI.html">wxGUI</a></em> without need to
+use command line. The tool is available from GRASS AddOns repository
+and needs to be installed to GRASS GIS by command:
+
+<div class="code"><pre>
+g.extension -s extension=wx.vnet
+</pre></div>
+
+The tool can be launched from Map Display toolbar submenu
+of <em>Analyze Map</em> button where is <em>Vector network analysis
+(experimental, GSoC 2012)</em> item.
+
+<p>
+<em>Vector Network Analysis Tool</em> currently allows to:
+
+<ul>
+ <li> perform these network analyses:
+ <ul>
+ <li> Shortest path
+ (<em><a href="v.net.path.html">v.net.path</a></em>)</li>
+ <li> Salesman
+ (<em><a href="v.net.salesman.html">v.net.salesman</a></em>)</li>
+ <li> Flow (<em><a href="v.net.flow.html">v.net.flow</a></em>)</li>
+ <li> Allocate subnets for nearest centers
+ (<em><a href="v.net.alloc.html">v.net.alloc</a></em>)</li>
+ <li> Steiner tree for the network and given terminals
+ (<em><a href="v.net.distance.html">v.net.distance</a></em>)</li>
+ <li> Splits net by cost isolines
+ (<em><a href="v.net.iso.html">v.net.iso</a></em>)</li>
+ </ul>
+ </li>
+ <li>show and set all data needed for the analysis (points, attribute
+ tables, compute costs)</li>
+ <li>show analysis results (maps and it's attribute tables)</li>
+ <li>snapping to nodes</li>
+ <li>browse previous analysis results</li>
+</ul>
+
+<h2>NOTES</h2>
+
+The tool is split into tabs. Every tab represents some functionality:
+
+<ul>
+ <li> <tt>Parameters</tt> tab - It is used for setting vector map and
+ it's layer on which analysis will be done. Also it is possible to
+ set columns with cost values from attribute table connected
+ to particular layer.</li>
+ <li> <tt>Points</tt> tab - It manages points, which are used for
+ analysis.</li>
+ <li> <tt>Output</tt> tab - There is a output console, which shows
+ information about running analysis. </li>
+ <li> <tt>Input tables</tt> tab - When existing vector map and it's
+ existing layers are set in <tt>Parameters</tt> tab, this tab is
+ dynamically added. It shows attribute tables of node and arc layers,
+ which were chosen for analysis. It is also possible to compute cost
+ values in this tab. This can be done by right mouse button click on
+ column label. Then from pop-up menu choose <tt>Add column</tt>,
+ where new column for cost values can be created. After that by right
+ mouse button click on the added column label can be chosen
+ item <tt>Field calculator</tt>. This tool allows to compute cost
+ values.</li>
+ <li> <tt>Result tables</tt> tab - Result of vector network analysis
+ is always vector map. Some vector network analysis results can also
+ include attribute tables. If such a table is connected to result
+ map, this tab is showed and allows to browse these data.</li>
+</ul>
+
+<h2>KNOWN ISSUES</h2>
+
+When some change is done in layer tree of Map Display, temporary
+vector map representing result of analysis is not rendered
+(use <tt>Show result</tt> button in toolbar to render it again).
+
+<h2>SEE ALSO</h2>
+
+<em>
+ <a href="wxGUI.html">wxGUI</a><br>
+ <a href="wxGUI.Components.html">wxGUI components</a>
+</em>
+
+<p>
+See also the
+user <a href="http://grass.osgeo.org/wiki/WxGUI_Vector_Network_Analysis_Tool">wiki</a>
+page.
+
+<h2>AUTHOR</h2>
+
+Stepan
+Turek, <a href="http://grass.osgeo.org/wiki/GRASS_GSoC_2012_WxGUI_front_end_for_vector_analysis_modules">Google
+Summer of Code 2012</a> (mentor: Martin Landa)
+
+<p>
+<i>$Date$</i>
Property changes on: grass/trunk/gui/wxpython/docs/wxGUI.vnet.html
___________________________________________________________________
Added: svn:mime-type
+ text/html
Added: svn:keywords
+ Author Date Id
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/frame.py 2013-01-03 16:59:02 UTC (rev 54514)
+++ grass/trunk/gui/wxpython/lmgr/frame.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -725,6 +725,13 @@
cmd = self.GetMenuCmd(event)
GUI(parent = self).ParseCommand(cmd)
+ def OnVNet(self, event):
+ """Vector network analysis tool"""
+ if self.GetMapDisplay():
+ self.GetMapDisplay().OnVNet(event)
+ else:
+ self.NewDisplay(show = True).OnVNet(event)
+
def OnVDigit(self, event):
"""!Start vector digitizer
"""
Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py 2013-01-03 16:59:02 UTC (rev 54514)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -1335,24 +1335,17 @@
"""!Returns toolbar with zooming tools"""
return self.toolbars['map']
- def OnVnet(self, event):
+ def OnVNet(self, event):
"""!Dialog for v.net* modules
"""
if self.dialogs['vnet']:
return
- else:
- try:
- from vnet.dialogs import VNETDialog
- except ImportError:
- GError(_("Extension <%s> not available, run '%s' to install it.") % \
- ('wx.vnet', 'g.extension -s extension=wx.vnet'),
- parent = self, showTraceback = False)
- return
+
+ from vnet.dialogs import VNETDialog
+ self.dialogs['vnet'] = VNETDialog(parent = self)
+ self.dialogs['vnet'].CenterOnScreen()
+ self.dialogs['vnet'].Show()
- self.dialogs['vnet'] = VNETDialog(parent = self)
- self.dialogs['vnet'].CenterOnScreen()
- self.dialogs['vnet'].Show()
-
def SwitchTool(self, toolbar, event):
"""!Calls UpdateTools to manage connected toolbars"""
self.UpdateTools(event)
Modified: grass/trunk/gui/wxpython/mapdisp/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/toolbars.py 2013-01-03 16:59:02 UTC (rev 54514)
+++ grass/trunk/gui/wxpython/mapdisp/toolbars.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -47,7 +47,7 @@
'histogram' : MetaIcon(img = 'layer-raster-histogram',
label = _('Create histogram of raster map')),
'vnet' : MetaIcon(img = 'line-split',
- label = _('Vector network analysis (experimental, GSoC 2012)')),
+ label = _('Vector network analysis tool')),
}
NvizIcons = {
@@ -239,12 +239,12 @@
def OnAnalyze(self, event):
"""!Analysis tools menu
"""
- self._onMenu(((MapIcons["measure"], self.parent.OnMeasure),
- (MapIcons["profile"], self.parent.OnProfile),
- (MapIcons["scatter"], self.parent.OnScatterplot),
- (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
+ self._onMenu(((MapIcons["measure"], self.parent.OnMeasure),
+ (MapIcons["profile"], self.parent.OnProfile),
+ (MapIcons["scatter"], self.parent.OnScatterplot),
+ (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
(BaseIcons["histogramD"], self.parent.OnHistogram),
- (MapIcons["vnet"], self.parent.OnVnet)))
+ (MapIcons["vnet"], self.parent.OnVNet)))
def OnDecoration(self, event):
"""!Decorations overlay menu
Added: grass/trunk/gui/wxpython/vnet/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/vnet/dialogs.py (rev 0)
+++ grass/trunk/gui/wxpython/vnet/dialogs.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -0,0 +1,2827 @@
+"""!
+ at package vnet.dialogs
+
+ at brief Dialog for vector network analysis front-end
+
+Classes:
+ - dialogs::VNETDialog
+ - dialogs::PtsList
+ - dialogs::SettingsDialog
+ - dialogs::AddLayerDialog
+ - dialogs::VnetTmpVectMaps
+ - dialogs::VectMap
+ - dialogs::History
+ - dialogs::VnetStatusbar
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
+"""
+
+import os
+import sys
+import types
+try:
+ import grass.lib.vector as vectlib
+ from ctypes import pointer, byref, c_char_p, c_int, c_double
+ haveCtypes = True
+except ImportError:
+ haveCtypes = False
+
+from copy import copy
+from grass.script import core as grass
+
+import wx
+import wx.aui
+import wx.lib.flatnotebook as FN
+import wx.lib.colourselect as csel
+
+from core import globalvar, utils
+from core.settings import UserSettings
+from core.gcmd import RunCommand, GMessage
+
+from dbmgr.base import DbMgrBase
+
+from gui_core.widgets import GNotebook
+from gui_core.goutput import GConsoleWindow
+from core.gconsole import CmdThread, EVT_CMD_DONE, GConsole
+from gui_core.gselect import Select, LayerSelect, ColumnSelect
+
+from vnet.widgets import PointsList
+from vnet.toolbars import MainToolbar, PointListToolbar, AnalysisToolbar
+
+#Main TODOs
+# - when layer tree of is changed, tmp result map is removed from render list
+# - optimization of map drawing
+# - tmp maps - add number of process
+# - destructor problem - when GRASS GIS is closed with open VNETDialog,
+# it's destructor is not called
+
+class VNETDialog(wx.Dialog):
+ def __init__(self, parent,
+ id = wx.ID_ANY, title = _("GRASS GIS Vector Network Analysis Tool"),
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+ """!Dialog for vector network analysis"""
+
+ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+ self.parent = parent # mapdisp.frame MapFrame class
+ self.mapWin = parent.MapWindow
+
+ # contains current analysis result (do not have to be last one, when history is browsed),
+ # it is instance of VectMap class
+ self.tmp_result = None
+
+ # initialization of History class used for saving and reading data from file
+ # it is used for browsing analysis results
+ self.history = History(self)
+
+ # variable, which appends unique number to every name of map, which is saved into history
+ self.histTmpVectMapNum = 0
+
+ # list of maps, which will be saved into history
+ self.tmpVectMapsToHist = []
+
+ # this class instance manages all temporary vector maps created during life of VNETDialog
+ self.tmpMaps = VnetTmpVectMaps(parent = self)
+
+ # setting initialization
+ self._initSettings()
+
+ # registration graphics for drawing
+ self.pointsToDraw = self.mapWin.RegisterGraphicsToDraw(graphicsType = "point",
+ setStatusFunc = self.SetPointStatus)
+ self.SetPointDrawSettings()
+
+ # information, whether mouse event handler is registered in map window
+ self.handlerRegistered = False
+
+ # get attribute table columns only with numbers (for cost columns in vnet analysis)
+ self.columnTypes = ['integer', 'double precision']
+
+ # initialization of v.net.* analysis parameters (data which characterizes particular analysis)
+ self._initVnetParams()
+
+ # toobars
+ self.toolbars = {}
+ self.toolbars['mainToolbar'] = MainToolbar(parent = self)
+ self.toolbars['analysisToolbar'] = AnalysisToolbar(parent = self)
+ #
+ # Fancy gui
+ #
+ self._mgr = wx.aui.AuiManager(self)
+
+ # Columns in points list
+ # TODO init dynamically, not hard typed v.net.path
+ self.cols = [
+ ['type', _('type'), [_(""), _("Start point"), _("End point")], 0],
+ ['topology', _('topology'), None, ""]
+ ]
+
+ self.mainPanel = wx.Panel(parent=self)
+ self.notebook = GNotebook(parent = self.mainPanel,
+ style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
+ FN.FNB_NO_X_BUTTON)
+
+ # statusbar
+ self.stPriorities = {'important' : 5, 'iformation' : 1}
+ self.stBar = VnetStatusbar(parent = self.mainPanel, style = 0)
+ self.stBar.SetFieldsCount(number = 1)
+
+
+ # Create tabs
+
+ # Stores all data related to snapping
+ self.snapData = {}
+ self.snapData['snap_mode'] = False
+ # Stores widgets which sets some of analysis parameters (e. g. v.ne.iso -> iso lines)
+ self.anSettings = {}
+ self._createPointsPage()
+
+ # stores widgets which are shown on parameters page
+ # they set data, on which analysis will be done
+ self.inputData = {}
+ self._createParametersPage()
+
+ # Output console for analysis
+ self._createOutputPage()
+
+ # Stores data which are needed for attribute table browser of analysis input map layers
+ self.inpDbMgrData = {}
+ self._createInputDbMgrPage()
+
+ # Stores data which are need for attribute table browser of analysis result map layers
+ self.resultDbMgrData = {}
+ self._createResultDbMgrPage()
+
+ self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+ self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
+
+ self._addPanes()
+ self._doVnetDialogLayout()
+ self._mgr.Update()
+
+ # adds 2 points into list
+ for i in range(2):
+ self.list.AddItem()
+ self.list.EditCellIndex(i, 'type', i + 1)
+ self.list.CheckItem(i, True)
+
+ # selects first point
+ self.list.selected = 0
+ self.list.Select(self.list.selected)
+
+ dlgSize = (420, 560)
+ self.SetMinSize(dlgSize)
+ self.SetInitialSize(dlgSize)
+
+ #fix goutput's pane size (required for Mac OSX)
+ if self.gwindow:
+ self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
+
+ self.OnAnalysisChanged(None)
+ self.notebook.SetSelectionByName("parameters")
+ self.toolbars['analysisToolbar'].SetMinSize((-1, self.toolbars['mainToolbar'].GetSize()[1]))
+
+ def __del__(self):
+ """!Removes temp layers, unregisters handlers and graphics"""
+ update = self.tmpMaps.DeleteAllTmpMaps()
+
+ self.mapWin.UnregisterGraphicsToDraw(self.pointsToDraw)
+
+ if self.handlerRegistered:
+ self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
+ self.OnMapClickHandler)
+ if update:
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+ else:
+ self.mapWin.UpdateMap(render=False, renderVector=False)
+
+
+ def _addPanes(self):
+ """!Adds toolbar pane and pane with tabs"""
+ self._mgr.AddPane(self.toolbars['mainToolbar'],
+ wx.aui.AuiPaneInfo().
+ Name("pointlisttools").Caption(_("Point list toolbar")).
+ ToolbarPane().Top().Row(0).
+ Dockable(False).
+ CloseButton(False).Layer(0))
+
+ self._mgr.AddPane(self.toolbars['analysisToolbar'],
+ wx.aui.AuiPaneInfo().
+ Name("pointlisttools").Caption(_("Point list toolbar")).
+ ToolbarPane().Top().Row(1).
+ Dockable(False).
+ CloseButton(False).Layer(0))
+
+ self._mgr.AddPane(self.mainPanel,
+ wx.aui.AuiPaneInfo().
+ Name("tabs").CaptionVisible(visible = False).
+ Center().
+ Dockable(False).
+ CloseButton(False).Layer(0))
+ def _doVnetDialogLayout(self):
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ sizer.Add(item = self.notebook, proportion = 1,
+ flag = wx.EXPAND)
+
+ sizer.Add(item = self.stBar, proportion = 0)
+
+ self.mainPanel.SetSizer(sizer)
+
+ sizer.Fit(self)
+ self.Layout()
+
+ def _createPointsPage(self):
+ """!Tab with points list and analysis settings"""
+ pointsPanel = wx.Panel(parent = self)
+ maxValue = 1e8
+
+ listBox = wx.StaticBox(parent = pointsPanel, id = wx.ID_ANY,
+ label =" %s " % _("Points for analysis:"))
+
+ self.notebook.AddPage(page = pointsPanel,
+ text=_('Points'),
+ name = 'points')
+
+ self.list = PtsList(parent = pointsPanel, dialog = self, cols = self.cols)
+ self.toolbars['pointsList'] = PointListToolbar(parent = pointsPanel, list = self.list)
+
+ anSettingsPanel = wx.Panel(parent = pointsPanel)
+
+ anSettingsBox = wx.StaticBox(parent = anSettingsPanel, id = wx.ID_ANY,
+ label =" %s " % _("Analysis settings:"))
+
+ maxDistPanel = wx.Panel(parent = anSettingsPanel)
+ maxDistLabel = wx.StaticText(parent = maxDistPanel, id = wx.ID_ANY, label = _("Maximum distance of point to the network:"))
+ self.anSettings["max_dist"] = wx.SpinCtrl(parent = maxDistPanel, id = wx.ID_ANY, min = 0, max = maxValue)
+ self.anSettings["max_dist"].SetValue(100000) #TODO init val
+
+ #showCutPanel = wx.Panel(parent = anSettingsPanel)
+ #self.anSettings["show_cut"] = wx.CheckBox(parent = showCutPanel, id=wx.ID_ANY,
+ # label = _("Show minimal cut"))
+ #self.anSettings["show_cut"].Bind(wx.EVT_CHECKBOX, self.OnShowCut)
+
+ isoLinesPanel = wx.Panel(parent = anSettingsPanel)
+ isoLineslabel = wx.StaticText(parent = isoLinesPanel, id = wx.ID_ANY, label = _("Iso lines:"))
+ self.anSettings["iso_lines"] = wx.TextCtrl(parent = isoLinesPanel, id = wx.ID_ANY)
+ self.anSettings["iso_lines"].SetValue("1000,2000,3000")
+
+ # Layout
+ AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
+
+ listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
+
+ listSizer.Add(item = self.toolbars['pointsList'], proportion = 0)
+ listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
+
+ anSettingsSizer = wx.StaticBoxSizer(anSettingsBox, wx.VERTICAL)
+
+ maxDistSizer = wx.BoxSizer(wx.HORIZONTAL)
+ maxDistSizer.Add(item = maxDistLabel, flag = wx.ALIGN_CENTER_VERTICAL, proportion = 1)
+ maxDistSizer.Add(item = self.anSettings["max_dist"],
+ flag = wx.EXPAND | wx.ALL, border = 5, proportion = 0)
+ maxDistPanel.SetSizer(maxDistSizer)
+ anSettingsSizer.Add(item = maxDistPanel, proportion = 1, flag = wx.EXPAND)
+
+ #showCutSizer = wx.BoxSizer(wx.HORIZONTAL)
+ #showCutPanel.SetSizer(showCutSizer)
+ #showCutSizer.Add(item = self.anSettings["show_cut"],
+ # flag = wx.EXPAND | wx.ALL, border = 5, proportion = 0)
+ #anSettingsSizer.Add(item = showCutPanel, proportion = 1, flag = wx.EXPAND)
+
+ isoLinesSizer = wx.BoxSizer(wx.HORIZONTAL)
+ isoLinesSizer.Add(item = isoLineslabel, flag = wx.ALIGN_CENTER_VERTICAL, proportion = 1)
+ isoLinesSizer.Add(item = self.anSettings["iso_lines"],
+ flag = wx.EXPAND | wx.ALL, border = 5, proportion = 1)
+ isoLinesPanel.SetSizer(isoLinesSizer)
+ anSettingsSizer.Add(item = isoLinesPanel, proportion = 1, flag = wx.EXPAND)
+
+ AnalysisSizer.Add(item = listSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+ AnalysisSizer.Add(item = anSettingsPanel, proportion = 0, flag = wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM, border = 5)
+
+ anSettingsPanel.SetSizer(anSettingsSizer)
+ pointsPanel.SetSizer(AnalysisSizer)
+
+ def OnShowCut(self, event):
+ """!Shows vector map with minimal cut (v.net.flow) - not yet implemented"""
+ val = event.IsChecked()
+ if val and self.tmp_result:
+ self.tmp_result.DeleteRenderLayer()
+ cmd = self.GetLayerStyle()
+ self.vnetFlowTmpCut.AddRenderLayer(cmd)
+ else:
+ self.vnetFlowTmpCut.DeleteRenderLayer()
+ cmd = self.GetLayerStyle()
+ self.tmp_result.AddRenderLayer(cmd)
+
+ self.mapWin.UpdateMap(render = True, renderVector = True)
+
+ def _createOutputPage(self):
+ """!Tab with output console"""
+ outputPanel = wx.Panel(parent = self)
+ self.notebook.AddPage(page = outputPanel,
+ text = _("Output"),
+ name = 'output')
+
+ self.goutput = GConsole(guiparent = self)
+ self.gwindow = GConsoleWindow(parent = outputPanel, gconsole = self.goutput)
+
+ #Layout
+ outputSizer = wx.BoxSizer(wx.VERTICAL)
+ outputSizer.Add(item = self.gwindow, proportion = 1, flag = wx.EXPAND)
+ self.gwindow.SetMinSize((-1,-1))
+
+ outputPanel.SetSizer(outputSizer)
+
+ def _createParametersPage(self):
+ """!Tab for selection of data for analysis"""
+ dataPanel = wx.Panel(parent=self)
+ self.notebook.AddPage(page = dataPanel,
+ text=_('Parameters'),
+ name = 'parameters')
+ label = {}
+ dataSelects = [
+ ['input', "Choose vector map for analysis:", Select],
+ ['alayer', "Arc layer number or name:", LayerSelect],
+ ['nlayer', "Node layer number or name:", LayerSelect],
+ ['afcolumn', self.attrCols['afcolumn']['label'], ColumnSelect],
+ ['abcolumn', self.attrCols['abcolumn']['label'], ColumnSelect],
+ ['ncolumn', self.attrCols['ncolumn']['label'], ColumnSelect],
+ ]
+
+ selPanels = {}
+ for dataSel in dataSelects:
+ selPanels[dataSel[0]] = wx.Panel(parent = dataPanel)
+ if dataSel[0] == 'input' and self.mapWin.tree:
+ self.inputData[dataSel[0]] = dataSel[2](parent = selPanels[dataSel[0]],
+ size = (-1, -1),
+ type = 'vector')
+
+ icon = wx.Image(os.path.join(globalvar.ETCICONDIR, "grass", "layer-vector-add.png"))
+ icon.Rescale(18, 18)
+ icon = wx.BitmapFromImage(icon)
+ self.addToTreeBtn = wx.BitmapButton(parent = selPanels[dataSel[0]],
+ bitmap = icon,
+ size = globalvar.DIALOG_COLOR_SIZE)
+ self.addToTreeBtn.SetToolTipString(_("Add vector map into layer tree"))
+ self.addToTreeBtn.Disable()
+ self.addToTreeBtn.Bind(wx.EVT_BUTTON, self.OnToTreeBtn)
+ else:
+ self.inputData[dataSel[0]] = dataSel[2](parent = selPanels[dataSel[0]],
+ size = (-1, -1))
+ label[dataSel[0]] = wx.StaticText(parent = selPanels[dataSel[0]],
+ name = dataSel[0])
+ label[dataSel[0]].SetLabel(dataSel[1])
+
+ self.inputData['input'].Bind(wx.EVT_TEXT, self.OnVectSel)
+ self.inputData['alayer'].Bind(wx.EVT_TEXT, self.OnALayerSel)
+ self.inputData['nlayer'].Bind(wx.EVT_TEXT, self.OnNLayerSel)
+
+ # Layout
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ box = wx.StaticBox(dataPanel, -1, "Vector map and layers for analysis")
+ bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+ mainSizer.Add(item = bsizer, proportion = 0,
+ flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+
+ for sel in ['input', 'alayer', 'nlayer']:
+ if sel== 'input' and self.mapWin.tree:
+ btn = self.addToTreeBtn
+ else:
+ btn = None
+ selPanels[sel].SetSizer(self._doSelLayout(title = label[sel],
+ sel = self.inputData[sel],
+ btn = btn))
+ bsizer.Add(item = selPanels[sel], proportion = 1,
+ flag = wx.EXPAND)
+
+ box = wx.StaticBox(dataPanel, -1, "Costs")
+ bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+ mainSizer.Add(item = bsizer, proportion = 0,
+ flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+
+ for sel in ['afcolumn', 'abcolumn', 'ncolumn']:
+ selPanels[sel].SetSizer(self._doSelLayout(title = label[sel], sel = self.inputData[sel]))
+ bsizer.Add(item = selPanels[sel], proportion = 0,
+ flag = wx.EXPAND)
+
+ dataPanel.SetSizer(mainSizer)
+
+ def _doSelLayout(self, title, sel, btn = None):
+ # helper function for layout of self.inputData widgets initialized in _createParametersPage
+ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
+ selTitleSizer.Add(item = title, proportion = 1,
+ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+
+ selSizer.Add(item = selTitleSizer, proportion = 0,
+ flag = wx.EXPAND)
+
+ if btn:
+ selFiledSizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+ selFiledSizer.Add(item = sel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL)
+
+ selFiledSizer.Add(item = btn, proportion = 0,
+ flag = wx.EXPAND | wx.ALL)
+
+ selSizer.Add(item = selFiledSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+ else:
+ selSizer.Add(item = sel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+ return selSizer
+
+ def _createInputDbMgrPage(self):
+ """!Tab with attribute tables of analysis input layers"""
+ self.inpDbMgrData['dbMgr'] = DbMgrBase()
+
+ selMapName = None
+ # if selected vector map is in layer tree then set it
+ if self.mapWin.tree and self.mapWin.tree.layer_selected:
+ selMapData = self.mapWin.tree.GetPyData(self.mapWin.tree.layer_selected)[0]
+ if selMapData['type'] == 'vector': # wrap somehow in LayerTree
+ selMapName = selMapData['maplayer'].name
+
+ self.inpDbMgrData['browse'] = self.inpDbMgrData['dbMgr'].CreateDbMgrPage(parent = self.notebook,
+ pageName = 'browse')
+ self.inpDbMgrData['browse'].SetTabAreaColour(globalvar.FNPageColor)
+
+ self.inpDbMgrData['input'] = None
+ if selMapName:
+ self.inputData['input'].SetValue(selMapName)
+ self.OnVectSel(None)
+
+ def _updateInputDbMgrPage(self, show):
+ """!Show or hide input tables tab"""
+ if show and self.notebook.GetPageIndexByName('inputDbMgr') == -1:
+ self.notebook.AddPage(page = self.inpDbMgrData['browse'],
+ text=_('Input tables'),
+ name = 'inputDbMgr')
+ elif not show:
+ self.notebook.RemovePage(page = 'inputDbMgr')
+
+ def _createResultDbMgrPage(self):
+ """!Tab with attribute tables of analysis result layers"""
+ self.resultDbMgrData['dbMgr'] = DbMgrBase()
+ self.resultDbMgrData['browse'] = self.resultDbMgrData['dbMgr'].CreateDbMgrPage(parent = self.notebook,
+ pageName = 'browse')
+ self.resultDbMgrData['browse'].SetTabAreaColour(globalvar.FNPageColor)
+
+ if self.tmp_result:
+ self.resultDbMgrData['input'] = self.tmp_result.GetVectMapName()
+ else:
+ self.resultDbMgrData['input'] = None
+
+ def _updateResultDbMgrPage(self):
+ """!Show or Hide Result tables tab"""
+ # analysis, which created result
+ analysis = self.resultDbMgrData['analysis']
+ #TODO maybe no need to store this information, just check it has attribute table, if so show it
+ haveDbMgr = self.vnetParams[analysis]["resultProps"]["dbMgr"]
+
+ if haveDbMgr and self.notebook.GetPageIndexByName('resultDbMgr') == -1:
+ self.notebook.AddPage(page = self.resultDbMgrData['browse'],
+ text=_('Result tables'),
+ name = 'resultDbMgr')
+ elif not haveDbMgr:
+ self.notebook.RemovePage(page = 'resultDbMgr')
+
+ def OnPageChanged(self, event):
+ """!Tab switched"""
+ if event.GetEventObject() == self.notebook:
+ dbMgrIndxs = []
+ dbMgrIndxs.append(self.notebook.GetPageIndexByName('inputDbMgr'))
+ dbMgrIndxs.append(self.notebook.GetPageIndexByName('resultDbMgr'))
+ if self.notebook.GetSelection() in dbMgrIndxs:
+ self.stBar.AddStatusItem(text = _('Loading tables...'),
+ key = 'dbMgr',
+ priority = self.stPriorities['important'])
+ self._updateDbMgrData()
+ self.stBar.RemoveStatusItem(key = 'dbMgr')
+ # update columns (when some is added in input tables browser), TODO needs optimization
+ elif self.notebook.GetSelection() == self.notebook.GetPageIndexByName('parameters'):
+ self.OnALayerSel(None)
+ self.OnNLayerSel(None)
+
+ def _updateDbMgrData(self):
+ """!Updates input/result tables page """
+ if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('inputDbMgr'):
+ self._updateInputDbMgrData()
+ elif self.notebook.GetSelection() == self.notebook.GetPageIndexByName('resultDbMgr'):
+ self._updateResultDbMgrData()
+ else:
+ self.stBar.RemoveStatusItem('manager')
+
+ def _updateInputDbMgrData(self):
+ """!Loads data according to selected layers in Parameters tab"""
+ inpSel = self.inputData['input'].GetValue().strip()
+ # changed vector map
+ if self.inpDbMgrData['input'] != inpSel:
+ wx.BeginBusyCursor()
+ self.inpDbMgrData['dbMgr'].ChangeVectorMap(vectorName = inpSel)
+ self.inpDbMgrData['input'] = inpSel
+ for layerName in ['alayer', 'nlayer']:
+ try:
+ layer = int(self.inputData[layerName].GetValue())
+ except ValueError:
+ continue
+ self.inpDbMgrData['browse'].AddLayer(layer)
+ wx.EndBusyCursor()
+ # same vector map
+ else:
+ needLayers = []
+ browseLayers = self.inpDbMgrData['browse'].GetAddedLayers()
+ for layerName in ['alayer', 'nlayer']:
+ try:
+ inpLayer = int(self.inputData[layerName].GetValue())
+ except ValueError:
+ continue
+
+ if inpLayer in browseLayers:
+ needLayers.append(inpLayer)
+ continue
+ else:
+ wx.BeginBusyCursor()
+ self.inpDbMgrData['browse'].AddLayer(inpLayer)
+ wx.EndBusyCursor()
+ needLayers.append(inpLayer)
+
+ for layer in browseLayers:
+ if layer not in needLayers:
+ self.inpDbMgrData['browse'].DeletePage(layer)
+
+ def _updateResultDbMgrData(self):
+ """!Loads data from analysis result map"""
+ if not self.tmp_result:
+ return
+ vectName = self.tmp_result.GetVectMapName()
+
+ if self.resultDbMgrData['input'] != vectName:
+ wx.BeginBusyCursor()
+ dbMgr = self.resultDbMgrData['dbMgr']
+ dbMgr.ChangeVectorMap(vectorName = vectName)
+
+ for layer in dbMgr.GetVectorLayers():
+ self.resultDbMgrData['browse'].AddLayer(layer)
+
+ self.resultDbMgrData['input'] = vectName
+ wx.EndBusyCursor()
+
+ def OnToTreeBtn(self, event):
+ """!Add vector map into layer tree"""
+ vectorMap = self.inputData['input'].GetValue()
+ vectMapName, mapSet = self._parseMapStr(vectorMap)
+ vectorMap = vectMapName + '@' + mapSet
+ existsMap = grass.find_file(name = vectMapName,
+ element = 'vector',
+ mapset = mapSet)
+ if not existsMap["name"]:
+ return
+
+ cmd = ['d.vect',
+ 'map=' + vectorMap]
+
+ if self.mapWin.tree.FindItemByData(key = 'name', value = vectorMap) is None:
+ self.mapWin.tree.AddLayer(ltype = "vector",
+ lcmd = cmd,
+ lname =vectorMap,
+ lchecked = True)
+
+ def OnVectSel(self, event):
+ """!When vector map is selected it populates other comboboxes in Parameters tab (layer selects, columns selects)"""
+ if self.snapData['snap_mode']:
+ self.OnSnapping(event = None)
+
+ vectMapName, mapSet = self._parseMapStr(self.inputData['input'].GetValue())
+ vectorMap = vectMapName + '@' + mapSet
+
+ self.inputData['alayer'].Clear()
+ self.inputData['nlayer'].Clear()
+
+ self.inputData['alayer'].InsertLayers(vector = vectorMap)
+ self.inputData['nlayer'].InsertLayers(vector = vectorMap)
+
+ items = self.inputData['alayer'].GetItems()
+ itemsLen = len(items)
+ if itemsLen < 1:
+ if self.mapWin.tree:
+ self.addToTreeBtn.Disable()
+ if hasattr(self, 'inpDbMgrData'):
+ self._updateInputDbMgrPage(show = False)
+ self.inputData['alayer'].SetValue("")
+ self.inputData['nlayer'].SetValue("")
+ for sel in ['afcolumn', 'abcolumn', 'ncolumn']:
+ self.inputData[sel].Clear()
+ self.inputData[sel].SetValue("")
+ return
+ elif itemsLen == 1:
+ self.inputData['alayer'].SetSelection(0)
+ self.inputData['nlayer'].SetSelection(0)
+ elif itemsLen >= 1:
+ if unicode("1") in items:
+ iItem = items.index(unicode("1"))
+ self.inputData['alayer'].SetSelection(iItem)
+ if unicode("2") in items:
+ iItem = items.index(unicode("2"))
+ self.inputData['nlayer'].SetSelection(iItem)
+
+ if self.mapWin.tree:
+ self.addToTreeBtn.Enable()
+ if hasattr(self, 'inpDbMgrData'):
+ self._updateInputDbMgrPage(show = True)
+
+ self.OnALayerSel(event)
+ self.OnNLayerSel(event)
+
+ def OnALayerSel(self, event):
+ """!When arc layer from vector map is selected, populates corespondent columns selects"""
+ self.inputData['afcolumn'].InsertColumns(vector = self.inputData['input'].GetValue(),
+ layer = self.inputData['alayer'].GetValue(),
+ type = self.columnTypes)
+ self.inputData['abcolumn'].InsertColumns(vector = self.inputData['input'].GetValue(),
+ layer = self.inputData['alayer'].GetValue(),
+ type = self.columnTypes)
+
+
+ def OnNLayerSel(self, event):
+ """!When node layer from vector map is selected, populates corespondent column select"""
+ if self.snapData['snap_mode']:
+ self.OnSnapping(event = None)
+
+ self.inputData['ncolumn'].InsertColumns(vector = self.inputData['input'].GetValue(),
+ layer = self.inputData['nlayer'].GetValue(),
+ type = self.columnTypes)
+
+ def _getInvalidInputs(self, inpToTest):
+ """!Check of analysis input data for invalid values (Parameters tab)"""
+ # dict of invalid values {key from self.itemData (comboboxes from Parameters tab) : invalid value}
+ errInput = {}
+
+ mapVal = self.inputData['input'].GetValue()
+ mapName, mapSet = self._parseMapStr(mapVal)
+ vectMaps = grass.list_grouped('vect')[mapSet]
+
+ # check of vector map
+ if not inpToTest or "input" in inpToTest:
+ if mapName not in vectMaps:
+ errInput['input'] = mapVal
+
+ # check of arc/node layer
+ for layerSelName in ['alayer', 'nlayer'] :
+ if not inpToTest or layerSelName in inpToTest:
+
+ layerItems = self.inputData[layerSelName].GetItems()
+ layerVal = self.inputData[layerSelName].GetValue().strip()
+ if layerVal not in layerItems:
+ errInput[layerSelName] = layerVal
+
+ # check of costs columns
+ currModCols = self.vnetParams[self.currAnModule]["cmdParams"]["cols"]
+ for col, colData in self.attrCols.iteritems():
+ if not inpToTest or col in inpToTest:
+
+ if col not in currModCols:
+ continue
+
+ if "inputField" in self.attrCols[col]:
+ colInptF = self.attrCols[col]["inputField"]
+ else:
+ colInptF = col
+
+ if not self.inputData[colInptF].IsShown():
+ continue
+ colVal = self.inputData[colInptF].GetValue().strip()
+
+ if not colVal:
+ continue
+ if colVal not in self.inputData[colInptF].GetItems():
+ errInput[col] = colVal
+
+ return errInput
+
+ def _parseMapStr(self, vectMapStr):
+ """!Create full map name (add current mapset if it is not present in name)"""
+ mapValSpl = vectMapStr.strip().split("@")
+ if len(mapValSpl) > 1:
+ mapSet = mapValSpl[1]
+ else:
+ mapSet = grass.gisenv()['MAPSET']
+ mapName = mapValSpl[0]
+
+ return mapName, mapSet
+
+ def InputsErrorMsgs(self, msg, inpToTest = None):
+ """!Checks input data in Parameters tab and shows messages if some value is not valid
+
+ @param msg (str) - message added to start of message string
+ @param inpToTest (list) - list of keys from self.itemData map, which says which input data should be checked,
+ if is empty or None, all input data are checked
+ @return True - if checked inputs are OK
+ @return False - if some of checked inputs is not ok
+ """
+ errInput = self._getInvalidInputs(inpToTest)
+
+ errMapStr = ""
+ if errInput.has_key('input'):
+ self.notebook.SetSelectionByName("parameters")
+ if errInput['input']:
+ errMapStr = _("Vector map '%s' does not exist.") % (errInput['input'])
+ else:
+ errMapStr = _("Vector map was not chosen.")
+
+
+ if errMapStr:
+ GMessage(parent = self,
+ message = msg + "\n" + errMapStr)
+ return False
+
+ errLayerStr = ""
+ for layer, layerLabel in {'alayer' : _("arc layer"),
+ 'nlayer' : _("node layer")}.iteritems():
+
+ if errInput.has_key(layer):
+ if errInput[layer]:
+ errLayerStr += _("Chosen %s '%s' does not exist in vector map '%s'.\n") % \
+ (layerLabel, self.inputData[layer].GetValue(), self.inputData['input'].GetValue())
+ else:
+ errLayerStr += _("Choose existing %s.\n") % \
+ (layerLabel)
+ if errLayerStr:
+ GMessage(parent = self,
+ message = msg + "\n" + errLayerStr)
+ return False
+
+ errColStr = ""
+ for col, colData in self.attrCols.iteritems():
+ if col in errInput.iterkeys():
+ errColStr += _("Chosen column '%s' does not exist in attribute table of layer '%s' of vector map '%s'.\n") % \
+ (errInput[col], self.inputData[layer].GetValue(), self.inputData['input'].GetValue())
+
+ if errColStr:
+ self.notebook.SetSelectionByName("parameters")
+ GMessage(parent = self,
+ message = msg + "\n" + errColStr)
+ return False
+
+ return True
+
+ def OnCloseDialog(self, event):
+ """!Cancel dialog"""
+ self.parent.dialogs['vnet'] = None
+ self.Destroy()
+
+ def SetPointStatus(self, item, itemIndex):
+ """!Before point is drawn, decides properties of drawing style"""
+ key = self.list.GetItemData(itemIndex)
+ cats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+
+ if key == self.list.selected:
+ wxPen = "selected"
+ elif not self.list.IsChecked(key):
+ wxPen = "unused"
+ item.hide = False
+ elif len(cats) > 1:
+ idx = self.list.GetCellSelIdx(key, 'type')
+ if idx == 2: #End/To/Sink point
+ wxPen = "used2cat"
+ else:
+ wxPen = "used1cat"
+ else:
+ wxPen = "used1cat"
+
+ item.SetPropertyVal('label', str(itemIndex + 1))
+ item.SetPropertyVal('penName', wxPen)
+
+ def OnMapClickHandler(self, event):
+ """!Take coordinates from map window"""
+ if event == 'unregistered':
+ ptListToolbar = self.toolbars['pointsList']
+ if ptListToolbar:
+ ptListToolbar.ToggleTool( id = ptListToolbar.GetToolId("insertPoint"),
+ toggle = False)
+ self.handlerRegistered = False
+ return
+
+ self.notebook.SetSelectionByName("points")
+ if not self.list.itemDataMap:
+ self.list.AddItem(None)
+
+ e, n = self.mapWin.GetLastEN()
+
+ index = self.list.selected
+ key = self.list.GetItemData(index)
+
+ if self.snapData['snap_mode']:
+ coords = [e, n]
+ if self._snapPoint(coords):
+ msg = ("snapped to node")
+ else:
+ msg = _("new point")
+
+ e = coords[0]
+ n = coords[1]
+
+ else:
+ msg = _("new point")
+
+ self.list.EditCellKey(key = self.list.selected ,
+ colName = 'topology',
+ cellData = msg)
+
+ self.pointsToDraw.GetItem(key).SetCoords([e, n])
+
+ if self.list.selected == self.list.GetItemCount() - 1:
+ self.list.selected = 0
+ else:
+ self.list.selected += 1
+ self.list.Select(self.list.selected)
+
+ def _snapPoint(self, coords):
+ """!Find nearest node to click coordinates (within given threshold)"""
+ e = coords[0]
+ n = coords[1]
+
+ # compute threshold
+ snapTreshPix = int(UserSettings.Get(group ='vnet',
+ key = 'other',
+ subkey = 'snap_tresh'))
+ res = max(self.mapWin.Map.region['nsres'], self.mapWin.Map.region['ewres'])
+ snapTreshDist = snapTreshPix * res
+
+ vectorMap = self.inputData['input'].GetValue()
+ vectMapName, mapSet = self._parseMapStr(vectorMap)
+ inpMapExists = grass.find_file(name = vectMapName,
+ element = 'vector',
+ mapset = mapSet)
+ if not inpMapExists['name']:
+ return False
+
+ openedMap = pointer(vectlib.Map_info())
+ ret = vectlib.Vect_open_old2(openedMap,
+ c_char_p(vectMapName),
+ c_char_p(mapSet),
+ c_char_p(self.inputData['alayer'].GetValue()))
+ if ret == 1:
+ vectlib.Vect_close(openedMap)
+ if ret != 2:
+ return False
+
+ nodeNum = vectlib.Vect_find_node(openedMap,
+ c_double(e),
+ c_double(n),
+ c_double(0),
+ c_double(snapTreshDist),
+ vectlib.WITHOUT_Z)
+
+ if nodeNum > 0:
+ e = c_double(0)
+ n = c_double(0)
+ vectlib.Vect_get_node_coor(openedMap,
+ nodeNum,
+ byref(e),
+ byref(n),
+ None); # z
+ e = e.value
+ n = n.value
+ else:
+ vectlib.Vect_close(openedMap)
+ return False
+
+ coords[0] = e
+ coords[1] = n
+ return True
+
+ def OnAnalyze(self, event):
+ """!Called when network analysis is started"""
+ # Check of parameters for analysis
+ if not self.InputsErrorMsgs(msg = _("Analysis can not be done.")):
+ return
+
+ if self.tmp_result:
+ self.tmp_result.DeleteRenderLayer()
+
+ # history - delete data in buffer for hist step
+ self.history.DeleteNewHistStepData()
+ # empty list for maps to be saved to history
+ self.tmpVectMapsToHist= []
+ # create new map (included to history) for result of analysis
+ self.tmp_result = self.NewTmpVectMapToHist('vnet_tmp_result')
+
+ if not self.tmp_result:
+ return
+
+ self.stBar.AddStatusItem(text = _('Analysing...'),
+ key = 'analyze',
+ priority = self.stPriorities['important'])
+
+ # for case there is some map with same name
+ # (when analysis does not produce any map, this map would have been shown as result)
+ RunCommand('g.remove',
+ vect = self.tmp_result.GetVectMapName())
+
+ # save data from
+ self._saveAnInputToHist()
+
+ self.resultDbMgrData['analysis'] = self.currAnModule
+
+ # Creates part of cmd fro analysis
+ cmdParams = [self.currAnModule]
+ cmdParams.extend(self._getInputParams())
+ cmdParams.append("output=" + self.tmp_result.GetVectMapName())
+
+ catPts = self._getPtByCat()
+
+ if self.currAnModule == "v.net.path":
+ self._vnetPathRunAn(cmdParams, catPts)
+ else:
+ self._runAn(cmdParams, catPts)
+
+ def _vnetPathRunAn(self, cmdParams, catPts):
+ """!Called when analysis is run for v.net.path module"""
+ if len(self.pointsToDraw.GetAllItems()) < 1:
+ return
+ cats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+
+ cmdPts = []
+ for cat in cats:
+ if len(catPts[cat[0]]) < 1:
+ GMessage(parent = self,
+ message=_("Please choose '%s' and '%s' point.") % (cats[0][1], cats[1][1]))
+ return
+ cmdPts.append(catPts[cat[0]][0])
+
+ resId = 1
+ inpPoints = str(resId) + " " + str(cmdPts[0][0]) + " " + str(cmdPts[0][1]) + \
+ " " + str(cmdPts[1][0]) + " " + str(cmdPts[1][1])
+
+ self.coordsTmpFile = grass.tempfile()
+ coordsTmpFileOpened = open(self.coordsTmpFile, 'w')
+ coordsTmpFileOpened.write(inpPoints)
+ coordsTmpFileOpened.close()
+
+ cmdParams.append("file=" + self.coordsTmpFile)
+
+ cmdParams.append("dmax=" + str(self.anSettings["max_dist"].GetValue()))
+ cmdParams.append("input=" + self.inputData['input'].GetValue())
+
+ cmdParams.append("--overwrite")
+ self._prepareCmd(cmd = cmdParams)
+
+ self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunAnDone)
+
+ def _vnetPathRunAnDone(self, cmd, returncode):
+ """!Called when v.net.path analysis is done"""
+ grass.try_remove(self.coordsTmpFile)
+
+ self._saveHistStep()
+ self.tmp_result.SaveVectMapState()
+
+ self._updateResultDbMgrPage()
+ self._updateDbMgrData()
+
+ cmd = self.GetLayerStyle()
+ self.tmp_result.AddRenderLayer(cmd)
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+ mainToolbar = self.toolbars['mainToolbar']
+ id = vars(mainToolbar)['showResult']
+ mainToolbar.ToggleTool(id =id,
+ toggle = True)
+
+ self.stBar.RemoveStatusItem(key = 'analyze')
+
+ def _runAn(self, cmdParams, catPts):
+ """!Called for all v.net.* analysis (except v.net.path)"""
+
+ cats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+
+ if len(cats) > 1:
+ for cat in cats:
+ if len(catPts[cat[0]]) < 1:
+ GMessage(parent = self,
+ message = _("Please choose '%s' and '%s' point.") \
+ % (cats[0][1], cats[1][1]))
+ return
+ else:
+ for cat in cats:
+ if len(catPts[cat[0]]) < 2:
+ GMessage(parent = self,
+ message = _("Please choose at least two points."))
+ return
+
+ # TODO add also to thread for analysis?
+ vcatResult = RunCommand("v.category",
+ input = self.inputData['input'].GetValue(),
+ option = "report",
+ flags = "g",
+ read = True)
+
+ vcatResult = vcatResult.splitlines()
+ for cat in vcatResult:#TODO
+ cat = cat.split()
+ if "all" in cat:
+ maxCat = int(cat[4])
+ break
+
+ layerNum = self.inputData["nlayer"].GetValue().strip()
+
+ pt_ascii, catsNums = self._getAsciiPts (catPts = catPts,
+ maxCat = maxCat,
+ layerNum = layerNum)
+
+ self.tmpPtsAsciiFile = grass.tempfile()#TODO better tmp files cleanup (make class for managing tmp files)
+ tmpPtsAsciiFileOpened = open(self.tmpPtsAsciiFile, 'w')
+ tmpPtsAsciiFileOpened.write(pt_ascii)
+ tmpPtsAsciiFileOpened.close()
+
+ self.tmpInPts = self._addTmpMapAnalysisMsg("vnet_tmp_in_pts")
+ if not self.tmpInPts:
+ return
+
+ self.tmpInPtsConnected = self._addTmpMapAnalysisMsg("vnet_tmp_in_pts_connected")
+ if not self.tmpInPtsConnected:
+ return
+
+ cmdParams.append("input=" + self.tmpInPtsConnected.GetVectMapName())
+ cmdParams.append("--overwrite")
+
+ # append parameters needed for particular analysis
+ if self.currAnModule == "v.net.distance":
+ cmdParams.append("from_layer=1")
+ cmdParams.append("to_layer=1")
+ elif self.currAnModule == "v.net.flow":
+ self.vnetFlowTmpCut = self.NewTmpVectMapToHist('vnet_tmp_flow_cut')
+ if not self.vnetFlowTmpCut:
+ return
+ cmdParams.append("cut=" + self.vnetFlowTmpCut.GetVectMapName())
+ elif self.currAnModule == "v.net.iso":
+ costs = self.anSettings["iso_lines"].GetValue()
+ cmdParams.append("costs=" + costs)
+ for catName, catNum in catsNums.iteritems():
+ if catNum[0] == catNum[1]:
+ cmdParams.append(catName + "=" + str(catNum[0]))
+ else:
+ cmdParams.append(catName + "=" + str(catNum[0]) + "-" + str(catNum[1]))
+
+ # create and run commands which goes to analysis thread
+ cmdVEdit = [
+ "v.edit",
+ "map=" + self.tmpInPts.GetVectMapName(),
+ "input=" + self.tmpPtsAsciiFile,
+ "tool=create",
+ "--overwrite",
+ "-n"
+ ]
+ self._prepareCmd(cmdVEdit)
+ self.goutput.RunCmd(command = cmdVEdit)
+
+ cmdVNet = [
+ "v.net",
+ "points=" + self.tmpInPts.GetVectMapName(),
+ "input=" + self.inputData['input'].GetValue(),
+ "output=" + self.tmpInPtsConnected.GetVectMapName(),
+ "alayer=" + self.inputData["alayer"].GetValue().strip(),
+ "nlayer=" + self.inputData["nlayer"].GetValue().strip(),
+ "operation=connect",
+ "thresh=" + str(self.anSettings["max_dist"].GetValue()),
+ "--overwrite"
+ ] #TODO snapping to nodes optimization
+ self._prepareCmd(cmdVNet)
+ self.goutput.RunCmd(command = cmdVNet)
+
+ self._prepareCmd(cmdParams)
+ self.goutput.RunCmd(command = cmdParams, onDone = self._runAnDone)
+
+ def _runAnDone(self, cmd, returncode):
+ """!Called when analysis is done"""
+ self.tmpMaps.DeleteTmpMap(self.tmpInPts) #TODO remove earlier (OnDone lambda?)
+ self.tmpMaps.DeleteTmpMap(self.tmpInPtsConnected)
+ grass.try_remove(self.tmpPtsAsciiFile)
+
+ self._saveHistStep()
+ self.tmp_result.SaveVectMapState()
+ if self.currAnModule == "v.net.flow":
+ self.vnetFlowTmpCut.SaveVectMapState()
+ self._updateResultDbMgrPage()
+ self._updateDbMgrData()
+
+ cmd = self.GetLayerStyle()
+ self.tmp_result.AddRenderLayer(cmd)
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+ mainToolbar = self.toolbars['mainToolbar']
+ id = vars(mainToolbar)['showResult']
+ mainToolbar.ToggleTool(id =id,
+ toggle = True)
+
+ self.stBar.RemoveStatusItem(key = 'analyze')
+
+ def _getInputParams(self):
+ """!Return list of chosen values (vector map, layers) from Parameters tab.
+
+ The list items are in form to be used in command for analysis e.g. 'alayer=1'.
+ """
+
+ inParams = []
+ for col in self.vnetParams[self.currAnModule]["cmdParams"]["cols"]:
+
+ if "inputField" in self.attrCols[col]:
+ colInptF = self.attrCols[col]["inputField"]
+ else:
+ colInptF = col
+
+ inParams.append(col + '=' + self.inputData[colInptF].GetValue())
+
+ for layer in ['alayer', 'nlayer']:
+ inParams.append(layer + "=" + self.inputData[layer].GetValue().strip())
+
+ return inParams
+
+ def _getPtByCat(self):
+ """!Return points separated by theirs categories"""
+ cats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+
+ ptByCats = {}
+ for cat in self.vnetParams[self.currAnModule]["cmdParams"]["cats"]:
+ ptByCats[cat[0]] = []
+
+ for i in range(len(self.list.itemDataMap)):
+ key = self.list.GetItemData(i)
+ if self.list.IsChecked(key):
+ for cat in cats:
+ if cat[1] == self.list.itemDataMap[key][1] or len(ptByCats) == 1:
+ ptByCats[cat[0]].append(self.pointsToDraw.GetItem(key).GetCoords())
+ continue
+
+ return ptByCats
+
+ def _getAsciiPts (self, catPts, maxCat, layerNum):
+ """!Return points separated by categories in GRASS ASCII vector representation"""
+ catsNums = {}
+ pt_ascii = ""
+ catNum = maxCat
+
+ for catName, pts in catPts.iteritems():
+
+ catsNums[catName] = [catNum + 1]
+ for pt in pts:
+ catNum += 1
+ pt_ascii += "P 1 1\n"
+ pt_ascii += str(pt[0]) + " " + str(pt[1]) + "\n"
+ pt_ascii += str(layerNum) + " " + str(catNum) + "\n"
+
+ catsNums[catName].append(catNum)
+
+ return pt_ascii, catsNums
+
+ def _prepareCmd(self, cmd):
+ """!Helper function for preparation of cmd list into form for RunCmd method"""
+ for c in cmd[:]:
+ if c.find("=") == -1:
+ continue
+ v = c.split("=")
+ if len(v) != 2:
+ cmd.remove(c)
+ elif not v[1].strip():
+ cmd.remove(c)
+
+ def GetLayerStyle(self):
+ """!Returns cmd for d.vect, with set style for analysis result"""
+ resProps = self.vnetParams[self.currAnModule]["resultProps"]
+
+ width = UserSettings.Get(group='vnet', key='res_style', subkey= "line_width")
+ layerStyleCmd = ["layer=1",'width=' + str(width)]
+
+ if "catColor" in resProps:
+ layerStyleCmd.append('flags=c')
+ elif "singleColor" in resProps:
+ col = UserSettings.Get(group='vnet', key='res_style', subkey= "line_color")
+ layerStyleCmd.append('color=' + str(col[0]) + ':' + str(col[1]) + ':' + str(col[2]))
+
+ if "attrColColor" in resProps:
+ colorStyle = UserSettings.Get(group='vnet', key='res_style', subkey= "color_table")
+ invert = UserSettings.Get(group='vnet', key='res_style', subkey= "invert_colors")
+
+ layerStyleVnetColors = [
+ "v.colors",
+ "map=" + self.tmp_result.GetVectMapName(),
+ "color=" + colorStyle,
+ "column=" + resProps["attrColColor"],
+ ]
+ if invert:
+ layerStyleVnetColors.append("-n")
+
+ layerStyleVnetColors = utils.CmdToTuple(layerStyleVnetColors)
+
+ RunCommand( layerStyleVnetColors[0],
+ **layerStyleVnetColors[1])
+
+ return layerStyleCmd
+
+ def OnShowResult(self, event):
+ """!Show/hide analysis result"""
+ mainToolbar = self.toolbars['mainToolbar']
+ id = vars(mainToolbar)['showResult']
+ toggleState = mainToolbar.GetToolState(id)
+
+ if not self.tmp_result:
+ mainToolbar.ToggleTool(id =id,
+ toggle = False)
+ elif toggleState:
+ self._checkResultMapChanged(self.tmp_result)
+ cmd = self.GetLayerStyle()
+ self.tmp_result.AddRenderLayer(cmd)
+ else:
+ cmd = self.GetLayerStyle()
+ self.tmp_result.DeleteRenderLayer()
+
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+
+ def OnInsertPoint(self, event):
+ """!Registers/unregisters mouse handler into map window"""
+ if self.handlerRegistered == False:
+ self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
+ self.OnMapClickHandler,
+ wx.StockCursor(wx.CURSOR_CROSS))
+ self.handlerRegistered = True
+ else:
+ self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
+ self.OnMapClickHandler)
+ self.handlerRegistered = False
+
+ def OnSaveTmpLayer(self, event):
+ """!Permanently saves temporary map of analysis result"""
+ dlg = AddLayerDialog(parent = self)
+
+ msg = _("Vector map with analysis result does not exist.")
+ if dlg.ShowModal() == wx.ID_OK:
+
+ if not hasattr(self.tmp_result, "GetVectMapName"):
+ GMessage(parent = self,
+ message = msg)
+ return
+
+ mapToAdd = self.tmp_result.GetVectMapName()
+ mapToAddEx = grass.find_file(name = mapToAdd,
+ element = 'vector',
+ mapset = grass.gisenv()['MAPSET'])
+
+ if not mapToAddEx["name"]:
+ GMessage(parent = self,
+ message = msg)
+ dlg.Destroy()
+ return
+
+ addedMap = dlg.vectSel.GetValue()
+ mapName, mapSet = self._parseMapStr(addedMap)
+ if mapSet != grass.gisenv()['MAPSET']:
+ GMessage(parent = self,
+ message = _("Map can be saved only to currently set mapset"))
+ return
+ existsMap = grass.find_file(name = mapName,
+ element = 'vector',
+ mapset = grass.gisenv()['MAPSET'])
+ dlg.Destroy()
+ if existsMap["name"]:
+ dlg = wx.MessageDialog(parent = self.parent.parent,
+ message = _("Vector map %s already exists. " +
+ "Do you want to overwrite it?") %
+ (existsMap["fullname"]),
+ caption = _("Overwrite map layer"),
+ style = wx.YES_NO | wx.NO_DEFAULT |
+ wx.ICON_QUESTION | wx.CENTRE)
+ ret = dlg.ShowModal()
+ if ret == wx.ID_NO:
+ dlg.Destroy()
+ return
+ dlg.Destroy()
+
+ RunCommand("g.copy",
+ overwrite = True,
+ vect = [self.tmp_result.GetVectMapName(), mapName])
+
+ cmd = self.GetLayerStyle()#TODO get rid of insert
+ cmd.insert(0, 'd.vect')
+ cmd.append('map=%s' % mapName)
+
+ if not self.mapWin.tree:
+ return
+
+ if self.mapWin.tree.FindItemByData(key = 'name', value = mapName) is None:
+ self.mapWin.tree.AddLayer(ltype = "vector",
+ lname = mapName,
+ lcmd = cmd,
+ lchecked = True)
+ else:
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+
+ def OnSettings(self, event):
+ """!Displays vnet settings dialog"""
+ dlg = SettingsDialog(parent=self, id=wx.ID_ANY, title=_('Settings'))
+
+ if dlg.ShowModal() == wx.ID_OK:
+ pass
+
+ dlg.Destroy()
+
+ def OnAnalysisChanged(self, event):
+ """!Updates dialog when analysis is changed"""
+ # set chosen analysis
+ iAn = self.toolbars['analysisToolbar'].anChoice.GetSelection()
+ self.currAnModule = self.vnetModulesOrder[iAn]
+
+ # update dialog for particular analysis
+ if self.currAnModule == "v.net.iso":
+ self.anSettings['iso_lines'].GetParent().Show()
+ else:
+ self.anSettings['iso_lines'].GetParent().Hide()
+
+ #if self.currAnModule == "v.net.flow": TODO not implemented
+ # self.anSettings['show_cut'].GetParent().Show()
+ #else:
+ # self.anSettings['show_cut'].GetParent().Hide()
+
+ # show only corresponding selects for chosen v.net module
+ skip = []
+ for col in self.attrCols.iterkeys():
+ if "inputField" in self.attrCols[col]:
+ colInptF = self.attrCols[col]["inputField"]
+ else:
+ colInptF = col
+
+ if col in skip:
+ continue
+
+ inputPanel = self.inputData[colInptF].GetParent()
+ if col in self.vnetParams[self.currAnModule]["cmdParams"]["cols"]:
+ inputPanel.Show()
+ inputPanel.FindWindowByName(colInptF).SetLabel(self.attrCols[col]["label"])
+ inputPanel.Layout()
+ if col != colInptF:
+ skip.append(colInptF)
+ else:
+ self.inputData[colInptF].GetParent().Hide()
+ self.Layout()
+
+ # if module has only one category -> hide type column in points list otherwise show it
+ if len(self.vnetParams[self.currAnModule]["cmdParams"]["cats"]) > 1:
+ if not self.list.IsShown('type'):
+ self.list.ShowColumn('type', 1)
+
+ currParamsCats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+ self.list.AdaptPointsList(currParamsCats)
+ else:
+ if self.list.IsShown('type'):
+ self.list.HideColumn('type')
+
+ # for v.net.path just one 'Start point' and one 'End point' can be checked
+ if self.currAnModule == "v.net.path":
+ self.list.UpdateCheckedItems(index = None)
+
+ def OnSnapping(self, event):
+ """!Start/stop snapping mode"""
+ ptListToolbar = self.toolbars['pointsList']
+
+ if not haveCtypes:
+ ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+ toggle = False)
+ GMessage(parent = self,
+ message = _("Unable to use ctypes. \n") + \
+ _("Snapping mode can not be activated."))
+ return
+
+ if not event or not event.IsChecked():
+ if not event:
+ ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+ toggle = False)
+ if self.tmpMaps.HasTmpVectMap("vnet_snap_points"):
+ self.snapPts.DeleteRenderLayer()
+ self.mapWin.UpdateMap(render = False, renderVector = False)
+
+ if self.snapData.has_key('cmdThread'):
+ self.snapData['cmdThread'].abort()
+
+ self.snapData['snap_mode'] = False
+ return
+
+ if not self.InputsErrorMsgs(msg = _("Snapping mode can not be activated."),
+ inpToTest = ["input", "nlayer"]):
+
+ ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+ toggle = False)
+ return
+
+ if not self.tmpMaps.HasTmpVectMap("vnet_snap_points"):
+ endStr = _("Do you really want to activate snapping and overwrite it?")
+ self.snapPts = self.tmpMaps.AddTmpVectMap("vnet_snap_points", endStr)
+
+ if not self.snapPts:
+ ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+ toggle = False)
+ return
+ elif self.snapPts.VectMapState() == 0:
+ dlg = wx.MessageDialog(parent = self.parent,
+ message = _("Temporary map '%s' was changed outside " +
+ "vector analysis tool.\n"
+ "Do you really want to activate " +
+ "snapping and overwrite it? ") % \
+ self.snapPts.GetVectMapName(),
+ caption = _("Overwrite map"),
+ style = wx.YES_NO | wx.NO_DEFAULT |
+ wx.ICON_QUESTION | wx.CENTRE)
+
+ ret = dlg.ShowModal()
+ dlg.Destroy()
+
+ if ret == wx.ID_NO:
+ self.tmpMaps.DeleteTmpMap(self.snapPts)
+ ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+ toggle = False)
+ return
+
+ self.snapData['snap_mode'] = True
+
+ inpName = self.inputData['input'].GetValue()
+ inpName, mapSet = self._parseMapStr(inpName)
+ inpFullName = inpName + '@' + mapSet
+ computeNodes = True
+
+ if not self.snapData.has_key("inputMap"):
+ pass
+ elif inpFullName != self.snapData["inputMap"].GetVectMapName():
+ self.snapData["inputMap"] = VectMap(self, inpFullName)
+ elif self.snapData["inputMapNlayer"] == self.inputData["nlayer"].GetValue():
+ if self.snapData["inputMap"].VectMapState() == 1:
+ computeNodes = False
+
+ # new map need
+ if computeNodes:
+ self.stBar.AddStatusItem(text = _('Computing nodes...'),
+ key = 'snap',
+ priority = self.stPriorities['important'])
+ if not self.snapData.has_key('cmdThread'):
+ self.snapData['cmdThread'] = CmdThread(self)
+
+ cmd = ["v.to.points", "input=" + self.inputData['input'].GetValue(),
+ "output=" + self.snapPts.GetVectMapName(),
+ "llayer=" + self.inputData["nlayer"].GetValue(),
+ "-n", "--overwrite"]
+ # process GRASS command with argument
+ self.snapData["inputMap"] = VectMap(self, inpFullName)
+ self.snapData["inputMap"].SaveVectMapState()
+
+ self.Bind(EVT_CMD_DONE, self._onToPointsDone)
+ self.snapData['cmdThread'].RunCmd(cmd)
+
+ self.snapData["inputMapNlayer"] = self.inputData["nlayer"].GetValue()
+ # map is already created and up to date for input data
+ else:
+ self.snapPts.AddRenderLayer()
+ self.mapWin.UpdateMap(render = True, renderVector = True)
+
+
+ def _onToPointsDone(self, event):
+ """!Update map window, when map with nodes to snap is created"""
+ self.stBar.RemoveStatusItem(key = 'snap')
+ if not event.aborted:
+ self.snapPts.SaveVectMapState()
+ self.snapPts.AddRenderLayer()
+ self.mapWin.UpdateMap(render = True, renderVector = True)
+
+ def OnUndo(self, event):
+ """!Step back in history"""
+ histStepData = self.history.GetPrev()
+ self.toolbars['mainToolbar'].UpdateUndoRedo()
+
+ if histStepData:
+ self._updateHistStepData(histStepData)
+
+ def OnRedo(self, event):
+ """!Step forward in history"""
+ histStepData = self.history.GetNext()
+ self.toolbars['mainToolbar'].UpdateUndoRedo()
+
+ if histStepData:
+ self._updateHistStepData(histStepData)
+
+ def _saveAnInputToHist(self):
+ """!Save all data needed for analysis into history buffer"""
+ pts = self.pointsToDraw.GetAllItems()
+
+ for iPt, pt in enumerate(pts):
+ ptName = "pt" + str(iPt)
+
+ coords = pt.GetCoords()
+ self.history.Add(key = "points",
+ subkey = [ptName, "coords"],
+ value = coords)
+ # save type column
+ # if is shown
+ if len(self.vnetParams[self.currAnModule]["cmdParams"]["cats"]) > 1:
+ cat = self.list.GetCellSelIdx(iPt, 'type')
+ self.history.Add(key = "points",
+ subkey = [ptName, "catIdx"],
+ value = cat)
+ # if is hidden
+ else:
+ self.history.Add(key = 'points_hidden_cols',
+ subkey = 'type',
+ value = self.list.GetHiddenColSelIdxs('type'))
+
+ topology = self.list.GetCellValue(iPt, 'topology')
+ self.history.Add(key = "points",
+ subkey = [ptName, "topology"],
+ value = topology)
+
+
+ self.history.Add(key = "points",
+ subkey = [ptName, "checked"],
+ value = self.list.IsChecked(iPt))
+
+ for inpName, inp in self.inputData.iteritems():
+
+ if inpName == "input":
+ vectMapName, mapSet = self._parseMapStr(inp.GetValue())
+ inpMapFullName = vectMapName + '@' + mapSet
+ inpMap = VectMap(self, inpMapFullName)
+ self.history.Add(key = "other",
+ subkey = "input_modified",
+ value = inpMap.GetLastModified())
+ inpVal = inpMapFullName
+ else:
+ inpVal = inp.GetValue()
+
+ self.history.Add(key = "input_data",
+ subkey = inpName,
+ value = inpVal)
+
+ self.history.Add(key = "vnet_modules",
+ subkey = "curr_module",
+ value = self.currAnModule)
+
+ for settName, sett in self.anSettings.iteritems():
+ self.history.Add(key = "an_settings",
+ subkey = settName,
+ value = sett.GetValue())
+
+
+ def _saveHistStep(self):
+ """!Save new step into history"""
+ removedHistData = self.history.SaveHistStep()
+ self.toolbars['mainToolbar'].UpdateUndoRedo()
+
+ if not removedHistData:
+ return
+
+ # delete temporary maps in history steps which were deleted
+ for removedStep in removedHistData.itervalues():
+ mapsNames = removedStep["tmp_data"]["maps"]
+ for vectMapName in mapsNames:
+ tmpMap = self.tmpMaps.GetTmpVectMap(vectMapName)
+ self.tmpMaps.DeleteTmpMap(tmpMap)
+
+ def _updateHistStepData(self, histStepData):
+ """!Updates dialog according to chosen history step"""
+
+ # set analysis module
+ self.currAnModule = histStepData["vnet_modules"]["curr_module"]
+
+ # optimization -map is not re-rendered when change in points list is done
+ self.list.SetUpdateMap(updateMap = False)
+
+ # delete points list items
+ while self.list.GetSelected() != wx.NOT_FOUND:
+ self.list.DeleteItem()
+
+ # show/hide 'type' column according to particular analysis
+ if len(self.vnetParams[self.currAnModule]["cmdParams"]["cats"]) > 1:
+ hasType = True
+ self.list.ShowColumn('type', 1)
+ else:
+ hasType = False
+ self.list.HideColumn('type')
+
+ # add points to list
+ for iPt in range(len(histStepData["points"])):
+ ptData = histStepData["points"]["pt" + str(iPt)]
+ coords = ptData["coords"]
+ self.list.AddItem()
+ item = self.pointsToDraw.GetItem(iPt)
+ item.SetCoords(coords)
+
+ if hasType:
+ self.list.EditCellKey(iPt, 'type', int(ptData["catIdx"]))
+
+ self.list.EditCellKey(iPt, 'topology', ptData["topology"])
+
+ if ptData["checked"]:
+ self.list.CheckItem(iPt, True)
+
+ if hasType:
+ currParamsCats = self.vnetParams[self.currAnModule]["cmdParams"]["cats"]
+ self.list.AdaptPointsList(currParamsCats)
+ else:
+ self.list.SetHiddenSelIdxs('type', histStepData["points_hidden_cols"]["type"])
+
+ # set analysis combobox
+ anChoice = self.toolbars['analysisToolbar'].anChoice
+ anChoice.SetSelection(self.vnetModulesOrder.index(self.currAnModule))
+
+ # update analysis result maps
+ mapsNames = histStepData["tmp_data"]["maps"]
+ for vectMapName in mapsNames:
+ if "vnet_tmp_result" in vectMapName:
+ self.tmp_result.DeleteRenderLayer()
+ self.tmp_result = self.tmpMaps.GetTmpVectMap(vectMapName)
+ self._checkResultMapChanged(self.tmp_result)
+
+ cmd = self.GetLayerStyle()
+ self.tmp_result.AddRenderLayer(cmd)
+
+ # update Parameters tab
+ histInputData = histStepData["input_data"]
+ for inpName, inp in histInputData.iteritems():
+ self.inputData[inpName].SetValue(str(inp))
+ if inpName == "input":
+ inpMap = inp
+
+ prevInpModTime = str(histStepData["other"]["input_modified"])
+ currInpModTime = VectMap(self, inpMap).GetLastModified()
+
+ if currInpModTime.strip()!= prevInpModTime.strip():
+ dlg = wx.MessageDialog(parent = self,
+ message = _("Input map '%s' for analysis was changed outside " +
+ "vector network analysis tool.\n" +
+ "Topology column may not " +
+ "correspond to changed situation.") %\
+ inpMap,
+ caption = _("Input changed outside"),
+ style = wx.ICON_INFORMATION| wx.CENTRE)
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ # update Points tab (analysis settings)
+ histAnSettData = histStepData["an_settings"]
+ for settName, sett in histAnSettData.iteritems():
+ if settName == 'iso_lines':
+ sett = str(sett)
+ self.anSettings[settName].SetValue(sett)
+
+ self.resultDbMgrData['analysis'] = self.currAnModule
+ self._updateResultDbMgrPage()
+ self._updateDbMgrData()
+
+ self.OnAnalysisChanged(None)
+ self.list.SetUpdateMap(updateMap = True)
+ self.mapWin.UpdateMap(render=True, renderVector=True)
+
+ def _checkResultMapChanged(self, resultVectMap):
+ """!Check if map was modified outside"""
+ if resultVectMap.VectMapState() == 0:
+ dlg = wx.MessageDialog(parent = self,
+ message = _("Temporary map '%s' with result " +
+ "was changed outside vector network analysis tool.\n" +
+ "Showed result may not correspond " +
+ "original analysis result.") %\
+ resultVectMap.GetVectMapName(),
+ caption = _("Result changed outside"),
+ style = wx.ICON_INFORMATION| wx.CENTRE)
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ def NewTmpVectMapToHist(self, prefMapName):
+ """!Add new vector map, which will be saved into history step"""
+
+ mapName = prefMapName + str(self.histTmpVectMapNum)
+ self.histTmpVectMapNum += 1
+
+ tmpMap = self._addTmpMapAnalysisMsg(mapName)
+ if not tmpMap:
+ return tmpMap
+
+ self.tmpVectMapsToHist.append(tmpMap.GetVectMapName())
+ self.history.Add(key = "tmp_data",
+ subkey = "maps",
+ value = self.tmpVectMapsToHist)
+
+ return tmpMap
+
+ def _addTmpMapAnalysisMsg(self, mapName):
+ """!Wraped AddTmpVectMap"""
+ msg = _("Temporary map %s already exists.\n" +
+ "Do you want to continue in analysis and overwrite it?") \
+ % (mapName +'@' + grass.gisenv()['MAPSET'])
+ tmpMap = self.tmpMaps.AddTmpVectMap(mapName, msg)
+ return tmpMap
+
+
+ def _initVnetParams(self):
+ """!Initializes parameters for different v.net.* modules """
+
+ self.attrCols = {
+ 'afcolumn' : {
+ "label" : _("Arc forward/both direction(s) cost column:"),
+ "name" : _("arc forward/both")
+ },
+ 'abcolumn' : {
+ "label" : _("Arc backward direction cost column:"),
+ "name" : _("arc backward")
+ },
+ 'acolumn' : {
+ "label" : _("Arcs' cost column (for both directions):"),
+ "name" : _("arc"),
+ "inputField" : 'afcolumn',
+ },
+ 'ncolumn' : {
+ "label" : _("Node cost column:"),
+ "name" : _("node")
+ }
+ }
+
+ self.vnetParams = {
+ "v.net.path" : {
+ "label" : _("Shortest path %s") % "(v.net.path)",
+ "cmdParams" : {
+ "cats" : [
+ ["st_pt", _("Start point")],
+ ["end_pt", _("End point")]
+ ],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn',
+ 'ncolumn'
+ ],
+ },
+ "resultProps" : {
+ "singleColor" : None,
+ "dbMgr" : True
+ }
+ },
+
+ "v.net.salesman" : {
+ "label" : _("Traveling salesman %s") % "(v.net.salesman)",
+ "cmdParams" : {
+ "cats" : [["ccats", None]],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn'
+ ],
+ },
+ "resultProps" : {
+ "singleColor" : None,
+ "dbMgr" : False
+ }
+ },
+ "v.net.flow" : {
+ "label" : _("Maximum flow %s") % "(v.net.flow)",
+ "cmdParams" : {
+ "cats" : [
+ ["source_cats", _("Source point")],
+ ["sink_cats", _("Sink point")]
+ ],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn',
+ 'ncolumn'
+ ]
+ },
+ "resultProps" : {
+ "attrColColor": "flow",
+ "dbMgr" : True
+ }
+ },
+ "v.net.alloc" : {
+ "label" : _("Subnets for nearest centers %s") % "(v.net.alloc)",
+ "cmdParams" : {
+ "cats" : [["ccats", None]],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn',
+ 'ncolumn'
+ ]
+ },
+ "resultProps" : {
+ "catColor" : None,
+ "dbMgr" : False
+ }
+ },
+ "v.net.steiner" : {
+ "label" : _("Steiner tree for the network and given terminals %s") % "(v.net.steiner)",
+ "cmdParams" : {
+ "cats" : [["tcats", None]],
+ "cols" : [
+ 'acolumn',
+ ]
+ },
+ "resultProps" : {
+ "singleColor" : None,
+ "dbMgr" : False
+ }
+ },
+ "v.net.distance" : {
+ "label" : _("Shortest distance via the network %s") % "(v.net.distance)",
+ "cmdParams" : {
+ "cats" : [
+ ["from_cats", "From point"],
+ ["to_cats", "To point"]
+ ],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn',
+ 'ncolumn'
+ ],
+ },
+ "resultProps" : {
+ "catColor" : None,
+ "dbMgr" : True
+ }
+ },
+ "v.net.iso" : {
+ "label" : _("Cost isolines %s") % "(v.net.iso)",
+ "cmdParams" : {
+ "cats" : [["ccats", None]],
+ "cols" : [
+ 'afcolumn',
+ 'abcolumn',
+ 'ncolumn'
+ ]
+ },
+ "resultProps" : {
+ "catColor" : None,
+ "dbMgr" : False
+ }
+ }
+ }
+ # order in combobox for choose of analysis
+ self.vnetModulesOrder = ["v.net.path",
+ "v.net.salesman",
+ "v.net.flow",
+ "v.net.alloc",
+ "v.net.distance",
+ "v.net.iso",
+ #"v.net.steiner"
+ ]
+ self.currAnModule = self.vnetModulesOrder[0]
+
+ def _initSettings(self):
+ """!Initialization of settings (if not already defined)"""
+ # initializes default settings
+ initSettings = [
+ ['res_style', 'line_width', 5],
+ ['res_style', 'line_color', (192,0,0)],
+ ['res_style', 'color_table', 'byr'],
+ ['res_style', 'invert_colors', False],
+ ['point_symbol', 'point_size', 10],
+ ['point_symbol', 'point_width', 2],
+ ['point_colors', "unused", (131,139,139)],
+ ['point_colors', "used1cat", (192,0,0)],
+ ['point_colors', "used2cat", (0,0,255)],
+ ['point_colors', "selected", (9,249,17)],
+ ['other', "snap_tresh", 10],
+ ['other', "max_hist_steps", 5]
+ ]
+
+ for init in initSettings:
+ UserSettings.ReadSettingsFile()
+ UserSettings.Append(dict = UserSettings.userSettings,
+ group ='vnet',
+ key = init[0],
+ subkey =init[1],
+ value = init[2],
+ overwrite = False)
+
+
+ def SetPointDrawSettings(self):
+ """!Set settings for drawing of points"""
+ ptSize = int(UserSettings.Get(group='vnet', key='point_symbol', subkey = 'point_size'))
+ self.pointsToDraw.SetPropertyVal("size", ptSize)
+
+ colors = UserSettings.Get(group='vnet', key='point_colors')
+ ptWidth = int(UserSettings.Get(group='vnet', key='point_symbol', subkey = 'point_width'))
+
+ textProp = self.pointsToDraw.GetPropertyVal("text")
+ textProp["font"].SetPointSize(ptSize + 2)
+
+ for colKey, col in colors.iteritems():
+ pen = self.pointsToDraw.GetPen(colKey)
+ if pen:
+ pen.SetColour(wx.Colour(col[0], col[1], col[2], 255))
+ pen.SetWidth(ptWidth)
+ else:
+ self.pointsToDraw.AddPen(colKey, wx.Pen(colour = wx.Colour(col[0], col[1], col[2], 255), width = ptWidth))
+
+class PtsList(PointsList):
+ def __init__(self, parent, dialog, cols, id=wx.ID_ANY):
+ """! List with points for analysis"""
+ self.updateMap = True
+ self.dialog = dialog # VNETDialog class
+
+ PointsList.__init__(self, parent = parent, cols = cols, id = id)
+
+ def AddItem(self, event = None, updateMap = True):
+ """!Append point to list"""
+ self.dialog.pointsToDraw.AddItem(coords = [0,0])
+
+ PointsList.AddItem(self, event)
+
+ self.EditCellKey(key = self.selected ,
+ colName = 'topology',
+ cellData = _("new point"))
+
+ def DeleteItem(self, event = None):
+ """!Delete selected point in list"""
+ key = self.GetItemData(self.selected)
+ if self.selected != wx.NOT_FOUND:
+ item = self.dialog.pointsToDraw.GetItem(key)
+ self.dialog.pointsToDraw.DeleteItem(item)
+
+ PointsList.DeleteItem(self, event)
+
+ def OnItemSelected(self, event):
+ """!Item selected"""
+ PointsList.OnItemSelected(self, event)
+
+ if self.updateMap:
+ self.dialog.mapWin.UpdateMap(render=False, renderVector=False)
+
+ def AdaptPointsList(self, currParamsCats):
+ """Rename category values when module is changed. Expample: Start point -> Sink point"""
+ colValues = [""]
+
+ for ptCat in currParamsCats:
+ colValues.append(ptCat[1])
+ colNum = self._getColumnNum('type')
+ self.ChangeColEditable('type', colValues)
+
+ for iItem, item in enumerate(self.itemDataMap):
+ self.EditCellKey(iItem, 'type', self.selIdxs[iItem][colNum])
+
+ if not item[1]:
+ self.CheckItem(iItem, False)
+
+ def OnCheckItem(self, index, flag):
+ """!Item is checked/unchecked"""
+ key = self.GetItemData(index)
+ checkedVal = self.itemDataMap[key][1]
+
+ currModule = self.dialog.currAnModule #TODO public func
+ cats = self.dialog.vnetParams[currModule]["cmdParams"]["cats"]
+
+ if self.updateMap:
+ self.dialog.mapWin.UpdateMap(render=False, renderVector=False)
+
+ if len(cats) <= 1:
+ return
+
+ if checkedVal == "":
+ self.CheckItem(key, False)
+ return
+
+ if currModule == "v.net.path" and flag:
+ self.UpdateCheckedItems(index)
+
+ def UpdateCheckedItems(self, index):
+ """!For v.net.path - max. just one checked 'Start point' and 'End point'"""
+ alreadyChecked = []
+ colNum = self._getColumnNum('type')
+ if colNum == -1:
+ return
+ if index:
+ checkedKey = self.GetItemData(index)
+ checkedVal = self.selIdxs[checkedKey][colNum]
+ alreadyChecked.append(checkedVal)
+ else:
+ checkedKey = -1
+
+ for iKey, idx in enumerate(self.selIdxs):
+ index = self._findIndex(iKey)
+ if (idx[colNum] in alreadyChecked and checkedKey != iKey) \
+ or idx[colNum] == 0:
+ self.CheckItem(index, False)
+ elif self.IsChecked(index):
+ alreadyChecked.append(idx[colNum])
+
+ def SetUpdateMap(self, updateMap):
+ """!Update/Not update map window when some change in list is made"""
+ self.updateMap = updateMap
+
+ def GetHiddenColSelIdxs(self, colName):
+ """!Get indexes of chosen values in hidden 'type' column"""
+ if self.hiddenCols.has_key(colName):
+ return self.hiddenCols[colName]['selIdxs']
+ return None
+
+ def SetHiddenSelIdxs(self, colName, selIdxs):
+ """!Set indexes of chosen values in hidden 'type' column and update text in it's cells"""
+ if self.hiddenCols.has_key(colName):
+ self.hiddenCols[colName]['selIdxs'] = map(int, selIdxs)
+ self.hiddenCols[colName]['itemDataMap'] = []
+ # update text in hidden column cells
+ for idx in self.hiddenCols[colName]['selIdxs']:
+ self.hiddenCols[colName]['itemDataMap'].append(self.hiddenCols[colName]['colsData'][2][idx])
+ return True
+ return False
+
+class SettingsDialog(wx.Dialog):
+ def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.DEFAULT_DIALOG_STYLE):
+ """!Settings dialog"""
+ wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+
+ maxValue = 1e8
+ self.parent = parent
+ self.settings = {}
+
+ rules = RunCommand('v.colors',
+ read = True,
+ flags = 'l')
+
+ settsLabels = {}
+
+ settsLabels['color_table'] = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = _('Color table style %s:') % '(v.net.flow)')
+ self.settings['color_table'] = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = rules.split(),
+ style = wx.CB_READONLY, size = (180, -1))
+
+ setStyle = UserSettings.Get(group ='vnet', key = "res_style", subkey = "color_table")
+ i = self.settings['color_table'].FindString(setStyle)
+ if i != wx.NOT_FOUND:
+ self.settings['color_table'].Select(i)
+
+ self.settings["invert_colors"] = wx.CheckBox(parent = self, id=wx.ID_ANY,
+ label = _('Invert colors %s:') % '(v.net.flow)')
+ setInvert = UserSettings.Get(group ='vnet', key = "res_style", subkey = "invert_colors")
+ self.settings["invert_colors"].SetValue(setInvert)
+
+ self.colorsSetts = {
+ "line_color" : ["res_style", _("Line color:")],
+ "unused" : ["point_colors", _("Color for unused point:")],
+ "used1cat" : ["point_colors", _("Color for Start/From/Source/Used point:")],
+ "used2cat" : ["point_colors", _("Color for End/To/Sink point:")],
+ "selected" : ["point_colors", _("Color for selected point:")]
+ }
+
+ for settKey, sett in self.colorsSetts.iteritems():
+ settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+ col = UserSettings.Get(group ='vnet', key = sett[0], subkey = settKey)
+ self.settings[settKey] = csel.ColourSelect(parent = self, id = wx.ID_ANY,
+ colour = wx.Colour(col[0],
+ col[1],
+ col[2],
+ 255))
+
+ self.sizeSetts = {
+ "line_width" : ["res_style", _("Line width:")],
+ "point_size" : ["point_symbol", _("Point size:")],
+ "point_width" : ["point_symbol", _("Point width:")],
+ "snap_tresh" : ["other", _("Snapping treshold in pixels:")],
+ "max_hist_steps" : ["other", _("Maximum number of results in history:")]
+ }
+
+ for settKey, sett in self.sizeSetts.iteritems():
+ settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+ self.settings[settKey] = wx.SpinCtrl(parent = self, id = wx.ID_ANY, min = 1, max = 50)
+ size = int(UserSettings.Get(group = 'vnet', key = sett[0], subkey = settKey))
+ self.settings[settKey].SetValue(size)
+
+
+ # buttons
+ self.btnSave = wx.Button(self, wx.ID_SAVE)
+ self.btnApply = wx.Button(self, wx.ID_APPLY)
+ self.btnClose = wx.Button(self, wx.ID_CLOSE)
+ self.btnApply.SetDefault()
+
+ # bindings
+ self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+ self.btnApply.SetToolTipString(_("Apply changes for the current session"))
+ self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+ self.btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+ self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+ self.btnClose.SetToolTipString(_("Close dialog"))
+
+ #Layout
+
+ # Analysis result style layout
+ self.SetMinSize(self.GetBestSize())
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ styleBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label =" %s " % _("Analysis result style:"))
+ styleBoxSizer = wx.StaticBoxSizer(styleBox, wx.VERTICAL)
+
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
+ gridSizer.AddGrowableCol(1)
+
+ row = 0
+ gridSizer.Add(item = settsLabels["line_color"], flag = wx.ALIGN_CENTER_VERTICAL, pos =(row, 0))
+ gridSizer.Add(item = self.settings["line_color"],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos =(row, 1))
+
+ row += 1
+ gridSizer.Add(item = settsLabels["line_width"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ gridSizer.Add(item = self.settings["line_width"],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos = (row, 1))
+ row += 1
+ gridSizer.Add(item = settsLabels['color_table'], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ gridSizer.Add(item = self.settings['color_table'],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos = (row, 1))
+
+ row += 1
+ gridSizer.Add(item = self.settings["invert_colors"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+
+ styleBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+
+ # Point style layout
+ ptsStyleBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label =" %s " % _("Point style:"))
+ ptsStyleBoxSizer = wx.StaticBoxSizer(ptsStyleBox, wx.VERTICAL)
+
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
+ gridSizer.AddGrowableCol(1)
+
+ row = 0
+ setts = dict(self.colorsSetts.items() + self.sizeSetts.items())
+
+ settsOrder = ["selected", "used1cat", "used2cat", "unused", "point_size", "point_width"]
+ for settKey in settsOrder:
+ sett = setts[settKey]
+ gridSizer.Add(item = settsLabels[settKey], flag = wx.ALIGN_CENTER_VERTICAL, pos =(row, 0))
+ gridSizer.Add(item = self.settings[settKey],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos =(row, 1))
+ row += 1
+
+ ptsStyleBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+
+ # Other settings layout
+ otherBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label =" %s " % _("Other settings"))
+ otherBoxSizer = wx.StaticBoxSizer(otherBox, wx.VERTICAL)
+
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
+ gridSizer.AddGrowableCol(1)
+
+ row = 0
+ for otherSettName in ["snap_tresh", "max_hist_steps"]:
+ gridSizer.Add(item = settsLabels[otherSettName], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ gridSizer.Add(item = self.settings[otherSettName],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos = (row, 1))
+ row += 1
+
+ otherBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(self.btnApply, flag = wx.LEFT | wx.RIGHT, border = 5)
+ btnSizer.Add(self.btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
+ btnSizer.Add(self.btnClose, flag = wx.LEFT | wx.RIGHT, border = 5)
+
+ sizer.Add(item = styleBoxSizer, flag = wx.EXPAND | wx.ALL, border = 5)
+ sizer.Add(item = ptsStyleBoxSizer, flag = wx.EXPAND | wx.ALL, border = 5)
+ sizer.Add(item = otherBoxSizer, flag = wx.EXPAND | wx.ALL, border = 5)
+ sizer.Add(item = btnSizer, flag = wx.EXPAND | wx.ALL, border = 5, proportion = 0)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def OnSave(self, event):
+ """!Button 'Save' pressed"""
+ self.UpdateSettings()
+
+ fileSettings = {}
+ UserSettings.ReadSettingsFile(settings=fileSettings)
+ fileSettings['vnet'] = UserSettings.Get(group='vnet')
+ UserSettings.SaveToFile(fileSettings)
+
+ self.Close()
+
+ def UpdateSettings(self):
+ UserSettings.Set(group ='vnet', key = "res_style", subkey ='line_width',
+ value = self.settings["line_width"].GetValue())
+
+ for settKey, sett in self.colorsSetts.iteritems():
+ col = tuple(self.settings[settKey].GetColour())
+ UserSettings.Set(group = 'vnet',
+ key = sett[0],
+ subkey = settKey,
+ value = col)
+
+ for settKey, sett in self.sizeSetts.iteritems():
+ UserSettings.Set(group = 'vnet', key = sett[0], subkey = settKey,
+ value = self.settings[settKey].GetValue())
+
+ UserSettings.Set(group = 'vnet', key = 'res_style', subkey = 'color_table',
+ value = self.settings['color_table'].GetStringSelection())
+
+ UserSettings.Set(group = 'vnet', key = 'res_style', subkey = 'invert_colors',
+ value = self.settings['invert_colors'].IsChecked())
+
+ self.parent.SetPointDrawSettings()
+ if not self.parent.tmp_result or \
+ not self.parent.tmpMaps.HasTmpVectMap(self.parent.tmp_result.GetVectMapName()):
+ self.parent.mapWin.UpdateMap(render=False, renderVector=False)
+ elif self.parent.tmp_result.GetRenderLayer():
+ cmd = self.parent.GetLayerStyle()
+ self.parent.tmp_result.AddRenderLayer(cmd)
+ self.parent.mapWin.UpdateMap(render=True, renderVector=True)#TODO optimization
+ else:
+ self.parent.mapWin.UpdateMap(render=False, renderVector=False)
+
+ def OnApply(self, event):
+ """!Button 'Apply' pressed"""
+ self.UpdateSettings()
+ #self.Close()
+
+ def OnClose(self, event):
+ """!Button 'Cancel' pressed"""
+ self.Close()
+
+class AddLayerDialog(wx.Dialog):
+ def __init__(self, parent,id=wx.ID_ANY,
+ title =_("Save analysis result"), style=wx.DEFAULT_DIALOG_STYLE):
+ """!Save analysis result"""
+ wx.Dialog.__init__(self, parent, id, title = _(title), style = style)
+
+ self.panel = wx.Panel(parent = self)
+
+ # text fields and it's captions
+ self.vectSel = Select(parent = self.panel, type = 'vector',
+ mapsets = [grass.gisenv()['MAPSET']],size = (-1, -1))
+ self.vectSellabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Name:"))
+
+ # buttons
+ self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+ self.btnOk = wx.Button(self.panel, wx.ID_OK)
+ self.btnOk.SetDefault()
+
+ self.SetInitialSize((400, -1))
+ self._layout()
+
+ def _layout(self):
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.StaticBox (parent = self.panel, id = wx.ID_ANY,
+ label = "Vector map")
+
+ boxSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+
+ boxSizer.Add(item = self.vectSellabel,
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ proportion = 0)
+
+ boxSizer.Add(item = self.vectSel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ sizer.Add(item = boxSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOk)
+ btnSizer.Realize()
+
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+ self.panel.SetSizer(sizer)
+ sizer.Fit(self)
+
+class VnetTmpVectMaps:
+ """!Class which creates, stores and destroys all tmp maps created during analysis"""
+ def __init__(self, parent):
+ self.tmpMaps = [] # temporary maps
+ self.parent = parent
+ self.mapWin = self.parent.mapWin
+
+ def AddTmpVectMap(self, mapName, msg):
+ """!New temporary map
+
+ @return instance of VectMap representing temporary map
+ """
+ currMapSet = grass.gisenv()['MAPSET']
+ tmpMap = grass.find_file(name = mapName,
+ element = 'vector',
+ mapset = currMapSet)
+
+ fullName = tmpMap["fullname"]
+ # map already exists
+ if fullName:
+ #TODO move dialog out of class, AddTmpVectMap(self, mapName, overvrite = False)
+ dlg = wx.MessageDialog(parent = self.parent,
+ message = msg,
+ caption = _("Overwrite map layer"),
+ style = wx.YES_NO | wx.NO_DEFAULT |
+ wx.ICON_QUESTION | wx.CENTRE)
+
+ ret = dlg.ShowModal()
+ dlg.Destroy()
+
+ if ret == wx.ID_NO:
+ return None
+ else:
+ fullName = mapName + "@" + currMapSet
+
+ newVectMap = VectMap(self, fullName)
+ self.tmpMaps.append(newVectMap)
+
+ return newVectMap
+
+ def HasTmpVectMap(self, vectMapName):
+ """
+ @param vectMapName name of vector map
+
+ @return True if it contains the map
+ @return False if not
+ """
+
+ mapValSpl = vectMapName.strip().split("@")
+ if len(mapValSpl) > 1:
+ mapSet = mapValSpl[1]
+ else:
+ mapSet = grass.gisenv()['MAPSET']
+ mapName = mapValSpl[0]
+ fullName = mapName + "@" + mapSet
+
+ for vectTmpMap in self.tmpMaps:
+ if vectTmpMap.GetVectMapName() == fullName:
+ return True
+ return False
+
+ def GetTmpVectMap(self, vectMapName):
+ """ Get instance of VectMap with name vectMapName"""
+ for vectMap in self.tmpMaps:
+ if vectMap.GetVectMapName() == vectMapName.strip():
+ return vectMap
+ return None
+
+ def RemoveFromTmpMaps(self, vectMap):
+ """!Temporary map is removed from the class instance however it is not deleted
+
+ @param vectMap instance of VectMap class to be removed
+
+ @return True if was removed
+ @return False if does not contain the map
+ """
+ try:
+ self.tmpMaps.remove(vectMap)
+ return True
+ except ValueError:
+ return False
+
+ def DeleteTmpMap(self, vectMap):
+ """!Temporary map is removed from the class and it is deleted
+
+ @param vectMap instance of VectMap class to be deleted
+
+ @return True if was removed
+ @return False if does not contain the map
+ """
+ if vectMap:
+ vectMap.DeleteRenderLayer()
+ RunCommand('g.remove',
+ vect = vectMap.GetVectMapName())
+ self.RemoveFromTmpMaps(vectMap)
+ return True
+ return False
+
+ def DeleteAllTmpMaps(self):
+ """Delete all temporary maps in the class"""
+ update = False
+ for tmpMap in self.tmpMaps:
+ RunCommand('g.remove',
+ vect = tmpMap.GetVectMapName())
+ if tmpMap.DeleteRenderLayer():
+ update = True
+ return update
+
+class VectMap:
+ """!Represents map
+ It can check if it was modified or render it
+ """
+ def __init__(self, parent, fullName):
+ self.fullName = fullName
+ self.parent = parent
+ self.renderLayer = None
+ self.modifTime = None # time, for modification check
+
+ def __del__(self):
+
+ self.DeleteRenderLayer()
+
+ def AddRenderLayer(self, cmd = None):
+ """!Add map from map window layers to render """
+ existsMap = grass.find_file(name = self.fullName,
+ element = 'vector',
+ mapset = grass.gisenv()['MAPSET'])
+
+ if not existsMap["name"]:
+ self.DeleteRenderLayer()
+ return False
+
+ if not cmd:
+ cmd = []
+ cmd.insert(0, 'd.vect')
+ cmd.append('map=%s' % self.fullName)
+
+ if self.renderLayer:
+ self.DeleteRenderLayer()
+
+ self.renderLayer = self.parent.mapWin.Map.AddLayer(type = "vector", command = cmd,
+ l_active=True, name = self.fullName,
+ l_hidden = True, l_opacity = 1.0,
+ l_render = False, pos = -1)
+ return True
+
+ def DeleteRenderLayer(self):
+ """!Remove map from map window layers to render"""
+ if self.renderLayer:
+ self.parent.mapWin.Map.DeleteLayer(self.renderLayer)
+ self.renderLayer = None
+ return True
+ return False
+
+ def GetRenderLayer(self):
+ return self.renderLayer
+
+ def GetVectMapName(self):
+ return self.fullName
+
+ def SaveVectMapState(self):
+ """!Save modification time for vector map"""
+ self.modifTime = self.GetLastModified()
+
+ def VectMapState(self):
+ """!Checks if map was modified
+
+ @return -1 - if no modification time was saved
+ @return 0 - if map was modified
+ @return 1 - if map was not modified
+ """
+ if self.modifTime is None:
+ return -1
+ if self.modifTime != self.GetLastModified():
+ return 0
+ return 1
+
+ def GetLastModified(self):
+ """!Get modification time
+
+ @return MAP DATE time string from vector map head file
+ """
+
+ mapValSpl = self.fullName.split("@")
+ mapSet = mapValSpl[1]
+ mapName = mapValSpl[0]
+
+ headPath = os.path.join(grass.gisenv()['GISDBASE'],
+ grass.gisenv()['LOCATION_NAME'],
+ mapSet,
+ "vector",
+ mapName,
+ "head")
+ try:
+ head = open(headPath, 'r')
+ for line in head.readlines():
+ i = line.find('MAP DATE:', )
+ if i == 0:
+ head.close()
+ return line.split(':', 1)[1].strip()
+
+ head.close()
+ return ""
+ except IOError:
+ return ""
+
+class History:
+ """!Class which reads and saves history data (based on gui.core.settings Settings class file save/load)"""
+ def __init__(self, parent):
+
+ # max number of steps in history (zero based)
+ self.maxHistSteps = 3
+ # current history step
+ self.currHistStep = 0
+ # number of steps saved in history
+ self.histStepsNum = 0
+
+ # dict contains data saved in history for current history step
+ self.currHistStepData = {}
+
+ # buffer for data to be saved into history
+ self.newHistStepData = {}
+
+ self.histFile = grass.tempfile()
+
+ # key/value separator
+ self.sep = ';'
+
+ def __del__(self):
+ grass.try_remove(self.histFile)
+
+ def GetNext(self):
+ """!Go one step forward in history"""
+ self.currHistStep -= 1
+ self.currHistStepData.clear()
+ self.currHistStepData = self._getHistStepData(self.currHistStep)
+
+ return self.currHistStepData
+
+ def GetPrev(self):
+ """!Go one step back in history"""
+ self.currHistStep += 1
+ self.currHistStepData.clear()
+ self.currHistStepData = self._getHistStepData(self.currHistStep)
+
+ return self.currHistStepData
+
+ def GetStepsNum(self):
+ """!Get number of steps saved in history"""
+ return self.histStepsNum
+
+ def GetCurrHistStep(self):
+ """!Get current history step"""
+ return self.currHistStep
+
+ def Add(self, key, subkey, value):
+ """!Add new data into buffer"""
+ if key not in self.newHistStepData:
+ self.newHistStepData[key] = {}
+
+ if type(subkey) == types.ListType:
+ if subkey[0] not in self.newHistStepData[key]:
+ self.newHistStepData[key][subkey[0]] = {}
+ self.newHistStepData[key][subkey[0]][subkey[1]] = value
+ else:
+ self.newHistStepData[key][subkey] = value
+
+ def SaveHistStep(self):
+ """!Create new history step with data in buffer"""
+ self.maxHistSteps = UserSettings.Get(group ='vnet',
+ key = 'other',
+ subkey = 'max_hist_steps')
+ self.currHistStep = 0
+
+ newHistFile = grass.tempfile()
+ newHist = open(newHistFile, "w")
+
+ self._saveNewHistStep(newHist)
+
+ oldHist = open(self.histFile)
+ removedHistData = self._savePreviousHist(newHist, oldHist)
+
+ oldHist.close()
+ newHist.close()
+ grass.try_remove(self.histFile)
+ self.histFile = newHistFile
+
+ self.newHistStepData.clear()
+
+ return removedHistData
+
+ def _savePreviousHist(self, newHist, oldHist):
+ """!Save previous history into new file"""
+ newHistStep = False
+ removedHistData = {}
+ newHistStepsNum = self.histStepsNum
+
+ for line in oldHist.readlines():
+ if not line.strip():
+ newHistStep = True
+ newHistStepsNum += 1
+ continue
+
+ if newHistStep:
+ newHistStep = False
+
+ line = line.split("=")
+ line[1] = str(newHistStepsNum)
+ line = "=".join(line)
+
+ if newHistStepsNum >= self.maxHistSteps:
+ removedHistStep = removedHistData[line] = {}
+ continue
+ else:
+ newHist.write('%s%s%s' % (os.linesep, line, os.linesep))
+ self.histStepsNum = newHistStepsNum
+ else:
+ if newHistStepsNum >= self.maxHistSteps:
+ self._parseLine(line, removedHistStep)
+ else:
+ newHist.write('%s' % line)
+
+ return removedHistData
+
+ def _saveNewHistStep(self, newHist):
+ """!Save buffer (new step) data into file"""
+ newHist.write('%s%s%s' % (os.linesep, "history step=0", os.linesep))
+ for key in self.newHistStepData.keys():
+ subkeys = self.newHistStepData[key].keys()
+ newHist.write('%s%s' % (key, self.sep))
+ for idx in range(len(subkeys)):
+ value = self.newHistStepData[key][subkeys[idx]]
+ if type(value) == types.DictType:
+ if idx > 0:
+ newHist.write('%s%s%s' % (os.linesep, key, self.sep))
+ newHist.write('%s%s' % (subkeys[idx], self.sep))
+ kvalues = self.newHistStepData[key][subkeys[idx]].keys()
+ srange = range(len(kvalues))
+ for sidx in srange:
+ svalue = self._parseValue(self.newHistStepData[key][subkeys[idx]][kvalues[sidx]])
+ newHist.write('%s%s%s' % (kvalues[sidx], self.sep, svalue))
+ if sidx < len(kvalues) - 1:
+ newHist.write('%s' % self.sep)
+ else:
+ if idx > 0 and \
+ type( self.newHistStepData[key][subkeys[idx - 1]]) == types.DictType:
+ newHist.write('%s%s%s' % (os.linesep, key, self.sep))
+ value = self._parseValue(self.newHistStepData[key][subkeys[idx]])
+ newHist.write('%s%s%s' % (subkeys[idx], self.sep, value))
+ if idx < len(subkeys) - 1 and \
+ type(self.newHistStepData[key][subkeys[idx + 1]]) != types.DictType:
+ newHist.write('%s' % self.sep)
+ newHist.write(os.linesep)
+ self.histStepsNum = 0
+
+ def _parseValue(self, value, read = False):
+ """!Parse value"""
+ if read: # -> read data (cast values)
+
+ if value:
+ if value[0] == '[' and value[-1] == ']':# TODO, possible wrong interpretation
+ value = value[1:-1].split(',')
+ value = map(self._castValue, value)
+ return value
+
+ if value == 'True':
+ value = True
+ elif value == 'False':
+ value = False
+ elif value == 'None':
+ value = None
+ elif ':' in value: # -> color
+ try:
+ value = tuple(map(int, value.split(':')))
+ except ValueError: # -> string
+ pass
+ else:
+ try:
+ value = int(value)
+ except ValueError:
+ try:
+ value = float(value)
+ except ValueError:
+ pass
+ else: # -> write data
+ if type(value) == type(()): # -> color
+ value = str(value[0]) + ':' +\
+ str(value[1]) + ':' + \
+ str(value[2])
+
+ return value
+
+ def _castValue(self, value):
+ """!Cast value"""
+ try:
+ value = int(value)
+ except ValueError:
+ try:
+ value = float(value)
+ except ValueError:
+ value = value[1:-1]
+
+ return value
+
+ def _getHistStepData(self, histStep):
+ """!Load data saved in history step"""
+ hist = open(self.histFile)
+ histStepData = {}
+
+ newHistStep = False
+ isSearchedHistStep = False
+ for line in hist.readlines():
+
+ if not line.strip() and isSearchedHistStep:
+ break
+ elif not line.strip():
+ newHistStep = True
+ continue
+ elif isSearchedHistStep:
+ self._parseLine(line, histStepData)
+
+ if newHistStep:
+ line = line.split("=")
+ if int(line[1]) == histStep:
+ isSearchedHistStep = True
+ newHistStep = False
+
+ hist.close()
+ return histStepData
+
+ def _parseLine(self, line, histStepData):
+ """!Parse line in file with history"""
+ line = line.rstrip('%s' % os.linesep).split(self.sep)
+ key = line[0]
+ kv = line[1:]
+ idx = 0
+ subkeyMaster = None
+ if len(kv) % 2 != 0: # multiple (e.g. nviz)
+ subkeyMaster = kv[0]
+ del kv[0]
+ idx = 0
+ while idx < len(kv):
+ if subkeyMaster:
+ subkey = [subkeyMaster, kv[idx]]
+ else:
+ subkey = kv[idx]
+ value = kv[idx+1]
+ value = self._parseValue(value, read = True)
+ if key not in histStepData:
+ histStepData[key] = {}
+
+ if type(subkey) == types.ListType:
+ if subkey[0] not in histStepData[key]:
+ histStepData[key][subkey[0]] = {}
+ histStepData[key][subkey[0]][subkey[1]] = value
+ else:
+ histStepData[key][subkey] = value
+ idx += 2
+
+ def DeleteNewHistStepData(self):
+ """!Delete buffer data for new history step"""
+ self.newHistStepData.clear()
+
+class VnetStatusbar(wx.StatusBar):
+ """!Extends wx.StatusBar class with functionality to show multiple messages with the highest priority"""
+ def __init__(self, parent, style, id = wx.ID_ANY, **kwargs):
+
+ wx.StatusBar.__init__(self, parent, id, style, **kwargs)
+
+ self.maxPriority = 0
+ self.statusItems = []
+
+ def AddStatusItem(self, text, key, priority):
+ """!Add new item to show
+
+ @param text - string to show
+ @param key - item identifier, if already contains
+ item with same identifier, overwrites this item
+ @param priority - only items with highest priority are showed
+ """
+ statusTextItem = {
+ 'text' : text,
+ 'key' : key,
+ 'priority' : priority
+ }
+
+ for item in self.statusItems:
+ if item['key'] == statusTextItem['key']:
+ self.statusItems.remove(item)
+ self.statusItems.append(statusTextItem)
+ if self.maxPriority < statusTextItem['priority']:
+ self.maxPriority = statusTextItem['priority']
+ self._updateStatus()
+
+ def _updateStatus(self):
+
+ currStatusText = ''
+ for item in reversed(self.statusItems):
+ if item['priority'] == self.maxPriority:
+ if currStatusText:
+ currStatusText += '; '
+ currStatusText += item['text']
+ wx.StatusBar.SetStatusText(self, currStatusText)
+
+ def RemoveStatusItem(self, key):
+ """!Remove item
+
+ @param key - item identifier
+ """
+ update = False
+ for iItem, item in enumerate(self.statusItems):
+ if item['key'] == key:
+ if item['priority'] == self.maxPriority:
+ update = True
+ self.statusItems.pop(iItem)
+ if update:
+ for item in self.statusItems:
+ self.maxPriority = 0
+ if self.maxPriority < item['priority']:
+ self.maxPriority = item['priority']
+ self._updateStatus()
Property changes on: grass/trunk/gui/wxpython/vnet/dialogs.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/vnet/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/vnet/toolbars.py (rev 0)
+++ grass/trunk/gui/wxpython/vnet/toolbars.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -0,0 +1,176 @@
+"""!
+ at package vnet.toolbars
+
+ at brief Vector network analysis dialog - toolbars
+
+Classes:
+ - toolbars::PointListToolbar
+ - toolbars::MainToolbar
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
+"""
+
+import wx
+
+from icon import MetaIcon
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from core.gcmd import RunCommand
+class PointListToolbar(BaseToolbar):
+ """!Toolbar for managing list of points
+
+ @param parent reference to VNETDialog
+ """
+ def __init__(self, parent, list):
+ BaseToolbar.__init__(self, parent)
+ self.list = list
+ self.InitToolbar(self._toolbarData())
+
+ # realize the toolbar
+ self.Realize()
+
+
+ def _toolbarData(self):
+
+ icons = {
+ 'insertPoint' : MetaIcon(img = 'pointer',
+ label = _('Insert points from Map Display')),
+ 'snapping' : MetaIcon(img = 'move',
+ label = _('Activate snapping to nodes')),
+ 'pointAdd' : MetaIcon(img = 'point-create',
+ label = _('Add new point')),
+ 'pointDelete' : MetaIcon(img = 'gcp-delete',
+ label = _('Delete selected point'))
+ }
+
+ return self._getToolbarData((('insertPoint', icons['insertPoint'],
+ self.list.dialog.OnInsertPoint,#TODO self.list.dialog
+ wx.ITEM_CHECK),
+ ('snapping', icons['snapping'],
+ self.list.dialog.OnSnapping,
+ wx.ITEM_CHECK),
+ (None, ),
+ ('pointAdd', icons["pointAdd"],
+ self.list.AddItem),
+ ('pointDelete', icons["pointDelete"],
+ self.list.DeleteItem)))
+
+ def GetToolId(self, toolName): #TODO can be useful in base
+
+ return vars(self)[toolName]
+
+class MainToolbar(BaseToolbar):
+ """!Main toolbar
+ """
+ def __init__(self, parent):
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+
+ choices = []
+
+ for moduleName in self.parent.vnetModulesOrder:
+ choices.append(self.parent.vnetParams[moduleName]['label'])
+
+ self.UpdateUndoRedo()
+ # realize the toolbar
+ self.Realize()
+
+ def _toolbarData(self):
+
+ icons = {
+ 'run' : MetaIcon(img = 'execute',
+ label = _('Execute analysis')),
+ 'undo' : MetaIcon(img = 'undo',
+ label = _('Go to previous analysis result')),
+ 'redo' : MetaIcon(img = 'redo',
+ label = _('Go to next analysis result')),
+ 'showResult' : MetaIcon(img = 'layer-add',
+ label = _("Show analysis result")),
+ 'saveTempLayer' : MetaIcon(img = 'map-export',
+ label = _('Save temporary result')),
+ 'settings' : BaseIcons['settings'].SetLabel( _('Vector network analysis settings')),
+ 'help' : MetaIcon(img = 'help',
+ label = _('Show manual'))
+ }
+
+ return self._getToolbarData((
+ ("run", icons['run'],
+ self.parent.OnAnalyze),
+ (None, ),
+ ("undo", icons['undo'],
+ self.parent.OnUndo),
+ ("redo", icons['redo'],
+ self.parent.OnRedo),
+ (None, ),
+ ("showResult", icons['showResult'],
+ self.parent.OnShowResult, wx.ITEM_CHECK),
+ ("saveTempLayer", icons['saveTempLayer'],
+ self.parent.OnSaveTmpLayer),
+ (None, ),
+ ('settings', icons["settings"],
+ self.parent.OnSettings),
+ ('help', icons["help"],
+ self.OnHelp),
+ ("quit", BaseIcons['quit'],
+ self.parent.OnCloseDialog)
+ ))
+
+
+ def UpdateUndoRedo(self):
+
+ id = vars(self)['showResult']
+ self.ToggleTool(id =id,
+ toggle = True)
+
+ if self.parent.history.GetCurrHistStep() >= self.parent.history.GetStepsNum():
+ self.Enable("undo", False)
+ else:
+ self.Enable("undo", True)
+
+ if self.parent.history.GetCurrHistStep() <= 0:
+ self.Enable("redo", False)
+ else:
+ self.Enable("redo", True)
+
+ def OnHelp(self, event) :
+ RunCommand('g.manual',
+ entry = 'wxGUI.VNet')
+
+class AnalysisToolbar(BaseToolbar):
+ """!Main toolbar
+ """
+ def __init__(self, parent):
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+
+ choices = []
+
+ for moduleName in self.parent.vnetModulesOrder:
+ choices.append(self.parent.vnetParams[moduleName]['label'])
+
+ self.anChoice = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = choices,
+ style = wx.CB_READONLY, size = (350, 30))#FIXME
+ self.anChoice.SetToolTipString(_('Availiable analyses'))
+ self.anChoice.SetSelection(0)
+
+ self.anChoiceId = self.AddControl(self.anChoice)
+ self.parent.Bind(wx.EVT_COMBOBOX, self.parent.OnAnalysisChanged, self.anChoiceId)
+
+ # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+ self.anChoice.Hide()
+ self.anChoice.Show()
+ # realize the toolbar
+ self.Realize()
+
+ def _toolbarData(self):
+
+ icons = {}
+
+ return self._getToolbarData(())
\ No newline at end of file
Property changes on: grass/trunk/gui/wxpython/vnet/toolbars.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/vnet/widgets.py
===================================================================
--- grass/trunk/gui/wxpython/vnet/widgets.py (rev 0)
+++ grass/trunk/gui/wxpython/vnet/widgets.py 2013-01-03 17:46:18 UTC (rev 54515)
@@ -0,0 +1,638 @@
+"""!
+ at package vnet.widgets
+
+ at brief Base class for list of points.
+
+Classes:
+ - widgets::PointsList
+ - widgets::EditItem
+
+(C) 2012 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 Original author Michael Barton
+ at author Original version improved by Martin Landa <landa.martin gmail.com>
+ at author Rewritten by Markus Metz redesign georectfier -> GCP Manage
+ at author Stepan Turek <stepan.turek seznam.cz> (Created PointsList from GCPList) (GSoC 2012, mentor: Martin Landa)
+"""
+
+import os
+import wx
+from copy import copy, deepcopy
+
+import wx
+from wx.lib.mixins.listctrl import CheckListCtrlMixin, ColumnSorterMixin, ListCtrlAutoWidthMixin, TextEditMixin
+
+from core import globalvar
+
+
+class PointsList(wx.ListCtrl,
+ CheckListCtrlMixin,
+ ListCtrlAutoWidthMixin,
+ ColumnSorterMixin):
+
+ def __init__(self, parent, cols, id=wx.ID_ANY,
+ pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
+ wx.LC_SINGLE_SEL):
+ """!Creates list for points.
+
+ PointsList class was created from GCPList class in GCP manager. It is possible
+ to be shared by GCP and VNET front end.
+
+ Important parameters:
+ @param cols is list containing list items. which represents columns.
+ This columns will be added in order as they are in list.
+ Class will add as first column "use" with number of point and checkbox.
+ Structure of list item must be this:
+ -1. item: column name
+ -2. item: column label
+ -3. item: If column is editable by user, it must contain convert function to convert
+ inserted string to it's type for sorting. Use None for not editable
+ columns. Values for insertion can be in list. This allows insert
+ just values in the list.
+ -4. item: Default value for column cell. Value should be given in it's type
+ in order to sorting would work properly. If 3. item is list, it must be index
+ of some item in the list.
+
+ Example of cols parameter:
+ column name, column label, convert function, default val
+ @code
+ cols = [
+ ['E', _('source E'), float, 0.0],
+ ['N', _('source N'), float, 0.0],
+ ['E', _('target E'), float, 0.0],
+ ['N', _('target N'), float, 0.0],
+ ['F_Err', _('Forward error'), None, 0],
+ ['B_Err', _(Backward error'), None, 0]
+ ['type', _('type'), [_(""), _("Start point"), _("End point")], 0] # Select from 3 choices ("Start point", "End point"),
+ # Choice with index 0 ("") is default.
+ ]
+ @endcode
+ """
+
+ wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+ # Mixin settings
+ CheckListCtrlMixin.__init__(self)
+ ListCtrlAutoWidthMixin.__init__(self)
+ # TextEditMixin.__init__(self)
+
+ # inserts first column with points numbers and checkboxes
+ cols.insert(0, ['use', _('use'), False, 0])
+
+ self.colsData = cols
+ self.dataTypes = {"colName" : 0,
+ "colLabel" : 1,
+ "colEditable" : 2,
+ "itemDefaultValue" : 3} # just for better understanding
+
+ # information whether list items are checked or not
+ self.CheckList = []
+
+ self._createCols()
+ self.hiddenCols = {}
+
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+ self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
+ self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+
+ self.selected = wx.NOT_FOUND
+ self.selectedkey = -1
+
+
+ # CheckListCtrlMixin must set an ImageList first
+ self.il = self.GetImageList(wx.IMAGE_LIST_SMALL)
+
+ # images for column sorting
+ SmallUpArrow = wx.BitmapFromImage(self.getSmallUpArrowImage())
+ SmallDnArrow = wx.BitmapFromImage(self.getSmallDnArrowImage())
+ self.sm_dn = self.il.Add(SmallDnArrow)
+ self.sm_up = self.il.Add(SmallUpArrow)
+
+ # initialize column sorter
+ self.itemDataMap = []
+ ncols = self.GetColumnCount()
+ ColumnSorterMixin.__init__(self, ncols)
+
+ # init to ascending sort on first click
+ self._colSortFlag = [1] * ncols
+
+ # same structure as itemDataMap, information about choice index selected
+ # if cell is in column without values to choose then is -1
+ self.selIdxs = []
+
+ self.ResizeColumns()
+ self.SetColumnWidth(0, 50)
+
+ def _createCols(self):
+ """!Creates columns in list"""
+ if 0:
+ # normal, simple columns
+ for col in enumerate(self.colsData):
+ iLabel = self.dataTypes["colLabel"]
+ self.InsertColumn(col[0], col[1][iLabel])
+ else:
+ # the hard way: we want images on the column header
+ info = wx.ListItem()
+ info.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT)
+ info.SetImage(-1)
+ info.m_format = wx.LIST_FORMAT_LEFT
+
+ for col in enumerate(self.colsData):
+ iLabel = self.dataTypes["colLabel"]
+ info.SetText(col[1][iLabel])
+ self.InsertColumnInfo(col[0], info)
+
+ def AddItem(self, event):
+ """!Appends an item to list with default values"""
+ iDefVal = self.dataTypes["itemDefaultValue"]
+ iColEd = self.dataTypes["colEditable"]
+ itemData = []
+ itemIndexes = []
+ for col in self.colsData:
+ if type(col[iColEd]).__name__ == "list":
+ itemData.append(col[iColEd][col[iDefVal]])
+ itemIndexes.append(col[iDefVal])
+ else:
+ itemData.append(col[iDefVal])
+ itemIndexes.append(-1)# not a choise column
+
+
+ self.selIdxs.append(itemIndexes)
+
+ for hCol in self.hiddenCols.itervalues():
+ defVal = hCol['colsData'][iDefVal]
+ if type(hCol['colsData'][iColEd]).__name__ == "list":
+ hCol['itemDataMap'].append(hCol['colsData'][iColEd][defVal])
+ hCol['selIdxs'].append(defVal)
+ else:
+ hCol['itemDataMap'].append(defVal)
+ hCol['selIdxs'].append(-1)
+
+ self.selectedkey = self.GetItemCount()
+
+ itemData[0] = self.selectedkey + 1
+ self.itemDataMap.append(copy(itemData))
+
+ self.Append(map(str, itemData))
+
+ self.selected = self.GetItemCount() - 1
+ self.SetItemData(self.selected, self.selectedkey)
+
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+ self.ResizeColumns()
+
+ return self.selected
+
+ def GetCellValue(self, key, colName):
+ """!Get value in cell of list using key (same regardless of sorting)"""
+ colNum = self._getColumnNum(colName)
+ iColEd = self.dataTypes["colEditable"]
+ return self.itemDataMap[key][colNum]
+
+ def GetCellSelIdx(self, key, colName):
+ """!Get selected index in cell of list using key (same regardless of sorting)
+
+ @return number of chosen value, if column has values to choose
+ @return -1 if column does not has values to choose
+ """
+ colNum = self._getColumnNum(colName)
+ iColEd = self.dataTypes["colEditable"]
+ return self.selIdxs[key][colNum]
+
+ def EditCellIndex(self, index, colName, cellData):
+ """!Changes value in list using key (same regardless of sorting)"""
+ colNum = self._getColumnNum(colName)
+ key = self.GetItemData(index)
+
+ iColEd = self.dataTypes["colEditable"]
+ if type(self.colsData[colNum][iColEd]).__name__ == "list":
+ cellVal = self.colsData[colNum][iColEd][cellData]
+ self.selIdxs[key][colNum] = cellData
+ else:
+ cellVal = cellData
+ self.selIdxs[key][colNum] = -1
+
+ self.itemDataMap[key][colNum] = cellVal
+ self.SetStringItem(index, colNum, str(cellVal))
+
+ def EditCellKey(self, key, colName, cellData):
+ """!Changes value in list using index (changes during sorting)"""
+ colNum = self._getColumnNum(colName)
+
+ iColEd = self.dataTypes["colEditable"]
+ if type(self.colsData[colNum][iColEd]).__name__ == "list":
+ cellVal = self.colsData[colNum][iColEd][cellData]
+ self.selIdxs[key][colNum] = cellData
+ else:
+ cellVal = cellData
+ self.selIdxs[key][colNum] = -1
+
+ self.itemDataMap[key][colNum] = cellVal
+ index = self._findIndex(key)
+
+ self.SetStringItem(index, colNum, str(cellVal))
+
+ def _findIndex(self, key):
+ """!Find index for key"""
+ index = -1
+ while True:
+ index = self.GetNextItem(index,
+ wx.LIST_NEXT_BELOW)
+ if key == self.GetItemData(index):
+ return index
+ if index == -1:
+ break
+ return -1
+
+ def ChangeColEditable(self, colName, colType):
+ """!Change 3. item in constructor parameter cols (see the class constructor hint)"""
+ colNum = self._getColumnNum(colName)
+ iColEd = self.dataTypes["colEditable"]
+ self.colsData[colNum][iColEd] = colType
+
+ def DeleteItem(self, event = None):
+ """!Delete selected item in list"""
+ if self.selected == wx.NOT_FOUND:
+ return
+
+ key = self.GetItemData(self.selected)
+ wx.ListCtrl.DeleteItem(self, self.selected)
+
+ del self.itemDataMap[key]
+ self.selIdxs.pop(key)
+
+ # update hidden columns
+ for hCol in self.hiddenCols.itervalues():
+ hCol['itemDataMap'].pop(key)
+ hCol['selIdxs'].pop(key)
+
+ # update key and point number
+ for newkey in range(key, len(self.itemDataMap)):
+ index = self.FindItemData(-1, newkey + 1)
+ self.itemDataMap[newkey][0] = newkey
+ self.SetStringItem(index, 0, str(newkey + 1))
+ self.SetItemData(index, newkey)
+
+ # update selected
+ if self.GetItemCount() > 0:
+ if self.selected < self.GetItemCount():
+ self.selectedkey = self.GetItemData(self.selected)
+ else:
+ self.selected = self.GetItemCount() - 1
+ self.selectedkey = self.GetItemData(self.selected)
+
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+ else:
+ self.selected = wx.NOT_FOUND
+ self.selectedkey = -1
+
+ def ClearItem(self, event):
+ """"!Set all values to default in selected item of points list and uncheck it."""
+ if self.selected == wx.NOT_FOUND:
+ return
+ index = self.selected
+
+ iDefVal = self.dataTypes["itemDefaultValue"]
+ iColEd = self.dataTypes["colEditable"]
+
+ i = 0
+ for col in self.colsData:
+ if i == 0:
+ i += 1
+ continue
+ if type(col[iColEd]).__name__ == "list":
+ self.EditCell(index, i, col[iColEd][col[iDefVal]])
+ else:
+ self.EditCell(index, i, col[iDefVal])
+ i += 1
+ self.CheckItem(index, False)
+
+ def ResizeColumns(self, minWidth = [90, 120]):
+ """!Resize columns"""
+ for i in range(self.GetColumnCount()):
+ self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
+ # first column is checkbox, don't set to minWidth
+ if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]:
+ self.SetColumnWidth(i, minWidth[i > 4])
+
+ self.SendSizeEvent()
+
+ def GetSelected(self):
+ """!Get index of selected item."""
+ return self.selected
+
+ # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+ def GetSortImages(self):
+ return (self.sm_dn, self.sm_up)
+
+ # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+ def GetListCtrl(self):
+ return self
+
+ def OnItemActivated(self, event):
+ """!When item is double clicked, open editor to edit editable columns."""
+ data = []
+
+ index = event.GetIndex()
+ key = self.GetItemData(index)
+ changed = False
+ iColEd = self.dataTypes["colEditable"]
+
+ for col in enumerate(self.colsData):
+ if col[1][iColEd]:
+ data.append([col[0], #culumn number
+ self.GetItem(index, col[0]).GetText(), #cell value
+ col[1][iColEd]]) #convert function for type check
+
+ if not data:
+ return
+ dlg = self.CreateEditDialog(data = data, pointNo = key)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ editedData = dlg.GetValues() # string
+
+ if len(editedData) == 0:
+ GError(parent = self,
+ message=_("Invalid value inserted. Operation canceled."))
+ else:
+ i = 0
+ for editedCell in editedData:
+ if editedCell[1] != data[i][1]:
+ self.SetStringItem(index, editedCell[0], str(editedCell[1]))
+ self.itemDataMap[key][editedCell[0]] = editedCell[1]
+ changed = True
+ i += 1
+
+ self.selIdxs[key] = dlg.GetSelectionIndexes()
+ dlg.Destroy()
+ return changed
+
+ def CreateEditDialog(self, data, pointNo):
+ """!Helper function
+ It is possible to define in child derived class
+ and adapt created dialog (e. g. it's title...)
+ """
+
+ return EditItem(parent=self, id=wx.ID_ANY, data = data, pointNo=pointNo)
+
+ def OnColClick(self, event):
+ """!ListCtrl forgets selected item..."""
+ self.selected = self.FindItemData(-1, self.selectedkey)
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+ event.Skip()
+
+ def OnItemSelected(self, event):
+ """!Updates class attributes holding information about selected item"""
+ if self.selected != event.GetIndex():
+ self.selected = event.GetIndex()
+ self.selectedkey = self.GetItemData(self.selected)
+
+ event.Skip()
+
+ def getSmallUpArrowImage(self):
+ """!Get arrow up symbol for indication of sorting"""
+ stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_up_arrow.png'), 'rb')
+ try:
+ img = wx.ImageFromStream(stream)
+ finally:
+ stream.close()
+ return img
+
+ def getSmallDnArrowImage(self):
+ """!Get arrow down symbol for indication of sorting"""
+ stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_down_arrow.png'), 'rb')
+ try:
+ img = wx.ImageFromStream(stream)
+ finally:
+ stream.close()
+ return img
+
+ def _getColumnNum(self, colName):
+ """!Get position of column among showed columns
+
+ @param colName - name of column
+
+ @return index of columns or -1 if col was not found
+ """
+
+ for iCol, col in enumerate(self.colsData):
+ if colName == col[0]:
+ return iCol
+
+ return -1
+
+ def HideColumn(self, colName):
+ """!Hide column (hidden columns are not editable)
+
+ @param colName - name of column
+
+ @return True - if column was hidden
+ @return False - if position is not valid or column is not showed
+ """
+ colNum = self._getColumnNum(colName)
+ if colNum == -1:
+ return False
+
+ hiddenCol = self.GetColumn(colNum)
+ self.DeleteColumn(colNum)
+
+ self.hiddenCols[colName] = {}
+ self.hiddenCols[colName]['wxCol'] = hiddenCol
+ hiddenMaps = []
+ hiddenSelIdxs = []
+ for item in self.itemDataMap:
+ hiddenMaps.append(item.pop(colNum))
+ for item in self.selIdxs:
+ hiddenSelIdxs.append(item.pop(colNum))
+
+ self.hiddenCols[colName]['itemDataMap'] = hiddenMaps
+ self.hiddenCols[colName]['selIdxs'] = hiddenSelIdxs
+ self.hiddenCols[colName]['colsData'] = self.colsData.pop(colNum)
+ self.ResizeColumns()
+
+ return True
+
+ def ShowColumn(self, colName, pos):
+ """!Show column
+
+ @param colName - name of column
+ @param pos - zero based index of position among showed columns (including added 'use' column)
+
+ @return True - if column was shown
+ @return False - if position is not valid or column is not hidden
+ """
+ if pos < 0 and pos >= self.self.GetColumnCount():
+ return False
+ if self.hiddenCols.has_key(colName):
+ col = self.hiddenCols[colName]
+
+ for item in enumerate(self.itemDataMap):
+ item[1].insert(pos, col['itemDataMap'][item[0]])
+ for item in enumerate(self.selIdxs):
+ item[1].insert(pos, col['selIdxs'][item[0]])
+
+ self.colsData.insert(pos, col['colsData'])
+
+ self.InsertColumnItem(pos, col['wxCol'])
+ self.ResizeColumns()
+ del self.hiddenCols[colName]
+ return True
+
+ return False
+
+ def IsShown(self, colName):
+ """!Is column shown
+
+ @param colName - name of column
+
+ @return True - if is shown
+ @return False - if is not shown
+ """
+
+ if self._getColumnNum(colName) == -1:
+ return False
+ else:
+ return True
+
+class EditItem(wx.Dialog):
+
+ def __init__(self, parent, data, pointNo, itemCap = "Point No." ,id=wx.ID_ANY,
+ title =_("Edit point"), style=wx.DEFAULT_DIALOG_STYLE):
+ """!Dialog for editing item cells in list"""
+
+ wx.Dialog.__init__(self, parent, id, title=_(title), style=style)
+
+ self.parent = parent
+ panel = wx.Panel(parent=self)
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
+ label=" %s %s " % (_(itemCap), str(pointNo + 1)))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+ # source coordinates
+ gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+
+ self.fields = []
+ self.data = deepcopy(data)
+
+
+ col = 0
+ row = 0
+ iField = 0
+ for cell in self.data:
+
+ # Select
+ if type(cell[2]).__name__ == "list":
+ self.fields.append(wx.ComboBox(parent = panel, id = wx.ID_ANY,
+ choices = cell[2],
+ style = wx.CB_READONLY,
+ size = (110, -1)))
+ # Text field
+ else:
+ if cell[2] == float:
+ validator = FloatValidator()
+ elif cell[2] == int:
+ validator = IntegerValidator()
+ else:
+ validator = None
+
+ if validator:
+ self.fields.append(wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+ validator = validator, size=(150, -1)))
+ else:
+ self.fields.append(wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+ size=(150, -1)))
+ self.fields[iField].SetValue(str(cell[1]))
+
+ label = wx.StaticText(parent = panel, id=wx.ID_ANY,
+ label = _(parent.GetColumn(cell[0]).GetText()) + ":") # name of column)
+
+ gridSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL,
+ pos=(row, col))
+
+ col += 1
+
+ gridSizer.Add(item=self.fields[iField],
+ pos=(row, col))
+
+
+ if col%3 == 0:
+ col = 0
+ row += 1
+ else:
+ col += 1
+
+ iField += 1
+
+ boxSizer.Add(item=gridSizer, proportion=1,
+ flag=wx.EXPAND | wx.ALL, border=5)
+
+ sizer.Add(item=boxSizer, proportion=1,
+ flag=wx.EXPAND | wx.ALL, border=5)
+
+ #
+ # buttons
+ #
+ self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
+ self.btnOk = wx.Button(panel, wx.ID_OK)
+ self.btnOk.SetDefault()
+
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOk)
+ btnSizer.Realize()
+
+ sizer.Add(item=btnSizer, proportion=0,
+ flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+ panel.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def GetValues(self):
+ """!Return list of values (as strings).
+ """
+
+ iField = 0
+ for cell in self.data:
+ value = self.fields[iField].GetValue()
+
+ if type(cell[2]).__name__ == "list":
+ cell[1] = value
+ else:
+ try:
+ cell[1] = cell[2](value)
+ except ValueError:
+ return []
+ iField += 1
+
+ return self.data
+
+ def GetSelectionIndexes(self):
+ """!Return indexes of selected values (works just for choice columns).
+ """
+ iField = 0
+ itemIndexes = []
+ for cell in self.parent.colsData:
+ if type(cell[2]).__name__ == "list":
+ itemIndexes.append(self.fields[iField].GetSelection())
+ else:
+ itemIndexes.append(-1) # not a choise column
+ if cell[2]:
+ iField += 1
+
+ return itemIndexes
+
+
+
Property changes on: grass/trunk/gui/wxpython/vnet/widgets.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/trunk/gui/wxpython/xml/menudata.xml 2013-01-03 16:59:02 UTC (rev 54514)
+++ grass/trunk/gui/wxpython/xml/menudata.xml 2013-01-03 17:46:18 UTC (rev 54515)
@@ -2314,6 +2314,13 @@
<menu>
<label>Network analysis</label>
<items>`
+ <menuitem>
+ <label>Vector network analysis tool</label>
+ <help>Tool for interactive vector network analysis.</help>
+ <keywords>gui,vector,network</keywords>
+ <handler>OnVNet</handler>
+ </menuitem>
+ <separator />
<menuitem>
<label>Network preparation</label>
<help>Performs network maintenance.</help>
More information about the grass-commit
mailing list