Skip to content

Commit

Permalink
Add an explicit argument to set QgsGeometryValidator method
Browse files Browse the repository at this point in the history
Previously this was always read from settings when using
QgsGeometryValidator. It's now an explicit argument
when constructing QgsGeometryValidator or calling the
static validation methods, allowing choice of internal/GEOS
validation methods.

Helps remove more QgsSettings use from core.
  • Loading branch information
nyalldawson committed Jun 7, 2017
1 parent 5f1a78d commit 64a86e3
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.
%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 64a86e3

Please sign in to comment.