mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
[FEATURE] allow editing of invalid geometry in node tool
git-svn-id: http://svn.osgeo.org/qgis/trunk@12749 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
parent
41654d59ff
commit
59b0d23cdd
@ -24,6 +24,10 @@
|
||||
#include <math.h>
|
||||
#include <QMouseEvent>
|
||||
#include <QMessageBox>
|
||||
#include "qgslogger.h"
|
||||
#include "qgisapp.h"
|
||||
|
||||
#include <QStatusBar>
|
||||
|
||||
|
||||
QgsRubberBand* QgsMapToolNodeTool::createRubberBandMarker( QgsPoint center, QgsVectorLayer* vlayer )
|
||||
@ -128,7 +132,7 @@ void QgsMapToolNodeTool::layerModified( bool onlyGeometry )
|
||||
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 @@ void QgsMapToolNodeTool::createMovingRubberBands()
|
||||
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 @@ void QgsMapToolNodeTool::removeRubberBands()
|
||||
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,8 +940,46 @@ void SelectionFeature::setSelectedFeature( int featureId, QgsVectorLayer* vlaye
|
||||
{
|
||||
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()
|
||||
@ -983,7 +1025,10 @@ void SelectionFeature::deleteSelectedVertexes()
|
||||
}
|
||||
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 @@ void SelectionFeature::deleteSelectedVertexes()
|
||||
QMessageBox::Ok );
|
||||
}
|
||||
|
||||
if ( count != 0 && GEOSisValid( f.geometry()->asGeos() ) )
|
||||
if ( count != 0 && ( !wasValid || isValid ) )
|
||||
{
|
||||
mVlayer->endEditCommand();
|
||||
}
|
||||
@ -1054,7 +1099,9 @@ void SelectionFeature::moveSelectedVertexes( double changeX, double changeY )
|
||||
}
|
||||
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 @@ void SelectionFeature::moveSelectedVertexes( double changeX, double changeY )
|
||||
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 @@ void SelectionFeature::deleteVertexMap()
|
||||
VertexEntry entry = mVertexMap.takeLast();
|
||||
delete entry.vertexMarker;
|
||||
}
|
||||
|
||||
while ( !mGeomErrorMarkers.isEmpty() )
|
||||
{
|
||||
delete mGeomErrorMarkers.takeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectionFeature::isSelected( int vertexNr )
|
||||
@ -1134,6 +1187,7 @@ void SelectionFeature::createVertexMapPolygon()
|
||||
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 @@ void SelectionFeature::createVertexMapPolygon()
|
||||
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 @@ void SelectionFeature::createVertexMapLine()
|
||||
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 @@ void SelectionFeature::createVertexMapLine()
|
||||
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 @@ void SelectionFeature::createVertexMapPoint()
|
||||
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 @@ void SelectionFeature::createVertexMapPoint()
|
||||
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 @@ QgsVectorLayer* SelectionFeature::vlayer()
|
||||
{
|
||||
return mVlayer;
|
||||
}
|
||||
|
||||
|
||||
@ -161,7 +161,7 @@ class SelectionFeature: public QObject
|
||||
* @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 @@ class SelectionFeature: public QObject
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Deletes whole vertex map.
|
||||
*/
|
||||
@ -198,10 +197,15 @@ class SelectionFeature: public QObject
|
||||
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 @@ class SelectionFeature: public QObject
|
||||
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 @@ class QgsMapToolNodeTool: public QgsMapToolVertexEdit
|
||||
|
||||
/** flag to tell if edition points */
|
||||
bool mIsPoint;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -2360,7 +2360,7 @@ double QgsGeometry::closestVertexWithContext( const QgsPoint& point, int& atVert
|
||||
return -1;
|
||||
|
||||
const GEOSGeometry *g = GEOSGetExteriorRing( mGeos );
|
||||
if ( g == NULL )
|
||||
if ( !g )
|
||||
return -1;
|
||||
|
||||
const GEOSCoordSequence *sequence = GEOSGeom_getCoordSeq( g );
|
||||
@ -5442,7 +5442,7 @@ int QgsGeometry::topologicalTestPointsSplit( const GEOSGeometry* splitLine, QLis
|
||||
|
||||
testPoints.clear();
|
||||
GEOSGeometry* intersectionGeom = GEOSIntersection( mGeos, splitLine );
|
||||
if ( intersectionGeom == NULL )
|
||||
if ( !intersectionGeom )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -5853,17 +5853,17 @@ QgsMultiPolygon QgsGeometry::asMultiPolygon()
|
||||
|
||||
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 @@ double QgsGeometry::distance( QgsGeometry& geom )
|
||||
|
||||
QgsGeometry* QgsGeometry::buffer( double distance, int segments )
|
||||
{
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
{
|
||||
exportWkbToGeos();
|
||||
}
|
||||
@ -5898,7 +5898,7 @@ QgsGeometry* QgsGeometry::buffer( double distance, int segments )
|
||||
|
||||
QgsGeometry* QgsGeometry::simplify( double tolerance )
|
||||
{
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
{
|
||||
exportWkbToGeos();
|
||||
}
|
||||
@ -5915,7 +5915,7 @@ QgsGeometry* QgsGeometry::simplify( double tolerance )
|
||||
|
||||
QgsGeometry* QgsGeometry::centroid()
|
||||
{
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
{
|
||||
exportWkbToGeos();
|
||||
}
|
||||
@ -5932,7 +5932,7 @@ QgsGeometry* QgsGeometry::centroid()
|
||||
|
||||
QgsGeometry* QgsGeometry::convexHull()
|
||||
{
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
{
|
||||
exportWkbToGeos();
|
||||
}
|
||||
@ -5950,15 +5950,15 @@ QgsGeometry* QgsGeometry::convexHull()
|
||||
|
||||
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::intersection( QgsGeometry* geometry )
|
||||
|
||||
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::combine( QgsGeometry* geometry )
|
||||
|
||||
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::difference( QgsGeometry* geometry )
|
||||
|
||||
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 @@ QgsGeometry* QgsGeometry::symDifference( QgsGeometry* geometry )
|
||||
|
||||
QList<QgsGeometry*> QgsGeometry::asGeometryCollection()
|
||||
{
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
{
|
||||
exportWkbToGeos();
|
||||
if ( mGeos == NULL )
|
||||
if ( !mGeos )
|
||||
return QList<QgsGeometry*>();
|
||||
}
|
||||
|
||||
@ -6258,3 +6258,228 @@ int QgsGeometry::avoidIntersections()
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,6 +362,23 @@ class CORE_EXPORT QgsGeometry
|
||||
*/
|
||||
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 @@ class CORE_EXPORT QgsGeometry
|
||||
/** 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
|
||||
|
||||
|
||||
@ -22,6 +22,95 @@
|
||||
#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 )
|
||||
{
|
||||
|
||||
@ -20,9 +20,40 @@
|
||||
#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 @@ class CORE_EXPORT QgsPoint
|
||||
//! 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 @@ inline std::ostream& operator << ( std::ostream& os, const QgsPoint &p )
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
#endif //QGSPOINT_H
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user