[GRASS-SVN] r57823 - in sandbox/turek/scatter_plot: . gui/wxpython gui/wxpython/docs gui/wxpython/iclass gui/wxpython/mapdisp gui/wxpython/scatt_plot include include/defs lib/imagery
svn_grass at osgeo.org
svn_grass at osgeo.org
Mon Sep 23 09:22:37 PDT 2013
Author: turek
Date: 2013-09-23 09:22:36 -0700 (Mon, 23 Sep 2013)
New Revision: 57823
Added:
sandbox/turek/scatter_plot/gui/wxpython/docs/
sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html
sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
sandbox/turek/scatter_plot/gui/wxpython/iclass/
sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py
sandbox/turek/scatter_plot/gui/wxpython/mapdisp/
sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py
sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py
Modified:
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
sandbox/turek/scatter_plot/include/defs/imagery.h
sandbox/turek/scatter_plot/include/imagery.h
sandbox/turek/scatter_plot/lib/imagery/scatt.c
sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
sandbox/turek/scatter_plot/testing_patch.diff
Log:
scattter plot: gsoc final source
Added: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI.iscatt.html 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,85 @@
+<!-- meta page description: wxGUI Interactive Scatter Plot Tool-->
+<!-- meta page index: wxGUI -->
+
+<h2>DESCRIPTION</h2>
+
+<em>Interactive Scatter Plot Tool</em> allows to analyze group of raster maps.
+ The tool is integrated into <em><a href="wxGUI.iclass.html">
+ Supervised Classification Tool (wx.iclass) </a></em> (see the screen shot bellow).
+ Also it is possible to launch it from Map Display Window
+ (Analyze map → Interactive Scatter Plot Tool).
+
+The main idea of the tool is that everything is linked together
+(scatter plots together and mapwindow with the scatter plots).
+
+The main feature of the tool is the interactivity, which allows user to:
+<ul>
+ <li> work with multiple plots, which represents multiple raster bands combinations.
+ </li>
+ <li> highlight plotted points in open scatter plots according to currently
+ chosen pixels for classes by it's training areas (supported only in wx.iclass)
+ </li>
+ <li> be able to define areas in plots and the tool will highlight pixels in
+ map display window and corresponding points in other plots
+ </li>
+ <li> plot of confidence ellipses
+ </li>
+</ul>
+
+<h2>TOOL CONTROLS LAYOUT</h2>
+
+<center>
+<br>
+<img src="wxGUI_iscatt.png" border="1" alt="Interactive Scatter Plot Tool">
+<br><br>
+</center>
+
+If editing mode is activated (the green polygon tool in toolbar), the areas which were selected
+in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6 B_7 inside the ellipse.
+Opacity and color of the selected area can be set in settings. The area corresponds to the active class (in this case clouds).
+Selected areas are subset of areas, which belongs to the category.
+<p>
+In the editing mode it is possible to select certain area by the polygon, which can be created and edited by tools in editing toolbar.
+After the area is selected, we can include or exclude it into active category by clicking on the first (plus) respectively
+second (minus) tool in the editing toolbar. In mapwindow corresponding pixels are shown by the class raster representing
+selected areas in scatter plots. In this case we can see clouds class raster (blue), forest class raster (green) and
+water class raster (red).
+
+<h2>NOTES</h2>
+
+The tool can analyze only integer (CELL) rasters.
+
+It works with 8 bit range smoothly.
+The tool is capable of processing data up to 12 bit range, however it is not recommended open many scatter plots in 12/11 bit mode.
+It could require significant amount of memory and plot rendering time can be longer.
+Analyzing of raster data with higher range is not recommended.
+The raster range can be lowered using <em><a href="r.rescale.html">r.rescale</a></em>.
+
+<p>
+
+<h2>KNOWN ISSUES</h2>
+
+Selection of areas in mapwindow is currently supported only if the tool was launched from
+<em><a href="wxGUI.iclass.html">Supervised Classification Tool (wx.iclass) </a></em>
+
+<h2>SEE ALSO</h2>
+
+<em>
+ <a href="wxGUI.html">wxGUI</a><br>
+ <a href="wxGUI.components.html">wxGUI components</a>
+ <a href="r.rescale.html">r.rescale</a><br>
+</em>
+
+<p>
+See also the
+user <a href="http://grasswiki.osgeo.org/wiki/WxGUI_Interactive_Scatter_Plot_Tool">wiki</a>
+page.
+
+<h2>AUTHOR</h2>
+
+Stepan
+Turek, <a href="http://grasswiki.osgeo.org/wiki/GRASS_GSoC_2013_GRASS_GIS_Interactive_Scatter_Plot_Tool">Google
+Summer of Code 2013</a> (mentor: Martin Landa)
+
+<p>
+<i>$Date: 2013-07-04 09:55:17 +0200 (Thu, 04 Jul 2013) $</i>
Added: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
===================================================================
(Binary files differ)
Property changes on: sandbox/turek/scatter_plot/gui/wxpython/docs/wxGUI_iscatt.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/iclass/plots.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,320 @@
+"""!
+ at package iclass.plots
+
+ at brief wxIClass plots (histograms, coincidence plots).
+
+Classes:
+ - plots::PlotPanel
+
+(C) 2006-2011 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Vaclav Petras <wenzeslaus gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+import wx.lib.plot as plot
+import wx.lib.scrolledpanel as scrolled
+from core.utils import _
+from core.gcmd import GError
+
+class PlotPanel(scrolled.ScrolledPanel):
+ """!Panel for drawing multiple plots.
+
+ There are two types of plots: histograms and coincidence plots.
+ Histograms show frequency of cell category values in training areas
+ for each band and for one category. Coincidence plots show min max range
+ of classes for each band.
+ """
+ def __init__(self, parent, giface, stats_data):
+ scrolled.ScrolledPanel.__init__(self, parent)
+
+ self.SetupScrolling(scroll_x = False, scroll_y = True)
+ self._giface = giface
+ self.parent = parent
+ self.canvasList = []
+ self.bandList = []
+ self.stats_data = stats_data
+ self.currentCat = None
+
+ self.mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+ self._createControlPanel()
+ self._createPlotPanel()
+ self._createScatterPlotPanel()
+
+ self.SetSizer(self.mainSizer)
+ self.mainSizer.Fit(self)
+ self.Layout()
+
+ def _createPlotPanel(self):
+
+ self.canvasPanel = wx.Panel(parent=self)
+ self.mainSizer.Add(item = self.canvasPanel, proportion = 1, flag = wx.EXPAND, border = 0)
+ self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
+ self.canvasPanel.SetSizer(self.canvasSizer)
+
+ def _createControlPanel(self):
+ self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
+ choices = [_("Histograms"),
+ _("Coincident plots"),
+ _("Scatter plots")])
+ self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+ self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
+
+ def _createScatterPlotPanel(self):
+ """!Init interactive scatterplot tools
+ """
+ try:
+ from scatt_plot.frame import IClassIScattPanel
+ self.scatt_plot_panel = IClassIScattPanel(parent=self,
+ giface=self._giface,
+ iclass_mapwin = self.parent.GetFirstWindow())
+ self.mainSizer.Add(self.scatt_plot_panel, proportion = 1, flag = wx.EXPAND, border = 0)
+ self.scatt_plot_panel.Hide()
+ except ImportError as e:#TODO
+ self.scatt_error = _("Scatter plot functionality is disabled. Reason:\n" \
+ "Unable to import packages needed for scatter plot.\n%s" % e)
+ GError(self.scatt_error)
+ self.scatt_plot_panel = None
+
+ def OnPlotTypeSelected(self, event):
+ """!Plot type selected"""
+
+ if self.plotSwitch.GetSelection() in [0, 1]:
+ self.SetupScrolling(scroll_x = False, scroll_y = True)
+ if self.scatt_plot_panel:
+ self.scatt_plot_panel.Hide()
+ self.canvasPanel.Show()
+ self.Layout()
+
+ elif self.plotSwitch.GetSelection() == 2:
+ self.SetupScrolling(scroll_x = False, scroll_y = False)
+ if self.scatt_plot_panel:
+ self.scatt_plot_panel.Show()
+ else:
+ GError(self.scatt_error)
+ self.canvasPanel.Hide()
+ self.Layout()
+
+ if self.currentCat is None:
+ return
+
+ if self.plotSwitch.GetSelection() == 0:
+ stat = self.stats_data.GetStatistics(self.currentCat)
+ if not stat.IsReady():
+ self.ClearPlots()
+ return
+ self.DrawHistograms(stat)
+ else:
+ self.DrawCoincidencePlots()
+
+ self.Layout()
+
+
+ def StddevChanged(self):
+ """!Standard deviation multiplier changed, redraw histograms"""
+ if self.plotSwitch.GetSelection() == 0:
+ stat = self.stats_data.GetStatistics(self.currentCat)
+ self.UpdateRanges(stat)
+
+ def EnableZoom(self, type, enable = True):
+ for canvas in self.canvasList:
+ canvas.SetEnableZoom(enable)
+
+ #canvas.zoom = type
+
+ def EnablePan(self, enable = True):
+ for canvas in self.canvasList:
+ canvas.SetEnableDrag(enable)
+
+ def DestroyPlots(self):
+ """!Destroy all plot canvases"""
+ for panel in self.canvasList:
+ panel.Destroy()
+
+ self.canvasList = []
+
+ def ClearPlots(self):
+ """!Clears plot canvases"""
+ for bandIdx in range(len(self.bandList)):
+ self.canvasList[bandIdx].Clear()
+
+ def Reset(self):
+ """!Reset plots (when new map imported)"""
+ self.currentCat = None
+ self.ClearPlots()
+ # bands are still the same
+
+ def CreatePlotCanvases(self):
+ """!Create plot canvases according to the number of bands"""
+ for band in self.bandList:
+ canvas = plot.PlotCanvas(self.canvasPanel)
+ canvas.SetMinSize((-1, 140))
+ canvas.SetFontSizeTitle(10)
+ canvas.SetFontSizeAxis(8)
+ self.canvasList.append(canvas)
+
+ self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+
+ self.SetVirtualSize(self.GetBestVirtualSize())
+ self.Layout()
+
+ def UpdatePlots(self, group, subgroup, currentCat, stats_data):
+ """!Update plots after new analysis
+
+ @param group imagery group
+ @param subgroup imagery group
+ @param currentCat currently selected category (class)
+ @param stats_data StatisticsData instance (defined in statistics.py)
+ """
+ self.stats_data = stats_data
+ self.currentCat = currentCat
+ self.bandList = self.parent.GetGroupLayers(group, subgroup)
+
+ graphType = self.plotSwitch.GetSelection()
+
+ stat = self.stats_data.GetStatistics(currentCat)
+ if not stat.IsReady() and graphType == 0:
+ return
+
+ self.DestroyPlots()
+ self.CreatePlotCanvases()
+ self.OnPlotTypeSelected(None)
+
+ def UpdateCategory(self, cat):
+ self.currentCat = cat
+
+ def DrawCoincidencePlots(self):
+ """!Draw coincidence plots"""
+ for bandIdx in range(len(self.bandList)):
+ self.canvasList[bandIdx].SetYSpec(type = 'none')
+ lines = []
+ level = 0.5
+ lines.append(self.DrawInvisibleLine(level))
+
+ cats = self.stats_data.GetCategories()
+ for i, cat in enumerate(cats):
+ stat = self.stats_data.GetStatistics(cat)
+ if not stat.IsReady():
+ continue
+ color = stat.color
+ level = i + 1
+ line = self.DrawCoincidenceLine(level, color, stat.bands[bandIdx])
+ lines.append(line)
+
+ # invisible
+ level += 0.5
+ lines.append(self.DrawInvisibleLine(level))
+
+ plotGraph = plot.PlotGraphics(lines, title = self.bandList[bandIdx])
+ self.canvasList[bandIdx].Draw(plotGraph)
+
+ def DrawCoincidenceLine(self, level, color, bandValues):
+ """!Draw line between band min and max values
+
+ @param level y coordinate of line
+ @param color class color
+ @param bandValues BandStatistics instance
+ """
+ minim = bandValues.min
+ maxim = bandValues.max
+ points = [(minim, level), (maxim, level)]
+ color = wx.Colour(*map(int, color.split(':')))
+ return plot.PolyLine(points, colour = color, width = 4)
+
+ def DrawInvisibleLine(self, level):
+ """!Draw white line to achieve better margins"""
+ points = [(100, level), (101, level)]
+ return plot.PolyLine(points, colour = wx.WHITE, width = 1)
+
+ def DrawHistograms(self, statistics):
+ """!Draw histograms for one class
+
+ @param statistics statistics for one class
+ """
+ self.histogramLines = []
+ for bandIdx in range(len(self.bandList)):
+ self.canvasList[bandIdx].Clear()
+ self.canvasList[bandIdx].SetYSpec(type = 'auto')
+ histgramLine = self.CreateHistogramLine(bandValues = statistics.bands[bandIdx])
+
+ meanLine = self.CreateMean(bandValues = statistics.bands[bandIdx])
+
+ minLine = self.CreateMin(bandValues = statistics.bands[bandIdx])
+
+ maxLine = self.CreateMax(bandValues = statistics.bands[bandIdx])
+
+ self.histogramLines.append([histgramLine, meanLine, minLine, maxLine])
+
+ maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
+ minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
+
+ plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
+ title = self.bandList[bandIdx])
+ self.canvasList[bandIdx].Draw(plotGraph)
+
+ def CreateMinRange(self, bandValues):
+ maxVal = max(bandValues.histo)
+ rMin = bandValues.rangeMin
+
+ points = [(rMin, 0), (rMin, maxVal)]
+
+ return plot.PolyLine(points, colour = wx.RED, width = 1)
+
+ def CreateMaxRange(self, bandValues):
+ maxVal = max(bandValues.histo)
+ rMax = bandValues.rangeMax
+ points = [(rMax, 0), (rMax, maxVal)]
+
+ return plot.PolyLine(points, colour = wx.RED, width = 1)
+
+ def CreateMean(self, bandValues):
+ maxVal = max(bandValues.histo)
+ mean = bandValues.mean
+ points = [(mean, 0), (mean, maxVal)]
+
+ return plot.PolyLine(points, colour = wx.BLUE, width = 1)
+
+ def CreateMin(self, bandValues):
+ maxVal = max(bandValues.histo)
+ minim = bandValues.min
+ points = [(minim, 0), (minim, maxVal)]
+
+ return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
+
+ def CreateMax(self, bandValues):
+ maxVal = max(bandValues.histo)
+ maxim = bandValues.max
+ points = [(maxim, 0), (maxim, maxVal)]
+
+ return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
+
+ def CreateHistogramLine(self, bandValues):
+ points = []
+ for cellCat, count in enumerate(bandValues.histo):
+ if cellCat < bandValues.min - 5:
+ continue
+ if cellCat > bandValues.max + 5:
+ break
+ points.append((cellCat, count))
+
+ return plot.PolyLine(points, colour = wx.BLACK, width = 1)
+
+ def UpdateRanges(self, statistics):
+ """!Redraw ranges lines in histograms when std dev multiplier changes
+
+ @param statistics python Statistics instance
+ """
+ for bandIdx in range(len(self.bandList)):
+ self.canvasList[bandIdx].Clear()
+ maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
+ minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
+
+ plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
+ title = self.bandList[bandIdx])
+ self.canvasList[bandIdx].Draw(plotGraph)
+
Added: sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/mapdisp/frame.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,1234 @@
+"""!
+ at package mapdisp.frame
+
+ at brief Map display with toolbar for various display management
+functions, and additional toolbars (vector digitizer, 3d view).
+
+Can be used either from Layer Manager or as d.mon backend.
+
+Classes:
+ - mapdisp::MapFrame
+
+(C) 2006-2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (SingleMapFrame, handlers support)
+ at author Anna Kratochvilova <kratochanna gmail.com> (SingleMapFrame)
+ at author Stepan Turek <stepan.turek seznam.cz> (handlers support)
+"""
+
+import os
+import sys
+import copy
+
+from core import globalvar
+import wx
+import wx.aui
+
+if os.path.join(globalvar.ETCWXDIR, "icons") not in sys.path:
+ sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+if os.path.join(globalvar.ETCDIR, "python") not in sys.path:
+ sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
+
+from core import globalvar
+from core.render import Map
+from vdigit.toolbars import VDigitToolbar
+from mapdisp.toolbars import MapToolbar, NvizIcons
+from mapdisp.gprint import PrintOptions
+from core.gcmd import GError, GMessage
+from dbmgr.dialogs import DisplayAttributesDialog
+from core.utils import ListOfCatsToRange, GetLayerNameFromCmd, _
+from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
+from core.debug import Debug
+from core.settings import UserSettings
+from gui_core.mapdisp import SingleMapFrame
+from mapwin.base import MapWindowProperties
+from gui_core.query import QueryDialog, PrepareQueryResults
+from mapwin.buffered import BufferedMapWindow
+from mapwin.decorations import TextLayerDialog, \
+ LegendController, BarscaleController, ArrowController
+from modules.histogram import HistogramFrame
+from wxplot.histogram import HistogramPlotFrame
+from wxplot.profile import ProfileFrame
+from wxplot.scatter import ScatterFrame
+from mapwin.analysis import ProfileController, MeasureDistanceController
+from gui_core.forms import GUI
+
+from mapdisp import statusbar as sb
+
+import grass.script as grass
+
+from grass.pydispatch.signal import Signal
+
+
+class MapFrame(SingleMapFrame):
+ """!Main frame for map display window. Drawing takes place in
+ child double buffered drawing window.
+ """
+ def __init__(self, parent, giface, title = _("GRASS GIS - Map display"),
+ toolbars = ["map"], tree = None, notebook = None, lmgr = None,
+ page = None, Map = Map(), auimgr = None, name = 'MapWindow', **kwargs):
+ """!Main map display window with toolbars, statusbar and
+ 2D map window, 3D map window and digitizer.
+
+ @param toolbars array of activated toolbars, e.g. ['map', 'digit']
+ @param tree reference to layer tree
+ @param notebook control book ID in Layer Manager
+ @param lmgr Layer Manager
+ @param page notebook page with layer tree
+ @param Map instance of render.Map
+ @param auimgs AUI manager
+ @param name frame name
+ @param kwargs wx.Frame attributes
+ """
+ SingleMapFrame.__init__(self, parent = parent, title = title,
+ Map = Map, auimgr = auimgr, name = name, **kwargs)
+
+ self._giface = giface
+ # Layer Manager object
+ # need by GLWindow (a lot), VDigitWindow (a little bit)
+ self._layerManager = lmgr
+ # Layer Manager layer tree object
+ # used for VDigit toolbar and window and GLWindow
+ self.tree = tree
+ # Notebook page holding the layer tree
+ # used only in OnCloseWindow
+ self.page = page
+ # Layer Manager layer tree notebook
+ # used only in OnCloseWindow
+ self.layerbook = notebook
+
+ # Emitted when starting (switching to) 3D mode.
+ # Parameter firstTime specifies if 3D was already actived.
+ self.starting3dMode = Signal("MapFrame.starting3dMode")
+
+ # Emitted when ending (switching from) 3D mode.
+ self.ending3dMode = Signal("MapFrame.ending3dMode")
+
+ # properties are shared in other objects, so defining here
+ self.mapWindowProperties = MapWindowProperties()
+ self.mapWindowProperties.setValuesFromUserSettings()
+
+ #
+ # Add toolbars
+ #
+ for toolb in toolbars:
+ self.AddToolbar(toolb)
+
+ #
+ # Add statusbar
+ #
+
+ # items for choice
+ self.statusbarItems = [sb.SbCoordinates,
+ sb.SbRegionExtent,
+ sb.SbCompRegionExtent,
+ sb.SbShowRegion,
+ sb.SbAlignExtent,
+ sb.SbResolution,
+ sb.SbDisplayGeometry,
+ sb.SbMapScale,
+ sb.SbGoTo,
+ sb.SbProjection]
+
+ self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
+ sb.SbDisplayGeometry,
+ sb.SbShowRegion,
+ sb.SbResolution,
+ sb.SbMapScale)
+
+ # create statusbar and its manager
+ statusbar = self.CreateStatusBar(number = 4, style = 0)
+ statusbar.SetStatusWidths([-5, -2, -1, -1])
+ self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
+
+ # fill statusbar manager
+ self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
+ self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
+ sbRender = sb.SbRender(self, statusbar = statusbar, position = 3)
+ self.statusbarManager.AddStatusbarItem(sbRender)
+
+ self.statusbarManager.Update()
+
+ #
+ self.Map.updateProgress.connect(self.statusbarManager.SetProgress)
+
+ # init decoration objects
+ self.decorations = {}
+ self.legend = LegendController(self.Map, self._giface)
+ self.barscale = BarscaleController(self.Map, self._giface)
+ self.arrow = ArrowController(self.Map, self._giface)
+ self.decorations[self.legend.id] = self.legend
+ self.decorations[self.barscale.id] = self.barscale
+ self.decorations[self.arrow.id] = self.arrow
+
+ self.mapWindowProperties.autoRenderChanged.connect(
+ lambda value:
+ self.OnRender(None) if value else None)
+
+ #
+ # Init map display (buffered DC & set default cursor)
+ #
+ self.MapWindow2D = BufferedMapWindow(self, giface = self._giface,
+ Map=self.Map,
+ properties=self.mapWindowProperties,
+ overlays=self.decorations)
+ self.MapWindow2D.mapQueried.connect(self.Query)
+ self.MapWindow2D.overlayActivated.connect(self._activateOverlay)
+ self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
+ self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
+ for overlay in (self.legend, self.barscale, self.arrow):
+ overlay.overlayChanged.connect(lambda: self.MapWindow2D.UpdateMap(render=False, renderVector=False))
+ self._setUpMapWindow(self.MapWindow2D)
+
+ self.MapWindow2D.mouseHandlerUnregistered.connect(self.ResetPointer)
+
+ self.MapWindow2D.InitZoomHistory()
+ self.MapWindow2D.zoomChanged.connect(self.StatusbarUpdate)
+
+ self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
+ # default is 2D display mode
+ self.MapWindow = self.MapWindow2D
+ self.MapWindow.SetNamedCursor('default')
+ # used by vector digitizer
+ self.MapWindowVDigit = None
+ # used by Nviz (3D display mode)
+ self.MapWindow3D = None
+
+ #
+ # initialize region values
+ #
+ self._initMap(Map = self.Map)
+
+ self.toolbars['map'].SelectDefault()
+ #
+ # Bind various events
+ #
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+
+ #
+ # Update fancy gui style
+ #
+ self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
+ Dockable(False).BestSize((-1,-1)).Name('2d').
+ CloseButton(False).DestroyOnClose(True).
+ Layer(0))
+ self._mgr.Update()
+
+ #
+ # Init print module and classes
+ #
+ self.printopt = PrintOptions(self, self.MapWindow)
+
+ #
+ # Re-use dialogs
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
+ self.dialogs['scatt_plot'] = None
+ self.dialogs['category'] = None
+ self.dialogs['vnet'] = None
+ self.dialogs['query'] = None
+
+ self.measureDistController = None
+
+ def GetMapWindow(self):
+ return self.MapWindow
+
+ def _addToolbarVDigit(self):
+ """!Add vector digitizer toolbar
+ """
+ from vdigit.main import haveVDigit, VDigit
+
+ if not haveVDigit:
+ from vdigit import errorMsg
+
+ self.toolbars['map'].combo.SetValue(_("2D view"))
+
+ GError(_("Unable to start wxGUI vector digitizer.\n"
+ "Details: %s") % errorMsg, parent = self)
+ return
+
+ if not self.MapWindowVDigit:
+ from vdigit.mapwindow import VDigitWindow
+ self.MapWindowVDigit = VDigitWindow(parent=self, giface=self._giface,
+ properties=self.mapWindowProperties,
+ Map=self.Map, tree=self.tree,
+ lmgr=self._layerManager,
+ overlays=self.decorations)
+ self._setUpMapWindow(self.MapWindowVDigit)
+ self.MapWindowVDigit.digitizingInfo.connect(
+ lambda text:
+ self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(text))
+ self.MapWindowVDigit.digitizingInfoUnavailable.connect(
+ lambda:
+ self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
+ self.MapWindowVDigit.Show()
+ self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
+ Dockable(False).BestSize((-1,-1)).Name('vdigit').
+ CloseButton(False).DestroyOnClose(True).
+ Layer(0))
+
+ self.MapWindow = self.MapWindowVDigit
+
+ if self._mgr.GetPane('2d').IsShown():
+ self._mgr.GetPane('2d').Hide()
+ elif self._mgr.GetPane('3d').IsShown():
+ self._mgr.GetPane('3d').Hide()
+ self._mgr.GetPane('vdigit').Show()
+ self.toolbars['vdigit'] = VDigitToolbar(parent=self, toolSwitcher=self._toolSwitcher,
+ MapWindow = self.MapWindow,
+ digitClass=VDigit, giface=self._giface,
+ layerTree=self.tree)
+ self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
+
+ self._mgr.AddPane(self.toolbars['vdigit'],
+ wx.aui.AuiPaneInfo().
+ Name("vdigittoolbar").Caption(_("Vector Digitizer Toolbar")).
+ ToolbarPane().Top().Row(1).
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(False).TopDockable(True).
+ CloseButton(False).Layer(2).
+ BestSize((self.toolbars['vdigit'].GetBestSize())))
+ # change mouse to draw digitized line
+ self.MapWindow.mouse['box'] = "point"
+ self.MapWindow.zoomtype = 0
+ self.MapWindow.pen = wx.Pen(colour = 'red', width = 2, style = wx.SOLID)
+ self.MapWindow.polypen = wx.Pen(colour = 'green', width = 2, style = wx.SOLID)
+
+ def AddNviz(self):
+ """!Add 3D view mode window
+ """
+ from nviz.main import haveNviz, GLWindow, errorMsg
+
+ # check for GLCanvas and OpenGL
+ if not haveNviz:
+ self.toolbars['map'].combo.SetValue(_("2D view"))
+ GError(parent = self,
+ message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
+ "was not found or loaded properly.\n"
+ "Switching back to 2D display mode.\n\nDetails: %s" % errorMsg))
+ return
+
+ # here was disabling 3D for other displays, now done on starting3dMode
+
+ self.toolbars['map'].Enable2D(False)
+ # add rotate tool to map toolbar
+ self.toolbars['map'].InsertTool((('rotate', NvizIcons['rotate'],
+ self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
+ self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
+ tool=self.toolbars['map'].rotate)
+ self.toolbars['map'].InsertTool((('flyThrough', NvizIcons['flyThrough'],
+ self.OnFlyThrough, wx.ITEM_CHECK, 8),))
+ self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
+ tool=self.toolbars['map'].flyThrough)
+ self.toolbars['map'].ChangeToolsDesc(mode2d = False)
+ # update status bar
+
+ self.statusbarManager.HideStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+ self.statusbarManager.SetMode(0)
+
+ # erase map window
+ self.MapWindow.EraseMap()
+
+ self._giface.WriteCmdLog(_("Starting 3D view mode..."))
+ self.SetStatusText(_("Please wait, loading data..."), 0)
+
+ # create GL window
+ if not self.MapWindow3D:
+ self.MapWindow3D = GLWindow(self, giface = self._giface, id = wx.ID_ANY, frame = self,
+ Map = self.Map, tree = self.tree, lmgr = self._layerManager)
+ self._setUpMapWindow(self.MapWindow3D)
+ self.MapWindow3D.mapQueried.connect(self.Query)
+ self.MapWindow = self.MapWindow3D
+ self.MapWindow.SetNamedCursor('default')
+
+ # here was AddNvizTools in lmgr
+ self.starting3dMode.emit(firstTime=True)
+
+ # switch from MapWindow to MapWindowGL
+ self._mgr.GetPane('2d').Hide()
+ self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
+ Dockable(False).BestSize((-1,-1)).Name('3d').
+ CloseButton(False).DestroyOnClose(True).
+ Layer(0))
+
+ self.MapWindow3D.Show()
+ self.MapWindow3D.ResetViewHistory()
+ self.MapWindow3D.UpdateView(None)
+ self.MapWindow3D.overlayActivated.connect(self._activateOverlay)
+ self.MapWindow3D.overlayHidden.connect(self._hideOverlay)
+ self.legend.overlayChanged.connect(lambda: self.MapWindow3D.UpdateOverlays())
+ else:
+ self.MapWindow = self.MapWindow3D
+ os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True, windres3 = True)
+ self.MapWindow3D.GetDisplay().Init()
+ del os.environ['GRASS_REGION']
+
+ # switch from MapWindow to MapWindowGL
+ self._mgr.GetPane('2d').Hide()
+ self._mgr.GetPane('3d').Show()
+
+ # here was AddNvizTools in lmgr and updating of pages
+ self.starting3dMode.emit(firstTime=False)
+
+ self.MapWindow3D.ResetViewHistory()
+
+ self._giface.updateMap.disconnect(self.MapWindow2D.UpdateMap)
+ self._giface.updateMap.connect(self.MapWindow3D.UpdateMap)
+ self.MapWindow3D.overlays = self.MapWindow2D.overlays
+ self.MapWindow3D.textdict = self.MapWindow2D.textdict
+ # update overlays needs to be called after because getClientSize
+ # is called during update and it must give reasonable values
+ wx.CallAfter(self.MapWindow3D.UpdateOverlays)
+
+ self.SetStatusText("", 0)
+ self._mgr.Update()
+
+ def Disable3dMode(self):
+ """Disables 3D mode (NVIZ) in user interface."""
+ # TODO: this is broken since item is removed but switch is drived by index
+ if '3D' in self.toolbars['map'].combo.GetString(1):
+ self.toolbars['map'].combo.Delete(1)
+
+ def RemoveNviz(self):
+ """!Restore 2D view"""
+ try:
+ self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
+ self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
+ except AttributeError:
+ pass
+
+ # update status bar
+ self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+ self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
+ key = 'statusbarMode',
+ subkey = 'selection'))
+ self.SetStatusText(_("Please wait, unloading data..."), 0)
+ self._giface.WriteCmdLog(_("Switching back to 2D view mode..."))
+ if self.MapWindow3D:
+ self.MapWindow3D.OnClose(event = None)
+ # switch from MapWindowGL to MapWindow
+ self._mgr.GetPane('2d').Show()
+ self._mgr.GetPane('3d').Hide()
+
+ self.MapWindow = self.MapWindow2D
+ # here was RemoveNvizTools form lmgr
+ self.ending3dMode.emit()
+ try:
+ self.MapWindow2D.overlays = self.MapWindow3D.overlays
+ self.MapWindow2D.textdict = self.MapWindow3D.textdict
+ except AttributeError:
+ pass
+ # TODO: here we end because self.MapWindow3D is None for a while
+ self._giface.updateMap.disconnect(self.MapWindow3D.UpdateMap)
+ self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
+
+ self.MapWindow.UpdateMap()
+ self._mgr.Update()
+ self.GetMapToolbar().SelectDefault()
+
+ def AddToolbar(self, name, fixed = False):
+ """!Add defined toolbar to the window
+
+ Currently recognized toolbars are:
+ - 'map' - basic map toolbar
+ - 'vdigit' - vector digitizer
+
+ @param name toolbar to add
+ @param fixed fixed toolbar
+ """
+ # default toolbar
+ if name == "map":
+ self.toolbars['map'] = MapToolbar(self, toolSwitcher=self._toolSwitcher)
+
+ self._mgr.AddPane(self.toolbars['map'],
+ wx.aui.AuiPaneInfo().
+ Name("maptoolbar").Caption(_("Map Toolbar")).
+ ToolbarPane().Top().Name('mapToolbar').
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(False).TopDockable(True).
+ CloseButton(False).Layer(2).
+ BestSize((self.toolbars['map'].GetBestSize())))
+
+ # vector digitizer
+ elif name == "vdigit":
+ self.toolbars['map'].combo.SetValue(_("Digitize"))
+ self._addToolbarVDigit()
+
+ if fixed:
+ self.toolbars['map'].combo.Disable()
+
+ self._mgr.Update()
+
+ def RemoveToolbar (self, name):
+ """!Removes defined toolbar from the window
+
+ @todo Only hide, activate by calling AddToolbar()
+ """
+ # cannot hide main toolbar
+ if name == "map":
+ return
+
+ self._mgr.DetachPane(self.toolbars[name])
+ self._toolSwitcher.RemoveToolbarFromGroup('mouseUse', self.toolbars[name])
+ self.toolbars[name].Destroy()
+ self.toolbars.pop(name)
+
+ if name == 'vdigit':
+ self._mgr.GetPane('vdigit').Hide()
+ self._mgr.GetPane('2d').Show()
+ self.MapWindow = self.MapWindow2D
+
+ self.toolbars['map'].combo.SetValue(_("2D view"))
+ self.toolbars['map'].Enable2D(True)
+
+ self._mgr.Update()
+
+ def IsPaneShown(self, name):
+ """!Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
+ if self._mgr.GetPane(name).IsOk():
+ return self._mgr.GetPane(name).IsShown()
+ return False
+
+ def RemoveQueryLayer(self):
+ """!Removes temporary map layers (queries)"""
+ qlayer = self.GetMap().GetListOfLayers(name = globalvar.QUERYLAYER)
+ for layer in qlayer:
+ self.GetMap().DeleteLayer(layer)
+
+ def OnRender(self, event):
+ """!Re-render map composition (each map layer)
+ """
+ self.RemoveQueryLayer()
+
+ # deselect features in vdigit
+ if self.GetToolbar('vdigit'):
+ if self.MapWindow.digit:
+ self.MapWindow.digit.GetDisplay().SetSelected([])
+ self.MapWindow.UpdateMap(render = True, renderVector = True)
+ else:
+ self.MapWindow.UpdateMap(render = True)
+
+ # update statusbar
+ self.StatusbarUpdate()
+
+ def OnPointer(self, event):
+ """!Pointer button clicked
+ """
+ self.MapWindow.SetModePointer()
+
+ if self.GetToolbar('vdigit'):
+ self.toolbars['vdigit'].action['id'] = -1
+ self.toolbars['vdigit'].action['desc']=''
+
+ def OnRotate(self, event):
+ """!Rotate 3D view
+ """
+ self.MapWindow.mouse['use'] = "rotate"
+
+ # change the cursor
+ self.MapWindow.SetNamedCursor('hand')
+
+ def OnFlyThrough(self, event):
+ """!Fly-through mode
+ """
+ self.MapWindow.mouse['use'] = "fly"
+
+ # change the cursor
+ self.MapWindow.SetNamedCursor('hand')
+ self.MapWindow.SetFocus()
+
+ def SaveToFile(self, event):
+ """!Save map to image
+ """
+ if self.IsPaneShown('3d'):
+ filetype = "TIF file (*.tif)|*.tif|PPM file (*.ppm)|*.ppm"
+ ltype = [{ 'ext' : 'tif', 'type' : 'tif' },
+ { 'ext' : 'ppm', 'type' : 'ppm' }]
+ else:
+ img = self.MapWindow.img
+ if not img:
+ GMessage(parent = self,
+ message = _("Nothing to render (empty map). Operation canceled."))
+ return
+ filetype, ltype = GetImageHandlers(img)
+
+ # get size
+ dlg = ImageSizeDialog(self)
+ dlg.CentreOnParent()
+ if dlg.ShowModal() != wx.ID_OK:
+ dlg.Destroy()
+ return
+ width, height = dlg.GetValues()
+ dlg.Destroy()
+
+ # get filename
+ dlg = wx.FileDialog(parent = self,
+ message = _("Choose a file name to save the image "
+ "(no need to add extension)"),
+ wildcard = filetype,
+ style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ if not path:
+ dlg.Destroy()
+ return
+
+ base, ext = os.path.splitext(path)
+ fileType = ltype[dlg.GetFilterIndex()]['type']
+ extType = ltype[dlg.GetFilterIndex()]['ext']
+ if ext != extType:
+ path = base + '.' + extType
+
+ self.MapWindow.SaveToFile(path, fileType,
+ width, height)
+
+ dlg.Destroy()
+
+ def PrintMenu(self, event):
+ """
+ Print options and output menu for map display
+ """
+ printmenu = wx.Menu()
+ # Add items to the menu
+ setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+ printmenu.AppendItem(setup)
+ self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
+
+ preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+ printmenu.AppendItem(preview)
+ self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+
+ doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
+ printmenu.AppendItem(doprint)
+ self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
+
+ # Popup the menu. If an item is selected then its handler
+ # will be called before PopupMenu returns.
+ self.PopupMenu(printmenu)
+ printmenu.Destroy()
+
+ def OnCloseWindow(self, event):
+ """!Window closed.
+ Also close associated layer tree page
+ """
+ Debug.msg(2, "MapFrame.OnCloseWindow(): function starts")
+ pgnum = None
+ self.Map.Clean()
+
+ # close edited map and 3D tools properly
+ if self.GetToolbar('vdigit'):
+ maplayer = self.toolbars['vdigit'].GetLayer()
+ if maplayer:
+ self.toolbars['vdigit'].OnExit()
+ if self.IsPaneShown('3d'):
+ self.RemoveNviz()
+
+ if not self._layerManager:
+ self.Destroy()
+ elif self.page:
+ pgnum = self.layerbook.GetPageIndex(self.page)
+ if pgnum > -1:
+ self.layerbook.DeletePage(pgnum)
+ Debug.msg(2, "MapFrame.OnCloseWindow(): function ends")
+
+ def Query(self, x, y):
+ """!Query selected layers.
+
+ @param x,y coordinates
+ @param layers selected tree item layers
+ """
+ layers = self._giface.GetLayerList().GetSelectedLayers(checkedOnly=False)
+ rast = []
+ vect = []
+ for layer in layers:
+ name, found = GetLayerNameFromCmd(layer.cmd)
+ if not found:
+ continue
+ ltype = layer.maplayer.GetType()
+ if ltype == 'raster':
+ rast.append(name)
+ elif ltype in ('rgb', 'his'):
+ for iname in name.split('\n'):
+ rast.append(iname)
+ elif ltype in ('vector', 'thememap', 'themechart'):
+ vect.append(name)
+ if vect:
+ # check for vector maps open to be edited
+ digitToolbar = self.GetToolbar('vdigit')
+ if digitToolbar:
+ lmap = digitToolbar.GetLayer().GetName()
+ for name in vect:
+ if lmap == name:
+ self._giface.WriteWarning(_("Vector map <%s> "
+ "opened for editing - skipped.") % lmap)
+ vect.remove(name)
+
+ if not (rast + vect):
+ GMessage(parent = self,
+ message = _('No raster or vector map layer selected for querying.'))
+ return
+
+ # set query snap distance for v.what at map unit equivalent of 10 pixels
+ qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
+
+ # TODO: replace returning None by exception or so
+ try:
+ east, north = self.MapWindow.Pixel2Cell((x, y))
+ except TypeError:
+ return
+
+ if not self.IsPaneShown('3d'):
+ self.QueryMap(east, north, qdist, rast, vect)
+ else:
+ if rast:
+ self.MapWindow.QuerySurface(x, y)
+ if vect:
+ self.QueryMap(east, north, qdist, rast = [], vect = vect)
+
+ def QueryMap(self, east, north, qdist, rast, vect):
+ """!Query raster or vector map layers by r/v.what
+
+ @param east,north coordinates
+ @param qdist query distance
+ @param rast raster map names
+ @param vect vector map names
+ """
+ Debug.msg(1, "QueryMap(): raster=%s vector=%s" % (','.join(rast),
+ ','.join(vect)))
+
+ # use display region settings instead of computation region settings
+ self.tmpreg = os.getenv("GRASS_REGION")
+ os.environ["GRASS_REGION"] = self.Map.SetRegion(windres = False)
+
+ rastQuery = []
+ vectQuery = []
+ if rast:
+ rastQuery = grass.raster_what(map=rast, coord=(east, north))
+ if vect:
+ vectQuery = grass.vector_what(map=vect, coord=(east, north), distance=qdist)
+ self._QueryMapDone()
+ if 'Id' in vectQuery:
+ self._queryHighlight(vectQuery)
+
+ result = rastQuery + vectQuery
+ result = PrepareQueryResults(coordinates = (east, north), result = result)
+ if self.dialogs['query']:
+ self.dialogs['query'].Raise()
+ self.dialogs['query'].SetData(result)
+ else:
+ self.dialogs['query'] = QueryDialog(parent = self, data = result)
+ self.dialogs['query'].Bind(wx.EVT_CLOSE, self._oncloseQueryDialog)
+ self.dialogs['query'].Show()
+
+ def _oncloseQueryDialog(self, event):
+ self.dialogs['query'] = None
+ event.Skip()
+
+ def _queryHighlight(self, vectQuery):
+ """!Highlight category from query."""
+ cats = name = None
+ for res in vectQuery:
+ cats = {res['Layer']: [res['Category']]}
+ name = res['Map']
+ try:
+ qlayer = self.Map.GetListOfLayers(name = globalvar.QUERYLAYER)[0]
+ except IndexError:
+ qlayer = None
+
+ if not (cats and name):
+ if qlayer:
+ self.Map.DeleteLayer(qlayer)
+ self.MapWindow.UpdateMap(render = False, renderVector = False)
+ return
+
+ if not self.IsPaneShown('3d') and self.IsAutoRendered():
+ # highlight feature & re-draw map
+ if qlayer:
+ qlayer.SetCmd(self.AddTmpVectorMapLayer(name, cats,
+ useId = False,
+ addLayer = False))
+ else:
+ qlayer = self.AddTmpVectorMapLayer(name, cats, useId = False)
+
+ # set opacity based on queried layer
+ # TODO fix
+ # opacity = layer.maplayer.GetOpacity(float = True)
+ # qlayer.SetOpacity(opacity)
+
+ self.MapWindow.UpdateMap(render = False, renderVector = False)
+
+ def _QueryMapDone(self):
+ """!Restore settings after querying (restore GRASS_REGION)
+ """
+ if hasattr(self, "tmpreg"):
+ if self.tmpreg:
+ os.environ["GRASS_REGION"] = self.tmpreg
+ elif 'GRASS_REGION' in os.environ:
+ del os.environ["GRASS_REGION"]
+ elif 'GRASS_REGION' in os.environ:
+ del os.environ["GRASS_REGION"]
+
+ if hasattr(self, "tmpreg"):
+ del self.tmpreg
+
+ def OnQuery(self, event):
+ """!Query tools menu"""
+ self.MapWindow.mouse['use'] = "query"
+ self.MapWindow.mouse['box'] = "point"
+ self.MapWindow.zoomtype = 0
+
+ # change the cursor
+ self.MapWindow.SetNamedCursor('cross')
+
+ def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
+ """!Add temporal vector map layer to map composition
+
+ @param name name of map layer
+ @param useId use feature id instead of category
+ """
+ # color settings from ATM
+ color = UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'color')
+ colorStr = str(color[0]) + ":" + \
+ str(color[1]) + ":" + \
+ str(color[2])
+
+ # icon used in vector display and its size
+ icon = ''
+ size = 0
+ # here we know that there is one selected layer and it is vector
+ vparam = self._giface.GetLayerList().GetSelectedLayers()[0].cmd
+ for p in vparam:
+ if '=' in p:
+ parg,pval = p.split('=', 1)
+ if parg == 'icon': icon = pval
+ elif parg == 'size': size = float(pval)
+
+ pattern = ["d.vect",
+ "map=%s" % name,
+ "color=%s" % colorStr,
+ "fcolor=%s" % colorStr,
+ "width=%d" % UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'width')]
+ if icon != '':
+ pattern.append('icon=%s' % icon)
+ if size > 0:
+ pattern.append('size=%i' % size)
+
+ if useId:
+ cmd = pattern
+ cmd.append('-i')
+ cmd.append('cats=%s' % str(cats))
+ else:
+ cmd = []
+ for layer in cats.keys():
+ cmd.append(copy.copy(pattern))
+ lcats = cats[layer]
+ cmd[-1].append("layer=%d" % layer)
+ cmd[-1].append("cats=%s" % ListOfCatsToRange(lcats))
+
+ if addLayer:
+ if useId:
+ return self.Map.AddLayer(ltype = 'vector', name = globalvar.QUERYLAYER, command = cmd,
+ active = True, hidden = True, opacity = 1.0)
+ else:
+ return self.Map.AddLayer(ltype = 'command', name = globalvar.QUERYLAYER, command = cmd,
+ active = True, hidden = True, opacity = 1.0)
+ else:
+ return cmd
+
+ def OnMeasure(self, event):
+ if not self.measureDistController:
+ self.measureDistController = MeasureDistanceController(self._giface,
+ mapWindow=self.GetMapWindow())
+ self.measureDistController.Start()
+
+ def OnProfile(self, event):
+ """!Launch profile tool
+ """
+ rasters = []
+ layers = self._giface.GetLayerList().GetSelectedLayers()
+ for layer in layers:
+ if layer.type == 'raster':
+ rasters.append(layer.maplayer.name)
+ self.Profile(rasters=rasters)
+
+ def Profile(self, rasters=None):
+ """!Launch profile tool"""
+ self.profileController = ProfileController(self._giface,
+ mapWindow=self.GetMapWindow())
+ win = ProfileFrame(parent=self, rasterList=rasters,
+ units=self.Map.projinfo['units'],
+ controller=self.profileController)
+ win.Show()
+ # Open raster select dialog to make sure that a raster (and
+ # the desired raster) is selected to be profiled
+ win.OnSelectRaster(None)
+
+ def OnHistogramPyPlot(self, event):
+ """!Init PyPlot histogram display canvas and tools
+ """
+ raster = []
+
+ for layer in self._giface.GetLayerList().GetSelectedLayers():
+ if layer.maplayer.GetType() == 'raster':
+ raster.append(layer.maplayer.GetName())
+
+ win = HistogramPlotFrame(parent = self, rasterList = raster)
+ win.CentreOnParent()
+ win.Show()
+
+ def OnScatterplot(self, event):
+ """!Init PyPlot scatterplot display canvas and tools
+ """
+ raster = []
+
+ for layer in self._giface.GetLayerList().GetSelectedLayers():
+ if layer.maplayer.GetType() == 'raster':
+ raster.append(layer.maplayer.GetName())
+
+ win = ScatterFrame(parent = self, rasterList = raster)
+
+ win.CentreOnParent()
+ win.Show()
+ # Open raster select dialog to make sure that at least 2 rasters (and the desired rasters)
+ # are selected to be plotted
+ win.OnSelectRaster(None)
+
+ def OnHistogram(self, event):
+ """!Init histogram display canvas and tools
+ """
+ win = HistogramFrame(self, giface=self._giface)
+
+ win.CentreOnParent()
+ win.Show()
+ win.Refresh()
+ win.Update()
+
+ def _activateOverlay(self, overlayId):
+ """!Launch decoration dialog according to overlay id.
+
+ @param overlayId id of overlay
+ """
+ if overlayId > 100:
+ self.OnAddText(None)
+ elif overlayId == 0:
+ self.AddLegend(showDialog=True)
+ elif overlayId == 1:
+ self.AddBarscale(showDialog=True)
+ elif overlayId == 2:
+ self.AddArrow(showDialog=True)
+
+ def _hideOverlay(self, overlayId):
+ """!Hide overlay.
+
+ @param overlayId id of overlay
+ """
+ self.decorations[overlayId].Hide()
+
+ def AddBarscale(self, cmd=None, showDialog=None):
+ """!Handler for scale bar map decoration menu selection."""
+ if self.IsPaneShown('3d'):
+ self.MapWindow3D.SetDrawScalebar((70, 70))
+ return
+
+ if self.barscale.IsShown() and showDialog is None:
+ self.barscale.Hide()
+ return
+
+ if cmd:
+ self.barscale.cmd = cmd
+
+ if not showDialog:
+ self.barscale.Show()
+ return
+
+ # Decoration overlay control dialog
+ if self.barscale.dialog:
+ if self.barscale.dialog.IsShown():
+ self.barscale.dialog.SetFocus()
+ self.barscale.dialog.Raise()
+ else:
+ self.barscale.dialog.Show()
+ else:
+ # If location is latlon, only display north arrow (scale won't work)
+ # proj = self.Map.projinfo['proj']
+ # if proj == 'll':
+ # barcmd = 'd.barscale -n'
+ # else:
+ # barcmd = 'd.barscale'
+
+ # decoration overlay control dialog
+ GUI(parent=self, giface=self._giface, show=True,
+ modal=False).ParseCommand(self.barscale.cmd,
+ completed=(self.barscale.GetOptData, None, None))
+
+ self.MapWindow.mouse['use'] = 'pointer'
+
+ def AddLegend(self, cmd=None, showDialog=None):
+ """!Handler for legend map decoration menu selection."""
+ if self.legend.IsShown() and showDialog is None:
+ self.legend.Hide()
+ return
+ if cmd:
+ self.legend.cmd = cmd
+ else:
+ layers = self._giface.GetLayerList().GetSelectedLayers()
+ for layer in layers:
+ if layer.type == 'raster':
+ self.legend.cmd.append('map=%s' % layer.maplayer.name)
+ break
+
+ if not showDialog and self.legend.CmdIsValid():
+ self.legend.Show()
+ return
+
+ # Decoration overlay control dialog
+ if self.legend.dialog:
+ if self.legend.dialog.IsShown():
+ self.legend.dialog.SetFocus()
+ self.legend.dialog.Raise()
+ else:
+ self.legend.dialog.Show()
+ else:
+ GUI(parent=self, giface=self._giface, show=True,
+ modal=False).ParseCommand(self.legend.cmd,
+ completed=(self.legend.GetOptData, None, None))
+
+ self.MapWindow.mouse['use'] = 'pointer'
+
+ def AddArrow(self, cmd=None, showDialog=None):
+ """!Handler for north arrow menu selection."""
+ if self.IsPaneShown('3d'):
+ # here was opening of appearance page of nviz notebook
+ # but now moved to MapWindow3D where are other problematic nviz calls
+ self.MapWindow3D.SetDrawArrow((70, 70))
+ return
+
+ if self.arrow.IsShown() and showDialog is None:
+ self.arrow.Hide()
+ return
+ if cmd:
+ self.arrow.cmd = cmd
+
+ if not showDialog:
+ self.arrow.Show()
+ return
+
+ # Decoration overlay control dialog
+ if self.arrow.dialog:
+ if self.arrow.dialog.IsShown():
+ self.arrow.dialog.SetFocus()
+ self.arrow.dialog.Raise()
+ else:
+ self.arrow.dialog.Show()
+ else:
+ GUI(parent=self, giface=self._giface, show=True,
+ modal=False).ParseCommand(self.arrow.cmd,
+ completed=(self.arrow.GetOptData, None, None))
+
+ self.MapWindow.mouse['use'] = 'pointer'
+
+ def OnAddText(self, event):
+ """!Handler for text decoration menu selection.
+ """
+ if self.MapWindow.dragid > -1:
+ id = self.MapWindow.dragid
+ self.MapWindow.dragid = -1
+ else:
+ # index for overlay layer in render
+ if len(self.MapWindow.textdict.keys()) > 0:
+ id = max(self.MapWindow.textdict.keys()) + 1
+ else:
+ id = 101
+
+ self.dialogs['text'] = TextLayerDialog(parent = self, ovlId = id,
+ title = _('Add text layer'),
+ size = (400, 200))
+ self.dialogs['text'].CenterOnParent()
+
+ # If OK button pressed in decoration control dialog
+ if self.dialogs['text'].ShowModal() == wx.ID_OK:
+ text = self.dialogs['text'].GetValues()['text']
+ active = self.dialogs['text'].GetValues()['active']
+
+ # delete object if it has no text or is not active
+ if text == '' or active == False:
+ try:
+ self.MapWindow2D.pdc.ClearId(id)
+ self.MapWindow2D.pdc.RemoveId(id)
+ del self.MapWindow.textdict[id]
+ if self.IsPaneShown('3d'):
+ self.MapWindow3D.UpdateOverlays()
+ self.MapWindow.UpdateMap()
+ else:
+ self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+ except:
+ pass
+ return
+
+
+ self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
+
+ if self.IsPaneShown('3d'):
+ self.MapWindow3D.UpdateOverlays()
+ self.MapWindow3D.UpdateMap()
+ else:
+ self.MapWindow2D.pdc.ClearId(id)
+ self.MapWindow2D.pdc.SetId(id)
+ self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+
+ self.MapWindow.mouse['use'] = 'pointer'
+
+ def GetOptData(self, dcmd, type, params, propwin):
+ """!Callback method for decoration overlay command generated by
+ dialog created in menuform.py
+ """
+ # Reset comand and rendering options in render.Map. Always render decoration.
+ # Showing/hiding handled by PseudoDC
+ self.Map.ChangeOverlay(ovltype = type, type = 'overlay', name = '', command = dcmd,
+ active = True, render = False)
+ self.params[type] = params
+ self.propwin[type] = propwin
+
+ def OnZoomToMap(self, event):
+ """!Set display extents to match selected raster (including
+ NULLs) or vector map.
+ """
+ Debug.msg(3, "MapFrame.OnZoomToMap()")
+ layers = None
+ if self.IsStandalone():
+ layers = self.MapWindow.GetMap().GetListOfLayers(active = False)
+
+ self.MapWindow.ZoomToMap(layers = layers)
+
+ def OnZoomToRaster(self, event):
+ """!Set display extents to match selected raster map (ignore NULLs)
+ """
+ self.MapWindow.ZoomToMap(ignoreNulls = True)
+
+ def OnZoomToSaved(self, event):
+ """!Set display geometry to match extents in
+ saved region file
+ """
+ self.MapWindow.ZoomToSaved()
+
+ def OnDisplayToWind(self, event):
+ """!Set computational region (WIND file) to match display
+ extents
+ """
+ self.MapWindow.DisplayToWind()
+
+ def OnSaveDisplayRegion(self, event):
+ """!Save display extents to named region file.
+ """
+ self.MapWindow.SaveRegion(display = True)
+
+ def OnSaveWindRegion(self, event):
+ """!Save computational region to named region file.
+ """
+ self.MapWindow.SaveRegion(display = False)
+
+ def OnZoomMenu(self, event):
+ """!Popup Zoom menu
+ """
+ zoommenu = wx.Menu()
+
+ for label, handler in ((_('Zoom to computational region'), self.OnZoomToWind),
+ (_('Zoom to default region'), self.OnZoomToDefault),
+ (_('Zoom to saved region'), self.OnZoomToSaved),
+ (_('Set computational region from display extent'), self.OnDisplayToWind),
+ (_('Save display geometry to named region'), self.OnSaveDisplayRegion),
+ (_('Save computational region to named region'), self.OnSaveWindRegion)):
+ mid = wx.MenuItem(zoommenu, wx.ID_ANY, label)
+ zoommenu.AppendItem(mid)
+ self.Bind(wx.EVT_MENU, handler, mid)
+
+ # Popup the menu. If an item is selected then its handler will
+ # be called before PopupMenu returns.
+ self.PopupMenu(zoommenu)
+ zoommenu.Destroy()
+
+ def SetProperties(self, render = False, mode = 0, showCompExtent = False,
+ constrainRes = False, projection = False, alignExtent = True):
+ """!Set properies of map display window"""
+ self.mapWindowProperties.autoRender = render
+ self.statusbarManager.SetMode(mode)
+ self.StatusbarUpdate()
+ self.mapWindowProperties.showRegion = showCompExtent
+ self.mapWindowProperties.alignExtent = alignExtent
+ self.mapWindowProperties.resolution = constrainRes
+ self.SetProperty('projection', projection)
+
+ def IsStandalone(self):
+ """!Check if Map display is standalone
+
+ @depreciated
+ """
+ # TODO: once it is removed from 2 places in vdigit it can be deleted
+ # here and also in base class and other classes in the tree (hopefully)
+ # and one place here still uses IsStandalone
+ Debug.msg(1, "MapFrame.IsStandalone(): Method IsStandalone is"
+ "depreciated, use some general approach instead such as"
+ " Signals or giface")
+ if self._layerManager:
+ return False
+
+ return True
+
+ def GetLayerManager(self):
+ """!Get reference to Layer Manager
+
+ @return window reference
+ @return None (if standalone)
+
+ @depreciated
+ """
+ Debug.msg(1, "MapFrame.GetLayerManager(): Method GetLayerManager is"
+ "depreciated, use some general approach instead such as"
+ " Signals or giface")
+ return self._layerManager
+
+ def GetMapToolbar(self):
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['map']
+
+ def OnIScatt(self, event):
+ """!Init interactive scatterplot tools
+ """
+ if self.dialogs['scatt_plot']:
+ self.dialogs['scatt_plot'].Raise()
+ return
+
+ from scatt_plot.frame import IScattDialog
+ self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
+
+ self.dialogs['scatt_plot'].CenterOnScreen()
+ self.dialogs['scatt_plot'].Show()
+
+ def OnVNet(self, event):
+ """!Dialog for v.net* modules
+ """
+ if self.dialogs['vnet']:
+ self.dialogs['vnet'].Raise()
+ return
+
+ from vnet.dialogs import VNETDialog
+ self.dialogs['vnet'] = VNETDialog(parent=self, giface=self._giface)
+ self.dialogs['vnet'].CenterOnScreen()
+ self.dialogs['vnet'].Show()
+
+ def ResetPointer(self):
+ """Sets pointer mode.
+
+ Sets pointer and toggles it (e.g. after unregistration of mouse
+ handler).
+ """
+ self.GetMapToolbar().SelectDefault()
Added: sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/mapdisp/toolbars.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,269 @@
+"""!
+ at package mapdisp.toolbars
+
+ at brief Map display frame - toolbars
+
+Classes:
+ - toolbars::MapToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from nviz.main import haveNviz
+from vdigit.main import haveVDigit
+from icons.icon import MetaIcon
+from core.utils import _
+
+MapIcons = {
+ 'query' : MetaIcon(img = 'info',
+ label = _('Query raster/vector map(s)'),
+ desc = _('Query selected raster/vector map(s)')),
+ 'addBarscale': MetaIcon(img = 'scalebar-add',
+ label = _('Show/hide scale bar')),
+ 'addLegend' : MetaIcon(img = 'legend-add',
+ label = _('Show/hide legend')),
+ 'addNorthArrow': MetaIcon(img = 'north-arrow-add',
+ label = _('Show/hide north arrow')),
+ 'analyze' : MetaIcon(img = 'layer-raster-analyze',
+ label = _('Analyze map'),
+ desc = _('Measuring, profiling, histogramming, ...')),
+ 'measure' : MetaIcon(img = 'measure-length',
+ label = _('Measure distance')),
+ 'profile' : MetaIcon(img = 'layer-raster-profile',
+ label = _('Profile surface map')),
+ 'scatter' : MetaIcon(img = 'layer-raster-profile',
+ label = _("Create bivariate scatterplot of raster maps")),
+ 'addText' : MetaIcon(img = 'text-add',
+ label = _('Add text layer')),
+ 'histogram' : MetaIcon(img = 'layer-raster-histogram',
+ label = _('Create histogram of raster map')),
+ 'vnet' : MetaIcon(img = 'vector-tools',
+ label = _('Vector network analysis tool')),
+ 'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
+ label = _("Interactive scatter plot tool")),
+ }
+
+NvizIcons = {
+ 'rotate' : MetaIcon(img = '3d-rotate',
+ label = _('Rotate 3D scene'),
+ desc = _('Drag with mouse to rotate 3D scene')),
+ 'flyThrough': MetaIcon(img = 'flythrough',
+ label = _('Fly-through mode'),
+ desc = _('Drag with mouse, hold Ctrl down for different mode'
+ ' or Shift to accelerate')),
+ 'zoomIn' : BaseIcons['zoomIn'].SetLabel(desc = _('Click mouse to zoom')),
+ 'zoomOut' : BaseIcons['zoomOut'].SetLabel(desc = _('Click mouse to unzoom'))
+ }
+
+class MapToolbar(BaseToolbar):
+ """!Map Display toolbar
+ """
+ def __init__(self, parent, toolSwitcher):
+ """!Map Display constructor
+
+ @param parent reference to MapFrame
+ """
+ BaseToolbar.__init__(self, parent=parent, toolSwitcher=toolSwitcher) # MapFrame
+
+ self.InitToolbar(self._toolbarData())
+ self._default = self.pointer
+
+ # optional tools
+ choices = [ _('2D view'), ]
+ self.toolId = { '2d' : 0 }
+ if self.parent.GetLayerManager():
+ log = self.parent.GetLayerManager().GetLogWindow()
+
+ if haveNviz:
+ choices.append(_('3D view'))
+ self.toolId['3d'] = 1
+ else:
+ from nviz.main import errorMsg
+ if self.parent.GetLayerManager():
+ log.WriteCmdLog(_('3D view mode not available'))
+ log.WriteWarning(_('Reason: %s') % str(errorMsg))
+
+ self.toolId['3d'] = -1
+
+ if haveVDigit:
+ choices.append(_('Digitize'))
+ if self.toolId['3d'] > -1:
+ self.toolId['vdigit'] = 2
+ else:
+ self.toolId['vdigit'] = 1
+ else:
+ from vdigit.main import errorMsg
+ if self.parent.GetLayerManager():
+ log.WriteCmdLog(_('Vector digitizer not available'))
+ log.WriteWarning(_('Reason: %s') % errorMsg)
+ log.WriteLog(_('Note that the wxGUI\'s vector digitizer is currently disabled '
+ '(hopefully this will be fixed soon). '
+ 'Please keep an eye out for updated versions of GRASS. '
+ 'In the meantime you can use "v.digit" from the Develop Vector menu.'), wrap = 60)
+
+ self.toolId['vdigit'] = -1
+
+ self.combo = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = choices,
+ style = wx.CB_READONLY, size = (110, -1))
+ self.combo.SetSelection(0)
+
+ self.comboid = self.AddControl(self.combo)
+ self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
+
+ # realize the toolbar
+ self.Realize()
+
+ # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+ self.combo.Hide()
+ self.combo.Show()
+
+ for tool in (self.pointer, self.query, self.pan, self.zoomIn, self.zoomOut):
+ self.toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self, tool=tool)
+
+ self.EnableTool(self.zoomBack, False)
+
+ self.FixSize(width = 90)
+
+ def _toolbarData(self):
+ """!Toolbar data"""
+ return self._getToolbarData((('displayMap', BaseIcons['display'],
+ self.parent.OnDraw),
+ ('renderMap', BaseIcons['render'],
+ self.parent.OnRender),
+ ('erase', BaseIcons['erase'],
+ self.parent.OnErase),
+ (None, ),
+ ('pointer', BaseIcons['pointer'],
+ self.parent.OnPointer,
+ wx.ITEM_CHECK),
+ ('query', MapIcons['query'],
+ self.parent.OnQuery,
+ wx.ITEM_CHECK),
+ ('pan', BaseIcons['pan'],
+ self.parent.OnPan,
+ wx.ITEM_CHECK),
+ ('zoomIn', BaseIcons['zoomIn'],
+ self.parent.OnZoomIn,
+ wx.ITEM_CHECK),
+ ('zoomOut', BaseIcons['zoomOut'],
+ self.parent.OnZoomOut,
+ wx.ITEM_CHECK),
+ ('zoomExtent', BaseIcons['zoomExtent'],
+ self.parent.OnZoomToMap),
+ ('zoomBack', BaseIcons['zoomBack'],
+ self.parent.OnZoomBack),
+ ('zoomMenu', BaseIcons['zoomMenu'],
+ self.parent.OnZoomMenu),
+ (None, ),
+ ('analyze', MapIcons['analyze'],
+ self.OnAnalyze),
+ (None, ),
+ ('overlay', BaseIcons['overlay'],
+ self.OnDecoration),
+ (None, ),
+ ('saveFile', BaseIcons['saveFile'],
+ self.parent.SaveToFile),
+ ('printMap', BaseIcons['print'],
+ self.parent.PrintMenu),
+ (None, ))
+ )
+ def InsertTool(self, data):
+ """!Insert tool to toolbar
+
+ @param data toolbar data"""
+ data = self._getToolbarData(data)
+ for tool in data:
+ self.CreateTool(*tool)
+ self.Realize()
+
+ self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+ self.parent._mgr.Update()
+
+ def RemoveTool(self, tool):
+ """!Remove tool from toolbar
+
+ @param tool tool id"""
+ self.DeleteTool(tool)
+
+ self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+ self.parent._mgr.Update()
+
+ def ChangeToolsDesc(self, mode2d):
+ """!Change description of zoom tools for 2D/3D view"""
+ if mode2d:
+ icons = BaseIcons
+ else:
+ icons = NvizIcons
+ for i, data in enumerate(self._data):
+ for tool in (('zoomIn', 'zoomOut')):
+ if data[0] == tool:
+ tmp = list(data)
+ tmp[4] = icons[tool].GetDesc()
+ self._data[i] = tuple(tmp)
+
+ def OnSelectTool(self, event):
+ """!Select / enable tool available in tools list
+ """
+ tool = event.GetSelection()
+
+ if tool == self.toolId['2d']:
+ self.ExitToolbars()
+ self.Enable2D(True)
+ self.ChangeToolsDesc(mode2d = True)
+
+ elif tool == self.toolId['3d'] and \
+ not (self.parent.MapWindow3D and self.parent.IsPaneShown('3d')):
+ self.ExitToolbars()
+ self.parent.AddNviz()
+
+ elif tool == self.toolId['vdigit'] and \
+ not self.parent.GetToolbar('vdigit'):
+ self.ExitToolbars()
+ self.parent.AddToolbar("vdigit")
+ self.parent.MapWindow.SetFocus()
+
+ def OnAnalyze(self, event):
+ """!Analysis tools menu
+ """
+ self._onMenu(((MapIcons["measure"], self.parent.OnMeasure),
+ (MapIcons["profile"], self.parent.OnProfile),
+ (MapIcons["scatter"], self.parent.OnScatterplot),
+ (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
+ (BaseIcons["histogramD"], self.parent.OnHistogram),
+ (MapIcons["vnet"], self.parent.OnVNet),
+ (MapIcons["interact_scatter"],
+ self.parent.OnIScatt)))
+
+ def OnDecoration(self, event):
+ """!Decorations overlay menu
+ """
+ self._onMenu(((MapIcons["addLegend"], lambda evt: self.parent.AddLegend()),
+ (MapIcons["addBarscale"], lambda evt: self.parent.AddBarscale()),
+ (MapIcons["addNorthArrow"], lambda evt: self.parent.AddArrow()),
+ (MapIcons["addText"], self.parent.OnAddText)))
+
+
+ def ExitToolbars(self):
+ if self.parent.GetToolbar('vdigit'):
+ self.parent.toolbars['vdigit'].OnExit()
+ if self.parent.GetLayerManager() and \
+ self.parent.GetLayerManager().IsPaneShown('toolbarNviz'):
+ self.parent.RemoveNviz()
+
+ def Enable2D(self, enabled):
+ """!Enable/Disable 2D display mode specific tools"""
+ for tool in (self.zoomMenu,
+ self.analyze,
+ self.printMap):
+ self.EnableTool(tool, enabled)
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/__init__.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,8 +1,11 @@
all = [
'dialogs',
+ 'controllers',
+ 'frame',
'gthreading',
'plots',
- 'sc_pl_core',
+ 'scatt_core',
'toolbars',
+ 'scatt_core',
'core_c',
]
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/controllers.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -4,6 +4,14 @@
@brief Controller layer for scatter plot tool.
Classes:
+ - controllers::ScattsManager
+ - controllers::PlotsRenderingManager
+ - controllers::CategoriesManager
+ - controllers::IMapWinDigitConnection
+ - controllers::IClassDigitConnection
+ - controllers::IMapDispConnection
+ - controllers::IClassConnection
+ - controllers::gThread
(C) 2013 by the GRASS Development Team
@@ -14,78 +22,129 @@
"""
import os
import sys
+from copy import deepcopy
-#TODO
+import time
+import threading
+import Queue
+from core.gconsole import EVT_CMD_DONE
+
import wx
-from core.gcmd import GException, GError, RunCommand
+from core.gcmd import GException, GError, GMessage, RunCommand
+from core.settings import UserSettings
+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
+from scatt_plot.scatt_core import Core, idBandsToidScatt
+from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
-from scatt_plot.sc_pl_core import Core, idBandsToidScatt
+from iclass.dialogs import IClassGroupDialog
-from scatt_plot.gthreading import gThread
-from core.gconsole import EVT_CMD_DONE
-
+import grass.script as grass
from grass.pydispatch.signal import Signal
-class ScattsManager(wx.EvtHandler):
- def __init__(self, guiparent, giface, iclass_mapwin):
+class ScattsManager:
+ """!Main controller."""
+ def __init__(self, guiparent, giface, iclass_mapwin = None):
#TODO remove iclass parameter
- wx.EvtHandler.__init__(self)
self.giface = giface
self.mapDisp = giface.GetMapDisplay()
- self.mapWin = giface.GetMapWindow()
if iclass_mapwin:
self.mapWin = iclass_mapwin
+ else:
+ self.mapWin = giface.GetMapWindow()
self.guiparent = guiparent
+ self.show_add_scatt_plot = False
+
self.core = Core()
- self.scatts_dt = self.core.GetScattsData()
self.cats_mgr = CategoriesManager(self, self.core)
+ self.render_mgr = PlotsRenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
- self.thread = gThread(self);
+ self.thread = gThread();
- self.scatt_plts = {}
- self.added_cats_rasts = {}
+ self.plots = {}
- self.cats_to_update = []
+ self.plot_mode = None
+ self.pol_sel_mode = [False, None]
- self.edit_cat_type = None
-
self.data_set = False
+ self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
+
+ self.renderingStarted = self.render_mgr.renderingStarted
+ self.renderingFinished = self.render_mgr.renderingFinished
+
+ self.computingStarted = Signal("ScattsManager.computingStarted")
+
if iclass_mapwin:
- self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
+ self.digit_conn = IClassDigitConnection(self, self.mapWin, self.core.CatRastUpdater())
self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
else:
- self.mapWin_conn = None
- self.iclass_conn = None
+ self.digit_conn = IMapWinDigitConnection()
+ self.iclass_conn = IMapDispConnection(scatt_mgr=self, cats_mgr=self.cats_mgr, giface=self.giface)
- self.tasks_pids = {
- 'add_scatt' : [],
- 'set_data' : -1,
- 'set_edit_cat_data' : -1,
- 'mapwin_conn' : [],
- 'render_plots' : -1
- }
+ self._initSettings()
- self.Bind(EVT_CMD_DONE, self.OnThreadDone)
+ self.modeSet = Signal("ScattsManager.mondeSet")
- def SetData(self, bands):
+ def CleanUp(self):
+ self.thread.Run(callable=self.core.CleanUp)
+ self.added_cats_rasts = []
+
+ self.cats_mgr.CleanUp()
+ for scatt_id, scatt in self.plots.items():
+ if scatt['scatt']:
+ scatt['scatt'].CleanUp()
+
+ def _initSettings(self):
+ """!Initialization of settings (if not already defined)"""
+ # initializes default settings
+ initSettings = [
+ ['selection', 'sel_pol', (255,255,0)],
+ ['selection', 'sel_pol_vertex', (255,0,0)],
+ ['selection', 'sel_area', (0,255,19)],
+ ['selection', "snap_tresh", 10],
+ ['selection', 'sel_area_opacty', 50],
+ ['ellipses', 'show_ellips', True],
+ ]
+
+ for init in initSettings:
+ UserSettings.ReadSettingsFile()
+ UserSettings.Append(dict = UserSettings.userSettings,
+ group ='scatt',
+ key = init[0],
+ subkey =init[1],
+ value = init[2],
+ overwrite = False)
+
+ def SetData(self):
+ self.iclass_conn.SetData()
+
+ def SetBands(self, bands):
+ self.busy = wx.BusyInfo(_("Loading data..."))
+ self.CleanUp()
self.data_set = False
- self.tasks_pids['set_data'] = self.thread.GetId()
- self.thread.Run(callable = self.core.SetData, bands = bands)
+
+ if self.show_add_scatt_plot:
+ show_add=True
+ else:
+ show_add=False
- #for i in range(1):
- # self.AddScattPlot(i)
+ self.thread.Run(callable=self.core.SetData, bands=bands,
+ ondone=self.SetDataDone, userdata={"show_add" : show_add})
def SetDataDone(self, event):
+ del self.busy
self.data_set = True
- self.cats_mgr.InitCoreCats()
+ self.iclass_conn.SyncCats()
+ if event.userdata['show_add']:
+ self.AddScattPlot()
+
def OnOutput(self, event):
"""!Print thread output according to debug level.
"""
@@ -94,186 +153,310 @@
def GetBands(self):
return self.core.GetBands()
- def AddScattPlot(self, scatt_id):
+ def AddScattPlot(self):
+ if not self.data_set and self.iclass_conn:
+ self.show_add_scatt_plot = True
+ self.iclass_conn.SetData()
+ self.show_add_scatt_plot = False
+ return
+ if not self.data_set:
+ GError(_('No data set.'))
+ return
- self.tasks_pids['add_scatt'].append(self.thread.GetId())
- self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
+ self.computingStarted.emit()
- def RenderScattPlts(self):
+ bands = self.core.GetBands()
- cats_attrs = self.cats_mgr.GetCategoriesAttrs()
- for scatt_id, scatt in self.scatt_plts.iteritems():
- scatt_dt = self.scatts_dt.GetScatt(scatt_id)
- scatt.Plot(scatt_dt, cats_attrs)
+ #added_bands_ids = []
+ #for scatt_id in self.plots):
+ # added_bands_ids.append[idBandsToidScatt(scatt_id)]
+ self.digit_conn.Update()
+ dlg = AddScattPlotDialog(parent = self.guiparent,
+ bands = bands,
+ added_scatts_ids = self.plots.keys())
+ if dlg.ShowModal() == wx.ID_OK:
- def AddScattPlotDone(self, event):
-
- scatt_id = event.kwds['scatt_id']
+ scatt_ids = []
+ sel_bands = dlg.GetBands()
- #TODO guiparent - not very good
- self.scatt_plts[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
+ for b_1, b_2 in sel_bands:
+
+ transpose = False
+ if b_1 > b_2:
+ transpose = True
+ tmp_band = b_2
+ b_2 = b_1
+ b_1 = tmp_band
- if self.edit_cat_type:
- self.scatt_plts[scatt_id].StartCategoryEdit()
+ scatt_id = idBandsToidScatt(b_1, b_2, len(bands))
+ if self.plots.has_key(scatt_id):
+ continue
- scatt_dt = self.scatts_dt.GetScatt(scatt_id)
+ self.plots[scatt_id] = {'transpose' : transpose,
+ 'scatt' : None}
+ scatt_ids.append(scatt_id)
- cats_attrs = self.cats_mgr.GetCategoriesAttrs()
+ self._addScattPlot(scatt_ids)
+
+ dlg.Destroy()
+
+ def _addScattPlot(self, scatt_ids):
+ self.render_mgr.NewRunningProcess()
+ self.thread.Run(callable=self.core.AddScattPlots,
+ scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
- self.scatt_plts[scatt_id].Plot(scatt_dt, cats_attrs)
- #for scatt in scatt_dt.itervalues():
- # del scatt
- self.scatt_plts[scatt_id].GetParent().Show()
- print "ok %d" % scatt_id
+ def AddScattPlotDone(self, event):
- def CleanUp(self):
- self.core.CleanUp()
+ scatt_ids = event.kwds['scatt_ids']
+ for s_id in scatt_ids:
+ trans = self.plots[s_id]['transpose']
- for scatt_id, scatt in self.scatt_plts.items():
- scatt.CleanUp()
- del self.scatt_plts[scatt_id]
+ self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(scatt_id=s_id,
+ transpose=trans)
- def OnThreadDone(self, event):
-
- if event.exception:
- GError(str(event.exception))
- return
+ self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
+ self.plots[s_id]['scatt'].cursorMove.connect(lambda x, y, scatt_id:
+ self.cursorPlotMove.emit(x=x, y=y,
+ scatt_id=scatt_id))
- if event.pid in self.tasks_pids['mapwin_conn']:
- self.tasks_pids['mapwin_conn'].remove(event.pid)
- updated_cats = event.ret
+ if self.plot_mode:
+ self.plots[s_id]['scatt'].SetMode(self.plot_mode)
+ self.plots[s_id]['scatt'].ZoomToExtend()
- for cat in updated_cats:
- if cat not in self.cats_to_update:
- self.cats_to_update.append(cat)
+ self.render_mgr.RunningProcessDone()
- if not self.tasks_pids['mapwin_conn']:
- self.tasks_pids['render_plots'] = self.thread.GetId()
- self.thread.Run(callable = self.core.ComputeCatScatts,
- cats_ids = self.cats_to_update[:])
- del self.cats_to_update[:]
-
- return
+ def PlotClosed(self, scatt_id):
+ del self.plots[scatt_id]
- if self.tasks_pids['render_plots'] == event.pid:
- #TODO how to put it to the thread - kils gui
- self.RenderScattPlts()
- return
-
- if event.pid in self.tasks_pids['add_scatt']:
- self.AddScattPlotDone(event)
- return
+ def SetPlotsMode(self, mode):
- if self.tasks_pids['set_data'] == event.pid:
- self.SetDataDone(event)
- return
-
- if self.tasks_pids['set_edit_cat_data'] == event.pid:
- self.SetEditCatDataDone(event)
- return
+ self.plot_mode = mode
+ for scatt in self.plots.itervalues():
+ if scatt['scatt']:
+ scatt['scatt'].SetMode(mode)
- def EditCat(self, edit_type):
+ self.modeSet.emit(mode = mode)
- self.edit_cat_type = edit_type
+ def ActivateSelectionPolygonMode(self, activate):
+ self.pol_sel_mode[0] = activate
+ for scatt in self.plots.itervalues():
+ if not scatt['scatt']:
+ continue
+ scatt['scatt'].SetSelectionPolygonMode(activate)
+ if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
+ self.SetPlotsMode(None)
- if self.edit_cat_type:
- for scatt in self.scatt_plts.itervalues():
- scatt.StopCategoryEdit()
- scatt.StartCategoryEdit()
+ self.render_mgr.RunningProcessDone()
+ return activate
- else:
- for scatt in self.scatt_plts.itervalues():
- scatt.StopCategoryEdit()
+ def ProcessSelectionPolygons(self, process_mode):
+ scatts_polygons = {}
+ for scatt_id, scatt in self.plots.iteritems():
+ if not scatt['scatt']:
+ continue
+ coords = scatt['scatt'].GetCoords()
+ if coords is not None:
+ scatts_polygons[scatt_id] = coords
- def SetEditCatData(self, scatt_id, bbox):
-
+ if not scatts_polygons:
+ return
+
value = 1
- if self.edit_cat_type == 'remove':
+ if process_mode == 'remove':
value = 0
- self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
+ sel_cat_id = self.cats_mgr.GetSelectedCat()
+ if not sel_cat_id:
+ dlg = wx.MessageDialog(parent = self.guiparent,
+ message = _("In order to select arrea in scatter plot, "
+ "you have to select class first.\n\n"
+ "There is no class yet, "
+ "do you want to create one?"),
+ caption = _("No class selected"),
+ style = wx.YES_NO)
+ if dlg.ShowModal() == wx.ID_YES:
+ self.iclass_conn.EmptyCategories()
sel_cat_id = self.cats_mgr.GetSelectedCat()
-
- self.thread.Run(callable = self.core.SetEditCatData,
- scatt_id = scatt_id,
+ if not sel_cat_id:
+ return
+
+ for scatt in self.plots.itervalues():
+ if scatt['scatt']:
+ scatt['scatt'].SetEmpty()
+
+ self.computingStarted.emit()
+
+ self.render_mgr.NewRunningProcess()
+ self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
+ self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
+
+ self.thread.Run(callable = self.core.UpdateCategoryWithPolygons,
cat_id = sel_cat_id,
- bbox = bbox,
- value = value)
-
+ scatts_pols = scatts_polygons,
+ value = value, ondone=self.SetEditCatDataDone)
+
def SetEditCatDataDone(self, event):
+ self.render_mgr.RunningProcessDone()
+ if event.exception:
+ GError(_("Error occured during computation of scatter plot category:\n%s"),
+ parent = self.guiparent, showTraceback = False)
- self.RenderScattPlts()
+ cat_id = event.ret
+ self.iclass_conn.RenderCatRast(cat_id)
+
+ def SettingsUpdated(self, chanaged_setts):
+ self.render_mgr.RenderRequest()
- cat_id = event.kwds["cat_id"]
+ #['ellipses', 'show_ellips']
+ def GetCategoriesManager(self):
+ return self.cats_mgr
- cat_rast = self.core.GetCatRastOut(cat_id)
+class PlotsRenderingManager:
+ """!Manages rendering of scatter plot.
+ @todo still space for optimalization
+ """
+ def __init__(self, scatt_mgr, cats_mgr, core):
+ self.scatt_mgr = scatt_mgr
+ self.cats_mgr = cats_mgr
+ self.core = core
+ self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
- if cat_rast not in self.added_cats_rasts.values():
+ self.runningProcesses = 0
- cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+ self.data_to_render = {}
+ self.render_queue = []
- #TODO use standard raster lib
- name = "cat_%d" % cat_id
- ret, err_msg = RunCommand('r.external',
- output = name,
- getErrorMsg = True,
- input = cat_rast,
- flags = "o",
- overwrite = True)
+ self.cat_ids = []
+ self.cat_cond_ids = []
- if ret != 0:
- GError(_("r.external failed\n%s" % err_msg))
+ self.renderingStarted = Signal("ScattsManager.renderingStarted")
+ self.renderingFinished = Signal("ScattsManager.renderingFinished")
- region = self.core.GetRegion()
- ret, err_msg = RunCommand('r.region',
- map = name,
- getErrorMsg = True,
- n = "%f" % region['n'],
- s = "%f" % region['s'],
- e = "%f" % region['e'],
- w = "%f" % region['w'],
- )
+ def AddRenderRequest(self, scatts):
+ for scatt_id, cat_ids in scatts:
+ if not self.data_to_render.has_key[scatt_id]:
+ self.data_to_render = cat_ids
+ else:
+ for c in cat_ids:
+ if c not in self.data_to_render[scatt_id]:
+ self.data_to_render[scatt_id].append(c)
- region = self.core.GetRegion()
- ret, err_msg = RunCommand('r.colors',
- map = name,
- rules = "-",
- stdin = "1 %s" % cats_attrs["color"],
- getErrorMsg = True)
+ def NewRunningProcess(self):
+ self.runningProcesses += 1
- if ret != 0:
- GError(_("r.region failed\n%s" % err_msg))
+ def RunningProcessDone(self):
+ self.runningProcesses -= 1
+ if self.runningProcesses <= 1:
+ self.RenderScattPlts()
- self.mapWin.Map.AddLayer(ltype = "raster", name = "cat_%d" % cat_id, render = True,
- command = ["d.rast", "map=%s" % name, "values=1"])
+ def RenderRequest(self):
+ if self.runningProcesses <= 1:
+ self.RenderScattPlts()
- #elif self.giface.GetLayerTree():
- # self.giface.GetLayerTree().AddLayer("raster", lname = "cat_%d" % cat_id, lchecked = True,
- # lcmd = ["d.rast", "map=%s" % name, "values=1"])
+ def CategoryChanged(self, cat_ids):
+ for c in cat_ids:
+ if c not in self.cat_ids:
+ self.cat_ids.append(c)
- if ret != 0:
- GError(_("r.region failed\n%s" % err_msg))
+ def CategoryCondsChanged(self, cat_ids):
+ for c in cat_ids:
+ if c not in self.cat_cond_ids:
+ self.cat_cond_ids.append(c)
- self.added_cats_rasts[cat_id] = cat_rast
+ def RenderScattPlts(self, scatt_ids = None):
+ if len(self.render_queue) > 1:
+ return
+
+ self.renderingStarted.emit()
+ self.render_queue.append(self.scatt_mgr.thread.GetId())
- self.giface.updateMap.emit()
+ cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
+ cats = self.cats_mgr.GetCategories()[:]
+ self.scatt_mgr.thread.Run(callable=self._renderscattplts, scatt_ids=scatt_ids,
+ cats=cats, cats_attrs=cats_attrs,
+ ondone=self.RenderingDone)
- def DigitDataChanged(self, vectMap, digit):
+ def _renderscattplts(self, scatt_ids, cats, cats_attrs):
+ cats.reverse()
+ cats.insert(0, 0)
+ for i_scatt_id, scatt in self.scatt_mgr.plots.items():
+ if scatt_ids is not None and \
+ i_scatt_id not in scatt_ids:
+ continue
+ if not scatt['scatt']:
+ continue
+
+ scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
+ if self._showConfEllipses():
+ ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id, cats_attrs)
+ else:
+ ellipses_dt = {}
+
+ for c in scatt_dt.iterkeys():
+ try:
+ self.cat_ids.remove(c)
+ scatt_dt[c]['render']=True
+ except:
+ scatt_dt[c]['render']=False
+
+ if self.scatt_mgr.pol_sel_mode[0]:
+ self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
+
+ scatt['scatt'].Plot(cats_order=cats,
+ scatts=scatt_dt,
+ ellipses=ellipses_dt,
+ styles=cats_attrs)
+
+ def RenderingDone(self, event):
+ self.render_queue.remove(event.pid)
+ if not self.render_queue:
+ self.renderingFinished.emit()
+
+ def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
+
+ cat_id = self.cats_mgr.GetSelectedCat()
+ if not cat_id:
+ return
+
+ sel_a_cat_id = -1
+
+ s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
+ if not s:
+ return
+
+ cats_order.append(sel_a_cat_id)
+
+ col = UserSettings.Get(group='scatt',
+ key='selection',
+ subkey='sel_area')
+
+ col = ":".join(map(str, col))
+ opac = UserSettings.Get(group='scatt',
+ key='selection',
+ subkey='sel_area_opacty') / 100.0
+
+ cats_attrs[sel_a_cat_id] = {'color' : col,
+ 'opacity' : opac,
+ 'show' : True}
+
+ scatt_dt[sel_a_cat_id] = s[cat_id]
- if self.mapWin_conn:
- self.mapWin_conn.DigitDataChanged(vectMap, digit)
- return 1
- else:
- return 0
+ scatt_dt[sel_a_cat_id]['render'] = False
+ if cat_id in self.cat_cond_ids:
+ scatt_dt[sel_a_cat_id]['render'] = True
+ self.cat_cond_ids.remove(cat_id)
- def GetCategoriesManager(self):
- return self.cats_mgr
+ def _showConfEllipses(self):
+ return UserSettings.Get(group='scatt',
+ key="ellipses",
+ subkey="show_ellips")
class CategoriesManager:
-
+ """!Manages categories list of scatter plot.
+ """
def __init__(self, scatt_mgr, core):
self.core = core
@@ -282,27 +465,48 @@
self.cats = {}
self.cats_ids = []
- self.sel_cat_id = -1
+ self.sel_cat_id = None
+ self.exportRaster = None
+
self.initialized = Signal('CategoriesManager.initialized')
+ self.cleanuped = Signal('CategoriesManager.cleanuped')
self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
self.deletedCategory = Signal('CategoriesManager.deletedCategory')
self.addedCategory = Signal('CategoriesManager.addedCategory')
- def Clear(self):
-
+ def CleanUp(self):
+
self.cats.clear()
del self.cats_ids[:]
- self.sel_cat_id = -1
+ self.sel_cat_id = None
- def InitCoreCats(self):
- if self.scatt_mgr.data_set:
- for cat_id in self.cats_ids:
- self.core.AddCategory(cat_id)
+ def ChangePosition(self, cat_id, new_pos):
+ if new_pos >= len(self.cats_ids):
+ return False
- def AddCategory(self, cat_id = None, name = None, color = None):
+ try:
+ pos = self.cats_ids.index(cat_id)
+ except:
+ return False
+ if pos > new_pos:
+ pos -= 1
+
+ self.cats_ids.remove(cat_id)
+
+ self.cats_ids.insert(new_pos, cat_id)
+
+ self.scatt_mgr.render_mgr.RenderRequest()
+ return True
+
+ def _addCategory(self, cat_id):
+ self.scatt_mgr.thread.Run(callable = self.core.AddCategory,
+ cat_id = cat_id)
+
+ def AddCategory(self, cat_id = None, name = None, color = None, nstd = None):
+
if cat_id is None:
if self.cats_ids:
cat_id = max(self.cats_ids) + 1
@@ -310,37 +514,62 @@
cat_id = 1
if self.scatt_mgr.data_set:
- ret = self.core.AddCategory(cat_id)
- if ret < 0: #TODO
- return -1;
+ self.scatt_mgr.thread.Run(callable = self.core.AddCategory,
+ cat_id = cat_id)
+ #TODO check number of cats
+ #if ret < 0: #TODO
+ # return -1;
self.cats[cat_id] = {
- 'name' : _('Category %s' % cat_id ),
- 'color' : "0:0:0"
+ 'name' : 'class_%d' % cat_id,
+ 'color' : "0:0:0",
+ 'opacity' : 1.0,
+ 'show' : True,
+ 'nstd' : 1.0,
}
- self.cats_ids.append(cat_id)
+ self.cats_ids.insert(0, cat_id)
+
if name is not None:
self.cats[cat_id]["name"] = name
if color is not None:
self.cats[cat_id]["color"] = color
+ if nstd is not None:
+ self.cats[cat_id]["nstd"] = nstd
+
self.addedCategory.emit(cat_id = cat_id,
name = self.cats[cat_id]["name"],
color = self.cats[cat_id]["color"] )
return cat_id
def SetCategoryAttrs(self, cat_id, attrs_dict):
+ render = False
+ update_cat_rast = []
+
for k, v in attrs_dict.iteritems():
+ if not render and k in ['color', 'opacity', 'show', 'nstd']:
+ render = True
+ if k in ['color', 'name']:
+ update_cat_rast.append(k)
+
self.cats[cat_id][k] = v
+ if render:
+ self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
+ self.scatt_mgr.render_mgr.RenderRequest()
+
+ if update_cat_rast:
+ self.scatt_mgr.iclass_conn.UpdateCategoryRaster(cat_id, update_cat_rast)
+
self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
def DeleteCategory(self, cat_id):
if self.scatt_mgr.data_set:
- self.core.DeleteCategory(cat_id)
+ self.scatt_mgr.thread.Run(callable = self.core.DeleteCategory,
+ cat_id = cat_id)
del self.cats[cat_id]
self.cats_ids.remove(cat_id)
@@ -349,6 +578,8 @@
#TODO emit event?
def SetSelectedCat(self, cat_id):
self.sel_cat_id = cat_id
+ if self.scatt_mgr.pol_sel_mode[0]:
+ self.scatt_mgr.render_mgr.RenderRequest()
def GetSelectedCat(self):
return self.sel_cat_id
@@ -364,7 +595,50 @@
def GetCategories(self):
return self.cats_ids[:]
-class MapWinConnection:
+ def SetCategoryPosition(self):
+ if newindex > oldindex:
+ newindex -= 1
+
+ self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
+
+ def ExportCatRast(self, cat_id):
+
+ cat_attrs = self.GetCategoryAttrs(cat_id)
+
+ dlg = ExportCategoryRaster(parent=self.scatt_mgr.guiparent,
+ rasterName=self.exportRaster,
+ title=_("Export scatter plot raster of class <%s>") % cat_attrs['name'])
+
+ if dlg.ShowModal() == wx.ID_OK:
+ self.exportCatRast = dlg.GetRasterName()
+ dlg.Destroy()
+
+ self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
+ userdata={'name' : cat_attrs['name']},
+ cat_id=cat_id,
+ rast_name=self.exportCatRast,
+ ondone=self.OnExportCatRastDone)
+
+ def OnExportCatRastDone(self, event):
+ ret, err = event.ret
+ if ret == 0:
+ cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
+ GMessage(_("Scatter plot raster of class <%s> exported to raster map <%s>.") %
+ (event.userdata['name'], event.kwds['rast_name']))
+ else:
+ GMessage(_("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") %
+ (event.userdata['name'], event.kwds['rast_name'], err))
+
+
+class IMapWinDigitConnection:
+ """!Manage comunication of the scatter plot with digitizer in mapwindow (does not work).
+ """
+ def Update(self):
+ pass
+
+class IClassDigitConnection:
+ """!Manages comunication of the scatter plot with digitizer in wx.iclass.
+ """
def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
self.mapWin = mapWin
self.vectMap = None
@@ -372,107 +646,269 @@
self.scatt_mgr = scatt_mgr
self.cats_mgr = scatt_mgr.cats_mgr
+ self.cats_to_update = []
+ self.tasks_pids = {'mapwin_conn' : []}
+
self.thread = self.scatt_mgr.thread
#TODO
self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
- #def ChangeMap(self, vectMap, layers_cats):
- # self.vectMap = vectMap
- # self.layers_cats = layers_cats
+ def Update(self):
+ self.thread.Run(callable=self.scatt_rast_updater.SyncWithMap)
- #ret, region, msg = RunCommand("v.to.rast",
- # flags = "gp",
- # getErrorMsg = True,
- # read = True)
-
def _connectSignals(self):
self.digit.featureAdded.connect(self.AddFeature)
self.digit.areasDeleted.connect(self.DeleteAreas)
self.digit.featuresDeleted.connect(self.DeleteAreas)
- self.digit.vertexMoved.connect(self.ChangeVertex)
- self.digit.vertexRemoved.connect(self.ChangeVertex)
- self.digit.lineEdited.connect(self.ChangeVertex)
- self.digit.featuresMoved.connect(self.MoveFeatures)
+ self.digit.vertexMoved.connect(self.EditedFeature)
+ self.digit.vertexRemoved.connect(self.EditedFeature)
+ self.digit.lineEdited.connect(self.EditedFeature)
+ self.digit.featuresMoved.connect(self.EditedFeature)
- def AddFeature(self, new_geom, cat):
+ def AddFeature(self, new_bboxs, new_areas_cats):
if not self.scatt_mgr.data_set:
return
+ self.scatt_mgr.computingStarted.emit()
- self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable = self.scatt_rast_updater.AddedFeature,
- new_geom = new_geom,
- cat = cat)
+ self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
+ new_bboxs = new_bboxs,
+ old_bboxs = [],
+ old_areas_cats = [],
+ new_areas_cats = new_areas_cats,
+ ondone=self.OnDone)
- def DeleteAreas(self, old_geoms, old_areas_cats):
+ def DeleteAreas(self, old_bboxs, old_areas_cats):
if not self.scatt_mgr.data_set:
return
+ self.scatt_mgr.computingStarted.emit()
- self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable = self.scatt_rast_updater.DeletedAreas,
- old_geoms = old_geoms,
- old_areas_cats = old_areas_cats)
-
- def ChangeVertex(self, new_geom, new_areas_cats, old_geom, old_areas_cats):
- if not self.scatt_mgr.data_set:
- return
-
- self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable = self.scatt_rast_updater.ChangedVertex,
- new_geom = new_geom,
- old_geom = old_geom,
+ self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
+ new_bboxs = [],
+ old_bboxs = old_bboxs,
old_areas_cats = old_areas_cats,
- new_areas_cats = new_areas_cats)
+ new_areas_cats = [],
+ ondone=self.OnDone)
- def MoveFeatures(self, old_geoms, old_areas_cats, new_areas_cats, move):
+ def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
if not self.scatt_mgr.data_set:
return
+ self.scatt_mgr.computingStarted.emit()
- self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
- self.thread.Run(callable = self.scatt_rast_updater.MovedFeatures,
- move = move,
- old_geoms = old_geoms,
+ self.tasks_pids['mapwin_conn'].append(self.thread.GetId())
+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
+ new_bboxs = new_bboxs,
+ old_bboxs = old_bboxs,
old_areas_cats = old_areas_cats,
- new_areas_cats = new_areas_cats)
+ new_areas_cats = new_areas_cats,
+ ondone=self.OnDone)
def DigitDataChanged(self, vectMap, digit):
self.digit = digit
self.vectMap = vectMap
+ self.digit.EmitSignals(emit = True)
+
self.scatt_rast_updater.SetVectMap(vectMap)
self._connectSignals()
+ def OnDone(self, event):
+ self.tasks_pids['mapwin_conn'].remove(event.pid)
+ updated_cats = event.ret
+ for cat in updated_cats:
+ if cat not in self.cats_to_update:
+ self.cats_to_update.append(cat)
+
+ if not self.tasks_pids['mapwin_conn']:
+ self.tasks_pids['render_plots'] = self.thread.GetId()
+ self.thread.Run(callable = self.scatt_mgr.core.ComputeCatsScatts,
+ cats_ids = self.cats_to_update[:], ondone=self.Render)
+ del self.cats_to_update[:]
+
+ def Render(self, event):
+ self.scatt_mgr.render_mgr.RenderScattPlts()
+
+class IMapDispConnection:
+ """!Manage comunication of the scatter plot with mapdisplay in mapwindow.
+ """
+ def __init__(self, scatt_mgr, cats_mgr, giface):
+ self.scatt_mgr = scatt_mgr
+ self.cats_mgr = cats_mgr
+ self.set_g = {'group' : None, 'subg' : None}
+ self.giface = giface
+ self.added_cats_rasts = {}
+
+ def SetData(self):
+ dlg = IClassGroupDialog(self.scatt_mgr.guiparent,
+ group=self.set_g['group'],
+ subgroup=self.set_g['subg'])
+
+ bands = []
+ while True:
+ if dlg.ShowModal() == wx.ID_OK:
+
+ bands = dlg.GetGroupBandsErr(parent=self.scatt_mgr.guiparent)
+ if bands:
+ name, s = dlg.GetData()
+ group = grass.find_file(name = name, element = 'group')
+ self.set_g['group'] = group['name']
+ self.set_g['subg'] = s
+
+ break
+ else:
+ break
+
+ dlg.Destroy()
+
+ if bands:
+ self.scatt_mgr.SetBands(bands)
+
+ def EmptyCategories(self):
+ return None
+
+ def SyncCats(self, cats_ids = None):
+ pass
+
+ def UpdateCategoryRaster(self, cat_id, attrs, render = True):
+
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+ if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
+ return
+ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+ if "color" in attrs:
+ ret, err_msg = RunCommand('r.colors',
+ map=cat_rast,
+ rules="-",
+ stdin="1 %s" % cats_attrs["color"],
+ getErrorMsg=True)
+
+ if ret != 0:
+ GError("r.colors failed\n%s" % err_msg)
+ if render:
+ self.giface.updateMap.emit()
+
+ if "name" in attrs:
+ #TODO hack
+ self.giface.GetLayerList()._tree.SetItemText(self.added_cats_rasts[cat_id],
+ cats_attrs['name'])
+ cats_attrs["name"]
+
+ def RenderCatRast(self, cat_id):
+
+ if not cat_id in self.added_cats_rasts.iterkeys():
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+ cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
+ self.UpdateCategoryRaster(cat_id, ['color'], render = False)
+
+ cmd = ['d.rast', 'map=%s' % cat_rast]
+ #HACK
+ layer = self.giface.GetLayerList()._tree.AddLayer(ltype="raster",
+ lname=cat_name,
+ lcmd=cmd,
+ lchecked=True)
+ self.added_cats_rasts[cat_id] = layer
+ else: #TODO settings
+ self.giface.updateMap.emit()
+
class IClassConnection:
+ """!Manage comunication of the scatter plot with mapdisplay in wx.iclass.
+ """
def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
self.iclass_frame = iclass_frame
- self.stats_data = self.iclass_frame.stats_data
- self.cats_mgr = cats_mgr
- self.scatt_mgr = scatt_mgr
+ self.stats_data = self.iclass_frame.stats_data
+ self.cats_mgr = cats_mgr
+ self.scatt_mgr= scatt_mgr
+ self.added_cats_rasts = []
self.stats_data.statisticsAdded.connect(self.AddCategory)
self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
self.stats_data.statisticsSet.connect(self.SetCategory)
+ self.iclass_frame.groupSet.connect(self.GroupSet)
+
self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
self.cats_mgr.addedCategory.connect(self.AddStatistics)
+ self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
+
self.SyncCats()
- def SyncCats(self):
+ def UpdateCategoryRaster(self, cat_id, attrs, render = True):
+ if not self.scatt_mgr.data_set:
+ return
+
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+ if not cat_rast:
+ return
+
+ if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
+ return
+ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+ train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
+
+ if "color" in attrs:
+ ret, err_msg = RunCommand('r.colors',
+ map=cat_rast,
+ rules="-",
+ stdin="1 %s" % cats_attrs["color"],
+ getErrorMsg=True)
+
+ if ret != 0:
+ GError("r.colors failed\n%s" % err_msg)
+ if render:
+ train_mgr.Render()
+
+ if "name" in attrs:
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+ train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
+ cats_attrs["name"]
+
+ def RenderCatRast(self, cat_id):
+
+ train_mgr, preview_mgr = self.iclass_frame.GetMapManagers()
+ if not cat_id in self.added_cats_rasts:
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+
+ cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
+ self.UpdateCategoryRaster(cat_id, ['color'], render = False)
+ train_mgr.AddLayer(cat_rast, alias = cat_name)
+
+ self.added_cats_rasts.append(cat_id)
+ else: #TODO settings
+ train_mgr.Render()
+
+ def SetData(self):
+ self.iclass_frame.AddBands()
+
+ def EmptyCategories(self):
+ self.iclass_frame.OnCategoryManager(None)
+
+ def SyncCats(self, cats_ids = None):
self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
cats = self.stats_data.GetCategories()
for c in cats:
+ if cats_ids and c not in cats_ids:
+ continue
stats = self.stats_data.GetStatistics(c)
- self.cats_mgr.AddCategory(c, stats.name, stats.color)
+ self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
self.cats_mgr.addedCategory.connect(self.AddStatistics)
+ def CategoryChanged(self, cat):
+ self.cats_mgr.SetSelectedCat(cat)
+
def AddCategory(self, cat, name, color):
self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
- self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
+ stats = self.stats_data.GetStatistics(cat)
+ self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color, nstd = stats.nstd)
self.cats_mgr.addedCategory.connect(self.AddStatistics)
def DeleteCategory(self, cat):
@@ -480,7 +916,6 @@
self.cats_mgr.DeleteCategory(cat)
self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-
def DeletAllCategories(self):
self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
@@ -493,7 +928,8 @@
self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
cats_attr = {}
- for attr in ['name', 'color']:
+
+ for attr in ['name', 'color', 'nstd']:
if stats.has_key(attr):
cats_attr[attr] = stats[attr]
@@ -516,3 +952,117 @@
self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
self.stats_data.DeleteStatistics(cat_id)
self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
+
+ def GroupSet(self, group, subgroup):
+ kwargs = {}
+ if subgroup:
+ kwargs['subgroup'] = subgroup
+
+ res = RunCommand('i.group',
+ flags = 'g',
+ group = group,
+ read = True, **kwargs).strip()
+
+ if res.split('\n')[0]:
+ bands = res.split('\n')
+ self.scatt_mgr.SetBands(bands)
+
+class gThread(threading.Thread, wx.EvtHandler):
+ """!Thread for scatter plot backend"""
+ requestId = 0
+
+ def __init__(self, requestQ=None, resultQ=None, **kwds):
+ wx.EvtHandler.__init__(self)
+
+ threading.Thread.__init__(self, **kwds)
+
+ if requestQ is None:
+ self.requestQ = Queue.Queue()
+ else:
+ self.requestQ = requestQ
+
+ if resultQ is None:
+ self.resultQ = Queue.Queue()
+ else:
+ self.resultQ = resultQ
+
+ self.setDaemon(True)
+
+ self._want_abort_all = False
+
+ self.Bind(EVT_CMD_DONE, self.OnDone)
+ self.start()
+
+ def Run(self, *args, **kwds):
+ """!Run command in queue
+
+ @param args unnamed command arguments
+ @param kwds named command arguments
+
+ @return request id in queue
+ """
+ gThread.requestId += 1
+ self.requestQ.put((gThread.requestId, args, kwds))
+
+ return gThread.requestId
+
+ def GetId(self):
+ """!Get id for next command"""
+ return gThread.requestId + 1
+
+ def SetId(self, id):
+ """!Set starting id"""
+ gThread.requestId = id
+
+ def run(self):
+ os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
+ while True:
+ requestId, args, kwds = self.requestQ.get()
+ for key in ('callable', 'ondone', 'userdata'):
+ if key in kwds:
+ vars()[key] = kwds[key]
+ del kwds[key]
+ else:
+ vars()[key] = None
+
+ requestTime = time.time()
+
+ #if self._want_abort_all and self.requestCmd is not None:
+ # self.requestCmd.abort()
+ # if self.requestQ.empty():
+ # self._want_abort_all = False
+
+ # prepare
+ ret = None
+ exception = None
+ time.sleep(.1)
+
+ #to
+ #try:
+ ret = vars()['callable'](*args, **kwds)
+ #except Exception as e:
+ # exception = e;
+
+ self.resultQ.put((requestId, ret))
+
+ event = wxCmdDone(ondone=vars()['ondone'],
+ kwds=kwds,
+ args=args, #TODO expand args to kwds
+ ret=ret,
+ exception=exception,
+ userdata=vars()['userdata'],
+ pid=requestId)
+
+ # send event
+ wx.PostEvent(self, event)
+
+ def OnDone(self, event):
+ if event.ondone:
+ event.ondone(event)
+
+ def abort(self, abortall=True):
+ """!Abort command(s)"""
+ if abortall:
+ self._want_abort_all = True
+ if self.requestQ.empty():
+ self._want_abort_all = False
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/core_c.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,10 +1,8 @@
"""!
@package scatt_plot.scatt_plot
- at brief Functions which work with scatter plot c code.
+ at brief Wrappers for scatter plot C backend.
-Classes:
-
(C) 2013 by the GRASS Development Team
This program is free software under the GNU General Public License
@@ -13,80 +11,128 @@
@author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
"""
+import sys
+import numpy as np
from multiprocessing import Process, Queue
from ctypes import *
try:
from grass.lib.imagery import *
from grass.lib.gis import Cell_head, G_get_window
- #from grass.lib.raster import struct_Range
+except ImportError, e:
+ sys.stderr.write(_("Loading ctypes libs failed"))
+from core.gcmd import GException
-except ImportError, e:
- sys.stderr.write(_("Loading imagery lib failed"))
+def Rasterize(polygon, rast, region, value):
+
+ pol_size = len(polygon) * 2
+ pol = np.array(polygon, dtype=float)
-def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out):
+ c_uint8_p = POINTER(c_uint8)
+ c_double_p = POINTER(c_double)
- return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, None)
+ rows, cols = rast.shape
- # Queue object for interprocess communication
+ #TODO creating of region is on many places
+ region['rows'] = rows
+ region['cols'] = cols
+
+ region['nsres'] = 1.0
+ region['ewres'] = 1.0
+
+ pol_p = pol.ctypes.data_as(c_double_p)
+ rast_p = rast.ctypes.data_as(c_uint8_p)
+
+ cell_h = _regionToCellHead(region)
+ I_rasterize(pol_p,
+ len(polygon),
+ value,
+ pointer(cell_h), rast_p)
+
+def ApplyColormap(vals, vals_mask, colmap, out_vals):
+
+ c_uint8_p = POINTER(c_uint8)
+
+ vals_p = vals.ctypes.data_as(c_uint8_p)
+
+
+ if hasattr(vals_mask, "ctypes"):
+ vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
+ else: #vals mask is empty (all data are selected)
+ vals_mask_p = None
+ colmap_p = colmap.ctypes.data_as(c_uint8_p)
+ out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
+
+ vals_size = vals.reshape((-1)).shape[0]
+ I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
+
+def MergeArrays(merged_arr, overlay_arr, alpha):
+ if merged_arr.shape != overlay_arr.shape:
+ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
+
+ c_uint8_p = POINTER(c_uint8)
+ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
+ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
+
+ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
+
+def MergeArrays(merged_arr, overlay_arr, alpha):
+ if merged_arr.shape != overlay_arr.shape:
+ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
+
+ c_uint8_p = POINTER(c_uint8)
+ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
+ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
+
+ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
+
+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
+
q = Queue()
-
- # The separate render process
p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
- n_bands, scatts, cats_rasts_in, cats_rasts_out, q))
+ n_bands, scatts, cats_rasts_conds, cats_rasts, q))
p.start()
ret = q.get()
p.join()
-
+
return ret[0], ret[1]
def UpdateCatRast(patch_rast, region, cat_rast):
-
- #return _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts, None)
- # Queue object for interprocess communication
-
- #_updateCatRastProcess(patch_rast, region, cat_rast, None)
- #return
q = Queue()
- # The separate render process
p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
p.start()
- #ret = q.get()
+ ret = q.get()
p.join()
- #return ret[0], ret[1]
+ return ret
def CreateCatRast(region, cat_rast):
-
cell_head = _regionToCellHead(region)
I_create_cat_rast(pointer(cell_head), cat_rast)
-def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_in, cats_rasts_out, output_queue):
+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
#TODO names for types not 0 and 1?
- sccats_c, vals_dt = _getComputationStruct(scatts, 0, n_bands)
- scatt_conds_c, vals_dt2 = _getComputationStruct(scatt_conds, 1, n_bands)
+ sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
+ scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
char_bands = _stringListToCharArr(bands)
- char_cats_rasts_out = _stringListToCharArr(cats_rasts_out)
- char_cats_rasts_in = _stringListToCharArr(cats_rasts_in)
-
+
cell_head = _regionToCellHead(region)
ret = I_compute_scatts(pointer(cell_head),
pointer(scatt_conds_c),
+ pointer(cats_rasts_conds_c),
pointer(char_bands),
n_bands,
pointer(sccats_c),
- pointer(char_cats_rasts_in),
- pointer(char_cats_rasts_out))
+ pointer(cats_rasts_c))
I_sc_free_cats(pointer(sccats_c))
I_sc_free_cats(pointer(scatt_conds_c))
- #output_queue.put((ret, scatts))
- return (ret, scatts)
+ output_queue.put((ret, scatts))
def _getBandcRange( band_info):
band_c_range = struct_Range()
@@ -96,7 +142,6 @@
return band_c_range
-
def _regionToCellHead(region):
cell_head = struct_Cell_head()
G_get_window(pointer(cell_head))
@@ -131,27 +176,21 @@
return char_arr
-def _getComputationStruct(cats, cats_type, n_bands):
+def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
sccats = struct_scCats()
I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
-
- #for i in range(sccats.n_a_cats):
- # cat_id = sccats.cats_ids[i]
- # I_sc_delete_cat(pointer(sccats), cat_id)
- # i -= 1
- vals_dt = []
+ refs = []
+ cats_rasts_core = []
+
for cat_id, scatt_ids in cats.iteritems():
- I_sc_add_cat(pointer(sccats), cat_id)
+ cat_c_id = I_sc_add_cat(pointer(sccats))
+ cats_rasts_core.append(cats_rasts[cat_id])
for scatt_id, dt in scatt_ids.iteritems():
# if key is missing condition is always True (full scatter plor is computed)
- if cats[cat_id].has_key(scatt_id):
-
vals = dt['np_vals']
- b1_info = dt['bands_info']['b1']
- b2_info = dt['bands_info']['b2']
scatt_vals = scdScattData()
@@ -163,65 +202,27 @@
pass
else:
return None
-
- b1_c_info = _getBandcRange(b1_info)
- b2_c_info =_getBandcRange(b2_info)
data_p = vals.ctypes.data_as(c_void_p)
- I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p,
- b1_c_info, b2_c_info)
+ I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
- vals_dt.append(scatt_vals)
+ refs.append(scatt_vals)
I_sc_insert_scatt_data(pointer(sccats),
pointer(scatt_vals),
- cat_id, scatt_id)
+ cat_c_id, scatt_id)
- return sccats, vals_dt
+ cats_rasts_c = _stringListToCharArr(cats_rasts_core)
+ return sccats, cats_rasts_c, refs
+
def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
-
- #TODO names for types not 0 and 1?
-
cell_head = _regionToCellHead(region)
- cat_rast
- I_insert_patch_to_cat_rast(patch_rast,
+ ret = I_insert_patch_to_cat_rast(patch_rast,
pointer(cell_head),
cat_rast)
- #print re
- #output_queue.put((ret, scatts))
- #return (ret, scatts)
+ output_queue.put(ret)
-"""
-class A:
- def __init__(self):
- self.i = 0
- def incr(self):
- self.i += 1
- return self.i
-
-def worker(input, output):
- print "ahoj"
- for func, args in iter(input.get, 'STOP'):
- print "jedu"
- result = func.incr
- output.put(result)
-
-def test():
- # Create queues
- task_queue = Queue()
- done_queue = Queue()
-
- # Start worker processes
- a = A()
-
- prc = Process(target=worker, args=(task_queue, done_queue)).start()
- task_queue.put((a,1))
-
- data = done_queue.get()
- print data
- task_queue.put('STOP')
-"""
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/dialogs.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,9 +1,14 @@
"""!
@package scatt_plot.dialogs
- at brief GUI.
+ at brief Dialogs widgets.
Classes:
+ - dialogs::AddScattPlotDialog
+ - dialogs::ExportCategoryRaster
+ - dialogs::SettingsDialog
+ - dialogs::ManageBusyCursorMixin
+ - dialogs::RenameClassDialog
(C) 2013 by the GRASS Development Team
@@ -16,219 +21,58 @@
import sys
import wx
-import wx.lib.mixins.listctrl as listmix
-import wx.lib.flatnotebook as FN
-import wx.aui
-
-from core import globalvar
-from core.gcmd import GException, GError, RunCommand
-
+from scatt_plot.scatt_core import idBandsToidScatt
from gui_core.gselect import Select
-from gui_core.widgets import GNotebook
+import wx.lib.colourselect as csel
-from scatt_plot.controllers import ScattsManager
-from scatt_plot.toolbars import MainToolbar, CategoriesToolbar
-from scatt_plot.sc_pl_core import Core, idBandsToidScatt
-from scatt_plot.plots import ScatterPlotWidget
+import grass.script as grass
+from core import globalvar
+from core.gcmd import GMessage
+from core.settings import UserSettings
+from gui_core.dialogs import SimpleDialog
-class ScattPlotMainDialog(wx.Dialog):
- def __init__(self, parent, giface, iclass_mapwin = None,
- id = wx.ID_ANY, title = _("GRASS GIS Interactive Scatter Plot Tool"),
- style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-
- wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
- self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+class AddScattPlotDialog(wx.Dialog):
- #TODO remove iclass parameter
- self.scatt_mgr = ScattsManager(guiparent = self, giface = giface, iclass_mapwin = iclass_mapwin)
+ def __init__(self, parent, bands, added_scatts_ids, id = wx.ID_ANY):
+ wx.Dialog.__init__(self, parent, title = ("Add scatter plots"), id = id)
- # toobars
- self.toolbars = {}
- self.toolbars['mainToolbar'] = MainToolbar(parent = self)
-
- self.mainPanel = wx.Panel(parent=self)
- self.notebook = GNotebook(parent = self.mainPanel,
- style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
- FN.FNB_NO_X_BUTTON)
+ self.added_scatts_ids = added_scatts_ids
+ self.bands = bands
- # Fancy gui
- self._mgr = wx.aui.AuiManager(self)
- self._addPanes()
- self._mgr.Update()
+ self.x_band = None
+ self.y_band = None
- self._createCategoryPage()
-
- self.edit_cat_tools = {
- 'editCatAdd' : {
- 'toggle' : False,
- 'type' : 'add'
- },
- 'editCatRemove' : {
- 'toggle' : False,
- 'type' : 'remove'
- }
- }
- self._doLayout()
- self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
-
- dlgSize = (220, 400)
- self.SetMinSize(dlgSize)
- self.SetInitialSize(dlgSize)
-
- #fix goutput's pane size (required for Mac OSX)
- #if self.gwindow:
- # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
-
- def _doLayout(self):
-
- sizer = wx.BoxSizer(wx.VERTICAL)
-
- sizer.Add(item = self.notebook, proportion = 1,
- flag = wx.EXPAND)
-
- self.mainPanel.SetSizer(sizer)
-
- sizer.Fit(self)
- self.Layout()
-
- #TODO split to panel?
- def _createCategoryPage(self):
- catsPanel = wx.Panel(parent = self)
-
-
- self.notebook.AddPage(page = catsPanel,
- text=_('Categories'),
- name = 'categories')
-
- self.cats_list = CategoryListCtrl(parent = catsPanel,
- cats_mgr = self.scatt_mgr.GetCategoriesManager())
- self.toolbars['catsList'] = CategoriesToolbar(parent = catsPanel,
- cats_list = self.cats_list,
- scatts_dlg = self)
-
- AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
-
- catsSizer = wx.BoxSizer(wx.VERTICAL)
-
- catsSizer.Add(item = self.toolbars['catsList'], proportion = 0)
- catsSizer.Add(item = self.cats_list, proportion = 1, flag = wx.EXPAND)
-
- catsPanel.SetSizer(catsSizer)
-
- def _addPanes(self):
- """!Adds toolbar pane and pane with tabs"""
- self._mgr.AddPane(self.toolbars['mainToolbar'],
- wx.aui.AuiPaneInfo().
- Name("pointlisttools").Caption(_("Point cats_list toolbar")).
- ToolbarPane().Top().Row(0).
- Dockable(False).
- CloseButton(False).Layer(0))
-
- self._mgr.AddPane(self.mainPanel,
- wx.aui.AuiPaneInfo().
- Name("tabs").CaptionVisible(visible = False).
- Center().
- Dockable(False).
- CloseButton(False).Layer(0))
-
- def OnCloseDialog(self, event):
- """!Close dialog"""
- self.scatt_mgr.CleanUp()
- self.Destroy()
-
- def OnSettings(self, event):
- pass
-
- def OnSetData(self, event):
-
- dlg = SetDataDialog(parent=self)
-
- if dlg.ShowModal() == wx.ID_OK:
- bands = dlg.GetBands()
- self.scatt_mgr.SetData(bands)
-
- dlg.Destroy()
-
-
- def NewScatterPlot(self, scatt_id):
- #TODO needs to be resolved (should be in this class)
- scatt_dlg = ScatterPlotDialog(parent = self, scatt_mgr = self.scatt_mgr, scatt_id = scatt_id)
- return scatt_dlg.GetPlot()
-
- def OnSettings(self, event):
- pass
-
- def OnAddScattPl(self, event):
-
- bands = self.scatt_mgr.GetBands()
- if not bands:
- GError(_('No data set for scatter plot.'))
- return
-
- dlg = AddScattPlotDialog(parent = self, bands = bands)
-
- if dlg.ShowModal() == wx.ID_OK:
- self.scatt_mgr.AddScattPlot(dlg.GetScattId())
-
- dlg.Destroy()
-
- def EditCatAdd(self):
- self._setEditCat(tool_name = 'editCatAdd')
-
- def EditCatRemove(self):
- self._setEditCat(tool_name = 'editCatRemove')
-
- def _setEditCat(self, tool_name):
-
- if self.edit_cat_tools[tool_name]['toggle'] == False:
-
- for i_tool_name in self.edit_cat_tools.iterkeys():
- if i_tool_name == tool_name:
- continue
-
- self.edit_cat_tools[i_tool_name]['toggle'] = False
-
- toolbar = self.toolbars['catsList']
- toolbar.ToggleTool(vars(toolbar)[i_tool_name], False)
-
- self.edit_cat_tools[tool_name]['toggle'] = True
- self.scatt_mgr.EditCat(edit_type = self.edit_cat_tools[tool_name]['type'])
- return
-
- self.scatt_mgr.EditCat(edit_type = None)
- self.edit_cat_tools[tool_name]['toggle'] = False
-
- def GetScattMgr(self):
- return self.scatt_mgr
-
-
-class SetDataDialog(wx.Dialog):
-
- def __init__(self, parent, id = wx.ID_ANY):
-
- wx.Dialog.__init__(self, parent, title = ("Select imagery group "), id = id)
-
- self.bands = []
-
-
+ self.added_bands_ids = []
+ self.sel_bands_ids = []
self._createWidgets()
- self.group.SetValue("B_sk")
def _createWidgets(self):
self.labels = {}
self.params = {}
- self.group_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name of raster group for input data:"))
+ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("x axis:"))
- self.group = Select(parent = self, type = 'group',
- size = globalvar.DIALOG_GSELECT_SIZE)
+ self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = self.bands,
+ style = wx.CB_READONLY, size = (350, 30))
+ self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("y axis:"))
+
+ self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
+ choices = self.bands,
+ style = wx.CB_READONLY, size = (350, 30))
+
+ self.scattsBox = wx.ListBox(parent = self, id = wx.ID_ANY, size = (-1, 150),
+ style = wx.LB_MULTIPLE | wx.LB_NEEDED_SB)
+
# buttons
- self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
+ self.btn_add = wx.Button(parent=self, id=wx.ID_ANY, label = _("Add"))
+ self.btn_remove = wx.Button(parent=self, id=wx.ID_ANY, label = _("Remove"))
- self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
+ self.btn_close = wx.Button(parent=self, id=wx.ID_CANCEL)
+ self.btn_ok = wx.Button(parent=self, id=wx.ID_OK, label = _("&OK"))
self._layout()
@@ -239,25 +83,40 @@
regionSizer = wx.BoxSizer(wx.HORIZONTAL)
- dialogSizer.Add(item = self._addSelectSizer(title = self.group_label,
- sel = self.group))
+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label,
+ sel = self.band_1_ch))
+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label,
+ sel = self.band_2_ch))
+
+
+ dialogSizer.Add(item=self.btn_add, proportion=0, flag = wx.TOP, border = 5)
+
+ box = wx.StaticBox(self, id = wx.ID_ANY,
+ label = " %s " % _("Bands of scatter plots to be added (x y):"))
+ sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+ sizer.Add(item=self.scattsBox, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
+ sizer.Add(item=self.btn_remove, proportion=0, flag=wx.TOP, border = 5)
+
+ dialogSizer.Add(item=sizer, proportion=1, flag = wx.EXPAND | wx.TOP, border = 5)
+
# buttons
self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
self.btnsizer.Add(item = self.btn_close, proportion = 0,
- flag = wx.ALL | wx.ALIGN_CENTER,
+ flag = wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
border = 10)
self.btnsizer.Add(item = self.btn_ok, proportion = 0,
- flag = wx.ALL | wx.ALIGN_CENTER,
+ flag = wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
border = 10)
dialogSizer.Add(item = self.btnsizer, proportion = 0,
- flag = wx.ALIGN_CENTER)
+ flag = wx.ALIGN_CENTER | wx.TOP, border = 5)
border.Add(item = dialogSizer, proportion = 0,
- flag = wx.ALL, border = 5)
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 10)
self.SetSizer(border)
self.Layout()
@@ -266,7 +125,17 @@
# bindings
self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+ self.btn_add.Bind(wx.EVT_BUTTON, self.OnAdd)
+ self.btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
+ def OnOk(self, event):
+
+ if not self.GetBands():
+ GMessage(parent=self, message=_("No scatter plots selected."))
+ return
+
+ event.Skip()
+
def _addSelectSizer(self, title, sel):
"""!Helper layout function.
"""
@@ -274,21 +143,21 @@
selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
selTitleSizer.Add(item = title, proportion = 1,
- flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+ flag = wx.TOP | wx.EXPAND, border = 5)
selSizer.Add(item = selTitleSizer, proportion = 0,
flag = wx.EXPAND)
selSizer.Add(item = sel, proportion = 1,
- flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+ flag = wx.EXPAND | wx.TOP| wx.ALIGN_CENTER_VERTICAL,
border = 5)
return selSizer
def GetBands(self):
+ """!Get layers"""
+ return self.sel_bands_ids
- return self.bands
-
def OnClose(self, event):
"""!Close dialog
"""
@@ -296,317 +165,353 @@
self.Destroy()
event.Skip()
- def OnOk(self, event):
+ def OnRemoveLayer(self, event):
+ """!Remove layer from listbox"""
+ while self.scattsBox.GetSelections():
+ sel = self.scattsBox.GetSelections()[0]
+ self.scattsBox.Delete(sel)
+ self.sel_bands_ids.pop(sel)
+
+ def OnAdd(self, event):
"""!
"""
- ret, stdout, err_msg = RunCommand("i.group",
- getErrorMsg = True,
- read = True,
- quiet = True,
- group = self.group.GetValue().strip(),
- flags = 'g')
+ b_x = self.band_1_ch.GetSelection()
+ b_y = self.band_2_ch.GetSelection()
- if ret != 0:
- GError("%s module failed:\n%s" % ("<i.group>", err_msg))
+ if b_x < 0 or b_y < 0:
+ GMessage(parent=self, message=_("Select both x and y bands."))
return
+ if b_y == b_x:
+ GMessage(parent=self, message=_("Selected bands must be different."))
+ return
- self.bands = stdout.split('\n')
+ scatt_id = idBandsToidScatt(b_x, b_y, len(self.bands))
+ if scatt_id in self.added_scatts_ids:
+ GMessage(parent=self,
+ message=_("Scatter plot with same band combination (regardless x y order) "
+ "is already displayed."))
+ return
- for band in self.bands[:]:
- if not band:
- self.bands.remove(band)
-
- event.Skip()
+ if [b_x, b_y] in self.sel_bands_ids or [b_y, b_x] in self.sel_bands_ids:
+ GMessage(parent=self,
+ message=_("Scatter plot with same bands combination (regardless x y order) "
+ "has been already added into the list."))
+ return
- #TODO catch event or...
- def GetSelectedCatId(self):
-
- return self.cats_list.GetSelectedCatId()
+ self.sel_bands_ids.append([b_x, b_y])
-class AddScattPlotDialog(wx.Dialog):
+ b_x_str = self.band_1_ch.GetStringSelection()
+ b_y_str = self.band_2_ch.GetStringSelection()
- def __init__(self, parent, bands, id = wx.ID_ANY):
-
- wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
+ text = b_x_str + " " + b_y_str
- self.bands = bands
+ self.scattsBox.Append(text)
+ event.Skip()
- self.scatt_id = None
+class ExportCategoryRaster(wx.Dialog):
+ def __init__(self, parent, title, rasterName = None, id = wx.ID_ANY,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+ **kwargs):
+ """!Dialog for export of category raster.
+
+ @param parent window
+ @param rasterName name of vector layer for export
+ @param title window title
+ """
+ wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+
+ self.rasterName = rasterName
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+ self.btnOK = wx.Button(parent = self.panel, id = wx.ID_OK)
+ self.btnOK.SetDefault()
+ self.btnOK.Enable(False)
+ self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
+
+ self.__layout()
+
+ self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
+ self.OnTextChanged(None)
+ wx.CallAfter(self.vectorNameCtrl.SetFocus)
- self._createWidgets()
+ def OnTextChanged(self, event):
+ """!Name of new vector map given.
+
+ Enable/diable OK button.
+ """
+ file = self.vectorNameCtrl.GetValue()
+ if len(file) > 0:
+ self.btnOK.Enable(True)
+ else:
+ self.btnOK.Enable(False)
+
+ def __layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ dataSizer = wx.BoxSizer(wx.VERTICAL)
+
+ dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Enter name of new vector map:")),
+ proportion = 0, flag = wx.ALL, border = 3)
+ self.vectorNameCtrl = Select(parent = self.panel, type = 'raster',
+ mapsets = [grass.gisenv()['MAPSET']],
+ size = globalvar.DIALOG_GSELECT_SIZE)
+ if self.rasterName:
+ self.vectorNameCtrl.SetValue(self.rasterName)
+ dataSizer.Add(item = self.vectorNameCtrl,
+ proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+
+
+ # buttons
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOK)
+ btnSizer.Realize()
+
+ sizer.Add(item = dataSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ self.panel.SetSizer(sizer)
+ sizer.Fit(self)
+
+ self.SetMinSize(self.GetSize())
+
+ def GetRasterName(self):
+ """!Returns vector name"""
+ return self.vectorNameCtrl.GetValue()
- def _createWidgets(self):
+ def OnOK(self, event):
+ """!Checks if map exists and can be overwritten."""
+ overwrite = UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled')
+ rast_name = self.GetRasterName()
+ res = grass.find_file(rast_name, element = 'cell')
+ if res['fullname'] and overwrite is False:
+ qdlg = wx.MessageDialog(parent = self,
+ message = _("Raster map <%s> already exists."
+ " Do you want to overwrite it?" % rast_name) ,
+ caption = _("Raster <%s> exists" % rast_name),
+ style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+ if qdlg.ShowModal() == wx.ID_YES:
+ event.Skip()
+ qdlg.Destroy()
+ else:
+ event.Skip()
- self.labels = {}
- self.params = {}
+class SettingsDialog(wx.Dialog):
+ def __init__(self, parent, id, title, scatt_mgr, pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.DEFAULT_DIALOG_STYLE):
+ """!Settings dialog"""
+ wx.Dialog.__init__(self, parent, id, title, pos, size, style)
- self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 1:"))
+ self.scatt_mgr = scatt_mgr
- self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
- choices = self.bands,
- style = wx.CB_READONLY, size = (350, 30))
+ maxValue = 1e8
+ self.parent = parent
+ self.settings = {}
- self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Band 2:"))
+ settsLabels = {}
- self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
- choices = self.bands,
- style = wx.CB_READONLY, size = (350, 30))
+ self.settings["show_ellips"] = wx.CheckBox(parent = self, id=wx.ID_ANY,
+ label = _('Show confidence ellipses'))
+ show_ellips = UserSettings.Get(group ='scatt', key = "ellipses", subkey = "show_ellips")
+ self.settings["show_ellips"].SetValue(show_ellips)
- # buttons
- self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
-
- self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
- self._layout()
+ self.colorsSetts = {
+ "sel_pol" : ["selection", _("Selection polygon color:")],
+ "sel_pol_vertex" : ["selection", _("Color of selection polygon vertex:")],
+ "sel_area" : ["selection", _("Selected area color:")]
+ }
- def _layout(self):
+ for settKey, sett in self.colorsSetts.iteritems():
+ settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+ col = UserSettings.Get(group ='scatt', key = sett[0], subkey = settKey)
+ self.settings[settKey] = csel.ColourSelect(parent = self, id = wx.ID_ANY,
+ colour = wx.Colour(col[0],
+ col[1],
+ col[2],
+ 255))
- border = wx.BoxSizer(wx.VERTICAL)
- dialogSizer = wx.BoxSizer(wx.VERTICAL)
+ self.sizeSetts = {
+ "snap_tresh" : ["selection", _("Snapping treshold in pixels:")],
+ "sel_area_opacty" : ["selection", _("Selected area opacity:")]
+ }
- regionSizer = wx.BoxSizer(wx.HORIZONTAL)
+ for settKey, sett in self.sizeSetts.iteritems():
+ settsLabels[settKey] = wx.StaticText(parent = self, id = wx.ID_ANY, label = sett[1])
+ self.settings[settKey] = wx.SpinCtrl(parent = self, id = wx.ID_ANY, min=0, max = 100)
+ size = int(UserSettings.Get(group = 'scatt', key = sett[0], subkey = settKey))
+ self.settings[settKey].SetValue(size)
- dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label,
- sel = self.band_1_ch))
- dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label,
- sel = self.band_2_ch))
-
# buttons
- self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+ self.btnSave = wx.Button(self, wx.ID_SAVE)
+ self.btnApply = wx.Button(self, wx.ID_APPLY)
+ self.btnClose = wx.Button(self, wx.ID_CLOSE)
+ self.btnApply.SetDefault()
- self.btnsizer.Add(item = self.btn_close, proportion = 0,
- flag = wx.ALL | wx.ALIGN_CENTER,
- border = 10)
-
- self.btnsizer.Add(item = self.btn_ok, proportion = 0,
- flag = wx.ALL | wx.ALIGN_CENTER,
- border = 10)
+ # bindings
+ self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+ self.btnApply.SetToolTipString(_("Apply changes for the current session"))
+ self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+ self.btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+ self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+ self.btnClose.SetToolTipString(_("Close dialog"))
- dialogSizer.Add(item = self.btnsizer, proportion = 0,
- flag = wx.ALIGN_CENTER)
+ #Layout
- border.Add(item = dialogSizer, proportion = 0,
- flag = wx.ALL, border = 5)
+ # Analysis result style layout
+ self.SetMinSize(self.GetBestSize())
- self.SetSizer(border)
- self.Layout()
- self.Fit()
+ sizer = wx.BoxSizer(wx.VERTICAL)
- # bindings
- self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
- self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
+ sel_pol_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label =" %s " % _("Selection style:"))
+ selPolBoxSizer = wx.StaticBoxSizer(sel_pol_box, wx.VERTICAL)
- def _addSelectSizer(self, title, sel):
- """!Helper layout function.
- """
- selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
- selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
- selTitleSizer.Add(item = title, proportion = 1,
- flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+ row = 0
+ setts = dict(self.colorsSetts.items() + self.sizeSetts.items())
- selSizer.Add(item = selTitleSizer, proportion = 0,
- flag = wx.EXPAND)
+ settsOrder = ["sel_pol", "sel_pol_vertex", "sel_area",
+ "sel_area_opacty", "snap_tresh"]
+ for settKey in settsOrder:
+ sett = setts[settKey]
+ gridSizer.Add(item = settsLabels[settKey], flag = wx.ALIGN_CENTER_VERTICAL, pos =(row, 0))
+ gridSizer.Add(item = self.settings[settKey],
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5,
+ pos =(row, 1))
+ row += 1
- selSizer.Add(item = sel, proportion = 1,
- flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
- border = 5)
+ gridSizer.AddGrowableCol(1)
+ selPolBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
- return selSizer
+ ell_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label =" %s " % _("Ellipses settings:"))
+ ellPolBoxSizer = wx.StaticBoxSizer(ell_box, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
- def OnClose(self, event):
- """!Close dialog
- """
- if not self.IsModal():
- self.Destroy()
- event.Skip()
+ sett = setts[settKey]
- def OnOk(self, event):
- """!
- """
- band_1 = self.band_1_ch.GetSelection()
- band_2 = self.band_2_ch.GetSelection()
+ row = 0
+ gridSizer.Add(item=self.settings["show_ellips"],
+ flag=wx.ALIGN_CENTER_VERTICAL,
+ pos=(row, 0))
+ gridSizer.AddGrowableCol(1)
+ ellPolBoxSizer.Add(item=gridSizer, flag=wx.EXPAND)
- if band_1 == band_2:
- GError(_("Selected bands must be different."))
- return
-
- #TODO axes selection
- if band_1 > band_2:
- tmp_band = band_2
- band_2 = band_1
- band_1 = band_2
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(self.btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
+ btnSizer.Add(self.btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
+ btnSizer.Add(self.btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
- self.scatt_id = idBandsToidScatt(band_1, band_2, len(self.bands))
+ sizer.Add(item=selPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+ sizer.Add(item=ellPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+ sizer.Add(item=btnSizer, flag=wx.EXPAND | wx.ALL, border=5, proportion=0)
- event.Skip()
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def OnSave(self, event):
+ """!Button 'Save' pressed"""
+ self.UpdateSettings()
- def GetScattId(self):
- return self.scatt_id
+ fileSettings = {}
+ UserSettings.ReadSettingsFile(settings=fileSettings)
+ fileSettings['scatt'] = UserSettings.Get(group='scatt')
+ UserSettings.SaveToFile(fileSettings)
-class ScatterPlotDialog(wx.Dialog):
- def __init__(self, parent, scatt_mgr, scatt_id,
- id = wx.ID_ANY, title = _("Test plot"),
- style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-
- wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
- self.scatt = ScatterPlotWidget(parent = self,
- scatt_mgr = scatt_mgr,
- scatt_id = scatt_id)
+ self.Close()
- def GetPlot(self):
- return self.scatt
+ def UpdateSettings(self):
- def _doLayout(self):
- self.main_sizer = wx.BoxSizer(wx.VERTICAL)
- self.main_sizer.Add(self.self)
+ chanaged_setts = [];
+ for settKey, sett in self.colorsSetts.iteritems():
+ col = tuple(self.settings[settKey].GetColour())
+ col_s = UserSettings.Get(group='scatt', key=sett[0], subkey=settKey)
+ if col_s != col:
+ UserSettings.Set(group='scatt',
+ key=sett[0],
+ subkey=settKey,
+ value=col)
+ chanaged_setts.append([settKey, sett[0]])
- self.panel.SetSizer(self.main_sizer)
- self.main_sizer.Fit(self)
-
- def OnPlotClosed(self, scatt_id):
- self.Destroy()
+ for settKey, sett in self.sizeSetts.iteritems():
+ val = self.settings[settKey].GetValue()
+ val_s = UserSettings.Get(group ='scatt', key = sett[0], subkey = settKey)
-class CategoryListCtrl(wx.ListCtrl,
- listmix.ListCtrlAutoWidthMixin,
- listmix.TextEditMixin):
+ if val_s != val:
+ UserSettings.Set(group = 'scatt', key = sett[0], subkey = settKey,
+ value = val)
+ chanaged_setts.append([settKey, sett[0]])
- def __init__(self, parent, cats_mgr, id = wx.ID_ANY):
+ val = self.settings['show_ellips'].IsChecked()
+ val_s = UserSettings.Get(group ='scatt', key='ellipses', subkey='show_ellips')
- wx.ListCtrl.__init__(self, parent, id,
- style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)
- self.columns = ((_('Category name'), 'name'),
- (_('Color'), 'color'))
- self.Populate(columns = self.columns)
-
- self.cats_mgr = cats_mgr
- self.SetItemCount(len(self.cats_mgr.GetCategories()))
+ if val != val_s:
+ UserSettings.Set(group='scatt', key='ellipses', subkey='show_ellips',
+ value=val)
+ chanaged_setts.append(['ellipses', 'show_ellips'])
- self.rightClickedItemIdx = wx.NOT_FOUND
-
- listmix.ListCtrlAutoWidthMixin.__init__(self)
+ if chanaged_setts:
+ self.scatt_mgr.SettingsUpdated(chanaged_setts)
- listmix.TextEditMixin.__init__(self)
-
- self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
- self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
-
- self.cats_mgr.setCategoryAttrs.connect(self.Update)
- self.cats_mgr.deletedCategory.connect(self.Update)
- self.cats_mgr.addedCategory.connect(self.Update)
+ def OnApply(self, event):
+ """!Button 'Apply' pressed"""
+ self.UpdateSettings()
+ #self.Close()
- def Update(self, **kwargs):
- self.SetItemCount(len(self.cats_mgr.GetCategories()))
- self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+ def OnClose(self, event):
+ """!Button 'Cancel' pressed"""
+ self.Close()
- def InitCoreCats(self):
- self.SetItemCount(len(self.cats_mgr.GetCategories()))
- self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+class ManageBusyCursorMixin:
+ def __init__(self, window):
+ self.win = window
- def SetVirtualData(self, row, column, text):
- attr = self.columns[column][1]
- if attr == 'name':
- try:
- text.encode('ascii')
- except UnicodeEncodeError:
- GMessage(parent = self, message = _("Please use only ASCII characters."))
- return
+ self.is_busy = False
+ self.cur_inside = False
- cat_id = self.cats_mgr.GetCategories()[row]
+ self.win.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
+ self.win.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
- self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
- self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
- self.cats_mgr.setCategoryAttrs.connect(self.Update)
-
- self.Select(row)
-
- def Populate(self, columns):
- for i, col in enumerate(columns):
- self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+ def OnLeave(self, event):
+ self.cur_inside = False
+ self.busy_cur = None
- self.SetColumnWidth(0, 100)
- self.SetColumnWidth(1, 100)
-
- def AddCategory(self):
+ def OnEnter(self, event):
+ self.cur_inside = True
+ if self.is_busy:
+ self.busy_cur = wx.BusyCursor()
- self.cats_mgr.addedCategory.disconnect(self.Update)
- cat_id = self.cats_mgr.AddCategory()
- self.cats_mgr.addedCategory.connect(self.Update)
-
- if cat_id < 0:
- GError(_("Maximum limit of categories number was reached."))
+ def UpdateCur(self, busy):
+ self.is_busy = busy
+ if self.cur_inside and self.is_busy:
+ self.busy_cur = wx.BusyCursor()
return
- self.SetItemCount(len(self.cats_mgr.GetCategories()))
-
- def DeleteCategory(self):
- indexList = sorted(self.GetSelectedIndices(), reverse = True)
- cats = []
- for i in indexList:
- # remove temporary raster
- cat_id = self.cats_mgr.GetCategories()[i]
-
- cats.append(cat_id)
- self.cats_mgr.deletedCategory.disconnect(self.Update)
- self.cats_mgr.DeleteCategory(cat_id)
- self.cats_mgr.deletedCategory.connect(self.Update)
-
- self.SetItemCount(len(self.cats_mgr.GetCategories()))
-
- def OnSel(self, event):
- self.cats_mgr.SetSelectedCat(event.GetIndex() + 1)
+ self.busy_cur = None
- event.Skip()
+class RenameClassDialog(SimpleDialog):
+ def __init__(self, parent, old_name, title=("Change class name")):
+ SimpleDialog.__init__(self, parent, title)
- def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
- indices = []
- lastFound = -1
- while True:
- index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
- if index == -1:
- break
- else:
- lastFound = index
- indices.append(index)
- return indices
+ self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY)
+ self.name.SetValue(old_name)
- def OnEdit(self, event):
- currentItem = event.m_itemIndex
- currentCol = event.m_col
+ self.dataSizer.Add(self.name, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
- if currentCol == 1:
- dlg = wx.ColourDialog(self)
- dlg.GetColourData().SetChooseFull(True)
+ self.panel.SetSizer(self.sizer)
+ self.name.SetMinSize((200, -1))
+ self.sizer.Fit(self)
- if dlg.ShowModal() == wx.ID_OK:
- color = dlg.GetColourData().GetColour().Get()
- color = ':'.join(map(str, color))
- self.SetVirtualData(currentItem, currentCol, color)
- dlg.Destroy()
- wx.CallAfter(self.SetFocus)
-
- event.Skip()
-
-
- def DeselectAll(self):
- """!Deselect all items"""
- indexList = self.GetSelectedIndices()
- for i in indexList:
- self.Select(i, on = 0)
-
- # no highlight
- self.OnCategorySelected(None)
-
- def OnGetItemText(self, item, col):
- attr = self.columns[col][1]
- cat_id = self.cats_mgr.GetCategories()[item]
-
- return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
-
- def OnGetItemImage(self, item):
- return -1
-
- def OnGetItemAttr(self, item):
- return None
+ def GetNewName(self):
+ return self.name.GetValue()
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/frame.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,694 @@
+"""!
+ at package scatt_plot.dialogs
+
+ at brief Main scatter plot widgets.
+
+Classes:
+ - frame::IClassIScattPanel
+ - frame::IScattDialog
+ - frame::MapDispIScattPanel
+ - frame::ScatterPlotsPanel
+ - frame::CategoryListCtrl
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+import wx.lib.mixins.listctrl as listmix
+
+from core import globalvar
+from core.gcmd import GException, GError, RunCommand
+
+from gui_core.gselect import Select
+from gui_core.dialogs import SetOpacityDialog
+from scatt_plot.controllers import ScattsManager
+from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
+from scatt_plot.scatt_core import idScattToidBands
+from scatt_plot.dialogs import ManageBusyCursorMixin, RenameClassDialog
+from scatt_plot.plots import ScatterPlotWidget
+from iclass.dialogs import ContrastColor
+
+try:
+ from agw import aui
+except ImportError:
+ import wx.lib.agw.aui as aui
+
+class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
+ def __init__(self, parent, giface, iclass_mapwin = None,
+ id = wx.ID_ANY):
+
+ #wx.SplitterWindow.__init__(self, parent = parent, id = id,
+ # style = wx.SP_LIVE_UPDATE)
+ wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+ ManageBusyCursorMixin.__init__(self, window=self)
+
+ self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface,
+ iclass_mapwin=iclass_mapwin)
+
+ # toobars
+ self.toolbars = {}
+ self.toolbars['mainToolbar'] = self._createMainToolbar()
+ self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
+
+ self._createCategoryPanel(self)
+
+ self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
+
+ self.mainsizer = wx.BoxSizer(wx.VERTICAL)
+ self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
+ self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
+ self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
+ self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
+
+ self.catsPanel.Hide()
+ self.toolbars['editingToolbar'].Hide()
+
+ self.SetSizer(self.mainsizer)
+
+ self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
+ self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
+ self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
+
+ #self.SetSashGravity(0.5)
+ #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
+ self.Layout()
+
+ def UpdateCur(self, busy):
+ self.plot_panel.SetBusy(busy)
+ ManageBusyCursorMixin.UpdateCur(self, busy)
+
+ def _selCatInIScatt(self):
+ return False
+
+ def _createMainToolbar(self):
+ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
+
+ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
+ return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
+
+
+ def NewScatterPlot(self, scatt_id, transpose):
+ return self.plot_panel.NewScatterPlot(scatt_id, transpose)
+
+ def ShowPlotEditingToolbar(self, show):
+ self.toolbars["editingToolbar"].Show(show)
+ self.Layout()
+
+ def ShowCategoryPanel(self, show):
+ self.catsPanel.Show(show)
+
+ #if show:
+ # self.SetSashSize(5)
+ #else:
+ # self.SetSashSize(0)
+ self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
+ self.Layout()
+
+ def _createCategoryPanel(self, parent):
+ self.catsPanel = wx.Panel(parent=parent)
+ self.cats_list = CategoryListCtrl(parent=self.catsPanel,
+ cats_mgr=self.scatt_mgr.GetCategoriesManager(),
+ sel_cats_in_iscatt=self._selCatInIScatt())
+
+ self.catsPanel.SetMinSize((-1, 100))
+ self.catsPanel.SetInitialSize((-1, 150))
+
+ box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
+ label=' %s ' % _("Classes"),)
+ catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
+
+ self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
+
+ catsSizer.Add(item=self.cats_list, proportion=1, flag=wx.EXPAND | wx.TOP, border = 5)
+ if self.toolbars['categoryToolbar']:
+ catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
+
+ self.catsPanel.SetSizer(catsSizer)
+
+ def CleanUp(self):
+ pass
+
+ def _createCategoryToolbar(self, parent):
+ return CategoryToolbar(parent=parent,
+ scatt_mgr=self.scatt_mgr,
+ cats_list=self.cats_list)
+
+class IScattDialog(wx.Dialog):
+ def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
+ id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
+ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+ self.iscatt_panel = MapDispIScattPanel(self, giface)
+
+ mainsizer = wx.BoxSizer(wx.VERTICAL)
+ mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
+
+ self.SetSizer(mainsizer)
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+ self.SetMinSize((300, 300))
+
+ def OnCloseWindow(self, event):
+ event.Skip()
+ #self.
+
+class MapDispIScattPanel(IClassIScattPanel):
+ def __init__(self, parent, giface,
+ id=wx.ID_ANY, **kwargs):
+ IClassIScattPanel.__init__(self, parent=parent, giface=giface,
+ id=id, **kwargs)
+
+ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
+ return ScattsManager(guiparent = self, giface = giface)
+
+ def _createMainToolbar(self):
+ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
+
+ def _selCatInIScatt(self):
+ return True
+
+class ScatterPlotsPanel(scrolled.ScrolledPanel):
+ def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
+
+ scrolled.ScrolledPanel.__init__(self, parent)
+ self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
+
+ self.scatt_mgr = scatt_mgr
+
+ self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
+
+ #self._createCategoryPanel()
+ # Fancy gui
+ self._mgr = aui.AuiManager(self.mainPanel)
+ #self._mgr.SetManagedWindow(self)
+
+ self._mgr.Update()
+
+ self._doLayout()
+ self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
+ self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
+
+ self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
+
+ dlgSize = (-1, 400)
+ #self.SetBestSize(dlgSize)
+ #self.SetInitialSize(dlgSize)
+ self.SetAutoLayout(1)
+ #fix goutput's pane size (required for Mac OSX)
+ #if self.gwindow:
+ # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
+ self.ignore_scroll = 0
+ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
+
+ self.scatt_i = 1
+ self.scatt_id_scatt_i = {}
+ self.transpose = {}
+ self.scatts = {}
+
+ self.Bind(wx.EVT_CLOSE, self.OnClose)
+
+ self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
+
+ def SetBusy(self, busy):
+ for scatt in self.scatts.itervalues():
+ scatt.UpdateCur(busy)
+
+ def CursorPlotMove(self, x, y, scatt_id):
+
+ try:
+ x = int(round(x))
+ y = int(round(y))
+ coords = True
+ except:
+ coords = False
+
+ pane = self._getPane(scatt_id)
+ caption = self._creteCaption(scatt_id)
+ if coords:
+ caption += " %d, %d" % (x, y)
+
+ pane.Caption(caption)
+ self._mgr.RefreshCaptions()
+
+ def _getPane(self, scatt_id):
+ scatt_i = self.scatt_id_scatt_i[scatt_id]
+ name = self._getScatterPlotName(scatt_i)
+ return self._mgr.GetPane(name)
+
+ def ScatterPlotClosed(self, scatt_id):
+
+ scatt_i = self.scatt_id_scatt_i[scatt_id]
+
+ name = self._getScatterPlotName(scatt_i)
+ pane = self._mgr.GetPane(name)
+
+ del self.scatt_id_scatt_i[scatt_id]
+ del self.scatts[scatt_id]
+
+ if pane.IsOk():
+ self._mgr.ClosePane(pane)
+ self._mgr.Update()
+
+ def OnMouseWheel(self, event):
+ #TODO very ugly find some better solution
+ self.ignore_scroll = 3
+ event.Skip()
+
+ def ScrollChildIntoView(self, child):
+ #For aui manager it does not work and returns position always to the top -> deactivated.
+ pass
+
+ def OnPlotPaneClosed(self, event):
+ if isinstance(event.pane.window, ScatterPlotWidget):
+ event.pane.window.CleanUp()
+
+ def OnScrollChanged(self, event):
+ wx.CallAfter(self.Layout)
+
+ def OnScroll(self, event):
+ if self.ignore_scroll > 0:
+ self.ignore_scroll -= 1
+ else:
+ event.Skip()
+
+ #wx.CallAfter(self._mgr.Update)
+ #wx.CallAfter(self.Layout)
+
+ def _doLayout(self):
+
+ mainsizer = wx.BoxSizer(wx.VERTICAL)
+ mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
+ self.SetSizer(mainsizer)
+
+ self.Layout()
+ self.SetupScrolling()
+
+ def OnClose(self, event):
+ """!Close dialog"""
+ #TODO
+ print "closed"
+ self.scatt_mgr.CleanUp()
+ self.Destroy()
+
+ def OnSettings(self, event):
+ pass
+
+ def _newScatterPlotName(self, scatt_id):
+ name = self._getScatterPlotName(self.scatt_i)
+ self.scatt_id_scatt_i[scatt_id] = self.scatt_i
+ self.scatt_i += 1
+ return name
+
+ def _getScatterPlotName(self, i):
+ name = "scatter plot %d" % i
+ return name
+
+ def NewScatterPlot(self, scatt_id, transpose):
+ #TODO needs to be resolved (should be in this class)
+
+ scatt = ScatterPlotWidget(parent = self.mainPanel,
+ scatt_mgr = self.scatt_mgr,
+ scatt_id = scatt_id,
+ transpose = transpose)
+ scatt.plotClosed.connect(self.ScatterPlotClosed)
+ self.transpose[scatt_id] = transpose
+
+ caption = self._creteCaption(scatt_id)
+ self._mgr.AddPane(scatt,
+ aui.AuiPaneInfo().Dockable(True).Floatable(True).
+ Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
+ Caption(caption).
+ Center().Position(1).MaximizeButton(True).
+ MinimizeButton(True).CaptionVisible(True).
+ CloseButton(True).Layer(0))
+
+ self._mgr.Update()
+
+ self.SetVirtualSize(self.GetBestVirtualSize())
+ self.Layout()
+
+ self.scatts[scatt_id] = scatt
+
+ return scatt
+
+ def _creteCaption(self, scatt_id):
+
+ transpose = self.transpose[scatt_id]
+ bands = self.scatt_mgr.GetBands()
+
+ #TODO too low level
+ b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
+
+ x_b = bands[b1_id].split('@')[0]
+ y_b = bands[b2_id].split('@')[0]
+
+ if transpose:
+ tmp = x_b
+ x_b = y_b
+ y_b = tmp
+
+ return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
+
+ def GetScattMgr(self):
+ return self.scatt_mgr
+
+class CategoryListCtrl(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin):
+ #listmix.TextEditMixin):
+
+ def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
+
+ wx.ListCtrl.__init__(self, parent, id,
+ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
+ wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
+ self.columns = ((_('Class name'), 'name'), )
+ #(_('Color'), 'color'))
+
+ self.sel_cats_in_iscatt = sel_cats_in_iscatt
+
+ self.Populate(columns = self.columns)
+
+ self.cats_mgr = cats_mgr
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ self.rightClickedItemIdx = wx.NOT_FOUND
+
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+ #listmix.TextEditMixin.__init__(self)
+
+ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
+ self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) #wxGTK
+
+ #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
+
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
+ self.cats_mgr.deletedCategory.connect(self.Update)
+ self.cats_mgr.addedCategory.connect(self.Update)
+ self.cats_mgr.cleanuped.connect(self.Update)
+
+ def Update(self, **kwargs):
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def InitCoreCats(self):
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def SetVirtualData(self, row, column, text):
+ attr = self.columns[column][1]
+ if attr == 'name':
+ try:
+ text.encode('ascii')
+ except UnicodeEncodeError:
+ GMessage(parent = self, message = _("Please use only ASCII characters."))
+ return
+
+ cat_id = self.cats_mgr.GetCategories()[row]
+
+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+ self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
+
+ self.Select(row)
+
+ def Populate(self, columns):
+ for i, col in enumerate(columns):
+ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+
+ #self.SetColumnWidth(0, 100)
+ #self.SetColumnWidth(1, 100)
+
+ def AddCategory(self):
+
+ self.cats_mgr.addedCategory.disconnect(self.Update)
+ cat_id = self.cats_mgr.AddCategory()
+ self.cats_mgr.addedCategory.connect(self.Update)
+
+ if cat_id < 0:
+ GError(_("Maximum limit of categories number was reached."))
+ return
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ def DeleteCategory(self):
+ indexList = sorted(self.GetSelectedIndices(), reverse = True)
+ cats = []
+ for i in indexList:
+ # remove temporary raster
+ cat_id = self.cats_mgr.GetCategories()[i]
+
+ cats.append(cat_id)
+
+ self.cats_mgr.deletedCategory.disconnect(self.Update)
+ self.cats_mgr.DeleteCategory(cat_id)
+ self.cats_mgr.deletedCategory.connect(self.Update)
+
+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
+ def OnSel(self, event):
+ if self.sel_cats_in_iscatt:
+ indexList = self.GetSelectedIndices()
+ sel_cats = []
+ cats = self.cats_mgr.GetCategories()
+ for i in indexList:
+ sel_cats.append(cats[i])
+
+ if sel_cats:
+ self.cats_mgr.SetSelectedCat(sel_cats[0])
+ event.Skip()
+
+ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
+ indices = []
+ lastFound = -1
+ while True:
+ index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
+ if index == -1:
+ break
+ else:
+ lastFound = index
+ indices.append(index)
+ return indices
+
+ def DeselectAll(self):
+ """!Deselect all items"""
+ indexList = self.GetSelectedIndices()
+ for i in indexList:
+ self.Select(i, on = 0)
+
+ # no highlight
+ self.OnCategorySelected(None)
+
+ def OnGetItemText(self, item, col):
+ attr = self.columns[col][1]
+ cat_id = self.cats_mgr.GetCategories()[item]
+
+ return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
+
+ def OnGetItemImage(self, item):
+ return -1
+
+ def OnGetItemAttr(self, item):
+ cat_id = self.cats_mgr.GetCategories()[item]
+
+ cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+ if cattr['show']:
+ c = cattr['color']
+
+ back_c = wx.Colour(*map(int, c.split(':')))
+ text_c = wx.Colour(*ContrastColor(back_c))
+ else:
+ back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
+ text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
+
+ # if it is in scope of the method, gui falls, using self solved it
+ self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
+ return self.l
+
+ def OnCategoryRightUp(self, event):
+ """!Show context menu on right click"""
+ item, flags = self.HitTest((event.GetX(), event.GetY()))
+ if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
+ self.rightClickedItemIdx = item
+
+ # generate popup-menu
+ cat_idx = self.rightClickedItemIdx
+
+ cats = self.cats_mgr.GetCategories()
+ cat_id = cats[cat_idx]
+ showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
+
+ menu = wx.Menu()
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Rename class"))
+ self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Set color"))
+ self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Change opacity level"))
+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
+
+ if showed:
+ text = _("Hide")
+ else:
+ text = _("Show")
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text = text)
+ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
+ attrs={'show' : not showed}),
+ id=item_id)
+
+ menu.AppendSeparator()
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Move to top"))
+ self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
+ if cat_idx == 0:
+ menu.Enable(item_id, False)
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Move to bottom"))
+ self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
+ if cat_idx == len(cats) - 1:
+ menu.Enable(item_id, False)
+
+ menu.AppendSeparator()
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Move category up"))
+ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
+ if cat_idx == 0:
+ menu.Enable(item_id, False)
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Move category down"))
+ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
+ if cat_idx == len(cats) - 1:
+ menu.Enable(item_id, False)
+
+ menu.AppendSeparator()
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Export class raster"))
+ self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def OnExportCatRast(self, event):
+ """!Export training areas"""
+ #TODO
+ # GMessage(parent=self, message=_("No class raster to export."))
+ # return
+
+ cat_idx = self.rightClickedItemIdx
+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
+
+ self.cats_mgr.ExportCatRast(cat_id)
+
+ def OnMoveUp(self, event):
+ cat_idx = self.rightClickedItemIdx
+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
+ self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def OnMoveDown(self, event):
+ cat_idx = self.rightClickedItemIdx
+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
+ self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def OnMoveTop(self, event):
+ cat_idx = self.rightClickedItemIdx
+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
+ self.cats_mgr.ChangePosition(cat_id, 0)
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def OnMoveBottom(self, event):
+ cat_idx = self.rightClickedItemIdx
+ cats = self.cats_mgr.GetCategories()
+ cat_id = cats[cat_idx]
+ self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
+ def OnSetColor(self, event):
+ """!Popup opacity level indicator"""
+ cat_idx = self.rightClickedItemIdx
+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
+
+ col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
+ col = map(int, col.split(':'))
+
+ col_data = wx.ColourData()
+ col_data.SetColour(wx.Colour(*col))
+
+ dlg = wx.ColourDialog(self, col_data)
+ dlg.GetColourData().SetChooseFull(True)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ color = dlg.GetColourData().GetColour().Get()
+ color = ':'.join(map(str, color))
+ self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
+
+ dlg.Destroy()
+
+ def OnPopupOpacityLevel(self, event):
+ """!Popup opacity level indicator"""
+
+ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
+ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+ value = cat_attrs['opacity'] * 100
+ name = cat_attrs['name']
+
+ dlg = SetOpacityDialog(self, opacity = value,
+ title = _("Change opacity of class <%s>" % name))
+
+ dlg.applyOpacity.connect(lambda value:
+ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
+ dlg.CentreOnParent()
+
+ if dlg.ShowModal() == wx.ID_OK:
+ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
+
+ dlg.Destroy()
+
+ def OnRename(self, event):
+ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
+ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
+
+ dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
+ dlg.CentreOnParent()
+
+ while True:
+ if dlg.ShowModal() == wx.ID_OK:
+ name = dlg.GetNewName().strip()
+ if not name:
+ GMessage(parent=self, message=_("Empty name was inserted."))
+ else:
+ self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
+ break
+ else:
+ break
+
+ dlg.Destroy()
+
+ def _setCatAttrs(self, cat_id, attrs):
+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+ self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/plots.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -14,82 +14,83 @@
"""
import wx
import numpy as np
-import random
+from math import ceil
+#TODO testing
+import time
+from multiprocessing import Process, Queue
+
+from copy import deepcopy
+from scatt_plot.core_c import MergeArrays, ApplyColormap
+from scatt_plot.dialogs import ManageBusyCursorMixin
+from core.settings import UserSettings
+
try:
- haveMatPlot = True
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
- FigureCanvasWxAgg as FigCanvas, \
- NavigationToolbar2WxAgg as NavigationToolbar
-except ImportError:
- haveMatPlot = False
+ FigureCanvasWxAgg as FigCanvas
+ from matplotlib.lines import Line2D
+ from matplotlib.artist import Artist
+ from matplotlib.mlab import dist_point_to_segment
+ from matplotlib.patches import Polygon, Ellipse, Rectangle
+ import matplotlib.image as mi
+ import matplotlib.colors as mcolors
+ import matplotlib.cbook as cbook
+except ImportError as e:
+ raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
-class ScatterPlotWidget(wx.Panel):
- def __init__(self, parent, scatt_id, scatt_mgr,
+import grass.script as grass
+from grass.pydispatch.signal import Signal
+
+class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):
+ def __init__(self, parent, scatt_id, scatt_mgr, transpose,
id = wx.ID_ANY):
-
+ #TODO should not be transpose and scatt_id but x, y
wx.Panel.__init__(self, parent, id)
+ # bacause of aui (if floatable it can not take cursor from parent)
+ ManageBusyCursorMixin.__init__(self, window=self)
self.parent = parent
+ self.full_extend = None
+ self.mode = None
self._createWidgets()
self._doLayout()
self.scatt_id = scatt_id
self.scatt_mgr = scatt_mgr
- self.press_coords = None
self.cidpress = None
self.cidrelease = None
- def StartCategoryEdit(self):
- 'connect to all the events we need'
- self.cidpress = self.canvas.mpl_connect(
- 'button_press_event', self.OnPress)
- self.cidrelease = self.canvas.mpl_connect(
- 'button_release_event', self.OnRelease)
- #self.cidmotion = self.canvas.mpl_connect(
- # 'motion_notify_event', self.OnRelease)
+ self.rend_dt = {}
- def OnPress(self, event):
- 'on button press we will see if the mouse is over us and store some data'
+ self.transpose = transpose
- if event.xdata and event.ydata:
- self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
- else:
- self.press_coords = None
+ self.inverse = False
- def OnRelease(self, event):
- 'on release we reset the press data'
+ self.SetSize((200, 100))
+ self.Layout()
- if event.xdata and event.ydata and self.press_coords:
+ self.base_scale = 1.2
+ self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
- bbox = {}
- if event.ydata > self.press_coords['y']:
- bbox['up_y'] = event.ydata
- bbox['btm_y'] = self.press_coords['y']
- else:
- bbox['up_y'] = self.press_coords['y']
- bbox['btm_y'] = event.ydata
+ self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
+ self.cursorMove = Signal("ScatterPlotWidget.cursorMove")
- if event.xdata > self.press_coords['x']:
- bbox['up_x'] = event.xdata
- bbox['btm_x'] = self.press_coords['x']
- else:
- bbox['up_x'] = self.press_coords['x']
- bbox['btm_x'] = event.xdata
+ self.contex_menu = ScatterPlotContextMenu(plot = self)
- self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
+ self.ciddscroll = None
- def StopCategoryEdit(self):
- 'disconnect all the stored connection ids'
+ self.canvas.mpl_connect('motion_notify_event', self.Motion)
+ self.canvas.mpl_connect('button_press_event', self.OnPress)
+ self.canvas.mpl_connect('button_release_event', self.OnRelease)
+ self.canvas.mpl_connect('draw_event', self.draw_callback)
+ self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)
- if self.cidpress:
- self.canvas.mpl_disconnect(self.cidpress)
- if self.cidrelease:
- self.canvas.mpl_disconnect(self.cidrelease)
- #self.canvas.mpl_disconnect(self.cidmotion)
+ def draw_callback(self, event):
+ self.polygon_drawer.draw_callback(event)
+ self.axes.draw_artist(self.zoom_rect)
def _createWidgets(self):
@@ -97,103 +98,856 @@
# 5x4 inches, 100 dots-per-inch
#
self.dpi = 100
- self.fig = Figure((5.0, 4.0), dpi=self.dpi)
+ self.fig = Figure((1.0, 1.0), dpi=self.dpi)
+ self.fig.autolayout = True
+
self.canvas = FigCanvas(self, -1, self.fig)
- # Since we have only one plot, we can use add_axes
- # instead of add_subplot, but then the subplot
- # configuration tool in the navigation toolbar wouldn't
- # work.
- #
- self.axes = self.fig.add_subplot(111)
-
- # Bind the 'pick' event for clicking on one of the bars
- #
- self.canvas.mpl_connect('button_press_event', self.on_pick)
-
+ self.axes = self.fig.add_axes([0.0,0.0,1,1])
- # Create the navigation toolbar, tied to the canvas
- #
- self.toolbar = NavigationToolbar(self.canvas)
-
+ pol = Polygon(list(zip([0], [0])), animated=True)
+ self.axes.add_patch(pol)
+ self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
+
+ self.zoom_wheel_coords = None
+ self.zoom_rect_coords = None
+ self.zoom_rect = Polygon(list(zip([0], [0])), facecolor = 'none')
+ self.zoom_rect.set_visible(False)
+ self.axes.add_patch(self.zoom_rect)
+
+ def ZoomToExtend(self):
+ if self.full_extend:
+ self.axes.axis(self.full_extend)
+ self.canvas.draw()
+
+ def SetMode(self, mode):
+ self._deactivateMode()
+ if mode == 'zoom':
+ self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.ZoomWheel)
+ self.mode = 'zoom'
+ elif mode == 'zoom_extend':
+ self.mode = 'zoom_extend'
+ elif mode == 'pan':
+ self.mode = 'pan'
+ elif mode:
+ self.polygon_drawer.SetMode(mode)
+
+ def SetSelectionPolygonMode(self, activate):
+ self.polygon_drawer.SetSelectionPolygonMode(activate)
+
+ def _deactivateMode(self):
+ self.mode = None
+ self.polygon_drawer.SetMode(None)
+
+ if self.ciddscroll:
+ self.canvas.mpl_disconnect(self.ciddscroll)
+
+ self.zoom_rect.set_visible(False)
+ self._stopCategoryEdit()
+
+ def GetCoords(self):
+
+ coords = self.polygon_drawer.GetCoords()
+ if coords is None:
+ return
+
+ if self.transpose:
+ for c in coords:
+ tmp = c[0]
+ c[0] = c[1]
+ c[1] = tmp
+
+ return coords
+
+ def SetEmpty(self):
+ return self.polygon_drawer.SetEmpty()
+
+ def OnRelease(self, event):
+ if not self.mode == "zoom": return
+ self.zoom_rect.set_visible(False)
+ self.ZoomRectangle(event)
+ self.canvas.draw()
+
+ def OnPress(self, event):
+ 'on button press we will see if the mouse is over us and store some data'
+ if not event.inaxes:
+ return
+ if self.mode == "zoom_extend":
+ self.ZoomToExtend()
+
+ if event.xdata and event.ydata:
+ self.zoom_wheel_coords = { 'x' : event.xdata, 'y' : event.ydata}
+ self.zoom_rect_coords = { 'x' : event.xdata, 'y' : event.ydata}
+ else:
+ self.zoom_wheel_coords = None
+ self.zoom_rect_coords = None
+
+ def _stopCategoryEdit(self):
+ 'disconnect all the stored connection ids'
+
+ if self.cidpress:
+ self.canvas.mpl_disconnect(self.cidpress)
+ if self.cidrelease:
+ self.canvas.mpl_disconnect(self.cidrelease)
+ #self.canvas.mpl_disconnect(self.cidmotion)
+
def _doLayout(self):
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
- self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
- self.main_sizer.AddSpacer(10)
-
self.SetSizer(self.main_sizer)
self.main_sizer.Fit(self)
- def Plot(self, scatts, styles):
+ def Plot(self, cats_order, scatts, ellipses, styles):
""" Redraws the figure
"""
-
+
+ callafter_list = []
+
+ if self.full_extend:
+ cx = self.axes.get_xlim()
+ cy = self.axes.get_ylim()
+ c = cx + cy
+ else:
+ c = None
+
+ q = Queue()
+ p = Process(target=MergeImg, args=(cats_order, scatts, styles,
+ self.rend_dt, q))
+ p.start()
+ merged_img, self.full_extend, self.rend_dt = q.get()
+ p.join()
+
+ #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
self.axes.clear()
- for cat_id, scatt in scatts.iteritems():
+ self.axes.axis('equal')
+
+ if self.transpose:
+ merged_img = np.transpose(merged_img, (1, 0, 2))
+
+ img = imshow(self.axes, merged_img,
+ extent= [int(ceil(x)) for x in self.full_extend],
+ origin='lower',
+ interpolation='nearest',
+ aspect="equal")
+
+ callafter_list.append([self.axes.draw_artist, [img]])
+ callafter_list.append([grass.try_remove, [merged_img.filename]])
+
+ for cat_id in cats_order:
if cat_id == 0:
- cmap = matplotlib.cm.jet
- cmap.set_bad('w',1.)
- cmap._init()
- cmap._lut[len(cmap._lut) - 1, -1] = 0
- else:
- colors = styles[cat_id]['color'].split(":")
+ continue
+ if not ellipses.has_key(cat_id):
+ continue
+
+ e = ellipses[cat_id]
+ if not e:
+ continue
- cmap = matplotlib.cm.jet
- cmap.set_bad('w',1.)
- cmap._init()
- cmap._lut[len(cmap._lut) - 1, -1] = 0
- cmap._lut[:, 0] = int(colors[0])/255.0
- cmap._lut[:, 1] = int(colors[1])/255.0
- cmap._lut[:, 2] = int(colors[2])/255.0
+ colors = styles[cat_id]['color'].split(":")
+ if self.transpose:
+ e['theta'] = 360 - e['theta'] + 90
+ if e['theta'] >= 360:
+ e['theta'] = abs(360 - e['theta'])
+
+ e['pos'] = [e['pos'][1], e['pos'][0]]
- masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
+ ellip = Ellipse(xy=e['pos'],
+ width=e['width'],
+ height=e['height'],
+ angle=e['theta'],
+ edgecolor="w",
+ linewidth=1.5,
+ facecolor='None')
+ self.axes.add_artist(ellip)
+ callafter_list.append([self.axes.draw_artist, [ellip]])
+ color = map(lambda v : int(v)/255.0, styles[cat_id]['color'].split(":"))
- b1_i = scatt['bands_info']['b1']
- b2_i = scatt['bands_info']['b2']
+ ellip = Ellipse(xy=e['pos'],
+ width=e['width'],
+ height=e['height'],
+ angle=e['theta'],
+ edgecolor=color,
+ linewidth=1,
+ facecolor='None')
+
+ self.axes.add_artist(ellip)
+ callafter_list.append([self.axes.draw_artist, [ellip]])
- #self.axes.set_xlim((0, 270))
- #self.axes.set_ylim((0, 270))
- np.savetxt("/home/ostepok/Desktop/data.txt", scatt['np_vals'], fmt = '%d')
+ center = Line2D([e['pos'][0]], [e['pos'][1]],
+ marker='x',
+ markeredgecolor='w',
+ #markerfacecolor=color,
+ markersize=2)
+ self.axes.add_artist(center)
+ callafter_list.append([self.axes.draw_artist, [center]])
+ callafter_list.append([self.fig.canvas.blit, []])
- #TODO needs optimization
- img = self.axes.imshow(masked_cat, cmap = cmap,
- origin = 'lower',
- extent = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5),
- interpolation='nearest')
-
- self.canvas.draw()
+ if c:
+ self.axes.axis(c)
+ wx.CallAfter(lambda : self.CallAfter(callafter_list))
+
+ def CallAfter(self, funcs_list):
+ while funcs_list:
+ fcn, args = funcs_list.pop(0)
+ fcn(*args)
-
- def on_pick(self, event):
- pass
- # The event received here is of the type
- # matplotlib.backend_bases.PickEvent
- #
- # It carries lots of information, of which we're using
- # only a small amount here.
- #
- #box_points = event.x
+ self.canvas.draw()
+
+ def CleanUp(self):
+ self.plotClosed.emit(scatt_id = self.scatt_id)
+ self.Destroy()
+
+ def ZoomWheel(self, event):
+ # get the current x and y limits
+ if not event.inaxes:
+ return
+ # tcaswell
+ # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
+ cur_xlim = self.axes.get_xlim()
+ cur_ylim = self.axes.get_ylim()
- #msg = "You've clicked on a bar with coords:\n"
+ xdata = event.xdata
+ ydata = event.ydata
+ if event.button == 'up':
+ scale_factor = 1/self.base_scale
+ elif event.button == 'down':
+ scale_factor = self.base_scale
+ else:
+ scale_factor = 1
+
+ extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
+ xdata + (cur_xlim[1] - xdata) * scale_factor,
+ ydata - (ydata - cur_ylim[0]) * scale_factor,
+ ydata + (cur_ylim[1] - ydata) * scale_factor)
+
+ self.axes.axis(extend)
- #dlg = wx.MessageDialog(
- # self,
- # msg,
- # "Click!",
- # wx.OK | wx.ICON_INFORMATION)
+ self.canvas.draw()
- #dlg.ShowModal()
- #dlg.Destroy()
+ def ZoomRectangle(self, event):
+ # get the current x and y limits
+ if not self.mode == "zoom": return
+ if event.inaxes is None: return
+ if event.button != 1: return
- def on_exit(self, event):
+ cur_xlim = self.axes.get_xlim()
+ cur_ylim = self.axes.get_ylim()
+
+ x1, y1 = event.xdata, event.ydata
+ x2 = deepcopy(self.zoom_rect_coords['x'])
+ y2 = deepcopy(self.zoom_rect_coords['y'])
+
+ if x1 == x2 or y1 == y2:
+ return
+
+ self.axes.axis((x1, x2, y1, y2))
+ #self.axes.set_xlim(x1, x2)#, auto = True)
+ #self.axes.set_ylim(y1, y2)#, auto = True)
+ self.canvas.draw()
+
+ def Motion(self, event):
+ self.PanMotion(event)
+ self.ZoomRectMotion(event)
+
+ if event.inaxes is None:
+ return
+
+ self.cursorMove.emit(x=event.xdata, y=event.ydata, scatt_id=self.scatt_id)
+
+ def OnCanvasLeave(self, event):
+ self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)
+
+ def PanMotion(self, event):
+ 'on mouse movement'
+ if not self.mode == "pan":
+ return
+ if event.inaxes is None:
+ return
+ if event.button != 1:
+ return
+
+ cur_xlim = self.axes.get_xlim()
+ cur_ylim = self.axes.get_ylim()
+
+ x,y = event.xdata, event.ydata
+
+ mx = (x - self.zoom_wheel_coords['x']) * 0.6
+ my = (y - self.zoom_wheel_coords['y']) * 0.6
+
+ extend = (cur_xlim[0] - mx, cur_xlim[1] - mx, cur_ylim[0] - my, cur_ylim[1] - my)
+
+ self.zoom_wheel_coords['x'] = x
+ self.zoom_wheel_coords['y'] = y
+
+ self.axes.axis(extend)
+
+ #self.canvas.copy_from_bbox(self.axes.bbox)
+ #self.canvas.restore_region(self.background)
+ self.canvas.draw()
+
+ def ZoomRectMotion(self, event):
+ if not self.mode == "zoom": return
+ if event.inaxes is None: return
+ if event.button != 1: return
+
+ x1, y1 = event.xdata, event.ydata
+ self.zoom_rect.set_visible(True)
+ x2 = self.zoom_rect_coords['x']
+ y2 = self.zoom_rect_coords['y']
+
+ self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))
+
+ #self.axes.draw_artist(self.zoom_rect)
+ self.canvas.draw()
+
+def MergeImg(cats_order, scatts, styles, rend_dt, output_queue):
+
+ init = True
+ merged_img = None
+ merge_tmp = grass.tempfile()
+ for cat_id in cats_order:
+ if not scatts.has_key(cat_id):
+ continue
+ scatt = scatts[cat_id]
+ #print "color map %d" % cat_id
+ #TODO make more general
+ if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
+ not styles[cat_id]['show']):
+ if rend_dt.has_key(cat_id) and not rend_dt[cat_id]:
+ del rend_dt[cat_id]
+ continue
+ if init:
+
+ b2_i = scatt['bands_info']['b1']
+ b1_i = scatt['bands_info']['b2']
+
+ full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5)
- self.CleanUp()
+ # if it does not need to be updated and was already rendered
+ if not _renderCat(cat_id, rend_dt, scatt, styles):
+ # is empty - has only zeros
+ if rend_dt[cat_id] is None:
+ continue
+ else:
+ masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
+ vmax = np.amax(masked_cat)
+ # totally empty -> no need to render
+ if vmax == 0:
+ render_cat_ids[cat_id] = None
+ continue
+
+ cmap = _getColorMap(cat_id, styles)
+ masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
+
+ cmap = np.uint8(cmap._lut * 255)
+ sh =masked_cat.shape
+
+ rend_dt[cat_id] = {}
+ if cat_id != 0:
+ rend_dt[cat_id]['color'] = styles[cat_id]['color']
+ rend_dt[cat_id]['dt'] = np.memmap(grass.tempfile(), dtype='uint8', mode='w+',
+ shape=(sh[0], sh[1], 4))
+ #colored_cat = np.zeros(dtype='uint8', )
+ ApplyColormap(masked_cat, masked_cat.mask, cmap, rend_dt[cat_id]['dt'])
+
+ #colored_cat = np.uint8(cmap(masked_cat) * 255)
+ del masked_cat
+ del cmap
+
+ #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
+ if init:
+ merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+',
+ shape=rend_dt[cat_id]['dt'].shape)
+ merged_img[:] = rend_dt[cat_id]['dt']
+ init = False
+ else:
+ MergeArrays(merged_img, rend_dt[cat_id]['dt'], styles[cat_id]['opacity'])
+
+ """
+ #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape)
+ c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
+
+ #TODO apply strides and there will be no need for loop
+ #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
+
+ for i in range(3):
+ merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
+ merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
+
+ del c_img_a
+ """
+
+ output_queue.put((merged_img, full_extend, rend_dt))
+
+def _renderCat(cat_id, rend_dt, scatt, styles):
+ return True
+
+ if not rend_dt.has_key(cat_id):
+ return True
+ if not rend_dt[cat_id]:
+ return False
+ if scatt['render']:
+ return True
+ if cat_id != 0 and \
+ rend_dt[cat_id]['color'] != styles[cat_id]['color']:
+ return True
+
+ return False
+
+def _getColorMap(cat_id, styles):
+ cmap = matplotlib.cm.jet
+ if cat_id == 0:
+ cmap.set_bad('w',1.)
+ cmap._init()
+ cmap._lut[len(cmap._lut) - 1, -1] = 0
+ else:
+ colors = styles[cat_id]['color'].split(":")
+
+ cmap.set_bad('w',1.)
+ cmap._init()
+ cmap._lut[len(cmap._lut) - 1, -1] = 0
+ cmap._lut[:, 0] = int(colors[0])/255.0
+ cmap._lut[:, 1] = int(colors[1])/255.0
+ cmap._lut[:, 2] = int(colors[2])/255.0
+
+ return cmap
+
+class ScatterPlotContextMenu:
+ def __init__(self, plot):
+
+ self.plot = plot
+ self.canvas = plot.canvas
+ self.cidpress = self.canvas.mpl_connect(
+ 'button_press_event', self.ContexMenu)
+
+ def ContexMenu(self, event):
+ if not event.inaxes:
+ return
+
+ if event.button == 3:
+ menu = wx.Menu()
+ menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
+
+ for item in menu_items:
+ item_id = wx.ID_ANY
+ menu.Append(item_id, text = item[1])
+ menu.Bind(wx.EVT_MENU, item[2], id = item_id)
+
+ wx.CallAfter(self.ShowMenu, menu)
+
+ def ShowMenu(self, menu):
+ self.plot.PopupMenu(menu)
+ menu.Destroy()
+ self.plot.ReleaseMouse()
+
+class PolygonDrawer:
+ """
+ An polygon editor.
+ """
+ def __init__(self, ax, pol, empty_pol):
+ if pol.figure is None:
+ raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
+ self.ax = ax
+ self.canvas = pol.figure.canvas
+
+ self.showverts = True
+
+ self.pol = pol
+ self.empty_pol = empty_pol
+
+ x, y = zip(*self.pol.xy)
+
+ style = self._getPolygonStyle()
+
+ self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
+ self.ax.add_line(self.line)
+ #self._update_line(pol)
+
+ cid = self.pol.add_callback(self.poly_changed)
+ self.moving_ver_idx = None # the active vert
+
+ self.mode = None
+
+ if self.empty_pol:
+ self._show(False)
+
+ #self.canvas.mpl_connect('draw_event', self.draw_callback)
+ self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
+ self.canvas.mpl_connect('button_release_event', self.button_release_callback)
+ self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
+
+ self.it = 0
+
+ def _getPolygonStyle(self):
+ style = {}
+ style['sel_pol'] = UserSettings.Get(group='scatt',
+ key='selection',
+ subkey='sel_pol')
+ style['sel_pol_vertex'] = UserSettings.Get(group='scatt',
+ key='selection',
+ subkey='sel_pol_vertex')
+
+ style['sel_pol'] = [i / 255.0 for i in style['sel_pol']]
+ style['sel_pol_vertex'] = [i / 255.0 for i in style['sel_pol_vertex']]
+
+ return style
+
+ def _getSnapTresh(self):
+ return UserSettings.Get(group='scatt',
+ key='selection',
+ subkey='snap_tresh')
+
+ def SetMode(self, mode):
+ self.mode = mode
+
+ def SetSelectionPolygonMode(self, activate):
- def CleanUp(self):
+ self.Show(activate)
+ if not activate and self.mode:
+ self.SetMode(None)
+
+ def Show(self, show):
+ if show:
+ if not self.empty_pol:
+ self._show(True)
+ else:
+ self._show(False)
+
+ def GetCoords(self):
+ if self.empty_pol:
+ return None
+
+ coords = deepcopy(self.pol.xy)
+ return coords
+
+ def SetEmpty(self):
+ self._setEmptyPol(True)
+
+ def _setEmptyPol(self, empty_pol):
+ self.empty_pol = empty_pol
+ if self.empty_pol:
+ #TODO
+ self.pol.xy = np.array([[0, 0]])
+ self._show(not empty_pol)
+
+ def _show(self, show):
+
+ self.show = show
+
+ self.line.set_visible(self.show)
+ self.pol.set_visible(self.show)
+
+ self.Redraw()
+
+ def Redraw(self):
+ if self.show:
+ self.ax.draw_artist(self.pol)
+ self.ax.draw_artist(self.line)
+ self.canvas.blit(self.ax.bbox)
+ self.canvas.draw()
+
+ def draw_callback(self, event):
+
+ style=self._getPolygonStyle()
+ self.pol.set_facecolor(style['sel_pol'])
+ self.line.set_markerfacecolor(style['sel_pol_vertex'])
+
+ self.background = self.canvas.copy_from_bbox(self.ax.bbox)
+ self.ax.draw_artist(self.pol)
+ self.ax.draw_artist(self.line)
+
+ def poly_changed(self, pol):
+ 'this method is called whenever the polygon object is called'
+ # only copy the artist props to the line (except visibility)
+ vis = self.line.get_visible()
+ Artist.update_from(self.line, pol)
+ self.line.set_visible(vis) # don't use the pol visibility state
+
+ def get_ind_under_point(self, event):
+ 'get the index of the vertex under point if within treshold'
+
+ # display coords
+ xy = np.asarray(self.pol.xy)
+ xyt = self.pol.get_transform().transform(xy)
+ xt, yt = xyt[:, 0], xyt[:, 1]
+ d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
+ indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
+ ind = indseq[0]
+
+ if d[ind]>=self._getSnapTresh():
+ ind = None
+
+ return ind
+
+ def OnButtonPressed(self, event):
+ if not event.inaxes:
+ return
+
+ if event.button in [2, 3]:
+ return
+
+ if self.mode == "delete_vertex":
+ self._deleteVertex(event)
+ elif self.mode == "add_boundary_vertex":
+ self._addVertexOnBoundary(event)
+ elif self.mode == "add_vertex":
+ self._addVertex(event)
+ elif self.mode == "remove_polygon":
+ self.SetEmpty()
+ self.moving_ver_idx = self.get_ind_under_point(event)
+
+ def button_release_callback(self, event):
+ 'whenever a mouse button is released'
+ if not self.showverts: return
+ if event.button != 1: return
+ self.moving_ver_idx = None
+
+ def ShowVertices(self, show):
+ self.showverts = show
+ self.line.set_visible(self.showverts)
+ if not self.showverts: self.moving_ver_idx = None
+
+ def _deleteVertex(self, event):
+ ind = self.get_ind_under_point(event)
+
+ if ind is None or self.empty_pol:
+ return
+
+ if len(self.pol.xy) <= 2:
+ self.empty_pol = True
+ self._show(False)
+ return
+
+ coords = []
+ for i,tup in enumerate(self.pol.xy):
+ if i == ind:
+ continue
+ elif i == 0 and ind == len(self.pol.xy) - 1:
+ continue
+ elif i == len(self.pol.xy) - 1 and ind == 0:
+ continue
+
+ coords.append(tup)
+
+ self.pol.xy = coords
+ self.line.set_data(zip(*self.pol.xy))
+
+ self.Redraw()
+
+ def _addVertexOnBoundary(self, event):
+ if self.empty_pol:
+ return
+
+ xys = self.pol.get_transform().transform(self.pol.xy)
+ p = event.x, event.y # display coords
+ for i in range(len(xys)-1):
+ s0 = xys[i]
+ s1 = xys[i+1]
+ d = dist_point_to_segment(p, s0, s1)
+
+ if d<=self._getSnapTresh():
+ self.pol.xy = np.array(
+ list(self.pol.xy[:i + 1]) +
+ [(event.xdata, event.ydata)] +
+ list(self.pol.xy[i + 1:]))
+ self.line.set_data(zip(*self.pol.xy))
+ break
+
+ self.Redraw()
+
+ def _addVertex(self, event):
+
+ if self.empty_pol:
+ pt = (event.xdata, event.ydata)
+ self.pol.xy = np.array([pt, pt])
+ self._show(True)
+ self.empty_pol = False
+ else:
+ self.pol.xy = np.array(
+ [(event.xdata, event.ydata)] +
+ list(self.pol.xy[1:]) +
+ [(event.xdata, event.ydata)])
+
+ self.line.set_data(zip(*self.pol.xy))
- self.parent.OnPlotClosed(self.scatt_id)
- self.Destroy()
\ No newline at end of file
+ self.Redraw()
+
+ def motion_notify_callback(self, event):
+ 'on mouse movement'
+ if not self.mode == "move_vertex": return
+ if not self.showverts: return
+ if self.empty_pol: return
+ if self.moving_ver_idx is None: return
+ if event.inaxes is None: return
+ if event.button != 1: return
+
+ self.it += 1
+
+ x,y = event.xdata, event.ydata
+
+ self.pol.xy[self.moving_ver_idx] = x,y
+ if self.moving_ver_idx == 0:
+ self.pol.xy[len(self.pol.xy) - 1] = x,y
+ elif self.moving_ver_idx == len(self.pol.xy) - 1:
+ self.pol.xy[0] = x,y
+
+ self.line.set_data(zip(*self.pol.xy))
+
+ self.canvas.restore_region(self.background)
+
+ self.Redraw()
+
+class ModestImage(mi.AxesImage):
+ """
+ Computationally modest image class.
+
+ ModestImage is an extension of the Matplotlib AxesImage class
+ better suited for the interactive display of larger images. Before
+ drawing, ModestImage resamples the data array based on the screen
+ resolution and view window. This has very little affect on the
+ appearance of the image, but can substantially cut down on
+ computation since calculations of unresolved or clipped pixels
+ are skipped.
+
+ The interface of ModestImage is the same as AxesImage. However, it
+ does not currently support setting the 'extent' property. There
+ may also be weird coordinate warping operations for images that
+ I'm not aware of. Don't expect those to work either.
+
+ Author: Chris Beaumont <beaumont at hawaii.edu>
+ """
+ def __init__(self, minx=0.0, miny=0.0, *args, **kwargs):
+ if 'extent' in kwargs and kwargs['extent'] is not None:
+ raise NotImplementedError("ModestImage does not support extents")
+
+ self._full_res = None
+ self._sx, self._sy = None, None
+ self._bounds = (None, None, None, None)
+ self.minx = minx
+ self.miny = miny
+
+ super(ModestImage, self).__init__(*args, **kwargs)
+
+ def set_data(self, A):
+ """
+ Set the image array
+
+ ACCEPTS: numpy/PIL Image A
+ """
+ self._full_res = A
+ self._A = A
+
+ if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
+ np.float):
+ raise TypeError("Image data can not convert to float")
+
+ if (self._A.ndim not in (2, 3) or
+ (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
+ raise TypeError("Invalid dimensions for image data")
+
+ self._imcache =None
+ self._rgbacache = None
+ self._oldxslice = None
+ self._oldyslice = None
+ self._sx, self._sy = None, None
+
+ def get_array(self):
+ """Override to return the full-resolution array"""
+ return self._full_res
+
+ def _scale_to_res(self):
+ """ Change self._A and _extent to render an image whose
+ resolution is matched to the eventual rendering."""
+
+ ax = self.axes
+ ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
+ xlim, ylim = ax.get_xlim(), ax.get_ylim()
+ dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
+
+ y0 = max(self.miny, ylim[0] - 5)
+ y1 = min(self._full_res.shape[0] + self.miny, ylim[1] + 5)
+ x0 = max(self.minx, xlim[0] - 5)
+ x1 = min(self._full_res.shape[1] + self.minx, xlim[1] + 5)
+ y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
+
+ sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
+ sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
+
+ # have we already calculated what we need?
+ if sx == self._sx and sy == self._sy and \
+ x0 == self._bounds[0] and x1 == self._bounds[1] and \
+ y0 == self._bounds[2] and y1 == self._bounds[3]:
+ return
+
+ self._A = self._full_res[y0 - self.miny:y1 - self.miny:sy,
+ x0 - self.minx:x1 - self.minx:sx]
+
+ x1 = x0 + self._A.shape[1] * sx
+ y1 = y0 + self._A.shape[0] * sy
+
+ self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
+ self._sx = sx
+ self._sy = sy
+ self._bounds = (x0, x1, y0, y1)
+ self.changed()
+
+ def draw(self, renderer, *args, **kwargs):
+ self._scale_to_res()
+ super(ModestImage, self).draw(renderer, *args, **kwargs)
+
+def imshow(axes, X, cmap=None, norm=None, aspect=None,
+ interpolation=None, alpha=None, vmin=None, vmax=None,
+ origin=None, extent=None, shape=None, filternorm=1,
+ filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
+ """Similar to matplotlib's imshow command, but produces a ModestImage
+
+ Unlike matplotlib version, must explicitly specify axes
+ Author: Chris Beaumont <beaumont at hawaii.edu>
+ """
+
+ if not axes._hold:
+ axes.cla()
+ if norm is not None:
+ assert(isinstance(norm, mcolors.Normalize))
+ if aspect is None:
+ aspect = rcParams['image.aspect']
+ axes.set_aspect(aspect)
+
+ if extent:
+ minx=extent[0]
+ miny=extent[2]
+ else:
+ minx=0.0
+ miny=0.0
+
+ im = ModestImage(minx, miny, axes, cmap, norm, interpolation, origin, extent,
+ filternorm=filternorm,
+ filterrad=filterrad, resample=resample, **kwargs)
+
+ im.set_data(X)
+ im.set_alpha(alpha)
+ axes._set_artist_props(im)
+
+ if im.get_clip_path() is None:
+ # image does not already have clipping set, clip to axes patch
+ im.set_clip_path(axes.patch)
+
+ #if norm is None and shape is None:
+ # im.set_clim(vmin, vmax)
+ if vmin is not None or vmax is not None:
+ im.set_clim(vmin, vmax)
+ else:
+ im.autoscale_None()
+ im.set_url(url)
+
+ # update ax.dataLim, and, if autoscaling, set viewLim
+ # to tightly fit the image, regardless of dataLim.
+ im.set_extent(im.get_extent())
+
+ axes.images.append(im)
+ im._remove_method = lambda h: axes.images.remove(h)
+
+ return im
Added: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py (rev 0)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/scatt_core.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -0,0 +1,835 @@
+"""!
+ at package scatt_plot.scatt_plot
+
+ at brief Non GUI functions.
+
+Classes:
+ - scatt_core::Core
+ - scatt_core::CatRastUpdater
+ - scatt_core::AnalyzedData
+ - scatt_core::ScattPlotsCondsData
+ - scatt_core::ScattPlotsData
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+import os
+import sys
+
+import time
+
+import numpy as np
+
+# used iclass perimeters algorithm instead of convolve2d
+#from matplotlib.path import Path
+#from scipy.signal import convolve2d
+
+from math import sqrt, ceil, floor
+from copy import deepcopy
+
+from core.gcmd import GException, GError, RunCommand
+
+import grass.script as grass
+
+from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
+ Rasterize, SC_SCATT_DATA, SC_SCATT_CONDITIONS
+
+class Core:
+ """!Represents scatter plot backend.
+ """
+ def __init__(self):
+
+ self.an_data = AnalyzedData()
+
+ self.scatts_dt = ScattPlotsData(self.an_data)
+ self.scatt_conds_dt = ScattPlotsCondsData(self.an_data)
+
+ self.cat_rast_updater = CatRastUpdater(self.scatts_dt, self.an_data, self)
+
+ def SetData(self, bands):
+ """Set bands for analysis.
+ """
+ self.an_data.Create(bands)
+
+ n_bands = len(self.GetBands())
+
+ self.scatts_dt.Create(n_bands)
+ self.scatt_conds_dt.Create(n_bands)
+
+ def AddCategory(self, cat_id):
+ self.scatts_dt.AddCategory(cat_id)
+ return self.scatt_conds_dt.AddCategory(cat_id)
+
+ def DeleteCategory(self, cat_id):
+ self.scatts_dt.DeleteCategory(cat_id)
+ self.scatt_conds_dt.DeleteCategory(cat_id)
+
+ def CleanUp(self):
+ self.scatts_dt.CleanUp()
+ self.scatt_conds_dt.CleanUp()
+
+ def GetBands(self):
+ return self.an_data.GetBands()
+
+ def GetScattsData(self):
+ return self.scatts_dt, self.scatt_conds_dt;
+
+ def GetRegion(self):
+ return self.an_data.GetRegion()
+
+ def GetCatRast(self, cat_id):
+ return self.scatts_dt.GetCatRast(cat_id)
+
+ def AddScattPlots(self, scatt_ids):
+
+ for s_id in scatt_ids:
+ self.scatts_dt.AddScattPlot(scatt_id = s_id)
+
+ cats_ids = self.scatts_dt.GetCategories()
+ self.ComputeCatsScatts(cats_ids)
+
+ def SetEditCatData(self, cat_id, scatt_id, bbox, value):
+
+ if cat_id not in self.scatts_dt.GetCategories():
+ raise GException(_("Select category for editing."))
+
+ if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
+ return None
+
+ arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+
+ for k, v in bbox.iteritems():
+ bbox[k] = self._validExtend(v)
+
+ arr[bbox['btm_y'] : bbox['up_y'], bbox['btm_x'] : bbox['up_x']] = value
+
+ self.ComputeCatsScatts([cat_id])
+ return cat_id
+
+ def ComputeCatsScatts(self, cats_ids):
+
+ requested_dt = {}
+ requested_dt_conds = {}
+
+ for c in cats_ids:
+ requested_dt_conds[c] = self.scatt_conds_dt.GetCatScatts(c)
+ requested_dt[c] = self.scatts_dt.GetCatScatts(c)
+
+ scatt_conds = self.scatt_conds_dt.GetData(requested_dt_conds)
+ scatts = self.scatts_dt.GetData(requested_dt)
+
+ bands = self.an_data.GetBands()
+
+ cats_rasts = self.scatts_dt.GetCatsRasts()
+ cats_rasts_conds = self.scatts_dt.GetCatsRastsConds()
+
+ returncode, scatts = ComputeScatts(self.an_data.GetRegion(), scatt_conds, bands,
+ len(self.GetBands()), scatts, cats_rasts_conds, cats_rasts)
+
+ #print "ComputeScatts"
+ #print returncode
+ if returncode < 0:
+ GException(_("Computing of scatter plots failed."))
+ #self.scatts_dt.SetData(scatts)
+
+ def CatRastUpdater(self):
+ return self.cat_rast_updater
+
+ def UpdateCategoryWithPolygons(self, cat_id, scatts_pols, value):
+ start_time = time.clock()
+
+ if cat_id not in self.scatts_dt.GetCategories():
+ raise GException(_("Select category for editing."))
+
+ for scatt_id, coords in scatts_pols.iteritems():
+
+ if self.scatt_conds_dt.AddScattPlot(cat_id, scatt_id) < 0:
+ return False
+
+ b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
+ b = self.scatts_dt.GetBandsInfo(scatt_id)
+
+ region = {}
+ region['s'] = b['b2']['min'] - 0.5
+ region['n'] = b['b2']['max'] + 0.5
+
+ region['w'] = b['b1']['min'] - 0.5
+ region['e'] = b['b1']['max'] + 0.5
+
+ arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+ Rasterize(polygon=coords,
+ rast=arr,
+ region=region,
+ value=value)
+
+ # previous way of rasterization / used scipy
+ #raster_pol = RasterizePolygon(coords, b['b1']['range'], b['b1']['min'],
+ # b['b2']['range'], b['b2']['min'])
+
+ #raster_ind = np.where(raster_pol > 0)
+ #arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+
+ #arr[raster_ind] = value
+ #arr.flush()
+
+ self.ComputeCatsScatts([cat_id])
+ return cat_id
+
+ def ExportCatRast(self, cat_id, rast_name):
+
+ cat_rast = self.scatts_dt.GetCatRast(cat_id);
+ if not cat_rast:
+ return 1
+
+ return RunCommand("g.copy",
+ rast=cat_rast + ',' + rast_name,
+ getErrorMsg=True,
+ overwrite=True)
+
+ def _validExtend(self, val):
+ #TODO do it general
+ if val > 255:
+ val = 255
+ elif val < 0:
+ val = 0
+
+ return val
+
+class CatRastUpdater:
+ """!Update backend data structures according to selected areas in mapwindow.
+ """
+ def __init__(self, scatts_dt, an_data, core):
+ self.scatts_dt = scatts_dt
+ self.an_data = an_data # TODO may be confusing
+ self.core = core
+ self.vectMap = None
+
+ def SetVectMap(self, vectMap):
+ self.vectMap = vectMap
+
+ def SyncWithMap(self):
+ #TODO possible optimization - bbox only of vertex and its two neighbours
+
+ region = self.an_data.GetRegion()
+
+ bbox = {}
+ bbox['maxx'] = region['e']
+ bbox['minx'] = region['w']
+ bbox['maxy'] = region['n']
+ bbox['miny'] = region['s']
+
+ updated_cats = []
+
+ for cat_id in self.scatts_dt.GetCategories():
+ if cat_id == 0:
+ continue
+
+ cat = [{1 : [cat_id]}]
+ self._updateCatRast(bbox, cat, updated_cats)
+
+ return updated_cats
+
+ def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
+ #TODO possible optimization - bbox only of vertex and its two neighbours
+
+ bboxs = old_bboxs + new_bboxs
+ areas_cats = old_areas_cats + new_areas_cats
+
+ updated_cats = []
+
+ for i in range(len(areas_cats)):
+ self._updateCatRast(bboxs[i], areas_cats[i], updated_cats)
+
+ return updated_cats
+
+ def _updateCatRast(self, bbox, areas_cats, updated_cats):
+
+ rasterized_cats = []
+ for c in range(len(areas_cats)):
+
+ if not areas_cats[c]:
+ continue
+
+ layer = areas_cats[c].keys()[0]
+ cat = areas_cats[c][layer][0]
+
+ if cat in rasterized_cats:
+ continue
+
+ rasterized_cats.append(cat)
+ updated_cats.append(cat)
+
+ grass_region = self._create_grass_region_env(bbox)
+
+ #TODO hack
+ patch_rast = "temp_scatt_patch"
+ self._rasterize(grass_region, layer, cat, patch_rast)
+
+ region = self.an_data.GetRegion()
+ ret = UpdateCatRast(patch_rast, region, self.scatts_dt.GetCatRastCond(cat))
+ if ret < 0:
+ GException(_("Patching category raster conditions file failed."))
+ RunCommand("g.remove",
+ rast = patch_rast)
+
+ def _rasterize(self, grass_region, layer, cat, out_rast):
+
+ #TODO different thread may be problem when user edits map
+ environs = os.environ.copy()
+ environs['GRASS_VECTOR_TEMPORARY'] = '1'
+
+ ret, text, msg = RunCommand("v.category",
+ input=self.vectMap,
+ getErrorMsg = True,
+ option='report',
+ read=True,
+ env=environs)
+
+ ret, text, msg = RunCommand("v.build",
+ map = self.vectMap,
+ getErrorMsg = True,
+ read = True,
+ env = environs)
+
+ if ret != 0:
+ GException(_("v.build failed:\n%s" % msg))
+
+ environs = os.environ.copy()
+ environs["GRASS_REGION"] = grass_region["GRASS_REGION"]
+ environs['GRASS_VECTOR_TEMPORARY'] = '1'
+
+ ret, text, msg = RunCommand("v.to.rast",
+ input = self.vectMap,
+ use = "cat",
+ layer = str(layer),
+ cat = str(cat),
+ output = out_rast,
+ getErrorMsg = True,
+ read = True,
+ overwrite = True,
+ env = environs)
+
+ if ret != 0:
+ GException(_("v.to.rast failed:\n%s" % msg))
+
+ def _create_grass_region_env(self, bbox):
+
+ r = self.an_data.GetRegion()
+ new_r = {}
+
+ if bbox["maxy"] <= r["s"]:
+ return 0
+ elif bbox["maxy"] >= r["n"]:
+ new_r["n"] = bbox["maxy"]
+ else:
+ new_r["n"] = ceil((bbox["maxy"] - r["s"]) / r["nsres"]) * r["nsres"] + r["s"]
+
+ if bbox["miny"] >= r["n"]:
+ return 0
+ elif bbox["miny"] <= r["s"]:
+ new_r["s"] = bbox["miny"]
+ else:
+ new_r["s"] = floor((bbox["miny"] - r["s"]) / r["nsres"]) * r["nsres"] + r["s"]
+
+ if bbox["maxx"] <= r["w"]:
+ return 0
+ elif bbox["maxx"] >= r["e"]:
+ new_r["e"] = bbox["maxx"]
+ else:
+ new_r["e"] = ceil((bbox["maxx"] - r["w"]) / r["ewres"]) * r["ewres"] + r["w"]
+
+ if bbox["minx"] >= r["e"]:
+ return 0
+ elif bbox["minx"] <= r["w"]:
+ new_r["w"] = bbox["minx"]
+ else:
+ new_r["w"] = floor((bbox["minx"] - r["w"]) / r["ewres"]) * r["ewres"] + r["w"]
+
+ #TODO check regions resolution
+ new_r["nsres"] = r["nsres"]
+ new_r["ewres"] = r["ewres"]
+
+ return {"GRASS_REGION" : grass.region_env(**new_r)}
+
+class AnalyzedData:
+ """!Represents analyzed data (bands, region).
+ """
+ def __init__(self):
+
+ self.bands = []
+ self.bands_info = {}
+
+ self.region = None
+
+ def GetRegion(self):
+ return self.region
+
+ def Create(self, bands):
+
+ self.bands = bands
+ self.region = None
+
+ ret, region, msg = RunCommand("g.region",
+ flags = "gp",
+ getErrorMsg = True,
+ read = True)
+
+ if ret != 0:
+ raise GException("g.region failed:\n%s" % msg)
+
+
+ self.bands_info = {}
+ for b in bands:
+ self.bands_info[b] = self._getRasterInfo(b)
+ if self.bands_info[b]["datatype"] != "CELL":
+ raise GException(_("Raster <%s> is not <CELL> type.") % (b))
+ #TODO size of raster check
+
+ self.region = self._parseRegion(region)
+
+ def _getRasterInfo(self, rast):
+ """
+ """
+ ret, out, msg = RunCommand("r.info",
+ map = rast,
+ flags = "rg",
+ getErrorMsg = True,
+ read = True)
+
+ if ret != 0:
+ raise GException("r.info failed:\n%s" % msg)
+
+ out = out.split("\n")
+ raster_info = {}
+
+ for b in out:
+ if not b.strip():
+ continue
+ k, v = b.split("=")
+ if k == "datatype":
+ pass
+ elif k in ['rows', 'cols', 'cells', 'min', 'max']:
+ v = int(v)
+ else:
+ v = float(v)
+
+ raster_info[k] = v
+
+ raster_info['range'] = raster_info['max'] - raster_info['min'] + 1
+ return raster_info
+
+ def GetBands(self):
+ return self.bands
+
+ def GetBandInfo(self, band_id):
+ band = self.bands[band_id]
+ return self.bands_info[band]
+
+ def _parseRegion(self, region_str):
+
+ region = {}
+ region_str = region_str.splitlines()
+
+ for param in region_str:
+ k, v = param.split("=")
+ if k in ["rows", "cols", "cells"]:
+ v = int(v)
+ else:
+ v = float(v)
+ region[k] = v
+
+ return region
+
+class ScattPlotsCondsData:
+ """!Data structure for selected areas in scatter plot(condtions).
+ """
+ def __init__(self, an_data):
+
+ self.an_data = an_data
+
+ #TODO
+ self.max_n_cats = 10
+
+ self.dtype = 'uint8'
+ self.type = 1;
+ self.CleanUp()
+
+ def CleanUp(self):
+
+ self.cats = {}
+
+ self.n_scatts = -1
+ self.n_bands = -1
+
+ for cat_id in self.cats.keys():
+ self.DeleteCategory(cat_id)
+
+ def Create(self, n_bands):
+
+ self.CleanUp()
+
+ self.n_scatts = (n_bands - 1) * n_bands / 2;
+ self.n_bands = n_bands
+
+ self.AddCategory(cat_id = 0)
+
+ def AddCategory(self, cat_id):
+
+ if cat_id not in self.cats.keys():
+ self.cats[cat_id] = {}
+ return cat_id
+ return -1
+
+ def DeleteCategory(self, cat_id):
+
+ if cat_id not in self.cats.keys():
+ return False
+
+ for scatt in self.cats[cat_id].itervalues():
+ del scatt['np_vals']
+
+ del self.cats[cat_id]
+
+ return True
+
+ def GetCategories(self):
+ return self.cats.keys()
+
+ def GetCatScatts(self, cat_id):
+
+ if not self.cats.has_key(cat_id):
+ return False
+
+ return self.cats[cat_id].keys()
+
+
+ def AddScattPlot(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return -1
+
+ if self.cats[cat_id].has_key(scatt_id):
+ return 0
+
+ b_i = self.GetBandsInfo(scatt_id)
+
+ shape = (b_i['b2']['max'] - b_i['b2']['min'] + 1, b_i['b1']['max'] - b_i['b1']['min'] + 1)
+
+ np_vals = np.memmap(grass.tempfile(), dtype=self.dtype, mode='w+', shape = shape)
+
+ self.cats[cat_id][scatt_id] = {
+ 'np_vals' : np_vals,
+ }
+
+ return 1
+
+ def GetBandsInfo(self, scatt_id):
+ b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands()))
+
+ b1_info = self.an_data.GetBandInfo(b1)
+ b2_info = self.an_data.GetBandInfo(b2)
+
+ bands_info = {'b1' : b1_info,
+ 'b2' : b2_info}
+
+ return bands_info
+
+ def DeleScattPlot(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return False
+
+ if not self.cats[cat_id].has_key(scatt_id):
+ return False
+
+ del self.cats[cat_id][scatt_id]
+ return True
+
+ def GetValuesArr(self, cat_id, scatt_id):
+
+ if not self.cats.has_key(cat_id):
+ return None
+
+ if not self.cats[cat_id].has_key(scatt_id):
+ return None
+
+ return self.cats[cat_id][scatt_id]['np_vals']
+
+ def GetData(self, requested_dt):
+
+ cats = {}
+ for cat_id, scatt_ids in requested_dt.iteritems():
+ if not cats.has_key(cat_id):
+ cats[cat_id] = {}
+ for scatt_id in scatt_ids:
+ # if key is missing condition is always True (full scatter plor is computed)
+ if self.cats[cat_id].has_key(scatt_id):
+ cats[cat_id][scatt_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals'],
+ 'bands_info' : self.GetBandsInfo(scatt_id)}
+
+ return cats
+
+ def SetData(self, cats):
+
+ for cat_id, scatt_ids in cats.iteritems():
+ for scatt_id in scatt_ids:
+ # if key is missing condition is always True (full scatter plor is computed)
+ if self.cats[cat_id].has_key(scatt_id):
+ self.cats[cat_id][scatt_id]['np_vals'] = cats[cat_id][scatt_id]['np_vals']
+
+ def GetScatt(self, scatt_id, cats_ids = None):
+ scatts = {}
+ for cat_id in self.cats.iterkeys():
+ if cats_ids and cat_id not in cats_ids:
+ continue
+ if not self.cats[cat_id].has_key(scatt_id):
+ continue
+
+ scatts[cat_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals'],
+ 'bands_info' : self.GetBandsInfo(scatt_id)}
+ return scatts
+
+
+class ScattPlotsData(ScattPlotsCondsData):
+ """!Data structure for computed points (classes) in scatter plots.\
+ """
+ def __init__(self, an_data):
+
+ self.cats_rasts = {}
+ self.cats_rasts_conds = {}
+ self.scatts_ids = []
+
+ ScattPlotsCondsData.__init__(self, an_data)
+
+ self.dtype = 'uint32'
+
+ #TODO
+ self.type = 0
+
+ def AddCategory(self, cat_id):
+ cat_id = ScattPlotsCondsData.AddCategory(self, cat_id)
+ if cat_id < 0:
+ return cat_id
+
+ for scatt_id in self.scatts_ids:
+ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
+
+ if cat_id == 0:
+ self.cats_rasts_conds[cat_id] = None
+ self.cats_rasts[cat_id] = None
+ else:
+ self.cats_rasts_conds[cat_id] = grass.tempfile()
+ self.cats_rasts[cat_id] = "temp_cat_rast_%d" % cat_id
+ region = self.an_data.GetRegion()
+ CreateCatRast(region, self.cats_rasts_conds[cat_id])
+
+ return cat_id
+
+ def DeleteCategory(self, cat_id):
+
+ ScattPlotsCondsData.DeleteCategory(self, cat_id)
+
+ grass.try_remove(self.cats_rasts_conds[cat_id])
+ del self.cats_rasts_conds[cat_id]
+
+ RunCommand("g.remove",
+ rast=self.cats_rasts[cat_id])
+ del self.cats_rasts[cat_id]
+
+ return True
+
+ def AddScattPlot(self, scatt_id):
+
+ if scatt_id in self.scatts_ids:
+ return False
+
+ self.scatts_ids.append(scatt_id)
+ for cat_id in self.cats.iterkeys():
+ ScattPlotsCondsData.AddScattPlot(self, cat_id, scatt_id)
+ self.cats[cat_id][scatt_id]['ellipse'] = None
+
+ return True
+
+ def DeleteScatterPlot(self, scatt_id):
+
+ if scatt_id not in self.scatts_ids:
+ return False
+
+ self.scatts_ids.remove(scatt_id)
+
+ for cat_id in self.cats.iterkeys():
+ ScattPlotsCondsData.DeleteScattPlot(self, cat_id, scatt_id)
+
+ return True
+
+ def GetEllipses(self, scatt_id, styles):
+ if scatt_id not in self.scatts_ids:
+ return False
+
+ scatts = {}
+ for cat_id in self.cats.iterkeys():
+ if cat_id == 0:
+ continue
+ nstd = styles[cat_id]['nstd']
+ scatts[cat_id] = self._getEllipse(cat_id, scatt_id, nstd)
+
+ return scatts
+
+ def _getEllipse(self, cat_id, scatt_id, nstd):
+ # Joe Kington
+ # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
+
+ data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
+
+ b = self.GetBandsInfo(scatt_id)
+ sel_pts = np.where(data > 0)
+
+ x = sel_pts[1]
+ y = sel_pts[0]
+
+ flatten_data = data.reshape([-1])
+ flatten_sel_pts = np.nonzero(flatten_data)
+ weights = flatten_data[flatten_sel_pts]
+ if len(weights) == 0:
+ return None
+
+ x_avg = np.average(x, weights=weights)
+ y_avg = np.average(y, weights=weights)
+ pos = np.array([x_avg + b['b1']['min'], y_avg + b['b2']['min']])
+
+ x_diff = (x - x_avg)
+ y_diff = (y - y_avg)
+
+ x_diff = (x - x_avg)
+ y_diff = (y - y_avg)
+
+ diffs = x_diff * y_diff.T
+ cov = np.dot(diffs, weights) / (np.sum(weights) - 1)
+
+ diffs = x_diff * x_diff.T
+ var_x = np.dot(diffs, weights) / (np.sum(weights) - 1)
+
+ diffs = y_diff * y_diff.T
+ var_y = np.dot(diffs, weights) / (np.sum(weights) - 1)
+
+ cov = np.array([[var_x, cov],[cov, var_y]])
+
+ def eigsorted(cov):
+ vals, vecs = np.linalg.eigh(cov)
+ order = vals.argsort()[::-1]
+ return vals[order], vecs[:,order]
+
+ vals, vecs = eigsorted(cov)
+ theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))
+
+ # Width and height are "full" widths, not radius
+ width, height = 2 * nstd * np.sqrt(vals)
+
+ ellipse = {'pos' : pos,
+ 'width' : width,
+ 'height' : height,
+ 'theta' : theta}
+
+ del data
+ del flatten_data
+ del flatten_sel_pts
+ del weights
+ del sel_pts
+ return ellipse
+
+ def CleanUp(self):
+
+ ScattPlotsCondsData.CleanUp(self)
+ for tmp in self.cats_rasts_conds.itervalues():
+ grass.try_remove(tmp)
+ for tmp in self.cats_rasts.itervalues():
+ RunCommand("g.remove",
+ rast=tmp,
+ getErrorMsg=True)
+
+ self.cats_rasts = {}
+ self.cats_rasts_conds = {}
+
+ def GetCatRast(self, cat_id):
+ if self.cats_rasts.has_key(cat_id):
+ return self.cats_rasts[cat_id]
+ return None
+
+ def GetCatRastCond(self, cat_id):
+ return self.cats_rasts_conds[cat_id]
+
+ def GetCatsRastsConds(self):
+ max_cat_id = max(self.cats_rasts_conds.keys())
+
+ cats_rasts_conds = [''] * (max_cat_id + 1)
+ for i_cat_id, i_rast in self.cats_rasts_conds.iteritems():
+ cats_rasts_conds[i_cat_id] = i_rast
+
+ return cats_rasts_conds
+
+ def GetCatsRasts(self):
+ max_cat_id = max(self.cats_rasts.keys())
+
+ cats_rasts = [''] * (max_cat_id + 1)
+ for i_cat_id, i_rast in self.cats_rasts.iteritems():
+ cats_rasts[i_cat_id] = i_rast
+
+ return cats_rasts
+
+
+# not used, using iclass_perimeter algorith instead of scipy convolve2d
+"""
+def RasterizePolygon(pol, height, min_h, width, min_w):
+
+ # Joe Kington
+ # http://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask
+
+ #poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)]
+
+ nx = width
+ ny = height
+
+ x, y = np.meshgrid(np.arange(-0.5 + min_w, nx + 0.5 + min_w, dtype=float),
+ np.arange(-0.5 + min_h, ny + 0.5 + min_h, dtype=float))
+ x, y = x.flatten(), y.flatten()
+
+ points = np.vstack((x,y)).T
+
+ p = Path(pol)
+ grid = p.contains_points(points)
+ grid = grid.reshape((ny + 1, nx + 1))
+ raster = np.zeros((height, width), dtype=np.uint8)#TODO bool
+
+ #TODO shift by 0.5
+ B = np.ones((2,2))/4
+ raster = convolve2d(grid, B, 'valid')
+
+ return raster
+"""
+
+def idScattToidBands(scatt_id, n_bands):
+ """!Get bands ids from scatter plot id."""
+ n_b1 = n_bands - 1
+
+ band_1 = (int) ((2 * n_b1 + 1 - sqrt(((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2)
+
+ band_2 = scatt_id - (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_1 + 1
+
+ return band_1, band_2
+
+def idBandsToidScatt(band_1_id, band_2_id, n_bands):
+ """!Get scatter plot id from band ids."""
+ if band_2_id < band_1_id:
+ tmp = band_1_id
+ band_1_id = band_2_id
+ band_2_id = tmp
+
+ n_b1 = n_bands - 1
+
+ scatt_id = (band_1_id * (2 * n_b1 + 1) - band_1_id * band_1_id) / 2 + band_2_id - band_1_id - 1
+
+ return scatt_id
\ No newline at end of file
Modified: sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py
===================================================================
--- sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/gui/wxpython/scatt_plot/toolbars.py 2013-09-23 16:22:36 UTC (rev 57823)
@@ -13,94 +13,255 @@
@author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
"""
-
-
import wx
-from icons.icon import MetaIcon
+from icons.icon import MetaIcon
from gui_core.toolbars import BaseToolbar, BaseIcons
-from core.gcmd import RunCommand
+from core.gcmd import RunCommand
+from core.gcmd import GException, GError, RunCommand
+from scatt_plot.scatt_core import idBandsToidScatt
+from scatt_plot.dialogs import SettingsDialog
class MainToolbar(BaseToolbar):
"""!Main toolbar
"""
- def __init__(self, parent):
+ def __init__(self, parent, scatt_mgr, opt_tools=None):
BaseToolbar.__init__(self, parent)
-
+ self.scatt_mgr = scatt_mgr
+ self.opt_tools = opt_tools
+
self.InitToolbar(self._toolbarData())
# realize the toolbar
self.Realize()
+ self.scatt_mgr.modeSet.connect(self.ModeSet)
def _toolbarData(self):
icons = {
- 'set_data' : MetaIcon(img = 'layer-raster-add',
- label = _('Set input raster data for plots')),
- 'settings' : BaseIcons['settings'].SetLabel( _('Ssettings')),
- 'help' : MetaIcon(img = 'help',
+ 'selectGroup' : MetaIcon(img = 'layer-group-add',
+ label = _('Select imagery group')),
+ 'settings' : BaseIcons['settings'].SetLabel( _('Settings')),
+ 'help' : MetaIcon(img = 'help',
label = _('Show manual')),
- 'add_scatt_pl' : MetaIcon(img = 'layer-raster-analyze',
- label = _('Add scatter plot'))
+ 'add_scatt_pl' : MetaIcon(img = 'layer-raster-analyze',
+ label = _('Add scatter plot')),
+ 'selCatPol' : MetaIcon(img = 'polygon',
+ label = _('Select area with polygon')),
+ 'pan' : MetaIcon(img = 'pan',
+ label = _('Pan mode for scatter plots')),
+ 'zoomIn' : MetaIcon(img = 'zoom-in',
+ label = _('Zoom mode for scatter plots (left mouse button, wheel)')),
+ 'zoomExtent' : MetaIcon(img = 'zoom-extent',
+ label = _('Zoom to scatter plot data extend mode (click on scatter plot for zooming to extend)')),
+ 'cats_mgr' : MetaIcon(img = 'table-manager',
+ label = _('Show/hide class manager'))
}
+
+ tools = [
+ ('add_scatt', icons["add_scatt_pl"],
+ lambda event : self.scatt_mgr.AddScattPlot()),
+ (None, ),
+ ("cats_mgr", icons['cats_mgr'],
+ lambda event: self.parent.ShowCategoryPanel(event.Checked()), wx.ITEM_CHECK),
+ (None, ),
+ ("pan", icons["pan"],
+ lambda event: self.SetPloltsMode(event, 'pan'),
+ wx.ITEM_CHECK),
+ ("zoom", icons["zoomIn"],
+ lambda event: self.SetPloltsMode(event, 'zoom'),
+ wx.ITEM_CHECK),
+ ("zoom_extend", icons["zoomExtent"],
+ lambda event: self.SetPloltsMode(event, 'zoom_extend'),
+ wx.ITEM_CHECK),
+ (None, ),
+ ('sel_pol_mode', icons['selCatPol'],
+ self.ActivateSelectionPolygonMode,
+ wx.ITEM_CHECK),
+ (None, ),
+ ('settings', icons["settings"],
+ self.OnSettings),
+ ('help', icons["help"],
+ self.OnHelp)
+ ]
+ if self.opt_tools and "add_group" in self.opt_tools:
+ tools.insert(0, ("selectGroup", icons['selectGroup'],
+ lambda event : self.scatt_mgr.SetData()))
+
+ return self._getToolbarData(tools)
+
+ def GetToolId(self, toolName): #TODO can be useful in base
+ return vars(self)[toolName]
+
+ def SetPloltsMode(self, event, tool_name):
+ self.scatt_mgr.modeSet.disconnect(self.ModeSet)
+ if event.Checked() == True:
+ for i_tool_data in self._data:
+ i_tool_name = i_tool_data[0]
+ if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
+ continue
+ if i_tool_name == tool_name:
+ continue
+ i_tool_id = vars(self)[i_tool_name]
+ self.ToggleTool(i_tool_id, False)
+
+ self.scatt_mgr.SetPlotsMode(mode = tool_name)
+ else:
+ self.scatt_mgr.SetPlotsMode(mode = None)
+ self.scatt_mgr.modeSet.connect(self.ModeSet)
+
+ def ActivateSelectionPolygonMode(self, event):
+
+ activated = self.scatt_mgr.ActivateSelectionPolygonMode(event.Checked())
+ self.parent.ShowPlotEditingToolbar(activated)
+
+ i_tool_id = vars(self)['sel_pol_mode']
+ self.ToggleTool(i_tool_id, activated)
+
+ def ModeSet(self, mode):
+ self.UnsetMode()
+
+ def UnsetMode(self):
+ for i_tool_data in self._data:
+ i_tool_name = i_tool_data[0]
+ if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]:
+ continue
+ i_tool_id = vars(self)[i_tool_name]
+ self.ToggleTool(i_tool_id, False)
+
+ def OnSettings(self, event):
+ dlg = SettingsDialog(parent=self, id=wx.ID_ANY,
+ title=_('Settings'), scatt_mgr = self.scatt_mgr)
+
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ def OnHelp(self, event) :
+ RunCommand('g.manual',
+ entry = 'wxGUI.iscatt')
+
+class EditingToolbar(BaseToolbar):
+ """!Main toolbar
+ """
+ def __init__(self, parent, scatt_mgr):
+ BaseToolbar.__init__(self, parent)
+ self.scatt_mgr = scatt_mgr
+
+ self.InitToolbar(self._toolbarData())
+
+ # realize the toolbar
+ self.Realize()
+ self.scatt_mgr.modeSet.connect(self.ModeSet)
+
+ def _toolbarData(self):
+ """!Toolbar data
+ """
+ self.icons = {
+ 'sel_add' : MetaIcon(img = 'layer-add',
+ label = _('Include selected area to class'),
+ desc = _('Include selected area to class')),
+ 'sel_remove' : MetaIcon(img = 'layer-remove',
+ label = _('Exclude selected area from class'),
+ desc = _('Exclude selected area from class')),
+ 'addVertex' : MetaIcon(img = 'vertex-create',
+ label = _('Add new vertex'),
+ desc = _('Add new vertex to polygon boundary scatter plot')),
+ 'editLine' : MetaIcon(img = 'polygon-create',
+ label = _('Create selection polygon'),
+ desc = _('Add new vertex between last and first points of the boundary')),
+ 'moveVertex' : MetaIcon(img = 'vertex-move',
+ label = _('Move vertex'),
+ desc = _('Move boundary vertex')),
+ 'removeVertex' : MetaIcon(img = 'vertex-delete',
+ label = _('Remove vertex'),
+ desc = _('Remove boundary vertex')),
+ 'delete' : MetaIcon(img = 'polygon-delete',
+ label = _("Remove polygon (click on scatter plot for removing it's polygon)")),
+ }
+
return self._getToolbarData((
- ('sat_data', icons["set_data"],
- self.parent.OnSetData),
+ ("sel_add", self.icons["sel_add"],
+ lambda event: self.scatt_mgr.ProcessSelectionPolygons('add')),
+ ("sel_remove", self.icons['sel_remove'],
+ lambda event: self.scatt_mgr.ProcessSelectionPolygons('remove')),
(None, ),
- ('add_scatt', icons["add_scatt_pl"],
- self.parent.OnAddScattPl),
- #('settings', icons["settings"],
- # self.parent.OnSettings),
- #('help', icons["help"],
- # self.OnHelp),
- ("quit", BaseIcons['quit'],
- self.parent.OnCloseDialog)
+ ("add_vertex", self.icons["editLine"],
+ lambda event: self.SetMode(event, 'add_vertex'),
+ wx.ITEM_CHECK),
+ ("add_boundary_vertex", self.icons['addVertex'],
+ lambda event: self.SetMode(event, 'add_boundary_vertex'),
+ wx.ITEM_CHECK),
+ ("move_vertex", self.icons["moveVertex"],
+ lambda event: self.SetMode(event, 'move_vertex'),
+ wx.ITEM_CHECK),
+ ('delete_vertex', self.icons['removeVertex'],
+ lambda event: self.SetMode(event, 'delete_vertex'),
+ wx.ITEM_CHECK),
+ ('remove_polygon', self.icons['delete'],
+ lambda event: self.SetMode(event, 'remove_polygon'),
+ wx.ITEM_CHECK)
))
- def OnHelp(self, event) :
- RunCommand('g.manual',
- entry = 'wxGUI.scatt_plot')
+ def SetMode(self, event, tool_name):
+ self.scatt_mgr.modeSet.disconnect(self.ModeSet)
+ if event.Checked() == True:
+ for i_tool_data in self._data:
+ i_tool_name = i_tool_data[0]
+ if not i_tool_name:
+ continue
+ if i_tool_name == tool_name:
+ continue
+ i_tool_id = vars(self)[i_tool_name]
+ self.ToggleTool(i_tool_id, False)
+ self.scatt_mgr.SetPlotsMode(tool_name)
+ else:
+ self.scatt_mgr.SetPlotsMode(None)
+ self.scatt_mgr.modeSet.connect(self.ModeSet)
-class CategoriesToolbar(BaseToolbar):
+ def ModeSet(self, mode):
- def __init__(self, parent, cats_list, scatts_dlg):
+ if mode in ['zoom', 'pan', 'zoom_extend', None]:
+ self.UnsetMode()
+
+ def UnsetMode(self):
+ for i_tool_data in self._data:
+ i_tool_name = i_tool_data[0]
+ if not i_tool_name:
+ continue
+ i_tool_id = vars(self)[i_tool_name]
+ self.ToggleTool(i_tool_id, False)
+
+ def GetToolId(self, toolName):
+ return vars(self)[toolName]
+
+class CategoryToolbar(BaseToolbar):
+ """!Main toolbar
+ """
+ def __init__(self, parent, scatt_mgr, cats_list):
BaseToolbar.__init__(self, parent)
+ self.scatt_mgr = scatt_mgr
+ self.cats_mgr = self.scatt_mgr.GetCategoriesManager()
self.cats_list = cats_list
- self.scatts_dlg = scatts_dlg
self.InitToolbar(self._toolbarData())
-
# realize the toolbar
self.Realize()
-
def _toolbarData(self):
-
- icons = {
- 'editCatAdd' : MetaIcon(img = 'polygon-create',
- label = _('Add region to category mode')),
- 'editCatRemove' : MetaIcon(img = 'polygon-delete',
- label = _('Remove region to category mode')),
- 'addCat' : MetaIcon(img = 'layer-add',
- label = _('Add category')),
- 'deleteCat' : MetaIcon(img = 'layer-remove',
- label = _('Delete category'))
+ """!Toolbar data
+ """
+ self.icons = {
+ 'add_class' : MetaIcon(img = 'layer-add',
+ label = _('Add class')),
+ 'remove_class' : MetaIcon(img = 'layer-remove',
+ label = _('Remove selected class'))
}
- return self._getToolbarData((('editCatAdd', icons['editCatAdd'],
- lambda event: self.scatts_dlg.EditCatAdd(),
- wx.ITEM_CHECK),
- ('editCatRemove', icons['editCatRemove'],
- lambda event: self.scatts_dlg.EditCatRemove(),
- wx.ITEM_CHECK),
- (None, ),
- ('addCat', icons["addCat"],
- lambda event: self.cats_list.AddCategory()),
- ('deleteCat', icons["deleteCat"],
- lambda event: self.cats_list.DeleteCategory())))
-
- def GetToolId(self, toolName): #TODO can be useful in base
-
- return vars(self)[toolName]
\ No newline at end of file
+ return self._getToolbarData((
+ ("add_class", self.icons["add_class"],
+ lambda event: self.cats_mgr.AddCategory()),
+ ("remove_class", self.icons['remove_class'],
+ lambda event: self.cats_list.DeleteCategory()),
+ ))
\ No newline at end of file
Modified: sandbox/turek/scatter_plot/include/defs/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/defs/imagery.h 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/include/defs/imagery.h 2013-09-23 16:22:36 UTC (rev 57823)
@@ -92,6 +92,7 @@
int I_list_group_simple(const struct Ref *, FILE *);
/* list_subgp.c */
+char ** I_list_subgroups(const char *, int *);
int I_list_subgroup(const char *, const char *, const struct Ref *, FILE *);
int I_list_subgroup_simple(const struct Ref *, FILE *);
@@ -110,29 +111,28 @@
FILE *I_fopen_subgroup_ref_new(const char *, const char *);
FILE *I_fopen_subgroup_ref_old(const char *, const char *);
-/* scatt_plt.c */
+/* scatt_sccats.c */
void I_sc_init_cats(struct scCats *, int, int);
void I_sc_free_cats(struct scCats *);
-void I_sc_get_active_categories(int *, int *, struct scCats *);
-int I_sc_add_cat(struct scCats *, int);
-int I_sc_delete_cat(struct scCats *, int);
+int I_sc_add_cat(struct scCats *);
int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
-int I_sc_remove_scatt_data(struct scCats *, struct scdScattData *, int, int);
-int I_sc_set_value(struct scCats *, int, int, int, int);
-void I_scd_init_scatt_data(struct scdScattData *, int, int, void *, struct Range, struct Range);
-void I_scd_free_scatt_data(struct scdScattData *);
-void I_scd_get_range_min_max(struct scdScattData *, CELL *, CELL *, CELL *, CELL *);
-void * I_scd_get_data_ptr(struct scdScattData *);
-int I_scd_set_value(struct scdScattData *, unsigned int, unsigned int);
+void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
+/* scatt.c */
int I_compute_scatts(struct Cell_head *, struct scCats *, const char **,
- int, struct scCats *, const char **, const char **);
+ const char **, int, struct scCats *, const char **);
int I_create_cat_rast(struct Cell_head *, const char *);
int I_insert_patch_to_cat_rast(const char *, struct Cell_head *, const char *);
+int I_id_scatt_to_bands(const int, const int, int *, int *);
+int I_bands_to_id_scatt(const int, const int, const int, int *);
+int I_merge_arrays(unsigned char *, unsigned char *, unsigned, unsigned, double);
+int I_apply_colormap(unsigned char *, unsigned char *, unsigned, unsigned char *, unsigned char *);
+int I_rasterize(double *, int, unsigned char, struct Cell_head *, unsigned char *);
+
/* sig.c */
int I_init_signatures(struct Signature *, int);
int I_new_signature(struct Signature *);
Modified: sandbox/turek/scatter_plot/include/imagery.h
===================================================================
--- sandbox/turek/scatter_plot/include/imagery.h 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/include/imagery.h 2013-09-23 16:22:36 UTC (rev 57823)
@@ -136,47 +136,55 @@
} IClass_statistics;
/* Scatter Plot backend */
+
+#define SC_SCATT_DATA 0
+#define SC_SCATT_CONDITIONS 1
+
+
+/*! Holds list of all catagories.
+ It can contain selected areas for scatter plots (SC_SCATT_CONDITIONS type) or computed scatter plots (SC_SCATT_DATA type).
+*/
struct scCats
{
- int type; //TODO do not identify it with number
+ int type; /*!< SC_SCATT_DATA -> computed scatter plots, SC_SCATT_CONDITIONS -> set conditions for scatter plots to be computed*/
- int n_cats;
+ int n_cats; /*!< number of alocated categories */
- int n_bands;
- int n_scatts;
+ int n_bands; /*!< number of analyzed bands */
+ int n_scatts; /*!< number of possible scattter plot which can be created from bands */
- int n_a_cats;
- int * cats_ids;
- int * cats_idxs;
+ int n_a_cats; /*!< number of used/active categories */
+ int * cats_ids; /*!< (cat_idx->cat_id) array index is internal idx (position in cats_arr) and id is saved in it's position*/
+ int * cats_idxs; /*!< (cat_id->cat_idx) array index is id and internal idx is saved in it's position*/
- struct scScatts ** cats_arr;
+ struct scScatts ** cats_arr; /*!< array of pointers to struct scScatts */
};
+
+/*! Holds list of all scatter plots, which belongs to category.
+*/
struct scScatts
{
- int n_a_scatts;
+ int n_a_scatts; /*!< number of used/active scatter plots*/
- int * scatts_bands;
- int * scatt_idxs;
+ int * scatts_bands; /*!< array of bands for which represents the scatter plots,
+ every scatter plot has assigned two bads (size of array is n_a_scatts * 2)*/
+ int * scatt_idxs; /*!< (scatt_id->scatt_idx) internal idx of the scatter plot (position in scatts_arr)*/
- struct scdScattData ** scatts_arr;
+ struct scdScattData ** scatts_arr; /*!< array of pointers to scdScattData */
};
-#define SC_SCATT_DATA 0
-#define SC_SCATT_CONDITIONS 1
-
+/*! Holds scatter plot data.
+*/
struct scdScattData
{
- int n_vals; //TODO delete???
-
- struct Range band_1_range;
- struct Range band_2_range;
+ int n_vals; /*!< Number of values in scatter plot. */
-
- unsigned char * b_conds_arr;
- unsigned int * scatt_vals_arr;
+ unsigned char * b_conds_arr; /*!< array of selected areas (used for SC_SCATT_CONDITIONS type) otherwise NULL */
+ unsigned int * scatt_vals_arr; /*!< array of computed areas (used for SC_SCATT_DATA type) otherwise NULL */
};
+
#define SIGNATURE_TYPE_MIXED 1
#define GROUPFILE "CURGROUP"
Modified: sandbox/turek/scatter_plot/lib/imagery/scatt.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt.c 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt.c 2013-09-23 16:22:36 UTC (rev 57823)
@@ -12,25 +12,44 @@
\author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
*/
-
-#include <grass/raster.h>
-#include <grass/imagery.h>
-#include <grass/gis.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
+#include <grass/gis.h>
+#include <grass/vector.h>
+#include <grass/raster.h>
+#include <grass/imagery.h>
+#include <grass/glocale.h>
-static const int CAT_RAST_SCATT_SEL = 2;
-static const int CAT_RAST_RAST_SEL = 1;
-static const int CAT_RAST_NULL = 0;
+#include "iclass_local_proto.h"
+struct rast_row
+{
+ CELL * row;
+ char * null_row;
+ struct Range rast_range; /*Range of whole raster.*/
+};
+
+/*!
+ \brief Create pgm header.
+
+ Scatter plot internally generates pgm files. These pgms have header in format created by this function.
+
+ \param region region to be pgm header generated for
+ \param [out] header header of pgm file
+ */
static int get_cat_rast_header(struct Cell_head * region, char * header){
return sprintf(header, "P5\n%d\n%d\n1\n", region->cols, region->rows);
}
+/*!
+ \brief Create category raster conditions file.
+
+ \param cat_rast_region region to be file generated for
+ \param cat_rast path of generated category raster file
+ */
int I_create_cat_rast(struct Cell_head * cat_rast_region, const char * cat_rast)
{
FILE * f_cat_rast;
@@ -41,14 +60,17 @@
unsigned char * row_data;
f_cat_rast = fopen(cat_rast, "wb");
- if(!f_cat_rast)
+ if(!f_cat_rast) {
+ G_warning("Unable to create category raster condition file <%s>.", cat_rast);
return -1;
+ }
head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cat_rast);
if (ferror(f_cat_rast)){
fclose(f_cat_rast);
+ G_warning(_("Unable to write header into category raster condition file <%s>."), cat_rast);
return -1;
}
@@ -58,11 +80,10 @@
for(i_row = 0; i_row < cat_rast_region->rows; i_row++) {
fwrite(row_data, sizeof(unsigned char), (cat_rast_region->cols)/sizeof(unsigned char), f_cat_rast);
- // TODO when code will be paralelized put it out of the loop
if (ferror(f_cat_rast))
{
fclose(f_cat_rast);
- G_debug(3, "Unable to write into file.");
+ G_warning(_("Unable to write into category raster condition file <%s>."), cat_rast);
return -1;
}
}
@@ -71,12 +92,22 @@
return 0;
}
-static int print_reg(struct Cell_head * intersec, const char * pref)
+static int print_reg(struct Cell_head * intersec, const char * pref, int dbg_level)
{
- G_message("%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south,
+ G_debug(dbg_level, "%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south,
intersec->east, intersec->west, intersec->ns_res, intersec->ew_res);
}
+/*!
+ \brief Find intersection region of two regions.
+
+ \param A pointer to intersected region
+ \param B pointer to intersected region
+ \param [out] intersec pointer to intersection region of regions A B (relevant params of the region are: south, north, east, west)
+
+ \return 0 if interection exists
+ \return -1 if regions does not intersect
+ */
static int regions_intersecion(struct Cell_head * A, struct Cell_head * B, struct Cell_head * intersec)
{
@@ -104,17 +135,34 @@
}
+/*!
+ \brief Get rows and cols numbers, which defines intersection of the regions.
+
+ \param A pointer to intersected region
+ \param B pointer to intersected region (A and B must have same resolution)
+ \param [out] A_bounds rows and cols numbers of A stored in south, north, east, west, which defines intersection of A and B
+ \param [out] B_bounds rows and cols numbers of B stored in south, north, east, west, which defines intersection of A and B
+
+ \return 0 if interection exists
+ \return -1 if regions do not intersect
+ \return -2 resolution of regions is not same
+*/
static int get_rows_and_cols_bounds(struct Cell_head * A, struct Cell_head * B, struct Cell_head * A_bounds, struct Cell_head * B_bounds)
{
float ns_res, ew_res;
struct Cell_head intersec;
- if(A->ns_res != B->ns_res)
- return -1;
+ /* TODO is it right check? */
+ if(abs(A->ns_res - B->ns_res) > GRASS_EPSILON) {
+ G_debug(0, "'get_rows_and_cols_bounds' ns_res does not fit, A->ns_res: %f B->ns_res: %f", A->ns_res, B->ns_res);
+ return -2;
+ }
- if(A->ew_res != B->ew_res)
- return -1;
+ if(abs(A->ew_res - B->ew_res) > GRASS_EPSILON) {
+ G_debug(0, "'get_rows_and_cols_bounds' ew_res does not fit, A->ew_res: %f B->ew_res: %f", A->ew_res, B->ew_res);
+ return -2;
+ }
ns_res = A->ns_res;
ew_res = A->ew_res;
@@ -137,7 +185,17 @@
return 0;
}
+/*!
+ \brief Insert raster map patch into pgm file.
+ Warning: calls Rast_set_window
+ \param patch_rast name of raster map
+ \param cat_rast_region region of category raster file
+ \param cat_rast path to category raster file
+
+ \return 0 on success
+ \return -1 on failure
+*/
int I_insert_patch_to_cat_rast(const char * patch_rast, struct Cell_head * cat_rast_region, const char * cat_rast)
{
@@ -145,13 +203,12 @@
struct Cell_head patch_region, patch_bounds, cat_rast_bounds;
char cat_rast_header[1024];//TODO magic number
int i_row, i_col, ncols, nrows, cat_rast_col, patch_col, val;
- int head_nchars;
+ int head_nchars, ret;
int fd_patch_rast, init_shift, step_shift;
unsigned char * patch_data;
- char * null_chunk;
+ char * null_chunk_row;
- //TODO G_free mapset (aslo in compute scatts)
const char *mapset;
struct Cell_head patch_lines, cat_rast_lines;
@@ -159,16 +216,16 @@
unsigned char * row_data;
f_cat_rast = fopen(cat_rast, "rb+");
- if(!f_cat_rast)
- return -10;
+ if(!f_cat_rast) {
+ G_warning(_("Unable to open category raster condtions file <%s>."), cat_rast);
+ return -1;
+ }
-
- /* TODO */
head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
-
- if ((mapset = G_find_raster2(patch_rast,"")) == NULL) {
+ if ((mapset = G_find_raster(patch_rast,"")) == NULL) {
fclose(f_cat_rast);
- return -2;
+ G_warning(_("Unable to find patch raster <%s>."), patch_rast);
+ return -1;
}
Rast_get_cellhd(patch_rast, mapset, &patch_region);
@@ -176,13 +233,26 @@
if ((fd_patch_rast = Rast_open_old(patch_rast, mapset)) < 0) {
fclose(f_cat_rast);
- return -3;
+ return -1;
}
- null_chunk = Rast_allocate_null_buf();
+ ret = get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds);
+ if(ret == -2) {
+ G_warning(_("Resolutions of patch <%s> and patched file <%s> are not same."), patch_rast, cat_rast);
- if(get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds) == -1) return -1;
+ Rast_close(fd_patch_rast);
+ fclose(f_cat_rast);
+ return -1;
+ }
+ else if (ret == -1){
+
+ Rast_close(fd_patch_rast);
+ fclose(f_cat_rast);
+
+ return 0;
+ }
+
ncols = cat_rast_bounds.east - cat_rast_bounds.west;
nrows = cat_rast_bounds.south - cat_rast_bounds.north;
@@ -191,19 +261,26 @@
init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
if(fseek(f_cat_rast, init_shift, SEEK_SET) != 0) {
- G_message("seek failed");
- return -4;
+ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
+
+ Rast_close(fd_patch_rast);
+ G_free(null_chunk_row);
+ fclose(f_cat_rast);
+
+ return -1;
}
step_shift = cat_rast_region->cols - ncols;
+
+ null_chunk_row = Rast_allocate_null_buf();
for(i_row = 0; i_row < nrows; i_row++) {
- Rast_get_null_value_row (fd_patch_rast, null_chunk, i_row + patch_bounds.north);
+ Rast_get_null_value_row (fd_patch_rast, null_chunk_row, i_row + patch_bounds.north);
for(i_col = 0; i_col < ncols; i_col++) {
patch_col = patch_bounds.west + i_col;
- if(null_chunk[patch_col] != 1)
+ if(null_chunk_row[patch_col] != 1)
patch_data[i_col] = 1 & 255;
else {
patch_data[i_col] = 0 & 255;
@@ -213,78 +290,122 @@
fwrite(patch_data, sizeof(unsigned char), (ncols)/sizeof(unsigned char), f_cat_rast);
if (ferror(f_cat_rast))
{
- G_free(null_chunk);
+ G_warning(_("Unable to write into category raster conditions file <%s>"), cat_rast);
+
+ Rast_close(fd_patch_rast);
+ G_free(null_chunk_row);
fclose(f_cat_rast);
- return -5;
+
+ return -1;
}
if(fseek(f_cat_rast, step_shift, SEEK_CUR) != 0) {
- G_message("seek failed");
- return -6;
+ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
+
+ Rast_close(fd_patch_rast);
+ G_free(null_chunk_row);
+ fclose(f_cat_rast);
+
+ return -1;
}
}
Rast_close(fd_patch_rast);
- G_free(null_chunk);
+ G_free(null_chunk_row);
fclose(f_cat_rast);
return 0;
}
-static inline void update_cat_scatt_plt(CELL ** chunks, char ** null_chunks, int chunk_size, unsigned short * belongs_pix, struct scScatts * scatts)
+/*!
+ \brief Updates scatter plots data in category by pixels which meets category conditions.
+
+ \param bands_rows data represents data describig one row from raster band
+ \param belongs_pix array which defines which pixels belongs to category (1 value) and which not (0 value)
+ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, which are modified according to values in belongs_pix
+*/
+static inline void update_cat_scatt_plts(struct rast_row * bands_rows, unsigned short * belongs_pix, struct scScatts * scatts)
{
- int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunks_pix, max_arr_idx;
+ int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunk_rows_pix, max_arr_idx;
- CELL * b_1_chunks;
- CELL * b_2_chunks;
- char * band_1_null_chunks,* band_2_null_chunks;
+ CELL * b_1_row;
+ CELL * b_2_row;
+ char * b_1_null_row,* b_2_null_row;
+ struct rast_row b_1_rast_row, b_2_rast_row;
struct Range b_1_range, b_2_range;
int b_1_range_size;
+ int row_size = Rast_window_cols();
+
int * scatts_bands = scatts->scatts_bands;
for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
{
- b_1_chunks = chunks[scatts_bands[i_scatt * 2]];
- b_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
+ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
+ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
- band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
- band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
+ b_1_row = b_1_rast_row.row;
+ b_2_row = b_2_rast_row.row;
- b_1_range = scatts->scatts_arr[i_scatt]->band_1_range;
- b_2_range = scatts->scatts_arr[i_scatt]->band_2_range;
+ b_1_null_row = b_1_rast_row.null_row;
+ b_2_null_row = b_2_rast_row.null_row;
+ b_1_range = b_1_rast_row.rast_range;
+ b_2_range = b_2_rast_row.rast_range;
+
b_1_range_size = b_1_range.max - b_1_range.min + 1;
max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ for(i_chunk_rows_pix = 0; i_chunk_rows_pix < row_size; i_chunk_rows_pix++)
{
- if(!belongs_pix[i_chunks_pix] ||
- band_1_null_chunks[i_chunks_pix] == 1 ||
- band_2_null_chunks[i_chunks_pix] == 1)
+ /* pixel does not belongs to scatter plot or has null value in one of the bands */
+ if(!belongs_pix[i_chunk_rows_pix] ||
+ b_1_null_row[i_chunk_rows_pix] == 1 ||
+ b_2_null_row[i_chunk_rows_pix] == 1)
continue;
- array_idx = b_1_chunks[i_chunks_pix] - b_1_range.min + (b_2_chunks[i_chunks_pix] - b_2_range.min) * b_1_range_size;
+ /* index in scatter plot array */
+ array_idx = b_1_row[i_chunk_rows_pix] - b_1_range.min + (b_2_row[i_chunk_rows_pix] - b_2_range.min) * b_1_range_size;
if(array_idx < 0 || array_idx >= max_arr_idx) {
- G_warning ("pixel out of range");
+ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
continue;
}
+
+ /* increment scatter plot value */
++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
}
}
}
-static inline int compute_scatts_from_chunk(struct scCats * scatt_plts, struct scCats * scatt_conds,
- CELL ** chunks, char ** null_chunks, int chunk_size, FILE ** f_cats_rasts_in,
- FILE ** f_cats_rasts_out, struct Cell_head *region, int i_chunk)
+/*!
+ \brief Computes scatter plots data from chunk_rows.
+
+ \param scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
+ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are selected areas (condtitions)stored
+
+ \param chunk_rows data arrays of chunk_rows from analyzed raster bands (all data in chunk_rows, null_chunk_rows and belongs_pix arrays represents same region)
+ \param null_chunk_rows null data arrays of chunk_rows from analyzed raster bands
+ \param row_size size of data in chunk_rows, null_chunk_rows and belongs_pix arrays
+ \param f_cats_rasts_conds file which stores selected areas (conditions) from mapwindow see I_create_cat_rast() and I_pa
+ \param fd_cats_rasts array of openedraster maps which represents all selected pixels for category
+ \param region analysis region
+
+ \return 0 on success
+ \return -1 on failure
+*/
+static inline int compute_scatts_from_chunk_row(struct Cell_head *region, struct scCats * scatt_conds,
+ FILE ** f_cats_rasts_conds, struct rast_row * bands_rows,
+ struct scCats * scatts, int * fd_cats_rasts)
{
- int i_chunks_pix, i_cat, i_scatt, n_a_scatts, i_cond;
+ int i_rows_pix, i_cat, i_scatt, n_a_scatts, n_pixs;
int cat_id, scatt_plts_cat_idx, array_idx, max_arr_idx;
- char * band_1_null_chunks,* band_2_null_chunks;
-
+ char * b_1_null_row,* b_2_null_row;
+ struct rast_row b_1_rast_row, b_2_rast_row;
+ CELL * cat_rast_row;
+
struct scScatts * scatts_conds;
- struct scScatts * scatt_plts_scatts;
+ struct scScatts * scatts_scatt_plts;
struct scdScattData * conds;
struct Range b_1_range, b_2_range;
@@ -293,116 +414,135 @@
int * scatts_bands;
struct scdScattData ** scatts_arr;
- CELL * b_1_chunks;
- CELL * b_2_chunks;
+ CELL * b_1_row;
+ CELL * b_2_row;
unsigned char * i_scatt_conds;
- unsigned short * belongs_pix = (unsigned short *) G_malloc(chunk_size * sizeof(unsigned short));
- unsigned char * rast_pixs = (unsigned char *) G_malloc(chunk_size * sizeof(unsigned char));
+ int row_size = Rast_window_cols();
+ unsigned short * belongs_pix = (unsigned short *) G_malloc(row_size * sizeof(unsigned short));
+ unsigned char * rast_pixs = (unsigned char *) G_malloc(row_size * sizeof(unsigned char));
+ cat_rast_row = Rast_allocate_c_buf();
+
+
for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
{
scatts_conds = scatt_conds->cats_arr[i_cat];
cat_id = scatt_conds->cats_ids[i_cat];
- scatt_plts_cat_idx = scatt_plts->cats_idxs[cat_id];
+ scatt_plts_cat_idx = scatts->cats_idxs[cat_id];
if(scatt_plts_cat_idx < 0)
continue;
+ scatts_scatt_plts = scatts->cats_arr[scatt_plts_cat_idx];
- scatt_plts_scatts = scatt_plts->cats_arr[scatt_plts_cat_idx];
+ G_zero(belongs_pix, row_size * sizeof(unsigned short));
- G_zero(belongs_pix, chunk_size * sizeof(unsigned short));
-
- if(!scatts_conds->n_a_scatts && !f_cats_rasts_in[i_cat]) {
- for(i_scatt = 0; i_scatt < scatt_plts_scatts->n_a_scatts; i_scatt++)
+ /* if category has no conditions defined, scatter plots without any constraint are computed (default scatter plots) */
+ if(!scatts_conds->n_a_scatts && !f_cats_rasts_conds[i_cat]) {
+ for(i_scatt = 0; i_scatt < scatts_scatt_plts->n_a_scatts; i_scatt++)
{
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
- belongs_pix[i_chunks_pix] = 1;
+ /* all pixels belongs */
+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
+ belongs_pix[i_rows_pix] = 1;
}
}
+ /* compute belonging pixels for defined conditions */
else
{
scatts_bands = scatts_conds->scatts_bands;
- if(f_cats_rasts_in[i_cat])
-
- fread(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_in[i_cat]);
- if (ferror(f_cats_rasts_in[i_cat]))
+ /* check conditions from category raster condtitions file */
+ if(f_cats_rasts_conds[i_cat]) {
+ n_pixs = fread(rast_pixs, sizeof(unsigned char), (row_size)/sizeof(unsigned char), f_cats_rasts_conds[i_cat]);
+
+ if (ferror(f_cats_rasts_conds[i_cat]))
{
G_free(rast_pixs);
G_free(belongs_pix);
- G_message("Unable to read from file.");
+ G_warning(_("Unable to read from category raster condtition file."));
return -1;
}
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ if (n_pixs != n_pixs) {
+ G_free(rast_pixs);
+ G_free(belongs_pix);
+ G_warning(_("Invalid size of category raster conditions file."));
+ return -1;
+
+ }
+
+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
{
- if(rast_pixs[i_chunks_pix] != 0 & 255)
- belongs_pix[i_chunks_pix] = 1;
+ if(rast_pixs[i_rows_pix] != 0 & 255)
+ belongs_pix[i_rows_pix] = 1;
}
+ }
- // test every defined conditions in scatter plots
+ /* check condtions defined in scatter plots*/
for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
{
- b_1_chunks = chunks[scatts_bands[i_scatt * 2]];
- b_2_chunks = chunks[scatts_bands[i_scatt * 2 + 1]];
+ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
+ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
- band_1_null_chunks = null_chunks[scatts_bands[i_scatt * 2]];
- band_2_null_chunks = null_chunks[scatts_bands[i_scatt * 2 + 1]];
+ b_1_row = b_1_rast_row.row;
+ b_2_row = b_2_rast_row.row;
- i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
+ b_1_null_row = b_1_rast_row.null_row;
+ b_2_null_row = b_2_rast_row.null_row;
- b_1_range = scatts_conds->scatts_arr[i_scatt]->band_1_range;
- b_2_range = scatts_conds->scatts_arr[i_scatt]->band_2_range;
+ b_1_range = b_1_rast_row.rast_range;
+ b_2_range = b_2_rast_row.rast_range;
b_1_range_size = b_1_range.max - b_1_range.min + 1;
-
max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
- //G_message("max_arr_idx scatt_conds: %d", max_arr_idx);
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
+ i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
+
+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
{
- if(belongs_pix[i_chunks_pix] ||
- band_1_null_chunks[i_chunks_pix] == 1 ||
- band_2_null_chunks[i_chunks_pix] == 1)
+ /* pixels already belongs to category from category raster conditions file or contains null value in one of the bands */
+ if(belongs_pix[i_rows_pix] ||
+ b_1_null_row[i_rows_pix] == 1 ||
+ b_2_null_row[i_rows_pix] == 1)
continue;
- array_idx = b_1_chunks[i_chunks_pix] - b_1_range.min + (b_2_chunks[i_chunks_pix] - b_2_range.min) * b_1_range_size;
- /*TODO test value */
+ array_idx = b_1_row[i_rows_pix] - b_1_range.min + (b_2_row[i_rows_pix] - b_2_range.min) * b_1_range_size;
if(array_idx < 0 || array_idx >= max_arr_idx) {
- G_warning ("pixel out of range");
+ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
continue;
}
+ /* pixels meets condtion defined in scatter plot */
if(i_scatt_conds[array_idx])
- belongs_pix[i_chunks_pix] = 1;
+ belongs_pix[i_rows_pix] = 1;
}
}
}
- if(f_cats_rasts_out[i_cat]) {
- for(i_chunks_pix = 0; i_chunks_pix < chunk_size; i_chunks_pix++)
- rast_pixs[i_chunks_pix] = belongs_pix[i_chunks_pix] & 255;
+ /* update category raster with belonging pixels */
+ if(fd_cats_rasts[i_cat] >= 0) {
+ Rast_set_null_value(cat_rast_row, Rast_window_cols(), CELL_TYPE);
- fwrite(rast_pixs, sizeof(unsigned char), (chunk_size)/sizeof(unsigned char), f_cats_rasts_out[i_cat]);
- // TODO when code will be paralelized put it out of the loop
- if (ferror(f_cats_rasts_out[i_cat]))
- {
- G_free(rast_pixs);
- G_free(belongs_pix);
- G_debug(3, "Unable to write into file.");
- return -1;
- }
+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
+ if(belongs_pix[i_rows_pix])
+ cat_rast_row[i_rows_pix] = belongs_pix[i_rows_pix];
+
+ Rast_put_c_row (fd_cats_rasts[i_cat], cat_rast_row);
}
- update_cat_scatt_plt(chunks, null_chunks, chunk_size, belongs_pix, scatt_plts_scatts);
+ /* update scatter plots with belonging pixels */
+ update_cat_scatt_plts(bands_rows, belongs_pix, scatts_scatt_plts);
}
+ G_free(cat_rast_row);
G_free(rast_pixs);
G_free(belongs_pix);
return 0;
}
+/*!
+ \brief Get list if bands needed to be opened for analysis from scCats struct.
+*/
static void get_needed_bands(struct scCats * cats, int * b_needed_bands)
{
// results in b_needed_bands - array of bools - if item has value 1, band (defined by item index) is needed to be opened
@@ -421,190 +561,338 @@
return;
}
-static void free_compute_scatts_data(int * fd_bands, CELL ** chunks, char ** null_chunks, int * n_a_bands, int * bands_ids,
- FILE ** f_cats_rasts_in, FILE ** f_cats_rasts_out, int n_a_cats)
+/*!
+ \brief Helper function for clean up.
+*/
+static void free_compute_scatts_data(int * fd_bands, struct rast_row * bands_rows, int n_a_bands, int * bands_ids,
+ int * fd_cats_rasts, FILE ** f_cats_rasts_conds, int n_a_cats)
{
int i, band_id;
- for(i = 0; i < (* n_a_bands); i++)
+ for(i = 0; i < n_a_bands; i++)
{
band_id = bands_ids[i];
if(band_id >= 0) {
Rast_close(fd_bands[i]);
- G_free(chunks[band_id]);
- G_free(null_chunks[band_id]);
+ G_free(bands_rows[band_id].row);
+ G_free(bands_rows[band_id].null_row);
}
}
-
- if(f_cats_rasts_in)
+
+ if(f_cats_rasts_conds)
for(i = 0; i < n_a_cats; i++)
- if(f_cats_rasts_in[i])
- fclose(f_cats_rasts_in[i]);
+ if(f_cats_rasts_conds[i])
+ fclose(f_cats_rasts_conds[i]);
- if(f_cats_rasts_out)
+ if(fd_cats_rasts)
for(i = 0; i < n_a_cats; i++)
- if(f_cats_rasts_out[i])
- fclose(f_cats_rasts_out[i]);
+ if(fd_cats_rasts[i] >= 0)
+ Rast_close(fd_cats_rasts[i]);
}
+/*!
+ \brief Compute scatter plots data.
-static int open_cats_rasts_files(const char ** cats_rasts, int n_a_cats, int * cats_ids, const char * mode, FILE ** f_cats_rasts)
-{
- int i_cat, id_cat;
+ If category has not defined no category raster condition file and no scatter plot with consdtion,
+ default scatter plot is computed.
+ Warning: calls Rast_set_window
- for(i_cat = 0; i_cat < n_a_cats; i_cat++)
- {
- id_cat = cats_ids[i_cat];
+ \param region analysis region, beaware that all input data must be prepared for this region (bands (their ranges), cats_rasts_conds rasters...)
+ \param region function calls Rast_set_window for this region
+ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are stored selected areas (conditions) in scatter plots
+ \param cats_rasts_conds paths to category raster conditions files representing selected areas (conditions) in rasters for every category
+ \param cats_rasts_conds index in array represents corresponding category id
+ \param cats_rasts_conds for manupulation with category raster conditions file see also I_id_scatt_to_bands() and I_insert_patch_to_cat_rast()
+ \param bands names of analyzed bands, order of bands is defined by their id
+ \param n_bands number of bands
+ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
+ \param [out] cats_rasts array of raster maps names where will be stored all selected pixels for every category
- if(cats_rasts[id_cat]) {
- f_cats_rasts[i_cat] = fopen(cats_rasts[id_cat], mode);
- if(!f_cats_rasts[i_cat]) return -1;
- }
- else
- f_cats_rasts[i_cat] = NULL;
- }
-
- return 0;
-}
-
-static int cats_rasts_write_header(int n_a_cats, struct Cell_head * region, FILE ** f_cats_rasts)
+ \return 0 on success
+ \return -1 on failure
+*/
+int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** cats_rasts_conds,
+ const char ** bands, int n_bands, struct scCats * scatts, const char ** cats_rasts)
{
- int i_cat, id_cat, head_nchars;
- char cat_rast_header[1024];//TODO magic number
-
- head_nchars = get_cat_rast_header(region, cat_rast_header);
-
- for(i_cat = 0; i_cat < n_a_cats; i_cat++)
- {
- if(!f_cats_rasts[i_cat])
- continue;
-
- fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cats_rasts[i_cat]);
- if (ferror(f_cats_rasts[i_cat])) return -1;
- }
-
- return head_nchars;
-
-}
-
-//TODO change name: I_UpdateScattData???
-int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** bands,
- int n_bands, struct scCats * scatt_plts, const char ** cats_rasts_in, const char ** cats_rasts_out)
-{/*scatt_plts must be zeros! */
- if (n_bands != scatt_plts->n_bands ||
- n_bands != scatt_conds->n_bands)
- return -1;
-
const char *mapset;
+ char header[1024];
- FILE * f_cats_rasts_out[scatt_conds->n_a_cats];
- FILE * f_cats_rasts_in[scatt_conds->n_a_cats];
+ int fd_cats_rasts[scatt_conds->n_a_cats];
+ FILE * f_cats_rasts_conds[scatt_conds->n_a_cats];
- CELL * chunks[n_bands];
- char * null_chunks[n_bands];
+ struct rast_row bands_rows[n_bands];
RASTER_MAP_TYPE data_type;
- int nrows, i_band, id_band, n_a_bands, band_id,
- chunk_size, i, i_chunk, i_row, head_nchars, i_cat;
+ int nrows, i_band, n_a_bands, band_id,
+ i, i_row, head_nchars, i_cat, id_cat;
+
int fd_bands[n_bands];
+ int bands_ids[n_bands];
+ int b_needed_bands[n_bands];
+
+ Rast_set_window(region);
+
for(i_band = 0; i_band < n_bands; i_band++)
fd_bands[i_band] = -1;
- int bands_ids[n_bands];
for(i_band = 0; i_band < n_bands; i_band++)
bands_ids[i_band] = -1;
- int b_needed_bands[n_bands];
+ if (n_bands != scatts->n_bands ||
+ n_bands != scatt_conds->n_bands)
+ return -1;
+
memset(b_needed_bands, 0, (size_t)n_bands * sizeof(int));
get_needed_bands(scatt_conds, &b_needed_bands[0]);
- get_needed_bands(scatt_plts, &b_needed_bands[0]);
+ get_needed_bands(scatts, &b_needed_bands[0]);
- Rast_set_window(region);
+ n_a_bands = 0;
- n_a_bands = 0;/*TODO realy needed?*/
- for(id_band = 0; id_band < n_bands; id_band++)
+ /* open band rasters, which are needed for computation */
+ for(band_id = 0; band_id < n_bands; band_id++)
{
- if(b_needed_bands[id_band])
+ if(b_needed_bands[band_id])
{
- G_debug(3, "Opening raster no. %d with name: %s", id_band, bands[id_band]);
+ G_debug(3, "Opening raster no. %d with name: %s", band_id, bands[band_id]);
- /* TODO solve returns*/
- if ((mapset = G_find_raster2(bands[id_band],"")) == NULL) {
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
+ if ((mapset = G_find_raster2(bands[band_id],"")) == NULL) {
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+ G_warning(_("Unbale to read find raster <%s>"), bands[band_id]);
return -1;
}
- if ((fd_bands[n_a_bands] = Rast_open_old(bands[id_band], mapset)) < 0) {
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], NULL, NULL, scatt_conds->n_a_cats);
+ if ((fd_bands[n_a_bands] = Rast_open_old(bands[band_id], mapset)) < 0) {
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+ G_warning(_("Unbale to open raster <%s>"), bands[band_id]);
return -1;
}
- //TODO check data type, minimum and maximum value
data_type = Rast_get_map_type(fd_bands[n_a_bands]);
- //if(data_type != CELL)
- // return -1;
+ if(data_type != CELL_TYPE) {
+ G_warning(_("Raster <%s> type is not <%s>"), bands[band_id], "CELL");
+ return -1;
+ }
- //What happens if it is not CELL tyoe
- chunks[id_band] = Rast_allocate_c_buf();
- null_chunks[id_band] = Rast_allocate_null_buf();
+ bands_rows[band_id].row = Rast_allocate_c_buf();
+ bands_rows[band_id].null_row = Rast_allocate_null_buf();
- bands_ids[n_a_bands] = id_band;
+ if(Rast_read_range(bands[band_id], mapset, &bands_rows[band_id].rast_range) != 1){
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
+ G_warning(_("Unable to read range of raster <%s>"), bands[band_id]);
+ return -1;
+ }
+ bands_ids[n_a_bands] = band_id;
++n_a_bands;
}
}
- if (open_cats_rasts_files(cats_rasts_out, scatt_conds->n_a_cats, scatt_conds->cats_ids, "wb", f_cats_rasts_out) != 0) {
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
- return -1;
- }
+ /* open category rasters condition files and category rasters */
+ for(i_cat = 0; i_cat < scatts->n_a_cats; i_cat++)
+ {
+ id_cat = scatts->cats_ids[i_cat];
+ if(cats_rasts[id_cat]) {
+ fd_cats_rasts[i_cat] = Rast_open_new(cats_rasts[id_cat], CELL_TYPE);
+ }
+ else
+ fd_cats_rasts[i_cat] = -1;
- head_nchars = cats_rasts_write_header(scatt_conds->n_a_cats, region, f_cats_rasts_out);
- if (head_nchars < 0) {
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], &f_cats_rasts_out[0], NULL, scatt_conds->n_a_cats);
- return -1;
+ if(cats_rasts_conds[id_cat]) {
+ f_cats_rasts_conds[i_cat] = fopen(cats_rasts_conds[id_cat], "r");
+ if(!f_cats_rasts_conds[i_cat]) {
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
+ f_cats_rasts_conds, f_cats_rasts_conds, scatt_conds->n_a_cats);
+ G_warning(_("Unable to open category raster condtition file <%s>"), bands[band_id]);
+ return -1;
+ }
+ }
+ else
+ f_cats_rasts_conds[i_cat] = NULL;
}
- if (open_cats_rasts_files(cats_rasts_in, scatt_conds->n_a_cats, scatt_conds->cats_ids, "rb", f_cats_rasts_in) != 0) {
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
- return -1;
- }
-
+ head_nchars = get_cat_rast_header(region, header);
for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
- if(f_cats_rasts_in[i_cat])
- fseek(f_cats_rasts_in[i_cat] , head_nchars, SEEK_SET);
+ if(f_cats_rasts_conds[i_cat])
+ if( fseek(f_cats_rasts_conds[i_cat] , head_nchars, SEEK_SET) != 0) {
+ G_warning(_("Corrupted category raster conditions file (fseek failed)"));
+ return -1;
+ }
- i_chunk = 0;
-
nrows = Rast_window_rows();
- chunk_size = Rast_window_cols();
+ /* analyze bands by rows */
for (i_row = 0; i_row < nrows; i_row++)
{
for(i_band = 0; i_band < n_a_bands; i_band++)
{
band_id = bands_ids[i_band];
- G_debug(3, "Reading data for band %d", band_id);
- Rast_get_c_row(fd_bands[i_band], chunks[band_id], i_row);
- Rast_get_null_value_row (fd_bands[i_band], null_chunks[band_id], i_row);
+ Rast_get_c_row(fd_bands[i_band], bands_rows[band_id].row, i_row);
+ Rast_get_null_value_row (fd_bands[i_band], bands_rows[band_id].null_row, i_row);
}
- if(compute_scatts_from_chunk(scatt_plts, scatt_conds, chunks, null_chunks, chunk_size, &f_cats_rasts_in[0], &f_cats_rasts_out[0], region, i_chunk) == -1)
+ if(compute_scatts_from_chunk_row(region, scatt_conds, f_cats_rasts_conds, bands_rows, scatts, fd_cats_rasts) == -1)
{
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, fd_cats_rasts,
+ f_cats_rasts_conds, scatt_conds->n_a_cats);
return -1;
}
}
- free_compute_scatts_data(&fd_bands[0], &chunks[0], &null_chunks[0], &n_a_bands,
- &bands_ids[0], &f_cats_rasts_out[0], &f_cats_rasts_in[0], scatt_conds->n_a_cats);
+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
+ fd_cats_rasts, f_cats_rasts_conds, scatt_conds->n_a_cats);
return 0;
-}
\ No newline at end of file
+}
+
+/*!
+ \brief Merge arrays according to opacity.
+ Every pixel in array must be represented by 4 values (RGBA).
+
+ Implementd for speeding up of scatter plots rendering.
+
+ \param merged_arr array which will be overlayd with overlay_arr
+ \param overlay_arr array to be merged_arr overlayed with
+ \param rows number of rows for the both arrays
+ \param cols number of columns for the both arrays
+ \param alpha transparency of the overlay array for merging
+
+ \return 0
+*/
+int I_merge_arrays(unsigned char * merged_arr, unsigned char * overlay_arr, unsigned rows, unsigned cols, double alpha)
+{
+ unsigned int i_row, i_col, i_b;
+ unsigned int row_idx, col_idx, idx;
+ unsigned int c_a_i, c_a;
+
+ for(i_row = 0; i_row < rows; i_row++)
+ {
+ row_idx = i_row * cols;
+ for(i_col = 0; i_col < cols; i_col++)
+ {
+ col_idx = 4 * (row_idx + i_col);
+ idx = col_idx + 3;
+
+ c_a = overlay_arr[idx] * alpha;
+ c_a_i = 255 - c_a;
+
+ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * 255) / 255;
+
+ for(i_b = 0; i_b < 3; i_b++)
+ {
+ idx = col_idx + i_b;
+ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * (int)overlay_arr[idx]) / 255;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ \brief Apply colromap to the raster.
+
+ Implementd for speeding up of scatter plots rendering.
+
+ \param vals array of values for applying the colormap
+ \param vals_mask maks of vals array
+ \param nvals number of items of vals_mask and vals array
+ \param colmap coulour map to be applied
+ \param[out] col_vals output raster with applied color map (length is 4 * nvals (RGBA))
+
+ \return 0
+*/
+int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned nvals, unsigned char * colmap, unsigned char * col_vals){
+ unsigned int i_val;
+ int v, i, i_cm;
+
+ for(i_val = 0; i_val < nvals; i_val++){
+ i_cm = 4 * i_val;
+
+ v = vals[i_val];
+
+ if(vals_mask && vals_mask[i_val])
+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[258 * 4 + i];
+ else if(v > 255)
+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[257 * 4 + i];
+ else if(v < 0)
+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[256 * 4 + i];
+ else
+ for(i = 0; i < 4; i++){ col_vals[i_cm + i] = colmap[v * 4 + i];
+ }
+ }
+ return 0;
+}
+
+
+/*!
+ \brief Wrapper for using of iclass perimeter rasterization by scatter plot.
+ Warning: calls Rast_set_window
+
+ \param polygon array of polygon coordinates [x, y, x, y...]
+ \param pol_n_pts number of points in the polygon array
+ \param val value to be assigned to cells, which belong to plygon
+ \param rast_region region of raster
+ \param[out] rast raster to be pologyn rasterized in
+
+ \return 0 on success
+ \return 1 on failure
+*/
+
+int I_rasterize(double * polygon, int pol_n_pts, unsigned char val, struct Cell_head *rast_region, unsigned char * rast){
+ int i;
+ int x0, x1, y;
+ int row, row_idx, i_col;
+
+ IClass_perimeter perimeter;
+
+ struct line_pnts *pol;
+ pol = Vect_new_line_struct();
+
+ for(i = 0; i < pol_n_pts; i++) {
+ Vect_append_point(pol,
+ polygon[i*2],
+ polygon[i*2 + 1],
+ 0.0);
+ }
+
+ Rast_set_window(rast_region);
+
+ make_perimeter(pol, &perimeter, rast_region);
+ for (i = 1; i < perimeter.npoints; i += 2) {
+ y = perimeter.points[i].y;
+ if (y != perimeter.points[i - 1].y) {
+ G_warning(_("prepare_signature: scan line %d has odd number of points."),
+ (i + 1) / 2);
+ return 1;
+ }
+
+ x0 = perimeter.points[i - 1].x;
+ x1 = perimeter.points[i].x;
+
+ if (x0 > x1) {
+ G_warning(_("signature: perimeter points out of order."));
+ return 1;
+ }
+
+ row = (rast_region->rows - y);
+ if(row < 0 || row >= rast_region->rows) {
+ continue;
+ }
+
+ row_idx = rast_region->cols * row;
+
+ for(i_col = x0; i_col <= x1; i_col++) {
+ if(i_col < 0 || i_col >= rast_region->cols) {
+ continue;
+ }
+ rast[row_idx + i_col] = val;
+ }
+ }
+
+ Vect_destroy_line_struct(pol);
+ G_free(perimeter.points);
+ return 0;
+}
Modified: sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c
===================================================================
--- sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/lib/imagery/scatt_sccats.c 2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,10 +1,8 @@
/*!
\file lib/imagery/scatt_cat_rast.c
- \brief Imagery library - functions for wx Scatter Plot Tool.
+ \brief Imagery library - functions for manipulation with scatter plot structs.
- Low level functions used by wx Scatter Plot Tool.
-
Copyright (C) 2013 by the GRASS Development Team
This program is free software under the GNU General Public License
@@ -22,44 +20,84 @@
#include <math.h>
#include <string.h>
-//TODO split formulas to be more understandable
-static void id_scatt_to_bands(const int scatt_id, const int n_bands, int * band_1, int * band_2)
+/*!
+ \brief Compute band ids from scatter plot id.
+
+ Scatter plot id describes which bands defines the scatter plot.
+
+ Let say we have 3 bands, their ids are 0, 1 and 2.
+ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
+ All scatter plots:
+ scatt_id b_1_id b_2_id
+ 0 0 1
+ 1 0 2
+ 2 1 2
+
+ \param scatt_id scatter plot id
+ \param n_bands number of bands
+ \param [out] b_1_id id of band1
+ \param[out] b_2_id id of band2
+
+ \return 0
+ */
+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
{
int n_b1 = n_bands - 1;
- * band_1 = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
+ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
- * band_2 = scatt_id - ((* band_1) * (2 * n_b1 + 1) - (* band_1) * (* band_1)) / 2 + (* band_1) + 1;
+ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
- return;
+ return 0;
}
-static void bands_to_id_scatt(const int band_1, const int band_2, const int n_bands, int * scatt_id)
+
+/*!
+ \brief Compute scatter plot id from band ids.
+
+ See also I_id_scatt_to_bands().
+
+ \param n_bands number of bands
+ \param b_1_id id of band1
+ \param b_1_id id of band2
+ \param [out] scatt_id scatter plot id
+
+ \return 0
+ */
+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
{
int n_b1 = n_bands - 1;
- * scatt_id = (band_1 * (2 * n_b1 + 1) - band_1 * band_1) / 2 + band_2 - band_1 - 1;
+ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
- return;
+ return 0;
}
+/*!
+ \brief Initialize structure for storing scatter plots data.
+
+ \param cats pointer to scCats struct
+ \param n_bands number of bands
+ \param type SC_SCATT_DATA - stores scatter plots
+ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
+ */
void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
{
int i_cat;
cats->type = type;
- cats->n_cats = 10;
+ cats->n_cats = 100;
cats->n_a_cats = 0;
cats->n_bands = n_bands;
cats->n_scatts = (n_bands - 1) * n_bands / 2;
- cats->cats_arr = (struct scScatts **) malloc(cats->n_cats * sizeof(struct scScatts *));
+ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
- cats->cats_ids = (int *) malloc(cats->n_cats * sizeof(int));
- cats->cats_idxs =(int *) malloc(cats->n_cats * sizeof(int));
+ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
+ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
cats->cats_idxs[i_cat] = -1;
@@ -67,6 +105,11 @@
return;
}
+/*!
+ \brief Free data of struct scCats, the structure itself remains alocated.
+
+ \param cats pointer to existing scCats struct
+ */
void I_sc_free_cats(struct scCats * cats)
{
int i_cat;
@@ -75,16 +118,16 @@
{
if(cats->cats_arr[i_cat])
{
- free(cats->cats_arr[i_cat]->scatt_idxs);
- free(cats->cats_arr[i_cat]->scatts_bands);
- free(cats->cats_arr[i_cat]->scatts_arr);
- free(cats->cats_arr[i_cat]);
+ G_free(cats->cats_arr[i_cat]->scatt_idxs);
+ G_free(cats->cats_arr[i_cat]->scatts_bands);
+ G_free(cats->cats_arr[i_cat]->scatts_arr);
+ G_free(cats->cats_arr[i_cat]);
}
}
- free(cats->cats_ids);
- free(cats->cats_idxs);
- free(cats->cats_arr);
+ G_free(cats->cats_ids);
+ G_free(cats->cats_idxs);
+ G_free(cats->cats_arr);
cats->n_cats = 0;
cats->n_a_cats = 0;
@@ -95,44 +138,60 @@
return;
}
+#if 0
void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
{
a_cats_ids = cats->cats_ids;
* n_a_cats = cats->n_a_cats;
}
+#endif
-int I_sc_add_cat(struct scCats * cats, int cat_id)
+/*!
+ \brief Add category.
+
+ Category represents group of scatter plots.
+
+ \param cats pointer to scCats struct
+
+ \return assigned category id (starts with 0)
+ \return -1 if maximum nuber of categories was reached
+ */
+int I_sc_add_cat(struct scCats * cats)
{
- int i_scatt;
+ int i_scatt, i_cat_id, cat_id;
int n_a_cats = cats->n_a_cats;
- if(cat_id < 0 || cat_id >= cats->n_cats)
+ if(cats->n_a_cats >= cats->n_cats)
return -1;
- if(cats->cats_idxs[cat_id] >= 0)
- return -1;
+ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
+ if(cats->cats_idxs[i_cat_id] < 0) {
+ cat_id = i_cat_id;
+ break;
+ }
cats->cats_ids[n_a_cats] = cat_id;
cats->cats_idxs[cat_id] = n_a_cats;
- cats->cats_arr[n_a_cats] = (struct scScatts *) malloc(sizeof(struct scScatts));
+ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
- cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) malloc(cats->n_scatts * sizeof(struct scdScattData *));
+ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
cats->cats_arr[n_a_cats]->n_a_scatts = 0;
- cats->cats_arr[n_a_cats]->scatts_bands = (int *) malloc(cats->n_scatts * 2 * sizeof(int));
+ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
- cats->cats_arr[n_a_cats]->scatt_idxs = (int *) malloc(cats->n_scatts * sizeof(int));
+ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++cats->n_a_cats;
- return 0;
+ return cat_id;
}
+#if 0
int I_sc_delete_cat(struct scCats * cats, int cat_id)
{
int cat_idx, i_cat;
@@ -144,10 +203,10 @@
if(cat_idx < 0)
return -1;
- free(cats->cats_arr[cat_idx]->scatt_idxs);
- free(cats->cats_arr[cat_idx]->scatts_bands);
- free(cats->cats_arr[cat_idx]->scatts_arr);
- free(cats->cats_arr[cat_idx]);
+ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
+ G_free(cats->cats_arr[cat_idx]->scatts_bands);
+ G_free(cats->cats_arr[cat_idx]->scatts_arr);
+ G_free(cats->cats_arr[cat_idx]);
for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
{
@@ -160,8 +219,19 @@
return 0;
}
-
+#endif
+/*!
+ \brief Insert scatter plot data .
+ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
+
+ \param cats pointer to scCats struct
+ \param cat_id id number of category.
+ \param scatt_id id number of scatter plot.
+
+ \return 0 on success
+ \return -1 on failure
+ */
int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
{
int band_1, band_2, cat_idx, n_a_scatts;
@@ -191,7 +261,7 @@
scatts->scatt_idxs[scatt_id] = n_a_scatts;
- id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
+ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
scatts->scatts_bands[n_a_scatts * 2] = band_1;
scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
@@ -202,6 +272,7 @@
return 0;
}
+#if 0
int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
{
int cat_idx, scatt_idx, n_init_scatts, i_scatt;
@@ -258,26 +329,26 @@
return 0;
}
+#endif
-void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data,
- struct Range band_1_range, struct Range band_2_range)
+/*!
+ \brief Insert scatter plot data.
+
+ \param scatt_data pointer to existing struct scdScattData
+ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
+ \param n_vals number of data values
+ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
+ */
+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
{
scatt_data->n_vals = n_vals;
- scatt_data->band_1_range.min = band_1_range.min;
- scatt_data->band_1_range.max = band_1_range.max;
- scatt_data->band_1_range.first_time = 0;
-
- scatt_data->band_2_range.min = band_2_range.min;
- scatt_data->band_2_range.max = band_2_range.max;
- scatt_data->band_2_range.first_time = 0;
-
if(type == SC_SCATT_DATA)
{
if(data)
scatt_data->scatt_vals_arr = (unsigned int *) data;
else {
- scatt_data->scatt_vals_arr = (unsigned int *) malloc(n_vals * sizeof(unsigned int));
+ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
}
scatt_data->b_conds_arr = NULL;
@@ -287,7 +358,7 @@
if(data)
scatt_data->b_conds_arr = (unsigned char *) data;
else {
- scatt_data->b_conds_arr = (unsigned char *) malloc(n_vals * sizeof(unsigned char));
+ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
}
scatt_data->scatt_vals_arr = NULL;
@@ -296,17 +367,8 @@
return;
}
-void I_scd_free_scatt_data(struct scdScattData * scatt_data)
-{
- free(scatt_data->b_conds_arr);
- free(scatt_data->scatt_vals_arr);
-
- scatt_data = NULL;
-
- return;
-}
-
+#if 0
void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
{
@@ -315,7 +377,7 @@
return;
}
-
+s
void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
{
if(!scatt_data->b_conds_arr)
@@ -340,3 +402,4 @@
return 0;
}
+#endif
Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff 2013-09-23 15:57:22 UTC (rev 57822)
+++ sandbox/turek/scatter_plot/testing_patch.diff 2013-09-23 16:22:36 UTC (rev 57823)
@@ -1,3 +1,413 @@
+Index: lib/imagery/scatt_sccats.c
+===================================================================
+--- lib/imagery/scatt_sccats.c (revision 0)
++++ lib/imagery/scatt_sccats.c (working copy)
+@@ -0,0 +1,405 @@
++/*!
++ \file lib/imagery/scatt_cat_rast.c
++
++ \brief Imagery library - functions for manipulation with scatter plot structs.
++
++ Copyright (C) 2013 by the GRASS Development Team
++
++ This program is free software under the GNU General Public License
++ (>=v2). Read the file COPYING that comes with GRASS for details.
++
++ \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++/*!
++ \brief Compute band ids from scatter plot id.
++
++ Scatter plot id describes which bands defines the scatter plot.
++
++ Let say we have 3 bands, their ids are 0, 1 and 2.
++ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
++ All scatter plots:
++ scatt_id b_1_id b_2_id
++ 0 0 1
++ 1 0 2
++ 2 1 2
++
++ \param scatt_id scatter plot id
++ \param n_bands number of bands
++ \param [out] b_1_id id of band1
++ \param[out] b_2_id id of band2
++
++ \return 0
++ */
++int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
++
++ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
++
++ return 0;
++}
++
++
++/*!
++ \brief Compute scatter plot id from band ids.
++
++ See also I_id_scatt_to_bands().
++
++ \param n_bands number of bands
++ \param b_1_id id of band1
++ \param b_1_id id of band2
++ \param [out] scatt_id scatter plot id
++
++ \return 0
++ */
++int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
++
++ return 0;
++}
++
++/*!
++ \brief Initialize structure for storing scatter plots data.
++
++ \param cats pointer to scCats struct
++ \param n_bands number of bands
++ \param type SC_SCATT_DATA - stores scatter plots
++ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
++ */
++void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
++{
++ int i_cat;
++
++ cats->type = type;
++
++ cats->n_cats = 100;
++ cats->n_a_cats = 0;
++
++ cats->n_bands = n_bands;
++ cats->n_scatts = (n_bands - 1) * n_bands / 2;
++
++ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
++ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
++
++ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
++ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
++
++ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
++ cats->cats_idxs[i_cat] = -1;
++
++ return;
++}
++
++/*!
++ \brief Free data of struct scCats, the structure itself remains alocated.
++
++ \param cats pointer to existing scCats struct
++ */
++void I_sc_free_cats(struct scCats * cats)
++{
++ int i_cat;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ if(cats->cats_arr[i_cat])
++ {
++ G_free(cats->cats_arr[i_cat]->scatt_idxs);
++ G_free(cats->cats_arr[i_cat]->scatts_bands);
++ G_free(cats->cats_arr[i_cat]->scatts_arr);
++ G_free(cats->cats_arr[i_cat]);
++ }
++ }
++
++ G_free(cats->cats_ids);
++ G_free(cats->cats_idxs);
++ G_free(cats->cats_arr);
++
++ cats->n_cats = 0;
++ cats->n_a_cats = 0;
++ cats->n_bands = 0;
++ cats->n_scatts = 0;
++ cats->type = -1;
++
++ return;
++}
++
++#if 0
++void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
++{
++ a_cats_ids = cats->cats_ids;
++ * n_a_cats = cats->n_a_cats;
++}
++#endif
++
++/*!
++ \brief Add category.
++
++ Category represents group of scatter plots.
++
++ \param cats pointer to scCats struct
++
++ \return assigned category id (starts with 0)
++ \return -1 if maximum nuber of categories was reached
++ */
++int I_sc_add_cat(struct scCats * cats)
++{
++ int i_scatt, i_cat_id, cat_id;
++ int n_a_cats = cats->n_a_cats;
++
++ if(cats->n_a_cats >= cats->n_cats)
++ return -1;
++
++ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
++ if(cats->cats_idxs[i_cat_id] < 0) {
++ cat_id = i_cat_id;
++ break;
++ }
++
++ cats->cats_ids[n_a_cats] = cat_id;
++ cats->cats_idxs[cat_id] = n_a_cats;
++
++ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
++
++ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
++ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
++
++ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
++
++ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
++
++ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
++ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
++ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++
++ ++cats->n_a_cats;
++
++ return cat_id;
++}
++
++#if 0
++int I_sc_delete_cat(struct scCats * cats, int cat_id)
++{
++ int cat_idx, i_cat;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
++ G_free(cats->cats_arr[cat_idx]->scatts_bands);
++ G_free(cats->cats_arr[cat_idx]->scatts_arr);
++ G_free(cats->cats_arr[cat_idx]);
++
++ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
++ {
++ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
++ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
++ }
++ cats->cats_idxs[cat_id] = -1;
++
++ --cats->n_a_cats;
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data .
++ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
++
++ \param cats pointer to scCats struct
++ \param cat_id id number of category.
++ \param scatt_id id number of scatter plot.
++
++ \return 0 on success
++ \return -1 on failure
++ */
++int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int band_1, band_2, cat_idx, n_a_scatts;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] >= 0)
++ return -1;
++
++ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
++ return -1;
++
++ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
++ return -1;
++
++ n_a_scatts = scatts->n_a_scatts;
++
++ scatts->scatt_idxs[scatt_id] = n_a_scatts;
++
++ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
++
++ scatts->scatts_bands[n_a_scatts * 2] = band_1;
++ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
++
++ scatts->scatts_arr[n_a_scatts] = scatt_data;
++ ++scatts->n_a_scatts;
++
++ return 0;
++}
++
++#if 0
++int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 && cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ scatt_data = scatts->scatts_arr[scatt_idx];
++
++ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
++ {
++ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
++ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
++ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
++ }
++ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
++
++ scatts->scatt_idxs[scatt_id] = -1;
++
++ scatt_data = scatts->scatts_arr[scatt_id];
++ scatts->n_a_scatts--;
++
++ return 0;
++}
++
++int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
++{
++ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
++ int cat_idx, scatt_idx, ret;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
++
++ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data.
++
++ \param scatt_data pointer to existing struct scdScattData
++ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
++ \param n_vals number of data values
++ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
++ */
++void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
++{
++ scatt_data->n_vals = n_vals;
++
++ if(type == SC_SCATT_DATA)
++ {
++ if(data)
++ scatt_data->scatt_vals_arr = (unsigned int *) data;
++ else {
++ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
++ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
++ }
++ scatt_data->b_conds_arr = NULL;
++ }
++ else if(type == SC_SCATT_CONDITIONS)
++ {
++ if(data)
++ scatt_data->b_conds_arr = (unsigned char *) data;
++ else {
++ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
++ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
++ }
++ scatt_data->scatt_vals_arr = NULL;
++ }
++
++ return;
++}
++
++
++#if 0
++void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
++{
++
++ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
++ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
++
++ return;
++}
++s
++void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
++{
++ if(!scatt_data->b_conds_arr)
++ return scatt_data->b_conds_arr;
++ else if(!scatt_data->scatt_vals_arr)
++ return scatt_data->scatt_vals_arr;
++
++ return NULL;
++}
++
++int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
++{
++ if(val_idx < 0 && val_idx > scatt_data->n_vals)
++ return -1;
++
++ if(scatt_data->b_conds_arr)
++ scatt_data->b_conds_arr[val_idx] = val;
++ else if(scatt_data->scatt_vals_arr)
++ scatt_data->scatt_vals_arr[val_idx] = val;
++ else
++ return -1;
++
++ return 0;
++}
++#endif
Index: lib/imagery/scatt.c
===================================================================
--- lib/imagery/scatt.c (revision 0)
@@ -2,3 +412,3 @@
+++ lib/imagery/scatt.c (working copy)
-@@ -0,0 +1,871 @@
+@@ -0,0 +1,898 @@
+/*!
@@ -757,6 +1167,7 @@
+ \brief Merge arrays according to opacity.
+ Every pixel in array must be represented by 4 values (RGBA).
+
++ Implementd for speeding up of scatter plots rendering.
+
+ \param merged_arr array which will be overlayd with overlay_arr
+ \param overlay_arr array to be merged_arr overlayed with
@@ -795,12 +1206,24 @@
+ return 0;
+}
+
++/*!
++ \brief Apply colromap to the raster.
++
++ Implementd for speeding up of scatter plots rendering.
+
-+int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned vals_size, unsigned char * colmap, unsigned char * col_vals){
++ \param vals array of values for applying the colormap
++ \param vals_mask maks of vals array
++ \param nvals number of items of vals_mask and vals array
++ \param colmap coulour map to be applied
++ \param[out] col_vals output raster with applied color map (length is 4 * nvals (RGBA))
++
++ \return 0
++*/
++int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned nvals, unsigned char * colmap, unsigned char * col_vals){
+ unsigned int i_val;
+ int v, i, i_cm;
+
-+ for(i_val = 0; i_val < vals_size; i_val++){
++ for(i_val = 0; i_val < nvals; i_val++){
+ i_cm = 4 * i_val;
+
+ v = vals[i_val];
@@ -819,7 +1242,21 @@
+}
+
+
-+int I_rasterize(double * polygon, int pol_n_pts, unsigned char * rast, unsigned char val, struct Cell_head *rast_region){
++/*!
++ \brief Wrapper for using of iclass perimeter rasterization by scatter plot.
++ Warning: calls Rast_set_window
++
++ \param polygon array of polygon coordinates [x, y, x, y...]
++ \param pol_n_pts number of points in the polygon array
++ \param val value to be assigned to cells, which belong to plygon
++ \param rast_region region of raster
++ \param[out] rast raster to be pologyn rasterized in
++
++ \return 0 on success
++ \return 1 on failure
++*/
++
++int I_rasterize(double * polygon, int pol_n_pts, unsigned char val, struct Cell_head *rast_region, unsigned char * rast){
+ int i;
+ int x0, x1, y;
+ int row, row_idx, i_col;
@@ -874,432 +1311,9 @@
+ G_free(perimeter.points);
+ return 0;
+}
-Index: lib/imagery/scatt_sccats.c
-===================================================================
---- lib/imagery/scatt_sccats.c (revision 0)
-+++ lib/imagery/scatt_sccats.c (working copy)
-@@ -0,0 +1,405 @@
-+/*!
-+ \file lib/imagery/scatt_cat_rast.c
-+
-+ \brief Imagery library - functions for manipulation with scatter plot structs.
-+
-+ Copyright (C) 2013 by the GRASS Development Team
-+
-+ This program is free software under the GNU General Public License
-+ (>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
-+ */
-+
-+#include <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/gis.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.h>
-+
-+/*!
-+ \brief Compute band ids from scatter plot id.
-+
-+ Scatter plot id describes which bands defines the scatter plot.
-+
-+ Let say we have 3 bands, their ids are 0, 1 and 2.
-+ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
-+ All scatter plots:
-+ scatt_id b_1_id b_2_id
-+ 0 0 1
-+ 1 0 2
-+ 2 1 2
-+
-+ \param scatt_id scatter plot id
-+ \param n_bands number of bands
-+ \param [out] b_1_id id of band1
-+ \param[out] b_2_id id of band2
-+
-+ \return 0
-+ */
-+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
-+
-+ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
-+
-+ return 0;
-+}
-+
-+
-+/*!
-+ \brief Compute scatter plot id from band ids.
-+
-+ See also I_id_scatt_to_bands().
-+
-+ \param n_bands number of bands
-+ \param b_1_id id of band1
-+ \param b_1_id id of band2
-+ \param [out] scatt_id scatter plot id
-+
-+ \return 0
-+ */
-+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
-+
-+ return 0;
-+}
-+
-+/*!
-+ \brief Initialize structure for storing scatter plots data.
-+
-+ \param cats pointer to scCats struct
-+ \param n_bands number of bands
-+ \param type SC_SCATT_DATA - stores scatter plots
-+ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
-+ */
-+void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
-+{
-+ int i_cat;
-+
-+ cats->type = type;
-+
-+ cats->n_cats = 100;
-+ cats->n_a_cats = 0;
-+
-+ cats->n_bands = n_bands;
-+ cats->n_scatts = (n_bands - 1) * n_bands / 2;
-+
-+ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
-+ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
-+
-+ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
-+ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
-+
-+ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
-+ cats->cats_idxs[i_cat] = -1;
-+
-+ return;
-+}
-+
-+/*!
-+ \brief Free data of struct scCats, the structure itself remains alocated.
-+
-+ \param cats pointer to existing scCats struct
-+ */
-+void I_sc_free_cats(struct scCats * cats)
-+{
-+ int i_cat;
-+
-+ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
-+ {
-+ if(cats->cats_arr[i_cat])
-+ {
-+ G_free(cats->cats_arr[i_cat]->scatt_idxs);
-+ G_free(cats->cats_arr[i_cat]->scatts_bands);
-+ G_free(cats->cats_arr[i_cat]->scatts_arr);
-+ G_free(cats->cats_arr[i_cat]);
-+ }
-+ }
-+
-+ G_free(cats->cats_ids);
-+ G_free(cats->cats_idxs);
-+ G_free(cats->cats_arr);
-+
-+ cats->n_cats = 0;
-+ cats->n_a_cats = 0;
-+ cats->n_bands = 0;
-+ cats->n_scatts = 0;
-+ cats->type = -1;
-+
-+ return;
-+}
-+
-+#if 0
-+void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
-+{
-+ a_cats_ids = cats->cats_ids;
-+ * n_a_cats = cats->n_a_cats;
-+}
-+#endif
-+
-+/*!
-+ \brief Add category.
-+
-+ Category represents group of scatter plots.
-+
-+ \param cats pointer to scCats struct
-+
-+ \return assigned category id (starts with 0)
-+ \return -1 if maximum nuber of categories was reached
-+ */
-+int I_sc_add_cat(struct scCats * cats)
-+{
-+ int i_scatt, i_cat_id, cat_id;
-+ int n_a_cats = cats->n_a_cats;
-+
-+ if(cats->n_a_cats >= cats->n_cats)
-+ return -1;
-+
-+ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
-+ if(cats->cats_idxs[i_cat_id] < 0) {
-+ cat_id = i_cat_id;
-+ break;
-+ }
-+
-+ cats->cats_ids[n_a_cats] = cat_id;
-+ cats->cats_idxs[cat_id] = n_a_cats;
-+
-+ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
-+
-+ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
-+ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
-+
-+ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
-+
-+ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
-+
-+ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
-+ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
-+ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
-+
-+ ++cats->n_a_cats;
-+
-+ return cat_id;
-+}
-+
-+#if 0
-+int I_sc_delete_cat(struct scCats * cats, int cat_id)
-+{
-+ int cat_idx, i_cat;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
-+ G_free(cats->cats_arr[cat_idx]->scatts_bands);
-+ G_free(cats->cats_arr[cat_idx]->scatts_arr);
-+ G_free(cats->cats_arr[cat_idx]);
-+
-+ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
-+ {
-+ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
-+ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
-+ }
-+ cats->cats_idxs[cat_id] = -1;
-+
-+ --cats->n_a_cats;
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data .
-+ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
-+
-+ \param cats pointer to scCats struct
-+ \param cat_id id number of category.
-+ \param scatt_id id number of scatter plot.
-+
-+ \return 0 on success
-+ \return -1 on failure
-+ */
-+int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int band_1, band_2, cat_idx, n_a_scatts;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] >= 0)
-+ return -1;
-+
-+ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
-+ return -1;
-+
-+ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
-+ return -1;
-+
-+ n_a_scatts = scatts->n_a_scatts;
-+
-+ scatts->scatt_idxs[scatt_id] = n_a_scatts;
-+
-+ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
-+
-+ scatts->scatts_bands[n_a_scatts * 2] = band_1;
-+ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
-+
-+ scatts->scatts_arr[n_a_scatts] = scatt_data;
-+ ++scatts->n_a_scatts;
-+
-+ return 0;
-+}
-+
-+#if 0
-+int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 && cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_idx];
-+
-+ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
-+ {
-+ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
-+ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
-+ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
-+ }
-+ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
-+
-+ scatts->scatt_idxs[scatt_id] = -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_id];
-+ scatts->n_a_scatts--;
-+
-+ return 0;
-+}
-+
-+int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
-+{
-+ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
-+ int cat_idx, scatt_idx, ret;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
-+
-+ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data.
-+
-+ \param scatt_data pointer to existing struct scdScattData
-+ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
-+ \param n_vals number of data values
-+ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
-+ */
-+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
-+{
-+ scatt_data->n_vals = n_vals;
-+
-+ if(type == SC_SCATT_DATA)
-+ {
-+ if(data)
-+ scatt_data->scatt_vals_arr = (unsigned int *) data;
-+ else {
-+ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
-+ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
-+ }
-+ scatt_data->b_conds_arr = NULL;
-+ }
-+ else if(type == SC_SCATT_CONDITIONS)
-+ {
-+ if(data)
-+ scatt_data->b_conds_arr = (unsigned char *) data;
-+ else {
-+ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
-+ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
-+ }
-+ scatt_data->scatt_vals_arr = NULL;
-+ }
-+
-+ return;
-+}
-+
-+
-+#if 0
-+void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
-+{
-+
-+ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
-+ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
-+
-+ return;
-+}
-+s
-+void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
-+{
-+ if(!scatt_data->b_conds_arr)
-+ return scatt_data->b_conds_arr;
-+ else if(!scatt_data->scatt_vals_arr)
-+ return scatt_data->scatt_vals_arr;
-+
-+ return NULL;
-+}
-+
-+int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
-+{
-+ if(val_idx < 0 && val_idx > scatt_data->n_vals)
-+ return -1;
-+
-+ if(scatt_data->b_conds_arr)
-+ scatt_data->b_conds_arr[val_idx] = val;
-+ else if(scatt_data->scatt_vals_arr)
-+ scatt_data->scatt_vals_arr[val_idx] = val;
-+ else
-+ return -1;
-+
-+ return 0;
-+}
-+#endif
-Index: include/defs/vedit.h
-===================================================================
---- include/defs/vedit.h (revision 57760)
-+++ include/defs/vedit.h (working copy)
-@@ -33,6 +33,8 @@
- int Vedit_merge_lines(struct Map_info *, struct ilist *);
-
- /* move.c */
-+int Vedit_move_areas(struct Map_info *, struct Map_info **, int,
-+ struct ilist *, double, double, double, int, double);
- int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
- struct ilist *, double, double, double, int, double);
-
Index: include/defs/imagery.h
===================================================================
---- include/defs/imagery.h (revision 57760)
+--- include/defs/imagery.h (revision 57820)
+++ include/defs/imagery.h (working copy)
@@ -111,6 +111,28 @@
FILE *I_fopen_subgroup_ref_new(const char *, const char *);
@@ -1325,14 +1339,14 @@
+
+int I_merge_arrays(unsigned char *, unsigned char *, unsigned, unsigned, double);
+int I_apply_colormap(unsigned char *, unsigned char *, unsigned, unsigned char *, unsigned char *);
-+int I_rasterize(double *, int, unsigned char *, unsigned char, struct Cell_head *);
++int I_rasterize(double *, int, unsigned char, struct Cell_head *, unsigned char *);
+
/* sig.c */
int I_init_signatures(struct Signature *, int);
int I_new_signature(struct Signature *);
Index: include/imagery.h
===================================================================
---- include/imagery.h (revision 57760)
+--- include/imagery.h (revision 57820)
+++ include/imagery.h (working copy)
@@ -135,6 +135,56 @@
@@ -1391,560 +1405,65 @@
#define SIGNATURE_TYPE_MIXED 1
#define GROUPFILE "CURGROUP"
-Index: gui/wxpython/vdigit/wxdigit.py
+Index: gui/wxpython/mapdisp/frame.py
===================================================================
---- gui/wxpython/vdigit/wxdigit.py (revision 57760)
-+++ gui/wxpython/vdigit/wxdigit.py (working copy)
-@@ -17,7 +17,7 @@
- (and NumPy would be an excellent candidate for acceleration via
- e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+--- gui/wxpython/mapdisp/frame.py (revision 57820)
++++ gui/wxpython/mapdisp/frame.py (working copy)
+@@ -231,6 +231,7 @@
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
++ self.dialogs['scatt_plot'] = None
+ self.dialogs['category'] = None
+ self.dialogs['vnet'] = None
+ self.dialogs['query'] = None
+@@ -1199,6 +1200,19 @@
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['map']
--(C) 2007-2011 by the GRASS Development Team
-+(C) 2007-2011, 2013 by the GRASS Development Team
-
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
-@@ -27,6 +27,8 @@
-
- import grass.script.core as grass
-
-+from grass.pydispatch.signal import Signal
-+
- from core.gcmd import GError
- from core.debug import Debug
- from core.settings import UserSettings
-@@ -176,7 +178,21 @@
-
- if self.poMapInfo:
- self.InitCats()
--
-+
-+ self.emit_signals = False
-+
-+ # signals which describes features changes during digitization,
-+ # activate them using EmitSignals method
-+ #TODO handle errors?
-+ self.featureAdded = Signal('IVDigit.featureAdded')
-+ self.areasDeleted = Signal('IVDigit.areasDeleted')
-+ self.vertexMoved = Signal('IVDigit.vertexMoved')
-+ self.vertexAdded = Signal('IVDigit.vertexAdded')
-+ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
-+ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
-+ self.featuresMoved = Signal('IVDigit.featuresMoved')
-+ self.lineEdited = Signal('IVDigit.lineEdited')
-+
- def __del__(self):
- Debug.msg(1, "IVDigit.__del__()")
- Vect_destroy_line_struct(self.poPoints)
-@@ -188,7 +204,12 @@
- Vect_close(self.poBgMapInfo)
- self.poBgMapInfo = self.popoBgMapInfo = None
- del self.bgMapInfo
--
-+
-+ def EmitSignals(self, emit):
-+ """!Activate/deactivate signals which describes features changes during digitization.
++ def OnIScatt(self, event):
++ """!Init interactive scatterplot tools
+ """
-+ self.emit_signals = emit
++ if self.dialogs['scatt_plot']:
++ self.dialogs['scatt_plot'].Raise()
++ return
+
- def CloseBackgroundMap(self):
- """!Close background vector map"""
- if not self.poBgMapInfo:
-@@ -394,7 +415,6 @@
-
- @return tuple (number of added features, feature ids)
- """
--
- layer = self._getNewFeaturesLayer()
- cat = self._getNewFeaturesCat()
-
-@@ -419,10 +439,15 @@
- return (-1, None)
-
- self.toolbar.EnableUndo()
--
-- return self._addFeature(vtype, points, layer, cat,
-- self._getSnapMode(), self._display.GetThreshold())
--
-+
-+ ret = self._addFeature(vtype, points, layer, cat,
-+ self._getSnapMode(), self._display.GetThreshold())
-+ if ret[0] > -1 and self.emit_signals:
-+ self.featureAdded.emit(new_bboxs=[self._createBbox(points)],
-+ new_areas_cats=[[{layer : [cat]}, None]])
-+
-+ return ret
-+
- def DeleteSelectedLines(self):
- """!Delete selected features
-
-@@ -434,16 +459,27 @@
- # collect categories for deleting if requested
- deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
- catDict = dict()
-+
-+ old_bboxs = []
-+ old_areas_cats = []
- if deleteRec:
- for i in self._display.selected['ids']:
-+
- if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
- self._error.ReadLine(i)
-
-- cats = self.poCats.contents
-- for j in range(cats.n_cats):
-- if cats.field[j] not in catDict.keys():
-- catDict[cats.field[j]] = list()
-- catDict[cats.field[j]].append(cats.cat[j])
-+ if self.emit_signals:
-+ ret = self._getLineAreaBboxCats(i)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ # catDict was not used -> put into comment
-+ #cats = self.poCats.contents
-+ #for j in range(cats.n_cats):
-+ # if cats.field[j] not in catDict.keys():
-+ # catDict[cats.field[j]] = list()
-+ # catDict[cats.field[j]].append(cats.cat[j])
-
- poList = self._display.GetSelectedIList()
- nlines = Vedit_delete_lines(self.poMapInfo, poList)
-@@ -456,7 +492,11 @@
- self._deleteRecords(catDict)
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresDeleted.emit(old_bboxs=old_bboxs,
-+ old_areas_cats=old_areas_cats)
-+
- return nlines
-
- def _deleteRecords(self, cats):
-@@ -512,22 +552,173 @@
-
- @return number of deleted
- """
-+ if len(self._display.selected['ids']) < 1:
-+ return 0
++ from scatt_plot.frame import IScattDialog
++ self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
+
- poList = self._display.GetSelectedIList()
- cList = poList.contents
-
- nareas = 0
-+ old_bboxs = []
-+ old_areas_cats = []
++ self.dialogs['scatt_plot'].CenterOnScreen()
++ self.dialogs['scatt_plot'].Show()
+
- for i in range(cList.n_values):
-+
- if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
- continue
--
-+
-+ if self.emit_signals:
-+ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
-+ if area > 0:
-+ bbox, cats = self._getaAreaBboxCats(area)
-+ old_bboxs += bbox
-+ old_areas_cats += cats
-+
- nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
-
- if nareas > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
-+ if self.emit_signals:
-+ self.areasDeleted.emit(old_bboxs=old_bboxs,
-+ old_areas_cats=old_areas_cats)
-+
-+ return nareas
-+
-+ def _getLineAreaBboxCats(self, ln_id):
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+
-+ if ltype == GV_CENTROID:
-+ #TODO centroid opttimization, can be edited also its area -> it will appear two times in new_ lists
-+ return self._getCentroidAreaBboxCats(ln_id)
-+ else:
-+ return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
-+
-+
-+ def _getCentroidAreaBboxCats(self, centroid):
-+ if not Vect_line_alive(self.poMapInfo, centroid):
-+ return None
-+
-+ area = Vect_get_centroid_area(self.poMapInfo, centroid)
-+ if area > 0:
-+ return self._getaAreaBboxCats(area)
-+ else:
-+ return None
-+
-+ def _getaAreaBboxCats(self, area):
-+ po_b_list = Vect_new_list()
-+ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
-+ b_list = po_b_list.contents
-+
-+ geoms = []
-+ areas_cats = []
-+
-+ if b_list.n_values > 0:
-+ for i_line in range(b_list.n_values):
-+
-+ line = b_list.value[i_line];
-+
-+ geoms.append(self._getBbox(abs(line)))
-+ areas_cats.append(self._getLineAreasCategories(abs(line)))
-
-- return nareas
-+ Vect_destroy_list(po_b_list);
-+
-+ return geoms, areas_cats
-+
-+ def _getLineAreasCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return []
-+
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+ if ltype != GV_BOUNDARY:
-+ return []
-+
-+ cats = [None, None]
-+
-+ left = c_int()
-+ right = c_int()
-+
-+ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
-+ areas = [left.value, right.value]
-+
-+ for i, a in enumerate(areas):
-+ if a > 0:
-+ centroid = Vect_get_area_centroid(self.poMapInfo, a)
-+ if centroid <= 0:
-+ continue
-+ c = self._getCategories(centroid)
-+ if c:
-+ cats[i] = c
-+
-+ return cats
-+
-+ def _getCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return none
-+
-+ poCats = Vect_new_cats_struct()
-+ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
-+ Vect_destroy_cats_struct(poCats)
-+ return None
-+
-+ cCats = poCats.contents
-+
-+ cats = {}
-+ for j in range(cCats.n_cats):
-+ if cats.has_key(cCats.field[j]):
-+ cats[cCats.field[j]].append(cCats.cat[j])
-+ else:
-+ cats[cCats.field[j]] = [cCats.cat[j]]
-
-+ Vect_destroy_cats_struct(poCats)
-+ return cats
-+
-+ def _getBbox(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return None
-+
-+ poPoints = Vect_new_line_struct()
-+ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
-+ Vect_destroy_line_struct(poPoints)
-+ return []
-+
-+ geom = self._convertGeom(poPoints)
-+ bbox = self._createBbox(geom)
-+ Vect_destroy_line_struct(poPoints)
-+ return bbox
-+
-+ def _createBbox(self, points):
-+
-+ bbox = {}
-+ for pt in points:
-+ if not bbox.has_key('maxy'):
-+ bbox['maxy'] = pt[1]
-+ bbox['miny'] = pt[1]
-+ bbox['maxx'] = pt[0]
-+ bbox['minx'] = pt[0]
-+ continue
-+
-+ if bbox['maxy'] < pt[1]:
-+ bbox['maxy'] = pt[1]
-+ elif bbox['miny'] > pt[1]:
-+ bbox['miny'] = pt[1]
-+
-+ if bbox['maxx'] < pt[0]:
-+ bbox['maxx'] = pt[0]
-+ elif bbox['minx'] > pt[0]:
-+ bbox['minx'] = pt[0]
-+ return bbox
-+
-+ def _convertGeom(self, poPoints):
-+
-+ Points = poPoints.contents
-+
-+ pts_geom = []
-+ for j in range(Points.n_points):
-+ pts_geom.append((Points.x[j], Points.y[j]))
-+
-+ return pts_geom
-+
- def MoveSelectedLines(self, move):
- """!Move selected features
-
-@@ -536,16 +727,45 @@
- if not self._checkMap():
- return -1
-
-+ nsel = len(self._display.selected['ids'])
-+ if nsel < 1:
-+ return -1
-+
- thresh = self._display.GetThreshold()
- snap = self._getSnapMode()
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ old_bboxs = []
-+ old_areas_cats = []
-+ for sel_id in self._display.selected['ids']:
-+ ret = self._getLineAreaBboxCats(sel_id)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList,
- move[0], move[1], 0,
- snap, thresh)
-+
- Vect_destroy_list(poList)
--
-+
-+ if nlines > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ ret = self._getLineAreaBboxCats(new_id)
-+ if ret:
-+ new_bboxs += ret[0]
-+ new_areas_cats += ret[1]
-+
- if nlines > 0 and self._settings['breakLines']:
- for i in range(1, nlines):
- self._breakLineAtIntersection(nlines + i, None, changeset)
-@@ -553,7 +773,13 @@
- if nlines > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresMoved.emit(new_bboxs=new_bboxs,
-+ old_bboxs=old_bboxs,
-+ old_areas_cats=old_areas_cats,
-+ new_areas_cats=new_areas_cats)
-+
- return nlines
-
- def MoveSelectedVertex(self, point, move):
-@@ -571,12 +797,21 @@
-
- if len(self._display.selected['ids']) != 1:
- return -1
--
-+
-+ # move only first found vertex in bbox
-+ poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, point[0], point[1], 0.0)
--
-- # move only first found vertex in bbox
-- poList = self._display.GetSelectedIList()
-+
- moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList, self.poPoints,
- self._display.GetThreshold(type = 'selectThresh'),
-@@ -584,7 +819,17 @@
- move[0], move[1], 0.0,
- 1, self._getSnapMode())
- Vect_destroy_list(poList)
--
-+
-+ if moved > 0 and self.emit_signals:
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+
-+ new_bboxs = []
-+ new_areas_cats = []
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_bboxs.append(self._getBbox(new_id))
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+
- if moved > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
-@@ -592,7 +837,13 @@
- if moved > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.vertexMoved.emit(new_bboxs=new_bboxs,
-+ new_areas_cats=new_areas_cats,
-+ old_areas_cats=old_areas_cats,
-+ old_bboxs=old_bboxs)
-+
- return moved
-
- def AddVertex(self, coords):
-@@ -681,6 +932,10 @@
- self._error.ReadLine(line)
- return -1
-
-+ if self.emit_signals:
-+ old_bboxs = [self._getBbox(line)]
-+ old_areas_cats = [self._getLineAreasCategories(line)]
-+
- # build feature geometry
- Vect_reset_line(self.poPoints)
- for p in coords:
-@@ -696,6 +951,9 @@
-
- newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
- self.poPoints, self.poCats)
-+ if newline > 0 and self.emit_signals:
-+ new_geom = [self._getBbox(newline)]
-+ new_areas_cats = [self._getLineAreasCategories(newline)]
-
- if newline > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(newline, None)
-@@ -703,7 +961,13 @@
- if newline > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.lineEdited.emit(old_bboxs=old_bboxs,
-+ old_areas_cats=old_areas_cats,
-+ new_bboxs=new_bboxs,
-+ new_areas_cats=new_areas_cats)
-+
- return newline
-
- def FlipLine(self):
-@@ -1514,6 +1778,16 @@
- return 0
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
-
-@@ -1525,15 +1799,35 @@
- else:
- ret = Vedit_remove_vertex(self.poMapInfo, poList,
- self.poPoints, thresh)
-+
- Vect_destroy_list(poList)
-+
-+ if ret > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+ new_bboxs.append(self._getBbox(new_id))
-
- if not add and ret > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
--
-+
- if ret > 0:
- self._addChangeset()
--
-+
-+ if ret > 0 and self.emit_signals:
-+ if add:
-+ self.vertexAdded.emit(old_bboxs=old_bboxs, new_bboxs=new_bboxs)
-+ else:
-+ self.vertexRemoved.emit(old_bboxs=old_bboxs,
-+ new_bboxs=new_bboxs,
-+ old_areas_cats=old_areas_cats,
-+ new_areas_cats=new_areas_cats)
-+
- return 1
-
- def GetLineCats(self, line):
-Index: gui/wxpython/vdigit/toolbars.py
+ def OnVNet(self, event):
+ """!Dialog for v.net* modules
+ """
+Index: gui/wxpython/mapdisp/toolbars.py
===================================================================
---- gui/wxpython/vdigit/toolbars.py (revision 57760)
-+++ gui/wxpython/vdigit/toolbars.py (working copy)
-@@ -17,6 +17,7 @@
- import wx
+--- gui/wxpython/mapdisp/toolbars.py (revision 57820)
++++ gui/wxpython/mapdisp/toolbars.py (working copy)
+@@ -49,6 +49,8 @@
+ label = _('Create histogram of raster map')),
+ 'vnet' : MetaIcon(img = 'vector-tools',
+ label = _('Vector network analysis tool')),
++ 'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
++ label = _("Interactive scatter plot tool")),
+ }
- from grass.script import core as grass
-+from grass.pydispatch.signal import Signal
-
- from gui_core.toolbars import BaseToolbar, BaseIcons
- from gui_core.dialogs import CreateNewVector
-@@ -42,6 +43,8 @@
- self.digit = None
- self._giface = giface
+ NvizIcons = {
+@@ -239,7 +241,9 @@
+ (MapIcons["scatter"], self.parent.OnScatterplot),
+ (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
+ (BaseIcons["histogramD"], self.parent.OnHistogram),
+- (MapIcons["vnet"], self.parent.OnVNet)))
++ (MapIcons["vnet"], self.parent.OnVNet),
++ (MapIcons["interact_scatter"],
++ self.parent.OnIScatt)))
-+ self.editingStarted = Signal("VDigitToolbar.editingStarted")
-+
- # currently selected map layer for editing (reference to MapLayer instance)
- self.mapLayer = None
- # list of vector layers from Layer Manager (only in the current mapset)
-@@ -860,6 +863,7 @@
- alpha = int(opacity * 255)
- self.digit.GetDisplay().UpdateSettings(alpha = alpha)
-
-+ self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
- return True
-
- def StopEditing(self):
+ def OnDecoration(self, event):
+ """!Decorations overlay menu
Index: gui/wxpython/iclass/plots.py
===================================================================
---- gui/wxpython/iclass/plots.py (revision 57760)
+--- gui/wxpython/iclass/plots.py (revision 57820)
+++ gui/wxpython/iclass/plots.py (working copy)
@@ -19,6 +19,7 @@
import wx.lib.plot as plot
@@ -2079,62 +1598,1043 @@
def DrawCoincidencePlots(self):
"""!Draw coincidence plots"""
for bandIdx in range(len(self.bandList)):
-Index: gui/wxpython/mapdisp/toolbars.py
+Index: gui/wxpython/docs/wxGUI_iscatt.png
===================================================================
---- gui/wxpython/mapdisp/toolbars.py (revision 57760)
-+++ gui/wxpython/mapdisp/toolbars.py (working copy)
-@@ -49,6 +49,8 @@
- label = _('Create histogram of raster map')),
- 'vnet' : MetaIcon(img = 'vector-tools',
- label = _('Vector network analysis tool')),
-+ 'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
-+ label = _("Interactive scatter plot tool")),
- }
-
- NvizIcons = {
-@@ -239,7 +241,9 @@
- (MapIcons["scatter"], self.parent.OnScatterplot),
- (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
- (BaseIcons["histogramD"], self.parent.OnHistogram),
-- (MapIcons["vnet"], self.parent.OnVNet)))
-+ (MapIcons["vnet"], self.parent.OnVNet),
-+ (MapIcons["interact_scatter"],
-+ self.parent.OnIScatt)))
-
- def OnDecoration(self, event):
- """!Decorations overlay menu
-Index: gui/wxpython/mapdisp/frame.py
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: gui/wxpython/docs/wxGUI_iscatt.png
===================================================================
---- gui/wxpython/mapdisp/frame.py (revision 57760)
-+++ gui/wxpython/mapdisp/frame.py (working copy)
-@@ -231,6 +231,7 @@
- #
- self.dialogs = {}
- self.dialogs['attributes'] = None
-+ self.dialogs['scatt_plot'] = None
- self.dialogs['category'] = None
- self.dialogs['vnet'] = None
- self.dialogs['query'] = None
-@@ -1199,6 +1200,19 @@
- """!Returns toolbar with zooming tools"""
- return self.toolbars['map']
-
-+ def OnIScatt(self, event):
-+ """!Init interactive scatterplot tools
-+ """
-+ if self.dialogs['scatt_plot']:
-+ self.dialogs['scatt_plot'].Raise()
+--- gui/wxpython/docs/wxGUI_iscatt.png (revision 57820)
++++ gui/wxpython/docs/wxGUI_iscatt.png (working copy)
+
+Property changes on: gui/wxpython/docs/wxGUI_iscatt.png
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++application/octet-stream
+\ No newline at end of property
+Index: gui/wxpython/docs/wxGUI.iscatt.html
+===================================================================
+--- gui/wxpython/docs/wxGUI.iscatt.html (revision 0)
++++ gui/wxpython/docs/wxGUI.iscatt.html (working copy)
+@@ -0,0 +1,85 @@
++<!-- meta page description: wxGUI Interactive Scatter Plot Tool-->
++<!-- meta page index: wxGUI -->
++
++<h2>DESCRIPTION</h2>
++
++<em>Interactive Scatter Plot Tool</em> allows to analyze group of raster maps.
++ The tool is integrated into <em><a href="wxGUI.iclass.html">
++ Supervised Classification Tool (wx.iclass) </a></em> (see the screen shot bellow).
++ Also it is possible to launch it from Map Display Window
++ (Analyze map → Interactive Scatter Plot Tool).
++
++The main idea of the tool is that everything is linked together
++(scatter plots together and mapwindow with the scatter plots).
++
++The main feature of the tool is the interactivity, which allows user to:
++<ul>
++ <li> work with multiple plots, which represents multiple raster bands combinations.
++ </li>
++ <li> highlight plotted points in open scatter plots according to currently
++ chosen pixels for classes by it's training areas (supported only in wx.iclass)
++ </li>
++ <li> be able to define areas in plots and the tool will highlight pixels in
++ map display window and corresponding points in other plots
++ </li>
++ <li> plot of confidence ellipses
++ </li>
++</ul>
++
++<h2>TOOL CONTROLS LAYOUT</h2>
++
++<center>
++<br>
++<img src="wxGUI_iscatt.png" border="1" alt="Interactive Scatter Plot Tool">
++<br><br>
++</center>
++
++If editing mode is activated (the green polygon tool in toolbar), the areas which were selected
++in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6 B_7 inside the ellipse.
++Opacity and color of the selected area can be set in settings. The area corresponds to the active class (in this case clouds).
++Selected areas are subset of areas, which belongs to the category.
++<p>
++In the editing mode it is possible to select certain area by the polygon, which can be created and edited by tools in editing toolbar.
++After the area is selected, we can include or exclude it into active category by clicking on the first (plus) respectively
++second (minus) tool in the editing toolbar. In mapwindow corresponding pixels are shown by the class raster representing
++selected areas in scatter plots. In this case we can see clouds class raster (blue), forest class raster (green) and
++water class raster (red).
++
++<h2>NOTES</h2>
++
++The tool can analyze only integer (CELL) rasters.
++
++It works with 8 bit range smoothly.
++The tool is capable of processing data up to 12 bit range, however it is not recommended open many scatter plots in 12/11 bit mode.
++It could require significant amount of memory and plot rendering time can be longer.
++Analyzing of raster data with higher range is not recommended.
++The raster range can be lowered using <em><a href="r.rescale.html">r.rescale</a></em>.
++
++<p>
++
++<h2>KNOWN ISSUES</h2>
++
++Selection of areas in mapwindow is currently supported only if the tool was launched from
++<em><a href="wxGUI.iclass.html">Supervised Classification Tool (wx.iclass) </a></em>
++
++<h2>SEE ALSO</h2>
++
++<em>
++ <a href="wxGUI.html">wxGUI</a><br>
++ <a href="wxGUI.components.html">wxGUI components</a>
++ <a href="r.rescale.html">r.rescale</a><br>
++</em>
++
++<p>
++See also the
++user <a href="http://grasswiki.osgeo.org/wiki/WxGUI_Interactive_Scatter_Plot_Tool">wiki</a>
++page.
++
++<h2>AUTHOR</h2>
++
++Stepan
++Turek, <a href="http://grasswiki.osgeo.org/wiki/GRASS_GSoC_2013_GRASS_GIS_Interactive_Scatter_Plot_Tool">Google
++Summer of Code 2013</a> (mentor: Martin Landa)
++
++<p>
++<i>$Date: 2013-07-04 09:55:17 +0200 (Thu, 04 Jul 2013) $</i>
+Index: gui/wxpython/scatt_plot/core_c.py
+===================================================================
+--- gui/wxpython/scatt_plot/core_c.py (revision 0)
++++ gui/wxpython/scatt_plot/core_c.py (working copy)
+@@ -0,0 +1,228 @@
++"""!
++ at package scatt_plot.scatt_plot
++
++ at brief Wrappers for scatter plot C backend.
++
++(C) 2013 by the GRASS Development Team
++
++This program is free software under the GNU General Public License
++(>=v2). Read the file COPYING that comes with GRASS for details.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++
++import sys
++import numpy as np
++from multiprocessing import Process, Queue
++
++from ctypes import *
++try:
++ from grass.lib.imagery import *
++ from grass.lib.gis import Cell_head, G_get_window
++except ImportError, e:
++ sys.stderr.write(_("Loading ctypes libs failed"))
++
++from core.gcmd import GException
++
++def Rasterize(polygon, rast, region, value):
++
++ pol_size = len(polygon) * 2
++ pol = np.array(polygon, dtype=float)
++
++ c_uint8_p = POINTER(c_uint8)
++ c_double_p = POINTER(c_double)
++
++ rows, cols = rast.shape
++
++ #TODO creating of region is on many places
++ region['rows'] = rows
++ region['cols'] = cols
++
++ region['nsres'] = 1.0
++ region['ewres'] = 1.0
++
++ pol_p = pol.ctypes.data_as(c_double_p)
++ rast_p = rast.ctypes.data_as(c_uint8_p)
++
++ cell_h = _regionToCellHead(region)
++ I_rasterize(pol_p,
++ len(polygon),
++ value,
++ pointer(cell_h), rast_p)
++
++def ApplyColormap(vals, vals_mask, colmap, out_vals):
++
++ c_uint8_p = POINTER(c_uint8)
++
++ vals_p = vals.ctypes.data_as(c_uint8_p)
++
++
++ if hasattr(vals_mask, "ctypes"):
++ vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
++ else: #vals mask is empty (all data are selected)
++ vals_mask_p = None
++ colmap_p = colmap.ctypes.data_as(c_uint8_p)
++ out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
++
++ vals_size = vals.reshape((-1)).shape[0]
++ I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
++
++def MergeArrays(merged_arr, overlay_arr, alpha):
++ if merged_arr.shape != overlay_arr.shape:
++ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
++
++ c_uint8_p = POINTER(c_uint8)
++ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
++ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
++
++ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
++
++def MergeArrays(merged_arr, overlay_arr, alpha):
++ if merged_arr.shape != overlay_arr.shape:
++ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
++
++ c_uint8_p = POINTER(c_uint8)
++ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
++ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
++
++ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
++
++def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
++
++ q = Queue()
++ p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
++ n_bands, scatts, cats_rasts_conds, cats_rasts, q))
++ p.start()
++ ret = q.get()
++ p.join()
++
++ return ret[0], ret[1]
++
++def UpdateCatRast(patch_rast, region, cat_rast):
++ q = Queue()
++ p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
++ p.start()
++ ret = q.get()
++ p.join()
++
++ return ret
++
++def CreateCatRast(region, cat_rast):
++ cell_head = _regionToCellHead(region)
++ I_create_cat_rast(pointer(cell_head), cat_rast)
++
++def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
++
++ #TODO names for types not 0 and 1?
++ sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
++ scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
++
++ char_bands = _stringListToCharArr(bands)
++
++ cell_head = _regionToCellHead(region)
++
++ ret = I_compute_scatts(pointer(cell_head),
++ pointer(scatt_conds_c),
++ pointer(cats_rasts_conds_c),
++ pointer(char_bands),
++ n_bands,
++ pointer(sccats_c),
++ pointer(cats_rasts_c))
++
++ I_sc_free_cats(pointer(sccats_c))
++ I_sc_free_cats(pointer(scatt_conds_c))
++
++ output_queue.put((ret, scatts))
++
++def _getBandcRange( band_info):
++ band_c_range = struct_Range()
++
++ band_c_range.max = band_info['max']
++ band_c_range.min = band_info['min']
++
++ return band_c_range
++
++def _regionToCellHead(region):
++ cell_head = struct_Cell_head()
++ G_get_window(pointer(cell_head))
++
++ convert_dict = {'n' : 'north', 'e' : 'east',
++ 'w' : 'west', 's' : 'south',
++ 'nsres' : 'ns_res',
++ 'ewres' : 'ew_res'}
++
++ for k, v in region.iteritems():
++ if k in ["rows", "cols", "cells"]:
++ v = int(v)
++ else:
++ v = float(v)
++
++ if convert_dict.has_key(k):
++ k = convert_dict[k]
++
++ setattr(cell_head, k, v)
++
++ return cell_head
++
++def _stringListToCharArr(str_list):
++
++ arr = c_char_p * len(str_list)
++ char_arr = arr()
++ for i, st in enumerate(str_list):
++ if st:
++ char_arr[i] = st
++ else:
++ char_arr[i] = None
++
++ return char_arr
++
++def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
++
++ sccats = struct_scCats()
++ I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
++
++ refs = []
++ cats_rasts_core = []
++
++ for cat_id, scatt_ids in cats.iteritems():
++ cat_c_id = I_sc_add_cat(pointer(sccats))
++ cats_rasts_core.append(cats_rasts[cat_id])
++
++ for scatt_id, dt in scatt_ids.iteritems():
++ # if key is missing condition is always True (full scatter plor is computed)
++ vals = dt['np_vals']
++
++ scatt_vals = scdScattData()
++
++ c_void_p = ctypes.POINTER(ctypes.c_void_p)
++
++ if cats_type == SC_SCATT_DATA:
++ vals[:] = 0
++ elif cats_type == SC_SCATT_CONDITIONS:
++ pass
++ else:
++ return None
++ data_p = vals.ctypes.data_as(c_void_p)
++ I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
++
++ refs.append(scatt_vals)
++
++ I_sc_insert_scatt_data(pointer(sccats),
++ pointer(scatt_vals),
++ cat_c_id, scatt_id)
++
++ cats_rasts_c = _stringListToCharArr(cats_rasts_core)
++
++ return sccats, cats_rasts_c, refs
++
++def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
++ cell_head = _regionToCellHead(region)
++
++
++ ret = I_insert_patch_to_cat_rast(patch_rast,
++ pointer(cell_head),
++ cat_rast)
++
++ output_queue.put(ret)
++
++
+Index: gui/wxpython/scatt_plot/frame.py
+===================================================================
+--- gui/wxpython/scatt_plot/frame.py (revision 0)
++++ gui/wxpython/scatt_plot/frame.py (working copy)
+@@ -0,0 +1,694 @@
++"""!
++ at package scatt_plot.dialogs
++
++ at brief Main scatter plot widgets.
++
++Classes:
++ - frame::IClassIScattPanel
++ - frame::IScattDialog
++ - frame::MapDispIScattPanel
++ - frame::ScatterPlotsPanel
++ - frame::CategoryListCtrl
++
++(C) 2013 by the GRASS Development Team
++
++This program is free software under the GNU General Public License
++(>=v2). Read the file COPYING that comes with GRASS for details.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++import wx
++import wx.lib.scrolledpanel as scrolled
++import wx.lib.mixins.listctrl as listmix
++
++from core import globalvar
++from core.gcmd import GException, GError, RunCommand
++
++from gui_core.gselect import Select
++from gui_core.dialogs import SetOpacityDialog
++from scatt_plot.controllers import ScattsManager
++from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
++from scatt_plot.scatt_core import idScattToidBands
++from scatt_plot.dialogs import ManageBusyCursorMixin, RenameClassDialog
++from scatt_plot.plots import ScatterPlotWidget
++from iclass.dialogs import ContrastColor
++
++try:
++ from agw import aui
++except ImportError:
++ import wx.lib.agw.aui as aui
++
++class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
++ def __init__(self, parent, giface, iclass_mapwin = None,
++ id = wx.ID_ANY):
++
++ #wx.SplitterWindow.__init__(self, parent = parent, id = id,
++ # style = wx.SP_LIVE_UPDATE)
++ wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
++ ManageBusyCursorMixin.__init__(self, window=self)
++
++ self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface,
++ iclass_mapwin=iclass_mapwin)
++
++ # toobars
++ self.toolbars = {}
++ self.toolbars['mainToolbar'] = self._createMainToolbar()
++ self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
++
++ self._createCategoryPanel(self)
++
++ self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
++
++ self.mainsizer = wx.BoxSizer(wx.VERTICAL)
++ self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
++ self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
++ self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
++ self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
++
++ self.catsPanel.Hide()
++ self.toolbars['editingToolbar'].Hide()
++
++ self.SetSizer(self.mainsizer)
++
++ self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
++ self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
++ self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
++
++ #self.SetSashGravity(0.5)
++ #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
++ self.Layout()
++
++ def UpdateCur(self, busy):
++ self.plot_panel.SetBusy(busy)
++ ManageBusyCursorMixin.UpdateCur(self, busy)
++
++ def _selCatInIScatt(self):
++ return False
++
++ def _createMainToolbar(self):
++ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
++
++ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
++ return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
++
++
++ def NewScatterPlot(self, scatt_id, transpose):
++ return self.plot_panel.NewScatterPlot(scatt_id, transpose)
++
++ def ShowPlotEditingToolbar(self, show):
++ self.toolbars["editingToolbar"].Show(show)
++ self.Layout()
++
++ def ShowCategoryPanel(self, show):
++ self.catsPanel.Show(show)
++
++ #if show:
++ # self.SetSashSize(5)
++ #else:
++ # self.SetSashSize(0)
++ self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
++ self.Layout()
++
++ def _createCategoryPanel(self, parent):
++ self.catsPanel = wx.Panel(parent=parent)
++ self.cats_list = CategoryListCtrl(parent=self.catsPanel,
++ cats_mgr=self.scatt_mgr.GetCategoriesManager(),
++ sel_cats_in_iscatt=self._selCatInIScatt())
++
++ self.catsPanel.SetMinSize((-1, 100))
++ self.catsPanel.SetInitialSize((-1, 150))
++
++ box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
++ label=' %s ' % _("Classes"),)
++ catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
++
++ self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
++
++ catsSizer.Add(item=self.cats_list, proportion=1, flag=wx.EXPAND | wx.TOP, border = 5)
++ if self.toolbars['categoryToolbar']:
++ catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
++
++ self.catsPanel.SetSizer(catsSizer)
++
++ def CleanUp(self):
++ pass
++
++ def _createCategoryToolbar(self, parent):
++ return CategoryToolbar(parent=parent,
++ scatt_mgr=self.scatt_mgr,
++ cats_list=self.cats_list)
++
++class IScattDialog(wx.Dialog):
++ def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
++ id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
++ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
++ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
++
++ self.iscatt_panel = MapDispIScattPanel(self, giface)
++
++ mainsizer = wx.BoxSizer(wx.VERTICAL)
++ mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
++
++ self.SetSizer(mainsizer)
++
++ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
++
++ self.SetMinSize((300, 300))
++
++ def OnCloseWindow(self, event):
++ event.Skip()
++ #self.
++
++class MapDispIScattPanel(IClassIScattPanel):
++ def __init__(self, parent, giface,
++ id=wx.ID_ANY, **kwargs):
++ IClassIScattPanel.__init__(self, parent=parent, giface=giface,
++ id=id, **kwargs)
++
++ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
++ return ScattsManager(guiparent = self, giface = giface)
++
++ def _createMainToolbar(self):
++ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
++
++ def _selCatInIScatt(self):
++ return True
++
++class ScatterPlotsPanel(scrolled.ScrolledPanel):
++ def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
++
++ scrolled.ScrolledPanel.__init__(self, parent)
++ self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
++
++ self.scatt_mgr = scatt_mgr
++
++ self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
++
++ #self._createCategoryPanel()
++ # Fancy gui
++ self._mgr = aui.AuiManager(self.mainPanel)
++ #self._mgr.SetManagedWindow(self)
++
++ self._mgr.Update()
++
++ self._doLayout()
++ self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
++ self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
++
++ self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
++
++ dlgSize = (-1, 400)
++ #self.SetBestSize(dlgSize)
++ #self.SetInitialSize(dlgSize)
++ self.SetAutoLayout(1)
++ #fix goutput's pane size (required for Mac OSX)
++ #if self.gwindow:
++ # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
++ self.ignore_scroll = 0
++ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
++
++ self.scatt_i = 1
++ self.scatt_id_scatt_i = {}
++ self.transpose = {}
++ self.scatts = {}
++
++ self.Bind(wx.EVT_CLOSE, self.OnClose)
++
++ self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
++
++ def SetBusy(self, busy):
++ for scatt in self.scatts.itervalues():
++ scatt.UpdateCur(busy)
++
++ def CursorPlotMove(self, x, y, scatt_id):
++
++ try:
++ x = int(round(x))
++ y = int(round(y))
++ coords = True
++ except:
++ coords = False
++
++ pane = self._getPane(scatt_id)
++ caption = self._creteCaption(scatt_id)
++ if coords:
++ caption += " %d, %d" % (x, y)
++
++ pane.Caption(caption)
++ self._mgr.RefreshCaptions()
++
++ def _getPane(self, scatt_id):
++ scatt_i = self.scatt_id_scatt_i[scatt_id]
++ name = self._getScatterPlotName(scatt_i)
++ return self._mgr.GetPane(name)
++
++ def ScatterPlotClosed(self, scatt_id):
++
++ scatt_i = self.scatt_id_scatt_i[scatt_id]
++
++ name = self._getScatterPlotName(scatt_i)
++ pane = self._mgr.GetPane(name)
++
++ del self.scatt_id_scatt_i[scatt_id]
++ del self.scatts[scatt_id]
++
++ if pane.IsOk():
++ self._mgr.ClosePane(pane)
++ self._mgr.Update()
++
++ def OnMouseWheel(self, event):
++ #TODO very ugly find some better solution
++ self.ignore_scroll = 3
++ event.Skip()
++
++ def ScrollChildIntoView(self, child):
++ #For aui manager it does not work and returns position always to the top -> deactivated.
++ pass
++
++ def OnPlotPaneClosed(self, event):
++ if isinstance(event.pane.window, ScatterPlotWidget):
++ event.pane.window.CleanUp()
++
++ def OnScrollChanged(self, event):
++ wx.CallAfter(self.Layout)
++
++ def OnScroll(self, event):
++ if self.ignore_scroll > 0:
++ self.ignore_scroll -= 1
++ else:
++ event.Skip()
++
++ #wx.CallAfter(self._mgr.Update)
++ #wx.CallAfter(self.Layout)
++
++ def _doLayout(self):
++
++ mainsizer = wx.BoxSizer(wx.VERTICAL)
++ mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
++ self.SetSizer(mainsizer)
++
++ self.Layout()
++ self.SetupScrolling()
++
++ def OnClose(self, event):
++ """!Close dialog"""
++ #TODO
++ print "closed"
++ self.scatt_mgr.CleanUp()
++ self.Destroy()
++
++ def OnSettings(self, event):
++ pass
++
++ def _newScatterPlotName(self, scatt_id):
++ name = self._getScatterPlotName(self.scatt_i)
++ self.scatt_id_scatt_i[scatt_id] = self.scatt_i
++ self.scatt_i += 1
++ return name
++
++ def _getScatterPlotName(self, i):
++ name = "scatter plot %d" % i
++ return name
++
++ def NewScatterPlot(self, scatt_id, transpose):
++ #TODO needs to be resolved (should be in this class)
++
++ scatt = ScatterPlotWidget(parent = self.mainPanel,
++ scatt_mgr = self.scatt_mgr,
++ scatt_id = scatt_id,
++ transpose = transpose)
++ scatt.plotClosed.connect(self.ScatterPlotClosed)
++ self.transpose[scatt_id] = transpose
++
++ caption = self._creteCaption(scatt_id)
++ self._mgr.AddPane(scatt,
++ aui.AuiPaneInfo().Dockable(True).Floatable(True).
++ Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
++ Caption(caption).
++ Center().Position(1).MaximizeButton(True).
++ MinimizeButton(True).CaptionVisible(True).
++ CloseButton(True).Layer(0))
++
++ self._mgr.Update()
++
++ self.SetVirtualSize(self.GetBestVirtualSize())
++ self.Layout()
++
++ self.scatts[scatt_id] = scatt
++
++ return scatt
++
++ def _creteCaption(self, scatt_id):
++
++ transpose = self.transpose[scatt_id]
++ bands = self.scatt_mgr.GetBands()
++
++ #TODO too low level
++ b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
++
++ x_b = bands[b1_id].split('@')[0]
++ y_b = bands[b2_id].split('@')[0]
++
++ if transpose:
++ tmp = x_b
++ x_b = y_b
++ y_b = tmp
++
++ return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
++
++ def GetScattMgr(self):
++ return self.scatt_mgr
++
++class CategoryListCtrl(wx.ListCtrl,
++ listmix.ListCtrlAutoWidthMixin):
++ #listmix.TextEditMixin):
++
++ def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
++
++ wx.ListCtrl.__init__(self, parent, id,
++ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
++ wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
++ self.columns = ((_('Class name'), 'name'), )
++ #(_('Color'), 'color'))
++
++ self.sel_cats_in_iscatt = sel_cats_in_iscatt
++
++ self.Populate(columns = self.columns)
++
++ self.cats_mgr = cats_mgr
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++ self.rightClickedItemIdx = wx.NOT_FOUND
++
++ listmix.ListCtrlAutoWidthMixin.__init__(self)
++
++ #listmix.TextEditMixin.__init__(self)
++
++ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
++ self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) #wxGTK
++
++ #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
++ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
++
++ self.cats_mgr.setCategoryAttrs.connect(self.Update)
++ self.cats_mgr.deletedCategory.connect(self.Update)
++ self.cats_mgr.addedCategory.connect(self.Update)
++ self.cats_mgr.cleanuped.connect(self.Update)
++
++ def Update(self, **kwargs):
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def InitCoreCats(self):
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def SetVirtualData(self, row, column, text):
++ attr = self.columns[column][1]
++ if attr == 'name':
++ try:
++ text.encode('ascii')
++ except UnicodeEncodeError:
++ GMessage(parent = self, message = _("Please use only ASCII characters."))
++ return
++
++ cat_id = self.cats_mgr.GetCategories()[row]
++
++ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
++ self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
++ self.cats_mgr.setCategoryAttrs.connect(self.Update)
++
++ self.Select(row)
++
++ def Populate(self, columns):
++ for i, col in enumerate(columns):
++ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
++
++ #self.SetColumnWidth(0, 100)
++ #self.SetColumnWidth(1, 100)
++
++ def AddCategory(self):
++
++ self.cats_mgr.addedCategory.disconnect(self.Update)
++ cat_id = self.cats_mgr.AddCategory()
++ self.cats_mgr.addedCategory.connect(self.Update)
++
++ if cat_id < 0:
++ GError(_("Maximum limit of categories number was reached."))
+ return
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
++
++ def DeleteCategory(self):
++ indexList = sorted(self.GetSelectedIndices(), reverse = True)
++ cats = []
++ for i in indexList:
++ # remove temporary raster
++ cat_id = self.cats_mgr.GetCategories()[i]
++
++ cats.append(cat_id)
+
-+ from scatt_plot.frame import IScattDialog
-+ self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
++ self.cats_mgr.deletedCategory.disconnect(self.Update)
++ self.cats_mgr.DeleteCategory(cat_id)
++ self.cats_mgr.deletedCategory.connect(self.Update)
++
++ self.SetItemCount(len(self.cats_mgr.GetCategories()))
+
-+ self.dialogs['scatt_plot'].CenterOnScreen()
-+ self.dialogs['scatt_plot'].Show()
++ def OnSel(self, event):
++ if self.sel_cats_in_iscatt:
++ indexList = self.GetSelectedIndices()
++ sel_cats = []
++ cats = self.cats_mgr.GetCategories()
++ for i in indexList:
++ sel_cats.append(cats[i])
+
- def OnVNet(self, event):
- """!Dialog for v.net* modules
- """
++ if sel_cats:
++ self.cats_mgr.SetSelectedCat(sel_cats[0])
++ event.Skip()
++
++ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
++ indices = []
++ lastFound = -1
++ while True:
++ index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
++ if index == -1:
++ break
++ else:
++ lastFound = index
++ indices.append(index)
++ return indices
++
++ def DeselectAll(self):
++ """!Deselect all items"""
++ indexList = self.GetSelectedIndices()
++ for i in indexList:
++ self.Select(i, on = 0)
++
++ # no highlight
++ self.OnCategorySelected(None)
++
++ def OnGetItemText(self, item, col):
++ attr = self.columns[col][1]
++ cat_id = self.cats_mgr.GetCategories()[item]
++
++ return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
++
++ def OnGetItemImage(self, item):
++ return -1
++
++ def OnGetItemAttr(self, item):
++ cat_id = self.cats_mgr.GetCategories()[item]
++
++ cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++ if cattr['show']:
++ c = cattr['color']
++
++ back_c = wx.Colour(*map(int, c.split(':')))
++ text_c = wx.Colour(*ContrastColor(back_c))
++ else:
++ back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
++ text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
++
++ # if it is in scope of the method, gui falls, using self solved it
++ self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
++ return self.l
++
++ def OnCategoryRightUp(self, event):
++ """!Show context menu on right click"""
++ item, flags = self.HitTest((event.GetX(), event.GetY()))
++ if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
++ self.rightClickedItemIdx = item
++
++ # generate popup-menu
++ cat_idx = self.rightClickedItemIdx
++
++ cats = self.cats_mgr.GetCategories()
++ cat_id = cats[cat_idx]
++ showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
++
++ menu = wx.Menu()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Rename class"))
++ self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Set color"))
++ self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Change opacity level"))
++ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++
++ if showed:
++ text = _("Hide")
++ else:
++ text = _("Show")
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text = text)
++ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
++ attrs={'show' : not showed}),
++ id=item_id)
++
++ menu.AppendSeparator()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move to top"))
++ self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
++ if cat_idx == 0:
++ menu.Enable(item_id, False)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move to bottom"))
++ self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
++ if cat_idx == len(cats) - 1:
++ menu.Enable(item_id, False)
++
++ menu.AppendSeparator()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category up"))
++ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
++ if cat_idx == 0:
++ menu.Enable(item_id, False)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category down"))
++ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
++ if cat_idx == len(cats) - 1:
++ menu.Enable(item_id, False)
++
++ menu.AppendSeparator()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Export class raster"))
++ self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
++
++ self.PopupMenu(menu)
++ menu.Destroy()
++
++ def OnExportCatRast(self, event):
++ """!Export training areas"""
++ #TODO
++ # GMessage(parent=self, message=_("No class raster to export."))
++ # return
++
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++
++ self.cats_mgr.ExportCatRast(cat_id)
++
++ def OnMoveUp(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnMoveDown(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnMoveTop(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, 0)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnMoveBottom(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cats = self.cats_mgr.GetCategories()
++ cat_id = cats[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnSetColor(self, event):
++ """!Popup opacity level indicator"""
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++
++ col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
++ col = map(int, col.split(':'))
++
++ col_data = wx.ColourData()
++ col_data.SetColour(wx.Colour(*col))
++
++ dlg = wx.ColourDialog(self, col_data)
++ dlg.GetColourData().SetChooseFull(True)
++
++ if dlg.ShowModal() == wx.ID_OK:
++ color = dlg.GetColourData().GetColour().Get()
++ color = ':'.join(map(str, color))
++ self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
++
++ dlg.Destroy()
++
++ def OnPopupOpacityLevel(self, event):
++ """!Popup opacity level indicator"""
++
++ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++ value = cat_attrs['opacity'] * 100
++ name = cat_attrs['name']
++
++ dlg = SetOpacityDialog(self, opacity = value,
++ title = _("Change opacity of class <%s>" % name))
++
++ dlg.applyOpacity.connect(lambda value:
++ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
++ dlg.CentreOnParent()
++
++ if dlg.ShowModal() == wx.ID_OK:
++ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
++
++ dlg.Destroy()
++
++ def OnRename(self, event):
++ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++ dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
++ dlg.CentreOnParent()
++
++ while True:
++ if dlg.ShowModal() == wx.ID_OK:
++ name = dlg.GetNewName().strip()
++ if not name:
++ GMessage(parent=self, message=_("Empty name was inserted."))
++ else:
++ self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
++ break
++ else:
++ break
++
++ dlg.Destroy()
++
++ def _setCatAttrs(self, cat_id, attrs):
++ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
++ self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
++ self.cats_mgr.setCategoryAttrs.connect(self.Update)
Index: gui/wxpython/scatt_plot/__init__.py
===================================================================
--- gui/wxpython/scatt_plot/__init__.py (revision 0)
@@ -2155,7 +2655,7 @@
===================================================================
--- gui/wxpython/scatt_plot/plots.py (revision 0)
+++ gui/wxpython/scatt_plot/plots.py (working copy)
-@@ -0,0 +1,950 @@
+@@ -0,0 +1,953 @@
+"""!
+ at package scatt_plot.dialogs
+
@@ -2221,6 +2721,8 @@
+ self.cidpress = None
+ self.cidrelease = None
+
++ self.rend_dt = {}
++
+ self.transpose = transpose
+
+ self.inverse = False
@@ -2242,7 +2744,7 @@
+ self.canvas.mpl_connect('button_press_event', self.OnPress)
+ self.canvas.mpl_connect('button_release_event', self.OnRelease)
+ self.canvas.mpl_connect('draw_event', self.draw_callback)
-+ self.canvas.mpl_connect('figure_leave_event', self.OnLeave)
++ self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)
+
+ def draw_callback(self, event):
+ self.polygon_drawer.draw_callback(event)
@@ -2367,13 +2869,14 @@
+ else:
+ c = None
+
-+ #q = Queue()
-+ #p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
-+ #p.start()
-+ #merged_img, self.full_extend = q.get()
-+ #p.join()
++ q = Queue()
++ p = Process(target=MergeImg, args=(cats_order, scatts, styles,
++ self.rend_dt, q))
++ p.start()
++ merged_img, self.full_extend, self.rend_dt = q.get()
++ p.join()
+
-+ merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
++ #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
+ self.axes.clear()
+ self.axes.axis('equal')
+
@@ -2512,7 +3015,7 @@
+
+ self.cursorMove.emit(x=event.xdata, y=event.ydata, scatt_id=self.scatt_id)
+
-+ def OnLeave(self, event):
++ def OnCanvasLeave(self, event):
+ self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)
+
+ def PanMotion(self, event):
@@ -2558,6 +3061,118 @@
+ #self.axes.draw_artist(self.zoom_rect)
+ self.canvas.draw()
+
++def MergeImg(cats_order, scatts, styles, rend_dt, output_queue):
++
++ init = True
++ merged_img = None
++ merge_tmp = grass.tempfile()
++ for cat_id in cats_order:
++ if not scatts.has_key(cat_id):
++ continue
++ scatt = scatts[cat_id]
++ #print "color map %d" % cat_id
++ #TODO make more general
++ if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
++ not styles[cat_id]['show']):
++ if rend_dt.has_key(cat_id) and not rend_dt[cat_id]:
++ del rend_dt[cat_id]
++ continue
++ if init:
++
++ b2_i = scatt['bands_info']['b1']
++ b1_i = scatt['bands_info']['b2']
++
++ full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5)
++
++ # if it does not need to be updated and was already rendered
++ if not _renderCat(cat_id, rend_dt, scatt, styles):
++ # is empty - has only zeros
++ if rend_dt[cat_id] is None:
++ continue
++ else:
++ masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
++ vmax = np.amax(masked_cat)
++ # totally empty -> no need to render
++ if vmax == 0:
++ render_cat_ids[cat_id] = None
++ continue
++
++ cmap = _getColorMap(cat_id, styles)
++ masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
++
++ cmap = np.uint8(cmap._lut * 255)
++ sh =masked_cat.shape
++
++ rend_dt[cat_id] = {}
++ if cat_id != 0:
++ rend_dt[cat_id]['color'] = styles[cat_id]['color']
++ rend_dt[cat_id]['dt'] = np.memmap(grass.tempfile(), dtype='uint8', mode='w+',
++ shape=(sh[0], sh[1], 4))
++ #colored_cat = np.zeros(dtype='uint8', )
++ ApplyColormap(masked_cat, masked_cat.mask, cmap, rend_dt[cat_id]['dt'])
++
++ #colored_cat = np.uint8(cmap(masked_cat) * 255)
++ del masked_cat
++ del cmap
++
++ #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
++ if init:
++ merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+',
++ shape=rend_dt[cat_id]['dt'].shape)
++ merged_img[:] = rend_dt[cat_id]['dt']
++ init = False
++ else:
++ MergeArrays(merged_img, rend_dt[cat_id]['dt'], styles[cat_id]['opacity'])
++
++ """
++ #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape)
++ c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
++
++ #TODO apply strides and there will be no need for loop
++ #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
++
++ for i in range(3):
++ merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
++ merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
++
++ del c_img_a
++ """
++
++ output_queue.put((merged_img, full_extend, rend_dt))
++
++def _renderCat(cat_id, rend_dt, scatt, styles):
++ return True
++
++ if not rend_dt.has_key(cat_id):
++ return True
++ if not rend_dt[cat_id]:
++ return False
++ if scatt['render']:
++ return True
++ if cat_id != 0 and \
++ rend_dt[cat_id]['color'] != styles[cat_id]['color']:
++ return True
++
++ return False
++
++def _getColorMap(cat_id, styles):
++ cmap = matplotlib.cm.jet
++ if cat_id == 0:
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ else:
++ colors = styles[cat_id]['color'].split(":")
++
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ cmap._lut[:, 0] = int(colors[0])/255.0
++ cmap._lut[:, 1] = int(colors[1])/255.0
++ cmap._lut[:, 2] = int(colors[2])/255.0
++
++ return cmap
++
+class ScatterPlotContextMenu:
+ def __init__(self, plot):
+
@@ -2671,6 +3286,9 @@
+
+ def _setEmptyPol(self, empty_pol):
+ self.empty_pol = empty_pol
++ if self.empty_pol:
++ #TODO
++ self.pol.xy = np.array([[0, 0]])
+ self._show(not empty_pol)
+
+ def _show(self, show):
@@ -2991,133 +3609,25 @@
+ im._remove_method = lambda h: axes.images.remove(h)
+
+ return im
-+
-+def MergeImg(cats_order, scatts, styles, output_queue):
-+
-+ start_time = time.clock()
-+ cmap_time = 0
-+ merge_time = 0
-+ mask_time = 0
-+ norm_time = 0
-+ max_time = 0
-+
-+ init = True
-+ merge_tmp = grass.tempfile()
-+ for cat_id in cats_order:
-+ if not scatts.has_key(cat_id):
-+ continue
-+ scatt = scatts[cat_id]
-+ #print "color map %d" % cat_id
-+ #TODO make more general
-+ if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
-+ not styles[cat_id]['show']):
-+ continue
-+ if init:
-+
-+ b2_i = scatt['bands_info']['b1']
-+ b1_i = scatt['bands_info']['b2']
-+
-+ full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5)
-+
-+ if cat_id == 0:
-+ cmap = matplotlib.cm.jet
-+ cmap.set_bad('w',1.)
-+ cmap._init()
-+ cmap._lut[len(cmap._lut) - 1, -1] = 0
-+ else:
-+ colors = styles[cat_id]['color'].split(":")
-+
-+ cmap = matplotlib.cm.jet
-+ cmap.set_bad('w',1.)
-+ cmap._init()
-+ cmap._lut[len(cmap._lut) - 1, -1] = 0
-+ cmap._lut[:, 0] = int(colors[0])/255.0
-+ cmap._lut[:, 1] = int(colors[1])/255.0
-+ cmap._lut[:, 2] = int(colors[2])/255.0
-+
-+ #if init:
-+ tmp = time.clock()
-+
-+ masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
-+ mask_time += time.clock() - tmp
-+
-+ tmp = time.clock()
-+ vmax = np.amax(masked_cat)
-+ max_time += time.clock() - tmp
-+
-+ tmp = time.clock()
-+ masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
-+ norm_time += time.clock() - tmp
-+
-+ #print masked_cat
-+ #print masked_cat.shape
-+ #print masked_cat.mask.dtype
-+ #
-+ #print "colored_cat.shape"
-+ #print colored_cat.shape
-+ #merged_img = np.memmap(merge_tmp, dtype='uint8',, shape=colored_cat.shape)
-+ tmp = time.clock()
-+ cmap = np.uint8(cmap._lut * 255)
-+ sh =masked_cat.shape
-+
-+ colored_cat = np.zeros(dtype='uint8', shape=(sh[0], sh[1], 4))
-+ ApplyColormap(masked_cat, masked_cat.mask, cmap, colored_cat)
-+
-+ #colored_cat = np.uint8(cmap(masked_cat) * 255)
-+ cmap_time += time.clock() - tmp
-+
-+ del masked_cat
-+ del cmap
-+
-+ #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
-+
-+ tmp = time.clock()
-+ if init:
-+ merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', shape=colored_cat.shape)
-+ merged_img[:] = colored_cat[:]
-+ init = False
-+ else:
-+ MergeArrays(merged_img, colored_cat, styles[cat_id]['opacity'])
-+
-+ """
-+ #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape)
-+ c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
-+
-+ #TODO apply strides and there will be no need for loop
-+ #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
-+
-+ for i in range(3):
-+ merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
-+ merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
-+
-+ del c_img_a
-+ """
-+ merge_time += time.clock() - tmp
-+
-+ del colored_cat
-+
-+ end_time = time.clock() - start_time
-+ #print "all time:%f" % (end_time)
-+ #print "cmap_time:%f" % (cmap_time / end_time * 100.0 )
-+ #print "merge_time:%f" % (merge_time / end_time * 100.0 )
-+ #print "mask_time:%f" % (mask_time / end_time * 100.0 )
-+ #print "nax_time:%f" % (max_time / end_time * 100.0 )
-+ #print "norm_time:%f" % (norm_time / end_time * 100.0 )
-+
-+ #output_queue.put((merged_img, full_extend))
-+ return merged_img, full_extend
-\ No newline at end of file
Index: gui/wxpython/scatt_plot/controllers.py
===================================================================
--- gui/wxpython/scatt_plot/controllers.py (revision 0)
+++ gui/wxpython/scatt_plot/controllers.py (working copy)
-@@ -0,0 +1,890 @@
+@@ -0,0 +1,1068 @@
+"""!
+ at package scatt_plot.controllers
+
+ at brief Controller layer for scatter plot tool.
+
+Classes:
++ - controllers::ScattsManager
++ - controllers::PlotsRenderingManager
++ - controllers::CategoriesManager
++ - controllers::IMapWinDigitConnection
++ - controllers::IClassDigitConnection
++ - controllers::IMapDispConnection
++ - controllers::IClassConnection
++ - controllers::gThread
+
+(C) 2013 by the GRASS Development Team
+
@@ -3128,23 +3638,28 @@
+"""
+import os
+import sys
++from copy import deepcopy
+
-+#TODO just for testing
+import time
++import threading
++import Queue
++from core.gconsole import EVT_CMD_DONE
+
+import wx
+
+from core.gcmd import GException, GError, GMessage, RunCommand
+from core.settings import UserSettings
-+from scatt_plot.gthreading import gThread
++from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
+from scatt_plot.scatt_core import Core, idBandsToidScatt
+from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
++
+from iclass.dialogs import IClassGroupDialog
+
+import grass.script as grass
+from grass.pydispatch.signal import Signal
+
+class ScattsManager:
++ """!Main controller."""
+ def __init__(self, guiparent, giface, iclass_mapwin = None):
+ #TODO remove iclass parameter
+
@@ -3163,7 +3678,7 @@
+ self.core = Core()
+
+ self.cats_mgr = CategoriesManager(self, self.core)
-+ self.render_mgr = RenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
++ self.render_mgr = PlotsRenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core)
+
+ self.thread = gThread();
+
@@ -3226,6 +3741,7 @@
+ self.iclass_conn.SetData()
+
+ def SetBands(self, bands):
++ self.busy = wx.BusyInfo(_("Loading data..."))
+ self.CleanUp()
+ self.data_set = False
+
@@ -3238,16 +3754,13 @@
+ ondone=self.SetDataDone, userdata={"show_add" : show_add})
+
+ def SetDataDone(self, event):
++ del self.busy
+ self.data_set = True
+
+ self.iclass_conn.SyncCats()
+ if event.userdata['show_add']:
-+ self.AddScattPlot()
++ self.AddScattPlot()
+
-+ def SettingsUpdated(self, event):
-+ #TODO optimization
-+ self.render_mgr.RenderScattPlts()
-+
+ def OnOutput(self, event):
+ """!Print thread output according to debug level.
+ """
@@ -3305,6 +3818,7 @@
+ dlg.Destroy()
+
+ def _addScattPlot(self, scatt_ids):
++ self.render_mgr.NewRunningProcess()
+ self.thread.Run(callable=self.core.AddScattPlots,
+ scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)
+
@@ -3326,7 +3840,7 @@
+ self.plots[s_id]['scatt'].SetMode(self.plot_mode)
+ self.plots[s_id]['scatt'].ZoomToExtend()
+
-+ self.render_mgr.RenderScattPlts(scatt_ids = scatt_ids)
++ self.render_mgr.RunningProcessDone()
+
+ def PlotClosed(self, scatt_id):
+ del self.plots[scatt_id]
@@ -3349,7 +3863,7 @@
+ if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
+ self.SetPlotsMode(None)
+
-+ self.render_mgr.RenderScattPlts()
++ self.render_mgr.RunningProcessDone()
+ return activate
+
+ def ProcessSelectionPolygons(self, process_mode):
@@ -3385,62 +3899,103 @@
+ return
+
+ for scatt in self.plots.itervalues():
-+ scatt['scatt'].SetEmpty()
++ if scatt['scatt']:
++ scatt['scatt'].SetEmpty()
+
+ self.computingStarted.emit()
+
++ self.render_mgr.NewRunningProcess()
++ self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
++ self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])
++
+ self.thread.Run(callable = self.core.UpdateCategoryWithPolygons,
+ cat_id = sel_cat_id,
+ scatts_pols = scatts_polygons,
+ value = value, ondone=self.SetEditCatDataDone)
+
+ def SetEditCatDataDone(self, event):
++ self.render_mgr.RunningProcessDone()
+ if event.exception:
+ GError(_("Error occured during computation of scatter plot category:\n%s"),
+ parent = self.guiparent, showTraceback = False)
+
+ cat_id = event.ret
-+
-+ self.render_mgr.RenderScattPlts()
+ self.iclass_conn.RenderCatRast(cat_id)
+
-+ def DigitDataChanged(self, vectMap, digit):
-+
-+ if self.digit_conn:
-+ self.digit_conn.DigitDataChanged(vectMap, digit)
-+ return 1
-+ else:
-+ return 0
++ def SettingsUpdated(self, chanaged_setts):
++ self.render_mgr.RenderRequest()
+
++ #['ellipses', 'show_ellips']
+ def GetCategoriesManager(self):
+ return self.cats_mgr
+
-+class RenderingManager:
++class PlotsRenderingManager:
++ """!Manages rendering of scatter plot.
+
++ @todo still space for optimalization
++ """
+ def __init__(self, scatt_mgr, cats_mgr, core):
+ self.scatt_mgr = scatt_mgr
+ self.cats_mgr = cats_mgr
+ self.core = core
+ self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
+
-+ self.scatts_to_render = {}
++ self.runningProcesses = 0
++
++ self.data_to_render = {}
+ self.render_queue = []
+
++ self.cat_ids = []
++ self.cat_cond_ids = []
++
+ self.renderingStarted = Signal("ScattsManager.renderingStarted")
+ self.renderingFinished = Signal("ScattsManager.renderingFinished")
+
++ def AddRenderRequest(self, scatts):
++ for scatt_id, cat_ids in scatts:
++ if not self.data_to_render.has_key[scatt_id]:
++ self.data_to_render = cat_ids
++ else:
++ for c in cat_ids:
++ if c not in self.data_to_render[scatt_id]:
++ self.data_to_render[scatt_id].append(c)
++
++ def NewRunningProcess(self):
++ self.runningProcesses += 1
++
++ def RunningProcessDone(self):
++ self.runningProcesses -= 1
++ if self.runningProcesses <= 1:
++ self.RenderScattPlts()
++
++ def RenderRequest(self):
++ if self.runningProcesses <= 1:
++ self.RenderScattPlts()
++
++ def CategoryChanged(self, cat_ids):
++ for c in cat_ids:
++ if c not in self.cat_ids:
++ self.cat_ids.append(c)
++
++ def CategoryCondsChanged(self, cat_ids):
++ for c in cat_ids:
++ if c not in self.cat_cond_ids:
++ self.cat_cond_ids.append(c)
++
+ def RenderScattPlts(self, scatt_ids = None):
+ if len(self.render_queue) > 1:
+ return
+
+ self.renderingStarted.emit()
+ self.render_queue.append(self.scatt_mgr.thread.GetId())
-+ self.scatt_mgr.thread.Run(callable = self._renderscattplts, scatt_ids = scatt_ids,
-+ ondone=self.RenderingDone)
+
-+ def _renderscattplts(self, scatt_ids):
-+ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++ cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
+ cats = self.cats_mgr.GetCategories()[:]
++ self.scatt_mgr.thread.Run(callable=self._renderscattplts, scatt_ids=scatt_ids,
++ cats=cats, cats_attrs=cats_attrs,
++ ondone=self.RenderingDone)
++
++ def _renderscattplts(self, scatt_ids, cats, cats_attrs):
+ cats.reverse()
+ cats.insert(0, 0)
+ for i_scatt_id, scatt in self.scatt_mgr.plots.items():
@@ -3456,15 +4011,25 @@
+ else:
+ ellipses_dt = {}
+
++ for c in scatt_dt.iterkeys():
++ try:
++ self.cat_ids.remove(c)
++ scatt_dt[c]['render']=True
++ except:
++ scatt_dt[c]['render']=False
++
+ if self.scatt_mgr.pol_sel_mode[0]:
+ self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
+
-+ scatt['scatt'].Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++ scatt['scatt'].Plot(cats_order=cats,
++ scatts=scatt_dt,
++ ellipses=ellipses_dt,
++ styles=cats_attrs)
+
+ def RenderingDone(self, event):
+ self.render_queue.remove(event.pid)
+ if not self.render_queue:
-+ self.renderingFinished.emit()
++ self.renderingFinished.emit()
+
+ def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
+
@@ -3472,7 +4037,7 @@
+ if not cat_id:
+ return
+
-+ sel_a_cat_id = max(cats_attrs.keys()) + 1
++ sel_a_cat_id = -1
+
+ s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
+ if not s:
@@ -3494,15 +4059,20 @@
+ 'show' : True}
+
+ scatt_dt[sel_a_cat_id] = s[cat_id]
++
++ scatt_dt[sel_a_cat_id]['render'] = False
++ if cat_id in self.cat_cond_ids:
++ scatt_dt[sel_a_cat_id]['render'] = True
++ self.cat_cond_ids.remove(cat_id)
+
-+
+ def _showConfEllipses(self):
+ return UserSettings.Get(group='scatt',
+ key="ellipses",
+ subkey="show_ellips")
+
+class CategoriesManager:
-+
++ """!Manages categories list of scatter plot.
++ """
+ def __init__(self, scatt_mgr, core):
+
+ self.core = core
@@ -3516,6 +4086,7 @@
+ self.exportRaster = None
+
+ self.initialized = Signal('CategoriesManager.initialized')
++ self.cleanuped = Signal('CategoriesManager.cleanuped')
+ self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
+ self.deletedCategory = Signal('CategoriesManager.deletedCategory')
+ self.addedCategory = Signal('CategoriesManager.addedCategory')
@@ -3543,7 +4114,7 @@
+
+ self.cats_ids.insert(new_pos, cat_id)
+
-+ self.scatt_mgr.render_mgr.RenderScattPlts()
++ self.scatt_mgr.render_mgr.RenderRequest()
+ return True
+
+ def _addCategory(self, cat_id):
@@ -3566,7 +4137,7 @@
+ # return -1;
+
+ self.cats[cat_id] = {
-+ 'name' : _('Class %s' % cat_id ),
++ 'name' : 'class_%d' % cat_id,
+ 'color' : "0:0:0",
+ 'opacity' : 1.0,
+ 'show' : True,
@@ -3602,7 +4173,8 @@
+ self.cats[cat_id][k] = v
+
+ if render:
-+ self.scatt_mgr.render_mgr.RenderScattPlts()
++ self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
++ self.scatt_mgr.render_mgr.RenderRequest()
+
+ if update_cat_rast:
+ self.scatt_mgr.iclass_conn.UpdateCategoryRaster(cat_id, update_cat_rast)
@@ -3623,7 +4195,7 @@
+ def SetSelectedCat(self, cat_id):
+ self.sel_cat_id = cat_id
+ if self.scatt_mgr.pol_sel_mode[0]:
-+ self.scatt_mgr.render_mgr.RenderScattPlts()
++ self.scatt_mgr.render_mgr.RenderRequest()
+
+ def GetSelectedCat(self):
+ return self.sel_cat_id
@@ -3661,7 +4233,7 @@
+ userdata={'name' : cat_attrs['name']},
+ cat_id=cat_id,
+ rast_name=self.exportCatRast,
-+ ondone=self.cats_mgr.OnExportCatRastDone)
++ ondone=self.OnExportCatRastDone)
+
+ def OnExportCatRastDone(self, event):
+ ret, err = event.ret
@@ -3675,10 +4247,14 @@
+
+
+class IMapWinDigitConnection:
++ """!Manage comunication of the scatter plot with digitizer in mapwindow (does not work).
++ """
+ def Update(self):
+ pass
+
+class IClassDigitConnection:
++ """!Manages comunication of the scatter plot with digitizer in wx.iclass.
++ """
+ def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
+ self.mapWin = mapWin
+ self.vectMap = None
@@ -3773,6 +4349,8 @@
+ self.scatt_mgr.render_mgr.RenderScattPlts()
+
+class IMapDispConnection:
++ """!Manage comunication of the scatter plot with mapdisplay in mapwindow.
++ """
+ def __init__(self, scatt_mgr, cats_mgr, giface):
+ self.scatt_mgr = scatt_mgr
+ self.cats_mgr = cats_mgr
@@ -3782,7 +4360,7 @@
+
+ def SetData(self):
+ dlg = IClassGroupDialog(self.scatt_mgr.guiparent,
-+ group=self.set_g['g'],
++ group=self.set_g['group'],
+ subgroup=self.set_g['subg'])
+
+ bands = []
@@ -3806,7 +4384,7 @@
+ self.scatt_mgr.SetBands(bands)
+
+ def EmptyCategories(self):
-+ pass
++ return None
+
+ def SyncCats(self, cats_ids = None):
+ pass
@@ -3855,6 +4433,8 @@
+ self.giface.updateMap.emit()
+
+class IClassConnection:
++ """!Manage comunication of the scatter plot with mapdisplay in wx.iclass.
++ """
+ def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
+ self.iclass_frame = iclass_frame
+ self.stats_data = self.iclass_frame.stats_data
@@ -4002,43 +4582,12 @@
+ if res.split('\n')[0]:
+ bands = res.split('\n')
+ self.scatt_mgr.SetBands(bands)
-\ No newline at end of file
-Index: gui/wxpython/scatt_plot/gthreading.py
-===================================================================
---- gui/wxpython/scatt_plot/gthreading.py (revision 0)
-+++ gui/wxpython/scatt_plot/gthreading.py (working copy)
-@@ -0,0 +1,125 @@
-+"""!
-+ at package scatt_plot.gthreading
+
-+Classes:
-+
-+(C) 2013 by the GRASS Development Team
-+
-+This program is free software under the GNU General Public License
-+(>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+
-+import os
-+import sys
-+import time
-+import threading
-+import Queue
-+from core.gconsole import EVT_CMD_DONE
-+
-+import wx
-+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
-+
+class gThread(threading.Thread, wx.EvtHandler):
-+ """!Thread for GRASS commands"""
++ """!Thread for scatter plot backend"""
+ requestId = 0
+
+ def __init__(self, requestQ=None, resultQ=None, **kwds):
-+ """!
-+ @param receiver event receiver (used in PostEvent)
-+ """
+ wx.EvtHandler.__init__(self)
+
+ threading.Thread.__init__(self, **kwds)
@@ -4133,18 +4682,22 @@
+ self._want_abort_all = True
+ if self.requestQ.empty():
+ self._want_abort_all = False
-\ No newline at end of file
Index: gui/wxpython/scatt_plot/dialogs.py
===================================================================
--- gui/wxpython/scatt_plot/dialogs.py (revision 0)
+++ gui/wxpython/scatt_plot/dialogs.py (working copy)
-@@ -0,0 +1,494 @@
+@@ -0,0 +1,517 @@
+"""!
+ at package scatt_plot.dialogs
+
-+ at brief GUI.
++ at brief Dialogs widgets.
+
+Classes:
++ - dialogs::AddScattPlotDialog
++ - dialogs::ExportCategoryRaster
++ - dialogs::SettingsDialog
++ - dialogs::ManageBusyCursorMixin
++ - dialogs::RenameClassDialog
+
+(C) 2013 by the GRASS Development Team
+
@@ -4166,6 +4719,7 @@
+from core import globalvar
+from core.gcmd import GMessage
+from core.settings import UserSettings
++from gui_core.dialogs import SimpleDialog
+
+class AddScattPlotDialog(wx.Dialog):
+
@@ -4452,7 +5006,7 @@
+ settsLabels = {}
+
+ self.settings["show_ellips"] = wx.CheckBox(parent = self, id=wx.ID_ANY,
-+ label = _('Show confidence ellipses'))
++ label = _('Show confidence ellipses'))
+ show_ellips = UserSettings.Get(group ='scatt', key = "ellipses", subkey = "show_ellips")
+ self.settings["show_ellips"].SetValue(show_ellips)
+
@@ -4528,7 +5082,7 @@
+ selPolBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+
+ ell_box = wx.StaticBox(parent = self, id = wx.ID_ANY,
-+ label =" %s " % _("Ellipses settings:"))
++ label =" %s " % _("Ellipses settings:"))
+ ellPolBoxSizer = wx.StaticBoxSizer(ell_box, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap = 1, hgap = 1)
+
@@ -4633,12 +5187,28 @@
+ return
+
+ self.busy_cur = None
-\ No newline at end of file
++
++class RenameClassDialog(SimpleDialog):
++ def __init__(self, parent, old_name, title=("Change class name")):
++ SimpleDialog.__init__(self, parent, title)
++
++ self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY)
++ self.name.SetValue(old_name)
++
++ self.dataSizer.Add(self.name, proportion = 0,
++ flag = wx.EXPAND | wx.ALL, border = 1)
++
++ self.panel.SetSizer(self.sizer)
++ self.name.SetMinSize((200, -1))
++ self.sizer.Fit(self)
++
++ def GetNewName(self):
++ return self.name.GetValue()
Index: gui/wxpython/scatt_plot/toolbars.py
===================================================================
--- gui/wxpython/scatt_plot/toolbars.py (revision 0)
+++ gui/wxpython/scatt_plot/toolbars.py (working copy)
-@@ -0,0 +1,264 @@
+@@ -0,0 +1,267 @@
+"""!
+ at package scatt_plot.toolbars
+
@@ -4721,9 +5291,9 @@
+ wx.ITEM_CHECK),
+ (None, ),
+ ('settings', icons["settings"],
-+ self.OnSettings)
-+ #('help', icons["help"],
-+ # self.OnHelp),
++ self.OnSettings),
++ ('help', icons["help"],
++ self.OnHelp)
+ ]
+
+ if self.opt_tools and "add_group" in self.opt_tools:
@@ -4778,6 +5348,10 @@
+ dlg.ShowModal()
+ dlg.Destroy()
+
++ def OnHelp(self, event) :
++ RunCommand('g.manual',
++ entry = 'wxGUI.iscatt')
++
+class EditingToolbar(BaseToolbar):
+ """!Main toolbar
+ """
@@ -4796,26 +5370,25 @@
+ """
+ self.icons = {
+ 'sel_add' : MetaIcon(img = 'layer-add',
-+ label = _('Include selected area to class.'),
-+ desc = _('Include selected area to class.')),
++ label = _('Include selected area to class'),
++ desc = _('Include selected area to class')),
+ 'sel_remove' : MetaIcon(img = 'layer-remove',
-+ label = _('Exclude selected area from class.'),
-+ desc = _('Exclude selected area from class.')),
++ label = _('Exclude selected area from class'),
++ desc = _('Exclude selected area from class')),
+ 'addVertex' : MetaIcon(img = 'vertex-create',
+ label = _('Add new vertex'),
+ desc = _('Add new vertex to polygon boundary scatter plot')),
+ 'editLine' : MetaIcon(img = 'polygon-create',
-+ label = _('Edit line/boundary'),
++ label = _('Create selection polygon'),
+ desc = _('Add new vertex between last and first points of the boundary')),
+ 'moveVertex' : MetaIcon(img = 'vertex-move',
+ label = _('Move vertex'),
+ desc = _('Move boundary vertex')),
+ 'removeVertex' : MetaIcon(img = 'vertex-delete',
+ label = _('Remove vertex'),
-+ desc = _('Remove boundary vertex.')),
++ desc = _('Remove boundary vertex')),
+ 'delete' : MetaIcon(img = 'polygon-delete',
-+ label = _('Remove polygon.'),
-+ desc = _('Remove polygon.')),
++ label = _("Remove polygon (click on scatter plot for removing it's polygon)")),
+ }
+
+ return self._getToolbarData((
@@ -4892,9 +5465,9 @@
+ """
+ self.icons = {
+ 'add_class' : MetaIcon(img = 'layer-add',
-+ label = _('Add class.')),
++ label = _('Add class')),
+ 'remove_class' : MetaIcon(img = 'layer-remove',
-+ label = _('Remove selected class.'))
++ label = _('Remove selected class'))
+ }
+
+ return self._getToolbarData((
@@ -4908,13 +5481,18 @@
===================================================================
--- gui/wxpython/scatt_plot/scatt_core.py (revision 0)
+++ gui/wxpython/scatt_plot/scatt_core.py (working copy)
-@@ -0,0 +1,811 @@
+@@ -0,0 +1,835 @@
+"""!
+ at package scatt_plot.scatt_plot
+
+ at brief Non GUI functions.
+
+Classes:
++ - scatt_core::Core
++ - scatt_core::CatRastUpdater
++ - scatt_core::AnalyzedData
++ - scatt_core::ScattPlotsCondsData
++ - scatt_core::ScattPlotsData
+
+(C) 2013 by the GRASS Development Team
+
@@ -4929,12 +5507,13 @@
+import time
+
+import numpy as np
-+#TODO
-+from matplotlib.path import Path
+
++# used iclass perimeters algorithm instead of convolve2d
++#from matplotlib.path import Path
++#from scipy.signal import convolve2d
++
+from math import sqrt, ceil, floor
+from copy import deepcopy
-+#from scipy.signal import convolve2d
+
+from core.gcmd import GException, GError, RunCommand
+
@@ -4944,6 +5523,8 @@
+ Rasterize, SC_SCATT_DATA, SC_SCATT_CONDITIONS
+
+class Core:
++ """!Represents scatter plot backend.
++ """
+ def __init__(self):
+
+ self.an_data = AnalyzedData()
@@ -4954,6 +5535,8 @@
+ self.cat_rast_updater = CatRastUpdater(self.scatts_dt, self.an_data, self)
+
+ def SetData(self, bands):
++ """Set bands for analysis.
++ """
+ self.an_data.Create(bands)
+
+ n_bands = len(self.GetBands())
@@ -5080,6 +5663,17 @@
+ self.ComputeCatsScatts([cat_id])
+ return cat_id
+
++ def ExportCatRast(self, cat_id, rast_name):
++
++ cat_rast = self.scatts_dt.GetCatRast(cat_id);
++ if not cat_rast:
++ return 1
++
++ return RunCommand("g.copy",
++ rast=cat_rast + ',' + rast_name,
++ getErrorMsg=True,
++ overwrite=True)
++
+ def _validExtend(self, val):
+ #TODO do it general
+ if val > 255:
@@ -5090,7 +5684,8 @@
+ return val
+
+class CatRastUpdater:
-+
++ """!Update backend data structures according to selected areas in mapwindow.
++ """
+ def __init__(self, scatts_dt, an_data, core):
+ self.scatts_dt = scatts_dt
+ self.an_data = an_data # TODO may be confusing
@@ -5245,7 +5840,8 @@
+ return {"GRASS_REGION" : grass.region_env(**new_r)}
+
+class AnalyzedData:
-+
++ """!Represents analyzed data (bands, region).
++ """
+ def __init__(self):
+
+ self.bands = []
@@ -5333,7 +5929,8 @@
+ return region
+
+class ScattPlotsCondsData:
-+
++ """!Data structure for selected areas in scatter plot(condtions).
++ """
+ def __init__(self, an_data):
+
+ self.an_data = an_data
@@ -5482,7 +6079,8 @@
+
+
+class ScattPlotsData(ScattPlotsCondsData):
-+
++ """!Data structure for computed points (classes) in scatter plots.\
++ """
+ def __init__(self, an_data):
+
+ self.cats_rasts = {}
@@ -5697,9 +6295,8 @@
+ return raster
+"""
+
-+#TODO move to utils?
+def idScattToidBands(scatt_id, n_bands):
-+
++ """!Get bands ids from scatter plot id."""
+ n_b1 = n_bands - 1
+
+ band_1 = (int) ((2 * n_b1 + 1 - sqrt(((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2)
@@ -5709,7 +6306,7 @@
+ return band_1, band_2
+
+def idBandsToidScatt(band_1_id, band_2_id, n_bands):
-+
++ """!Get scatter plot id from band ids."""
+ if band_2_id < band_1_id:
+ tmp = band_1_id
+ band_1_id = band_2_id
@@ -5721,1077 +6318,9 @@
+
+ return scatt_id
\ No newline at end of file
-Index: gui/wxpython/scatt_plot/core_c.py
-===================================================================
---- gui/wxpython/scatt_plot/core_c.py (revision 0)
-+++ gui/wxpython/scatt_plot/core_c.py (working copy)
-@@ -0,0 +1,232 @@
-+"""!
-+ at package scatt_plot.scatt_plot
-+
-+ at brief Functions which work with scatter plot c code.
-+
-+Classes:
-+
-+(C) 2013 by the GRASS Development Team
-+
-+This program is free software under the GNU General Public License
-+(>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+
-+#TODO just for testing
-+import time
-+
-+import sys
-+import numpy as np
-+from multiprocessing import Process, Queue
-+
-+from ctypes import *
-+try:
-+ from grass.lib.imagery import *
-+ from grass.lib.gis import Cell_head, G_get_window
-+except ImportError, e:
-+ sys.stderr.write(_("Loading ctypes libs failed"))
-+
-+from core.gcmd import GException
-+
-+def Rasterize(polygon, rast, region, value):
-+ pol_size = len(polygon) * 2
-+ pol = np.array(polygon, dtype=float)
-+
-+ c_uint8_p = POINTER(c_uint8)
-+ c_double_p = POINTER(c_double)
-+
-+ rows, cols = rast.shape
-+
-+ #TODO creating of region is on many places
-+ region['rows'] = rows
-+ region['cols'] = cols
-+
-+ region['nsres'] = 1.0
-+ region['ewres'] = 1.0
-+
-+ pol_p = pol.ctypes.data_as(c_double_p)
-+ rast_p = rast.ctypes.data_as(c_uint8_p)
-+
-+ cell_h = _regionToCellHead(region)
-+ I_rasterize(pol_p,
-+ len(polygon),
-+ rast_p, value,
-+ pointer(cell_h))
-+
-+def ApplyColormap(vals, vals_mask, colmap, out_vals):
-+
-+ c_uint8_p = POINTER(c_uint8)
-+
-+ vals_p = vals.ctypes.data_as(c_uint8_p)
-+
-+
-+ if hasattr(vals_mask, "ctypes"):
-+ vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
-+ else: #vals mask is empty (all data are selected)
-+ vals_mask_p = None
-+ colmap_p = colmap.ctypes.data_as(c_uint8_p)
-+ out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
-+
-+ vals_size = vals.reshape((-1)).shape[0]
-+ I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
-+
-+def MergeArrays(merged_arr, overlay_arr, alpha):
-+ if merged_arr.shape != overlay_arr.shape:
-+ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
-+
-+ c_uint8_p = POINTER(c_uint8)
-+ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
-+ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
-+
-+ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
-+
-+def MergeArrays(merged_arr, overlay_arr, alpha):
-+ if merged_arr.shape != overlay_arr.shape:
-+ GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
-+
-+ c_uint8_p = POINTER(c_uint8)
-+ merged_p = merged_arr.ctypes.data_as(c_uint8_p)
-+ overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
-+
-+ I_merge_arrays(merged_p, overlay_p, merged_arr.shape[0], merged_arr.shape[1], alpha)
-+
-+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
-+
-+ q = Queue()
-+ p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
-+ n_bands, scatts, cats_rasts_conds, cats_rasts, q))
-+ p.start()
-+ ret = q.get()
-+ p.join()
-+
-+ return ret[0], ret[1]
-+
-+def UpdateCatRast(patch_rast, region, cat_rast):
-+ q = Queue()
-+ p = Process(target=_updateCatRastProcess, args=(patch_rast, region, cat_rast, q))
-+ p.start()
-+ ret = q.get()
-+ p.join()
-+
-+ return ret
-+
-+def CreateCatRast(region, cat_rast):
-+ cell_head = _regionToCellHead(region)
-+ I_create_cat_rast(pointer(cell_head), cat_rast)
-+
-+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
-+
-+ #TODO names for types not 0 and 1?
-+ sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
-+ scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
-+
-+ char_bands = _stringListToCharArr(bands)
-+
-+ cell_head = _regionToCellHead(region)
-+
-+ ret = I_compute_scatts(pointer(cell_head),
-+ pointer(scatt_conds_c),
-+ pointer(cats_rasts_conds_c),
-+ pointer(char_bands),
-+ n_bands,
-+ pointer(sccats_c),
-+ pointer(cats_rasts_c))
-+
-+ I_sc_free_cats(pointer(sccats_c))
-+ I_sc_free_cats(pointer(scatt_conds_c))
-+
-+ output_queue.put((ret, scatts))
-+
-+def _getBandcRange( band_info):
-+ band_c_range = struct_Range()
-+
-+ band_c_range.max = band_info['max']
-+ band_c_range.min = band_info['min']
-+
-+ return band_c_range
-+
-+def _regionToCellHead(region):
-+ cell_head = struct_Cell_head()
-+ G_get_window(pointer(cell_head))
-+
-+ convert_dict = {'n' : 'north', 'e' : 'east',
-+ 'w' : 'west', 's' : 'south',
-+ 'nsres' : 'ns_res',
-+ 'ewres' : 'ew_res'}
-+
-+ for k, v in region.iteritems():
-+ if k in ["rows", "cols", "cells"]:
-+ v = int(v)
-+ else:
-+ v = float(v)
-+
-+ if convert_dict.has_key(k):
-+ k = convert_dict[k]
-+
-+ setattr(cell_head, k, v)
-+
-+ return cell_head
-+
-+def _stringListToCharArr(str_list):
-+
-+ arr = c_char_p * len(str_list)
-+ char_arr = arr()
-+ for i, st in enumerate(str_list):
-+ if st:
-+ char_arr[i] = st
-+ else:
-+ char_arr[i] = None
-+
-+ return char_arr
-+
-+def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
-+
-+ sccats = struct_scCats()
-+ I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type));
-+
-+ refs = []
-+ cats_rasts_core = []
-+
-+ for cat_id, scatt_ids in cats.iteritems():
-+ cat_c_id = I_sc_add_cat(pointer(sccats))
-+ cats_rasts_core.append(cats_rasts[cat_id])
-+
-+ for scatt_id, dt in scatt_ids.iteritems():
-+ # if key is missing condition is always True (full scatter plor is computed)
-+ vals = dt['np_vals']
-+
-+ scatt_vals = scdScattData()
-+
-+ c_void_p = ctypes.POINTER(ctypes.c_void_p)
-+
-+ if cats_type == SC_SCATT_DATA:
-+ vals[:] = 0
-+ elif cats_type == SC_SCATT_CONDITIONS:
-+ pass
-+ else:
-+ return None
-+ data_p = vals.ctypes.data_as(c_void_p)
-+ I_scd_init_scatt_data(pointer(scatt_vals), cats_type, len(vals), data_p)
-+
-+ refs.append(scatt_vals)
-+
-+ I_sc_insert_scatt_data(pointer(sccats),
-+ pointer(scatt_vals),
-+ cat_c_id, scatt_id)
-+
-+ cats_rasts_c = _stringListToCharArr(cats_rasts_core)
-+
-+ return sccats, cats_rasts_c, refs
-+
-+def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
-+ cell_head = _regionToCellHead(region)
-+
-+
-+ ret = I_insert_patch_to_cat_rast(patch_rast,
-+ pointer(cell_head),
-+ cat_rast)
-+
-+ output_queue.put(ret)
-+
-+
-Index: gui/wxpython/scatt_plot/frame.py
-===================================================================
---- gui/wxpython/scatt_plot/frame.py (revision 0)
-+++ gui/wxpython/scatt_plot/frame.py (working copy)
-@@ -0,0 +1,826 @@
-+"""!
-+ at package scatt_plot.dialogs
-+
-+ at brief GUI.
-+
-+Classes:
-+
-+(C) 2013 by the GRASS Development Team
-+
-+This program is free software under the GNU General Public License
-+(>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+import os
-+import sys
-+
-+import wx
-+import wx.lib.scrolledpanel as scrolled
-+import wx.lib.mixins.listctrl as listmix
-+
-+from core import globalvar
-+from core.gcmd import GException, GError, RunCommand
-+
-+from gui_core.gselect import Select
-+from gui_core.dialogs import SetOpacityDialog, SimpleDialog
-+
-+from scatt_plot.controllers import ScattsManager
-+from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
-+from scatt_plot.scatt_core import idScattToidBands
-+from scatt_plot.dialogs import ManageBusyCursorMixin
-+from scatt_plot.plots import ScatterPlotWidget
-+from iclass.dialogs import ContrastColor
-+
-+try:
-+ from agw import aui
-+except ImportError:
-+ import wx.lib.agw.aui as aui
-+
-+
-+class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
-+ def __init__(self, parent, giface, iclass_mapwin = None,
-+ id = wx.ID_ANY):
-+
-+ #wx.SplitterWindow.__init__(self, parent = parent, id = id,
-+ # style = wx.SP_LIVE_UPDATE)
-+ wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
-+ ManageBusyCursorMixin.__init__(self, window=self)
-+
-+ self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface,
-+ iclass_mapwin=iclass_mapwin)
-+
-+ # toobars
-+ self.toolbars = {}
-+ self.toolbars['mainToolbar'] = self._createMainToolbar()
-+ self.toolbars['editingToolbar'] = EditingToolbar(parent = self, scatt_mgr = self.scatt_mgr)
-+
-+ self._createCategoryPanel(self)
-+
-+ self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
-+
-+ self.mainsizer = wx.BoxSizer(wx.VERTICAL)
-+ self.mainsizer.Add(item = self.toolbars['mainToolbar'], proportion = 0, flag = wx.EXPAND)
-+ self.mainsizer.Add(item = self.toolbars['editingToolbar'], proportion = 0, flag = wx.EXPAND)
-+ self.mainsizer.Add(item = self.catsPanel, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT , border = 5)
-+ self.mainsizer.Add(item = self.plot_panel, proportion = 1, flag = wx.EXPAND)
-+
-+ self.catsPanel.Hide()
-+ self.toolbars['editingToolbar'].Hide()
-+
-+ self.SetSizer(self.mainsizer)
-+
-+ self.scatt_mgr.computingStarted.connect(lambda : self.UpdateCur(busy=True))
-+ self.scatt_mgr.renderingStarted.connect(lambda : self.UpdateCur(busy=True))
-+ self.scatt_mgr.renderingFinished.connect(lambda : self.UpdateCur(busy=False))
-+
-+ #self.SetSashGravity(0.5)
-+ #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
-+ self.Layout()
-+
-+ def UpdateCur(self, busy):
-+ self.plot_panel.SetBusy(busy)
-+ ManageBusyCursorMixin.UpdateCur(self, busy)
-+
-+ def _selCatInIScatt(self):
-+ return False
-+
-+ def _createMainToolbar(self):
-+ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr)
-+
-+ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
-+ return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
-+
-+
-+ def NewScatterPlot(self, scatt_id, transpose):
-+ return self.plot_panel.NewScatterPlot(scatt_id, transpose)
-+
-+ def ShowPlotEditingToolbar(self, show):
-+ self.toolbars["editingToolbar"].Show(show)
-+ self.Layout()
-+
-+ def ShowCategoryPanel(self, show):
-+ self.catsPanel.Show(show)
-+
-+ #if show:
-+ # self.SetSashSize(5)
-+ #else:
-+ # self.SetSashSize(0)
-+ self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
-+ self.Layout()
-+
-+ def _createCategoryPanel(self, parent):
-+ self.catsPanel = wx.Panel(parent=parent)
-+ self.cats_list = CategoryListCtrl(parent=self.catsPanel,
-+ cats_mgr=self.scatt_mgr.GetCategoriesManager(),
-+ sel_cats_in_iscatt=self._selCatInIScatt())
-+
-+ self.catsPanel.SetMinSize((-1, 100))
-+ self.catsPanel.SetInitialSize((-1, 150))
-+
-+ box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
-+ label=' %s ' % _("Classes"),)
-+ catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
-+
-+ self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
-+
-+ catsSizer.Add(item=self.cats_list, proportion=1, flag=wx.EXPAND | wx.TOP, border = 5)
-+ if self.toolbars['categoryToolbar']:
-+ catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
-+
-+ self.catsPanel.SetSizer(catsSizer)
-+
-+ def CleanUp(self):
-+ pass
-+
-+ def _createCategoryToolbar(self, parent):
-+ return CategoryToolbar(parent=parent,
-+ scatt_mgr=self.scatt_mgr,
-+ cats_list=self.cats_list)
-+
-+class IScattDialog(wx.Dialog):
-+ def __init__(self, parent, giface, title=_("GRASS GIS Interactive Scatter Plot Tool"),
-+ id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
-+ wx.Dialog.__init__(self, parent, id, style=style, title = title, **kwargs)
-+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-+
-+ self.iscatt_panel = MapDispIScattPanel(self, giface)
-+
-+ mainsizer = wx.BoxSizer(wx.VERTICAL)
-+ mainsizer.Add(item=self.iscatt_panel, proportion=1, flag=wx.EXPAND)
-+
-+ self.SetSizer(mainsizer)
-+
-+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
-+
-+ self.SetMinSize((300, 300))
-+
-+ def OnCloseWindow(self, event):
-+ event.Skip()
-+ #self.
-+
-+class MapDispIScattPanel(IClassIScattPanel):
-+ def __init__(self, parent, giface,
-+ id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-+ IClassIScattPanel.__init__(self, parent=parent, giface=giface,
-+ id=id, style=style, **kwargs)
-+
-+ def _createScattMgr(self, guiparent, giface, iclass_mapwin):
-+ return ScattsManager(guiparent = self, giface = giface)
-+
-+ def _createMainToolbar(self):
-+ return MainToolbar(parent = self, scatt_mgr = self.scatt_mgr, opt_tools=['add_group'])
-+
-+ def _selCatInIScatt(self):
-+ return True
-+
-+class ScatterPlotsPanel(scrolled.ScrolledPanel):
-+ def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
-+
-+ scrolled.ScrolledPanel.__init__(self, parent)
-+ self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
-+
-+ self.scatt_mgr = scatt_mgr
-+
-+ self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
-+
-+ #self._createCategoryPanel()
-+ # Fancy gui
-+ self._mgr = aui.AuiManager(self.mainPanel)
-+ #self._mgr.SetManagedWindow(self)
-+
-+ self._mgr.Update()
-+
-+ self._doLayout()
-+ self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
-+ self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
-+
-+ self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
-+
-+ dlgSize = (-1, 400)
-+ #self.SetBestSize(dlgSize)
-+ #self.SetInitialSize(dlgSize)
-+ self.SetAutoLayout(1)
-+ #fix goutput's pane size (required for Mac OSX)
-+ #if self.gwindow:
-+ # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
-+ self.ignore_scroll = 0
-+ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
-+
-+ self.scatt_i = 1
-+ self.scatt_id_scatt_i = {}
-+ self.transpose = {}
-+ self.scatts = {}
-+
-+ self.Bind(wx.EVT_CLOSE, self.OnClose)
-+
-+ self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
-+
-+ def SetBusy(self, busy):
-+ for scatt in self.scatts.itervalues():
-+ scatt.UpdateCur(busy)
-+
-+ def CursorPlotMove(self, x, y, scatt_id):
-+
-+ try:
-+ x = int(round(x))
-+ y = int(round(y))
-+ coords = True
-+ except:
-+ coords = False
-+
-+ pane = self._getPane(scatt_id)
-+ caption = self._creteCaption(scatt_id)
-+ if coords:
-+ caption += " %d, %d" % (x, y)
-+
-+ pane.Caption(caption)
-+ self._mgr.RefreshCaptions()
-+
-+ def _getPane(self, scatt_id):
-+ scatt_i = self.scatt_id_scatt_i[scatt_id]
-+ name = self._getScatterPlotName(scatt_i)
-+ return self._mgr.GetPane(name)
-+
-+ def ScatterPlotClosed(self, scatt_id):
-+
-+ scatt_i = self.scatt_id_scatt_i[scatt_id]
-+
-+ name = self._getScatterPlotName(scatt_i)
-+ pane = self._mgr.GetPane(name)
-+
-+ del self.scatt_id_scatt_i[scatt_id]
-+ del self.scatts[scatt_id]
-+
-+ if pane.IsOk():
-+ self._mgr.ClosePane(pane)
-+ self._mgr.Update()
-+
-+ def OnMouseWheel(self, event):
-+ #TODO very ugly find some better solution
-+ self.ignore_scroll = 3
-+ event.Skip()
-+
-+ def ScrollChildIntoView(self, child):
-+ #For aui manager it does not work and returns position always to the top -> deactivated.
-+ pass
-+
-+ def OnPlotPaneClosed(self, event):
-+ if isinstance(event.pane.window, ScatterPlotWidget):
-+ event.pane.window.CleanUp()
-+
-+ def OnScrollChanged(self, event):
-+ wx.CallAfter(self.Layout)
-+
-+ def OnScroll(self, event):
-+ if self.ignore_scroll > 0:
-+ self.ignore_scroll -= 1
-+ else:
-+ event.Skip()
-+
-+ #wx.CallAfter(self._mgr.Update)
-+ #wx.CallAfter(self.Layout)
-+
-+ def _doLayout(self):
-+
-+ mainsizer = wx.BoxSizer(wx.VERTICAL)
-+ mainsizer.Add(item = self.mainPanel, proportion = 1, flag = wx.EXPAND)
-+ self.SetSizer(mainsizer)
-+
-+ self.Layout()
-+ self.SetupScrolling()
-+
-+ def OnClose(self, event):
-+ """!Close dialog"""
-+ #TODO
-+ print "closed"
-+ self.scatt_mgr.CleanUp()
-+ self.Destroy()
-+
-+ def OnSettings(self, event):
-+ pass
-+
-+ def _newScatterPlotName(self, scatt_id):
-+ name = self._getScatterPlotName(self.scatt_i)
-+ self.scatt_id_scatt_i[scatt_id] = self.scatt_i
-+ self.scatt_i += 1
-+ return name
-+
-+ def _getScatterPlotName(self, i):
-+ name = "scatter plot %d" % i
-+ return name
-+
-+ def NewScatterPlot(self, scatt_id, transpose):
-+ #TODO needs to be resolved (should be in this class)
-+
-+ scatt = ScatterPlotWidget(parent = self.mainPanel,
-+ scatt_mgr = self.scatt_mgr,
-+ scatt_id = scatt_id,
-+ transpose = transpose)
-+ scatt.plotClosed.connect(self.ScatterPlotClosed)
-+ self.transpose[scatt_id] = transpose
-+
-+ caption = self._creteCaption(scatt_id)
-+ self._mgr.AddPane(scatt,
-+ aui.AuiPaneInfo().Dockable(True).Floatable(True).
-+ Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
-+ Caption(caption).
-+ Center().Position(1).MaximizeButton(True).
-+ MinimizeButton(True).CaptionVisible(True).
-+ CloseButton(True).Layer(0))
-+
-+ self._mgr.Update()
-+
-+ self.SetVirtualSize(self.GetBestVirtualSize())
-+ self.Layout()
-+
-+ self.scatts[scatt_id] = scatt
-+
-+ return scatt
-+
-+ def _creteCaption(self, scatt_id):
-+
-+ transpose = self.transpose[scatt_id]
-+ bands = self.scatt_mgr.GetBands()
-+
-+ #TODO too low level
-+ b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
-+
-+ x_b = bands[b1_id].split('@')[0]
-+ y_b = bands[b2_id].split('@')[0]
-+
-+ if transpose:
-+ tmp = x_b
-+ x_b = y_b
-+ y_b = tmp
-+
-+ return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
-+
-+ def GetScattMgr(self):
-+ return self.scatt_mgr
-+
-+class CategoryListCtrl(wx.ListCtrl,
-+ listmix.ListCtrlAutoWidthMixin):
-+ #listmix.TextEditMixin):
-+
-+ def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
-+
-+ wx.ListCtrl.__init__(self, parent, id,
-+ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
-+ wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
-+ self.columns = ((_('Class name'), 'name'), )
-+ #(_('Color'), 'color'))
-+
-+ self.sel_cats_in_iscatt = sel_cats_in_iscatt
-+
-+ self.Populate(columns = self.columns)
-+
-+ self.cats_mgr = cats_mgr
-+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+
-+ self.rightClickedItemIdx = wx.NOT_FOUND
-+
-+ listmix.ListCtrlAutoWidthMixin.__init__(self)
-+
-+ #listmix.TextEditMixin.__init__(self)
-+
-+ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
-+ self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) #wxGTK
-+
-+ #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
-+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
-+
-+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+ self.cats_mgr.deletedCategory.connect(self.Update)
-+ self.cats_mgr.addedCategory.connect(self.Update)
-+
-+ def Update(self, **kwargs):
-+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def InitCoreCats(self):
-+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def SetVirtualData(self, row, column, text):
-+ attr = self.columns[column][1]
-+ if attr == 'name':
-+ try:
-+ text.encode('ascii')
-+ except UnicodeEncodeError:
-+ GMessage(parent = self, message = _("Please use only ASCII characters."))
-+ return
-+
-+ cat_id = self.cats_mgr.GetCategories()[row]
-+
-+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
-+ self.cats_mgr.SetCategoryAttrs(cat_id, {attr : text})
-+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+
-+ self.Select(row)
-+
-+ def Populate(self, columns):
-+ for i, col in enumerate(columns):
-+ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
-+
-+ #self.SetColumnWidth(0, 100)
-+ #self.SetColumnWidth(1, 100)
-+
-+ def AddCategory(self):
-+
-+ self.cats_mgr.addedCategory.disconnect(self.Update)
-+ cat_id = self.cats_mgr.AddCategory()
-+ self.cats_mgr.addedCategory.connect(self.Update)
-+
-+ if cat_id < 0:
-+ GError(_("Maximum limit of categories number was reached."))
-+ return
-+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+
-+ def DeleteCategory(self):
-+ indexList = sorted(self.GetSelectedIndices(), reverse = True)
-+ cats = []
-+ for i in indexList:
-+ # remove temporary raster
-+ cat_id = self.cats_mgr.GetCategories()[i]
-+
-+ cats.append(cat_id)
-+
-+ self.cats_mgr.deletedCategory.disconnect(self.Update)
-+ self.cats_mgr.DeleteCategory(cat_id)
-+ self.cats_mgr.deletedCategory.connect(self.Update)
-+
-+ self.SetItemCount(len(self.cats_mgr.GetCategories()))
-+
-+ def OnSel(self, event):
-+ if self.sel_cats_in_iscatt:
-+ indexList = self.GetSelectedIndices()
-+ sel_cats = []
-+ cats = self.cats_mgr.GetCategories()
-+ for i in indexList:
-+ sel_cats.append(cats[i])
-+
-+ if sel_cats:
-+ self.cats_mgr.SetSelectedCat(sel_cats[0])
-+ event.Skip()
-+
-+ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
-+ indices = []
-+ lastFound = -1
-+ while True:
-+ index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
-+ if index == -1:
-+ break
-+ else:
-+ lastFound = index
-+ indices.append(index)
-+ return indices
-+
-+ def DeselectAll(self):
-+ """!Deselect all items"""
-+ indexList = self.GetSelectedIndices()
-+ for i in indexList:
-+ self.Select(i, on = 0)
-+
-+ # no highlight
-+ self.OnCategorySelected(None)
-+
-+ def OnGetItemText(self, item, col):
-+ attr = self.columns[col][1]
-+ cat_id = self.cats_mgr.GetCategories()[item]
-+
-+ return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
-+
-+ def OnGetItemImage(self, item):
-+ return -1
-+
-+ def OnGetItemAttr(self, item):
-+ cat_id = self.cats_mgr.GetCategories()[item]
-+ c = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
-+
-+ back_c = wx.Colour(*map(int, c.split(':')))
-+ text_c = wx.Colour(*ContrastColor(back_c))
-+
-+ # if it is in scope of the method, gui falls, using self solved it
-+ self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
-+ return self.l
-+
-+ def OnCategoryRightUp(self, event):
-+ """!Show context menu on right click"""
-+ item, flags = self.HitTest((event.GetX(), event.GetY()))
-+ if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
-+ self.rightClickedItemIdx = item
-+
-+ # generate popup-menu
-+ cat_idx = self.rightClickedItemIdx
-+
-+ cats = self.cats_mgr.GetCategories()
-+ cat_id = cats[cat_idx]
-+ showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
-+
-+ menu = wx.Menu()
-+
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move to top"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
-+ if cat_idx == 0:
-+ menu.Enable(item_id, False)
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move to bottom"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
-+ if cat_idx == len(cats) - 1:
-+ menu.Enable(item_id, False)
-+
-+ menu.AppendSeparator()
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move category up"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
-+ if cat_idx == 0:
-+ menu.Enable(item_id, False)
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move category down"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
-+ if cat_idx == len(cats) - 1:
-+ menu.Enable(item_id, False)
-+
-+ menu.AppendSeparator()
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Export class raster"))
-+ self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
-+
-+ menu.AppendSeparator()
-+
-+ if showed:
-+ text = _("Hide")
-+ else:
-+ text = _("Show")
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text = text)
-+ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
-+ attrs={'show' : not showed}),
-+ id=item_id)
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Rename class"))
-+ self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Set color"))
-+ self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
-+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Change opacity level"))
-+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
-+
-+
-+
-+ self.PopupMenu(menu)
-+ menu.Destroy()
-+
-+ def OnExportCatRast(self, event):
-+ """!Export training areas"""
-+ #TODO
-+ # GMessage(parent=self, message=_("No class raster to export."))
-+ # return
-+
-+ cat_idx = self.rightClickedItemIdx
-+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+
-+ self.cats_mgr.ExportCatRast(cat_id)
-+
-+ def OnMoveUp(self, event):
-+ cat_idx = self.rightClickedItemIdx
-+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+ self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def OnMoveDown(self, event):
-+ cat_idx = self.rightClickedItemIdx
-+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+ self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def OnMoveTop(self, event):
-+ cat_idx = self.rightClickedItemIdx
-+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+ self.cats_mgr.ChangePosition(cat_id, 0)
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def OnMoveBottom(self, event):
-+ cat_idx = self.rightClickedItemIdx
-+ cats = self.cats_mgr.GetCategories()
-+ cat_id = cats[cat_idx]
-+ self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
-+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
-+
-+ def OnSetColor(self, event):
-+ """!Popup opacity level indicator"""
-+ cat_idx = self.rightClickedItemIdx
-+ cat_id = self.cats_mgr.GetCategories()[cat_idx]
-+
-+ col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
-+ col = map(int, col.split(':'))
-+
-+ col_data = wx.ColourData()
-+ col_data.SetColour(wx.Colour(*col))
-+
-+ dlg = wx.ColourDialog(self, col_data)
-+ dlg.GetColourData().SetChooseFull(True)
-+
-+ if dlg.ShowModal() == wx.ID_OK:
-+ color = dlg.GetColourData().GetColour().Get()
-+ color = ':'.join(map(str, color))
-+ self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
-+
-+ dlg.Destroy()
-+
-+ def OnPopupOpacityLevel(self, event):
-+ """!Popup opacity level indicator"""
-+
-+ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
-+ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+ value = cat_attrs['opacity'] * 100
-+ name = cat_attrs['name']
-+
-+ dlg = SetOpacityDialog(self, opacity = value,
-+ title = _("Change opacity of class <%s>" % name))
-+
-+ dlg.applyOpacity.connect(lambda value:
-+ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value}))
-+ dlg.CentreOnParent()
-+
-+ if dlg.ShowModal() == wx.ID_OK:
-+ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
-+
-+ dlg.Destroy()
-+
-+ def OnRename(self, event):
-+ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
-+ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+
-+ dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
-+ dlg.CentreOnParent()
-+
-+ while True:
-+ if dlg.ShowModal() == wx.ID_OK:
-+ name = dlg.GetNewName().strip()
-+ if not name:
-+ GMessage(parent=self, message=_("Empty name was inserted."))
-+ else:
-+ self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
-+ break
-+ else:
-+ break
-+
-+ dlg.Destroy()
-+
-+ def _setCatAttrs(self, cat_id, attrs):
-+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
-+ self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
-+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
-+
-+class RenameClassDialog(SimpleDialog):
-+ def __init__(self, parent, old_name, title=("Change class name")):
-+ SimpleDialog.__init__(self, parent, title)
-+
-+ self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY)
-+ self.name.SetValue(old_name)
-+
-+ self.dataSizer.Add(self.name, proportion = 0,
-+ flag = wx.EXPAND | wx.ALL, border = 1)
-+
-+ self.panel.SetSizer(self.sizer)
-+ self.name.SetMinSize((200, -1))
-+ self.sizer.Fit(self)
-+
-+ def GetNewName(self):
-+ return self.name.GetValue()
-+
-+class AddScattPlotDialog(wx.Dialog):
-+
-+ def __init__(self, parent, bands, id = wx.ID_ANY):
-+
-+ wx.Dialog.__init__(self, parent, title = ("Add scatter plot"), id = id)
-+
-+ self.bands = bands
-+
-+ self.scatt_id = None
-+
-+ self._createWidgets()
-+
-+ def _createWidgets(self):
-+
-+ self.labels = {}
-+ self.params = {}
-+
-+ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("x axis:"))
-+
-+ self.band_1_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
-+ choices = self.bands,
-+ style = wx.CB_READONLY, size = (350, 30))
-+
-+ self.band_2_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("y axis:"))
-+
-+ self.band_2_ch = wx.ComboBox(parent = self, id = wx.ID_ANY,
-+ choices = self.bands,
-+ style = wx.CB_READONLY, size = (350, 30))
-+
-+ # buttons
-+ self.btn_close = wx.Button(parent = self, id = wx.ID_CANCEL)
-+
-+ self.btn_ok = wx.Button(parent = self, id = wx.ID_OK, label = _("&OK"))
-+
-+ self._layout()
-+
-+ def _layout(self):
-+
-+ border = wx.BoxSizer(wx.VERTICAL)
-+ dialogSizer = wx.BoxSizer(wx.VERTICAL)
-+
-+ regionSizer = wx.BoxSizer(wx.HORIZONTAL)
-+
-+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_1_label,
-+ sel = self.band_1_ch))
-+
-+ dialogSizer.Add(item = self._addSelectSizer(title = self.band_2_label,
-+ sel = self.band_2_ch))
-+
-+ # buttons
-+ self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
-+
-+ self.btnsizer.Add(item = self.btn_close, proportion = 0,
-+ flag = wx.ALL | wx.ALIGN_CENTER,
-+ border = 10)
-+
-+ self.btnsizer.Add(item = self.btn_ok, proportion = 0,
-+ flag = wx.ALL | wx.ALIGN_CENTER,
-+ border = 10)
-+
-+ dialogSizer.Add(item = self.btnsizer, proportion = 0,
-+ flag = wx.ALIGN_CENTER)
-+
-+ border.Add(item = dialogSizer, proportion = 0,
-+ flag = wx.ALL, border = 5)
-+
-+ self.SetSizer(border)
-+ self.Layout()
-+ self.Fit()
-+
-+ # bindings
-+ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
-+ self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
-+
-+ def _addSelectSizer(self, title, sel):
-+ """!Helper layout function.
-+ """
-+ selSizer = wx.BoxSizer(orient = wx.VERTICAL)
-+
-+ selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
-+ selTitleSizer.Add(item = title, proportion = 1,
-+ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
-+
-+ selSizer.Add(item = selTitleSizer, proportion = 0,
-+ flag = wx.EXPAND)
-+
-+ selSizer.Add(item = sel, proportion = 1,
-+ flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
-+ border = 5)
-+
-+ return selSizer
-+
-+ def OnClose(self, event):
-+ """!Close dialog
-+ """
-+ if not self.IsModal():
-+ self.Destroy()
-+ event.Skip()
-+
-+ def OnOk(self, event):
-+ """!
-+ """
-+ band_1 = self.band_1_ch.GetSelection()
-+ band_2 = self.band_2_ch.GetSelection()
-+
-+ if band_1 == band_2:
-+ GError(_("Selected bands must be different."))
-+ return
-+
-+ #TODO axes selection
-+ if band_1 > band_2:
-+ tmp_band = band_2
-+ band_2 = band_1
-+ band_1 = band_2
-+
-+ self.scatt_id = idBandsToidScatt(band_1, band_2, len(self.bands))
-+
-+ event.Skip()
-+
-+ def GetScattId(self):
-+ return self.scatt_id
-+
Index: gui/wxpython/vnet/dialogs.py
===================================================================
---- gui/wxpython/vnet/dialogs.py (revision 57760)
+--- gui/wxpython/vnet/dialogs.py (revision 57820)
+++ gui/wxpython/vnet/dialogs.py (working copy)
@@ -109,7 +109,7 @@
@@ -6825,7 +6354,7 @@
self.statusItems = []
Index: gui/wxpython/vnet/toolbars.py
===================================================================
---- gui/wxpython/vnet/toolbars.py (revision 57760)
+--- gui/wxpython/vnet/toolbars.py (revision 57820)
+++ gui/wxpython/vnet/toolbars.py (working copy)
@@ -19,9 +19,9 @@
@@ -6841,7 +6370,7 @@
class PointListToolbar(BaseToolbar):
Index: gui/wxpython/Makefile
===================================================================
---- gui/wxpython/Makefile (revision 57760)
+--- gui/wxpython/Makefile (revision 57820)
+++ gui/wxpython/Makefile (working copy)
@@ -13,7 +13,7 @@
$(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
@@ -6863,18 +6392,3 @@
DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
default: $(DSTFILES)
-Index: gui/icons/grass/polygon.png
-===================================================================
-Cannot display: file marked as a binary type.
-svn:mime-type = application/octet-stream
-Index: gui/icons/grass/polygon.png
-===================================================================
---- gui/icons/grass/polygon.png (revision 57760)
-+++ gui/icons/grass/polygon.png (working copy)
-
-Property changes on: gui/icons/grass/polygon.png
-___________________________________________________________________
-Added: svn:mime-type
-## -0,0 +1 ##
-+application/octet-stream
-\ No newline at end of property
More information about the grass-commit
mailing list