[GRASS-SVN] r57583 - sandbox/turek/scatter_plot
svn_grass at osgeo.org
svn_grass at osgeo.org
Mon Sep 2 08:01:50 PDT 2013
Author: turek
Date: 2013-09-02 08:01:50 -0700 (Mon, 02 Sep 2013)
New Revision: 57583
Modified:
sandbox/turek/scatter_plot/testing_patch.diff
Log:
scatter plot: bug fixies
Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff 2013-09-02 14:47:48 UTC (rev 57582)
+++ sandbox/turek/scatter_plot/testing_patch.diff 2013-09-02 15:01:50 UTC (rev 57583)
@@ -1,47 +1,6 @@
-Index: include/defs/imagery.h
-===================================================================
---- include/defs/imagery.h (revision 57556)
-+++ 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/defs/vedit.h
-===================================================================
---- include/defs/vedit.h (revision 57556)
-+++ 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/imagery.h
===================================================================
---- include/imagery.h (revision 57556)
+--- include/imagery.h (revision 57581)
+++ include/imagery.h (working copy)
@@ -135,6 +135,56 @@
@@ -100,6 +59,2345 @@
#define SIGNATURE_TYPE_MIXED 1
#define GROUPFILE "CURGROUP"
+Index: include/defs/vedit.h
+===================================================================
+--- include/defs/vedit.h (revision 57581)
++++ 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 57581)
++++ 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/wxpython/vdigit/wxdigit.py
+===================================================================
+--- gui/wxpython/vdigit/wxdigit.py (revision 57581)
++++ gui/wxpython/vdigit/wxdigit.py (working copy)
+@@ -17,7 +17,7 @@
+ (and NumPy would be an excellent candidate for acceleration via
+ e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+
+-(C) 2007-2011 by the GRASS Development Team
++(C) 2007-2011, 2013 by the GRASS Development Team
+
+ This program is free software under the GNU General Public License
+ (>=v2). Read the file COPYING that comes with GRASS for details.
+@@ -27,6 +27,8 @@
+
+ import grass.script.core as grass
+
++from grass.pydispatch.signal import Signal
++
+ from core.gcmd import GError
+ from core.debug import Debug
+ from core.settings import UserSettings
+@@ -176,7 +178,21 @@
+
+ if self.poMapInfo:
+ self.InitCats()
+-
++
++ self.emit_signals = False
++
++ # signals which describes features changes during digitization,
++ # activate them using EmitSignals method
++ #TODO signal for errors?
++ self.featureAdded = Signal('IVDigit.featureAdded')
++ self.areasDeleted = Signal('IVDigit.areasDeleted')
++ self.vertexMoved = Signal('IVDigit.vertexMoved')
++ self.vertexAdded = Signal('IVDigit.vertexAdded')
++ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
++ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
++ self.featuresMoved = Signal('IVDigit.featuresMoved')
++ self.lineEdited = Signal('IVDigit.lineEdited')
++
+ def __del__(self):
+ Debug.msg(1, "IVDigit.__del__()")
+ Vect_destroy_line_struct(self.poPoints)
+@@ -188,7 +204,12 @@
+ Vect_close(self.poBgMapInfo)
+ self.poBgMapInfo = self.popoBgMapInfo = None
+ del self.bgMapInfo
+-
++
++ def EmitSignals(self, emit):
++ """!Activate/deactivate signals which describes features changes during digitization.
++ """
++ self.emit_signals = emit
++
+ def CloseBackgroundMap(self):
+ """!Close background vector map"""
+ if not self.poBgMapInfo:
+@@ -394,7 +415,6 @@
+
+ @return tuple (number of added features, feature ids)
+ """
+-
+ layer = self._getNewFeaturesLayer()
+ cat = self._getNewFeaturesCat()
+
+@@ -419,10 +439,14 @@
+ return (-1, None)
+
+ self.toolbar.EnableUndo()
+-
+- return self._addFeature(vtype, points, layer, cat,
+- self._getSnapMode(), self._display.GetThreshold())
+-
++
++ ret = self._addFeature(vtype, points, layer, cat,
++ self._getSnapMode(), self._display.GetThreshold())
++ if ret[0] > -1 and self.emit_signals:
++ self.featureAdded.emit(new_bboxs = [self._createBbox(points)], new_areas_cats = [[{layer : [cat]}, None]])
++
++ return ret
++
+ def DeleteSelectedLines(self):
+ """!Delete selected features
+
+@@ -434,16 +458,27 @@
+ # collect categories for deleting if requested
+ deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
+ catDict = dict()
++
++ old_bboxs = []
++ old_areas_cats = []
+ if deleteRec:
+ for i in self._display.selected['ids']:
++
+ if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
+ self._error.ReadLine(i)
+
+- cats = self.poCats.contents
+- for j in range(cats.n_cats):
+- if cats.field[j] not in catDict.keys():
+- catDict[cats.field[j]] = list()
+- catDict[cats.field[j]].append(cats.cat[j])
++ if self.emit_signals:
++ ret = self._getLineAreaBboxCats(i)
++ if ret:
++ old_bboxs += ret[0]
++ old_areas_cats += ret[1]
++
++ # catDict was not used -> put into comment
++ #cats = self.poCats.contents
++ #for j in range(cats.n_cats):
++ # if cats.field[j] not in catDict.keys():
++ # catDict[cats.field[j]] = list()
++ # catDict[cats.field[j]].append(cats.cat[j])
+
+ poList = self._display.GetSelectedIList()
+ nlines = Vedit_delete_lines(self.poMapInfo, poList)
+@@ -456,7 +491,10 @@
+ self._deleteRecords(catDict)
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.featuresDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
++
+ return nlines
+
+ def _deleteRecords(self, cats):
+@@ -512,22 +550,173 @@
+
+ @return number of deleted
+ """
++ if len(self._display.selected['ids']) < 1:
++ return 0
++
+ poList = self._display.GetSelectedIList()
+ cList = poList.contents
+
+ nareas = 0
++ old_bboxs = []
++ old_areas_cats = []
++
+ for i in range(cList.n_values):
++
+ if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
+ continue
+-
++
++ if self.emit_signals:
++ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
++ if area > 0:
++ bbox, cats = self._getaAreaBboxCats(area)
++ old_bboxs += bbox
++ old_areas_cats += cats
++
+ nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
+
+ if nareas > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
++ if self.emit_signals:
++ self.areasDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
++
++ return nareas
++
++ def _getLineAreaBboxCats(self, ln_id):
++ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
++
++ if ltype == GV_CENTROID:
++ #TODO centroid opttimization, can be adited also its area -> it will appear two times in new_ lists
++ return self._getCentroidAreaBboxCats(ln_id)
++ else:
++ return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
++
++
++ def _getCentroidAreaBboxCats(self, centroid):
++ if not Vect_line_alive(self.poMapInfo, centroid):
++ return None
++
++ area = Vect_get_centroid_area(self.poMapInfo, centroid)
++ if area > 0:
++ return self._getaAreaBboxCats(area)
++ else:
++ return None
++
++ def _getaAreaBboxCats(self, area):
++
++ po_b_list = Vect_new_list()
++ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
++ b_list = po_b_list.contents
++
++ geoms = []
++ areas_cats = []
++
++ if b_list.n_values > 0:
++ for i_line in range(b_list.n_values):
++
++ line = b_list.value[i_line];
++
++ geoms.append(self._getBbox(abs(line)))
++ areas_cats.append(self._getLineAreasCategories(abs(line)))
+
+- return nareas
++ Vect_destroy_list(po_b_list);
++
++ return geoms, areas_cats
++
++ def _getLineAreasCategories(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return []
++
++ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
++ if ltype != GV_BOUNDARY:
++ return []
++
++ cats = [None, None]
++
++ left = c_int()
++ right = c_int()
++
++ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
++ areas = [left.value, right.value]
++
++ for i, a in enumerate(areas):
++ if a > 0:
++ centroid = Vect_get_area_centroid(self.poMapInfo, a)
++ if centroid <= 0:
++ continue
++ c = self._getCategories(centroid)
++ if c:
++ cats[i] = c
++
++ return cats
++
++ def _getCategories(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return none
++
++ poCats = Vect_new_cats_struct()
++ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
++ Vect_destroy_cats_struct(poCats)
++ return None
++
++ cCats = poCats.contents
++
++ cats = {}
++ for j in range(cCats.n_cats):
++ if cats.has_key(cCats.field[j]):
++ cats[cCats.field[j]].append(cCats.cat[j])
++ else:
++ cats[cCats.field[j]] = [cCats.cat[j]]
+
++ Vect_destroy_cats_struct(poCats)
++ return cats
++
++ def _getBbox(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return None
++
++ poPoints = Vect_new_line_struct()
++ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
++ Vect_destroy_line_struct(poPoints)
++ return []
++
++ geom = self._convertGeom(poPoints)
++ bbox = self._createBbox(geom)
++ Vect_destroy_line_struct(poPoints)
++ return bbox
++
++ def _createBbox(self, points):
++
++ bbox = {}
++ for pt in points:
++ if not bbox.has_key('maxy'):
++ bbox['maxy'] = pt[1]
++ bbox['miny'] = pt[1]
++ bbox['maxx'] = pt[0]
++ bbox['minx'] = pt[0]
++ continue
++
++ if bbox['maxy'] < pt[1]:
++ bbox['maxy'] = pt[1]
++ elif bbox['miny'] > pt[1]:
++ bbox['miny'] = pt[1]
++
++ if bbox['maxx'] < pt[0]:
++ bbox['maxx'] = pt[0]
++ elif bbox['minx'] > pt[0]:
++ bbox['minx'] = pt[0]
++ return bbox
++
++ def _convertGeom(self, poPoints):
++
++ Points = poPoints.contents
++
++ pts_geom = []
++ for j in range(Points.n_points):
++ pts_geom.append((Points.x[j], Points.y[j]))
++
++ return pts_geom
++
+ def MoveSelectedLines(self, move):
+ """!Move selected features
+
+@@ -536,16 +725,45 @@
+ if not self._checkMap():
+ return -1
+
++ nsel = len(self._display.selected['ids'])
++ if nsel < 1:
++ return -1
++
+ thresh = self._display.GetThreshold()
+ snap = self._getSnapMode()
+
+ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ old_bboxs = []
++ old_areas_cats = []
++ for sel_id in self._display.selected['ids']:
++ ret = self._getLineAreaBboxCats(sel_id)
++ if ret:
++ old_bboxs += ret[0]
++ old_areas_cats += ret[1]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList,
+ move[0], move[1], 0,
+ snap, thresh)
++
+ Vect_destroy_list(poList)
+-
++
++ if nlines > 0 and self.emit_signals:
++ new_bboxs = []
++ new_areas_cats = []
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ ret = self._getLineAreaBboxCats(new_id)
++ if ret:
++ new_bboxs += ret[0]
++ new_areas_cats += ret[1]
++
+ if nlines > 0 and self._settings['breakLines']:
+ for i in range(1, nlines):
+ self._breakLineAtIntersection(nlines + i, None, changeset)
+@@ -553,7 +771,13 @@
+ if nlines > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.featuresMoved.emit(new_bboxs = new_bboxs,
++ old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
+ return nlines
+
+ def MoveSelectedVertex(self, point, move):
+@@ -571,12 +795,21 @@
+
+ if len(self._display.selected['ids']) != 1:
+ return -1
+-
++
++ # move only first found vertex in bbox
++ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ cList = poList.contents
++ old_bboxs = [self._getBbox(cList.value[0])]
++ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, point[0], point[1], 0.0)
+-
+- # move only first found vertex in bbox
+- poList = self._display.GetSelectedIList()
++
+ moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList, self.poPoints,
+ self._display.GetThreshold(type = 'selectThresh'),
+@@ -584,7 +817,17 @@
+ move[0], move[1], 0.0,
+ 1, self._getSnapMode())
+ Vect_destroy_list(poList)
+-
++
++ if moved > 0 and self.emit_signals:
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++
++ new_bboxs = []
++ new_areas_cats = []
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ new_bboxs.append(self._getBbox(new_id))
++ new_areas_cats.append(self._getLineAreasCategories(new_id))
++
+ if moved > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+@@ -592,7 +835,13 @@
+ if moved > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.vertexMoved.emit(new_bboxs = new_bboxs,
++ new_areas_cats = new_areas_cats,
++ old_areas_cats = old_areas_cats,
++ old_bboxs = old_bboxs)
++
+ return moved
+
+ def AddVertex(self, coords):
+@@ -681,6 +930,10 @@
+ self._error.ReadLine(line)
+ return -1
+
++ if self.emit_signals:
++ old_bboxs = [self._getBbox(line)]
++ old_areas_cats = [self._getLineAreasCategories(line)]
++
+ # build feature geometry
+ Vect_reset_line(self.poPoints)
+ for p in coords:
+@@ -696,6 +949,9 @@
+
+ newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
+ self.poPoints, self.poCats)
++ if newline > 0 and self.emit_signals:
++ new_geom = [self._getBbox(newline)]
++ new_areas_cats = [self._getLineAreasCategories(newline)]
+
+ if newline > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(newline, None)
+@@ -703,7 +959,13 @@
+ if newline > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.lineEdited.emit(old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_bboxs = new_bboxs,
++ new_areas_cats = new_areas_cats)
++
+ return newline
+
+ def FlipLine(self):
+@@ -1514,6 +1776,16 @@
+ return 0
+
+ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ cList = poList.contents
++
++ old_bboxs = [self._getBbox(cList.value[0])]
++ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
+
+@@ -1525,15 +1797,35 @@
+ else:
+ ret = Vedit_remove_vertex(self.poMapInfo, poList,
+ self.poPoints, thresh)
++
+ Vect_destroy_list(poList)
++
++ if ret > 0 and self.emit_signals:
++ new_bboxs = []
++ new_areas_cats = []
++
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ new_areas_cats.append(self._getLineAreasCategories(new_id))
++ new_bboxs.append(self._getBbox(new_id))
+
+ if not add and ret > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+-
++
+ if ret > 0:
+ self._addChangeset()
+-
++
++ if ret > 0 and self.emit_signals:
++ if add:
++ self.vertexAdded.emit(old_bboxs = old_bboxs, new_bboxs = new_bboxs)
++ else:
++ self.vertexRemoved.emit(old_bboxs = old_bboxs,
++ new_bboxs = new_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
+ return 1
+
+ def GetLineCats(self, line):
+Index: gui/wxpython/vdigit/toolbars.py
+===================================================================
+--- gui/wxpython/vdigit/toolbars.py (revision 57581)
++++ gui/wxpython/vdigit/toolbars.py (working copy)
+@@ -17,6 +17,7 @@
+ import wx
+
+ from grass.script import core as grass
++from grass.pydispatch.signal import Signal
+
+ from gui_core.toolbars import BaseToolbar, BaseIcons
+ from gui_core.dialogs import CreateNewVector
+@@ -42,6 +43,8 @@
+ self.digit = None
+ self._giface = giface
+
++ self.editingStarted = Signal("VDigitToolbar.editingStarted")
++
+ # currently selected map layer for editing (reference to MapLayer instance)
+ self.mapLayer = None
+ # list of vector layers from Layer Manager (only in the current mapset)
+@@ -860,6 +863,7 @@
+ alpha = int(opacity * 255)
+ self.digit.GetDisplay().UpdateSettings(alpha = alpha)
+
++ self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
+ return True
+
+ def StopEditing(self):
+Index: gui/wxpython/iclass/dialogs.py
+===================================================================
+--- gui/wxpython/iclass/dialogs.py (revision 57581)
++++ 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 57581)
++++ 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 57581)
++++ 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,7 +483,7 @@
+
+ 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:
+@@ -488,7 +494,8 @@
+ """!Set imagery group"""
+ group = grass.find_file(name = name, element = 'group')
+ if group['name']:
+- self.group = group['name']
++ self.group = group['name']
++ self.groupSet.emit(group = group['name'])
+ else:
+ GError(_("Group <%s> not found") % name, parent = self)
+
+@@ -768,17 +775,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 +1115,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 57581)
++++ 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/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py (revision 57581)
++++ 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 57581)
++++ 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/scatt_plot/plots.py
+===================================================================
+--- gui/wxpython/scatt_plot/plots.py (revision 0)
++++ gui/wxpython/scatt_plot/plots.py (working copy)
+@@ -0,0 +1,695 @@
++"""!
++ at package scatt_plot.dialogs
++
++ at brief Ploting widgets.
++
++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 wx
++import numpy as np
++
++#TODO testing
++import time
++from multiprocessing import Process, Queue
++
++#TODO
++#from numpy.lib.stride_tricks import as_strided
++from copy import deepcopy
++
++try:
++ import matplotlib
++ matplotlib.use('WXAgg')
++ from matplotlib.figure import Figure
++ from matplotlib.backends.backend_wxagg import \
++ FigureCanvasWxAgg as FigCanvas, \
++ NavigationToolbar2WxAgg as NavigationToolbar
++ from matplotlib.lines import Line2D
++ from matplotlib.artist import Artist
++ from matplotlib.mlab import dist_point_to_segment
++ from matplotlib.patches import Polygon, Ellipse
++ import matplotlib.image as mi
++ import matplotlib.colors as mcolors
++ import matplotlib.cbook as cbook
++except ImportError as e:
++ raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
++
++
++import grass.script as grass
++from grass.pydispatch.signal import Signal
++
++#class PlotImages()?
++
++class ScatterPlotWidget(wx.Panel):
++ def __init__(self, parent, scatt_id, scatt_mgr,
++ id = wx.ID_ANY):
++
++ wx.Panel.__init__(self, parent, id)
++
++ self.parent = parent
++ self.full_extend = None
++
++ self._createWidgets()
++ self._doLayout()
++ self.scatt_id = scatt_id
++ self.scatt_mgr = scatt_mgr
++ self.press_coords = None
++
++ self.cidpress = None
++ self.cidrelease = None
++
++ self.SetSize((200, 100))
++ self.Layout()
++
++ self.base_scale = 2.0
++ self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
++
++ self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
++
++ self.contex_menu = ScatterPlotContextMenu(plot = self)
++
++ self.ciddscroll = None
++
++ def _createWidgets(self):
++
++ # Create the mpl Figure and FigCanvas objects.
++ # 5x4 inches, 100 dots-per-inch
++ #
++ self.dpi = 100
++ self.fig = Figure((1.0, 1.0), dpi=self.dpi)
++ self.canvas = FigCanvas(self, -1, self.fig)
++
++ self.axes = self.fig.add_axes([0,0,1,1])
++
++ pol = Polygon(list(zip([0], [0])), animated=True)
++ self.axes.add_patch(pol)
++ self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
++
++ self.toolbar = NavigationToolbar(self.canvas)
++
++ def ZoomToExtend(self):
++ if self.full_extend:
++ self.axes.axis(self.full_extend)
++ self.canvas.draw()
++
++ def SetMode(self, mode):
++ self._deactivateMode()
++ if mode == 'zoom':
++ self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.zoom)
++ elif mode == 'pan':
++ self.toolbar.pan()
++
++ def GetCoords(self):
++ return self.polygon_drawer.GetCoords()
++
++ def SetEmpty(self):
++ return self.polygon_drawer.SetEmpty()
++
++ def SetEditingMode(self, mode):
++ self.polygon_drawer.SetMode(mode)
++
++ def _deactivateMode(self):
++
++ #TODO do own pan
++ if self.toolbar._active == "PAN":
++ self.toolbar.pan()
++
++ if self.ciddscroll:
++ self.canvas.mpl_disconnect(self.ciddscroll)
++
++ self._stopCategoryEdit()
++
++
++ def OnPress(self, event):
++ 'on button press we will see if the mouse is over us and store some data'
++ if not event.inaxes:
++ return
++
++ if event.xdata and event.ydata:
++ self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
++ else:
++ self.press_coords = None
++
++ def OnRelease(self, event):
++ 'on release we reset the press data'
++
++ if event.xdata and event.ydata and self.press_coords:
++
++ bbox = {}
++ if event.ydata > self.press_coords['y']:
++ bbox['up_y'] = event.ydata
++ bbox['btm_y'] = self.press_coords['y']
++ else:
++ bbox['up_y'] = self.press_coords['y']
++ bbox['btm_y'] = event.ydata
++
++ if event.xdata > self.press_coords['x']:
++ bbox['up_x'] = event.xdata
++ bbox['btm_x'] = self.press_coords['x']
++ else:
++ bbox['up_x'] = self.press_coords['x']
++ bbox['btm_x'] = event.xdata
++
++ self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
++
++ def _stopCategoryEdit(self):
++ 'disconnect all the stored connection ids'
++
++ if self.cidpress:
++ self.canvas.mpl_disconnect(self.cidpress)
++ if self.cidrelease:
++ self.canvas.mpl_disconnect(self.cidrelease)
++ #self.canvas.mpl_disconnect(self.cidmotion)
++
++ def _doLayout(self):
++
++ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
++ self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
++ #self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
++ self.SetSizer(self.main_sizer)
++ self.main_sizer.Fit(self)
++
++ def Plot(self, scatts, ellipses, styles):
++ """ Redraws the figure
++ """
++ self.axes.clear()
++
++ callafter_list = []
++
++ q = Queue()
++ p = Process(target=MergeImg, args=(scatts, styles, q))
++ p.start()
++ merged_img, self.full_extend = q.get()
++ p.join()
++
++ img = imshow(self.axes, merged_img,
++ origin = 'lower',
++ extent = self.full_extend,
++ interpolation='nearest',
++ aspect = "auto")
++
++ 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:
++ continue
++
++ colors = styles[cat_id]['color'].split(":")
++ ellip = Ellipse(xy=e['pos'], width=e['width'], height=e['height'], angle=e['theta'], edgecolor = "r", facecolor = 'None')
++ self.axes.add_artist(ellip)
++ callafter_list.append([self.axes.draw_artist, [ellip]])
++
++ callafter_list.append([self.fig.canvas.blit, []])
++
++ wx.CallAfter(lambda : self.CallAfter(callafter_list))
++
++ def CallAfter(self, funcs_list):
++ while funcs_list:
++ fcn, args = funcs_list.pop(0)
++ fcn(*args)
++
++ self.canvas.draw()
++
++ def CleanUp(self):
++ self.plotClosed.emit(scatt_id = self.scatt_id)
++ self.Destroy()
++
++ def zoom(self, event):
++ # get the current x and y limits
++ if not event.inaxes:
++ return
++ # tcaswell
++ # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
++ cur_xlim = self.axes.get_xlim()
++ cur_ylim = self.axes.get_ylim()
++ cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
++ cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
++ xdata = event.xdata
++ ydata = event.ydata
++ if event.button == 'up':
++ scale_factor = 1/self.base_scale
++ elif event.button == 'down':
++ scale_factor = self.base_scale
++ else:
++ scale_factor = 1
++
++ self.axes.set_xlim([xdata - cur_xrange*scale_factor,
++ xdata + cur_xrange*scale_factor])
++ self.axes.set_ylim([ydata - cur_yrange*scale_factor,
++ ydata + cur_yrange*scale_factor])
++
++ self.canvas.draw()
++
++class ScatterPlotContextMenu:
++ def __init__(self, plot):
++
++ self.plot = plot
++ self.canvas = plot.canvas
++ self.cidpress = self.canvas.mpl_connect(
++ 'button_press_event', self.ContexMenu)
++
++ def ContexMenu(self, event):
++ if not event.inaxes:
++ return
++
++ if event.button == 3:
++ menu = wx.Menu()
++ menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
++
++ for item in menu_items:
++ item_id = wx.ID_ANY
++ menu.Append(item_id, text = item[1])
++ menu.Bind(wx.EVT_MENU, item[2], id = item_id)
++
++ wx.CallAfter(self.ShowMenu, menu)
++
++ def ShowMenu(self, menu):
++ self.plot.PopupMenu(menu)
++ menu.Destroy()
++ self.plot.ReleaseMouse()
++
++class PolygonDrawer:
++ """
++ An polygon editor.
++ """
++
++ showverts = True
++ epsilon = 5 #TODO settings max pixel distance to count as a vertex hit
++ def __init__(self, ax, pol, empty_pol):
++ if pol.figure is None:
++ raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
++ self.ax = ax
++ self.canvas = pol.figure.canvas
++
++ self.pol = pol
++ self.empty_pol = empty_pol
++
++ x, y = zip(*self.pol.xy)
++ self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
++ self.ax.add_line(self.line)
++ #self._update_line(pol)
++
++ cid = self.pol.add_callback(self.poly_changed)
++ self.moving_ver_idx = None # the active vert
++
++ self.mode = None
++
++ if self.empty_pol:
++ self.Show(False)
++
++ self.canvas.mpl_connect('draw_event', self.draw_callback)
++ self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
++ self.canvas.mpl_connect('button_release_event', self.button_release_callback)
++ self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
++
++ self.it = 0
++ def SetMode(self, mode):
++ self.mode = mode
++
++ def GetCoords(self):
++ if self.empty_pol:
++ return None
++
++ coords = deepcopy(self.pol.xy)
++ return coords
++
++ def SetEmpty(self):
++ self._setEmptyPol(True)
++
++ def _setEmptyPol(self, empty_pol):
++ self.empty_pol = empty_pol
++ self.Show(not empty_pol)
++
++ def Show(self, show):
++
++ self.show = show
++
++ self.line.set_visible(self.show)
++ self.pol.set_visible(self.show)
++
++ self.Redraw()
++
++ def Redraw(self):
++ if self.show:
++ self.ax.draw_artist(self.pol)
++ self.ax.draw_artist(self.line)
++ self.canvas.blit(self.ax.bbox)
++ self.canvas.draw()
++
++ def draw_callback(self, event):
++ self.background = self.canvas.copy_from_bbox(self.ax.bbox)
++ self.ax.draw_artist(self.pol)
++ self.ax.draw_artist(self.line)
++
++ def poly_changed(self, pol):
++ 'this method is called whenever the polygon object is called'
++ # only copy the artist props to the line (except visibility)
++ vis = self.line.get_visible()
++ Artist.update_from(self.line, pol)
++ self.line.set_visible(vis) # don't use the pol visibility state
++
++ def get_ind_under_point(self, event):
++ 'get the index of the vertex under point if within epsilon tolerance'
++
++ # display coords
++ xy = np.asarray(self.pol.xy)
++ xyt = self.pol.get_transform().transform(xy)
++ xt, yt = xyt[:, 0], xyt[:, 1]
++ d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
++ indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
++ ind = indseq[0]
++
++ if d[ind]>=self.epsilon:
++ ind = None
++
++ return ind
++
++ def OnButtonPressed(self, event):
++ if not event.inaxes:
++ return
++
++ if event.button in [2, 3]:
++ return
++
++ if self.mode == "delete_vertex":
++ self._deleteVertex(event)
++ elif self.mode == "add_boundary_vertex":
++ self._addVertexOnBoundary(event)
++ elif self.mode == "add_vertex":
++ self._addVertex(event)
++ self.moving_ver_idx = self.get_ind_under_point(event)
++
++ def button_release_callback(self, event):
++ 'whenever a mouse button is released'
++ if not self.showverts: return
++ if event.button != 1: return
++ self.moving_ver_idx = None
++
++ def ShowVertices(self, show):
++ self.showverts = show
++ self.line.set_visible(self.showverts)
++ if not self.showverts: self.moving_ver_idx = None
++
++ def _deleteVertex(self, event):
++ ind = self.get_ind_under_point(event)
++
++ if ind is None or self.empty_pol:
++ return
++
++ if len(self.pol.xy) <= 2:
++ self.empty_pol = True
++ self.Show(False)
++ return
++
++ coords = []
++ for i,tup in enumerate(self.pol.xy):
++ if i == ind:
++ continue
++ elif i == 0 and ind == len(self.pol.xy) - 1:
++ continue
++ elif i == len(self.pol.xy) - 1 and ind == 0:
++ continue
++
++ coords.append(tup)
++
++ self.pol.xy = coords
++ self.line.set_data(zip(*self.pol.xy))
++
++ self.Redraw()
++
++ def _addVertexOnBoundary(self, event):
++ if self.empty_pol:
++ return
++
++ xys = self.pol.get_transform().transform(self.pol.xy)
++ p = event.x, event.y # display coords
++ for i in range(len(xys)-1):
++ s0 = xys[i]
++ s1 = xys[i+1]
++ d = dist_point_to_segment(p, s0, s1)
++
++ if d<=self.epsilon:
++ self.pol.xy = np.array(
++ list(self.pol.xy[:i + 1]) +
++ [(event.xdata, event.ydata)] +
++ list(self.pol.xy[i + 1:]))
++ self.line.set_data(zip(*self.pol.xy))
++ break
++
++ self.Redraw()
++
++ def _addVertex(self, event):
++
++ if self.empty_pol:
++ pt = (event.xdata, event.ydata)
++ self.pol.xy = np.array([pt, pt])
++ self.Show(True)
++ self.empty_pol = False
++ else:
++ self.pol.xy = np.array(
++ [(event.xdata, event.ydata)] +
++ list(self.pol.xy[1:]) +
++ [(event.xdata, event.ydata)])
++
++ self.line.set_data(zip(*self.pol.xy))
++
++ self.Redraw()
++
++ def motion_notify_callback(self, event):
++ 'on mouse movement'
++ if not self.mode == "move_vertex": return
++ if not self.showverts: return
++ if self.empty_pol: return
++ if self.moving_ver_idx is None: return
++ if event.inaxes is None: return
++ if event.button != 1: return
++
++ self.it += 1
++
++ x,y = event.xdata, event.ydata
++
++ self.pol.xy[self.moving_ver_idx] = x,y
++ if self.moving_ver_idx == 0:
++ self.pol.xy[len(self.pol.xy) - 1] = x,y
++ elif self.moving_ver_idx == len(self.pol.xy) - 1:
++ self.pol.xy[0] = x,y
++
++ self.line.set_data(zip(*self.pol.xy))
++
++ self.canvas.restore_region(self.background)
++
++ self.Redraw()
++
++class ModestImage(mi.AxesImage):
++ """
++ Computationally modest image class.
++
++ ModestImage is an extension of the Matplotlib AxesImage class
++ better suited for the interactive display of larger images. Before
++ drawing, ModestImage resamples the data array based on the screen
++ resolution and view window. This has very little affect on the
++ appearance of the image, but can substantially cut down on
++ computation since calculations of unresolved or clipped pixels
++ are skipped.
++
++ The interface of ModestImage is the same as AxesImage. However, it
++ does not currently support setting the 'extent' property. There
++ may also be weird coordinate warping operations for images that
++ I'm not aware of. Don't expect those to work either.
++
++ Author: Chris Beaumont <beaumont at hawaii.edu>
++ """
++ def __init__(self, *args, **kwargs):
++ #why???
++ #if 'extent' in kwargs and kwargs['extent'] is not None:
++ # raise NotImplementedError("ModestImage does not support extents")
++
++ self._full_res = None
++ self._sx, self._sy = None, None
++ self._bounds = (None, None, None, None)
++ super(ModestImage, self).__init__(*args, **kwargs)
++
++ def set_data(self, A):
++ """
++ Set the image array
++
++ ACCEPTS: numpy/PIL Image A
++ """
++ self._full_res = A
++ self._A = A
++
++ if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
++ np.float):
++ raise TypeError("Image data can not convert to float")
++
++ if (self._A.ndim not in (2, 3) or
++ (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
++ raise TypeError("Invalid dimensions for image data")
++
++ self._imcache =None
++ self._rgbacache = None
++ self._oldxslice = None
++ self._oldyslice = None
++ self._sx, self._sy = None, None
++
++ def get_array(self):
++ """Override to return the full-resolution array"""
++ return self._full_res
++
++ def _scale_to_res(self):
++ """ Change self._A and _extent to render an image whose
++ resolution is matched to the eventual rendering."""
++
++ ax = self.axes
++ ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
++ xlim, ylim = ax.get_xlim(), ax.get_ylim()
++ dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
++
++ y0 = max(0, ylim[0] - 5)
++ y1 = min(self._full_res.shape[0], ylim[1] + 5)
++ x0 = max(0, xlim[0] - 5)
++ x1 = min(self._full_res.shape[1], xlim[1] + 5)
++ y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
++
++ sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
++ sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
++
++ # have we already calculated what we need?
++ if sx == self._sx and sy == self._sy and \
++ x0 == self._bounds[0] and x1 == self._bounds[1] and \
++ y0 == self._bounds[2] and y1 == self._bounds[3]:
++ return
++
++ self._A = self._full_res[y0:y1:sy, x0:x1:sx]
++ self._A = cbook.safe_masked_invalid(self._A)
++ x1 = x0 + self._A.shape[1] * sx
++ y1 = y0 + self._A.shape[0] * sy
++
++ self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
++ self._sx = sx
++ self._sy = sy
++ self._bounds = (x0, x1, y0, y1)
++ self.changed()
++
++ def draw(self, renderer, *args, **kwargs):
++ self._scale_to_res()
++ super(ModestImage, self).draw(renderer, *args, **kwargs)
++
++
++def imshow(axes, X, cmap=None, norm=None, aspect=None,
++ interpolation=None, alpha=None, vmin=None, vmax=None,
++ origin=None, extent=None, shape=None, filternorm=1,
++ filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
++ """Similar to matplotlib's imshow command, but produces a ModestImage
++
++ Unlike matplotlib version, must explicitly specify axes
++ Author: Chris Beaumont <beaumont at hawaii.edu>
++ """
++
++ if not axes._hold:
++ axes.cla()
++ if norm is not None:
++ assert(isinstance(norm, mcolors.Normalize))
++ if aspect is None:
++ aspect = rcParams['image.aspect']
++ axes.set_aspect(aspect)
++ im = ModestImage(axes, cmap, norm, interpolation, origin, extent,
++ filternorm=filternorm,
++ filterrad=filterrad, resample=resample, **kwargs)
++
++ im.set_data(X)
++ im.set_alpha(alpha)
++ axes._set_artist_props(im)
++
++ if im.get_clip_path() is None:
++ # image does not already have clipping set, clip to axes patch
++ im.set_clip_path(axes.patch)
++
++ #if norm is None and shape is None:
++ # im.set_clim(vmin, vmax)
++ if vmin is not None or vmax is not None:
++ im.set_clim(vmin, vmax)
++ else:
++ im.autoscale_None()
++ im.set_url(url)
++
++ # update ax.dataLim, and, if autoscaling, set viewLim
++ # to tightly fit the image, regardless of dataLim.
++ im.set_extent(im.get_extent())
++
++ axes.images.append(im)
++ im._remove_method = lambda h: axes.images.remove(h)
++
++ return im
++
++def MergeImg(scatts, styles, output_queue):
++
++ init = True
++ merge_tmp = grass.tempfile()
++ for cat_id, scatt in scatts.iteritems():
++ #print "color map %d" % cat_id
++ #TODO make more general
++ if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
++ not styles[cat_id]['show']):
++ continue
++ if init:
++ b1_i = scatt['bands_info']['b1']
++ b2_i = scatt['bands_info']['b2']
++
++ full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5)
++
++ if cat_id == 0:
++ cmap = matplotlib.cm.jet
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ else:
++ colors = styles[cat_id]['color'].split(":")
++
++ cmap = matplotlib.cm.jet
++ cmap.set_bad('w',1.)
++ cmap._init()
++ cmap._lut[len(cmap._lut) - 1, -1] = 0
++ cmap._lut[:, 0] = int(colors[0])/255.0
++ cmap._lut[:, 1] = int(colors[1])/255.0
++ cmap._lut[:, 2] = int(colors[2])/255.0
++
++ #if init:
++ masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
++
++ vmax = np.amax(masked_cat)
++ masked_cat = masked_cat / float(vmax)
++
++ colored_cat = np.uint8(cmap(masked_cat) * 255)
++ del masked_cat
++ del cmap
++
++ #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
++
++ if init:
++ merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', shape=colored_cat.shape)
++ merged_img[:] = colored_cat[:]
++ init = False
++ else:
++ #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape)
++ c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
++
++ #TODO apply strides and there will be no need for loop
++ #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
++
++ for i in range(3):
++ merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
++ merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
++
++ del c_img_a
++
++ del colored_cat
++ output_queue.put((merged_img, full_extend))
++ #return merged_img, full_extend
+\ No newline at end of file
+Index: gui/wxpython/scatt_plot/controllers.py
+===================================================================
+--- gui/wxpython/scatt_plot/controllers.py (revision 0)
++++ gui/wxpython/scatt_plot/controllers.py (working copy)
+@@ -0,0 +1,666 @@
++"""!
++ at package scatt_plot.controllers
++
++ at brief Controller layer for scatter plot tool.
++
++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
++
++#TODO just for testing
++import time
++
++#TODO
++import wx
++
++from core.gcmd import GException, GError, GMessage, RunCommand
++
++from scatt_plot.scatt_core import Core, idBandsToidScatt
++
++from scatt_plot.dialogs import AddScattPlotDialog
++from scatt_plot.gthreading import gThread
++from core.gconsole import EVT_CMD_DONE
++from grass.pydispatch.signal import Signal
++
++class ScattsManager(wx.EvtHandler):
++ def __init__(self, guiparent, giface, iclass_mapwin):
++ #TODO remove iclass parameter
++
++ wx.EvtHandler.__init__(self)
++ self.giface = giface
++ self.mapDisp = giface.GetMapDisplay()
++
++ if iclass_mapwin:
++ self.mapWin = iclass_mapwin
++ else:
++ self.mapWin = giface.GetMapWindow()
++
++ self.guiparent = guiparent
++
++ self.show_add_scatt_plot = False
++
++ self.core = Core()
++ self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
++
++ self.cats_mgr = CategoriesManager(self, self.core)
++
++ self.thread = gThread(self);
++
++ self.plots = {}
++ self.added_cats_rasts = {}
++
++ self.cats_to_update = []
++
++ self.plot_mode = None
++ self.pol_sel_mode = [False, None]
++
++ self.data_set = False
++
++ if iclass_mapwin:
++ self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
++ self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
++ else:
++ self.mapWin_conn = None
++ self.iclass_conn = None
++
++ self.tasks_pids = {
++ 'add_scatt' : [],
++ 'set_data' : -1,
++ 'set_data_add' : -1,
++ 'set_edit_cat_data' : -1,
++ 'mapwin_conn' : [],
++ 'render_plots' : -1,
++ 'render' : []
++ }
++
++ self.Bind(EVT_CMD_DONE, self.OnThreadDone)
++
++ def CleanUp(self):
++ self.core.CleanUp()
++
++ for scatt_id, scatt in self.plots.items():
++ scatt.CleanUp()
++
++ def OnThreadDone(self, event):
++
++ if event.exception:
++ GError(str(event.exception))
++ return
++
++ if event.pid in self.tasks_pids['mapwin_conn']:
++ self.tasks_pids['mapwin_conn'].remove(event.pid)
++ updated_cats = event.ret
++
++ for cat in updated_cats:
++ if cat not in self.cats_to_update:
++ self.cats_to_update.append(cat)
++
++ if not self.tasks_pids['mapwin_conn']:
++ self.tasks_pids['render_plots'] = self.thread.GetId()
++ self.thread.Run(callable = self.core.ComputeCatsScatts,
++ cats_ids = self.cats_to_update[:])
++ del self.cats_to_update[:]
++
++ return
++
++ if self.tasks_pids['render_plots'] == event.pid:
++ self.RenderScattPlts()
++ return
++
++ if event.pid in self.tasks_pids['render']:
++ self.tasks_pids['render'].remove(event.pid)
++ return
++
++ if event.pid in self.tasks_pids['add_scatt']:
++ self.tasks_pids['add_scatt'].remove(event.pid)
++ self.AddScattPlotDone(event)
++ return
++
++ if self.tasks_pids['set_data'] == event.pid:
++ self.SetDataDone(event)
++ return
++
++ if self.tasks_pids['set_data_add'] == event.pid:
++ self.SetDataDone(event)
++ self.AddScattPlot()
++ return
++
++ if self.tasks_pids['set_edit_cat_data'] == event.pid:
++ self.SetEditCatDataDone(event)
++ return
++
++ def SetData(self, bands):
++
++ self.CleanUp()
++
++ self.data_set = False
++
++ if self.show_add_scatt_plot:
++ self.tasks_pids['set_data_add'] = self.thread.GetId()
++ else:
++ self.tasks_pids['set_data'] = self.thread.GetId()
++
++ self.thread.Run(callable = self.core.SetData, bands = bands)
++
++ def SetDataDone(self, event):
++
++ self.data_set = True
++ self.cats_mgr.InitCoreCats()
++
++ def OnOutput(self, event):
++ """!Print thread output according to debug level.
++ """
++ print event.text
++
++ def GetBands(self):
++ return self.core.GetBands()
++
++ def AddScattPlot(self):
++ if not self.data_set and self.iclass_conn:
++ self.show_add_scatt_plot = True
++ self.iclass_conn.SetData()
++ self.show_add_scatt_plot = False
++ return
++ if not self.data_set:
++ GError(_('No data set.'))
++ return
++
++ dlg = AddScattPlotDialog(parent = self.guiparent, bands = self.core.GetBands())
++ if dlg.ShowModal() == wx.ID_OK:
++ self._addScattPlot(dlg.GetScattId())
++
++ dlg.Destroy()
++
++ return
++
++ #for testing
++ jj = len(self.core.GetBands()) - 1
++
++ max_k = 20
++ k = 0
++ for i in range(jj):
++ for j in range(i, jj):
++ print i * (len(self.core.GetBands()) - 1) + j
++ self._addScattPlot(i * (len(self.core.GetBands()) - 1) + j)
++ k += 1
++ if k == max_k:
++ break
++ if k == max_k:
++ break
++ jj -= 1
++
++
++ def _addScattPlot(self, scatt_id):
++ if self.plots.has_key(scatt_id):
++ GMessage(_("Scatter plot has been already added."))
++ return
++
++ self.tasks_pids['add_scatt'].append(self.thread.GetId())
++
++ self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
++
++ def RenderScattPlts(self, scatt_ids = None):
++ if len(self.tasks_pids['render']) > 1:
++ print "skip"
++ return
++
++ self.tasks_pids['render'].append(self.thread.GetId())
++ self.thread.Run(callable = self._renderscattplts, scatt_ids = scatt_ids)
++
++ def _renderscattplts(self, scatt_ids):
++ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++ for i_scatt_id, scatt in self.plots.items():
++ if scatt_ids is not None and i_scatt_id not in scatt_ids:
++ continue
++
++ scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
++ ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id)
++
++ if self.pol_sel_mode[0]:
++ self._getSelectedAreas(i_scatt_id, scatt_dt, cats_attrs)
++
++ scatt.Plot(scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++
++ def _getSelectedAreas(self, scatt_id, scatt_dt, cats_attrs):
++
++ cat_id = self.cats_mgr.GetSelectedCat()
++ if not cat_id:
++ return
++
++ sel_a_cat_id = max(cats_attrs.keys()) + 1
++
++ s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
++ if not s:
++ return
++ cats_attrs[sel_a_cat_id] = {'color' : "255:255:0",
++ 'opacity' : 0.7,
++ 'show' : True}
++
++ scatt_dt[sel_a_cat_id] = s[cat_id]
++
++ def AddScattPlotDone(self, event):
++
++ scatt_id = event.kwds['scatt_id']
++
++ #TODO guiparent - not very good
++ self.plots[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
++ self.plots[scatt_id].plotClosed.connect(self.PlotClosed)
++
++ if self.plot_mode:
++ self.plots[scatt_id].SetMode(self.plot_mode)
++
++ self.RenderScattPlts(scatt_ids = [scatt_id])
++
++ def PlotClosed(self, scatt_id):
++ del self.plots[scatt_id]
++
++ def SetPlotsMode(self, mode):
++
++ self.plot_mode = mode
++ for scatt in self.plots.itervalues():
++ scatt.SetMode(mode)
++ scatt.SetEditingMode(self.pol_sel_mode[1])
++
++ def ActivateSelectionPolygonMode(self, activate):
++ self.pol_sel_mode[0] = activate
++ self.RenderScattPlts()
++ return activate
++
++ def ProcessSelectionPolygons(self, process_mode):
++ scatts_polygons = {}
++ for scatt_id, scatt in self.plots.iteritems():
++ coords = scatt.GetCoords()
++ if coords is not None:
++ scatts_polygons[scatt_id] = coords
++
++ if not scatts_polygons:
++ return
++
++ value = 1
++ if process_mode == 'remove':
++ value = 0
++
++ sel_cat_id = self.cats_mgr.GetSelectedCat()
++ if not sel_cat_id:
++ dlg = wx.MessageDialog(parent = self.guiparent,
++ message = _("In order to select arrea in scatter plot, "
++ "you have to select class first.\n\n"
++ "There is no class yet, "
++ "do you want to create one?"),
++ caption = _("No class selected"),
++ style = wx.YES_NO)
++ if dlg.ShowModal() == wx.ID_YES:
++ self.iclass_conn.EmptyCategories()
++
++ sel_cat_id = self.cats_mgr.GetSelectedCat()
++ if not sel_cat_id:
++ return
++
++ for scatt in self.plots.itervalues():
++ scatt.SetEmpty()
++
++ self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
++ self.thread.Run(callable = self.core.UpdateCategoryWithPolygons,
++ cat_id = sel_cat_id,
++ scatts_pols = scatts_polygons,
++ value = value)
++
++ def SetPlotsEditingMode(self, mode):
++
++ self.pol_sel_mode[1] = mode
++ for scatt in self.plots.itervalues():
++ scatt.SetEditingMode(mode)
++
++ def SetEditCatDataDone(self, event):
++
++ if event.exception:
++ GError(_("Error occured during computation of scatter plot category:\n%s"),
++ parent = self.guiparent, showTraceback = False)
++
++ cat_id = event.ret
++
++ self.RenderScattPlts()
++
++ cat_id = event.kwds["cat_id"]
++
++ cat_rast = self.core.GetCatRast(cat_id)
++
++ if cat_rast not in self.added_cats_rasts.values():
++
++ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++
++ region = self.core.GetRegion()
++ ret, err_msg = RunCommand('r.region',
++ map = cat_rast,
++ getErrorMsg = True,
++ n = "%f" % region['n'],
++ s = "%f" % region['s'],
++ e = "%f" % region['e'],
++ w = "%f" % region['w'],
++ )
++
++ ret, err_msg = RunCommand('r.colors',
++ map = cat_rast,
++ rules = "-",
++ stdin = "1 %s" % cats_attrs["color"],
++ getErrorMsg = True)
++
++ if ret != 0:
++ GError(_("r.region failed\n%s" % err_msg))
++
++ self.mapWin.Map.AddLayer(ltype = "raster", name = "cat_%d" % cat_id, render = True,
++ command = ["d.rast", "map=%s" % cat_rast, "values=1"])
++
++
++ if ret != 0:
++ GError(_("r.region failed\n%s" % err_msg))
++
++ self.added_cats_rasts[cat_id] = cat_rast
++
++ self.giface.updateMap.emit()
++
++ def DigitDataChanged(self, vectMap, digit):
++
++ if self.mapWin_conn:
++ self.mapWin_conn.DigitDataChanged(vectMap, digit)
++ return 1
++ else:
++ return 0
++
++ def GetCategoriesManager(self):
++ return self.cats_mgr
++
++
++class CategoriesManager:
++
++ def __init__(self, scatt_mgr, core):
++
++ self.core = core
++ self.scatt_mgr = scatt_mgr
++
++ self.cats = {}
++ self.cats_ids = []
++
++ self.sel_cat_id = None
++
++ self.initialized = Signal('CategoriesManager.initialized')
++ self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
++ self.deletedCategory = Signal('CategoriesManager.deletedCategory')
++ self.addedCategory = Signal('CategoriesManager.addedCategory')
++
++ def Clear(self):
++
++ self.cats.clear()
++ del self.cats_ids[:]
++
++ self.sel_cat_id = None
++
++ def InitCoreCats(self):
++ if self.scatt_mgr.data_set:
++ for cat_id in self.cats_ids:
++ self.core.AddCategory(cat_id)
++
++ def AddCategory(self, cat_id = None, name = None, color = None):
++
++ if cat_id is None:
++ if self.cats_ids:
++ cat_id = max(self.cats_ids) + 1
++ else:
++ cat_id = 1
++
++ if self.scatt_mgr.data_set:
++ self.scatt_mgr.thread.Run(callable = self.core.AddCategory,
++ cat_id = cat_id)
++ #TODO check number of cats
++ #if ret < 0: #TODO
++ # return -1;
++
++ self.cats[cat_id] = {
++ 'name' : _('Category %s' % cat_id ),
++ 'color' : "0:0:0",
++ 'opacity' : 1.0,
++ 'show' : True
++ }
++
++ self.cats_ids.append(cat_id)
++
++ if name is not None:
++ self.cats[cat_id]["name"] = name
++
++ if color is not None:
++ self.cats[cat_id]["color"] = color
++
++ self.addedCategory.emit(cat_id = cat_id,
++ name = self.cats[cat_id]["name"],
++ color = self.cats[cat_id]["color"] )
++ return cat_id
++
++ def SetCategoryAttrs(self, cat_id, attrs_dict):
++ render = False
++ for k, v in attrs_dict.iteritems():
++ if not render and k in ['name', 'color', 'opacity', 'show']:
++ render = True
++
++ self.cats[cat_id][k] = v
++
++ #TODO optimization
++ if render:
++ self.scatt_mgr.RenderScattPlts()
++
++ self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
++
++ def DeleteCategory(self, cat_id):
++
++ if self.scatt_mgr.data_set:
++ self.scatt_mgr.thread.Run(callable = self.core.DeleteCategory,
++ cat_id = cat_id)
++ del self.cats[cat_id]
++ self.cats_ids.remove(cat_id)
++
++ self.deletedCategory.emit(cat_id = cat_id)
++
++ #TODO emit event?
++ def SetSelectedCat(self, cat_id):
++ self.sel_cat_id = cat_id
++ if self.scatt_mgr.pol_sel_mode[0]:
++ self.scatt_mgr.RenderScattPlts()
++
++ def GetSelectedCat(self):
++ return self.sel_cat_id
++
++ def GetCategoryAttrs(self, cat_id):
++ #TODO is mutable
++ return self.cats[cat_id]
++
++ def GetCategoriesAttrs(self):
++ #TODO is mutable
++ return self.cats
++
++ def GetCategories(self):
++ return self.cats_ids[:]
++
++ def SetCategoryPosition(self):
++ if newindex > oldindex:
++ newindex -= 1
++
++ self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
++
++class MapWinConnection:
++ def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
++ self.mapWin = mapWin
++ self.vectMap = None
++ self.scatt_rast_updater = scatt_rast_updater
++ self.scatt_mgr = scatt_mgr
++ self.cats_mgr = scatt_mgr.cats_mgr
++
++ self.thread = self.scatt_mgr.thread
++
++ #TODO
++ self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
++
++ #def ChangeMap(self, vectMap, layers_cats):
++ # self.vectMap = vectMap
++ # self.layers_cats = layers_cats
++
++ #ret, region, msg = RunCommand("v.to.rast",
++ # flags = "gp",
++ # getErrorMsg = True,
++ # read = True)
++
++ def _connectSignals(self):
++ self.digit.featureAdded.connect(self.AddFeature)
++ self.digit.areasDeleted.connect(self.DeleteAreas)
++ self.digit.featuresDeleted.connect(self.DeleteAreas)
++ self.digit.vertexMoved.connect(self.EditedFeature)
++ self.digit.vertexRemoved.connect(self.EditedFeature)
++ self.digit.lineEdited.connect(self.EditedFeature)
++ self.digit.featuresMoved.connect(self.EditedFeature)
++
++ def AddFeature(self, new_bboxs, new_areas_cats):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
++ new_bboxs = new_bboxs,
++ old_bboxs = [],
++ old_areas_cats = [],
++ new_areas_cats = new_areas_cats)
++
++ def DeleteAreas(self, old_bboxs, old_areas_cats):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
++ new_bboxs = [],
++ old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = [])
++
++
++ def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
++ if not self.scatt_mgr.data_set:
++ return
++
++ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
++ new_bboxs = new_bboxs,
++ old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
++ def DigitDataChanged(self, vectMap, digit):
++
++ self.digit = digit
++ self.vectMap = vectMap
++
++ self.digit.EmitSignals(emit = True)
++
++ self.scatt_rast_updater.SetVectMap(vectMap)
++
++ self._connectSignals()
++
++
++class IClassConnection:
++ def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
++ self.iclass_frame = iclass_frame
++ self.stats_data = self.iclass_frame.stats_data
++ self.cats_mgr = cats_mgr
++ self.scatt_mgr = scatt_mgr
++
++ self.stats_data.statisticsAdded.connect(self.AddCategory)
++ self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
++ self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
++ self.stats_data.statisticsSet.connect(self.SetCategory)
++
++ self.iclass_frame.groupSet.connect(self.GroupSet)
++
++ self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
++ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++ self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++ self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
++
++ self.SyncCats()
++
++ def SetData(self):
++ self.iclass_frame.AddBands()
++
++ def EmptyCategories(self):
++ self.iclass_frame.OnCategoryManager(None)
++
++ def SyncCats(self):
++ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++ cats = self.stats_data.GetCategories()
++ for c in cats:
++ stats = self.stats_data.GetStatistics(c)
++ self.cats_mgr.AddCategory(c, stats.name, stats.color)
++ self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++ def CategoryChanged(self, cat):
++ self.cats_mgr.SetSelectedCat(cat)
++
++ def AddCategory(self, cat, name, color):
++ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++ self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
++ self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++ def DeleteCategory(self, cat):
++ self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
++ self.cats_mgr.DeleteCategory(cat)
++ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++
++ def DeletAllCategories(self):
++
++ self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
++ cats = self.stats_data.GetCategories()
++ for c in cats:
++ self.cats_mgr.DeleteCategory(c)
++ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++
++ def SetCategory(self, cat, stats):
++
++ self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
++ cats_attr = {}
++ for attr in ['name', 'color']:
++ if stats.has_key(attr):
++ cats_attr[attr] = stats[attr]
++
++ if cats_attr:
++ self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
++ self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
++
++
++ def SetStatistics(self, cat_id, attrs_dict):
++ self.stats_data.statisticsSet.disconnect(self.SetCategory)
++ self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
++ self.stats_data.statisticsSet.connect(self.SetCategory)
++
++ def AddStatistics(self, cat_id, name, color):
++ self.stats_data.statisticsAdded.disconnect(self.AddCategory)
++ self.stats_data.AddStatistics(cat_id, name, color)
++ self.stats_data.statisticsAdded.connect(self.AddCategory)
++
++ def DeleteStatistics(self, cat_id):
++ self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
++ self.stats_data.DeleteStatistics(cat_id)
++ self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
++
++ def GroupSet(self, group):
++ res = RunCommand('i.group',
++ flags = 'g',
++ group = group, subgroup = group,
++ read = True).strip()
++ if res.split('\n')[0]:
++ bands = res.split('\n')
++ self.scatt_mgr.SetData(bands)
+\ No newline at end of file
Index: gui/wxpython/scatt_plot/gthreading.py
===================================================================
--- gui/wxpython/scatt_plot/gthreading.py (revision 0)
@@ -573,7 +2871,7 @@
===================================================================
--- gui/wxpython/scatt_plot/scatt_core.py (revision 0)
+++ gui/wxpython/scatt_plot/scatt_core.py (working copy)
-@@ -0,0 +1,784 @@
+@@ -0,0 +1,779 @@
+"""!
+ at package scatt_plot.scatt_plot
+
@@ -591,7 +2889,6 @@
+import os
+import sys
+
-+import Image
+import time
+
+import numpy as np
@@ -600,8 +2897,8 @@
+
+from math import sqrt, ceil, floor
+from copy import deepcopy
++from scipy.signal import convolve2d
+
-+from grass.script import core as grass
+from core.gcmd import GException, GError, RunCommand
+
+import grass.script as grass
@@ -643,7 +2940,7 @@
+ return self.an_data.GetBands()
+
+ def GetScattsData(self):
-+ return self.scatts_dt;
++ return self.scatts_dt, self.scatt_conds_dt;
+
+ def GetRegion(self):
+ return self.an_data.GetRegion()
@@ -676,7 +2973,6 @@
+ #arr.flush()
+ #del arr
+
-+ start_time = time.clock()
+ self.ComputeCatsScatts([cat_id])
+ #print "time"
+ #print time.clock() - start_time
@@ -712,7 +3008,8 @@
+ 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."))
+
@@ -727,24 +3024,14 @@
+ b2_info = self.an_data.GetBandInfo(b2)
+
+ raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
-+
-+ rescaled = (255.0 / raster_pol.max() * (raster_pol - raster_pol.min())).astype(np.uint8)
+
-+ im = Image.fromarray(rescaled)
-+ im.save('/home/ostepok/Desktop/obrazek.png')
-+
+ raster_ind = np.where(raster_pol > 0)
+ arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
+
+ arr[raster_ind] = value
+ #arr.flush()
-+
-+ rescaled = (255.0 / arr.max() * (arr - arr.min())).astype(np.uint8)
-+ im = Image.fromarray(rescaled)
-+ im.save('/home/ostepok/Desktop/arr_obrazek.png')
-+
++
+ self.ComputeCatsScatts([cat_id])
-+
+ return cat_id
+
+ def _validExtend(self, val):
@@ -1105,6 +3392,19 @@
+ # 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):
+
@@ -1176,18 +3476,6 @@
+
+ return True
+
-+ def GetScatt(self, scatt_id, cats_ids = None):
-+ if scatt_id not in self.scatts_ids:
-+ return False
-+
-+ scatts = {}
-+ for cat_id in self.cats.iterkeys():
-+ if cats_ids and cat_id not in cats_ids:
-+ continue
-+ scatts[cat_id] = {'np_vals' : self.cats[cat_id][scatt_id]['np_vals'],
-+ 'bands_info' : self.GetBandsInfo(scatt_id)}
-+ return scatts
-+
+ def GetEllipses(self, scatt_id):
+ if scatt_id not in self.scatts_ids:
+ return False
@@ -1320,17 +3608,22 @@
+
+ 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):
++ #for (y, x), value in np.ndenumerate(grid):
+
-+ if x >= width - 1: continue
-+ if y >= height - 1: continue
++ # 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
++ # 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
+
@@ -1362,7 +3655,7 @@
===================================================================
--- gui/wxpython/scatt_plot/core_c.py (revision 0)
+++ gui/wxpython/scatt_plot/core_c.py (working copy)
-@@ -0,0 +1,166 @@
+@@ -0,0 +1,170 @@
+"""!
+ at package scatt_plot.scatt_plot
+
@@ -1378,6 +3671,9 @@
+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+
++#TODO just for testing
++import time
++
+import sys
+from multiprocessing import Process, Queue
+
@@ -1392,13 +3688,14 @@
+
+
+def ComputeScatts(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts):
++
+ q = Queue()
+ p = Process(target=_computeScattsProcess, args=(region, scatt_conds, bands,
+ n_bands, scatts, cats_rasts_conds, cats_rasts, q))
+ p.start()
+ ret = q.get()
+ p.join()
-+
++
+ return ret[0], ret[1]
+
+def UpdateCatRast(patch_rast, region, cat_rast):
@@ -1417,8 +3714,8 @@
+def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts, cats_rasts_conds, cats_rasts, output_queue):
+
+ #TODO names for types not 0 and 1?
-+ sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, 0, n_bands)
-+ scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, 1, n_bands)
++ sccats_c, cats_rasts_c, refs = _getComputationStruct(scatts, cats_rasts, SC_SCATT_DATA, n_bands)
++ scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
+
+ char_bands = _stringListToCharArr(bands)
+
@@ -1533,7 +3830,7 @@
===================================================================
--- gui/wxpython/scatt_plot/frame.py (revision 0)
+++ gui/wxpython/scatt_plot/frame.py (working copy)
-@@ -0,0 +1,543 @@
+@@ -0,0 +1,555 @@
+"""!
+ at package scatt_plot.dialogs
+
@@ -1671,6 +3968,14 @@
+ self.ignore_scroll = 0
+ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
+
++ def ScatterPlotClosed(self, scatt_id):
++ name = self._getScatterPlotName(scatt_id)
++ pane = self._mgr.GetPane(name)
++
++ if pane.IsOk():
++ self._mgr.ClosePane(pane)
++ self._mgr.Update()
++
+ def OnMouseWheel(self, event):
+ #TODO very ugly find some better solution
+ self.ignore_scroll = 3
@@ -1713,12 +4018,16 @@
+ def OnSettings(self, event):
+ pass
+
++ def _getScatterPlotName(self, scatt_id):
++ return "scatter plot %d" % scatt_id
++
+ def NewScatterPlot(self, scatt_id):
+ #TODO needs to be resolved (should be in this class)
+
+ scatt = ScatterPlotWidget(parent = self.mainPanel,
+ scatt_mgr = self.scatt_mgr,
+ scatt_id = scatt_id)
++ scatt.plotClosed.connect(self.ScatterPlotClosed)
+
+ bands = self.scatt_mgr.GetBands()
+ #TODO too low level
@@ -1726,7 +4035,7 @@
+
+ self._mgr.AddPane(scatt,
+ aui.AuiPaneInfo().Dockable(True).Floatable(True).
-+ Name("scatter plot %d" % scatt_id).MinSize((-1, 300)).
++ Name(self._getScatterPlotName(scatt_id)).MinSize((-1, 300)).
+ Caption(("%s x: %s y: %s") % (_("scatter plot"), bands[b1_id], bands[b2_id])).
+ Center().Position(1).MaximizeButton(True).
+ MinimizeButton(True).CaptionVisible(True).
@@ -1945,7 +4254,7 @@
+ dlg.CentreOnParent()
+
+ if dlg.ShowModal() == wx.ID_OK:
-+ self._setCatAttrs(cat_id=cat_id, attrs='opacity', value=dlg.GetOpacity())
++ self._setCatAttrs(cat_id=cat_id, attrs={'opacity' : value})
+
+ dlg.Destroy()
+
@@ -2093,1313 +4402,9 @@
+ 'scatt_core',
+ 'core_c',
+ ]
-Index: gui/wxpython/scatt_plot/plots.py
-===================================================================
---- gui/wxpython/scatt_plot/plots.py (revision 0)
-+++ gui/wxpython/scatt_plot/plots.py (working copy)
-@@ -0,0 +1,671 @@
-+"""!
-+ at package scatt_plot.dialogs
-+
-+ at brief Ploting widgets.
-+
-+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 wx
-+import numpy as np
-+#TODO
-+#from numpy.lib.stride_tricks import as_strided
-+from copy import deepcopy
-+
-+try:
-+ import matplotlib
-+ matplotlib.use('WXAgg')
-+ from matplotlib.figure import Figure
-+ from matplotlib.backends.backend_wxagg import \
-+ FigureCanvasWxAgg as FigCanvas, \
-+ NavigationToolbar2WxAgg as NavigationToolbar
-+ from matplotlib.lines import Line2D
-+ from matplotlib.artist import Artist
-+ from matplotlib.mlab import dist_point_to_segment
-+ from matplotlib.patches import Polygon, Ellipse
-+ import matplotlib.image as mi
-+ import matplotlib.colors as mcolors
-+ import matplotlib.cbook as cbook
-+except ImportError as e:
-+ raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
-+
-+from grass.pydispatch.signal import Signal
-+
-+#class PlotImages()?
-+
-+class ScatterPlotWidget(wx.Panel):
-+ def __init__(self, parent, scatt_id, scatt_mgr,
-+ id = wx.ID_ANY):
-+
-+ wx.Panel.__init__(self, parent, id)
-+
-+ self.parent = parent
-+ self.full_extend = None
-+
-+ self._createWidgets()
-+ self._doLayout()
-+ self.scatt_id = scatt_id
-+ self.scatt_mgr = scatt_mgr
-+ self.press_coords = None
-+
-+ self.cidpress = None
-+ self.cidrelease = None
-+
-+ self.SetSize((200, 100))
-+ self.Layout()
-+
-+ self.base_scale = 2.0
-+ self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
-+
-+ self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
-+
-+ self.contex_menu = ScatterPlotContextMenu(plot = self)
-+
-+ self.ciddscroll = None
-+
-+ def _createWidgets(self):
-+
-+ # Create the mpl Figure and FigCanvas objects.
-+ # 5x4 inches, 100 dots-per-inch
-+ #
-+ self.dpi = 100
-+ self.fig = Figure((1.0, 1.0), dpi=self.dpi)
-+ self.canvas = FigCanvas(self, -1, self.fig)
-+
-+ self.axes = self.fig.add_axes([0,0,1,1])
-+
-+ pol = Polygon(list(zip([0], [0])), animated=True)
-+ self.axes.add_patch(pol)
-+ self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
-+
-+ self.toolbar = NavigationToolbar(self.canvas)
-+
-+ def ZoomToExtend(self):
-+ if self.full_extend:
-+ self.axes.axis(self.full_extend)
-+ self.canvas.draw()
-+
-+ def SetMode(self, mode):
-+ self._deactivateMode()
-+ if mode == 'zoom':
-+ self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.zoom)
-+ elif mode == 'pan':
-+ self.toolbar.pan()
-+
-+ def FinishDrawing(self):
-+ return self.polygon_drawer.FinishDrawing()
-+
-+ def SetEditingMode(self, mode):
-+ self.polygon_drawer.SetMode(mode)
-+
-+ def _deactivateMode(self):
-+
-+ #TODO do own pan
-+ if self.toolbar._active == "PAN":
-+ self.toolbar.pan()
-+
-+ if self.ciddscroll:
-+ self.canvas.mpl_disconnect(self.ciddscroll)
-+
-+ self._stopCategoryEdit()
-+
-+
-+ def OnPress(self, event):
-+ 'on button press we will see if the mouse is over us and store some data'
-+ if not event.inaxes:
-+ return
-+
-+ if event.xdata and event.ydata:
-+ self.press_coords = { 'x' : event.xdata, 'y' : event.ydata}
-+ else:
-+ self.press_coords = None
-+
-+ def OnRelease(self, event):
-+ 'on release we reset the press data'
-+
-+ if event.xdata and event.ydata and self.press_coords:
-+
-+ bbox = {}
-+ if event.ydata > self.press_coords['y']:
-+ bbox['up_y'] = event.ydata
-+ bbox['btm_y'] = self.press_coords['y']
-+ else:
-+ bbox['up_y'] = self.press_coords['y']
-+ bbox['btm_y'] = event.ydata
-+
-+ if event.xdata > self.press_coords['x']:
-+ bbox['up_x'] = event.xdata
-+ bbox['btm_x'] = self.press_coords['x']
-+ else:
-+ bbox['up_x'] = self.press_coords['x']
-+ bbox['btm_x'] = event.xdata
-+
-+ self.scatt_mgr.SetEditCatData(self.scatt_id, bbox)
-+
-+ def _stopCategoryEdit(self):
-+ 'disconnect all the stored connection ids'
-+
-+ if self.cidpress:
-+ self.canvas.mpl_disconnect(self.cidpress)
-+ if self.cidrelease:
-+ self.canvas.mpl_disconnect(self.cidrelease)
-+ #self.canvas.mpl_disconnect(self.cidmotion)
-+
-+ def _doLayout(self):
-+
-+ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
-+ self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
-+ #self.main_sizer.Add(self.toolbar, 0, wx.EXPAND)
-+ self.SetSizer(self.main_sizer)
-+ self.main_sizer.Fit(self)
-+
-+ def Plot(self, scatts, ellipses, styles):
-+ """ Redraws the figure
-+ """
-+
-+ self.axes.clear()
-+
-+ callafter_list = []
-+
-+ init = True
-+
-+ for cat_id, scatt in scatts.iteritems():
-+ #TODO make more general
-+ if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
-+ not styles[cat_id]['show']):
-+ continue
-+
-+ b1_i = scatt['bands_info']['b1']
-+ b2_i = scatt['bands_info']['b2']
-+
-+ self.full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5)
-+ if cat_id == 0:
-+ cmap = matplotlib.cm.jet
-+ cmap.set_bad('w',1.)
-+ cmap._init()
-+ cmap._lut[len(cmap._lut) - 1, -1] = 0
-+ else:
-+ colors = styles[cat_id]['color'].split(":")
-+
-+ cmap = matplotlib.cm.jet
-+ cmap.set_bad('w',1.)
-+ cmap._init()
-+ cmap._lut[len(cmap._lut) - 1, -1] = 0
-+ cmap._lut[:, 0] = int(colors[0])/255.0
-+ cmap._lut[:, 1] = int(colors[1])/255.0
-+ cmap._lut[:, 2] = int(colors[2])/255.0
-+
-+ masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
-+
-+ vmax = np.amax(masked_cat)
-+ masked_cat = masked_cat/ float(vmax)
-+
-+ colored_cat = np.uint8(cmap(masked_cat) * 255)
-+ #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
-+
-+ if init:
-+ merged_img = colored_cat
-+ init = False
-+ else:
-+ #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape)
-+
-+ c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
-+
-+ #TODO apply strides and there will be no need for loop
-+ #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
-+ for i in range(3):
-+ m_i = merged_img[:,:,i]
-+ merged_img[:,:,i] = (m_i * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
-+
-+ merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
-+
-+ del colored_cat
-+ del c_img_a
-+
-+ del masked_cat
-+
-+ img = imshow(self.axes, merged_img,
-+ origin = 'lower',
-+ extent = self.full_extend,
-+ interpolation='nearest',
-+ aspect = "auto")
-+
-+ callafter_list.append([self.axes.draw_artist, [img]])
-+
-+ for cat_id, e in ellipses.iteritems():
-+ if cat_id == 0 or not e:
-+ continue
-+
-+ colors = styles[cat_id]['color'].split(":")
-+ ellip = Ellipse(xy=e['pos'], width=e['width'], height=e['height'], angle=e['theta'], edgecolor = "r", facecolor = 'None')
-+ self.axes.add_artist(ellip)
-+ callafter_list.append([self.axes.draw_artist, [ellip]])
-+
-+ callafter_list.append([self.fig.canvas.blit, []])
-+
-+ wx.CallAfter(lambda : self.CallAfter(callafter_list))
-+
-+ def CallAfter(self, funcs_list):
-+ while funcs_list:
-+ fcn, args = funcs_list.pop(0)
-+ fcn(*args)
-+
-+ self.canvas.draw()
-+
-+ def CleanUp(self):
-+ self.plotClosed.emit(scatt_id = self.scatt_id)
-+ self.Destroy()
-+
-+ def zoom(self, event):
-+ # get the current x and y limits
-+ if not event.inaxes:
-+ return
-+ # tcaswell
-+ # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
-+ cur_xlim = self.axes.get_xlim()
-+ cur_ylim = self.axes.get_ylim()
-+ cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
-+ cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
-+ xdata = event.xdata
-+ ydata = event.ydata
-+ if event.button == 'up':
-+ scale_factor = 1/self.base_scale
-+ elif event.button == 'down':
-+ scale_factor = self.base_scale
-+ else:
-+ scale_factor = 1
-+
-+ self.axes.set_xlim([xdata - cur_xrange*scale_factor,
-+ xdata + cur_xrange*scale_factor])
-+ self.axes.set_ylim([ydata - cur_yrange*scale_factor,
-+ ydata + cur_yrange*scale_factor])
-+
-+ self.canvas.draw()
-+
-+class ScatterPlotContextMenu:
-+ def __init__(self, plot):
-+
-+ self.plot = plot
-+ self.canvas = plot.canvas
-+ self.cidpress = self.canvas.mpl_connect(
-+ 'button_press_event', self.ContexMenu)
-+
-+ def ContexMenu(self, event):
-+ if not event.inaxes:
-+ return
-+
-+ if event.button == 3:
-+ menu = wx.Menu()
-+ menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
-+
-+ for item in menu_items:
-+ item_id = wx.ID_ANY
-+ menu.Append(item_id, text = item[1])
-+ menu.Bind(wx.EVT_MENU, item[2], id = item_id)
-+
-+ wx.CallAfter(self.ShowMenu, menu)
-+
-+ def ShowMenu(self, menu):
-+ self.plot.PopupMenu(menu)
-+ menu.Destroy()
-+ self.plot.ReleaseMouse()
-+
-+class PolygonDrawer:
-+ """
-+ An polygon editor.
-+ """
-+
-+ showverts = True
-+ epsilon = 5 #TODO settings max pixel distance to count as a vertex hit
-+ def __init__(self, ax, pol, empty_pol):
-+ if pol.figure is None:
-+ raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
-+ self.ax = ax
-+ self.canvas = pol.figure.canvas
-+
-+ self.pol = pol
-+ self.empty_pol = empty_pol
-+
-+ x, y = zip(*self.pol.xy)
-+ self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
-+ self.ax.add_line(self.line)
-+ #self._update_line(pol)
-+
-+ cid = self.pol.add_callback(self.poly_changed)
-+ self.moving_ver_idx = None # the active vert
-+
-+ self.mode = None
-+
-+ if self.empty_pol:
-+ self.Show(False)
-+
-+ self.canvas.mpl_connect('draw_event', self.draw_callback)
-+ self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
-+ self.canvas.mpl_connect('button_release_event', self.button_release_callback)
-+ self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
-+
-+ self.it = 0
-+ def SetMode(self, mode):
-+ self.mode = mode
-+
-+ def FinishDrawing(self):
-+
-+ if self.empty_pol:
-+ return None
-+
-+ self._setEmptyPol(True)
-+ coords = deepcopy(self.pol.xy)
-+
-+ return coords
-+
-+ def _setEmptyPol(self, empty_pol):
-+ self.empty_pol = empty_pol
-+ self.Show(not empty_pol)
-+
-+ def Show(self, show):
-+
-+ self.show = show
-+
-+ self.line.set_visible(self.show)
-+ self.pol.set_visible(self.show)
-+
-+ self.Redraw()
-+
-+ def Redraw(self):
-+ if self.show:
-+ self.ax.draw_artist(self.pol)
-+ self.ax.draw_artist(self.line)
-+ self.canvas.blit(self.ax.bbox)
-+ self.canvas.draw()
-+
-+ def draw_callback(self, event):
-+ self.background = self.canvas.copy_from_bbox(self.ax.bbox)
-+ self.ax.draw_artist(self.pol)
-+ self.ax.draw_artist(self.line)
-+
-+ def poly_changed(self, pol):
-+ 'this method is called whenever the polygon object is called'
-+ # only copy the artist props to the line (except visibility)
-+ vis = self.line.get_visible()
-+ Artist.update_from(self.line, pol)
-+ self.line.set_visible(vis) # don't use the pol visibility state
-+
-+ def get_ind_under_point(self, event):
-+ 'get the index of the vertex under point if within epsilon tolerance'
-+
-+ # display coords
-+ xy = np.asarray(self.pol.xy)
-+ xyt = self.pol.get_transform().transform(xy)
-+ xt, yt = xyt[:, 0], xyt[:, 1]
-+ d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
-+ indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
-+ ind = indseq[0]
-+
-+ if d[ind]>=self.epsilon:
-+ ind = None
-+
-+ return ind
-+
-+ def OnButtonPressed(self, event):
-+ if not event.inaxes:
-+ return
-+
-+ if event.button in [2, 3]:
-+ return
-+
-+ if self.mode == "delete_vertex":
-+ self._deleteVertex(event)
-+ elif self.mode == "add_boundary_vertex":
-+ self._addVertexOnBoundary(event)
-+ elif self.mode == "add_vertex":
-+ self._addVertex(event)
-+ self.moving_ver_idx = self.get_ind_under_point(event)
-+
-+ def button_release_callback(self, event):
-+ 'whenever a mouse button is released'
-+ if not self.showverts: return
-+ if event.button != 1: return
-+ self.moving_ver_idx = None
-+
-+ def ShowVertices(self, show):
-+ self.showverts = show
-+ self.line.set_visible(self.showverts)
-+ if not self.showverts: self.moving_ver_idx = None
-+
-+ def _deleteVertex(self, event):
-+ ind = self.get_ind_under_point(event)
-+
-+ if ind is None or self.empty_pol:
-+ return
-+
-+ if len(self.pol.xy) <= 2:
-+ self.empty_pol = True
-+ self.Show(False)
-+ return
-+
-+ coords = []
-+ for i,tup in enumerate(self.pol.xy):
-+ if i == ind:
-+ continue
-+ elif i == 0 and ind == len(self.pol.xy) - 1:
-+ continue
-+ elif i == len(self.pol.xy) - 1 and ind == 0:
-+ continue
-+
-+ coords.append(tup)
-+
-+ self.pol.xy = coords
-+ self.line.set_data(zip(*self.pol.xy))
-+
-+ self.Redraw()
-+
-+ def _addVertexOnBoundary(self, event):
-+ if self.empty_pol:
-+ return
-+
-+ xys = self.pol.get_transform().transform(self.pol.xy)
-+ p = event.x, event.y # display coords
-+ for i in range(len(xys)-1):
-+ s0 = xys[i]
-+ s1 = xys[i+1]
-+ d = dist_point_to_segment(p, s0, s1)
-+
-+ if d<=self.epsilon:
-+ self.pol.xy = np.array(
-+ list(self.pol.xy[:i + 1]) +
-+ [(event.xdata, event.ydata)] +
-+ list(self.pol.xy[i + 1:]))
-+ self.line.set_data(zip(*self.pol.xy))
-+ break
-+
-+ self.Redraw()
-+
-+ def _addVertex(self, event):
-+
-+ if self.empty_pol:
-+ pt = (event.xdata, event.ydata)
-+ self.pol.xy = np.array([pt, pt])
-+ self.Show(True)
-+ self.empty_pol = False
-+ else:
-+ self.pol.xy = np.array(
-+ [(event.xdata, event.ydata)] +
-+ list(self.pol.xy[1:]) +
-+ [(event.xdata, event.ydata)])
-+
-+ self.line.set_data(zip(*self.pol.xy))
-+
-+ self.Redraw()
-+
-+ def motion_notify_callback(self, event):
-+ 'on mouse movement'
-+ if not self.mode == "move_vertex": return
-+ if not self.showverts: return
-+ if self.empty_pol: return
-+ if self.moving_ver_idx is None: return
-+ if event.inaxes is None: return
-+ if event.button != 1: return
-+
-+ self.it += 1
-+
-+ x,y = event.xdata, event.ydata
-+
-+ self.pol.xy[self.moving_ver_idx] = x,y
-+ if self.moving_ver_idx == 0:
-+ self.pol.xy[len(self.pol.xy) - 1] = x,y
-+ elif self.moving_ver_idx == len(self.pol.xy) - 1:
-+ self.pol.xy[0] = x,y
-+
-+ self.line.set_data(zip(*self.pol.xy))
-+
-+ self.canvas.restore_region(self.background)
-+
-+ self.Redraw()
-+
-+class ModestImage(mi.AxesImage):
-+ """
-+ Computationally modest image class.
-+
-+ ModestImage is an extension of the Matplotlib AxesImage class
-+ better suited for the interactive display of larger images. Before
-+ drawing, ModestImage resamples the data array based on the screen
-+ resolution and view window. This has very little affect on the
-+ appearance of the image, but can substantially cut down on
-+ computation since calculations of unresolved or clipped pixels
-+ are skipped.
-+
-+ The interface of ModestImage is the same as AxesImage. However, it
-+ does not currently support setting the 'extent' property. There
-+ may also be weird coordinate warping operations for images that
-+ I'm not aware of. Don't expect those to work either.
-+
-+ Author: Chris Beaumont <beaumont at hawaii.edu>
-+ """
-+ def __init__(self, *args, **kwargs):
-+ #why???
-+ #if 'extent' in kwargs and kwargs['extent'] is not None:
-+ # raise NotImplementedError("ModestImage does not support extents")
-+
-+ self._full_res = None
-+ self._sx, self._sy = None, None
-+ self._bounds = (None, None, None, None)
-+ super(ModestImage, self).__init__(*args, **kwargs)
-+
-+ def set_data(self, A):
-+ """
-+ Set the image array
-+
-+ ACCEPTS: numpy/PIL Image A
-+ """
-+ self._full_res = A
-+ self._A = A
-+
-+ if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
-+ np.float):
-+ raise TypeError("Image data can not convert to float")
-+
-+ if (self._A.ndim not in (2, 3) or
-+ (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
-+ raise TypeError("Invalid dimensions for image data")
-+
-+ self._imcache =None
-+ self._rgbacache = None
-+ self._oldxslice = None
-+ self._oldyslice = None
-+ self._sx, self._sy = None, None
-+
-+ def get_array(self):
-+ """Override to return the full-resolution array"""
-+ return self._full_res
-+
-+ def _scale_to_res(self):
-+ """ Change self._A and _extent to render an image whose
-+ resolution is matched to the eventual rendering."""
-+
-+ ax = self.axes
-+ ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
-+ xlim, ylim = ax.get_xlim(), ax.get_ylim()
-+ dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
-+
-+ y0 = max(0, ylim[0] - 5)
-+ y1 = min(self._full_res.shape[0], ylim[1] + 5)
-+ x0 = max(0, xlim[0] - 5)
-+ x1 = min(self._full_res.shape[1], xlim[1] + 5)
-+ y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
-+
-+ sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
-+ sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
-+
-+ # have we already calculated what we need?
-+ if sx == self._sx and sy == self._sy and \
-+ x0 == self._bounds[0] and x1 == self._bounds[1] and \
-+ y0 == self._bounds[2] and y1 == self._bounds[3]:
-+ return
-+
-+ self._A = self._full_res[y0:y1:sy, x0:x1:sx]
-+ self._A = cbook.safe_masked_invalid(self._A)
-+ x1 = x0 + self._A.shape[1] * sx
-+ y1 = y0 + self._A.shape[0] * sy
-+
-+ self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
-+ self._sx = sx
-+ self._sy = sy
-+ self._bounds = (x0, x1, y0, y1)
-+ self.changed()
-+
-+ def draw(self, renderer, *args, **kwargs):
-+ self._scale_to_res()
-+ super(ModestImage, self).draw(renderer, *args, **kwargs)
-+
-+
-+def imshow(axes, X, cmap=None, norm=None, aspect=None,
-+ interpolation=None, alpha=None, vmin=None, vmax=None,
-+ origin=None, extent=None, shape=None, filternorm=1,
-+ filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
-+ """Similar to matplotlib's imshow command, but produces a ModestImage
-+
-+ Unlike matplotlib version, must explicitly specify axes
-+ Author: Chris Beaumont <beaumont at hawaii.edu>
-+ """
-+
-+ if not axes._hold:
-+ axes.cla()
-+ if norm is not None:
-+ assert(isinstance(norm, mcolors.Normalize))
-+ if aspect is None:
-+ aspect = rcParams['image.aspect']
-+ axes.set_aspect(aspect)
-+ im = ModestImage(axes, cmap, norm, interpolation, origin, extent,
-+ filternorm=filternorm,
-+ filterrad=filterrad, resample=resample, **kwargs)
-+
-+ im.set_data(X)
-+ im.set_alpha(alpha)
-+ axes._set_artist_props(im)
-+
-+ if im.get_clip_path() is None:
-+ # image does not already have clipping set, clip to axes patch
-+ im.set_clip_path(axes.patch)
-+
-+ #if norm is None and shape is None:
-+ # im.set_clim(vmin, vmax)
-+ if vmin is not None or vmax is not None:
-+ im.set_clim(vmin, vmax)
-+ else:
-+ im.autoscale_None()
-+ im.set_url(url)
-+
-+ # update ax.dataLim, and, if autoscaling, set viewLim
-+ # to tightly fit the image, regardless of dataLim.
-+ im.set_extent(im.get_extent())
-+
-+ axes.images.append(im)
-+ im._remove_method = lambda h: axes.images.remove(h)
-+
-+ return im
-\ No newline at end of file
-Index: gui/wxpython/scatt_plot/controllers.py
-===================================================================
---- gui/wxpython/scatt_plot/controllers.py (revision 0)
-+++ gui/wxpython/scatt_plot/controllers.py (working copy)
-@@ -0,0 +1,621 @@
-+"""!
-+ at package scatt_plot.controllers
-+
-+ at brief Controller layer for scatter plot tool.
-+
-+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
-+
-+#TODO
-+import wx
-+
-+from core.gcmd import GException, GError, GMessage, RunCommand
-+
-+from scatt_plot.scatt_core import Core, idBandsToidScatt
-+
-+from scatt_plot.dialogs import AddScattPlotDialog
-+from scatt_plot.gthreading import gThread
-+from core.gconsole import EVT_CMD_DONE
-+from grass.pydispatch.signal import Signal
-+
-+class ScattsManager(wx.EvtHandler):
-+ def __init__(self, guiparent, giface, iclass_mapwin):
-+ #TODO remove iclass parameter
-+
-+ wx.EvtHandler.__init__(self)
-+ self.giface = giface
-+ self.mapDisp = giface.GetMapDisplay()
-+
-+ if iclass_mapwin:
-+ self.mapWin = iclass_mapwin
-+ else:
-+ self.mapWin = giface.GetMapWindow()
-+
-+ self.guiparent = guiparent
-+
-+ self.show_add_scatt_plot = False
-+
-+ self.core = Core()
-+ self.scatts_dt = self.core.GetScattsData()
-+
-+ self.cats_mgr = CategoriesManager(self, self.core)
-+
-+ self.thread = gThread(self);
-+
-+ self.plots = {}
-+ self.added_cats_rasts = {}
-+
-+ self.cats_to_update = []
-+
-+ self.plot_mode = None
-+ self.pol_sel_mode = [False, None]
-+
-+ self.data_set = False
-+
-+ if iclass_mapwin:
-+ self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
-+ self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
-+ else:
-+ self.mapWin_conn = None
-+ self.iclass_conn = None
-+
-+ self.tasks_pids = {
-+ 'add_scatt' : [],
-+ 'set_data' : -1,
-+ 'set_data_add' : -1,
-+ 'set_edit_cat_data' : -1,
-+ 'mapwin_conn' : [],
-+ 'render_plots' : -1
-+ }
-+
-+ self.Bind(EVT_CMD_DONE, self.OnThreadDone)
-+
-+ def OnThreadDone(self, event):
-+
-+ if event.exception:
-+ GError(str(event.exception))
-+ return
-+
-+ if event.pid in self.tasks_pids['mapwin_conn']:
-+ self.tasks_pids['mapwin_conn'].remove(event.pid)
-+ updated_cats = event.ret
-+
-+ for cat in updated_cats:
-+ if cat not in self.cats_to_update:
-+ self.cats_to_update.append(cat)
-+
-+ if not self.tasks_pids['mapwin_conn']:
-+ self.tasks_pids['render_plots'] = self.thread.GetId()
-+ self.thread.Run(callable = self.core.ComputeCatsScatts,
-+ cats_ids = self.cats_to_update[:])
-+ del self.cats_to_update[:]
-+
-+ return
-+
-+ if self.tasks_pids['render_plots'] == event.pid:
-+ self.RenderScattPlts()
-+ return
-+
-+ if event.pid in self.tasks_pids['add_scatt']:
-+ self.tasks_pids['add_scatt'].remove(event.pid)
-+ self.AddScattPlotDone(event)
-+ return
-+
-+ if self.tasks_pids['set_data'] == event.pid:
-+ self.SetDataDone(event)
-+ return
-+
-+ if self.tasks_pids['set_data_add'] == event.pid:
-+ self.SetDataDone(event)
-+ self.AddScattPlot()
-+ return
-+
-+ if self.tasks_pids['set_edit_cat_data'] == event.pid:
-+ self.SetEditCatDataDone(event)
-+ return
-+
-+ def SetData(self, bands):
-+ self.data_set = False
-+
-+ if self.show_add_scatt_plot:
-+ self.tasks_pids['set_data_add'] = self.thread.GetId()
-+ else:
-+ self.tasks_pids['set_data'] = self.thread.GetId()
-+
-+ self.thread.Run(callable = self.core.SetData, bands = bands)
-+
-+ def SetDataDone(self, event):
-+
-+ self.data_set = True
-+ self.cats_mgr.InitCoreCats()
-+
-+ def OnOutput(self, event):
-+ """!Print thread output according to debug level.
-+ """
-+ print event.text
-+
-+ def GetBands(self):
-+ return self.core.GetBands()
-+
-+ def AddScattPlot(self):
-+ if not self.data_set and self.iclass_conn:
-+ self.show_add_scatt_plot = True
-+ self.iclass_conn.SetData()
-+ self.show_add_scatt_plot = False
-+ return
-+ if not self.data_set:
-+ GError(_('No data set.'))
-+ return
-+
-+ dlg = AddScattPlotDialog(parent = self.guiparent, bands = self.core.GetBands())
-+ if dlg.ShowModal() == wx.ID_OK:
-+ self._addScattPlot(dlg.GetScattId())
-+
-+ dlg.Destroy()
-+
-+ return
-+
-+ #for testing
-+ jj = len(self.core.GetBands()) - 1
-+
-+ max_k = 20
-+ k = 0
-+ for i in range(jj):
-+ for j in range(i, jj):
-+ print i * (len(self.core.GetBands()) - 1) + j
-+ self._addScattPlot(i * (len(self.core.GetBands()) - 1) + j)
-+ k += 1
-+ if k == max_k:
-+ break
-+ if k == max_k:
-+ break
-+ jj -= 1
-+
-+
-+ def _addScattPlot(self, scatt_id):
-+ if self.plots.has_key(scatt_id):
-+ GMessage(_("Scatter plot has been already added."))
-+ return
-+
-+ self.tasks_pids['add_scatt'].append(self.thread.GetId())
-+
-+ self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id)
-+
-+ def RenderScattPlts(self):
-+
-+ cats_attrs = self.cats_mgr.GetCategoriesAttrs()
-+ for scatt_id, scatt in self.plots.iteritems():
-+ scatt_dt = self.scatts_dt.GetScatt(scatt_id)
-+ #scatt_dt = self.scatts_dt.GetScatt(scatt_id, cats_ids)
-+ ellipses_dt = self.scatts_dt.GetEllipses(scatt_id)
-+ self.thread.Run(callable = scatt.Plot, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
-+
-+ def AddScattPlotDone(self, event):
-+
-+ scatt_id = event.kwds['scatt_id']
-+
-+ #TODO guiparent - not very good
-+ self.plots[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id)
-+ self.plots[scatt_id].plotClosed.connect(self.PlotClosed)
-+
-+ if self.plot_mode:
-+ self.plots[scatt_id].SetMode(self.plot_mode)
-+
-+ if self.tasks_pids['add_scatt']:
-+ return
-+ #TODO no need to render all
-+ self.RenderScattPlts()
-+
-+ def PlotClosed(self, scatt_id):
-+ del self.plots[scatt_id]
-+
-+ def CleanUp(self):
-+ self.core.CleanUp()
-+
-+ for scatt_id, scatt in self.plots.items():
-+ scatt.CleanUp()
-+ del self.plots[scatt_id]
-+
-+ def SetPlotsMode(self, mode):
-+
-+ self.plot_mode = mode
-+ for scatt in self.plots.itervalues():
-+ scatt.SetMode(mode)
-+
-+ def ActivateSelectionPolygonMode(self, activate):
-+ self.pol_sel_mode[0] = activate
-+ return activate
-+
-+ def ProcessSelectionPolygons(self, process_mode):
-+
-+ scatts_polygons = {}
-+ for scatt_id, scatt in self.plots.iteritems():
-+ coords = scatt.FinishDrawing()
-+ if coords is not None:
-+ scatts_polygons[scatt_id] = coords
-+
-+ if not scatts_polygons:
-+ return
-+
-+ value = 1
-+ if process_mode == 'remove':
-+ value = 0
-+
-+ sel_cat_id = self.cats_mgr.GetSelectedCat()
-+ if not sel_cat_id:
-+ dlg = wx.MessageDialog(parent = self.guiparent,
-+ message = _("In order to select arrea in scatter plot, "
-+ "you have to select class first.\n\n"
-+ "There is no class yet, "
-+ "do you want to create one?"),
-+ caption = _("No class selected"),
-+ style = wx.YES_NO)
-+ if dlg.ShowModal() == wx.ID_YES:
-+ self.iclass_conn.EmptyCategories()
-+
-+
-+ sel_cat_id = self.cats_mgr.GetSelectedCat()
-+ if not sel_cat_id:
-+ return
-+
-+ self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
-+ self.thread.Run(callable = self.core.UpdateCategoryWithPolygons,
-+ cat_id = sel_cat_id,
-+ scatts_pols = scatts_polygons,
-+ value = value)
-+
-+ def SetPlotsEditingMode(self, mode):
-+
-+ self.pol_sel_mode[1] = mode
-+ for scatt in self.plots.itervalues():
-+ scatt.SetEditingMode(mode)
-+
-+ def SetEditCatDataDone(self, event):
-+
-+ if event.exception:
-+ GError(_("Error occured during computation of scatter plot category:\n%s"),
-+ parent = self.guiparent, showTraceback = False)
-+
-+ cat_id = event.ret
-+
-+ self.RenderScattPlts()
-+
-+ cat_id = event.kwds["cat_id"]
-+
-+ cat_rast = self.core.GetCatRast(cat_id)
-+
-+ if cat_rast not in self.added_cats_rasts.values():
-+
-+ cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+
-+
-+ region = self.core.GetRegion()
-+ ret, err_msg = RunCommand('r.region',
-+ map = cat_rast,
-+ getErrorMsg = True,
-+ n = "%f" % region['n'],
-+ s = "%f" % region['s'],
-+ e = "%f" % region['e'],
-+ w = "%f" % region['w'],
-+ )
-+
-+ ret, err_msg = RunCommand('r.colors',
-+ map = cat_rast,
-+ rules = "-",
-+ stdin = "1 %s" % cats_attrs["color"],
-+ getErrorMsg = True)
-+
-+ if ret != 0:
-+ GError(_("r.region failed\n%s" % err_msg))
-+
-+ self.mapWin.Map.AddLayer(ltype = "raster", name = "cat_%d" % cat_id, render = True,
-+ command = ["d.rast", "map=%s" % cat_rast, "values=1"])
-+
-+
-+ if ret != 0:
-+ GError(_("r.region failed\n%s" % err_msg))
-+
-+ self.added_cats_rasts[cat_id] = cat_rast
-+
-+ self.giface.updateMap.emit()
-+
-+ def DigitDataChanged(self, vectMap, digit):
-+
-+ if self.mapWin_conn:
-+ self.mapWin_conn.DigitDataChanged(vectMap, digit)
-+ return 1
-+ else:
-+ return 0
-+
-+ def GetCategoriesManager(self):
-+ return self.cats_mgr
-+
-+
-+class CategoriesManager:
-+
-+ def __init__(self, scatt_mgr, core):
-+
-+ self.core = core
-+ self.scatt_mgr = scatt_mgr
-+
-+ self.cats = {}
-+ self.cats_ids = []
-+
-+ self.sel_cat_id = None
-+
-+ self.initialized = Signal('CategoriesManager.initialized')
-+ self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
-+ self.deletedCategory = Signal('CategoriesManager.deletedCategory')
-+ self.addedCategory = Signal('CategoriesManager.addedCategory')
-+
-+ def Clear(self):
-+
-+ self.cats.clear()
-+ del self.cats_ids[:]
-+
-+ self.sel_cat_id = None
-+
-+ def InitCoreCats(self):
-+ if self.scatt_mgr.data_set:
-+ for cat_id in self.cats_ids:
-+ self.core.AddCategory(cat_id)
-+
-+ def AddCategory(self, cat_id = None, name = None, color = None):
-+
-+ if cat_id is None:
-+ if self.cats_ids:
-+ cat_id = max(self.cats_ids) + 1
-+ else:
-+ cat_id = 1
-+
-+ if self.scatt_mgr.data_set:
-+ ret = self.core.AddCategory(cat_id)
-+ if ret < 0: #TODO
-+ return -1;
-+
-+ self.cats[cat_id] = {
-+ 'name' : _('Category %s' % cat_id ),
-+ 'color' : "0:0:0",
-+ 'opacity' : 1.0,
-+ 'show' : True
-+ }
-+
-+ self.cats_ids.append(cat_id)
-+
-+ if name is not None:
-+ self.cats[cat_id]["name"] = name
-+
-+ if color is not None:
-+ self.cats[cat_id]["color"] = color
-+
-+ self.addedCategory.emit(cat_id = cat_id,
-+ name = self.cats[cat_id]["name"],
-+ color = self.cats[cat_id]["color"] )
-+ return cat_id
-+
-+ def SetCategoryAttrs(self, cat_id, attrs_dict):
-+ render = False
-+ for k, v in attrs_dict.iteritems():
-+ if not render and k in ['name', 'color', 'opacity', 'show']:
-+ render = True
-+
-+ self.cats[cat_id][k] = v
-+
-+ #TODO optimization
-+ if render:
-+ self.scatt_mgr.RenderScattPlts()
-+
-+ self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
-+
-+ def DeleteCategory(self, cat_id):
-+
-+ if self.scatt_mgr.data_set:
-+ self.core.DeleteCategory(cat_id)
-+ del self.cats[cat_id]
-+ self.cats_ids.remove(cat_id)
-+
-+ self.deletedCategory.emit(cat_id = cat_id)
-+
-+ #TODO emit event?
-+ def SetSelectedCat(self, cat_id):
-+ self.sel_cat_id = cat_id
-+
-+ def GetSelectedCat(self):
-+ return self.sel_cat_id
-+
-+ def GetCategoryAttrs(self, cat_id):
-+ #TODO is mutable
-+ return self.cats[cat_id]
-+
-+ def GetCategoriesAttrs(self):
-+ #TODO is mutable
-+ return self.cats
-+
-+ def GetCategories(self):
-+ return self.cats_ids[:]
-+
-+ def SetCategoryPosition(self):
-+ if newindex > oldindex:
-+ newindex -= 1
-+
-+ self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
-+
-+class MapWinConnection:
-+ def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
-+ self.mapWin = mapWin
-+ self.vectMap = None
-+ self.scatt_rast_updater = scatt_rast_updater
-+ self.scatt_mgr = scatt_mgr
-+ self.cats_mgr = scatt_mgr.cats_mgr
-+
-+ self.thread = self.scatt_mgr.thread
-+
-+ #TODO
-+ self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
-+
-+ #def ChangeMap(self, vectMap, layers_cats):
-+ # self.vectMap = vectMap
-+ # self.layers_cats = layers_cats
-+
-+ #ret, region, msg = RunCommand("v.to.rast",
-+ # flags = "gp",
-+ # getErrorMsg = True,
-+ # read = True)
-+
-+ def _connectSignals(self):
-+ self.digit.featureAdded.connect(self.AddFeature)
-+ self.digit.areasDeleted.connect(self.DeleteAreas)
-+ self.digit.featuresDeleted.connect(self.DeleteAreas)
-+ self.digit.vertexMoved.connect(self.EditedFeature)
-+ self.digit.vertexRemoved.connect(self.EditedFeature)
-+ self.digit.lineEdited.connect(self.EditedFeature)
-+ self.digit.featuresMoved.connect(self.EditedFeature)
-+
-+ def AddFeature(self, new_bboxs, new_areas_cats):
-+ if not self.scatt_mgr.data_set:
-+ return
-+
-+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
-+ new_bboxs = new_bboxs,
-+ old_bboxs = [],
-+ old_areas_cats = [],
-+ new_areas_cats = new_areas_cats)
-+
-+ def DeleteAreas(self, old_bboxs, old_areas_cats):
-+ if not self.scatt_mgr.data_set:
-+ return
-+
-+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
-+ new_bboxs = [],
-+ old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = [])
-+
-+
-+ def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
-+ if not self.scatt_mgr.data_set:
-+ return
-+
-+ self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+ self.thread.Run(callable = self.scatt_rast_updater.EditedFeature,
-+ new_bboxs = new_bboxs,
-+ old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = new_areas_cats)
-+
-+ def DigitDataChanged(self, vectMap, digit):
-+
-+ self.digit = digit
-+ self.vectMap = vectMap
-+
-+ self.digit.EmitSignals(emit = True)
-+
-+ self.scatt_rast_updater.SetVectMap(vectMap)
-+
-+ self._connectSignals()
-+
-+
-+class IClassConnection:
-+ def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
-+ self.iclass_frame = iclass_frame
-+ self.stats_data = self.iclass_frame.stats_data
-+ self.cats_mgr = cats_mgr
-+ self.scatt_mgr = scatt_mgr
-+
-+ self.stats_data.statisticsAdded.connect(self.AddCategory)
-+ self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
-+ self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
-+ self.stats_data.statisticsSet.connect(self.SetCategory)
-+
-+ self.iclass_frame.groupSet.connect(self.GroupSet)
-+
-+ self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
-+ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+ self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+ self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
-+
-+ self.SyncCats()
-+
-+ def SetData(self):
-+ self.iclass_frame.AddBands()
-+
-+ def EmptyCategories(self):
-+ self.iclass_frame.OnCategoryManager(None)
-+
-+ def SyncCats(self):
-+ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
-+ cats = self.stats_data.GetCategories()
-+ for c in cats:
-+ stats = self.stats_data.GetStatistics(c)
-+ self.cats_mgr.AddCategory(c, stats.name, stats.color)
-+ self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+ def CategoryChanged(self, cat):
-+ self.cats_mgr.SetSelectedCat(cat)
-+
-+ def AddCategory(self, cat, name, color):
-+ self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
-+ self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color)
-+ self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+ def DeleteCategory(self, cat):
-+ self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
-+ self.cats_mgr.DeleteCategory(cat)
-+ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+
-+ def DeletAllCategories(self):
-+
-+ self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
-+ cats = self.stats_data.GetCategories()
-+ for c in cats:
-+ self.cats_mgr.DeleteCategory(c)
-+ self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+
-+ def SetCategory(self, cat, stats):
-+
-+ self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
-+ cats_attr = {}
-+ for attr in ['name', 'color']:
-+ if stats.has_key(attr):
-+ cats_attr[attr] = stats[attr]
-+
-+ if cats_attr:
-+ self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
-+ self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
-+
-+
-+ def SetStatistics(self, cat_id, attrs_dict):
-+ self.stats_data.statisticsSet.disconnect(self.SetCategory)
-+ self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
-+ self.stats_data.statisticsSet.connect(self.SetCategory)
-+
-+ def AddStatistics(self, cat_id, name, color):
-+ self.stats_data.statisticsAdded.disconnect(self.AddCategory)
-+ self.stats_data.AddStatistics(cat_id, name, color)
-+ self.stats_data.statisticsAdded.connect(self.AddCategory)
-+
-+ def DeleteStatistics(self, cat_id):
-+ self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
-+ self.stats_data.DeleteStatistics(cat_id)
-+ self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
-+
-+ def GroupSet(self, group):
-+ res = RunCommand('i.group',
-+ flags = 'g',
-+ group = group, subgroup = group,
-+ read = True).strip()
-+ if res.split('\n')[0]:
-+ bands = res.split('\n')
-+ self.scatt_mgr.SetData(bands)
-\ No newline at end of file
Index: gui/wxpython/Makefile
===================================================================
---- gui/wxpython/Makefile (revision 57556)
+--- gui/wxpython/Makefile (revision 57581)
+++ gui/wxpython/Makefile (working copy)
@@ -13,7 +13,7 @@
$(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
@@ -3421,1341 +4426,6 @@
DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
default: $(DSTFILES)
-Index: gui/wxpython/vdigit/wxdigit.py
-===================================================================
---- gui/wxpython/vdigit/wxdigit.py (revision 57556)
-+++ gui/wxpython/vdigit/wxdigit.py (working copy)
-@@ -17,7 +17,7 @@
- (and NumPy would be an excellent candidate for acceleration via
- e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
-
--(C) 2007-2011 by the GRASS Development Team
-+(C) 2007-2011, 2013 by the GRASS Development Team
-
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
-@@ -27,6 +27,8 @@
-
- import grass.script.core as grass
-
-+from grass.pydispatch.signal import Signal
-+
- from core.gcmd import GError
- from core.debug import Debug
- from core.settings import UserSettings
-@@ -176,7 +178,21 @@
-
- if self.poMapInfo:
- self.InitCats()
--
-+
-+ self.emit_signals = False
-+
-+ # signals which describes features changes during digitization,
-+ # activate them using EmitSignals method
-+ #TODO signal for errors?
-+ self.featureAdded = Signal('IVDigit.featureAdded')
-+ self.areasDeleted = Signal('IVDigit.areasDeleted')
-+ self.vertexMoved = Signal('IVDigit.vertexMoved')
-+ self.vertexAdded = Signal('IVDigit.vertexAdded')
-+ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
-+ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
-+ self.featuresMoved = Signal('IVDigit.featuresMoved')
-+ self.lineEdited = Signal('IVDigit.lineEdited')
-+
- def __del__(self):
- Debug.msg(1, "IVDigit.__del__()")
- Vect_destroy_line_struct(self.poPoints)
-@@ -188,7 +204,12 @@
- Vect_close(self.poBgMapInfo)
- self.poBgMapInfo = self.popoBgMapInfo = None
- del self.bgMapInfo
--
-+
-+ def EmitSignals(self, emit):
-+ """!Activate/deactivate signals which describes features changes during digitization.
-+ """
-+ self.emit_signals = emit
-+
- def CloseBackgroundMap(self):
- """!Close background vector map"""
- if not self.poBgMapInfo:
-@@ -394,7 +415,6 @@
-
- @return tuple (number of added features, feature ids)
- """
--
- layer = self._getNewFeaturesLayer()
- cat = self._getNewFeaturesCat()
-
-@@ -419,10 +439,14 @@
- return (-1, None)
-
- self.toolbar.EnableUndo()
--
-- return self._addFeature(vtype, points, layer, cat,
-- self._getSnapMode(), self._display.GetThreshold())
--
-+
-+ ret = self._addFeature(vtype, points, layer, cat,
-+ self._getSnapMode(), self._display.GetThreshold())
-+ if ret[0] > -1 and self.emit_signals:
-+ self.featureAdded.emit(new_bboxs = [self._createBbox(points)], new_areas_cats = [[{layer : [cat]}, None]])
-+
-+ return ret
-+
- def DeleteSelectedLines(self):
- """!Delete selected features
-
-@@ -434,16 +458,27 @@
- # collect categories for deleting if requested
- deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
- catDict = dict()
-+
-+ old_bboxs = []
-+ old_areas_cats = []
- if deleteRec:
- for i in self._display.selected['ids']:
-+
- if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
- self._error.ReadLine(i)
-
-- cats = self.poCats.contents
-- for j in range(cats.n_cats):
-- if cats.field[j] not in catDict.keys():
-- catDict[cats.field[j]] = list()
-- catDict[cats.field[j]].append(cats.cat[j])
-+ if self.emit_signals:
-+ ret = self._getLineAreaBboxCats(i)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ # catDict was not used -> put into comment
-+ #cats = self.poCats.contents
-+ #for j in range(cats.n_cats):
-+ # if cats.field[j] not in catDict.keys():
-+ # catDict[cats.field[j]] = list()
-+ # catDict[cats.field[j]].append(cats.cat[j])
-
- poList = self._display.GetSelectedIList()
- nlines = Vedit_delete_lines(self.poMapInfo, poList)
-@@ -456,7 +491,10 @@
- self._deleteRecords(catDict)
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
-+
- return nlines
-
- def _deleteRecords(self, cats):
-@@ -512,22 +550,173 @@
-
- @return number of deleted
- """
-+ if len(self._display.selected['ids']) < 1:
-+ return 0
-+
- poList = self._display.GetSelectedIList()
- cList = poList.contents
-
- nareas = 0
-+ old_bboxs = []
-+ old_areas_cats = []
-+
- for i in range(cList.n_values):
-+
- if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
- continue
--
-+
-+ if self.emit_signals:
-+ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
-+ if area > 0:
-+ bbox, cats = self._getaAreaBboxCats(area)
-+ old_bboxs += bbox
-+ old_areas_cats += cats
-+
- nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
-
- if nareas > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
-+ if self.emit_signals:
-+ self.areasDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
-+
-+ return nareas
-+
-+ def _getLineAreaBboxCats(self, ln_id):
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+
-+ if ltype == GV_CENTROID:
-+ #TODO centroid opttimization, can be adited also its area -> it will appear two times in new_ lists
-+ return self._getCentroidAreaBboxCats(ln_id)
-+ else:
-+ return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
-+
-+
-+ def _getCentroidAreaBboxCats(self, centroid):
-+ if not Vect_line_alive(self.poMapInfo, centroid):
-+ return None
-+
-+ area = Vect_get_centroid_area(self.poMapInfo, centroid)
-+ if area > 0:
-+ return self._getaAreaBboxCats(area)
-+ else:
-+ return None
-+
-+ def _getaAreaBboxCats(self, area):
-+
-+ po_b_list = Vect_new_list()
-+ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
-+ b_list = po_b_list.contents
-+
-+ geoms = []
-+ areas_cats = []
-+
-+ if b_list.n_values > 0:
-+ for i_line in range(b_list.n_values):
-+
-+ line = b_list.value[i_line];
-+
-+ geoms.append(self._getBbox(abs(line)))
-+ areas_cats.append(self._getLineAreasCategories(abs(line)))
-
-- return nareas
-+ Vect_destroy_list(po_b_list);
-+
-+ return geoms, areas_cats
-+
-+ def _getLineAreasCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return []
-+
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+ if ltype != GV_BOUNDARY:
-+ return []
-+
-+ cats = [None, None]
-+
-+ left = c_int()
-+ right = c_int()
-+
-+ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
-+ areas = [left.value, right.value]
-+
-+ for i, a in enumerate(areas):
-+ if a > 0:
-+ centroid = Vect_get_area_centroid(self.poMapInfo, a)
-+ if centroid <= 0:
-+ continue
-+ c = self._getCategories(centroid)
-+ if c:
-+ cats[i] = c
-+
-+ return cats
-+
-+ def _getCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return none
-+
-+ poCats = Vect_new_cats_struct()
-+ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
-+ Vect_destroy_cats_struct(poCats)
-+ return None
-+
-+ cCats = poCats.contents
-+
-+ cats = {}
-+ for j in range(cCats.n_cats):
-+ if cats.has_key(cCats.field[j]):
-+ cats[cCats.field[j]].append(cCats.cat[j])
-+ else:
-+ cats[cCats.field[j]] = [cCats.cat[j]]
-
-+ Vect_destroy_cats_struct(poCats)
-+ return cats
-+
-+ def _getBbox(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return None
-+
-+ poPoints = Vect_new_line_struct()
-+ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
-+ Vect_destroy_line_struct(poPoints)
-+ return []
-+
-+ geom = self._convertGeom(poPoints)
-+ bbox = self._createBbox(geom)
-+ Vect_destroy_line_struct(poPoints)
-+ return bbox
-+
-+ def _createBbox(self, points):
-+
-+ bbox = {}
-+ for pt in points:
-+ if not bbox.has_key('maxy'):
-+ bbox['maxy'] = pt[1]
-+ bbox['miny'] = pt[1]
-+ bbox['maxx'] = pt[0]
-+ bbox['minx'] = pt[0]
-+ continue
-+
-+ if bbox['maxy'] < pt[1]:
-+ bbox['maxy'] = pt[1]
-+ elif bbox['miny'] > pt[1]:
-+ bbox['miny'] = pt[1]
-+
-+ if bbox['maxx'] < pt[0]:
-+ bbox['maxx'] = pt[0]
-+ elif bbox['minx'] > pt[0]:
-+ bbox['minx'] = pt[0]
-+ return bbox
-+
-+ def _convertGeom(self, poPoints):
-+
-+ Points = poPoints.contents
-+
-+ pts_geom = []
-+ for j in range(Points.n_points):
-+ pts_geom.append((Points.x[j], Points.y[j]))
-+
-+ return pts_geom
-+
- def MoveSelectedLines(self, move):
- """!Move selected features
-
-@@ -536,16 +725,45 @@
- if not self._checkMap():
- return -1
-
-+ nsel = len(self._display.selected['ids'])
-+ if nsel < 1:
-+ return -1
-+
- thresh = self._display.GetThreshold()
- snap = self._getSnapMode()
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ old_bboxs = []
-+ old_areas_cats = []
-+ for sel_id in self._display.selected['ids']:
-+ ret = self._getLineAreaBboxCats(sel_id)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList,
- move[0], move[1], 0,
- snap, thresh)
-+
- Vect_destroy_list(poList)
--
-+
-+ if nlines > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ ret = self._getLineAreaBboxCats(new_id)
-+ if ret:
-+ new_bboxs += ret[0]
-+ new_areas_cats += ret[1]
-+
- if nlines > 0 and self._settings['breakLines']:
- for i in range(1, nlines):
- self._breakLineAtIntersection(nlines + i, None, changeset)
-@@ -553,7 +771,13 @@
- if nlines > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresMoved.emit(new_bboxs = new_bboxs,
-+ old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = new_areas_cats)
-+
- return nlines
-
- def MoveSelectedVertex(self, point, move):
-@@ -571,12 +795,21 @@
-
- if len(self._display.selected['ids']) != 1:
- return -1
--
-+
-+ # move only first found vertex in bbox
-+ poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, point[0], point[1], 0.0)
--
-- # move only first found vertex in bbox
-- poList = self._display.GetSelectedIList()
-+
- moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList, self.poPoints,
- self._display.GetThreshold(type = 'selectThresh'),
-@@ -584,7 +817,17 @@
- move[0], move[1], 0.0,
- 1, self._getSnapMode())
- Vect_destroy_list(poList)
--
-+
-+ if moved > 0 and self.emit_signals:
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+
-+ new_bboxs = []
-+ new_areas_cats = []
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_bboxs.append(self._getBbox(new_id))
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+
- if moved > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
-@@ -592,7 +835,13 @@
- if moved > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.vertexMoved.emit(new_bboxs = new_bboxs,
-+ new_areas_cats = new_areas_cats,
-+ old_areas_cats = old_areas_cats,
-+ old_bboxs = old_bboxs)
-+
- return moved
-
- def AddVertex(self, coords):
-@@ -681,6 +930,10 @@
- self._error.ReadLine(line)
- return -1
-
-+ if self.emit_signals:
-+ old_bboxs = [self._getBbox(line)]
-+ old_areas_cats = [self._getLineAreasCategories(line)]
-+
- # build feature geometry
- Vect_reset_line(self.poPoints)
- for p in coords:
-@@ -696,6 +949,9 @@
-
- newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
- self.poPoints, self.poCats)
-+ if newline > 0 and self.emit_signals:
-+ new_geom = [self._getBbox(newline)]
-+ new_areas_cats = [self._getLineAreasCategories(newline)]
-
- if newline > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(newline, None)
-@@ -703,7 +959,13 @@
- if newline > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.lineEdited.emit(old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_bboxs = new_bboxs,
-+ new_areas_cats = new_areas_cats)
-+
- return newline
-
- def FlipLine(self):
-@@ -1514,6 +1776,16 @@
- return 0
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
-
-@@ -1525,15 +1797,35 @@
- else:
- ret = Vedit_remove_vertex(self.poMapInfo, poList,
- self.poPoints, thresh)
-+
- Vect_destroy_list(poList)
-+
-+ if ret > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+ new_bboxs.append(self._getBbox(new_id))
-
- if not add and ret > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
--
-+
- if ret > 0:
- self._addChangeset()
--
-+
-+ if ret > 0 and self.emit_signals:
-+ if add:
-+ self.vertexAdded.emit(old_bboxs = old_bboxs, new_bboxs = new_bboxs)
-+ else:
-+ self.vertexRemoved.emit(old_bboxs = old_bboxs,
-+ new_bboxs = new_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = new_areas_cats)
-+
- return 1
-
- def GetLineCats(self, line):
-Index: gui/wxpython/vdigit/toolbars.py
-===================================================================
---- gui/wxpython/vdigit/toolbars.py (revision 57556)
-+++ gui/wxpython/vdigit/toolbars.py (working copy)
-@@ -17,6 +17,7 @@
- import wx
-
- from grass.script import core as grass
-+from grass.pydispatch.signal import Signal
-
- from gui_core.toolbars import BaseToolbar, BaseIcons
- from gui_core.dialogs import CreateNewVector
-@@ -42,6 +43,8 @@
- self.digit = None
- self._giface = giface
-
-+ self.editingStarted = Signal("VDigitToolbar.editingStarted")
-+
- # currently selected map layer for editing (reference to MapLayer instance)
- self.mapLayer = None
- # list of vector layers from Layer Manager (only in the current mapset)
-@@ -860,6 +863,7 @@
- alpha = int(opacity * 255)
- self.digit.GetDisplay().UpdateSettings(alpha = alpha)
-
-+ self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
- return True
-
- def StopEditing(self):
-Index: gui/wxpython/mapdisp/toolbars.py
-===================================================================
---- gui/wxpython/mapdisp/toolbars.py (revision 57556)
-+++ 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 57556)
-+++ 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/iclass/dialogs.py
-===================================================================
---- gui/wxpython/iclass/dialogs.py (revision 57556)
-+++ 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 57556)
-+++ 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 57556)
-+++ 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,7 +483,7 @@
-
- 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:
-@@ -488,7 +494,8 @@
- """!Set imagery group"""
- group = grass.find_file(name = name, element = 'group')
- if group['name']:
-- self.group = group['name']
-+ self.group = group['name']
-+ self.groupSet.emit(group = group['name'])
- else:
- GError(_("Group <%s> not found") % name, parent = self)
-
-@@ -768,17 +775,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 +1115,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 57556)
-+++ 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: lib/imagery/scatt_sccats.c
-===================================================================
---- lib/imagery/scatt_sccats.c (revision 0)
-+++ lib/imagery/scatt_sccats.c (working copy)
-@@ -0,0 +1,405 @@
-+/*!
-+ \file lib/imagery/scatt_cat_rast.c
-+
-+ \brief Imagery library - functions for manipulation with scatter plot structs.
-+
-+ Copyright (C) 2013 by the GRASS Development Team
-+
-+ This program is free software under the GNU General Public License
-+ (>=v2). Read the file COPYING that comes with GRASS for details.
-+
-+ \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
-+ */
-+
-+#include <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/gis.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.h>
-+
-+/*!
-+ \brief Compute band ids from scatter plot id.
-+
-+ Scatter plot id describes which bands defines the scatter plot.
-+
-+ Let say we have 3 bands, their ids are 0, 1 and 2.
-+ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
-+ All scatter plots:
-+ scatt_id b_1_id b_2_id
-+ 0 0 1
-+ 1 0 2
-+ 2 1 2
-+
-+ \param scatt_id scatter plot id
-+ \param n_bands number of bands
-+ \param [out] b_1_id id of band1
-+ \param[out] b_2_id id of band2
-+
-+ \return 0
-+ */
-+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
-+
-+ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
-+
-+ return 0;
-+}
-+
-+
-+/*!
-+ \brief Compute scatter plot id from band ids.
-+
-+ See also I_id_scatt_to_bands().
-+
-+ \param n_bands number of bands
-+ \param b_1_id id of band1
-+ \param b_1_id id of band2
-+ \param [out] scatt_id scatter plot id
-+
-+ \return 0
-+ */
-+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
-+
-+ return 0;
-+}
-+
-+/*!
-+ \brief Initialize structure for storing scatter plots data.
-+
-+ \param cats pointer to scCats struct
-+ \param n_bands number of bands
-+ \param type SC_SCATT_DATA - stores scatter plots
-+ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
-+ */
-+void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
-+{
-+ int i_cat;
-+
-+ cats->type = type;
-+
-+ cats->n_cats = 100;
-+ cats->n_a_cats = 0;
-+
-+ cats->n_bands = n_bands;
-+ cats->n_scatts = (n_bands - 1) * n_bands / 2;
-+
-+ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
-+ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
-+
-+ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
-+ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
-+
-+ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
-+ cats->cats_idxs[i_cat] = -1;
-+
-+ return;
-+}
-+
-+/*!
-+ \brief Free data of struct scCats, the structure itself remains alocated.
-+
-+ \param cats pointer to existing scCats struct
-+ */
-+void I_sc_free_cats(struct scCats * cats)
-+{
-+ int i_cat;
-+
-+ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
-+ {
-+ if(cats->cats_arr[i_cat])
-+ {
-+ G_free(cats->cats_arr[i_cat]->scatt_idxs);
-+ G_free(cats->cats_arr[i_cat]->scatts_bands);
-+ G_free(cats->cats_arr[i_cat]->scatts_arr);
-+ G_free(cats->cats_arr[i_cat]);
-+ }
-+ }
-+
-+ G_free(cats->cats_ids);
-+ G_free(cats->cats_idxs);
-+ G_free(cats->cats_arr);
-+
-+ cats->n_cats = 0;
-+ cats->n_a_cats = 0;
-+ cats->n_bands = 0;
-+ cats->n_scatts = 0;
-+ cats->type = -1;
-+
-+ return;
-+}
-+
-+#if 0
-+void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
-+{
-+ a_cats_ids = cats->cats_ids;
-+ * n_a_cats = cats->n_a_cats;
-+}
-+#endif
-+
-+/*!
-+ \brief Add category.
-+
-+ Category represents group of scatter plots.
-+
-+ \param cats pointer to scCats struct
-+
-+ \return assigned category id (starts with 0)
-+ \return -1 if maximum nuber of categories was reached
-+ */
-+int I_sc_add_cat(struct scCats * cats)
-+{
-+ int i_scatt, i_cat_id, cat_id;
-+ int n_a_cats = cats->n_a_cats;
-+
-+ if(cats->n_a_cats >= cats->n_cats)
-+ return -1;
-+
-+ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
-+ if(cats->cats_idxs[i_cat_id] < 0) {
-+ cat_id = i_cat_id;
-+ break;
-+ }
-+
-+ cats->cats_ids[n_a_cats] = cat_id;
-+ cats->cats_idxs[cat_id] = n_a_cats;
-+
-+ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
-+
-+ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
-+ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
-+
-+ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
-+
-+ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
-+
-+ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
-+ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
-+ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
-+
-+ ++cats->n_a_cats;
-+
-+ return cat_id;
-+}
-+
-+#if 0
-+int I_sc_delete_cat(struct scCats * cats, int cat_id)
-+{
-+ int cat_idx, i_cat;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
-+ G_free(cats->cats_arr[cat_idx]->scatts_bands);
-+ G_free(cats->cats_arr[cat_idx]->scatts_arr);
-+ G_free(cats->cats_arr[cat_idx]);
-+
-+ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
-+ {
-+ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
-+ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
-+ }
-+ cats->cats_idxs[cat_id] = -1;
-+
-+ --cats->n_a_cats;
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data .
-+ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
-+
-+ \param cats pointer to scCats struct
-+ \param cat_id id number of category.
-+ \param scatt_id id number of scatter plot.
-+
-+ \return 0 on success
-+ \return -1 on failure
-+ */
-+int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int band_1, band_2, cat_idx, n_a_scatts;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] >= 0)
-+ return -1;
-+
-+ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
-+ return -1;
-+
-+ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
-+ return -1;
-+
-+ n_a_scatts = scatts->n_a_scatts;
-+
-+ scatts->scatt_idxs[scatt_id] = n_a_scatts;
-+
-+ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
-+
-+ scatts->scatts_bands[n_a_scatts * 2] = band_1;
-+ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
-+
-+ scatts->scatts_arr[n_a_scatts] = scatt_data;
-+ ++scatts->n_a_scatts;
-+
-+ return 0;
-+}
-+
-+#if 0
-+int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 && cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_idx];
-+
-+ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
-+ {
-+ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
-+ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
-+ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
-+ }
-+ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
-+
-+ scatts->scatt_idxs[scatt_id] = -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_id];
-+ scatts->n_a_scatts--;
-+
-+ return 0;
-+}
-+
-+int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
-+{
-+ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
-+ int cat_idx, scatt_idx, ret;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
-+
-+ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data.
-+
-+ \param scatt_data pointer to existing struct scdScattData
-+ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
-+ \param n_vals number of data values
-+ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
-+ */
-+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
-+{
-+ scatt_data->n_vals = n_vals;
-+
-+ if(type == SC_SCATT_DATA)
-+ {
-+ if(data)
-+ scatt_data->scatt_vals_arr = (unsigned int *) data;
-+ else {
-+ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
-+ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
-+ }
-+ scatt_data->b_conds_arr = NULL;
-+ }
-+ else if(type == SC_SCATT_CONDITIONS)
-+ {
-+ if(data)
-+ scatt_data->b_conds_arr = (unsigned char *) data;
-+ else {
-+ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
-+ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
-+ }
-+ scatt_data->scatt_vals_arr = NULL;
-+ }
-+
-+ return;
-+}
-+
-+
-+#if 0
-+void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
-+{
-+
-+ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
-+ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
-+
-+ return;
-+}
-+s
-+void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
-+{
-+ if(!scatt_data->b_conds_arr)
-+ return scatt_data->b_conds_arr;
-+ else if(!scatt_data->scatt_vals_arr)
-+ return scatt_data->scatt_vals_arr;
-+
-+ return NULL;
-+}
-+
-+int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
-+{
-+ if(val_idx < 0 && val_idx > scatt_data->n_vals)
-+ return -1;
-+
-+ if(scatt_data->b_conds_arr)
-+ scatt_data->b_conds_arr[val_idx] = val;
-+ else if(scatt_data->scatt_vals_arr)
-+ scatt_data->scatt_vals_arr[val_idx] = val;
-+ else
-+ return -1;
-+
-+ return 0;
-+}
-+#endif
Index: lib/imagery/scatt.c
===================================================================
--- lib/imagery/scatt.c (revision 0)
@@ -5508,9 +5178,419 @@
+ return 0;
+}
\ No newline at end of file
+Index: lib/imagery/scatt_sccats.c
+===================================================================
+--- lib/imagery/scatt_sccats.c (revision 0)
++++ lib/imagery/scatt_sccats.c (working copy)
+@@ -0,0 +1,405 @@
++/*!
++ \file lib/imagery/scatt_cat_rast.c
++
++ \brief Imagery library - functions for manipulation with scatter plot structs.
++
++ Copyright (C) 2013 by the GRASS Development Team
++
++ This program is free software under the GNU General Public License
++ (>=v2). Read the file COPYING that comes with GRASS for details.
++
++ \author Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++/*!
++ \brief Compute band ids from scatter plot id.
++
++ Scatter plot id describes which bands defines the scatter plot.
++
++ Let say we have 3 bands, their ids are 0, 1 and 2.
++ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
++ All scatter plots:
++ scatt_id b_1_id b_2_id
++ 0 0 1
++ 1 0 2
++ 2 1 2
++
++ \param scatt_id scatter plot id
++ \param n_bands number of bands
++ \param [out] b_1_id id of band1
++ \param[out] b_2_id id of band2
++
++ \return 0
++ */
++int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
++
++ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
++
++ return 0;
++}
++
++
++/*!
++ \brief Compute scatter plot id from band ids.
++
++ See also I_id_scatt_to_bands().
++
++ \param n_bands number of bands
++ \param b_1_id id of band1
++ \param b_1_id id of band2
++ \param [out] scatt_id scatter plot id
++
++ \return 0
++ */
++int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
++
++ return 0;
++}
++
++/*!
++ \brief Initialize structure for storing scatter plots data.
++
++ \param cats pointer to scCats struct
++ \param n_bands number of bands
++ \param type SC_SCATT_DATA - stores scatter plots
++ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
++ */
++void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
++{
++ int i_cat;
++
++ cats->type = type;
++
++ cats->n_cats = 100;
++ cats->n_a_cats = 0;
++
++ cats->n_bands = n_bands;
++ cats->n_scatts = (n_bands - 1) * n_bands / 2;
++
++ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
++ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
++
++ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
++ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
++
++ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
++ cats->cats_idxs[i_cat] = -1;
++
++ return;
++}
++
++/*!
++ \brief Free data of struct scCats, the structure itself remains alocated.
++
++ \param cats pointer to existing scCats struct
++ */
++void I_sc_free_cats(struct scCats * cats)
++{
++ int i_cat;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ if(cats->cats_arr[i_cat])
++ {
++ G_free(cats->cats_arr[i_cat]->scatt_idxs);
++ G_free(cats->cats_arr[i_cat]->scatts_bands);
++ G_free(cats->cats_arr[i_cat]->scatts_arr);
++ G_free(cats->cats_arr[i_cat]);
++ }
++ }
++
++ G_free(cats->cats_ids);
++ G_free(cats->cats_idxs);
++ G_free(cats->cats_arr);
++
++ cats->n_cats = 0;
++ cats->n_a_cats = 0;
++ cats->n_bands = 0;
++ cats->n_scatts = 0;
++ cats->type = -1;
++
++ return;
++}
++
++#if 0
++void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
++{
++ a_cats_ids = cats->cats_ids;
++ * n_a_cats = cats->n_a_cats;
++}
++#endif
++
++/*!
++ \brief Add category.
++
++ Category represents group of scatter plots.
++
++ \param cats pointer to scCats struct
++
++ \return assigned category id (starts with 0)
++ \return -1 if maximum nuber of categories was reached
++ */
++int I_sc_add_cat(struct scCats * cats)
++{
++ int i_scatt, i_cat_id, cat_id;
++ int n_a_cats = cats->n_a_cats;
++
++ if(cats->n_a_cats >= cats->n_cats)
++ return -1;
++
++ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
++ if(cats->cats_idxs[i_cat_id] < 0) {
++ cat_id = i_cat_id;
++ break;
++ }
++
++ cats->cats_ids[n_a_cats] = cat_id;
++ cats->cats_idxs[cat_id] = n_a_cats;
++
++ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
++
++ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
++ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
++
++ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
++
++ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
++
++ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
++ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
++ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++
++ ++cats->n_a_cats;
++
++ return cat_id;
++}
++
++#if 0
++int I_sc_delete_cat(struct scCats * cats, int cat_id)
++{
++ int cat_idx, i_cat;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
++ G_free(cats->cats_arr[cat_idx]->scatts_bands);
++ G_free(cats->cats_arr[cat_idx]->scatts_arr);
++ G_free(cats->cats_arr[cat_idx]);
++
++ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
++ {
++ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
++ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
++ }
++ cats->cats_idxs[cat_id] = -1;
++
++ --cats->n_a_cats;
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data .
++ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
++
++ \param cats pointer to scCats struct
++ \param cat_id id number of category.
++ \param scatt_id id number of scatter plot.
++
++ \return 0 on success
++ \return -1 on failure
++ */
++int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int band_1, band_2, cat_idx, n_a_scatts;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] >= 0)
++ return -1;
++
++ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
++ return -1;
++
++ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
++ return -1;
++
++ n_a_scatts = scatts->n_a_scatts;
++
++ scatts->scatt_idxs[scatt_id] = n_a_scatts;
++
++ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
++
++ scatts->scatts_bands[n_a_scatts * 2] = band_1;
++ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
++
++ scatts->scatts_arr[n_a_scatts] = scatt_data;
++ ++scatts->n_a_scatts;
++
++ return 0;
++}
++
++#if 0
++int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 && cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ scatt_data = scatts->scatts_arr[scatt_idx];
++
++ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
++ {
++ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
++ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
++ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
++ }
++ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
++
++ scatts->scatt_idxs[scatt_id] = -1;
++
++ scatt_data = scatts->scatts_arr[scatt_id];
++ scatts->n_a_scatts--;
++
++ return 0;
++}
++
++int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
++{
++ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
++ int cat_idx, scatt_idx, ret;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
++
++ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data.
++
++ \param scatt_data pointer to existing struct scdScattData
++ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
++ \param n_vals number of data values
++ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
++ */
++void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
++{
++ scatt_data->n_vals = n_vals;
++
++ if(type == SC_SCATT_DATA)
++ {
++ if(data)
++ scatt_data->scatt_vals_arr = (unsigned int *) data;
++ else {
++ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
++ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
++ }
++ scatt_data->b_conds_arr = NULL;
++ }
++ else if(type == SC_SCATT_CONDITIONS)
++ {
++ if(data)
++ scatt_data->b_conds_arr = (unsigned char *) data;
++ else {
++ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
++ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
++ }
++ scatt_data->scatt_vals_arr = NULL;
++ }
++
++ return;
++}
++
++
++#if 0
++void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
++{
++
++ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
++ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
++
++ return;
++}
++s
++void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
++{
++ if(!scatt_data->b_conds_arr)
++ return scatt_data->b_conds_arr;
++ else if(!scatt_data->scatt_vals_arr)
++ return scatt_data->scatt_vals_arr;
++
++ return NULL;
++}
++
++int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
++{
++ if(val_idx < 0 && val_idx > scatt_data->n_vals)
++ return -1;
++
++ if(scatt_data->b_conds_arr)
++ scatt_data->b_conds_arr[val_idx] = val;
++ else if(scatt_data->scatt_vals_arr)
++ scatt_data->scatt_vals_arr[val_idx] = val;
++ else
++ return -1;
++
++ return 0;
++}
++#endif
Index: lib/vector/Vlib/open.c
===================================================================
---- lib/vector/Vlib/open.c (revision 57556)
+--- lib/vector/Vlib/open.c (revision 57581)
+++ lib/vector/Vlib/open.c (working copy)
@@ -240,7 +240,9 @@
}
More information about the grass-commit
mailing list