[GRASS-SVN] r57604 - sandbox/turek/scatter_plot
svn_grass at osgeo.org
svn_grass at osgeo.org
Fri Sep 6 08:20:42 PDT 2013
Author: turek
Date: 2013-09-06 08:20:41 -0700 (Fri, 06 Sep 2013)
New Revision: 57604
Modified:
sandbox/turek/scatter_plot/README.txt
sandbox/turek/scatter_plot/testing_patch.diff
Log:
scatter plot bug fixing
Modified: sandbox/turek/scatter_plot/README.txt
===================================================================
--- sandbox/turek/scatter_plot/README.txt 2013-09-06 13:22:05 UTC (rev 57603)
+++ sandbox/turek/scatter_plot/README.txt 2013-09-06 15:20:41 UTC (rev 57604)
@@ -13,6 +13,18 @@
-> should not crash if input is empty (above: * is missing from lsat pattern name_ pat="lsat7_2000_*")
+-----------------
+NEEDS TO BE DONE:
+
+- change layers order #DONE
+- ellipses managment
+- flip of digitized area on scatter plot
+- synch whith digitizer
+- statusbar showing information about computations and coordinates
+- scatter plot settings
+- check for range of data, if big show warning
+- better temp files clean up
+- comment code
------------------
TODO suggestions:
Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff 2013-09-06 13:22:05 UTC (rev 57603)
+++ sandbox/turek/scatter_plot/testing_patch.diff 2013-09-06 15:20:41 UTC (rev 57604)
@@ -1,44 +1,3 @@
-Index: include/defs/vedit.h
-===================================================================
---- include/defs/vedit.h (revision 57601)
-+++ 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 57601)
-+++ include/defs/imagery.h (working copy)
-@@ -110,6 +110,23 @@
- FILE *I_fopen_subgroup_ref_new(const char *, const char *);
- FILE *I_fopen_subgroup_ref_old(const char *, const char *);
-
-+/* scatt_plt.c */
-+void I_sc_init_cats(struct scCats *, int, int);
-+void I_sc_free_cats(struct scCats *);
-+int I_sc_add_cat(struct scCats *);
-+int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
-+
-+void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
-+
-+int I_compute_scatts(struct Cell_head *, struct scCats *, 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 *);
-+
- /* sig.c */
- int I_init_signatures(struct Signature *, int);
- int I_new_signature(struct Signature *);
Index: include/imagery.h
===================================================================
--- include/imagery.h (revision 57601)
@@ -100,6 +59,47 @@
#define SIGNATURE_TYPE_MIXED 1
#define GROUPFILE "CURGROUP"
+Index: include/defs/vedit.h
+===================================================================
+--- include/defs/vedit.h (revision 57601)
++++ 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 57601)
++++ include/defs/imagery.h (working copy)
+@@ -110,6 +110,23 @@
+ FILE *I_fopen_subgroup_ref_new(const char *, const char *);
+ FILE *I_fopen_subgroup_ref_old(const char *, const char *);
+
++/* scatt_plt.c */
++void I_sc_init_cats(struct scCats *, int, int);
++void I_sc_free_cats(struct scCats *);
++int I_sc_add_cat(struct scCats *);
++int I_sc_insert_scatt_data(struct scCats *, struct scdScattData *, int, int);
++
++void I_scd_init_scatt_data(struct scdScattData *, int, int, void *);
++
++int I_compute_scatts(struct Cell_head *, struct scCats *, 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 *);
++
+ /* sig.c */
+ int I_init_signatures(struct Signature *, int);
+ int I_new_signature(struct Signature *);
Index: gui/icons/grass/polygon.png
===================================================================
Cannot display: file marked as a binary type.
@@ -115,1714 +115,6 @@
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
-Index: gui/wxpython/iclass/dialogs.py
-===================================================================
---- gui/wxpython/iclass/dialogs.py (revision 57601)
-+++ gui/wxpython/iclass/dialogs.py (working copy)
-@@ -333,13 +333,19 @@
- toolbar.SetCategories(catNames = catNames, catIdx = cats)
- if name in catNames:
- toolbar.choice.SetStringSelection(name)
-+ cat = toolbar.GetSelectedCategoryIdx()
- elif catNames:
- toolbar.choice.SetSelection(0)
--
-+ cat = toolbar.GetSelectedCategoryIdx()
-+ else:
-+ cat = None
-+
- if toolbar.choice.IsEmpty():
- toolbar.EnableControls(False)
- else:
- toolbar.EnableControls(True)
-+
-+ self.mapWindow.CategoryChanged(cat)
- # don't forget to update maps, histo, ...
-
- def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
-Index: gui/wxpython/iclass/toolbars.py
-===================================================================
---- gui/wxpython/iclass/toolbars.py (revision 57601)
-+++ gui/wxpython/iclass/toolbars.py (working copy)
-@@ -46,9 +46,7 @@
- 'importAreas' : MetaIcon(img = 'layer-import',
- label = _('Import training areas from vector map')),
- 'addRgb' : MetaIcon(img = 'layer-rgb-add',
-- label = _('Add RGB map layer')),
-- 'scatt_plot' : MetaIcon(img = 'layer-raster-analyze',
-- label = _('Open Scatter Plot Tool (EXPERIMENTAL GSoC 2013)')),
-+ label = _('Add RGB map layer'))
- }
-
- class IClassMapToolbar(BaseToolbar):
-@@ -117,10 +115,7 @@
- ("zoomBack", icons["zoomBack"],
- self.parent.OnZoomBack),
- ("zoomToMap", icons["zoomExtent"],
-- self.parent.OnZoomToMap),
-- (None, ),
-- ("scatt_plot", iClassIcons["scatt_plot"],
-- self.parent.OnScatterplot)
-+ self.parent.OnZoomToMap)
- ))
- class IClassToolbar(BaseToolbar):
- """!IClass toolbar
-@@ -156,7 +151,7 @@
- """!Toolbar data"""
- icons = iClassIcons
- return self._getToolbarData((("selectGroup", icons['selectGroup'],
-- self.parent.OnAddBands),
-+ lambda event : self.parent.AddBands()),
- (None, ),
- ("classManager", icons['classManager'],
- self.parent.OnCategoryManager),
-Index: gui/wxpython/iclass/frame.py
-===================================================================
---- gui/wxpython/iclass/frame.py (revision 57601)
-+++ gui/wxpython/iclass/frame.py (working copy)
-@@ -64,6 +64,8 @@
- IClassExportAreasDialog, IClassMapDialog
- from iclass.plots import PlotPanel
-
-+from grass.pydispatch.signal import Signal
-+
- class IClassMapFrame(DoubleMapFrame):
- """! wxIClass main frame
-
-@@ -114,6 +116,10 @@
- lambda:
- self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
- self.SetSize(size)
-+
-+ self.groupSet = Signal("IClassMapFrame.groupSet")
-+ self.categoryChanged = Signal('IClassMapFrame.categoryChanged')
-+
- #
- # Add toolbars
- #
-@@ -177,7 +183,7 @@
- self.dialogs['category'] = None
-
- # PyPlot init
-- self.plotPanel = PlotPanel(self, stats_data = self.stats_data)
-+ self.plotPanel = PlotPanel(self, giface = self._giface, stats_data = self.stats_data)
-
- self._addPanes()
- self._mgr.Update()
-@@ -237,7 +243,7 @@
- return False
-
- return vectorName
--
-+
- def RemoveTempVector(self):
- """!Removes temporary vector map with training areas"""
- ret = RunCommand(prog = 'g.remove',
-@@ -477,21 +483,47 @@
-
- self.Render(self.GetFirstWindow())
-
-- def OnAddBands(self, event):
-+ def AddBands(self):
- """!Add imagery group"""
- dlg = IClassGroupDialog(self, group = self.group)
-- if dlg.ShowModal() == wx.ID_OK:
-- self.SetGroup(dlg.GetGroup())
-+
-+ while True:
-+ if dlg.ShowModal() == wx.ID_OK:
-+ if self.SetGroup(dlg.GetGroup()):
-+ break
-+ else:
-+ break
-+
- dlg.Destroy()
-
- def SetGroup(self, name):
- """!Set imagery group"""
- group = grass.find_file(name = name, element = 'group')
- if group['name']:
-+ if not self.GroupData(group['name']):
-+ GError(_("No data found in group <%s>.\n" \
-+ "Please create subgroup with same name as group and add the data there.") \
-+ % name, parent = self)
-+ return False
- self.group = group['name']
-+ self.groupSet.emit(group = group['name'])
- else:
- GError(_("Group <%s> not found") % name, parent = self)
--
-+ return False
-+
-+ return True
-+
-+ def GroupData(self, group):
-+ res = RunCommand('i.group',
-+ flags = 'g',
-+ group = group, subgroup = group,
-+ read = True).strip()
-+ bands = None
-+ if res.split('\n')[0]:
-+ bands = res.split('\n')
-+
-+ return bands
-+
- def OnImportAreas(self, event):
- """!Import training areas"""
- # check if we have any changes
-@@ -768,17 +800,20 @@
-
- Updates number of stddev, histograms, layer in preview display.
- """
-- stat = self.stats_data.GetStatistics(currentCat)
-- nstd = stat.nstd
-- self.toolbars['iClass'].UpdateStddev(nstd)
--
-- self.plotPanel.UpdateCategory(currentCat)
-- self.plotPanel.OnPlotTypeSelected(None)
-+ if currentCat:
-+ stat = self.stats_data.GetStatistics(currentCat)
-+ nstd = stat.nstd
-+ self.toolbars['iClass'].UpdateStddev(nstd)
-+
-+ self.plotPanel.UpdateCategory(currentCat)
-+ self.plotPanel.OnPlotTypeSelected(None)
-
-- name = stat.rasterName
-- name = self.previewMapManager.GetAlias(name)
-- if name:
-- self.previewMapManager.SelectLayer(name)
-+ name = stat.rasterName
-+ name = self.previewMapManager.GetAlias(name)
-+ if name:
-+ self.previewMapManager.SelectLayer(name)
-+
-+ self.categoryChanged.emit(cat = currentCat)
-
- def DeleteAreas(self, cats):
- """!Removes all training areas of given categories
-@@ -1105,27 +1140,6 @@
- self.GetFirstWindow().SetModePointer()
- self.GetSecondWindow().SetModePointer()
-
-- def OnScatterplot(self, event):
-- """!Init interactive scatterplot tools
-- """
-- if self.dialogs['scatt_plot']:
-- self.dialogs['scatt_plot'].Raise()
-- return
--
-- try:
-- from scatt_plot.dialogs import ScattPlotMainDialog
-- except:
-- GError(parent = self, message = _("The Scatter Plot Tool is not installed."))
-- return
--
-- self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface, iclass_mapwin = self.GetFirstWindow())
--
-- scatt_mgr = self.dialogs['scatt_plot'].GetScattMgr()
-- scatt_mgr.DigitDataChanged(self.toolbars['vdigit'].mapLayer.GetName(), self.GetFirstWindow().GetDigit())
--
-- self.dialogs['scatt_plot'].CenterOnScreen()
-- self.dialogs['scatt_plot'].Show()
--
- class MapManager:
- """! Class for managing map renderer.
-
-Index: gui/wxpython/iclass/plots.py
-===================================================================
---- gui/wxpython/iclass/plots.py (revision 57601)
-+++ gui/wxpython/iclass/plots.py (working copy)
-@@ -19,6 +19,7 @@
- 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.
-@@ -28,7 +29,7 @@
- for each band and for one category. Coincidence plots show min max range
- of classes for each band.
- """
-- def __init__(self, parent, stats_data):
-+ def __init__(self, parent, giface, stats_data):
- scrolled.ScrolledPanel.__init__(self, parent)
-
- self.SetupScrolling(scroll_x = False, scroll_y = True)
-@@ -38,26 +39,71 @@
- self.stats_data = stats_data
- self.currentCat = None
-
-+ self._giface = giface
-+
- 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")])
-+ _("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 IClassScatterPlotsPanel
-+ self.scatt_plot_panel = IClassScatterPlotsPanel(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():
-@@ -66,7 +112,10 @@
- self.DrawHistograms(stat)
- else:
- self.DrawCoincidencePlots()
--
-+
-+ self.Layout()
-+
-+
- def StddevChanged(self):
- """!Standard deviation multiplier changed, redraw histograms"""
- if self.plotSwitch.GetSelection() == 0:
-@@ -89,7 +138,7 @@
- panel.Destroy()
-
- self.canvasList = []
--
-+
- def ClearPlots(self):
- """!Clears plot canvases"""
- for bandIdx in range(len(self.bandList)):
-@@ -104,15 +153,15 @@
- def CreatePlotCanvases(self):
- """!Create plot canvases according to the number of bands"""
- for band in self.bandList:
-- canvas = plot.PlotCanvas(self)
-+ canvas = plot.PlotCanvas(self.canvasPanel)
- canvas.SetMinSize((-1, 140))
- canvas.SetFontSizeTitle(10)
- canvas.SetFontSizeAxis(8)
- self.canvasList.append(canvas)
-
-- self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
--
-- self.SetVirtualSize(self.GetBestVirtualSize())
-+ self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
-+
-+ self.SetVirtualSize(self.GetBestVirtualSize())
- self.Layout()
-
- def UpdatePlots(self, group, currentCat, stats_data):
-@@ -138,7 +187,7 @@
-
- def UpdateCategory(self, cat):
- self.currentCat = cat
--
-+
- def DrawCoincidencePlots(self):
- """!Draw coincidence plots"""
- for bandIdx in range(len(self.bandList)):
-Index: gui/wxpython/mapdisp/toolbars.py
-===================================================================
---- gui/wxpython/mapdisp/toolbars.py (revision 57601)
-+++ gui/wxpython/mapdisp/toolbars.py (working copy)
-@@ -239,7 +239,8 @@
- (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["scatter"], self.parent.OnScatterplot2)))
-
- def OnDecoration(self, event):
- """!Decorations overlay menu
-Index: gui/wxpython/mapdisp/frame.py
-===================================================================
---- gui/wxpython/mapdisp/frame.py (revision 57601)
-+++ gui/wxpython/mapdisp/frame.py (working copy)
-@@ -225,6 +225,7 @@
- #
- self.dialogs = {}
- self.dialogs['attributes'] = None
-+ self.dialogs['scatt_plot'] = None
- self.dialogs['category'] = None
- self.dialogs['barscale'] = None
- self.dialogs['legend'] = None
-@@ -1168,6 +1169,19 @@
- """!Returns toolbar with zooming tools"""
- return self.toolbars['map']
-
-+ def OnScatterplot2(self, event):
-+ """!Init interactive scatterplot tools
-+ """
-+ if self.dialogs['scatt_plot']:
-+ self.dialogs['scatt_plot'].Raise()
-+ return
-+
-+ from scatt_plot.dialogs import ScattPlotMainDialog
-+ self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface)
-+
-+ self.dialogs['scatt_plot'].CenterOnScreen()
-+ self.dialogs['scatt_plot'].Show()
-+
- def OnVNet(self, event):
- """!Dialog for v.net* modules
- """
-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,134 @@
-+"""!
-+ 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
-+
-+import wx
-+from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
-+
-+class gThread(threading.Thread):
-+ """!Thread for GRASS commands"""
-+ requestId = 0
-+
-+ def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
-+ """!
-+ @param receiver event receiver (used in PostEvent)
-+ """
-+ 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.receiver = receiver
-+ self._want_abort_all = False
-+
-+ 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', 'onPrepare', '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
-+ if self.receiver:
-+ event = wxCmdPrepare(type = 'method',
-+ time=requestTime,
-+ pid=requestId)
-+
-+ wx.PostEvent(self.receiver, event)
-+
-+ # run command
-+ event = wxCmdRun(type = 'method',
-+ pid=requestId)
-+
-+ wx.PostEvent(self.receiver, event)
-+
-+ time.sleep(.1)
-+
-+ ret = None
-+ exception = None
-+
-+ #to
-+ #try:
-+ ret = vars()['callable'](*args, **kwds)
-+ #except Exception as e:
-+ # exception = e;
-+
-+ self.resultQ.put((requestId, ret))
-+
-+ time.sleep(.1)
-+
-+ if self.receiver:
-+ event = wxCmdDone(kwds=kwds,
-+ args=args, #TODO expand args to kwds
-+ ret=ret,
-+ exception=exception,
-+ userdata=vars()['userdata'],
-+ pid=requestId)
-+
-+ # send event
-+ wx.PostEvent(self.receiver, 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
-\ 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,149 @@
-+"""!
-+ 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
-+from scatt_plot.scatt_core import idBandsToidScatt
-+
-+from core.gcmd import GError
-+
-+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.x_band = None
-+ self.y_band = None
-+
-+ self._createWidgets()
-+
-+ def _createWidgets(self):
-+
-+ self.labels = {}
-+ self.params = {}
-+
-+ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("%s axis:" % "x"))
-+
-+ 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 = _("%s axis:" % "y"))
-+
-+ 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):
-+ """!
-+ """
-+ b_x = self.band_1_ch.GetSelection()
-+ b_y = self.band_2_ch.GetSelection()
-+
-+ err = True
-+
-+ if b_x < 0 or b_y < 0:
-+ GError(_("Select both x and y bands."))
-+ elif b_y == b_x:
-+ GError(_("Selected bands must be different."))
-+ else:
-+ err = False
-+
-+ if err:
-+ self.band_y = None
-+ self.band_x = None
-+ return
-+
-+ self.band_y = b_y
-+ self.band_x = b_x
-+
-+ event.Skip()
-+
-+ def GetBands(self):
-+ return (self.band_x, self.band_y)
-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,216 @@
-+"""!
-+ at package scatt_plot.toolbars
-+
-+ at brief Scatter plot - toolbars
-+
-+Classes:
-+ - toolbars::MainToolbar
-+
-+(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 wx
-+
-+from icons.icon import MetaIcon
-+from gui_core.toolbars import BaseToolbar, BaseIcons
-+from core.gcmd import RunCommand
-+from core.gcmd import GException, GError, RunCommand
-+from scatt_plot.scatt_core import idBandsToidScatt
-+
-+
-+class MainToolbar(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):
-+
-+ icons = {
-+ 'settings' : BaseIcons['settings'].SetLabel( _('Ssettings')),
-+ 'help' : MetaIcon(img = 'help',
-+ label = _('Show manual')),
-+ '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'))
-+ }
-+
-+ return self._getToolbarData((
-+ ('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)
-+ #('settings', icon["settings"],
-+ # self.parent.OnSettings),
-+ #('help', icons["help"],
-+ # self.OnHelp),
-+ ))
-+
-+ 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)
-+
-+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 = _('Edit line/boundary'),
-+ 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 = _('Edit line/boundary'),
-+ desc = _('Delete polygon')),
-+ }
-+
-+ return self._getToolbarData((
-+ ("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_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),
-+ ('delete', self.icons['delete'],
-+ lambda event: self.SetMode(event, 'delete'),
-+ wx.ITEM_CHECK)
-+ ))
-+
-+ 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)
-+
-+ def ModeSet(self, mode):
-+
-+ 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]
-Index: gui/wxpython/scatt_plot/scatt_core.py
-===================================================================
---- gui/wxpython/scatt_plot/scatt_core.py (revision 0)
-+++ gui/wxpython/scatt_plot/scatt_core.py (working copy)
-@@ -0,0 +1,779 @@
-+"""!
-+ at package scatt_plot.scatt_plot
-+
-+ at brief Non GUI functions.
-+
-+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 numpy as np
-+#TODO
-+from matplotlib.path import Path
-+
-+from math import sqrt, ceil, floor
-+from copy import deepcopy
-+from scipy.signal import convolve2d
-+
-+from core.gcmd import GException, GError, RunCommand
-+
-+import grass.script as grass
-+
-+from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
-+ SC_SCATT_DATA, SC_SCATT_CONDITIONS
-+
-+class Core:
-+ 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):
-+ 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 AddScattPlot(self, scatt_id):
-+
-+ self.scatts_dt.AddScattPlot(scatt_id = scatt_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
-+ #arr.flush()
-+ #del arr
-+
-+ self.ComputeCatsScatts([cat_id])
-+ #print "time"
-+ #print time.clock() - start_time
-+ 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()))
-+
-+ b1_info = self.an_data.GetBandInfo(b1)
-+ b2_info = self.an_data.GetBandInfo(b2)
-+
-+ raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
-+
-+ 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 _validExtend(self, val):
-+ #TODO do it general
-+ if val > 255:
-+ val = 255
-+ elif val < 0:
-+ val = 0
-+
-+ return val
-+
-+class CatRastUpdater:
-+
-+ 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 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.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):
-+
-+ region = self.an_data.GetRegion()
-+ new_region = {}
-+
-+ if bbox["maxy"] <= region["s"]:
-+ return 0
-+ elif bbox["maxy"] >= region["n"]:
-+ new_region["n"] = bbox["maxy"]
-+ else:
-+ new_region["n"] = ceil((bbox["maxy"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
-+
-+ if bbox["miny"] >= region["n"]:
-+ return 0
-+ elif bbox["miny"] <= region["s"]:
-+ new_region["s"] = bbox["miny"]
-+ else:
-+ new_region["s"] = floor((bbox["miny"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
-+
-+ if bbox["maxx"] <= region["w"]:
-+ return 0
-+ elif bbox["maxx"] >= region["e"]:
-+ new_region["e"] = bbox["maxx"]
-+ else:
-+ new_region["e"] = ceil((bbox["maxx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
-+
-+ if bbox["minx"] >= region["e"]:
-+ return 0
-+ elif bbox["minx"] <= region["w"]:
-+ new_region["w"] = bbox["minx"]
-+ else:
-+ new_region["w"] = floor((bbox["minx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
-+
-+ #TODO check regions resolutin
-+ new_region["nsres"] = region["nsres"]
-+ new_region["ewres"] = region["ewres"]
-+
-+ return {"GRASS_REGION" : grass.region_env(**new_region)}
-+
-+class AnalyzedData:
-+
-+ 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:
-+
-+ 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):
-+
-+ 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]
-+
-+ grass.try_remove(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):
-+ if scatt_id not in self.scatts_ids:
-+ return False
-+
-+ scatts = {}
-+ for cat_id in self.cats.iterkeys():
-+ if cat_id == 0:
-+ continue
-+ scatts[cat_id] = self._getEllipse(cat_id, scatt_id)
-+
-+ return scatts
-+
-+ def _getEllipse(self, cat_id, scatt_id):
-+ # Joe Kington
-+ # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
-+
-+ nstd = 2
-+ data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
-+
-+ 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, y_avg])
-+
-+ 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:
-+ grass.try_remove(tmp)
-+ for tmp in self.cats_rasts:
-+ grass.try_remove(tmp)
-+
-+ self.cats_rasts = {}
-+ self.cats_rasts_conds = {}
-+
-+
-+ def GetCatRast(self, cat_id):
-+ return self.cats_rasts[cat_id]
-+
-+ 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
-+
-+
-+def RasterizePolygon(pol, height, width):
-+
-+ # 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 + 1
-+ ny = height + 1
-+
-+ x, y = np.meshgrid(np.arange(nx), np.arange(ny))
-+ x, y = x.flatten(), y.flatten()
-+
-+ points = np.vstack((x,y)).T
-+
-+ p = Path(pol)
-+ grid = p.contains_points(points)
-+ grid = grid.reshape((ny,nx))
-+
-+ 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')
-+
-+
-+ #TODO this part is very inefficient, replace it with better solution
-+ #for (y, x), value in np.ndenumerate(grid):
-+
-+ # if x >= width - 1: continue
-+ # if y >= height - 1: continue
-+
-+ # if grid[y, x]:
-+ # raster[y, x] = 1
-+ # raster[y + 1, x] = 1
-+ # raster[y, x + 1] = 1
-+ # raster[y + 1, x + 1] = 1
-+
-+ return raster
-+
-+#TODO move to utils?
-+def idScattToidBands(scatt_id, n_bands):
-+
-+ 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):
-+
-+ if band_2_id < band_1_id:
-+ tmp = band_1
-+ 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
-+
Index: gui/wxpython/scatt_plot/core_c.py
===================================================================
--- gui/wxpython/scatt_plot/core_c.py (revision 0)
@@ -2002,7 +294,7 @@
===================================================================
--- gui/wxpython/scatt_plot/frame.py (revision 0)
+++ gui/wxpython/scatt_plot/frame.py (working copy)
-@@ -0,0 +1,567 @@
+@@ -0,0 +1,582 @@
+"""!
+ at package scatt_plot.dialogs
+
@@ -2386,8 +678,10 @@
+ self.rightClickedItemIdx = item
+
+ # generate popup-menu
++ cat_idx = self.rightClickedItemIdx
+
-+ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++ cats = self.cats_mgr.GetCategories()
++ cat_id = cats[cat_idx]
+ showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
+
+ menu = wx.Menu()
@@ -2399,22 +693,23 @@
+
+ item_id = wx.NewId()
+ menu.Append(item_id, text = text)
-+ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
++ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
+ attrs={'show' : not showed}),
-+ id=item_id)
-+
++ id=item_id)
+ menu.AppendSeparator()
-+ """
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move item up"))
-+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++
++ if cat_idx != 0:
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category up"))
++ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move item down"))
-+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
++ if cat_idx != len(cats) - 1:
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category down"))
++ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
+
+ menu.AppendSeparator()
-+ """
++
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Change opacity level"))
+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
@@ -2422,6 +717,18 @@
+ self.PopupMenu(menu)
+ menu.Destroy()
+
++ 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 OnPopupOpacityLevel(self, event):
+ """!Popup opacity level indicator"""
+
@@ -2590,7 +897,7 @@
===================================================================
--- gui/wxpython/scatt_plot/plots.py (revision 0)
+++ gui/wxpython/scatt_plot/plots.py (working copy)
-@@ -0,0 +1,824 @@
+@@ -0,0 +1,851 @@
+"""!
+ at package scatt_plot.dialogs
+
@@ -2784,7 +1091,7 @@
+ self.SetSizer(self.main_sizer)
+ self.main_sizer.Fit(self)
+
-+ def Plot(self, scatts, ellipses, styles):
++ def Plot(self, cats_order, scatts, ellipses, styles):
+ """ Redraws the figure
+ """
+
@@ -2799,7 +1106,7 @@
+ c = None
+
+ q = Queue()
-+ p = Process(target=MergeImg, args=(scatts, styles, self.transpose, q))
++ p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
+ p.start()
+ merged_img, self.full_extend = q.get()
+ p.join()
@@ -2815,9 +1122,15 @@
+ callafter_list.append([self.axes.draw_artist, [img]])
+ callafter_list.append([grass.try_remove, [merged_img.filename]])
+
-+ for cat_id, e in ellipses.iteritems():
-+ if cat_id == 0 or not e:
++ for cat_id in cats_order:
++ if cat_id == 0:
+ continue
++ if not ellipses.has_key(cat_id):
++ continue
++
++ e = ellipses[cat_id]
++ if not e:
++ continue
+
+ colors = styles[cat_id]['color'].split(":")
+ if self.transpose:
@@ -2901,6 +1214,9 @@
+ x2 = deepcopy(self.zoom_rect_coords['x'])
+ y2 = deepcopy(self.zoom_rect_coords['y'])
+
++ if x1 == x2 or y1 == y2:
++ return
++
+ self.axes.set_xlim(x1, x2)#, auto = True)
+ self.axes.set_ylim(y1, y2)#, auto = True)
+ self.canvas.draw()
@@ -3344,11 +1660,21 @@
+
+ return im
+
-+def MergeImg(scatts, styles, transpose, output_queue):
++def MergeImg(cats_order, scatts, styles, transpose, output_queue):
+
++ start_time = time.clock()
++
++
++ start_time = time.clock()
++ #cmap_time = 0
++
++
+ init = True
+ merge_tmp = grass.tempfile()
-+ for cat_id, scatt in scatts.iteritems():
++ 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 \
@@ -3389,7 +1715,10 @@
+ vmax = np.amax(masked_cat)
+ masked_cat = masked_cat / float(vmax)
+
++ #tmp = time.clock()
+ colored_cat = np.uint8(cmap(masked_cat) * 255)
++ #cmap_time += time.clock() + tmp
++
+ del masked_cat
+ del cmap
+
@@ -3413,6 +1742,11 @@
+ del c_img_a
+
+ del colored_cat
++
++ #end_time = time.clock() - start_time
++ #print "all time:%f" % (end_time)
++ #print "cmap_time:%f" % (end_time / cmap_time * 100.0 )
++
+ output_queue.put((merged_img, full_extend))
+ #return merged_img, full_extend
\ No newline at end of file
@@ -3420,7 +1754,7 @@
===================================================================
--- gui/wxpython/scatt_plot/controllers.py (revision 0)
+++ gui/wxpython/scatt_plot/controllers.py (working copy)
-@@ -0,0 +1,685 @@
+@@ -0,0 +1,708 @@
+"""!
+ at package scatt_plot.controllers
+
@@ -3648,7 +1982,6 @@
+
+ def RenderScattPlts(self, scatt_ids = None):
+ if len(self.tasks_pids['render']) > 1:
-+ print "skip"
+ return
+
+ self.tasks_pids['render'].append(self.thread.GetId())
@@ -3656,6 +1989,9 @@
+
+ def _renderscattplts(self, scatt_ids):
+ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++ cats = self.cats_mgr.GetCategories()[:]
++ cats.reverse()
++ cats.insert(0, 0)
+ for i_scatt_id, scatt in self.plots.items():
+ if scatt_ids is not None and i_scatt_id not in scatt_ids:
+ continue
@@ -3664,11 +2000,11 @@
+ ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id)
+
+ if self.pol_sel_mode[0]:
-+ self._getSelectedAreas(i_scatt_id, scatt_dt, cats_attrs)
++ self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
+
-+ scatt.Plot(scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++ scatt.Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
+
-+ def _getSelectedAreas(self, scatt_id, scatt_dt, cats_attrs):
++ def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
+
+ cat_id = self.cats_mgr.GetSelectedCat()
+ if not cat_id:
@@ -3679,6 +2015,8 @@
+ s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
+ if not s:
+ return
++
++ cats_order.append(sel_a_cat_id)
+ cats_attrs[sel_a_cat_id] = {'color' : "255:255:0",
+ 'opacity' : 0.7,
+ 'show' : True}
@@ -3851,6 +2189,25 @@
+ 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
++
++ 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.RenderScattPlts()
++ return True
++
+ def AddCategory(self, cat_id = None, name = None, color = None):
+
+ if cat_id is None:
@@ -3873,7 +2230,7 @@
+ 'show' : True
+ }
+
-+ self.cats_ids.append(cat_id)
++ self.cats_ids.insert(0, cat_id)
+
+ if name is not None:
+ self.cats[cat_id]["name"] = name
@@ -4107,30 +2464,1305 @@
+ bands = res.split('\n')
+ self.scatt_mgr.SetData(bands)
\ No newline at end of file
-Index: gui/wxpython/Makefile
+Index: gui/wxpython/scatt_plot/gthreading.py
===================================================================
---- gui/wxpython/Makefile (revision 57601)
-+++ gui/wxpython/Makefile (working copy)
-@@ -13,7 +13,7 @@
- $(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
- gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
- mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* vdigit/* \
-- vnet/*.py web_services/*.py wxplot/*.py) \
-+ vnet/*.py web_services/*.py wxplot/*.py scatt_plot/*.py) \
- gis_set.py gis_set_error.py wxgui.py README
-
- DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) \
-@@ -21,8 +21,9 @@
-
- PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
- gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
-- mapswipe vdigit wxplot web_services rlisetup vnet timeline)
-+ mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
-
+--- gui/wxpython/scatt_plot/gthreading.py (revision 0)
++++ gui/wxpython/scatt_plot/gthreading.py (working copy)
+@@ -0,0 +1,134 @@
++"""!
++ at package scatt_plot.gthreading
+
- DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
-
- default: $(DSTFILES)
++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
++
++import wx
++from core.gconsole import wxCmdRun, wxCmdDone, wxCmdPrepare
++
++class gThread(threading.Thread):
++ """!Thread for GRASS commands"""
++ requestId = 0
++
++ def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
++ """!
++ @param receiver event receiver (used in PostEvent)
++ """
++ 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.receiver = receiver
++ self._want_abort_all = False
++
++ 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', 'onPrepare', '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
++ if self.receiver:
++ event = wxCmdPrepare(type = 'method',
++ time=requestTime,
++ pid=requestId)
++
++ wx.PostEvent(self.receiver, event)
++
++ # run command
++ event = wxCmdRun(type = 'method',
++ pid=requestId)
++
++ wx.PostEvent(self.receiver, event)
++
++ time.sleep(.1)
++
++ ret = None
++ exception = None
++
++ #to
++ #try:
++ ret = vars()['callable'](*args, **kwds)
++ #except Exception as e:
++ # exception = e;
++
++ self.resultQ.put((requestId, ret))
++
++ time.sleep(.1)
++
++ if self.receiver:
++ event = wxCmdDone(kwds=kwds,
++ args=args, #TODO expand args to kwds
++ ret=ret,
++ exception=exception,
++ userdata=vars()['userdata'],
++ pid=requestId)
++
++ # send event
++ wx.PostEvent(self.receiver, 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
+\ 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,149 @@
++"""!
++ 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
++from scatt_plot.scatt_core import idBandsToidScatt
++
++from core.gcmd import GError
++
++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.x_band = None
++ self.y_band = None
++
++ self._createWidgets()
++
++ def _createWidgets(self):
++
++ self.labels = {}
++ self.params = {}
++
++ self.band_1_label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("%s axis:" % "x"))
++
++ 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 = _("%s axis:" % "y"))
++
++ 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):
++ """!
++ """
++ b_x = self.band_1_ch.GetSelection()
++ b_y = self.band_2_ch.GetSelection()
++
++ err = True
++
++ if b_x < 0 or b_y < 0:
++ GError(_("Select both x and y bands."))
++ elif b_y == b_x:
++ GError(_("Selected bands must be different."))
++ else:
++ err = False
++
++ if err:
++ self.band_y = None
++ self.band_x = None
++ return
++
++ self.band_y = b_y
++ self.band_x = b_x
++
++ event.Skip()
++
++ def GetBands(self):
++ return (self.band_x, self.band_y)
+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,216 @@
++"""!
++ at package scatt_plot.toolbars
++
++ at brief Scatter plot - toolbars
++
++Classes:
++ - toolbars::MainToolbar
++
++(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 wx
++
++from icons.icon import MetaIcon
++from gui_core.toolbars import BaseToolbar, BaseIcons
++from core.gcmd import RunCommand
++from core.gcmd import GException, GError, RunCommand
++from scatt_plot.scatt_core import idBandsToidScatt
++
++
++class MainToolbar(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):
++
++ icons = {
++ 'settings' : BaseIcons['settings'].SetLabel( _('Ssettings')),
++ 'help' : MetaIcon(img = 'help',
++ label = _('Show manual')),
++ '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'))
++ }
++
++ return self._getToolbarData((
++ ('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)
++ #('settings', icon["settings"],
++ # self.parent.OnSettings),
++ #('help', icons["help"],
++ # self.OnHelp),
++ ))
++
++ 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)
++
++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 = _('Edit line/boundary'),
++ 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 = _('Edit line/boundary'),
++ desc = _('Delete polygon')),
++ }
++
++ return self._getToolbarData((
++ ("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_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),
++ ('delete', self.icons['delete'],
++ lambda event: self.SetMode(event, 'delete'),
++ wx.ITEM_CHECK)
++ ))
++
++ 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)
++
++ def ModeSet(self, mode):
++
++ 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]
+Index: gui/wxpython/scatt_plot/scatt_core.py
+===================================================================
+--- gui/wxpython/scatt_plot/scatt_core.py (revision 0)
++++ gui/wxpython/scatt_plot/scatt_core.py (working copy)
+@@ -0,0 +1,779 @@
++"""!
++ at package scatt_plot.scatt_plot
++
++ at brief Non GUI functions.
++
++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 numpy as np
++#TODO
++from matplotlib.path import Path
++
++from math import sqrt, ceil, floor
++from copy import deepcopy
++from scipy.signal import convolve2d
++
++from core.gcmd import GException, GError, RunCommand
++
++import grass.script as grass
++
++from core_c import CreateCatRast, ComputeScatts, UpdateCatRast, \
++ SC_SCATT_DATA, SC_SCATT_CONDITIONS
++
++class Core:
++ 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):
++ 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 AddScattPlot(self, scatt_id):
++
++ self.scatts_dt.AddScattPlot(scatt_id = scatt_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
++ #arr.flush()
++ #del arr
++
++ self.ComputeCatsScatts([cat_id])
++ #print "time"
++ #print time.clock() - start_time
++ 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()))
++
++ b1_info = self.an_data.GetBandInfo(b1)
++ b2_info = self.an_data.GetBandInfo(b2)
++
++ raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
++
++ 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 _validExtend(self, val):
++ #TODO do it general
++ if val > 255:
++ val = 255
++ elif val < 0:
++ val = 0
++
++ return val
++
++class CatRastUpdater:
++
++ 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 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.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):
++
++ region = self.an_data.GetRegion()
++ new_region = {}
++
++ if bbox["maxy"] <= region["s"]:
++ return 0
++ elif bbox["maxy"] >= region["n"]:
++ new_region["n"] = bbox["maxy"]
++ else:
++ new_region["n"] = ceil((bbox["maxy"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
++
++ if bbox["miny"] >= region["n"]:
++ return 0
++ elif bbox["miny"] <= region["s"]:
++ new_region["s"] = bbox["miny"]
++ else:
++ new_region["s"] = floor((bbox["miny"] - region["s"]) / region["nsres"]) * region["nsres"] + region["s"]
++
++ if bbox["maxx"] <= region["w"]:
++ return 0
++ elif bbox["maxx"] >= region["e"]:
++ new_region["e"] = bbox["maxx"]
++ else:
++ new_region["e"] = ceil((bbox["maxx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
++
++ if bbox["minx"] >= region["e"]:
++ return 0
++ elif bbox["minx"] <= region["w"]:
++ new_region["w"] = bbox["minx"]
++ else:
++ new_region["w"] = floor((bbox["minx"] - region["w"]) / region["ewres"]) * region["ewres"] + region["w"]
++
++ #TODO check regions resolutin
++ new_region["nsres"] = region["nsres"]
++ new_region["ewres"] = region["ewres"]
++
++ return {"GRASS_REGION" : grass.region_env(**new_region)}
++
++class AnalyzedData:
++
++ 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:
++
++ 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):
++
++ 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]
++
++ grass.try_remove(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):
++ if scatt_id not in self.scatts_ids:
++ return False
++
++ scatts = {}
++ for cat_id in self.cats.iterkeys():
++ if cat_id == 0:
++ continue
++ scatts[cat_id] = self._getEllipse(cat_id, scatt_id)
++
++ return scatts
++
++ def _getEllipse(self, cat_id, scatt_id):
++ # Joe Kington
++ # http://stackoverflow.com/questions/12301071/multidimensional-confidence-intervals
++
++ nstd = 2
++ data = np.copy(self.cats[cat_id][scatt_id]['np_vals'])
++
++ 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, y_avg])
++
++ 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:
++ grass.try_remove(tmp)
++ for tmp in self.cats_rasts:
++ grass.try_remove(tmp)
++
++ self.cats_rasts = {}
++ self.cats_rasts_conds = {}
++
++
++ def GetCatRast(self, cat_id):
++ return self.cats_rasts[cat_id]
++
++ 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
++
++
++def RasterizePolygon(pol, height, width):
++
++ # 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 + 1
++ ny = height + 1
++
++ x, y = np.meshgrid(np.arange(nx), np.arange(ny))
++ x, y = x.flatten(), y.flatten()
++
++ points = np.vstack((x,y)).T
++
++ p = Path(pol)
++ grid = p.contains_points(points)
++ grid = grid.reshape((ny,nx))
++
++ 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')
++
++
++ #TODO this part is very inefficient, replace it with better solution
++ #for (y, x), value in np.ndenumerate(grid):
++
++ # if x >= width - 1: continue
++ # if y >= height - 1: continue
++
++ # if grid[y, x]:
++ # raster[y, x] = 1
++ # raster[y + 1, x] = 1
++ # raster[y, x + 1] = 1
++ # raster[y + 1, x + 1] = 1
++
++ return raster
++
++#TODO move to utils?
++def idScattToidBands(scatt_id, n_bands):
++
++ 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):
++
++ if band_2_id < band_1_id:
++ tmp = band_1
++ 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
++
Index: gui/wxpython/vdigit/wxdigit.py
===================================================================
--- gui/wxpython/vdigit/wxdigit.py (revision 57601)
@@ -4680,21 +4312,439 @@
return True
def StopEditing(self):
-Index: lib/vector/Vlib/open.c
+Index: gui/wxpython/Makefile
===================================================================
---- lib/vector/Vlib/open.c (revision 57601)
-+++ lib/vector/Vlib/open.c (working copy)
-@@ -240,7 +240,9 @@
+--- gui/wxpython/Makefile (revision 57601)
++++ gui/wxpython/Makefile (working copy)
+@@ -13,7 +13,7 @@
+ $(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
+ gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
+ mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* vdigit/* \
+- vnet/*.py web_services/*.py wxplot/*.py) \
++ vnet/*.py web_services/*.py wxplot/*.py scatt_plot/*.py) \
+ gis_set.py gis_set_error.py wxgui.py README
+
+ DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) \
+@@ -21,8 +21,9 @@
+
+ PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
+ gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
+- mapswipe vdigit wxplot web_services rlisetup vnet timeline)
++ mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
+
++
+ DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
+
+ default: $(DSTFILES)
+Index: gui/wxpython/mapdisp/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py (revision 57601)
++++ gui/wxpython/mapdisp/frame.py (working copy)
+@@ -225,6 +225,7 @@
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
++ self.dialogs['scatt_plot'] = None
+ self.dialogs['category'] = None
+ self.dialogs['barscale'] = None
+ self.dialogs['legend'] = None
+@@ -1168,6 +1169,19 @@
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['map']
+
++ def OnScatterplot2(self, event):
++ """!Init interactive scatterplot tools
++ """
++ if self.dialogs['scatt_plot']:
++ self.dialogs['scatt_plot'].Raise()
++ return
++
++ from scatt_plot.dialogs import ScattPlotMainDialog
++ self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface)
++
++ self.dialogs['scatt_plot'].CenterOnScreen()
++ self.dialogs['scatt_plot'].Show()
++
+ def OnVNet(self, event):
+ """!Dialog for v.net* modules
+ """
+Index: gui/wxpython/mapdisp/toolbars.py
+===================================================================
+--- gui/wxpython/mapdisp/toolbars.py (revision 57601)
++++ gui/wxpython/mapdisp/toolbars.py (working copy)
+@@ -239,7 +239,8 @@
+ (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["scatter"], self.parent.OnScatterplot2)))
+
+ def OnDecoration(self, event):
+ """!Decorations overlay menu
+Index: gui/wxpython/iclass/frame.py
+===================================================================
+--- gui/wxpython/iclass/frame.py (revision 57601)
++++ gui/wxpython/iclass/frame.py (working copy)
+@@ -64,6 +64,8 @@
+ IClassExportAreasDialog, IClassMapDialog
+ from iclass.plots import PlotPanel
+
++from grass.pydispatch.signal import Signal
++
+ class IClassMapFrame(DoubleMapFrame):
+ """! wxIClass main frame
+
+@@ -114,6 +116,10 @@
+ lambda:
+ self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
+ self.SetSize(size)
++
++ self.groupSet = Signal("IClassMapFrame.groupSet")
++ self.categoryChanged = Signal('IClassMapFrame.categoryChanged')
++
+ #
+ # Add toolbars
+ #
+@@ -177,7 +183,7 @@
+ self.dialogs['category'] = None
+
+ # PyPlot init
+- self.plotPanel = PlotPanel(self, stats_data = self.stats_data)
++ self.plotPanel = PlotPanel(self, giface = self._giface, stats_data = self.stats_data)
+
+ self._addPanes()
+ self._mgr.Update()
+@@ -237,7 +243,7 @@
+ return False
+
+ return vectorName
+-
++
+ def RemoveTempVector(self):
+ """!Removes temporary vector map with training areas"""
+ ret = RunCommand(prog = 'g.remove',
+@@ -477,21 +483,47 @@
+
+ self.Render(self.GetFirstWindow())
+
+- def OnAddBands(self, event):
++ def AddBands(self):
+ """!Add imagery group"""
+ dlg = IClassGroupDialog(self, group = self.group)
+- if dlg.ShowModal() == wx.ID_OK:
+- self.SetGroup(dlg.GetGroup())
++
++ while True:
++ if dlg.ShowModal() == wx.ID_OK:
++ if self.SetGroup(dlg.GetGroup()):
++ break
++ else:
++ break
++
+ dlg.Destroy()
+
+ def SetGroup(self, name):
+ """!Set imagery group"""
+ group = grass.find_file(name = name, element = 'group')
+ if group['name']:
++ if not self.GroupData(group['name']):
++ GError(_("No data found in group <%s>.\n" \
++ "Please create subgroup with same name as group and add the data there.") \
++ % name, parent = self)
++ return False
+ self.group = group['name']
++ self.groupSet.emit(group = group['name'])
+ else:
+ GError(_("Group <%s> not found") % name, parent = self)
+-
++ return False
++
++ return True
++
++ def GroupData(self, group):
++ res = RunCommand('i.group',
++ flags = 'g',
++ group = group, subgroup = group,
++ read = True).strip()
++ bands = None
++ if res.split('\n')[0]:
++ bands = res.split('\n')
++
++ return bands
++
+ def OnImportAreas(self, event):
+ """!Import training areas"""
+ # check if we have any changes
+@@ -768,17 +800,20 @@
+
+ Updates number of stddev, histograms, layer in preview display.
+ """
+- stat = self.stats_data.GetStatistics(currentCat)
+- nstd = stat.nstd
+- self.toolbars['iClass'].UpdateStddev(nstd)
+-
+- self.plotPanel.UpdateCategory(currentCat)
+- self.plotPanel.OnPlotTypeSelected(None)
++ if currentCat:
++ stat = self.stats_data.GetStatistics(currentCat)
++ nstd = stat.nstd
++ self.toolbars['iClass'].UpdateStddev(nstd)
++
++ self.plotPanel.UpdateCategory(currentCat)
++ self.plotPanel.OnPlotTypeSelected(None)
+
+- name = stat.rasterName
+- name = self.previewMapManager.GetAlias(name)
+- if name:
+- self.previewMapManager.SelectLayer(name)
++ name = stat.rasterName
++ name = self.previewMapManager.GetAlias(name)
++ if name:
++ self.previewMapManager.SelectLayer(name)
++
++ self.categoryChanged.emit(cat = currentCat)
+
+ def DeleteAreas(self, cats):
+ """!Removes all training areas of given categories
+@@ -1105,27 +1140,6 @@
+ self.GetFirstWindow().SetModePointer()
+ self.GetSecondWindow().SetModePointer()
+
+- def OnScatterplot(self, event):
+- """!Init interactive scatterplot tools
+- """
+- if self.dialogs['scatt_plot']:
+- self.dialogs['scatt_plot'].Raise()
+- return
+-
+- try:
+- from scatt_plot.dialogs import ScattPlotMainDialog
+- except:
+- GError(parent = self, message = _("The Scatter Plot Tool is not installed."))
+- return
+-
+- self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface, iclass_mapwin = self.GetFirstWindow())
+-
+- scatt_mgr = self.dialogs['scatt_plot'].GetScattMgr()
+- scatt_mgr.DigitDataChanged(self.toolbars['vdigit'].mapLayer.GetName(), self.GetFirstWindow().GetDigit())
+-
+- self.dialogs['scatt_plot'].CenterOnScreen()
+- self.dialogs['scatt_plot'].Show()
+-
+ class MapManager:
+ """! Class for managing map renderer.
+
+Index: gui/wxpython/iclass/plots.py
+===================================================================
+--- gui/wxpython/iclass/plots.py (revision 57601)
++++ gui/wxpython/iclass/plots.py (working copy)
+@@ -19,6 +19,7 @@
+ 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.
+@@ -28,7 +29,7 @@
+ for each band and for one category. Coincidence plots show min max range
+ of classes for each band.
+ """
+- def __init__(self, parent, stats_data):
++ def __init__(self, parent, giface, stats_data):
+ scrolled.ScrolledPanel.__init__(self, parent)
+
+ self.SetupScrolling(scroll_x = False, scroll_y = True)
+@@ -38,26 +39,71 @@
+ self.stats_data = stats_data
+ self.currentCat = None
+
++ self._giface = giface
++
+ 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")])
++ _("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 IClassScatterPlotsPanel
++ self.scatt_plot_panel = IClassScatterPlotsPanel(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():
+@@ -66,7 +112,10 @@
+ self.DrawHistograms(stat)
+ else:
+ self.DrawCoincidencePlots()
+-
++
++ self.Layout()
++
++
+ def StddevChanged(self):
+ """!Standard deviation multiplier changed, redraw histograms"""
+ if self.plotSwitch.GetSelection() == 0:
+@@ -89,7 +138,7 @@
+ panel.Destroy()
+
+ self.canvasList = []
+-
++
+ def ClearPlots(self):
+ """!Clears plot canvases"""
+ for bandIdx in range(len(self.bandList)):
+@@ -104,15 +153,15 @@
+ def CreatePlotCanvases(self):
+ """!Create plot canvases according to the number of bands"""
+ for band in self.bandList:
+- canvas = plot.PlotCanvas(self)
++ canvas = plot.PlotCanvas(self.canvasPanel)
+ canvas.SetMinSize((-1, 140))
+ canvas.SetFontSizeTitle(10)
+ canvas.SetFontSizeAxis(8)
+ self.canvasList.append(canvas)
+
+- self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+-
+- self.SetVirtualSize(self.GetBestVirtualSize())
++ self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
++
++ self.SetVirtualSize(self.GetBestVirtualSize())
+ self.Layout()
+
+ def UpdatePlots(self, group, currentCat, stats_data):
+@@ -138,7 +187,7 @@
+
+ def UpdateCategory(self, cat):
+ self.currentCat = cat
+-
++
+ def DrawCoincidencePlots(self):
+ """!Draw coincidence plots"""
+ for bandIdx in range(len(self.bandList)):
+Index: gui/wxpython/iclass/dialogs.py
+===================================================================
+--- gui/wxpython/iclass/dialogs.py (revision 57601)
++++ gui/wxpython/iclass/dialogs.py (working copy)
+@@ -333,13 +333,19 @@
+ toolbar.SetCategories(catNames = catNames, catIdx = cats)
+ if name in catNames:
+ toolbar.choice.SetStringSelection(name)
++ cat = toolbar.GetSelectedCategoryIdx()
+ elif catNames:
+ toolbar.choice.SetSelection(0)
+-
++ cat = toolbar.GetSelectedCategoryIdx()
++ else:
++ cat = None
++
+ if toolbar.choice.IsEmpty():
+ toolbar.EnableControls(False)
+ else:
+ toolbar.EnableControls(True)
++
++ self.mapWindow.CategoryChanged(cat)
+ # don't forget to update maps, histo, ...
+
+ def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
+Index: gui/wxpython/iclass/toolbars.py
+===================================================================
+--- gui/wxpython/iclass/toolbars.py (revision 57601)
++++ gui/wxpython/iclass/toolbars.py (working copy)
+@@ -46,9 +46,7 @@
+ 'importAreas' : MetaIcon(img = 'layer-import',
+ label = _('Import training areas from vector map')),
+ 'addRgb' : MetaIcon(img = 'layer-rgb-add',
+- label = _('Add RGB map layer')),
+- 'scatt_plot' : MetaIcon(img = 'layer-raster-analyze',
+- label = _('Open Scatter Plot Tool (EXPERIMENTAL GSoC 2013)')),
++ label = _('Add RGB map layer'))
}
- else {
- char file_path[GPATH_MAX];
--
-+ /* reduce to current mapset if search path was set */
-+ if(strcmp(Map->mapset, "") == 0)
-+ Map->mapset = G_store(G_mapset());
- /* temporary map: reduce to current mapset if search path
- * was set */
- if (strcmp(Map->mapset, "") == 0)
+
+ class IClassMapToolbar(BaseToolbar):
+@@ -117,10 +115,7 @@
+ ("zoomBack", icons["zoomBack"],
+ self.parent.OnZoomBack),
+ ("zoomToMap", icons["zoomExtent"],
+- self.parent.OnZoomToMap),
+- (None, ),
+- ("scatt_plot", iClassIcons["scatt_plot"],
+- self.parent.OnScatterplot)
++ self.parent.OnZoomToMap)
+ ))
+ class IClassToolbar(BaseToolbar):
+ """!IClass toolbar
+@@ -156,7 +151,7 @@
+ """!Toolbar data"""
+ icons = iClassIcons
+ return self._getToolbarData((("selectGroup", icons['selectGroup'],
+- self.parent.OnAddBands),
++ lambda event : self.parent.AddBands()),
+ (None, ),
+ ("classManager", icons['classManager'],
+ self.parent.OnCategoryManager),
Index: lib/imagery/scatt_sccats.c
===================================================================
--- lib/imagery/scatt_sccats.c (revision 0)
@@ -5857,3 +5907,18 @@
+ return 0;
+}
\ No newline at end of file
+Index: lib/vector/Vlib/open.c
+===================================================================
+--- lib/vector/Vlib/open.c (revision 57601)
++++ lib/vector/Vlib/open.c (working copy)
+@@ -240,7 +240,9 @@
+ }
+ else {
+ char file_path[GPATH_MAX];
+-
++ /* reduce to current mapset if search path was set */
++ if(strcmp(Map->mapset, "") == 0)
++ Map->mapset = G_store(G_mapset());
+ /* temporary map: reduce to current mapset if search path
+ * was set */
+ if (strcmp(Map->mapset, "") == 0)
More information about the grass-commit
mailing list