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

svn_qgis at osgeo.org svn_qgis at osgeo.org
Tue Jan 12 13:17:34 EST 2010


Author: jef
Date: 2010-01-12 13:17:34 -0500 (Tue, 12 Jan 2010)
New Revision: 12749

Modified:
   trunk/qgis/src/app/qgsmaptoolnodetool.cpp
   trunk/qgis/src/app/qgsmaptoolnodetool.h
   trunk/qgis/src/core/qgsgeometry.cpp
   trunk/qgis/src/core/qgsgeometry.h
   trunk/qgis/src/core/qgspoint.cpp
   trunk/qgis/src/core/qgspoint.h
Log:
[FEATURE] allow editing of invalid geometry in node tool

Modified: trunk/qgis/src/app/qgsmaptoolnodetool.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolnodetool.cpp	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/app/qgsmaptoolnodetool.cpp	2010-01-12 18:17:34 UTC (rev 12749)
@@ -24,8 +24,12 @@
 #include <math.h>
 #include <QMouseEvent>
 #include <QMessageBox>
+#include "qgslogger.h"
+#include "qgisapp.h"
 
+#include <QStatusBar>
 
+
 QgsRubberBand* QgsMapToolNodeTool::createRubberBandMarker( QgsPoint center, QgsVectorLayer* vlayer )
 {
   //create rubber band marker for moving points
@@ -128,7 +132,7 @@
     catch ( ... )
     {
       //GEOS is throwing exception when polygon is not valid to be able to edit it
-      //Only possibility to fix this operation sice node tool doesn't allow to strore invalid geometry after editing
+      //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
     }
@@ -148,7 +152,7 @@
   for ( int i = 0; i < vertexMap.size(); i++ )
   {
     //create rubber band
-    if ( vertexMap[i].selected && !( vertexMap[i].inRubberBand ) )
+    if ( vertexMap[i].selected && !vertexMap[i].inRubberBand )
     {
       geometry->adjacentVertices( i, beforeVertex, afterVertex );
       vertex = i;
@@ -774,7 +778,7 @@
   mTopologyMovingVertexes.clear();
   mTopologyRubberBandVertexes.clear();
 
-  //remove all data from selected feature (no change to rubber bands itself
+  //remove all data from selected feature (no change to rubber bands itself)
   if ( mSelectionFeature != NULL )
     mSelectionFeature->cleanRubberBandsData();
 }
@@ -936,10 +940,48 @@
   {
     mFeature = feature;
   }
+
   //createvertexmap
   createVertexMap();
+
+  validateGeometry();
 }
 
+void SelectionFeature::validateGeometry( QgsGeometry *g )
+{
+  QgsDebugMsg( "validating geometry" );
+
+  if ( g == NULL )
+    g = mFeature->geometry();
+
+  g->validateGeometry( mGeomErrors );
+
+  while ( !mGeomErrorMarkers.isEmpty() )
+  {
+    delete mGeomErrorMarkers.takeFirst();
+  }
+
+  QString tip;
+
+  for ( int i = 0; i < mGeomErrors.size(); i++ )
+  {
+    tip += mGeomErrors[i].what() + "\n";
+    if ( !mGeomErrors[i].hasWhere() )
+      continue;
+
+    QgsVertexMarker *vm = createVertexMarker( mGeomErrors[i].where(), QgsVertexMarker::ICON_X );
+    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 );
+}
+
 void SelectionFeature::deleteSelectedVertexes()
 {
   int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
@@ -983,7 +1025,10 @@
   }
   QgsFeature f;
   mVlayer->featureAtId( mFeatureId, f, true, false );
-  if ( !GEOSisValid( f.geometry()->asGeos() ) )
+
+  bool wasValid = false; // mGeomErrors.isEmpty();
+  bool isValid = GEOSisValid( f.geometry()->asGeos() );
+  if ( wasValid && !isValid )
   {
     QMessageBox::warning( NULL,
                           tr( "Node tool" ),
@@ -992,7 +1037,7 @@
                           QMessageBox::Ok );
   }
 
-  if ( count != 0 && GEOSisValid( f.geometry()->asGeos() ) )
+  if ( count != 0 && ( !wasValid || isValid ) )
   {
     mVlayer->endEditCommand();
   }
@@ -1054,7 +1099,9 @@
   }
   QgsFeature f;
   mVlayer->featureAtId( mFeatureId, f, true, false );
-  if ( !GEOSisValid( f.geometry()->asGeos() ) )
+  bool wasValid = false; // mGeomErrors.isEmpty();
+  bool isValid = GEOSisValid( f.geometry()->asGeos() );
+  if ( wasValid && !isValid )
   {
     QMessageBox::warning( NULL,
                           tr( "Node tool" ),
@@ -1068,17 +1115,18 @@
   else
   {
     mVlayer->endEditCommand();
+    validateGeometry( f.geometry() );
   }
   updateFeature();
 }
 
-QgsVertexMarker* SelectionFeature::createVertexMarker( QgsPoint center )
+QgsVertexMarker *SelectionFeature::createVertexMarker( QgsPoint center, QgsVertexMarker::IconType type )
 {
-  QgsVertexMarker* marker = new QgsVertexMarker( mCanvas );
+  QgsVertexMarker *marker = new QgsVertexMarker( mCanvas );
   QgsPoint newCenter = mCanvas->mapRenderer()->layerToMapCoordinates( mVlayer, center );
   marker->setCenter( newCenter );
 
-  marker->setIconType( QgsVertexMarker::ICON_BOX );
+  marker->setIconType( type );
 
   marker->setColor( Qt::red );
 
@@ -1103,6 +1151,11 @@
     VertexEntry entry = mVertexMap.takeLast();
     delete entry.vertexMarker;
   }
+
+  while ( !mGeomErrorMarkers.isEmpty() )
+  {
+    delete mGeomErrorMarkers.takeFirst();
+  }
 }
 
 bool SelectionFeature::isSelected( int vertexNr )
@@ -1134,6 +1187,7 @@
         entry.originalIndex = i;
         entry.inRubberBand = false;
         QgsVertexMarker* marker = createVertexMarker( poly[i] );
+        marker->setToolTip( tr( "ring %1, vertex %2" ).arg( i2 ).arg( i ) );
         entry.vertexMarker = marker;
         mVertexMap.insert( y + i, entry );
       }
@@ -1161,7 +1215,7 @@
           entry.originalIndex = y + i - 1;
           entry.inRubberBand = false;
           QgsVertexMarker* marker = createVertexMarker( poly[i] );
-
+          marker->setToolTip( tr( "polygon %1, ring %2, vertex %3" ).arg( i2 ).arg( i3 ).arg( i ) );
           entry.vertexMarker = marker;
           mVertexMap.insert( y + i, entry );
         }
@@ -1194,6 +1248,7 @@
         entry.originalIndex = i;
         entry.inRubberBand = false;
         QgsVertexMarker* marker = createVertexMarker( poly[i] );
+        marker->setToolTip( tr( "polyline %1, vertex %2" ).arg( i2 ).arg( i ) );
         entry.vertexMarker = marker;
         mVertexMap.insert( y + i, entry );
       }
@@ -1214,6 +1269,7 @@
       entry.originalIndex = i;
       entry.inRubberBand = false;
       QgsVertexMarker* marker = createVertexMarker( poly[i] );
+      marker->setToolTip( tr( "vertex %1" ).arg( i ) );
       entry.vertexMarker = marker;
       mVertexMap.insert( i, entry );
     }
@@ -1236,6 +1292,7 @@
       entry.originalIndex = 1;
       entry.inRubberBand = false;
       QgsVertexMarker* marker = createVertexMarker( poly[i] );
+      marker->setToolTip( tr( "point %1" ).arg( i ) );
       entry.vertexMarker = marker;
       mVertexMap.insert( i, entry );
     }
@@ -1251,6 +1308,7 @@
     entry.originalIndex = 1;
     entry.inRubberBand = false;
     QgsVertexMarker* marker = createVertexMarker( poly );
+    marker->setToolTip( tr( "single point" ) );
     entry.vertexMarker = marker;
     mVertexMap.insert( 1, entry );
   }
@@ -1372,4 +1430,3 @@
 {
   return mVlayer;
 }
-

Modified: trunk/qgis/src/app/qgsmaptoolnodetool.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoolnodetool.h	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/app/qgsmaptoolnodetool.h	2010-01-12 18:17:34 UTC (rev 12749)
@@ -161,7 +161,7 @@
      * @param center center of marker
      * @return created vertex marker
      */
-    QgsVertexMarker* createVertexMarker( QgsPoint center );
+    QgsVertexMarker* createVertexMarker( QgsPoint center, QgsVertexMarker::IconType type = QgsVertexMarker::ICON_BOX );
 
     /**
      * Getter for getting vector layer which selection is working
@@ -171,7 +171,6 @@
 
 
   private:
-
     /**
      * Deletes whole vertex map.
      */
@@ -198,10 +197,15 @@
     void createVertexMapPoint();
 
     /**
-     * Updates stored feauture to actual one loaded from layer
+     * Updates stored feature to actual one loaded from layer
      */
     void updateFeature();
 
+    /**
+     * Validates the geometry
+     */
+    void validateGeometry( QgsGeometry *g = NULL );
+
     QgsFeature* mFeature;
     int mFeatureId;
     bool mFeatureSelected;
@@ -209,6 +213,9 @@
     QgsRubberBand* mRubberBand;
     QList<VertexEntry> mVertexMap;
     QgsMapCanvas* mCanvas;
+
+    QList< QgsGeometry::Error > mGeomErrors;
+    QList< QgsVertexMarker * > mGeomErrorMarkers;
 };
 
 /**A maptool to move/deletes/adds vertices of line or polygon features*/
@@ -367,7 +374,6 @@
 
     /** flag to tell if edition points */
     bool mIsPoint;
-
 };
 
 

Modified: trunk/qgis/src/core/qgsgeometry.cpp
===================================================================
--- trunk/qgis/src/core/qgsgeometry.cpp	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/core/qgsgeometry.cpp	2010-01-12 18:17:34 UTC (rev 12749)
@@ -2360,7 +2360,7 @@
       return -1;
 
     const GEOSGeometry *g = GEOSGetExteriorRing( mGeos );
-    if ( g == NULL )
+    if ( !g )
       return -1;
 
     const GEOSCoordSequence *sequence = GEOSGeom_getCoordSeq( g );
@@ -5442,7 +5442,7 @@
 
   testPoints.clear();
   GEOSGeometry* intersectionGeom = GEOSIntersection( mGeos, splitLine );
-  if ( intersectionGeom == NULL )
+  if ( !intersectionGeom )
   {
     return 1;
   }
@@ -5853,17 +5853,17 @@
 
 double QgsGeometry::distance( QgsGeometry& geom )
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
 
-  if ( geom.mGeos == NULL )
+  if ( !geom.mGeos )
   {
     geom.exportWkbToGeos();
   }
 
-  if ( mGeos == NULL || geom.mGeos == NULL )
+  if ( !mGeos || !geom.mGeos )
     return -1.0;
 
   double dist = -1.0;
@@ -5880,7 +5880,7 @@
 
 QgsGeometry* QgsGeometry::buffer( double distance, int segments )
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
@@ -5898,7 +5898,7 @@
 
 QgsGeometry* QgsGeometry::simplify( double tolerance )
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
@@ -5915,7 +5915,7 @@
 
 QgsGeometry* QgsGeometry::centroid()
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
@@ -5932,7 +5932,7 @@
 
 QgsGeometry* QgsGeometry::convexHull()
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
@@ -5950,15 +5950,15 @@
 
 QgsGeometry* QgsGeometry::intersection( QgsGeometry* geometry )
 {
-  if ( geometry == NULL )
+  if ( !geometry )
   {
     return NULL;
   }
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
-  if ( geometry->mGeos == NULL )
+  if ( !geometry->mGeos )
   {
     geometry->exportWkbToGeos();
   }
@@ -5976,15 +5976,15 @@
 
 QgsGeometry* QgsGeometry::combine( QgsGeometry* geometry )
 {
-  if ( geometry == NULL )
+  if ( !geometry )
   {
     return NULL;
   }
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
-  if ( geometry->mGeos == NULL )
+  if ( !geometry->mGeos )
   {
     geometry->exportWkbToGeos();
   }
@@ -6015,15 +6015,15 @@
 
 QgsGeometry* QgsGeometry::difference( QgsGeometry* geometry )
 {
-  if ( geometry == NULL )
+  if ( !geometry )
   {
     return NULL;
   }
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
-  if ( geometry->mGeos == NULL )
+  if ( !geometry->mGeos )
   {
     geometry->exportWkbToGeos();
   }
@@ -6041,15 +6041,15 @@
 
 QgsGeometry* QgsGeometry::symDifference( QgsGeometry* geometry )
 {
-  if ( geometry == NULL )
+  if ( !geometry )
   {
     return NULL;
   }
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
   }
-  if ( geometry->mGeos == NULL )
+  if ( !geometry->mGeos )
   {
     geometry->exportWkbToGeos();
   }
@@ -6067,10 +6067,10 @@
 
 QList<QgsGeometry*> QgsGeometry::asGeometryCollection()
 {
-  if ( mGeos == NULL )
+  if ( !mGeos )
   {
     exportWkbToGeos();
-    if ( mGeos == NULL )
+    if ( !mGeos )
       return QList<QgsGeometry*>();
   }
 
@@ -6258,3 +6258,228 @@
 
   return returnValue;
 }
+
+//
+// distance of point q from line through p in direction v
+// return >0  => q lies left of the line
+//        <0  => q lies right of the line
+//
+static double distLine2Point( QgsPoint p, QgsVector v, QgsPoint q )
+{
+  if ( v.length() == 0 )
+  {
+    throw QgsException( QObject::tr( "invalid line" ) );
+  }
+
+  return ( v.x()*( q.y() - p.y() ) - v.y()*( q.x() - p.x() ) ) / v.length();
+}
+
+static bool intersectLines( QgsPoint p, QgsVector v, QgsPoint q, QgsVector w, QgsPoint &s )
+{
+  double d = v.y() * w.x() - v.x() * w.y();
+
+  if ( d == 0 )
+    return false;
+
+  double dx = q.x() - p.x();
+  double dy = q.y() - p.y();
+  double k = ( dy * w.x() - dx * w.y() ) / d;
+
+  s = p + v * k;
+
+  return true;
+}
+
+bool pointInRing( const QgsPolyline &ring, const QgsPoint &p )
+{
+  bool inside = false;
+  int j = ring.size() - 1;
+
+  for ( int i = 0; i < ring.size(); i++ )
+  {
+    if ( ring[i].x() == p.x() && ring[i].y() == p.y() )
+      return true;
+
+    if (( ring[i].y() < p.y() && ring[j].y() >= p.y() ) ||
+        ( ring[j].y() < p.y() && ring[i].y() >= p.y() ) )
+    {
+      if ( ring[i].x() + ( p.y() - ring[i].y() ) / ( ring[j].y() - ring[i].y() )*( ring[j].x() - ring[i].x() ) <= p.x() )
+        inside = !inside;
+    }
+
+    j = i;
+  }
+
+  return inside;
+}
+
+static bool ringInRing( const QgsPolyline &inside, const QgsPolyline &outside )
+{
+  for ( int i = 0; i < inside.size(); i++ )
+  {
+    if ( !pointInRing( outside, inside[i] ) )
+      return false;
+  }
+
+  return true;
+}
+
+bool ringIntersectsRing( const QgsPolyline &ring0, const QgsPolyline &ring1 )
+{
+  bool inside = false;
+  bool outside = false;
+
+  for ( int i = 0; i < ring0.size(); i++ )
+  {
+    if ( pointInRing( ring1, ring0[i] ) )
+    {
+      inside = true;
+    }
+    else
+    {
+      outside = true;
+    }
+
+    if ( outside && inside )
+      return true;
+  }
+
+  return false;
+}
+
+void QgsGeometry::validatePolyline( QList<Error> &errors, int i, const QgsPolyline &line )
+{
+  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 ];
+
+  if ( closed && line.size() < 3 )
+  {
+    QString msg = QObject::tr( "ring %1 with less than three points" ).arg( i );
+    QgsDebugMsg( msg );
+    errors << Error( msg );
+    return;
+  }
+
+  for ( int j = 0; j < line.size() - 3; j++ )
+  {
+    QgsVector v = line[j+1] - line[j];
+
+    int n = j == 0 && closed ? 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 ) )
+      {
+        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;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+void QgsGeometry::validatePolygon( QList<Error> &errors, int idx, const QgsPolygon &polygon )
+{
+  for ( int i = 1; i < polygon.size(); i++ )
+  {
+    if ( !ringInRing( polygon[i], polygon[0] ) )
+    {
+      QString msg = QObject::tr( "ring %1 of polygon %2 not in exterior ring" ).arg( i ).arg( idx );
+      QgsDebugMsg( msg );
+      errors << Error( msg );
+    }
+  }
+
+  for ( int i = 1; i < polygon.size(); i++ )
+  {
+    for ( int j = i + 1; j < polygon.size(); j++ )
+    {
+      if ( ringIntersectsRing( polygon[i], polygon[j] ) )
+      {
+        QString msg = QObject::tr( "interior rings %1 and %2 of polygon %3 intersect" ).arg( i ).arg( j ).arg( idx );
+        QgsDebugMsg( msg );
+        errors << Error( msg );
+      }
+    }
+  }
+
+  for ( int i = 0; i < polygon.size(); i++ )
+  {
+    validatePolyline( errors, i, polygon[i] );
+  }
+}
+
+void QgsGeometry::validateGeometry( QList<Error> &errors )
+{
+  errors.clear();
+
+  switch ( wkbType() )
+  {
+    case QGis::WKBPoint:
+    case QGis::WKBPoint25D:
+    case QGis::WKBMultiPoint:
+    case QGis::WKBMultiPoint25D:
+      break;
+
+    case QGis::WKBLineString:
+    case QGis::WKBLineString25D:
+      validatePolyline( errors, 0, asPolyline() );
+      break;
+
+    case QGis::WKBMultiLineString:
+    case QGis::WKBMultiLineString25D:
+    {
+      QgsMultiPolyline mp = asMultiPolyline();
+      for ( int i = 0; i < mp.size(); i++ )
+        validatePolyline( errors, i, mp[i] );
+    }
+    break;
+
+    case QGis::WKBPolygon:
+    case QGis::WKBPolygon25D:
+    {
+      validatePolygon( errors, 0, asPolygon() );
+    }
+    break;
+
+    case QGis::WKBMultiPolygon:
+    case QGis::WKBMultiPolygon25D:
+    {
+      QgsMultiPolygon mp = asMultiPolygon();
+      for ( int i = 0; i < mp.size(); i++ )
+        validatePolygon( errors, i, mp[i] );
+    }
+    break;
+
+    case QGis::WKBUnknown:
+      QgsDebugMsg( QObject::tr( "Unknown geometry type" ) );
+      errors << Error( QObject::tr( "Unknown geometry type" ) );
+      break;
+  }
+}

Modified: trunk/qgis/src/core/qgsgeometry.h
===================================================================
--- trunk/qgis/src/core/qgsgeometry.h	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/core/qgsgeometry.h	2010-01-12 18:17:34 UTC (rev 12749)
@@ -362,6 +362,23 @@
      */
     int avoidIntersections();
 
+
+    class Error
+    {
+        QString message;
+        QgsPoint location;
+        bool hasLocation;
+      public:
+        Error( QString m ) : message( m ), hasLocation( false ) {}
+        Error( QString m, QgsPoint p ) : message( m ), location( p ), hasLocation( true ) {}
+
+        QString what() { return message; };
+        QgsPoint where() { return location; }
+        bool hasWhere() { return hasLocation; }
+    };
+
+    void validateGeometry( QList<Error> &errors );
+
   private:
     // Private variables
 
@@ -494,6 +511,9 @@
     /** return polygon from wkb */
     QgsPolygon asPolygon( unsigned char*& ptr, bool hasZValue );
 
+    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
 

Modified: trunk/qgis/src/core/qgspoint.cpp
===================================================================
--- trunk/qgis/src/core/qgspoint.cpp	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/core/qgspoint.cpp	2010-01-12 18:17:34 UTC (rev 12749)
@@ -22,7 +22,96 @@
 #include <QTextStream>
 #include <QObject> // for tr()
 
+#include "qgsexception.h"
 
+//
+// QgsVector
+//
+
+QgsVector::QgsVector() : m_x( 0.0 ), m_y( 0.0 )
+{
+}
+
+QgsVector::QgsVector( double x, double y ) : m_x( x ), m_y( y )
+{
+}
+
+QgsVector QgsVector::operator-( void ) const
+{
+  return QgsVector( -m_x, -m_y );
+}
+
+QgsVector QgsVector::operator*( double scalar ) const
+{
+  return QgsVector( m_x*scalar, m_y*scalar );
+}
+
+QgsVector QgsVector::operator/( double scalar ) const
+{
+  return *this * ( 1.0 / scalar );
+}
+
+double QgsVector::operator*( QgsVector v ) const
+{
+  return m_x*v.m_x + m_y*v.m_y;
+}
+
+double QgsVector::length() const
+{
+  return sqrt( m_x*m_x + m_y*m_y );
+}
+
+double QgsVector::x() const
+{
+  return m_x;
+}
+
+double QgsVector::y() const
+{
+  return m_y;
+}
+
+// perpendicular vector (rotated 90° counter-clockwise)
+QgsVector QgsVector::perpVector() const
+{
+  return QgsVector( -m_y, m_x );
+}
+
+double QgsVector::angle( void ) const
+{
+  double ang = atan2( m_y, m_x );
+  return ang < 0.0 ? ang + 2.0*M_PI : ang;
+}
+
+double QgsVector::angle( QgsVector v ) const
+{
+  return v.angle() - angle();
+}
+
+QgsVector QgsVector::rotateBy( double rot ) const
+{
+  double ang = atan2( m_y, m_x ) + rot;
+  double len = length();
+  return QgsVector( len*cos( ang ), len*sin( ang ) );
+}
+
+QgsVector QgsVector::normal() const
+{
+  double len = length();
+
+  if ( len == 0.0 )
+  {
+    throw QgsException( "normal vector of null vector undefined" );
+  }
+
+  return *this / len;
+}
+
+
+//
+// QgsPoint
+//
+
 QgsPoint::QgsPoint( const QgsPoint& p )
 {
   m_x = p.x();

Modified: trunk/qgis/src/core/qgspoint.h
===================================================================
--- trunk/qgis/src/core/qgspoint.h	2010-01-12 18:15:55 UTC (rev 12748)
+++ trunk/qgis/src/core/qgspoint.h	2010-01-12 18:17:34 UTC (rev 12749)
@@ -20,10 +20,41 @@
 #define QGSPOINT_H
 
 #include <iostream>
-
 #include <QString>
 
 /** \ingroup core
+ * A class to represent a vector.
+ * Currently no Z axis / 2.5D support is implemented.
+ */
+
+class CORE_EXPORT QgsVector
+{
+    double m_x, m_y;
+
+  public:
+    QgsVector();
+    QgsVector( double x, double y );
+
+    QgsVector operator-( void ) const;
+    QgsVector operator*( double scalar ) const;
+    QgsVector operator/( double scalar ) const;
+    double operator*( QgsVector v ) const;
+    double length() const;
+
+    double x() const;
+    double y() const;
+
+    // perpendicular vector (rotated 90° counter-clockwise)
+    QgsVector perpVector() const;
+
+    double angle( void ) const;
+    double angle( QgsVector v ) const;
+    QgsVector rotateBy( double rot ) const;
+    QgsVector normal() const;
+
+};
+
+/** \ingroup core
  * A class to represent a point geometry.
  * Currently no Z axis / 2.5D support is implemented.
  */
@@ -131,6 +162,12 @@
     //! 3 if point is on open ray b.
     int onSegment( const QgsPoint& a, const QgsPoint& b ) const;
 
+    QgsVector operator-( QgsPoint p ) const { return QgsVector( m_x - p.m_x, m_y - p.m_y ); }
+    QgsPoint &operator+=( const QgsVector &v ) { *this = *this + v; return *this; }
+    QgsPoint &operator-=( const QgsVector &v ) { *this = *this - v; return *this; }
+    QgsPoint operator+( const QgsVector &v ) const { return QgsPoint( m_x + v.x(), m_y + v.y() ); }
+    QgsPoint operator-( const QgsVector &v ) const { return QgsPoint( m_x - v.x(), m_y - v.y() ); }
+
   private:
 
     //! x coordinate
@@ -158,6 +195,4 @@
   return os;
 }
 
-
 #endif //QGSPOINT_H
-



More information about the QGIS-commit mailing list