Skip to content

Commit

Permalink
Merge pull request #4691 from nyalldawson/validator
Browse files Browse the repository at this point in the history
Add an explicit argument to set QgsGeometryValidator method
  • Loading branch information
nyalldawson authored Jun 7, 2017
2 parents 5f1a78d + 64a86e3 commit fa9f202
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 122 deletions.
11 changes: 9 additions & 2 deletions python/core/geometry/qgsgeometry.sip
Original file line number Diff line number Diff line change
Expand Up @@ -1077,9 +1077,16 @@ Ring 0 is outer ring and can't be deleted.
%End
};

void validateGeometry( QList<QgsGeometry::Error> &errors /Out/ );
enum ValidationMethod
{
ValidatorQgisInternal,
ValidatorGeos,
};

void validateGeometry( QList<QgsGeometry::Error> &errors /Out/, ValidationMethod method = ValidatorQgisInternal );
%Docstring
Validate geometry and produce a list of geometry errors
Validate geometry and produce a list of geometry errors.
The ``method`` argument dictates which validator to utilize.
.. versionadded:: 1.5
.. note::

Expand Down
9 changes: 5 additions & 4 deletions python/core/qgsgeometryvalidator.sip
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ class QgsGeometryValidator : QThread
#include "qgsgeometryvalidator.h"
%End
public:
QgsGeometryValidator( const QgsGeometry *g, QList<QgsGeometry::Error> *errors = 0 );

QgsGeometryValidator( const QgsGeometry *g, QList<QgsGeometry::Error> *errors = 0, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal );
%Docstring
Constructor
Constructor for QgsGeometryValidator.

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Jun 7, 2017

Member

Surprising 😲

%End
~QgsGeometryValidator();

virtual void run();

void stop();

static void validateGeometry( const QgsGeometry *g, QList<QgsGeometry::Error> &errors /Out/ );
static void validateGeometry( const QgsGeometry *g, QList<QgsGeometry::Error> &errors /Out/, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal );
%Docstring
Validate geometry and produce a list of geometry errors
%End
Expand All @@ -36,7 +37,7 @@ Validate geometry and produce a list of geometry errors
public slots:
void addError( const QgsGeometry::Error & );

}; // class QgsGeometryValidator
};

/************************************************************************
* This file has been generated automatically from *
Expand Down
7 changes: 6 additions & 1 deletion src/app/nodetool/qgsnodetool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1814,7 +1814,12 @@ void QgsNodeTool::GeometryValidation::start( QgsGeometry &geom, QgsNodeTool *t,
{
tool = t;
layer = l;
validator = new QgsGeometryValidator( &geom );
QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal;
QgsSettings settings;
if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
method = QgsGeometry::ValidatorGeos;

validator = new QgsGeometryValidator( &geom, nullptr, method );
connect( validator, &QgsGeometryValidator::errorFound, tool, &QgsNodeTool::validationErrorFound );
connect( validator, &QThread::finished, tool, &QgsNodeTool::validationFinished );
validator->start();
Expand Down
5 changes: 4 additions & 1 deletion src/app/nodetool/qgsselectedfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ void QgsSelectedFeature::validateGeometry( QgsGeometry *g )
delete vm;
}

mValidator = new QgsGeometryValidator( g );
QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal;
if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
method = QgsGeometry::ValidatorGeos;
mValidator = new QgsGeometryValidator( g, nullptr, method );
connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsSelectedFeature::addError );
connect( mValidator, &QThread::finished, this, &QgsSelectedFeature::validationFinished );
mValidator->start();
Expand Down
4 changes: 2 additions & 2 deletions src/core/geometry/qgsgeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1964,9 +1964,9 @@ QgsGeometry QgsGeometry::makeValid()
}


void QgsGeometry::validateGeometry( QList<QgsGeometry::Error> &errors )
void QgsGeometry::validateGeometry( QList<QgsGeometry::Error> &errors, ValidationMethod method )
{
QgsGeometryValidator::validateGeometry( this, errors );
QgsGeometryValidator::validateGeometry( this, errors, method );
}

bool QgsGeometry::isGeosValid() const
Expand Down
15 changes: 13 additions & 2 deletions src/core/geometry/qgsgeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,11 +946,22 @@ class CORE_EXPORT QgsGeometry
bool hasWhere() { return hasLocation; }
};

/** Validate geometry and produce a list of geometry errors
/**
* Available methods for validating geometries.
* \since QGIS 3.0
*/
enum ValidationMethod
{
ValidatorQgisInternal, //!< Use internal QgsGeometryValidator method
ValidatorGeos, //!< Use GEOS validation methods
};

/** Validate geometry and produce a list of geometry errors.
* The \a method argument dictates which validator to utilize.
* \since QGIS 1.5
* \note Available in Python bindings since QGIS 1.6
**/
void validateGeometry( QList<QgsGeometry::Error> &errors SIP_OUT );
void validateGeometry( QList<QgsGeometry::Error> &errors SIP_OUT, ValidationMethod method = ValidatorQgisInternal );

/** Compute the unary union on a list of \a geometries. May be faster than an iterative union on a set of geometries.
* The returned geometry will be fully noded, i.e. a node will be created at every common intersection of the
Expand Down
216 changes: 111 additions & 105 deletions src/core/qgsgeometryvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ email : jef at norbit dot de
#include "qgsgeometryvalidator.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
#include "qgssettings.h"

QgsGeometryValidator::QgsGeometryValidator( const QgsGeometry *g, QList<QgsGeometry::Error> *errors )
QgsGeometryValidator::QgsGeometryValidator( const QgsGeometry *g, QList<QgsGeometry::Error> *errors, QgsGeometry::ValidationMethod method )
: QThread()
, mErrors( errors )
, mStop( false )
, mErrorCount( 0 )
, mMethod( method )
{
Q_ASSERT( g );
if ( g )
Expand Down Expand Up @@ -215,134 +215,140 @@ void QgsGeometryValidator::validatePolygon( int idx, const QgsPolygon &polygon )
void QgsGeometryValidator::run()
{
mErrorCount = 0;
QgsSettings settings;
if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
switch ( mMethod )
{
char *r = nullptr;
GEOSGeometry *g0 = mG.exportToGeos();
GEOSContextHandle_t handle = QgsGeometry::getGEOSHandler();
if ( !g0 )
case QgsGeometry::ValidatorGeos:
{
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:could not produce geometry for GEOS (check log window)" ) ) );
}
else
{
GEOSGeometry *g1 = nullptr;
char res = GEOSisValidDetail_r( handle, g0, GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE, &r, &g1 );
GEOSGeom_destroy_r( handle, g0 );
if ( res != 1 )
char *r = nullptr;
GEOSGeometry *g0 = mG.exportToGeos();
GEOSContextHandle_t handle = QgsGeometry::getGEOSHandler();
if ( !g0 )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:could not produce geometry for GEOS (check log window)" ) ) );
}
else
{
if ( g1 )
GEOSGeometry *g1 = nullptr;
char res = GEOSisValidDetail_r( handle, g0, GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE, &r, &g1 );
GEOSGeom_destroy_r( handle, g0 );
if ( res != 1 )
{
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( handle, g1 );

unsigned int n;
if ( GEOSCoordSeq_getSize_r( handle, cs, &n ) && n == 1 )
if ( g1 )
{
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( handle, g1 );

unsigned int n;
if ( GEOSCoordSeq_getSize_r( handle, cs, &n ) && n == 1 )
{
double x, y;
GEOSCoordSeq_getX_r( handle, cs, 0, &x );
GEOSCoordSeq_getY_r( handle, cs, 0, &y );
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ), QgsPointXY( x, y ) ) );
mErrorCount++;
}

GEOSGeom_destroy_r( handle, g1 );
}
else
{
double x, y;
GEOSCoordSeq_getX_r( handle, cs, 0, &x );
GEOSCoordSeq_getY_r( handle, cs, 0, &y );
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ), QgsPointXY( x, y ) ) );
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ) ) );
mErrorCount++;
}

GEOSGeom_destroy_r( handle, g1 );
}
else
{
emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ) ) );
mErrorCount++;
GEOSFree_r( handle, r );
}

GEOSFree_r( handle, r );
}
}

return;
}

QgsDebugMsg( "validation thread started." );

QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( mG.wkbType() );
//if ( flatType == QgsWkbTypes::Point || flatType == QgsWkbTypes::MultiPoint )
// break;
if ( flatType == QgsWkbTypes::LineString )
{
validatePolyline( 0, mG.asPolyline() );
}
else if ( flatType == QgsWkbTypes::MultiLineString )
{
QgsMultiPolyline mp = mG.asMultiPolyline();
for ( int i = 0; !mStop && i < mp.size(); i++ )
validatePolyline( i, mp[i] );
}
else if ( flatType == QgsWkbTypes::Polygon )
{
validatePolygon( 0, mG.asPolygon() );
}
else if ( flatType == QgsWkbTypes::MultiPolygon )
{
QgsMultiPolygon mp = mG.asMultiPolygon();
for ( int i = 0; !mStop && i < mp.size(); i++ )
{
validatePolygon( i, mp[i] );
break;
}

for ( int i = 0; !mStop && i < mp.size(); i++ )
case QgsGeometry::ValidatorQgisInternal:
{
if ( mp[i].isEmpty() )
QgsDebugMsg( "validation thread started." );

QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( mG.wkbType() );
//if ( flatType == QgsWkbTypes::Point || flatType == QgsWkbTypes::MultiPoint )
// break;
if ( flatType == QgsWkbTypes::LineString )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 has no rings" ).arg( i ) ) );
mErrorCount++;
continue;
validatePolyline( 0, mG.asPolyline() );
}

for ( int j = i + 1; !mStop && j < mp.size(); j++ )
else if ( flatType == QgsWkbTypes::MultiLineString )
{
if ( mp[j].isEmpty() )
continue;

if ( ringInRing( mp[i][0], mp[j][0] ) )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 inside polygon %2" ).arg( i ).arg( j ) ) );
mErrorCount++;
}
else if ( ringInRing( mp[j][0], mp[i][0] ) )
QgsMultiPolyline mp = mG.asMultiPolyline();
for ( int i = 0; !mStop && i < mp.size(); i++ )
validatePolyline( i, mp[i] );
}
else if ( flatType == QgsWkbTypes::Polygon )
{
validatePolygon( 0, mG.asPolygon() );
}
else if ( flatType == QgsWkbTypes::MultiPolygon )
{
QgsMultiPolygon mp = mG.asMultiPolygon();
for ( int i = 0; !mStop && i < mp.size(); i++ )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 inside polygon %2" ).arg( j ).arg( i ) ) );
mErrorCount++;
validatePolygon( i, mp[i] );
}
else

for ( int i = 0; !mStop && i < mp.size(); i++ )
{
checkRingIntersections( i, 0, mp[i][0], j, 0, mp[j][0] );
if ( mp[i].isEmpty() )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 has no rings" ).arg( i ) ) );
mErrorCount++;
continue;
}

for ( int j = i + 1; !mStop && j < mp.size(); j++ )
{
if ( mp[j].isEmpty() )
continue;

if ( ringInRing( mp[i][0], mp[j][0] ) )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 inside polygon %2" ).arg( i ).arg( j ) ) );
mErrorCount++;
}
else if ( ringInRing( mp[j][0], mp[i][0] ) )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "polygon %1 inside polygon %2" ).arg( j ).arg( i ) ) );
mErrorCount++;
}
else
{
checkRingIntersections( i, 0, mp[i][0], j, 0, mp[j][0] );
}
}
}
}
}
}

else if ( flatType == QgsWkbTypes::Unknown )
{
QgsDebugMsg( QObject::tr( "Unknown geometry type" ) );
emit errorFound( QgsGeometry::Error( QObject::tr( "Unknown geometry type %1" ).arg( mG.wkbType() ) ) );
mErrorCount++;
}
else if ( flatType == QgsWkbTypes::Unknown )
{
QgsDebugMsg( QObject::tr( "Unknown geometry type" ) );
emit errorFound( QgsGeometry::Error( QObject::tr( "Unknown geometry type %1" ).arg( mG.wkbType() ) ) );
mErrorCount++;
}

QgsDebugMsg( "validation finished." );
QgsDebugMsg( "validation finished." );

if ( mStop )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry validation was aborted." ) ) );
}
else if ( mErrorCount > 0 )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry has %1 errors." ).arg( mErrorCount ) ) );
}
if ( mStop )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry validation was aborted." ) ) );
}
else if ( mErrorCount > 0 )
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry has %1 errors." ).arg( mErrorCount ) ) );
}
#if 0
else
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry is valid." ) ) );
}
else
{
emit errorFound( QgsGeometry::Error( QObject::tr( "Geometry is valid." ) ) );
}
#endif
break;
}
}
}

void QgsGeometryValidator::addError( const QgsGeometry::Error &e )
Expand All @@ -351,9 +357,9 @@ void QgsGeometryValidator::addError( const QgsGeometry::Error &e )
*mErrors << e;
}

void QgsGeometryValidator::validateGeometry( const QgsGeometry *g, QList<QgsGeometry::Error> &errors )
void QgsGeometryValidator::validateGeometry( const QgsGeometry *g, QList<QgsGeometry::Error> &errors, QgsGeometry::ValidationMethod method )
{
QgsGeometryValidator *gv = new QgsGeometryValidator( g, &errors );
QgsGeometryValidator *gv = new QgsGeometryValidator( g, &errors, method );
connect( gv, &QgsGeometryValidator::errorFound, gv, &QgsGeometryValidator::addError );
gv->run();
gv->wait();
Expand Down
Loading

0 comments on commit fa9f202

Please sign in to comment.