Skip to content

Commit 75697d7

Browse files
committed
Cache validity check results
For non-point geometry subclasses (points are always valid!) we now cache the results of a geometry validity check. Subsequent checks utilise the cached result wherever possible. Because QgsGeometry/QgsFeature objects are implicitly shared, this means that we avoid a *lot* of duplicate validity checks as features and geometries are thrown around during processing model execution.
1 parent 16c114b commit 75697d7

19 files changed

Lines changed: 149 additions & 25 deletions

‎python/core/auto_generated/geometry/qgsabstractgeometry.sip.in‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,20 @@ Converts the geometry to a specified type.
607607
.. versionadded:: 2.14
608608
%End
609609

610+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const = 0;
611+
%Docstring
612+
Checks validity of the geometry, and returns ``True`` if the geometry is valid.
613+
614+
:param flags: indicates optional flags which control the type of validity checking performed
615+
(corresponding to QgsGeometry.ValidityFlags).
616+
617+
:return: - ``True`` if geometry is valid
618+
- error: will be set to the validity error message
619+
620+
621+
.. versionadded:: 3.8
622+
%End
623+
610624

611625
QgsGeometryPartIterator parts();
612626
%Docstring

‎python/core/auto_generated/geometry/qgscurve.sip.in‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ Returns a geometry without curves. Caller takes ownership
172172

173173
virtual QgsRectangle boundingBox() const;
174174

175+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const;
176+
175177

176178
virtual double xAt( int index ) const = 0;
177179
%Docstring

‎python/core/auto_generated/geometry/qgsgeometrycollection.sip.in‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ Returns a geometry without curves. Caller takes ownership
206206

207207
virtual QgsPoint vertexAt( QgsVertexId id ) const;
208208

209+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const;
210+
209211

210212
virtual bool addZValue( double zValue = 0 );
211213

‎python/core/auto_generated/geometry/qgsmultipoint.sip.in‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ Multi point geometry collection.
5050

5151
virtual double segmentLength( QgsVertexId startVertex ) const;
5252

53+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const;
54+
5355

5456

5557
virtual QgsMultiPoint *createEmptyWithSameType() const /Factory/;

‎python/core/auto_generated/geometry/qgspoint.sip.in‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ M value is preserved.
371371

372372
virtual QgsAbstractGeometry *boundary() const /Factory/;
373373

374+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const;
375+
374376

375377
virtual bool insertVertex( QgsVertexId position, const QgsPoint &vertex );
376378

‎python/core/auto_generated/geometry/qgssurface.sip.in‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ Ownership is transferred to the caller.
2525
%End
2626

2727
virtual QgsRectangle boundingBox() const;
28-
%Docstring
29-
Returns the minimal bounding box for the geometry
30-
%End
28+
29+
virtual bool isValid( QString &error /Out/, int flags = 0 ) const;
30+
31+
3132

3233
protected:
3334

3435
virtual void clearCache() const;
3536

37+
3638
};
3739

3840
/************************************************************************

‎src/core/geometry/qgsabstractgeometry.h‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,19 @@ class CORE_EXPORT QgsAbstractGeometry
577577
*/
578578
virtual bool convertTo( QgsWkbTypes::Type type );
579579

580+
/**
581+
* Checks validity of the geometry, and returns TRUE if the geometry is valid.
582+
*
583+
* \param error will be set to the validity error message
584+
* \param flags indicates optional flags which control the type of validity checking performed
585+
* (corresponding to QgsGeometry::ValidityFlags).
586+
*
587+
* \returns TRUE if geometry is valid
588+
*
589+
* \since QGIS 3.8
590+
*/
591+
virtual bool isValid( QString &error SIP_OUT, int flags = 0 ) const = 0;
592+
580593
#ifndef SIP_RUN
581594

582595
/**

‎src/core/geometry/qgscurve.cpp‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "qgslinestring.h"
2222
#include "qgspoint.h"
2323
#include "qgsmultipoint.h"
24+
#include "qgsgeos.h"
2425

2526
bool QgsCurve::operator==( const QgsAbstractGeometry &other ) const
2627
{
@@ -188,6 +189,25 @@ QgsRectangle QgsCurve::boundingBox() const
188189
return mBoundingBox;
189190
}
190191

192+
bool QgsCurve::isValid( QString &error, int flags ) const
193+
{
194+
if ( flags == 0 && mHasCachedValidity )
195+
{
196+
// use cached validity results
197+
error = mValidityFailureReason;
198+
return error.isEmpty();
199+
}
200+
201+
QgsGeos geos( this );
202+
bool res = geos.isValid( &error, flags & QgsGeometry::FlagAllowSelfTouchingHoles, nullptr );
203+
if ( flags == 0 )
204+
{
205+
mValidityFailureReason = !res ? error : QString();
206+
mHasCachedValidity = true;
207+
}
208+
return res;
209+
}
210+
191211
QPolygonF QgsCurve::asQPolygonF() const
192212
{
193213
const int nb = numPoints();
@@ -224,6 +244,8 @@ QgsCurve::Orientation QgsCurve::orientation() const
224244
void QgsCurve::clearCache() const
225245
{
226246
mBoundingBox = QgsRectangle();
247+
mHasCachedValidity = false;
248+
mValidityFailureReason.clear();
227249
QgsAbstractGeometry::clearCache();
228250
}
229251

‎src/core/geometry/qgscurve.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry
162162
QgsCurve *toCurveType() const override SIP_FACTORY;
163163

164164
QgsRectangle boundingBox() const override;
165+
bool isValid( QString &error SIP_OUT, int flags = 0 ) const override;
165166

166167
/**
167168
* Returns the x-coordinate of the specified node in the line string.
@@ -290,6 +291,9 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry
290291
private:
291292

292293
mutable QgsRectangle mBoundingBox;
294+
295+
mutable bool mHasCachedValidity = false;
296+
mutable QString mValidityFailureReason;
293297
};
294298

295299
#endif // QGSCURVE_H

‎src/core/geometry/qgsgeometry.cpp‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,15 +2504,7 @@ bool QgsGeometry::isGeosValid( const QgsGeometry::ValidityFlags flags ) const
25042504
return false;
25052505
}
25062506

2507-
// avoid calling geos for trivial point geometries
2508-
if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == QgsWkbTypes::PointGeometry )
2509-
{
2510-
return true;
2511-
}
2512-
2513-
QgsGeos geos( d->geometry.get() );
2514-
mLastError.clear();
2515-
return geos.isValid( &mLastError, flags & FlagAllowSelfTouchingHoles, nullptr );
2507+
return d->geometry->isValid( mLastError, static_cast< int >( flags ) );
25162508
}
25172509

25182510
bool QgsGeometry::isSimple() const

0 commit comments

Comments
 (0)