diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index cf1af95d20a..fca0129237e 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -1077,9 +1077,16 @@ Ring 0 is outer ring and can't be deleted. %End }; - void validateGeometry( QList &errors /Out/ ); + enum ValidationMethod + { + ValidatorQgisInternal, + ValidatorGeos, + }; + + void validateGeometry( QList &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:: diff --git a/python/core/qgsgeometryvalidator.sip b/python/core/qgsgeometryvalidator.sip index 6e18d55a17c..0f97afdac36 100644 --- a/python/core/qgsgeometryvalidator.sip +++ b/python/core/qgsgeometryvalidator.sip @@ -15,9 +15,10 @@ class QgsGeometryValidator : QThread #include "qgsgeometryvalidator.h" %End public: - QgsGeometryValidator( const QgsGeometry *g, QList *errors = 0 ); + + QgsGeometryValidator( const QgsGeometry *g, QList *errors = 0, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal ); %Docstring -Constructor + Constructor for QgsGeometryValidator. %End ~QgsGeometryValidator(); @@ -25,7 +26,7 @@ Constructor void stop(); - static void validateGeometry( const QgsGeometry *g, QList &errors /Out/ ); + static void validateGeometry( const QgsGeometry *g, QList &errors /Out/, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal ); %Docstring Validate geometry and produce a list of geometry errors %End @@ -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 * diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 69669a77890..e7b5790036e 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -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(); diff --git a/src/app/nodetool/qgsselectedfeature.cpp b/src/app/nodetool/qgsselectedfeature.cpp index a509fb33c20..ebc92530b73 100644 --- a/src/app/nodetool/qgsselectedfeature.cpp +++ b/src/app/nodetool/qgsselectedfeature.cpp @@ -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(); diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 81c7d24319c..38e975e3a24 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -1964,9 +1964,9 @@ QgsGeometry QgsGeometry::makeValid() } -void QgsGeometry::validateGeometry( QList &errors ) +void QgsGeometry::validateGeometry( QList &errors, ValidationMethod method ) { - QgsGeometryValidator::validateGeometry( this, errors ); + QgsGeometryValidator::validateGeometry( this, errors, method ); } bool QgsGeometry::isGeosValid() const diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 9aa21baeb1f..e130f77e2ea 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -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 &errors SIP_OUT ); + void validateGeometry( QList &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 diff --git a/src/core/qgsgeometryvalidator.cpp b/src/core/qgsgeometryvalidator.cpp index 755ff7bdf93..7b978eb80be 100644 --- a/src/core/qgsgeometryvalidator.cpp +++ b/src/core/qgsgeometryvalidator.cpp @@ -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 *errors ) +QgsGeometryValidator::QgsGeometryValidator( const QgsGeometry *g, QList *errors, QgsGeometry::ValidationMethod method ) : QThread() , mErrors( errors ) , mStop( false ) , mErrorCount( 0 ) + , mMethod( method ) { Q_ASSERT( g ); if ( g ) @@ -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 ) { - if ( g1 ) + 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 ) { - const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( handle, g1 ); - - unsigned int n; - if ( GEOSCoordSeq_getSize_r( handle, cs, &n ) && n == 1 ) + if ( g1 ) { - 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 ) ) ); + 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 + { + emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ) ) ); mErrorCount++; } - GEOSGeom_destroy_r( handle, g1 ); + GEOSFree_r( handle, r ); } - else - { - emit errorFound( QgsGeometry::Error( QObject::tr( "GEOS error:%1" ).arg( r ) ) ); - mErrorCount++; - } - - GEOSFree_r( handle, r ); } + + break; } - 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++ ) + case QgsGeometry::ValidatorQgisInternal: { - validatePolygon( i, mp[i] ); - } + QgsDebugMsg( "validation thread started." ); - for ( int i = 0; !mStop && i < mp.size(); i++ ) - { - if ( mp[i].isEmpty() ) + 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 ) ) ); + 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] ); + } + + for ( int i = 0; !mStop && i < mp.size(); i++ ) + { + 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++; - continue; } - for ( int j = i + 1; !mStop && j < mp.size(); j++ ) + QgsDebugMsg( "validation finished." ); + + if ( mStop ) { - 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] ); - } + 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." ) ) ); + } +#endif + break; } } - - 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." ); - - 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." ) ) ); - } -#endif } void QgsGeometryValidator::addError( const QgsGeometry::Error &e ) @@ -351,9 +357,9 @@ void QgsGeometryValidator::addError( const QgsGeometry::Error &e ) *mErrors << e; } -void QgsGeometryValidator::validateGeometry( const QgsGeometry *g, QList &errors ) +void QgsGeometryValidator::validateGeometry( const QgsGeometry *g, QList &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(); diff --git a/src/core/qgsgeometryvalidator.h b/src/core/qgsgeometryvalidator.h index a7d64fcd76c..6d08e808d59 100644 --- a/src/core/qgsgeometryvalidator.h +++ b/src/core/qgsgeometryvalidator.h @@ -29,15 +29,18 @@ class CORE_EXPORT QgsGeometryValidator : public QThread Q_OBJECT public: - //! Constructor - QgsGeometryValidator( const QgsGeometry *g, QList *errors = nullptr ); + + /** + * Constructor for QgsGeometryValidator. + */ + QgsGeometryValidator( const QgsGeometry *g, QList *errors = nullptr, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal ); ~QgsGeometryValidator(); void run() override; void stop(); //! Validate geometry and produce a list of geometry errors - static void validateGeometry( const QgsGeometry *g, QList &errors SIP_OUT ); + static void validateGeometry( const QgsGeometry *g, QList &errors SIP_OUT, QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal ); signals: void errorFound( const QgsGeometry::Error & ); @@ -58,6 +61,7 @@ class CORE_EXPORT QgsGeometryValidator : public QThread QList *mErrors; bool mStop; int mErrorCount; -}; // class QgsGeometryValidator + QgsGeometry::ValidationMethod mMethod = QgsGeometry::ValidatorQgisInternal; +}; #endif diff --git a/src/gui/qgsmaptoolcapture.cpp b/src/gui/qgsmaptoolcapture.cpp index 02edbe2cd6b..4322b06a5ff 100644 --- a/src/gui/qgsmaptoolcapture.cpp +++ b/src/gui/qgsmaptoolcapture.cpp @@ -663,7 +663,10 @@ void QgsMapToolCapture::validateGeometry() if ( !g ) return; - mValidator = new QgsGeometryValidator( g.get() ); + QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal; + if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 ) + method = QgsGeometry::ValidatorGeos; + mValidator = new QgsGeometryValidator( g.get(), nullptr, method ); connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError ); connect( mValidator, &QThread::finished, this, &QgsMapToolCapture::validationFinished ); mValidator->start();