Skip to content

Commit ed86d12

Browse files
committedSep 17, 2018
Add QgsSingleGeometryCheck
This class is a new subclass of QgsGeometryCheck that implements an interface that works on isolated geometries. It has no dependency on a context, apart from a configuration that can be passed in. This makes it suitable for checks that only work on in-memory information like self-intersection and other isValid checks. For reference the classes QgsGeometrySelfIntersectionCheck, QgsGeometryMultipartCheck, QgsGeometryTypeCheck and QgsGeometrySelfContactCheck have been ported to this interface.
1 parent 39e1f89 commit ed86d12

12 files changed

+413
-162
lines changed
 

‎src/analysis/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ SET(QGIS_ANALYSIS_SRCS
139139
vector/geometry_checker/qgsgeometryanglecheck.cpp
140140
vector/geometry_checker/qgsgeometryareacheck.cpp
141141
vector/geometry_checker/qgsgeometrycheck.cpp
142+
vector/geometry_checker/qgssinglegeometrycheck.cpp
142143
vector/geometry_checker/qgsgeometrychecker.cpp
143144
vector/geometry_checker/qgsgeometrycheckerutils.cpp
144145
vector/geometry_checker/qgsgeometrycontainedcheck.cpp
@@ -268,6 +269,7 @@ SET(QGIS_ANALYSIS_HDRS
268269
vector/geometry_checker/qgsgeometryareacheck.h
269270
vector/geometry_checker/qgsgeometrychecker.h
270271
vector/geometry_checker/qgsgeometrycheck.h
272+
vector/geometry_checker/qgssinglegeometrycheck.h
271273
vector/geometry_checker/qgsgeometrycontainedcheck.h
272274
vector/geometry_checker/qgsgeometrydanglecheck.h
273275
vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h

‎src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,18 @@
1616
#include "qgsgeometrymultipartcheck.h"
1717
#include "qgsfeaturepool.h"
1818

19-
void QgsGeometryMultipartCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
19+
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
2020
{
21-
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
22-
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
23-
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
21+
Q_UNUSED( configuration )
22+
QList<QgsSingleGeometryCheckError *> errors;
23+
24+
const QgsAbstractGeometry *geom = geometry.constGet();
25+
QgsWkbTypes::Type type = geom->wkbType();
26+
if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) )
2427
{
25-
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
26-
QgsWkbTypes::Type type = geom->wkbType();
27-
if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) )
28-
{
29-
errors.append( new QgsGeometryCheckError( this, layerFeature, geom->centroid() ) );
30-
}
28+
errors.append( new QgsSingleGeometryCheckError( this, geometry, geom->centroid() ) );
3129
}
30+
return errors;
3231
}
3332

3433
void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

‎src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
#ifndef QGS_GEOMETRY_MULTIPART_CHECK_H
1919
#define QGS_GEOMETRY_MULTIPART_CHECK_H
2020

21-
#include "qgsgeometrycheck.h"
21+
#include "qgssinglegeometrycheck.h"
2222

23-
class ANALYSIS_EXPORT QgsGeometryMultipartCheck : public QgsGeometryCheck
23+
class ANALYSIS_EXPORT QgsGeometryMultipartCheck : public QgsSingleGeometryCheck
2424
{
2525
public:
2626
explicit QgsGeometryMultipartCheck( QgsGeometryCheckerContext *context )
27-
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
28-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
27+
: QgsSingleGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
28+
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
2929
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
3030
QStringList resolutionMethods() const override;
3131
QString errorDescription() const override { return tr( "Multipart object with only one feature" ); }

‎src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.cpp

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,67 @@
1717
#include "qgsgeometryutils.h"
1818
#include "qgsfeaturepool.h"
1919

20-
void QgsGeometrySelfContactCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
20+
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
2121
{
22-
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
23-
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
24-
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
22+
QList<QgsSingleGeometryCheckError *> errors;
23+
const QgsAbstractGeometry *geom = geometry.constGet();
24+
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
2525
{
26-
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
27-
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
26+
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
2827
{
29-
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
30-
{
31-
// Test for self-contacts
32-
int n = geom->vertexCount( iPart, iRing );
33-
bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );
28+
// Test for self-contacts
29+
int n = geom->vertexCount( iPart, iRing );
30+
bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );
3431

35-
// Geometry ring without duplicate nodes
36-
QVector<int> vtxMap;
37-
QVector<QgsPoint> ring;
38-
vtxMap.append( 0 );
39-
ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
40-
for ( int i = 1; i < n; ++i )
41-
{
42-
QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
43-
if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > mContext->tolerance * mContext->tolerance )
44-
{
45-
vtxMap.append( i );
46-
ring.append( p );
47-
}
48-
}
49-
while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < mContext->tolerance * mContext->tolerance )
50-
{
51-
vtxMap.pop_back();
52-
ring.pop_back();
53-
}
54-
if ( isClosed )
32+
// Geometry ring without duplicate nodes
33+
QVector<int> vtxMap;
34+
QVector<QgsPoint> ring;
35+
vtxMap.append( 0 );
36+
ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
37+
for ( int i = 1; i < n; ++i )
38+
{
39+
QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
40+
if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > mContext->tolerance * mContext->tolerance )
5541
{
56-
vtxMap.append( n - 1 );
57-
ring.append( ring.front() );
42+
vtxMap.append( i );
43+
ring.append( p );
5844
}
59-
n = ring.size();
45+
}
46+
while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < mContext->tolerance * mContext->tolerance )
47+
{
48+
vtxMap.pop_back();
49+
ring.pop_back();
50+
}
51+
if ( isClosed )
52+
{
53+
vtxMap.append( n - 1 );
54+
ring.append( ring.front() );
55+
}
56+
n = ring.size();
6057

61-
// For each vertex, check whether it lies on a segment
62-
for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
58+
// For each vertex, check whether it lies on a segment
59+
for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
60+
{
61+
const QgsPoint &p = ring[iVert];
62+
for ( int i = 0, j = 1; j < n; i = j++ )
6363
{
64-
const QgsPoint &p = ring[iVert];
65-
for ( int i = 0, j = 1; j < n; i = j++ )
64+
if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
65+
{
66+
continue;
67+
}
68+
const QgsPoint &si = ring[i];
69+
const QgsPoint &sj = ring[j];
70+
QgsPoint q = QgsGeometryUtils::projectPointOnSegment( p, si, sj );
71+
if ( QgsGeometryUtils::sqrDistance2D( p, q ) < mContext->tolerance * mContext->tolerance )
6672
{
67-
if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
68-
{
69-
continue;
70-
}
71-
const QgsPoint &si = ring[i];
72-
const QgsPoint &sj = ring[j];
73-
QgsPoint q = QgsGeometryUtils::projectPointOnSegment( p, si, sj );
74-
if ( QgsGeometryUtils::sqrDistance2D( p, q ) < mContext->tolerance * mContext->tolerance )
75-
{
76-
errors.append( new QgsGeometryCheckError( this, layerFeature, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
77-
break; // No need to report same contact on different segments multiple times
78-
}
73+
errors.append( new QgsSingleGeometryCheckError( this, geometry, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
74+
break; // No need to report same contact on different segments multiple times
7975
}
8076
}
8177
}
8278
}
8379
}
80+
return errors;
8481
}
8582

8683
void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

‎src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
#ifndef QGS_GEOMETRY_SELFCONTACT_CHECK_H
1919
#define QGS_GEOMETRY_SELFCONTACT_CHECK_H
2020

21-
#include "qgsgeometrycheck.h"
21+
#include "qgssinglegeometrycheck.h"
2222

23-
class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsGeometryCheck
23+
class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsSingleGeometryCheck
2424
{
2525
public:
2626
QgsGeometrySelfContactCheck( QgsGeometryCheckerContext *context )
27-
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
28-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
27+
: QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
28+
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
2929
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes & ) const override;
3030
QStringList resolutionMethods() const override;
3131
QString errorDescription() const override { return tr( "Self contact" ); }

‎src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,62 +22,48 @@
2222
#include "qgsgeometryutils.h"
2323
#include "qgsfeaturepool.h"
2424

25-
bool QgsGeometrySelfIntersectionCheckError::isEqual( QgsGeometryCheckError *other ) const
25+
bool QgsGeometrySelfIntersectionCheckError::isEqual( const QgsSingleGeometryCheckError *other ) const
2626
{
27-
return QgsGeometryCheckError::isEqual( other ) &&
28-
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment1 == intersection().segment1 &&
29-
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment2 == intersection().segment2;
27+
return QgsSingleGeometryCheckError::isEqual( other ) &&
28+
static_cast<const QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment1 == intersection().segment1 &&
29+
static_cast<const QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment2 == intersection().segment2;
3030
}
3131

32-
bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QgsGeometryCheck::Changes &changes )
32+
bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QList<QgsGeometryCheck::Change> &changes )
3333
{
34-
if ( !QgsGeometryCheckError::handleChanges( changes ) )
35-
{
34+
if ( !QgsSingleGeometryCheckError::handleChanges( changes ) )
3635
return false;
37-
}
38-
for ( const QgsGeometryCheck::Change &change : changes[layerId()].value( featureId() ) )
36+
37+
for ( const QgsGeometryCheck::Change &change : changes )
3938
{
40-
if ( change.vidx.vertex == mInter.segment1 ||
41-
change.vidx.vertex == mInter.segment1 + 1 ||
42-
change.vidx.vertex == mInter.segment2 ||
43-
change.vidx.vertex == mInter.segment2 + 1 )
39+
if ( change.vidx.vertex == mIntersection.segment1 ||
40+
change.vidx.vertex == mIntersection.segment1 + 1 ||
41+
change.vidx.vertex == mIntersection.segment2 ||
42+
change.vidx.vertex == mIntersection.segment2 + 1 )
4443
{
4544
return false;
4645
}
4746
else if ( change.vidx.vertex >= 0 )
4847
{
49-
if ( change.vidx.vertex < mInter.segment1 )
48+
if ( change.vidx.vertex < mIntersection.segment1 )
5049
{
51-
mInter.segment1 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
50+
mIntersection.segment1 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
5251
}
53-
if ( change.vidx.vertex < mInter.segment2 )
52+
if ( change.vidx.vertex < mIntersection.segment2 )
5453
{
55-
mInter.segment2 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
54+
mIntersection.segment2 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
5655
}
5756
}
5857
}
5958
return true;
6059
}
6160

62-
63-
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
61+
void QgsGeometrySelfIntersectionCheckError::update( const QgsSingleGeometryCheckError *other )
6462
{
65-
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
66-
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
67-
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
68-
{
69-
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
70-
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
71-
{
72-
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
73-
{
74-
for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::selfIntersections( geom, iPart, iRing, mContext->tolerance ) )
75-
{
76-
errors.append( new QgsGeometrySelfIntersectionCheckError( this, layerFeature, inter.point, QgsVertexId( iPart, iRing ), inter ) );
77-
}
78-
}
79-
}
80-
}
63+
QgsSingleGeometryCheckError::update( other );
64+
// Static cast since this should only get called if isEqual == true
65+
const QgsGeometrySelfIntersectionCheckError *err = static_cast<const QgsGeometrySelfIntersectionCheckError *>( other );
66+
mIntersection.point = err->mIntersection.point;
8167
}
8268

8369
void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
@@ -89,9 +75,11 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i
8975
error->setObsolete();
9076
return;
9177
}
78+
9279
QgsGeometry featureGeom = feature.geometry();
9380
QgsAbstractGeometry *geom = featureGeom.get();
94-
QgsVertexId vidx = error->vidx();
81+
82+
const QgsVertexId vidx = error->vidx();
9583

9684
// Check if ring still exists
9785
if ( !vidx.isValid( geom ) )
@@ -100,7 +88,8 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i
10088
return;
10189
}
10290

103-
const QgsGeometryUtils::SelfIntersection &inter = static_cast<QgsGeometrySelfIntersectionCheckError *>( error )->intersection();
91+
const QgsGeometryCheckErrorSingle *singleError = static_cast<const QgsGeometryCheckErrorSingle *>( error );
92+
const QgsGeometryUtils::SelfIntersection &inter = static_cast<const QgsGeometrySelfIntersectionCheckError *>( singleError->singleError() )->intersection();
10493
// Check if error still applies
10594
bool ringIsClosed = false;
10695
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring, &ringIsClosed );
@@ -325,3 +314,22 @@ QStringList QgsGeometrySelfIntersectionCheck::resolutionMethods() const
325314
<< tr( "No action" );
326315
return methods;
327316
}
317+
318+
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
319+
{
320+
Q_UNUSED( configuration )
321+
322+
QList<QgsSingleGeometryCheckError *> errors;
323+
const QgsAbstractGeometry *geom = geometry.constGet();
324+
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
325+
{
326+
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
327+
{
328+
for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::selfIntersections( geom, iPart, iRing, mContext->tolerance ) )
329+
{
330+
errors.append( new QgsGeometrySelfIntersectionCheckError( this, geometry, inter.point, QgsVertexId( iPart, iRing ), inter ) );
331+
}
332+
}
333+
}
334+
return errors;
335+
}

‎src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.h

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,39 @@
1919
#define QGS_GEOMETRY_SELFINTERSECTION_CHECK_H
2020

2121
#include "qgsgeometryutils.h"
22-
#include "qgsgeometrycheck.h"
22+
#include "qgssinglegeometrycheck.h"
2323

24-
class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheckError : public QgsGeometryCheckError
24+
class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheckError : public QgsSingleGeometryCheckError
2525
{
2626
public:
27-
QgsGeometrySelfIntersectionCheckError( const QgsGeometryCheck *check,
28-
const QgsGeometryCheckerUtils::LayerFeature &layerFeature,
29-
const QgsPointXY &errorLocation,
30-
QgsVertexId vidx,
31-
const QgsGeometryUtils::SelfIntersection &inter )
32-
: QgsGeometryCheckError( check, layerFeature, errorLocation, vidx )
33-
, mInter( inter )
34-
{ }
35-
const QgsGeometryUtils::SelfIntersection &intersection() const { return mInter; }
36-
bool isEqual( QgsGeometryCheckError *other ) const override;
37-
bool handleChanges( const QgsGeometryCheck::Changes &changes ) override;
38-
void update( const QgsGeometrySelfIntersectionCheckError *other )
39-
{
40-
QgsGeometryCheckError::update( other );
41-
// Static cast since this should only get called if isEqual == true
42-
const QgsGeometrySelfIntersectionCheckError *err = static_cast<const QgsGeometrySelfIntersectionCheckError *>( other );
43-
mInter.point = err->mInter.point;
44-
}
27+
QgsGeometrySelfIntersectionCheckError( const QgsSingleGeometryCheck *check,
28+
const QgsGeometry &geometry,
29+
const QgsPoint &errorLocation,
30+
QgsVertexId vertexId,
31+
const QgsGeometryUtils::SelfIntersection &intersection )
32+
: QgsSingleGeometryCheckError( check, geometry, errorLocation, vertexId )
33+
, mIntersection( intersection )
34+
{}
35+
36+
const QgsGeometryUtils::SelfIntersection &intersection() const { return mIntersection; }
37+
bool isEqual( const QgsSingleGeometryCheckError *other ) const override;
38+
bool handleChanges( const QList<QgsGeometryCheck::Change> &changes ) override;
39+
void update( const QgsSingleGeometryCheckError *other ) override;
4540

4641
private:
47-
QgsGeometryUtils::SelfIntersection mInter;
42+
QgsGeometryUtils::SelfIntersection mIntersection;
4843
};
4944

50-
class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheck : public QgsGeometryCheck
45+
class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheck : public QgsSingleGeometryCheck
5146
{
5247
public:
5348
explicit QgsGeometrySelfIntersectionCheck( QgsGeometryCheckerContext *context )
54-
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
55-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
49+
: QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
5650
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
5751
QStringList resolutionMethods() const override;
5852
QString errorDescription() const override { return tr( "Self intersection" ); }
5953
QString errorName() const override { return QStringLiteral( "QgsGeometrySelfIntersectionCheck" ); }
54+
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
6055

6156
enum ResolutionMethod { ToMultiObject, ToSingleObjects, NoChange };
6257
};

‎src/analysis/vector/geometry_checker/qgsgeometrytypecheck.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,16 @@
2323
#include "qgsfeaturepool.h"
2424

2525

26-
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
26+
QList<QgsSingleGeometryCheckError *> QgsGeometryTypeCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
2727
{
28-
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
29-
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
30-
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
28+
QList<QgsSingleGeometryCheckError *> errors;
29+
const QgsAbstractGeometry *geom = geometry.constGet();
30+
QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() );
31+
if ( ( mAllowedTypes & ( 1 << type ) ) == 0 )
3132
{
32-
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
33-
QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() );
34-
if ( ( mAllowedTypes & ( 1 << type ) ) == 0 )
35-
{
36-
errors.append( new QgsGeometryTypeCheckError( this, layerFeature, geom->centroid(), type ) );
37-
}
33+
errors.append( new QgsGeometryTypeCheckError( this, geometry, geom->centroid(), type ) );
3834
}
35+
return errors;
3936
}
4037

4138
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
@@ -157,3 +154,14 @@ QStringList QgsGeometryTypeCheck::resolutionMethods() const
157154
<< tr( "No action" );
158155
return methods;
159156
}
157+
158+
bool QgsGeometryTypeCheckError::isEqual( const QgsSingleGeometryCheckError *other ) const
159+
{
160+
return QgsSingleGeometryCheckError::isEqual( other ) &&
161+
mFlatType == static_cast<const QgsGeometryTypeCheckError *>( other )->mFlatType;
162+
}
163+
164+
QString QgsGeometryTypeCheckError::description() const
165+
{
166+
return QStringLiteral( "%1 (%2)" ).arg( mCheck->errorDescription(), QgsWkbTypes::displayString( mFlatType ) );
167+
}

‎src/analysis/vector/geometry_checker/qgsgeometrytypecheck.h

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,36 @@
1818
#ifndef QGS_GEOMETRY_TYPE_CHECK_H
1919
#define QGS_GEOMETRY_TYPE_CHECK_H
2020

21-
#include "qgsgeometrycheck.h"
21+
#include "qgssinglegeometrycheck.h"
2222

23-
class ANALYSIS_EXPORT QgsGeometryTypeCheckError : public QgsGeometryCheckError
23+
class ANALYSIS_EXPORT QgsGeometryTypeCheckError : public QgsSingleGeometryCheckError
2424
{
2525
public:
26-
QgsGeometryTypeCheckError( const QgsGeometryCheck *check,
27-
const QgsGeometryCheckerUtils::LayerFeature &layerFeature,
28-
const QgsPointXY &errorLocation,
26+
QgsGeometryTypeCheckError( const QgsSingleGeometryCheck *check,
27+
const QgsGeometry &geometry,
28+
const QgsPoint &errorLocation,
2929
QgsWkbTypes::Type flatType )
30-
: QgsGeometryCheckError( check, layerFeature, errorLocation )
30+
: QgsSingleGeometryCheckError( check, geometry, errorLocation )
31+
, mFlatType( flatType )
3132
{
32-
mTypeName = QgsWkbTypes::displayString( flatType );
3333
}
3434

35-
bool isEqual( QgsGeometryCheckError *other ) const override
36-
{
37-
return QgsGeometryCheckError::isEqual( other ) &&
38-
mTypeName == static_cast<QgsGeometryTypeCheckError *>( other )->mTypeName;
39-
}
35+
bool isEqual( const QgsSingleGeometryCheckError *other ) const override;
4036

41-
QString description() const override { return QStringLiteral( "%1 (%2)" ).arg( mCheck->errorDescription(), mTypeName ); }
37+
QString description() const override;
4238

4339
private:
44-
QString mTypeName;
40+
QgsWkbTypes::Type mFlatType;
4541
};
4642

47-
class ANALYSIS_EXPORT QgsGeometryTypeCheck : public QgsGeometryCheck
43+
class ANALYSIS_EXPORT QgsGeometryTypeCheck : public QgsSingleGeometryCheck
4844
{
4945
public:
5046
QgsGeometryTypeCheck( QgsGeometryCheckerContext *context, int allowedTypes )
51-
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context )
47+
: QgsSingleGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context )
5248
, mAllowedTypes( allowedTypes )
5349
{}
54-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
50+
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
5551
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
5652
QStringList resolutionMethods() const override;
5753
QString errorDescription() const override { return tr( "Geometry type" ); }
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/***************************************************************************
2+
qgssinglegeometrycheck.cpp
3+
--------------------------------------
4+
Date : 6.9.2018
5+
Copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgssinglegeometrycheck.h"
17+
#include "qgspoint.h"
18+
19+
QgsSingleGeometryCheck::QgsSingleGeometryCheck( CheckType checkType, const QList<QgsWkbTypes::GeometryType> &compatibleGeometryTypes, QgsGeometryCheckerContext *context )
20+
: QgsGeometryCheck( checkType, compatibleGeometryTypes, context )
21+
{
22+
23+
}
24+
25+
void QgsSingleGeometryCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
26+
{
27+
Q_UNUSED( messages )
28+
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
29+
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
30+
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
31+
{
32+
const auto singleErrors = processGeometry( layerFeature.geometry(), QVariantMap() );
33+
for ( const auto error : singleErrors )
34+
errors.append( convertToGeometryCheckError( error, layerFeature ) );
35+
}
36+
}
37+
38+
QgsGeometryCheckErrorSingle *QgsSingleGeometryCheck::convertToGeometryCheckError( QgsSingleGeometryCheckError *singleGeometryCheckError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const
39+
{
40+
return new QgsGeometryCheckErrorSingle( singleGeometryCheckError, layerFeature );
41+
}
42+
43+
void QgsSingleGeometryCheckError::update( const QgsSingleGeometryCheckError *other )
44+
{
45+
Q_ASSERT( mCheck == other->mCheck );
46+
mErrorLocation = other->mErrorLocation;
47+
mVertexId = other->mVertexId;
48+
mGeometry = other->mGeometry;
49+
}
50+
51+
bool QgsSingleGeometryCheckError::isEqual( const QgsSingleGeometryCheckError *other ) const
52+
{
53+
return mGeometry == other->mGeometry
54+
&& mCheck == other->mCheck
55+
&& mErrorLocation == other->mErrorLocation
56+
&& mVertexId == other->mVertexId;
57+
}
58+
59+
bool QgsSingleGeometryCheckError::handleChanges( const QList<QgsGeometryCheck::Change> &changes )
60+
{
61+
Q_UNUSED( changes )
62+
return true;
63+
}
64+
65+
QString QgsSingleGeometryCheckError::description() const
66+
{
67+
return mCheck->errorDescription();
68+
}
69+
70+
const QgsSingleGeometryCheck *QgsSingleGeometryCheckError::check() const
71+
{
72+
return mCheck;
73+
}
74+
75+
QgsPoint QgsSingleGeometryCheckError::errorLocation() const
76+
{
77+
return mErrorLocation;
78+
}
79+
80+
QgsVertexId QgsSingleGeometryCheckError::vertexId() const
81+
{
82+
return mVertexId;
83+
}
84+
85+
QgsGeometryCheckErrorSingle::QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *error, const QgsGeometryCheckerUtils::LayerFeature &layerFeature )
86+
: QgsGeometryCheckError( error->check(), layerFeature, QgsPointXY( error->errorLocation() ), error->vertexId() )
87+
, mError( error )
88+
{
89+
90+
}
91+
92+
QgsSingleGeometryCheckError *QgsGeometryCheckErrorSingle::singleError() const
93+
{
94+
return mError;
95+
}
96+
97+
bool QgsGeometryCheckErrorSingle::handleChanges( const QgsGeometryCheck::Changes &changes )
98+
{
99+
if ( !QgsGeometryCheckError::handleChanges( changes ) )
100+
return false;
101+
102+
return mError->handleChanges( changes.value( layerId() ).value( featureId() ) );
103+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/***************************************************************************
2+
qgssinglegeometrycheck.h
3+
--------------------------------------
4+
Date : 6.9.2018
5+
Copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSSINGLEGEOMETRYCHECK_H
17+
#define QGSSINGLEGEOMETRYCHECK_H
18+
19+
#define SIP_NO_FILE
20+
21+
#include <QList>
22+
#include <QCoreApplication>
23+
24+
#include "qgsgeometry.h"
25+
#include "qgsgeometrycheck.h"
26+
27+
#include "qgis_analysis.h"
28+
29+
class QgsFeature;
30+
class QgsSingleGeometryCheck;
31+
32+
/**
33+
* An error from a QgsSingleGeometryCheck.
34+
*
35+
*/
36+
class ANALYSIS_EXPORT QgsSingleGeometryCheckError
37+
{
38+
public:
39+
QgsSingleGeometryCheckError( const QgsSingleGeometryCheck *check, const QgsGeometry &geometry, const QgsPoint &errorLocation, const QgsVertexId &vertexId = QgsVertexId() )
40+
: mCheck( check )
41+
, mGeometry( geometry )
42+
, mErrorLocation( errorLocation )
43+
, mVertexId( vertexId )
44+
{}
45+
46+
virtual ~QgsSingleGeometryCheckError() = default;
47+
48+
virtual void update( const QgsSingleGeometryCheckError *other );
49+
virtual bool isEqual( const QgsSingleGeometryCheckError *other ) const;
50+
virtual bool handleChanges( const QList<QgsGeometryCheck::Change> &changes );
51+
virtual QString description() const;
52+
53+
/**
54+
* The check that created this error.
55+
*
56+
* \since QGIS 3.4
57+
*/
58+
const QgsSingleGeometryCheck *check() const;
59+
60+
/**
61+
* The exact location of the error.
62+
*
63+
* \since QGIS 3.4
64+
*/
65+
QgsPoint errorLocation() const;
66+
67+
/**
68+
* The vertex id of the error. May be invalid depending on the check.
69+
*
70+
* \since QGIS 3.4
71+
*/
72+
QgsVertexId vertexId() const;
73+
74+
protected:
75+
const QgsSingleGeometryCheck *mCheck;
76+
QgsGeometry mGeometry;
77+
QgsPoint mErrorLocation;
78+
QgsVertexId mVertexId;
79+
};
80+
81+
/**
82+
* \ingroup analysis
83+
*
84+
* Wraps a QgsSingleGeometryError into a standard QgsGeometryCheckError.
85+
* The single error can be obtained via singleError.
86+
*
87+
* \since QGIS 3.4
88+
*/
89+
class ANALYSIS_EXPORT QgsGeometryCheckErrorSingle : public QgsGeometryCheckError
90+
{
91+
public:
92+
QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *singleError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature );
93+
94+
/**
95+
* The underlying single error.
96+
*/
97+
QgsSingleGeometryCheckError *singleError() const;
98+
99+
bool handleChanges( const QgsGeometryCheck::Changes &changes ) override;
100+
101+
private:
102+
QgsSingleGeometryCheckError *mError;
103+
};
104+
105+
/**
106+
* \ingroup analysis
107+
*
108+
* Base class for geometry checks for a single geometry without any context of the layer or other layers in the project.
109+
* Classic examples are validity checks like self-intersection.
110+
*
111+
* Subclasses need to implement the processGeometry method.
112+
*
113+
* \since QGIS 3.4
114+
*/
115+
class ANALYSIS_EXPORT QgsSingleGeometryCheck : public QgsGeometryCheck
116+
{
117+
public:
118+
QgsSingleGeometryCheck( CheckType checkType, const QList<QgsWkbTypes::GeometryType> &compatibleGeometryTypes, QgsGeometryCheckerContext *context );
119+
120+
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const final;
121+
122+
/**
123+
* Check the \a geometry for errors. It may make use of \a configuration options.
124+
*
125+
* Returns a list of QgsSingleGeometryCheckErrors, ownership is transferred to the caller.
126+
* An empty list is returned for geometries without errors.
127+
*
128+
* \since QGIS 3.4
129+
*/
130+
virtual QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const = 0;
131+
132+
private:
133+
134+
/**
135+
* Converts a QgsSingleGeometryCheckError to a QgsGeometryCheckErrorSingle.
136+
*
137+
* \since QGIS 3.4
138+
*/
139+
QgsGeometryCheckErrorSingle *convertToGeometryCheckError( QgsSingleGeometryCheckError *singleGeometryCheckError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const;
140+
141+
};
142+
143+
#endif // QGSSINGLEGEOMETRYCHECK_H

‎tests/src/geometry_checker/testqgsgeometrychecks.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -916,14 +916,14 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck()
916916
QCOMPARE( f.geometry().constGet()->vertexCount( 1, 1 ), 5 );
917917

918918
// Test change tracking
919-
QgsGeometrySelfIntersectionCheckError *err = static_cast<QgsGeometrySelfIntersectionCheckError *>( errs4[0] );
920-
QgsGeometryUtils::SelfIntersection oldInter = err->intersection();
921-
QVERIFY( err->handleChanges( change2changes( {err->layerId(), err->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, QgsVertexId( errs4[0]->vidx().part, errs4[0]->vidx().ring, 0 )} ) ) );
922-
QgsGeometryUtils::SelfIntersection newInter = err->intersection();
919+
QgsGeometryCheckErrorSingle *err = static_cast<QgsGeometryCheckErrorSingle *>( errs4[0] );
920+
QgsGeometryUtils::SelfIntersection oldInter = static_cast<QgsGeometrySelfIntersectionCheckError *>( err->singleError() )->intersection();
921+
QVERIFY( err->handleChanges( change2changes( {err->layerId(), err->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, QgsVertexId( err->vidx().part, errs4[0]->vidx().ring, 0 )} ) ) );
922+
QgsGeometryUtils::SelfIntersection newInter = static_cast<QgsGeometrySelfIntersectionCheckError *>( err->singleError() )->intersection();
923923
QVERIFY( oldInter.segment1 == newInter.segment1 + 1 );
924924
QVERIFY( oldInter.segment2 == newInter.segment2 + 1 );
925-
QVERIFY( err->handleChanges( change2changes( {err->layerId(), err->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeAdded, QgsVertexId( errs4[0]->vidx().part, errs4[0]->vidx().ring, 0 )} ) ) );
926-
newInter = err->intersection();
925+
QVERIFY( err->handleChanges( change2changes( {err->layerId(), errs4[0]->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeAdded, QgsVertexId( err->vidx().part, errs4[0]->vidx().ring, 0 )} ) ) );
926+
newInter = static_cast<QgsGeometrySelfIntersectionCheckError *>( err->singleError() )->intersection();
927927
QVERIFY( oldInter.segment1 == newInter.segment1 );
928928
QVERIFY( oldInter.segment2 == newInter.segment2 );
929929

0 commit comments

Comments
 (0)
Please sign in to comment.