Skip to content

Commit 31cc65d

Browse files
committed
[Geometry checker] Initial multi-layer support
1 parent e3fc73f commit 31cc65d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1488
-1086
lines changed

src/plugins/geometry_checker/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SET (geometrychecker_SRCS
2020
checks/qgsgeometrysegmentlengthcheck.cpp
2121
checks/qgsgeometryselfcontactcheck.cpp
2222
checks/qgsgeometryselfintersectioncheck.cpp
23+
checks/qgsgeometrysliverpolygoncheck.cpp
2324
checks/qgsgeometrytypecheck.cpp
2425
ui/qgsgeometrycheckerdialog.cpp
2526
ui/qgsgeometrycheckersetuptab.cpp

src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp

+44-37
Original file line numberDiff line numberDiff line change
@@ -17,61 +17,68 @@
1717
#include "qgsgeometryutils.h"
1818
#include "../utils/qgsfeaturepool.h"
1919

20-
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
20+
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
2121
{
22-
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
23-
Q_FOREACH ( QgsFeatureId featureid, featureIds )
22+
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
23+
for ( const QString &layerId : featureIds.keys() )
2424
{
25-
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
26-
QgsFeature feature;
27-
if ( !mFeaturePool->get( featureid, feature ) )
25+
if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) )
2826
{
2927
continue;
3028
}
31-
QgsGeometry g = feature.geometry();
32-
const QgsAbstractGeometry *geom = g.geometry();
33-
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
29+
for ( QgsFeatureId featureid : featureIds[layerId] )
3430
{
35-
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
31+
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
32+
QgsFeature feature;
33+
if ( !getFeaturePool( layerId )->get( featureid, feature ) )
3634
{
37-
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
38-
// Less than three points, no angles to check
39-
if ( nVerts < 3 )
40-
{
41-
continue;
42-
}
43-
for ( int iVert = 0; iVert < nVerts; ++iVert )
35+
continue;
36+
}
37+
QgsGeometry g = feature.geometry();
38+
const QgsAbstractGeometry *geom = g.geometry();
39+
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
40+
{
41+
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
4442
{
45-
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
46-
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
47-
const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
48-
QgsVector v21, v23;
49-
try
43+
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
44+
// Less than three points, no angles to check
45+
if ( nVerts < 3 )
5046
{
51-
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
52-
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
53-
}
54-
catch ( const QgsException & )
55-
{
56-
// Zero length vectors
5747
continue;
5848
}
59-
60-
double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
61-
if ( angle < mMinAngle )
49+
for ( int iVert = 0; iVert < nVerts; ++iVert )
6250
{
63-
errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
51+
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
52+
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
53+
const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
54+
QgsVector v21, v23;
55+
try
56+
{
57+
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
58+
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
59+
}
60+
catch ( const QgsException & )
61+
{
62+
// Zero length vectors
63+
continue;
64+
}
65+
66+
double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
67+
if ( angle < mMinAngle )
68+
{
69+
errors.append( new QgsGeometryCheckError( this, layerId, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
70+
}
6471
}
6572
}
6673
}
6774
}
6875
}
6976
}
7077

71-
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
78+
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
7279
{
7380
QgsFeature feature;
74-
if ( !mFeaturePool->get( error->featureId(), feature ) )
81+
if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) )
7582
{
7683
error->setObsolete();
7784
return;
@@ -132,15 +139,15 @@ void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method,
132139
}
133140
else
134141
{
135-
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
142+
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
136143
if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance()
137144
&& QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
138145
geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2
139146
{
140-
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
147+
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
141148
}
142149
feature.setGeometry( g );
143-
mFeaturePool->updateFeature( feature );
150+
getFeaturePool( error->layerId() )->updateFeature( feature );
144151
error->setFixed( method );
145152
}
146153
}

src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ class QgsGeometryAngleCheck : public QgsGeometryCheck
2323
Q_OBJECT
2424

2525
public:
26-
QgsGeometryAngleCheck( QgsFeaturePool *featurePool, double minAngle )
27-
: QgsGeometryCheck( FeatureNodeCheck, featurePool )
28-
, mMinAngle( minAngle )
26+
QgsGeometryAngleCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double minAngle )
27+
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools )
28+
, mMinAngle( minAngle )
2929
{}
30-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override;
31-
void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override;
30+
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
31+
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
3232
QStringList getResolutionMethods() const override;
3333
QString errorDescription() const override { return tr( "Minimal angle" ); }
3434
QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); }

src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp

+48-33
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,54 @@
1818
#include "qgsgeometryareacheck.h"
1919
#include "../utils/qgsfeaturepool.h"
2020

21-
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
21+
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
2222
{
23-
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
24-
Q_FOREACH ( QgsFeatureId featureid, featureIds )
23+
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
24+
for ( const QString &layerId : featureIds.keys() )
2525
{
26-
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
27-
QgsFeature feature;
28-
if ( !mFeaturePool->get( featureid, feature ) )
26+
if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) )
2927
{
3028
continue;
3129
}
32-
33-
QgsGeometry g = feature.geometry();
34-
QgsAbstractGeometry *geom = g.geometry();
35-
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
30+
for ( QgsFeatureId featureid : featureIds[layerId] )
3631
{
37-
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( geom );
38-
for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i )
32+
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
33+
QgsFeature feature;
34+
if ( !getFeaturePool( layerId )->get( featureid, feature ) )
3935
{
40-
double value;
41-
if ( checkThreshold( multiGeom->geometryN( i ), value ) )
36+
continue;
37+
}
38+
39+
QgsGeometry g = feature.geometry();
40+
QgsAbstractGeometry *geom = g.geometry();
41+
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
42+
{
43+
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( geom );
44+
for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i )
4245
{
43-
errors.append( new QgsGeometryCheckError( this, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) );
46+
double value;
47+
if ( checkThreshold( layerId, multiGeom->geometryN( i ), value ) )
48+
{
49+
errors.append( new QgsGeometryCheckError( this, layerId, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) );
50+
}
4451
}
4552
}
46-
}
47-
else
48-
{
49-
double value;
50-
if ( checkThreshold( geom, value ) )
53+
else
5154
{
52-
errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) );
55+
double value;
56+
if ( checkThreshold( layerId, geom, value ) )
57+
{
58+
errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) );
59+
}
5360
}
5461
}
5562
}
5663
}
5764

58-
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const
65+
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
5966
{
6067
QgsFeature feature;
61-
if ( !mFeaturePool->get( error->featureId(), feature ) )
68+
if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) )
6269
{
6370
error->setObsolete();
6471
return;
@@ -78,7 +85,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
7885
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
7986
{
8087
double value;
81-
if ( !checkThreshold( static_cast<QgsGeometryCollection *>( geom )->geometryN( vidx.part ), value ) )
88+
if ( !checkThreshold( error->layerId(), static_cast<QgsGeometryCollection *>( geom )->geometryN( vidx.part ), value ) )
8289
{
8390
error->setObsolete();
8491
return;
@@ -87,7 +94,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
8794
else
8895
{
8996
double value;
90-
if ( !checkThreshold( geom, value ) )
97+
if ( !checkThreshold( error->layerId(), geom, value ) )
9198
{
9299
error->setObsolete();
93100
return;
@@ -101,13 +108,13 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
101108
}
102109
else if ( method == Delete )
103110
{
104-
deleteFeatureGeometryPart( feature, vidx.part, changes );
111+
deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes );
105112
error->setFixed( method );
106113
}
107114
else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
108115
{
109116
QString errMsg;
110-
if ( mergeWithNeighbor( feature, vidx.part, method, mergeAttributeIndex, changes, errMsg ) )
117+
if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) )
111118
{
112119
error->setFixed( method );
113120
}
@@ -122,20 +129,28 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
122129
}
123130
}
124131

125-
bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
132+
bool QgsGeometryAreaCheck::checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const
133+
{
134+
value = geom->area();
135+
double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits();
136+
double threshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
137+
return value < threshold;
138+
}
139+
140+
bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
126141
{
127142
double maxVal = 0.;
128143
QgsFeature mergeFeature;
129144
int mergePartIdx = -1;
130145
bool matchFound = false;
131-
QgsGeometry g = feature.geometry();;
146+
QgsGeometry g = feature.geometry();
132147
QgsAbstractGeometry *geom = g.geometry();
133148

134149
// Search for touching neighboring geometries
135-
Q_FOREACH ( QgsFeatureId testId, mFeaturePool->getIntersects( g.boundingBox() ) )
150+
for ( QgsFeatureId testId : getFeaturePool( layerId )->getIntersects( g.boundingBox() ) )
136151
{
137152
QgsFeature testFeature;
138-
if ( !mFeaturePool->get( testId, testFeature ) )
153+
if ( !getFeaturePool( layerId )->get( testId, testFeature ) )
139154
{
140155
continue;
141156
}
@@ -210,9 +225,9 @@ bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx,
210225
{
211226
--mergePartIdx;
212227
}
213-
replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes );
228+
replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes );
214229
// Remove polygon from source geometry
215-
deleteFeatureGeometryPart( feature, partIdx, changes );
230+
deleteFeatureGeometryPart( layerId, feature, partIdx, changes );
216231

217232
return true;
218233
}

src/plugins/geometry_checker/checks/qgsgeometryareacheck.h

+8-8
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,23 @@ class QgsGeometryAreaCheck : public QgsGeometryCheck
2525
Q_OBJECT
2626

2727
public:
28-
QgsGeometryAreaCheck( QgsFeaturePool *featurePool, double threshold )
29-
: QgsGeometryCheck( FeatureCheck, featurePool )
30-
, mThreshold( threshold )
28+
QgsGeometryAreaCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double thresholdMapUnits )
29+
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools )
30+
, mThresholdMapUnits( thresholdMapUnits )
3131
{}
32-
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override;
33-
void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override;
32+
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
33+
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
3434
QStringList getResolutionMethods() const override;
3535
QString errorDescription() const override { return tr( "Minimal area" ); }
3636
QString errorName() const override { return QStringLiteral( "QgsGeometryAreaCheck" ); }
3737
private:
3838
enum ResolutionMethod { MergeLongestEdge, MergeLargestArea, MergeIdenticalAttribute, Delete, NoChange };
3939

40-
bool mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const;
41-
virtual bool checkThreshold( const QgsAbstractGeometry *geom, double &value ) const { value = geom->area(); return value < mThreshold; }
40+
virtual bool checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const;
41+
bool mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const;
4242

4343
protected:
44-
double mThreshold;
44+
double mThresholdMapUnits;
4545
};
4646

4747
#endif // QGS_GEOMETRY_AREA_CHECK_H

0 commit comments

Comments
 (0)