Browse Source

vdigit: signals used by scatter plot

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@57817 15284696-431f-4ddb-bdfa-cd5b030d7da7
Štěpán Turek 11 years ago
parent
commit
50d93a4352
2 changed files with 396 additions and 32 deletions
  1. 4 0
      gui/wxpython/vdigit/toolbars.py
  2. 392 32
      gui/wxpython/vdigit/wxdigit.py

+ 4 - 0
gui/wxpython/vdigit/toolbars.py

@@ -17,6 +17,7 @@ This program is free software under the GNU General Public License
 import wx
 import wx
 
 
 from grass.script import core as grass
 from grass.script import core as grass
+from grass.pydispatch.signal import Signal
 
 
 from gui_core.toolbars  import BaseToolbar, BaseIcons
 from gui_core.toolbars  import BaseToolbar, BaseIcons
 from gui_core.dialogs   import CreateNewVector
 from gui_core.dialogs   import CreateNewVector
@@ -42,6 +43,8 @@ class VDigitToolbar(BaseToolbar):
         self.digit         = None
         self.digit         = None
         self._giface       = giface
         self._giface       = giface
         
         
+        self.editingStarted = Signal("VDigitToolbar.editingStarted")
+
         # currently selected map layer for editing (reference to MapLayer instance)
         # currently selected map layer for editing (reference to MapLayer instance)
         self.mapLayer = None
         self.mapLayer = None
         # list of vector layers from Layer Manager (only in the current mapset)
         # list of vector layers from Layer Manager (only in the current mapset)
@@ -860,6 +863,7 @@ class VDigitToolbar(BaseToolbar):
             alpha = int(opacity * 255)
             alpha = int(opacity * 255)
             self.digit.GetDisplay().UpdateSettings(alpha = alpha)
             self.digit.GetDisplay().UpdateSettings(alpha = alpha)
         
         
+        self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
         return True
         return True
 
 
     def StopEditing(self):
     def StopEditing(self):

+ 392 - 32
gui/wxpython/vdigit/wxdigit.py

@@ -17,7 +17,7 @@ data processing with NumPy is much faster than iterating in Python
 (and NumPy would be an excellent candidate for acceleration via
 (and NumPy would be an excellent candidate for acceleration via
 e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
 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
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -27,17 +27,19 @@ This program is free software under the GNU General Public License
 
 
 import grass.script.core as grass
 import grass.script.core as grass
 
 
-from core.gcmd        import GError
-from core.debug       import Debug
-from core.settings    import UserSettings
+from grass.pydispatch.signal import Signal
+
+from core.gcmd import GError
+from core.debug import Debug
+from core.settings import UserSettings
 from core.utils import _
 from core.utils import _
 from vdigit.wxdisplay import DisplayDriver, GetLastError
 from vdigit.wxdisplay import DisplayDriver, GetLastError
 
 
 try:
 try:
-    from grass.lib.gis    import *
+    from grass.lib.gis import *
     from grass.lib.vector import *
     from grass.lib.vector import *
-    from grass.lib.vedit  import *
-    from grass.lib.dbmi   import *
+    from grass.lib.vedit import *
+    from grass.lib.dbmi import *
 except ImportError:
 except ImportError:
     pass
     pass
 
 
@@ -176,7 +178,40 @@ class IVDigit:
         
         
         if self.poMapInfo:
         if self.poMapInfo:
             self.InitCats()
             self.InitCats()
+
+        self.emit_signals = False
+
+        # signals which describes features changes during digitization, 
+        # activate them using EmitSignals method 
+
+        # currently implemented for functionality used by wx.iclass (for scatter plot)
         
         
+        # signals parameter description:
+        # old_bboxs - list of bboxes of boundary features, which covers changed areas
+        # it is bbox of old state (before edit)
+        # old_areas_cats - list of area categories of boundary features of old state (before edit)
+        # same position in both lists corresponds to same feature
+
+        # new_bboxs = list of bboxes of created features / after edit
+        # new_areas_cats list of areas cats of created features / after edit
+        # same position in both lists corresponds to same features
+
+        # for description of items in bbox and area_cats lists see return value of _getaAreaBboxCats
+
+        # TODO currently it is not possible to identify corresponded features
+        # in old and new lists (requires changed to vector updated format)
+        # TODO return feature type
+        
+        #TODO handle errors?
+        self.featureAdded = Signal('IVDigit.featureAdded')
+        self.areasDeleted = Signal('IVDigit.areasDeleted')
+        self.vertexMoved = Signal('IVDigit.vertexMoved')
+        self.vertexAdded = Signal('IVDigit.vertexAdded')
+        self.vertexRemoved = Signal('IVDigit.vertexRemoved')
+        self.featuresDeleted = Signal('IVDigit.featuresDeleted')
+        self.featuresMoved = Signal('IVDigit.featuresMoved')
+        self.lineEdited = Signal('IVDigit.lineEdited')
+
     def __del__(self):
     def __del__(self):
         Debug.msg(1, "IVDigit.__del__()")
         Debug.msg(1, "IVDigit.__del__()")
         Vect_destroy_line_struct(self.poPoints)
         Vect_destroy_line_struct(self.poPoints)
@@ -188,7 +223,12 @@ class IVDigit:
             Vect_close(self.poBgMapInfo)
             Vect_close(self.poBgMapInfo)
             self.poBgMapInfo = self.popoBgMapInfo = None
             self.poBgMapInfo = self.popoBgMapInfo = None
             del self.bgMapInfo
             del self.bgMapInfo
-        
+     
+    def EmitSignals(self, emit):
+        """!Activate/deactivate signals which describes features changes during digitization.
+        """
+        self.emit_signals = emit
+
     def CloseBackgroundMap(self):
     def CloseBackgroundMap(self):
         """!Close background vector map"""
         """!Close background vector map"""
         if not self.poBgMapInfo:
         if not self.poBgMapInfo:
@@ -394,7 +434,6 @@ class IVDigit:
         
         
         @return tuple (number of added features, feature ids)
         @return tuple (number of added features, feature ids)
         """
         """
-        
         layer = self._getNewFeaturesLayer()
         layer = self._getNewFeaturesLayer()
         cat = self._getNewFeaturesCat()
         cat = self._getNewFeaturesCat()
         
         
@@ -419,10 +458,15 @@ class IVDigit:
             return (-1, None)
             return (-1, None)
         
         
         self.toolbar.EnableUndo()
         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):
     def DeleteSelectedLines(self):
         """!Delete selected features
         """!Delete selected features
 
 
@@ -434,16 +478,27 @@ class IVDigit:
         # collect categories for deleting if requested
         # collect categories for deleting if requested
         deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
         deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
         catDict = dict()
         catDict = dict()
+
+        old_bboxs = []
+        old_areas_cats = []
         if deleteRec:
         if deleteRec:
             for i in self._display.selected['ids']:
             for i in self._display.selected['ids']:
+                
                 if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
                 if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
                     self._error.ReadLine(i)
                     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()
         poList = self._display.GetSelectedIList()
         nlines = Vedit_delete_lines(self.poMapInfo, poList)
         nlines = Vedit_delete_lines(self.poMapInfo, poList)
@@ -456,7 +511,11 @@ class IVDigit:
                 self._deleteRecords(catDict)
                 self._deleteRecords(catDict)
             self._addChangeset()
             self._addChangeset()
             self.toolbar.EnableUndo()
             self.toolbar.EnableUndo()
-        
+
+            if self.emit_signals:
+                self.featuresDeleted.emit(old_bboxs=old_bboxs, 
+                                          old_areas_cats=old_areas_cats)
+
         return nlines
         return nlines
             
             
     def _deleteRecords(self, cats):
     def _deleteRecords(self, cats):
@@ -512,22 +571,220 @@ class IVDigit:
 
 
         @return number of deleted 
         @return number of deleted 
         """
         """
+        if len(self._display.selected['ids']) < 1:
+            return 0
+        
         poList = self._display.GetSelectedIList()
         poList = self._display.GetSelectedIList()
         cList  = poList.contents
         cList  = poList.contents
         
         
         nareas = 0
         nareas = 0
+        old_bboxs = []
+        old_areas_cats = []
+
         for i in range(cList.n_values):
         for i in range(cList.n_values):
+
             if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
             if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
                 continue
                 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])
             nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
         
         
         if nareas > 0:
         if nareas > 0:
             self._addChangeset()
             self._addChangeset()
             self.toolbar.EnableUndo()
             self.toolbar.EnableUndo()
-        
+            if self.emit_signals:
+                self.areasDeleted.emit(old_bboxs=old_bboxs, 
+                                       old_areas_cats=old_areas_cats)        
+
         return nareas
         return nareas
+   
+    def _getLineAreaBboxCats(self, ln_id):
+        """!Helper function
+
+        @param id of feature
+        @return None if the feature does not exists
+        @return list of @see _getaAreaBboxCats
+        """
+        ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
+
+        if ltype == GV_CENTROID:
+            #TODO centroid opttimization, can be edited also its area -> it will appear two times in new_ lists
+            return self._getCentroidAreaBboxCats(ln_id)
+        else: 
+            return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
+
+
+    def _getCentroidAreaBboxCats(self, centroid):
+        """!Helper function
+
+        @param id of an centroid 
+        @return None if area does not exists
+        @return see return of _getaAreaBboxCats
+        """
+        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):
+        """!Helper function
+
+        @param area area id
+        @return list of categories @see _getLineAreasCategories and 
+        list of bboxes @see _getBbox of area boundary features
+        """
+        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)))
+        
+        Vect_destroy_list(po_b_list);
+
+        return geoms, areas_cats
+
+    def _getLineAreasCategories(self, ln_id):
+        """!Helper function
+
+        @param line_id id of boundary feature 
+        @return categories of areas on the left, right side of the feature
+        @return format: [[{layer : [cat]}, None]] means:
+                area to the left (list of layers which has cats list as values), 
+                area to the right (no area there in this case (None)) 
+        @return [] the feature is not boundary or does not exists
+        """
+        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):
+        """!Helper function
+
+        @param line_id id of feature
+        @return list of the feature categories [{layer : cats}, next layer...]
+        @return None feature does not exist
+        """
+        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):
+        """!Helper function
+
+        @param line_id id of line feature
+        @return bbox bounding box of the feature
+        @return None feature does not exist
+        """
+        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):
+        """!Helper function
+
+        @param points list of points [(x, y), ...] to be bbox created for
+        @return bbox bounding box of points {'maxx':, 'maxy':, 'minx':, 'miny'}
+        """
+        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):
+        """!Helper function
+            convert geom from ctypes line_pts to python list
+
+        @return coords in python list [(x, y),...] 
+        """
+        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):
     def MoveSelectedLines(self, move):
         """!Move selected features
         """!Move selected features
 
 
@@ -536,16 +793,45 @@ class IVDigit:
         if not self._checkMap():
         if not self._checkMap():
             return -1
             return -1
         
         
+        nsel = len(self._display.selected['ids'])
+        if nsel < 1:
+            return -1   
+        
         thresh = self._display.GetThreshold()
         thresh = self._display.GetThreshold()
         snap   = self._getSnapMode()
         snap   = self._getSnapMode()
         
         
         poList = self._display.GetSelectedIList()
         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),
         nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
                                   poList,
                                   poList,
                                   move[0], move[1], 0,
                                   move[0], move[1], 0,
                                   snap, thresh)
                                   snap, thresh)
+
         Vect_destroy_list(poList)
         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']:
         if nlines > 0 and self._settings['breakLines']:
             for i in range(1, nlines):
             for i in range(1, nlines):
                 self._breakLineAtIntersection(nlines + i, None, changeset)
                 self._breakLineAtIntersection(nlines + i, None, changeset)
@@ -553,7 +839,13 @@ class IVDigit:
         if nlines > 0:
         if nlines > 0:
             self._addChangeset()
             self._addChangeset()
             self.toolbar.EnableUndo()
             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
         return nlines
 
 
     def MoveSelectedVertex(self, point, move):
     def MoveSelectedVertex(self, point, move):
@@ -571,12 +863,21 @@ class IVDigit:
         
         
         if len(self._display.selected['ids']) != 1:
         if len(self._display.selected['ids']) != 1:
             return -1
             return -1
-        
-        Vect_reset_line(self.poPoints)
-        Vect_append_point(self.poPoints, point[0], point[1], 0.0)
-        
+
         # move only first found vertex in bbox 
         # move only first found vertex in bbox 
         poList = self._display.GetSelectedIList()
         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)
+
         moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
         moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
                                   poList, self.poPoints,
                                   poList, self.poPoints,
                                   self._display.GetThreshold(type = 'selectThresh'),
                                   self._display.GetThreshold(type = 'selectThresh'),
@@ -584,7 +885,17 @@ class IVDigit:
                                   move[0], move[1], 0.0,
                                   move[0], move[1], 0.0,
                                   1, self._getSnapMode())
                                   1, self._getSnapMode())
         Vect_destroy_list(poList)
         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']:
         if moved > 0 and self._settings['breakLines']:
             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
                                           None)
                                           None)
@@ -592,7 +903,13 @@ class IVDigit:
         if moved > 0:
         if moved > 0:
             self._addChangeset()
             self._addChangeset()
             self.toolbar.EnableUndo()
             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
         return moved
 
 
     def AddVertex(self, coords):
     def AddVertex(self, coords):
@@ -681,6 +998,10 @@ class IVDigit:
             self._error.ReadLine(line)
             self._error.ReadLine(line)
             return -1
             return -1
         
         
+        if self.emit_signals:
+            old_bboxs = [self._getBbox(line)]
+            old_areas_cats = [self._getLineAreasCategories(line)]
+
         # build feature geometry
         # build feature geometry
         Vect_reset_line(self.poPoints)
         Vect_reset_line(self.poPoints)
         for p in coords:
         for p in coords:
@@ -696,6 +1017,9 @@ class IVDigit:
         
         
         newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
         newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
                                     self.poPoints, self.poCats)
                                     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']:
         if newline > 0 and self._settings['breakLines']:
             self._breakLineAtIntersection(newline, None)
             self._breakLineAtIntersection(newline, None)
@@ -703,7 +1027,13 @@ class IVDigit:
         if newline > 0:
         if newline > 0:
             self._addChangeset()
             self._addChangeset()
             self.toolbar.EnableUndo()
             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
         return newline
 
 
     def FlipLine(self):
     def FlipLine(self):
@@ -1514,6 +1844,16 @@ class IVDigit:
             return 0
             return 0
         
         
         poList  = self._display.GetSelectedIList()
         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_reset_line(self.poPoints)
         Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
         Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
         
         
@@ -1525,15 +1865,35 @@ class IVDigit:
         else:
         else:
             ret = Vedit_remove_vertex(self.poMapInfo, poList,
             ret = Vedit_remove_vertex(self.poMapInfo, poList,
                                       self.poPoints, thresh)
                                       self.poPoints, thresh)
+
         Vect_destroy_list(poList)
         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']:
         if not add and ret > 0 and self._settings['breakLines']:
             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
                                           None)
                                           None)
-        
+
         if ret > 0:
         if ret > 0:
             self._addChangeset()
             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
         return 1
     
     
     def GetLineCats(self, line):
     def GetLineCats(self, line):