[QGIS Commit] r12830 - in trunk/qgis: python/core src/app src/core

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Jan 24 11:07:56 EST 2010


Author: jef
Date: 2010-01-24 11:07:55 -0500 (Sun, 24 Jan 2010)
New Revision: 12830

Modified:
   trunk/qgis/python/core/qgsgeometry.sip
   trunk/qgis/src/app/qgsmaptooladdfeature.cpp
   trunk/qgis/src/app/qgsmaptooladdisland.cpp
   trunk/qgis/src/app/qgsmaptooladdring.cpp
   trunk/qgis/src/app/qgsmaptoolcapture.cpp
   trunk/qgis/src/app/qgsmaptoolcapture.h
   trunk/qgis/src/app/qgsmaptoolnodetool.cpp
   trunk/qgis/src/app/qgsmaptoolreshape.cpp
   trunk/qgis/src/app/qgsmaptoolsplitfeatures.cpp
   trunk/qgis/src/core/qgsgeometry.cpp
   trunk/qgis/src/core/qgsgeometry.h
Log:
[FEATURE] add validation to capture tool

Modified: trunk/qgis/python/core/qgsgeometry.sip
===================================================================
--- trunk/qgis/python/core/qgsgeometry.sip	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/python/core/qgsgeometry.sip	2010-01-24 16:07:55 UTC (rev 12830)
@@ -90,6 +90,16 @@
     /** Returns true if wkb of the geometry is of WKBMulti* type */
     bool isMultipart();
 
+    /** compare geometries using GEOS
+      @note added in 1.5
+     */
+    bool isGeosEqual( QgsGeometry & );
+
+    /** check validity using GEOS
+      @note added in 1.5
+     */
+    bool isGeosValid();
+
     /**
        Set the geometry, feeding in a geometry in GEOS format.
     */

Modified: trunk/qgis/src/app/qgsmaptooladdfeature.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptooladdfeature.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptooladdfeature.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -59,11 +59,7 @@
        layerWKBType == QGis::WKBMultiLineString25D || layerWKBType == QGis::WKBPoint25D || layerWKBType == QGis::WKBMultiPoint25D )
   {
     QMessageBox::critical( 0, tr( "2.5D shape type not supported" ), tr( "Adding features to 2.5D shapetypes is not supported yet" ) );
-    delete mRubberBand;
-    mRubberBand = NULL;
-    mCapturing = FALSE;
-    mCaptureList.clear();
-    mCanvas->refresh();
+    stopCapturing();
     return;
   }
 
@@ -85,7 +81,7 @@
   }
 
   // POINT CAPTURING
-  if ( mCaptureMode == CapturePoint )
+  if ( mode() == CapturePoint )
   {
     //check we only use this tool for point/multipoint layers
     if ( vlayer->geometryType() != QGis::Point )
@@ -129,7 +125,7 @@
       QgsFeature* f = new QgsFeature( 0, "WKBPoint" );
 
       int size = 0;
-      char end = QgsApplication::endian();
+      char endian = QgsApplication::endian();
       unsigned char *wkb = NULL;
       int wkbtype = 0;
       double x = savePoint.x();
@@ -140,7 +136,7 @@
         size = 1 + sizeof( int ) + 2 * sizeof( double );
         wkb = new unsigned char[size];
         wkbtype = QGis::WKBPoint;
-        memcpy( &wkb[0], &end, 1 );
+        memcpy( &wkb[0], &endian, 1 );
         memcpy( &wkb[1], &wkbtype, sizeof( int ) );
         memcpy( &wkb[5], &x, sizeof( double ) );
         memcpy( &wkb[5] + sizeof( double ), &y, sizeof( double ) );
@@ -151,14 +147,14 @@
         wkb = new unsigned char[size];
         wkbtype = QGis::WKBMultiPoint;
         int position = 0;
-        memcpy( &wkb[position], &end, 1 );
+        memcpy( &wkb[position], &endian, 1 );
         position += 1;
         memcpy( &wkb[position], &wkbtype, sizeof( int ) );
         position += sizeof( int );
         int npoint = 1;
         memcpy( &wkb[position], &npoint, sizeof( int ) );
         position += sizeof( int );
-        memcpy( &wkb[position], &end, 1 );
+        memcpy( &wkb[position], &endian, 1 );
         position += 1;
         int pointtype = QGis::WKBPoint;
         memcpy( &wkb[position], &pointtype, sizeof( int ) );
@@ -209,10 +205,10 @@
     }
 
   }
-  else if ( mCaptureMode == CaptureLine || mCaptureMode == CapturePolygon )
+  else if ( mode() == CaptureLine || mode() == CapturePolygon )
   {
     //check we only use the line tool for line/multiline layers
-    if ( mCaptureMode == CaptureLine && vlayer->geometryType() != QGis::Line )
+    if ( mode() == CaptureLine && vlayer->geometryType() != QGis::Line )
     {
       QMessageBox::information( 0, tr( "Wrong editing tool" ),
                                 tr( "Cannot apply the 'capture line' tool on this vector layer" ) );
@@ -220,7 +216,7 @@
     }
 
     //check we only use the polygon tool for polygon/multipolygon layers
-    if ( mCaptureMode == CapturePolygon && vlayer->geometryType() != QGis::Polygon )
+    if ( mode() == CapturePolygon && vlayer->geometryType() != QGis::Polygon )
     {
       QMessageBox::information( 0, tr( "Wrong editing tool" ),
                                 tr( "Cannot apply the 'capture polygon' tool on this vector layer" ) );
@@ -244,52 +240,46 @@
 
     if ( e->button() == Qt::LeftButton )
     {
-      mCapturing = TRUE;
+      startCapturing();
     }
     else if ( e->button() == Qt::RightButton )
     {
       // End of string
 
-      mCapturing = FALSE;
-
       //lines: bail out if there are not at least two vertices
-      if ( mCaptureMode == CaptureLine && mCaptureList.size() < 2 )
+      if ( mode() == CaptureLine && size() < 2 )
       {
-        delete mRubberBand;
-        mRubberBand = NULL;
-        mCaptureList.clear();
+        stopCapturing();
         return;
       }
 
       //polygons: bail out if there are not at least two vertices
-      if ( mCaptureMode == CapturePolygon && mCaptureList.size() < 3 )
+      if ( mode() == CapturePolygon && size() < 3 )
       {
-        delete mRubberBand;
-        mRubberBand = NULL;
-        mCaptureList.clear();
+        stopCapturing();
         return;
       }
 
       //create QgsFeature with wkb representation
       QgsFeature* f = new QgsFeature( 0, "WKBLineString" );
       unsigned char* wkb;
-      int size;
-      char end = QgsApplication::endian();
+      int wkbsize;
+      char endian = QgsApplication::endian();
 
-      if ( mCaptureMode == CaptureLine )
+      if ( mode() == CaptureLine )
       {
         if ( layerWKBType == QGis::WKBLineString )
         {
-          size = 1 + 2 * sizeof( int ) + 2 * mCaptureList.size() * sizeof( double );
-          wkb = new unsigned char[size];
+          wkbsize = 1 + 2 * sizeof( int ) + 2 * size() * sizeof( double );
+          wkb = new unsigned char[wkbsize];
           int wkbtype = QGis::WKBLineString;
-          int length = mCaptureList.size();
-          memcpy( &wkb[0], &end, 1 );
+          int length = size();
+          memcpy( &wkb[0], &endian, 1 );
           memcpy( &wkb[1], &wkbtype, sizeof( int ) );
           memcpy( &wkb[1+sizeof( int )], &length, sizeof( int ) );
           int position = 1 + 2 * sizeof( int );
           double x, y;
-          for ( QList<QgsPoint>::iterator it = mCaptureList.begin(); it != mCaptureList.end(); ++it )
+          for ( QList<QgsPoint>::iterator it = begin(); it != end(); ++it )
           {
             QgsPoint savePoint = *it;
             x = savePoint.x();
@@ -304,27 +294,27 @@
         }
         else if ( layerWKBType == QGis::WKBMultiLineString )
         {
-          size = 1 + 2 * sizeof( int ) + 1 + 2 * sizeof( int ) + 2 * mCaptureList.size() * sizeof( double );
-          wkb = new unsigned char[size];
+          wkbsize = 1 + 2 * sizeof( int ) + 1 + 2 * sizeof( int ) + 2 * size() * sizeof( double );
+          wkb = new unsigned char[wkbsize];
           int position = 0;
           int wkbtype = QGis::WKBMultiLineString;
-          memcpy( &wkb[position], &end, 1 );
+          memcpy( &wkb[position], &endian, 1 );
           position += 1;
           memcpy( &wkb[position], &wkbtype, sizeof( int ) );
           position += sizeof( int );
           int nlines = 1;
           memcpy( &wkb[position], &nlines, sizeof( int ) );
           position += sizeof( int );
-          memcpy( &wkb[position], &end, 1 );
+          memcpy( &wkb[position], &endian, 1 );
           position += 1;
           int linewkbtype = QGis::WKBLineString;
           memcpy( &wkb[position], &linewkbtype, sizeof( int ) );
           position += sizeof( int );
-          int length = mCaptureList.size();
+          int length = size();
           memcpy( &wkb[position], &length, sizeof( int ) );
           position += sizeof( int );
           double x, y;
-          for ( QList<QgsPoint>::iterator it = mCaptureList.begin(); it != mCaptureList.end(); ++it )
+          for ( QList<QgsPoint>::iterator it = begin(); it != end(); ++it )
           {
             QgsPoint savePoint = *it;
             x = savePoint.x();
@@ -340,30 +330,28 @@
         else
         {
           QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) );
-          delete mRubberBand;
-          mRubberBand = NULL;
-          mCaptureList.clear();
+          stopCapturing();
           return; //unknown wkbtype
         }
-        f->setGeometryAndOwnership( &wkb[0], size );
+        f->setGeometryAndOwnership( &wkb[0], wkbsize );
       }
       else // polygon
       {
         if ( layerWKBType == QGis::WKBPolygon )
         {
-          size = 1 + 3 * sizeof( int ) + 2 * ( mCaptureList.size() + 1 ) * sizeof( double );
-          wkb = new unsigned char[size];
+          wkbsize = 1 + 3 * sizeof( int ) + 2 * ( size() + 1 ) * sizeof( double );
+          wkb = new unsigned char[wkbsize];
           int wkbtype = QGis::WKBPolygon;
-          int length = mCaptureList.size() + 1;//+1 because the first point is needed twice
+          int length = size() + 1;//+1 because the first point is needed twice
           int numrings = 1;
-          memcpy( &wkb[0], &end, 1 );
+          memcpy( &wkb[0], &endian, 1 );
           memcpy( &wkb[1], &wkbtype, sizeof( int ) );
           memcpy( &wkb[1+sizeof( int )], &numrings, sizeof( int ) );
           memcpy( &wkb[1+2*sizeof( int )], &length, sizeof( int ) );
           int position = 1 + 3 * sizeof( int );
           double x, y;
           QList<QgsPoint>::iterator it;
-          for ( it = mCaptureList.begin(); it != mCaptureList.end(); ++it )
+          for ( it = begin(); it != end(); ++it )
           {
             QgsPoint savePoint = *it;
             x = savePoint.x();
@@ -376,7 +364,7 @@
             position += sizeof( double );
           }
           // close the polygon
-          it = mCaptureList.begin();
+          it = begin();
           QgsPoint savePoint = *it;
           x = savePoint.x();
           y = savePoint.y();
@@ -388,21 +376,21 @@
         }
         else if ( layerWKBType == QGis::WKBMultiPolygon )
         {
-          size = 2 + 5 * sizeof( int ) + 2 * ( mCaptureList.size() + 1 ) * sizeof( double );
-          wkb = new unsigned char[size];
+          wkbsize = 2 + 5 * sizeof( int ) + 2 * ( size() + 1 ) * sizeof( double );
+          wkb = new unsigned char[wkbsize];
           int wkbtype = QGis::WKBMultiPolygon;
           int polygontype = QGis::WKBPolygon;
-          int length = mCaptureList.size() + 1;//+1 because the first point is needed twice
+          int length = size() + 1;//+1 because the first point is needed twice
           int numrings = 1;
           int numpolygons = 1;
           int position = 0; //pointer position relative to &wkb[0]
-          memcpy( &wkb[position], &end, 1 );
+          memcpy( &wkb[position], &endian, 1 );
           position += 1;
           memcpy( &wkb[position], &wkbtype, sizeof( int ) );
           position += sizeof( int );
           memcpy( &wkb[position], &numpolygons, sizeof( int ) );
           position += sizeof( int );
-          memcpy( &wkb[position], &end, 1 );
+          memcpy( &wkb[position], &endian, 1 );
           position += 1;
           memcpy( &wkb[position], &polygontype, sizeof( int ) );
           position += sizeof( int );
@@ -412,7 +400,7 @@
           position += sizeof( int );
           double x, y;
           QList<QgsPoint>::iterator it;
-          for ( it = mCaptureList.begin(); it != mCaptureList.end(); ++it )//add the captured points to the polygon
+          for ( it = begin(); it != end(); ++it )//add the captured points to the polygon
           {
             QgsPoint savePoint = *it;
             x = savePoint.x();
@@ -425,7 +413,7 @@
             position += sizeof( double );
           }
           // close the polygon
-          it = mCaptureList.begin();
+          it = begin();
           QgsPoint savePoint = *it;
           x = savePoint.x();
           y = savePoint.y();
@@ -436,12 +424,10 @@
         else
         {
           QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) );
-          delete mRubberBand;
-          mRubberBand = NULL;
-          mCaptureList.clear();
+          stopCapturing();
           return; //unknown wkbtype
         }
-        f->setGeometryAndOwnership( &wkb[0], size );
+        f->setGeometryAndOwnership( &wkb[0], wkbsize );
 
         int avoidIntersectionsReturn = f->geometry()->avoidIntersections();
         if ( avoidIntersectionsReturn == 1 )
@@ -453,9 +439,7 @@
           //bail out...
           QMessageBox::critical( 0, tr( "Error" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type" ) );
           delete f;
-          delete mRubberBand;
-          mRubberBand = 0;
-          mCaptureList.clear();
+          stopCapturing();
           return;
         }
         else if ( avoidIntersectionsReturn == 3 )
@@ -510,12 +494,7 @@
       }
       delete f;
 
-      delete mRubberBand;
-      mRubberBand = NULL;
-
-      // delete the elements of mCaptureList
-      mCaptureList.clear();
-      mCanvas->refresh();
+      stopCapturing();
     }
   }
 }

Modified: trunk/qgis/src/app/qgsmaptooladdisland.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptooladdisland.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptooladdisland.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -70,9 +70,7 @@
   if ( !selectionErrorMsg.isEmpty() )
   {
     QMessageBox::critical( 0, tr( "Error, could not add island" ), selectionErrorMsg );
-    mCaptureList.clear();
-    delete mRubberBand;
-    mRubberBand = 0;
+    stopCapturing();
     return;
   }
 
@@ -94,18 +92,15 @@
 
   if ( e->button() == Qt::LeftButton )
   {
-    mCapturing = TRUE;
+    startCapturing();
   }
   else if ( e->button() == Qt::RightButton )
   {
-    mCapturing = FALSE;
-    delete mRubberBand;
-    mRubberBand = 0;
-
     //close polygon
-    mCaptureList.push_back( *mCaptureList.begin() );
+    closePolygon();
+
     vlayer->beginEditCommand( tr( "Part added" ) );
-    int errorCode = vlayer->addIsland( mCaptureList );
+    int errorCode = vlayer->addIsland( points() );
     QString errorMessage;
 
     if ( errorCode != 0 )
@@ -144,13 +139,12 @@
       int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
       if ( topologicalEditing )
       {
-        addTopologicalPoints( mCaptureList );
+        addTopologicalPoints( points() );
       }
 
       vlayer->endEditCommand();
     }
 
-    mCaptureList.clear();
-    mCanvas->refresh();
+    stopCapturing();
   }
 }

Modified: trunk/qgis/src/app/qgsmaptooladdring.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptooladdring.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptooladdring.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -70,19 +70,14 @@
 
   if ( e->button() == Qt::LeftButton )
   {
-    mCapturing = TRUE;
+    startCapturing();
   }
   else if ( e->button() == Qt::RightButton )
   {
-    mCapturing = FALSE;
-    delete mRubberBand;
-    mRubberBand = 0;
+    closePolygon();
 
-    //close polygon
-    mCaptureList.push_back( *mCaptureList.begin() );
-
     vlayer->beginEditCommand( tr( "Ring added" ) );
-    int addRingReturnCode = vlayer->addRing( mCaptureList );
+    int addRingReturnCode = vlayer->addRing( points() );
     if ( addRingReturnCode != 0 )
     {
       QString errorMessage;
@@ -118,7 +113,7 @@
     {
       vlayer->endEditCommand();
     }
-    mCaptureList.clear();
-    mCanvas->refresh();
+
+    stopCapturing();
   }
 }

Modified: trunk/qgis/src/app/qgsmaptoolcapture.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolcapture.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptoolcapture.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -14,32 +14,27 @@
  ***************************************************************************/
 /* $Id$ */
 
-#include "qgsapplication.h"
-#include "qgsattributedialog.h"
-#include "qgscoordinatetransform.h"
-#include "qgsfield.h"
-#include "qgslogger.h"
-#include "qgsgeometry.h"
 #include "qgsmaptoolcapture.h"
+
+#include "qgisapp.h"
+#include "qgsvertexmarker.h"
+#include "qgscursors.h"
+#include "qgsrubberband.h"
 #include "qgsmapcanvas.h"
 #include "qgsmaprenderer.h"
-#include "qgsmaptopixel.h"
-#include "qgsfeature.h"
-#include "qgsproject.h"
-#include "qgsrubberband.h"
 #include "qgsvectorlayer.h"
-#include "qgsvectordataprovider.h"
-#include "qgscursors.h"
+
 #include <QCursor>
 #include <QPixmap>
 #include <QMessageBox>
 #include <QMouseEvent>
+#include <QStatusBar>
 
 
 QgsMapToolCapture::QgsMapToolCapture( QgsMapCanvas* canvas, enum CaptureMode tool )
     : QgsMapToolEdit( canvas ), mCaptureMode( tool ), mRubberBand( 0 )
 {
-  mCapturing = FALSE;
+  mCapturing = false;
 
   QPixmap mySelectQPixmap = QPixmap(( const char ** ) capture_point_cursor );
   mCursor = QCursor( mySelectQPixmap, 8, 8 );
@@ -49,8 +44,7 @@
 
 QgsMapToolCapture::~QgsMapToolCapture()
 {
-  delete mRubberBand;
-  mRubberBand = 0;
+  stopCapturing();
 }
 
 void QgsMapToolCapture::canvasMoveEvent( QMouseEvent * e )
@@ -80,10 +74,7 @@
 
 void QgsMapToolCapture::deactivate()
 {
-  delete mRubberBand;
-  mRubberBand = 0;
-  mCaptureList.clear();
-
+  stopCapturing();
   QgsMapTool::deactivate();
 }
 
@@ -129,7 +120,9 @@
       return 2;
     }
     mRubberBand->addPoint( mapPoint );
-    mCaptureList.push_back( layerPoint );
+    mCaptureList.append( layerPoint );
+
+    validateGeometry();
   }
 
   return 0;
@@ -148,14 +141,96 @@
     }
 
     mRubberBand->removeLastPoint();
-    mCaptureList.pop_back();
+    mCaptureList.removeLast();
+
+    validateGeometry();
   }
 }
 
 void QgsMapToolCapture::keyPressEvent( QKeyEvent* e )
 {
-  if ( e->key() == Qt::Key_Backspace )
+  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
   {
     undo();
   }
 }
+
+void QgsMapToolCapture::startCapturing()
+{
+  mCapturing = true;
+}
+
+void QgsMapToolCapture::stopCapturing()
+{
+  if ( mRubberBand )
+  {
+    delete mRubberBand;
+    mRubberBand = 0;
+  }
+
+  while ( !mGeomErrorMarkers.isEmpty() )
+  {
+    delete mGeomErrorMarkers.takeFirst();
+  }
+
+  mGeomErrors.clear();
+
+  mCapturing = false;
+  mCaptureList.clear();
+  mCanvas->refresh();
+}
+
+void QgsMapToolCapture::closePolygon()
+{
+  mCaptureList.append( mCaptureList[0] );
+}
+
+void QgsMapToolCapture::validateGeometry()
+{
+  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
+
+  mGeomErrors.clear();
+
+  while ( !mGeomErrorMarkers.isEmpty() )
+  {
+    delete mGeomErrorMarkers.takeFirst();
+  }
+
+  switch ( mCaptureMode )
+  {
+    case CapturePoint:
+      return;
+    case CaptureLine:
+      if ( mCaptureList.size() < 2 )
+        return;
+      QgsGeometry::validatePolyline( mGeomErrors, 0, mCaptureList.toVector() );
+      break;
+    case CapturePolygon:
+      if ( mCaptureList.size() < 3 )
+        return;
+      QgsGeometry::validatePolyline( mGeomErrors, 0, mCaptureList.toVector() << mCaptureList[0], true );
+      break;
+  }
+
+  QString tip;
+  for ( int i = 0; i < mGeomErrors.size(); i++ )
+  {
+    tip += mGeomErrors[i].what() + "\n";
+    if ( !mGeomErrors[i].hasWhere() )
+      continue;
+
+    QgsVertexMarker *vm =  new QgsVertexMarker( mCanvas );
+    vm->setCenter( mCanvas->mapRenderer()->layerToMapCoordinates( vlayer, mGeomErrors[i].where() ) );
+    vm->setIconType( QgsVertexMarker::ICON_X );
+    vm->setPenWidth( 2 );
+    vm->setToolTip( mGeomErrors[i].what() );
+    vm->setColor( Qt::green );
+    vm->setZValue( vm->zValue() + 1 );
+    mGeomErrorMarkers << vm;
+  }
+
+  QStatusBar *sb = QgisApp::instance()->statusBar();
+  sb->showMessage( QObject::tr( "%n geometry error(s) found.", "number of geometry errors", mGeomErrors.size() ) );
+  if ( !tip.isEmpty() )
+    sb->setToolTip( tip );
+}

Modified: trunk/qgis/src/app/qgsmaptoolcapture.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoolcapture.h	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptoolcapture.h	2010-01-24 16:07:55 UTC (rev 12830)
@@ -20,9 +20,11 @@
 #include "qgsmapcanvassnapper.h"
 #include "qgsmaptooledit.h"
 #include "qgspoint.h"
+#include "qgsgeometry.h"
 
 class QgsGeometry;
 class QgsRubberBand;
+class QgsVertexMarker;
 
 #include <QPoint>
 #include <QList>
@@ -69,7 +71,26 @@
     */
 
   protected:
+    /**Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates)
+     @return 0 in case of success, 1 if current layer is not a vector layer, 2 if coordinate transformation failed*/
+    int addVertex( const QPoint& p );
 
+    /**Removes the last vertex from mRubberBand and mCaptureList*/
+    void undo();
+
+    void startCapturing();
+    void stopCapturing();
+
+    CaptureMode mode() { return mCaptureMode; }
+
+
+    int size() { return mCaptureList.size(); }
+    QList<QgsPoint>::iterator begin() { return mCaptureList.begin(); }
+    QList<QgsPoint>::iterator end() { return mCaptureList.end(); }
+    const QList<QgsPoint> &points() { return mCaptureList; }
+    void closePolygon();
+
+  private:
     /** which capturing tool is being used */
     enum CaptureMode mCaptureMode;
 
@@ -82,13 +103,9 @@
     /** List to store the points of digitised lines and polygons (in layer coordinates)*/
     QList<QgsPoint> mCaptureList;
 
-    /**Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates)
-     @return 0 in case of success, 1 if current layer is not a vector layer, 2 if coordinate transformation failed*/
-    int addVertex( const QPoint& p );
-
-    /**Removes the last vertex from mRubberBand and mCaptureList*/
-    void undo();
-
+    void validateGeometry();
+    QList< QgsGeometry::Error > mGeomErrors;
+    QList< QgsVertexMarker * > mGeomErrorMarkers;
 };
 
 #endif

Modified: trunk/qgis/src/app/qgsmaptoolnodetool.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolnodetool.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptoolnodetool.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -53,8 +53,6 @@
   return marker;
 }
 
-
-
 QgsMapToolNodeTool::QgsMapToolNodeTool( QgsMapCanvas* canvas ): QgsMapToolVertexEdit( canvas )
 {
   mSelectionFeature = NULL;
@@ -121,20 +119,10 @@
   QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
   if ( mSelectionFeature != NULL && !mChangingGeometry && ( vlayer->featureAtId( mSelectionFeature->featureId(), feat, true, false ) ) )
   {
-    try
+    if ( !feat.geometry()->isGeosEqual( *mSelectionFeature->feature()->geometry() ) )
     {
-      if ( !GEOSEquals( feat.geometry()->asGeos(), mSelectionFeature->feature()->geometry()->asGeos() ) )
-      {
-        mSelectionFeature->updateFromFeature();
-        //throw error
-      }
-    }
-    catch ( ... )
-    {
-      //GEOS is throwing exception when polygon is not valid to be able to edit it
-      //Only possibility to fix this operation since node tool doesn't allow to store invalid geometry after editing
       mSelectionFeature->updateFromFeature();
-      //if it does update markers just to be sure of correctness
+      //throw error
     }
   }
 }
@@ -474,7 +462,7 @@
   }
   try
   {
-    if ( !GEOSEquals( feat.geometry()->asGeos(), mSelectionFeature->feature()->geometry()->asGeos() ) )
+    if ( !feat.geometry()->isGeosEqual( *mSelectionFeature->feature()->geometry() ) )
     {
       //update markers when geometries are not valid
       mSelectionFeature->updateFromFeature();
@@ -1032,7 +1020,7 @@
   mVlayer->featureAtId( mFeatureId, f, true, false );
 
   bool wasValid = false; // mGeomErrors.isEmpty();
-  bool isValid = GEOSisValid( f.geometry()->asGeos() );
+  bool isValid = f.geometry()->isGeosValid();
   if ( wasValid && !isValid )
   {
     QMessageBox::warning( NULL,
@@ -1106,7 +1094,7 @@
   QgsFeature f;
   mVlayer->featureAtId( mFeatureId, f, true, false );
   bool wasValid = false; // mGeomErrors.isEmpty();
-  bool isValid = GEOSisValid( f.geometry()->asGeos() );
+  bool isValid = f.geometry()->isGeosValid();
   if ( wasValid && !isValid )
   {
     QMessageBox::warning( NULL,

Modified: trunk/qgis/src/app/qgsmaptoolreshape.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolreshape.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptoolreshape.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -69,24 +69,21 @@
 
   if ( e->button() == Qt::LeftButton )
   {
-    mCapturing = TRUE;
+    startCapturing();
   }
   else if ( e->button() == Qt::RightButton )
   {
-    mCapturing = FALSE;
-    delete mRubberBand;
-    mRubberBand = 0;
-
     //find out bounding box of mCaptureList
-    if ( mCaptureList.size() < 1 )
+    if ( size() < 1 )
     {
+      stopCapturing();
       return;
     }
-    QgsPoint firstPoint = mCaptureList.at( 0 );
+    QgsPoint firstPoint = points().at( 0 );
     QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() );
-    for ( int i = 1; i < mCaptureList.size(); ++i )
+    for ( int i = 1; i < size(); ++i )
     {
-      bbox.combineExtentWith( mCaptureList.at( i ).x(), mCaptureList.at( i ).y() );
+      bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() );
     }
 
     //query all the features that intersect bounding box of capture line
@@ -104,7 +101,7 @@
       QgsGeometry* geom = f.geometry();
       if ( geom )
       {
-        reshapeReturn = geom->reshapeGeometry( mCaptureList );
+        reshapeReturn = geom->reshapeGeometry( points() );
         if ( reshapeReturn == 0 )
         {
           vlayer->changeGeometry( f.id(), geom );
@@ -122,7 +119,6 @@
       vlayer->destroyEditCommand();
     }
 
-    mCaptureList.clear();
-    mCanvas->refresh();
+    stopCapturing();
   }
 }

Modified: trunk/qgis/src/app/qgsmaptoolsplitfeatures.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolsplitfeatures.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/app/qgsmaptoolsplitfeatures.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -69,18 +69,14 @@
 
   if ( e->button() == Qt::LeftButton )
   {
-    mCapturing = TRUE;
+    startCapturing();
   }
   else if ( e->button() == Qt::RightButton )
   {
-    mCapturing = FALSE;
-    delete mRubberBand;
-    mRubberBand = 0;
-
     //bring up dialog if a split was not possible (polygon) or only done once (line)
     int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
     vlayer->beginEditCommand( tr( "Features split" ) );
-    int returnCode = vlayer->splitFeatures( mCaptureList, topologicalEditing );
+    int returnCode = vlayer->splitFeatures( points(), topologicalEditing );
     vlayer->endEditCommand();
     if ( returnCode == 4 )
     {
@@ -92,7 +88,6 @@
       QMessageBox::warning( 0, tr( "Split error" ), tr( "An error occured during feature splitting" ) );
     }
 
-    mCaptureList.clear();
-    mCanvas->refresh();
+    stopCapturing();
   }
 }

Modified: trunk/qgis/src/core/qgsgeometry.cpp
===================================================================
--- trunk/qgis/src/core/qgsgeometry.cpp	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/core/qgsgeometry.cpp	2010-01-24 16:07:55 UTC (rev 12830)
@@ -44,14 +44,11 @@
 class GEOSException
 {
   public:
-    GEOSException( const char *theMsg )
+    GEOSException( QString theMsg )
     {
-      if ( strcmp( theMsg, "Unknown exception thrown" ) == 0 && lastMsg )
+      if ( theMsg == "Unknown exception thrown"  && lastMsg.isNull() )
       {
-        delete [] theMsg;
-        char *aMsg = new char[strlen( lastMsg )+1];
-        strcpy( aMsg, lastMsg );
-        msg = aMsg;
+        msg = theMsg;
       }
       else
       {
@@ -69,47 +66,46 @@
     ~GEOSException()
     {
       if ( lastMsg == msg )
-        lastMsg = NULL;
-      delete [] msg;
+        lastMsg = QString::null;
     }
 
-    const char *what()
+    QString what()
     {
       return msg;
     }
 
   private:
-    const char *msg;
-    static const char *lastMsg;
+    QString msg;
+    static QString lastMsg;
 };
 
-const char *GEOSException::lastMsg = NULL;
+QString GEOSException::lastMsg;
 
 static void throwGEOSException( const char *fmt, ... )
 {
   va_list ap;
+  char buffer[1024];
+
   va_start( ap, fmt );
-  size_t buflen = vsnprintf( NULL, 0, fmt, ap );
-  char *msg = new char[buflen+1];
-  vsnprintf( msg, buflen + 1, fmt, ap );
+  vsnprintf( buffer, sizeof buffer, fmt, ap );
   va_end( ap );
 
-  QgsDebugMsg( QString( "GEOS exception encountered: %1" ).arg( msg ) );
+  QgsDebugMsg( QString( "GEOS exception encountered: %1" ).arg( buffer ) );
 
-  throw GEOSException( msg );
+  throw GEOSException( QString::fromUtf8( buffer ) );
 }
 
 static void printGEOSNotice( const char *fmt, ... )
 {
 #if defined(QGISDEBUG)
   va_list ap;
+  char buffer[1024];
+
   va_start( ap, fmt );
-  size_t buflen = vsnprintf( NULL, 0, fmt, ap );
-  char *msg = new char[buflen+1];
-  vsnprintf( msg, buflen + 1, fmt, ap );
+  vsnprintf( buffer, sizeof buffer, fmt, ap );
   va_end( ap );
-  QgsDebugMsg( QString( "GEOS notice: " ).arg( msg ) );
-  delete [] msg;
+
+  QgsDebugMsg( QString( "GEOS notice: %1" ).arg( QString::fromUtf8( buffer ) ) );
 #endif
 }
 
@@ -3157,7 +3153,7 @@
   return 0;
 }
 
-int QgsGeometry::splitGeometry( const QList<QgsPoint>& splitLine, QList<QgsGeometry*>& newGeometries, bool topological, QList<QgsPoint>& topologyTestPoints )
+int QgsGeometry::splitGeometry( const QList<QgsPoint>& splitLine, QList<QgsGeometry*>& newGeometries, bool topological, QList<QgsPoint> &topologyTestPoints )
 {
   int returnCode = 0;
 
@@ -6366,58 +6362,86 @@
   }
 }
 
-void QgsGeometry::validatePolyline( QList<Error> &errors, int i, const QgsPolyline &line )
+void QgsGeometry::validatePolyline( QList<Error> &errors, int i, QgsPolyline line, bool ring )
 {
-  if ( line.size() < 2 )
+  if ( ring )
   {
+    if ( line.size() < 3 )
+    {
+      QString msg = QObject::tr( "ring %1 with less than three points" ).arg( i );
+      QgsDebugMsg( msg );
+      errors << Error( msg );
+      return;
+    }
+
+    if ( line[0] != line[ line.size()-1 ] )
+    {
+      QString msg = QObject::tr( "ring %1 not closed" ).arg( i );
+      QgsDebugMsg( msg );
+      errors << Error( msg );
+      return;
+    }
+  }
+  else if ( line.size() < 2 )
+  {
     QString msg = QObject::tr( "line %1 with less than two points" ).arg( i );
     QgsDebugMsg( msg );
     errors << Error( msg );
     return;
   }
 
-  bool closed = line[0] == line[ line.size()-1 ];
+  int j = 0;
+  while ( j < line.size() - 1 )
+  {
+    int n = 0;
+    while ( j < line.size() && line[j] == line[j+1] )
+    {
+      line.remove( j );
+      n++;
+    }
 
-  if ( closed && line.size() < 3 )
-  {
-    QString msg = QObject::tr( "ring %1 with less than three points" ).arg( i );
-    QgsDebugMsg( msg );
-    errors << Error( msg );
-    return;
+    if ( n > 0 )
+    {
+      QString msg = QObject::tr( "line %1 contains %n duplicate node(s) at %2", "number of duplicate nodes", n ).arg( i ).arg( j );
+      QgsDebugMsg( msg );
+      errors << Error( msg, line[j] );
+    }
+
+    j++;
   }
 
-  for ( int j = 0; j < line.size() - 3; j++ )
+  for ( j = 0; j < line.size() - 3; j++ )
   {
     QgsVector v = line[j+1] - line[j];
+    double vl = v.length();
 
-    int n = j == 0 && closed ? line.size() - 2 : line.size() - 1;
+    int n = ( j == 0 && ring ) ? line.size() - 2 : line.size() - 1;
 
     for ( int k = j + 2; k < n; k++ )
     {
       QgsVector w = line[k+1] - line[k];
 
       QgsPoint s;
-      if ( intersectLines( line[j], v, line[k], w, s ) )
+      if ( !intersectLines( line[j], v, line[k], w, s ) )
+        continue;
+
+      double d = -distLine2Point( line[j], v.perpVector(), s );
+      if ( d < 0 || d > vl )
+        continue;
+
+      d = -distLine2Point( line[k], w.perpVector(), s );
+      if ( d < 0 || d > w.length() )
+        continue;
+
+      QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4" ).arg( j ).arg( k ).arg( i ).arg( s.toString() );
+      QgsDebugMsg( msg );
+      errors << Error( msg, s );
+      if ( errors.size() > 100 )
       {
-        double d = -distLine2Point( line[j], v.perpVector(), s );
-
-        if ( d >= 0 && d <= v.length() )
-        {
-          d = -distLine2Point( line[k], w.perpVector(), s );
-          if ( d >= 0 && d <= w.length() )
-          {
-            QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4" ).arg( j ).arg( k ).arg( i ).arg( s.toString() );
-            QgsDebugMsg( msg );
-            errors << Error( msg, s );
-            if ( errors.size() > 100 )
-            {
-              QString msg = QObject::tr( "stopping validation after more than 100 errors" );
-              QgsDebugMsg( msg );
-              errors << Error( msg );
-              return;
-            }
-          }
-        }
+        QString msg = QObject::tr( "stopping validation after more than 100 errors" );
+        QgsDebugMsg( msg );
+        errors << Error( msg );
+        return;
       }
     }
   }
@@ -6448,7 +6472,7 @@
   // check if rings are self-intersecting
   for ( int i = 0; i < polygon.size(); i++ )
   {
-    validatePolyline( errors, i, polygon[i] );
+    validatePolyline( errors, i, polygon[i], true );
   }
 }
 
@@ -6521,3 +6545,41 @@
       break;
   }
 }
+
+bool QgsGeometry::isGeosValid()
+{
+  try
+  {
+    GEOSGeometry *g = asGeos();
+
+    if ( !g )
+      return false;
+
+    return GEOSisValid( g );
+  }
+  catch ( GEOSException &e )
+  {
+    // looks like geometry is fubar
+    Q_UNUSED( e );
+    QgsDebugMsg( QString( "GEOS exception caught: %1" ).arg( e.what() ) );
+    return false;
+  }
+}
+
+bool QgsGeometry::isGeosEqual( QgsGeometry &g )
+{
+  try
+  {
+    GEOSGeometry *g0 = asGeos();
+    GEOSGeometry *g1 = g.asGeos();
+
+    return g0 && g1 && GEOSEquals( g0, g1 );
+  }
+  catch ( GEOSException &e )
+  {
+    // looks like geometry is fubar
+    Q_UNUSED( e );
+    QgsDebugMsg( QString( "GEOS exception caught: %1" ).arg( e.what() ) );
+    return false;
+  }
+}

Modified: trunk/qgis/src/core/qgsgeometry.h
===================================================================
--- trunk/qgis/src/core/qgsgeometry.h	2010-01-24 15:59:54 UTC (rev 12829)
+++ trunk/qgis/src/core/qgsgeometry.h	2010-01-24 16:07:55 UTC (rev 12830)
@@ -131,7 +131,16 @@
     /** Returns true if wkb of the geometry is of WKBMulti* type */
     bool isMultipart();
 
+    /** compare geometries using GEOS
+      @note added in 1.5
+     */
+    bool isGeosEqual( QgsGeometry & );
 
+    /** check validity using GEOS
+      @note added in 1.5
+     */
+    bool isGeosValid();
+
     double distance( QgsGeometry& geom );
 
     /**
@@ -250,7 +259,7 @@
     int splitGeometry( const QList<QgsPoint>& splitLine,
                        QList<QgsGeometry*>&newGeometries,
                        bool topological,
-                       QList<QgsPoint>& topologyTestPoints );
+                       QList<QgsPoint> &topologyTestPoints );
 
     /**Replaces a part of this geometry with another line
       @return 0 in case of success
@@ -379,6 +388,9 @@
 
     void validateGeometry( QList<Error> &errors );
 
+    static void validatePolyline( QList<Error> &errors, int i, QgsPolyline polyline, bool ring = false );
+    static void validatePolygon( QList<Error> &errors, int i, const QgsPolygon &polygon );
+
   private:
     // Private variables
 
@@ -511,13 +523,10 @@
     /** return polygon from wkb */
     QgsPolygon asPolygon( unsigned char*& ptr, bool hasZValue );
 
-    void checkRingIntersections( QList<Error> &errors,
-                                 int p0, int i0, const QgsPolyline &ring0,
-                                 int p1, int i1, const QgsPolyline &ring1 );
+    static void checkRingIntersections( QList<Error> &errors,
+                                        int p0, int i0, const QgsPolyline &ring0,
+                                        int p1, int i1, const QgsPolyline &ring1 );
 
-    void validatePolyline( QList<Error> &errors, int i, const QgsPolyline &polygon );
-    void validatePolygon( QList<Error> &errors, int i, const QgsPolygon &polygon );
-
     static int refcount;
 }; // class QgsGeometry
 



More information about the QGIS-commit mailing list