Skip to content

Commit 3d8ffcb

Browse files
committedOct 23, 2017
[Geometry checker] Initial multilayer support for overlap check
1 parent dd12b13 commit 3d8ffcb

File tree

2 files changed

+101
-68
lines changed

2 files changed

+101
-68
lines changed
 

‎src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp

Lines changed: 94 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,73 +13,92 @@
1313
* *
1414
***************************************************************************/
1515

16+
#include "qgscrscache.h"
1617
#include "qgsgeometryengine.h"
1718
#include "qgsgeometryoverlapcheck.h"
1819
#include "../utils/qgsfeaturepool.h"
1920

2021
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
2122
{
23+
double overlapThreshold = mThresholdMapUnits;
2224
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
23-
for ( const QString &layerId : featureIds.keys() )
25+
QList<QString> layerIds = featureIds.keys();
26+
for ( int i = 0, n = layerIds.length(); i < n; ++i )
2427
{
25-
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
26-
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) )
28+
QString layerIdA = layerIds[i];
29+
QgsFeaturePool *featurePoolA = mContext->featurePools[ layerIdA ];
30+
if ( !getCompatibility( featurePoolA->getLayer()->geometryType() ) )
2731
{
2832
continue;
2933
}
30-
double mapToLayerUnits = featurePool->getMapToLayerUnits();
31-
double overlapThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
32-
for ( QgsFeatureId featureid : featureIds[layerId] )
34+
QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
35+
36+
for ( QgsFeatureId featureIdA : featureIds[layerIdA] )
3337
{
3438
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
35-
QgsFeature feature;
36-
if ( !featurePool->get( featureid, feature ) )
39+
QgsFeature featureA;
40+
if ( !featurePoolA->get( featureIdA, featureA ) )
3741
{
3842
continue;
3943
}
40-
QgsGeometry featureGeom = feature.geometry();
41-
QgsAbstractGeometry *geom = featureGeom.geometry();
42-
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->tolerance );
4344

44-
QgsFeatureIds ids = featurePool->getIntersects( feature.geometry().boundingBox() );
45-
for ( QgsFeatureId otherid : ids )
46-
{
47-
// >= : only report overlaps once
48-
if ( otherid >= featureid )
49-
{
50-
continue;
51-
}
45+
QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
46+
featureGeomA->transform( crstA );
47+
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->tolerance );
48+
QgsRectangle bboxA = featureGeomA->boundingBox();
5249

53-
QgsFeature otherFeature;
54-
if ( !featurePool->get( otherid, otherFeature ) )
50+
for ( int j = i; j < n; ++j )
51+
{
52+
QString layerIdB = layerIds[j];
53+
QgsFeaturePool *featurePoolB = mContext->featurePools[ layerIdA ];
54+
if ( !getCompatibility( featurePoolB->getLayer()->geometryType() ) )
5555
{
5656
continue;
5757
}
58+
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );
5859

59-
QString errMsg;
60-
if ( geomEngine->overlaps( *otherFeature.geometry().geometry(), &errMsg ) )
60+
QgsFeatureIds idsB = featurePoolB->getIntersects( crstB.transform( bboxA, QgsCoordinateTransform::ReverseTransform ) );
61+
for ( QgsFeatureId featureIdB : idsB )
6162
{
62-
QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() );
63-
if ( interGeom && !interGeom->isEmpty() )
63+
// > : only report overlaps within same layer once
64+
if ( layerIdA == layerIdB && featureIdB >= featureIdA )
65+
{
66+
continue;
67+
}
68+
QgsFeature featureB;
69+
if ( !featurePoolB->get( featureIdB, featureB ) )
6470
{
65-
QgsGeometryCheckerUtils::filter1DTypes( interGeom );
66-
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
71+
continue;
72+
}
73+
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
74+
featureGeomB->transform( crstB );
75+
QString errMsg;
76+
if ( geomEngineA->overlaps( *featureGeomB, &errMsg ) )
77+
{
78+
QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB );
79+
if ( interGeom && !interGeom->isEmpty() )
6780
{
68-
double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
69-
if ( area > mContext->reducedTolerance && area < overlapThreshold )
81+
QgsGeometryCheckerUtils::filter1DTypes( interGeom );
82+
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
7083
{
71-
errors.append( new QgsGeometryOverlapCheckError( this, layerId, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) );
84+
double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
85+
if ( area > mContext->reducedTolerance && area < overlapThreshold )
86+
{
87+
errors.append( new QgsGeometryOverlapCheckError( this, layerIdA, featureIdA, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, qMakePair( layerIdB, featureIdB ) ) );
88+
}
7289
}
7390
}
91+
else if ( !errMsg.isEmpty() )
92+
{
93+
messages.append( tr( "Overlap check between features %1:%2 and %3:%4 %5" ).arg( layerIdA ).arg( featureIdA ).arg( layerIdB ).arg( featureIdB ).arg( errMsg ) );
94+
}
95+
delete interGeom;
7496
}
75-
else if ( !errMsg.isEmpty() )
76-
{
77-
messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
78-
}
79-
delete interGeom;
97+
delete featureGeomB;
8098
}
8199
}
82-
delete geomEngine;
100+
delete geomEngineA;
101+
delete featureGeomA;
83102
}
84103
}
85104
}
@@ -89,30 +108,41 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
89108
QString errMsg;
90109
QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error );
91110

92-
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
93-
QgsFeature feature;
94-
QgsFeature otherFeature;
95-
if ( !featurePool->get( error->featureId(), feature ) ||
96-
!featurePool->get( overlapError->otherId(), otherFeature ) )
111+
QgsFeaturePool *featurePoolA = mContext->featurePools[ overlapError->layerId() ];
112+
QgsFeaturePool *featurePoolB = mContext->featurePools[ overlapError->overlappedFeature().first ];
113+
QgsFeature featureA;
114+
QgsFeature featureB;
115+
if ( !featurePoolA->get( overlapError->featureId(), featureA ) ||
116+
!featurePoolB->get( overlapError->overlappedFeature().second, featureB ) )
97117
{
98118
error->setObsolete();
99119
return;
100120
}
101-
QgsGeometry featureGeom = feature.geometry();
102-
QgsAbstractGeometry *geom = featureGeom.geometry();
103-
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->tolerance );
121+
QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
122+
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );
104123

105124
// Check if error still applies
106-
if ( !geomEngine->overlaps( otherFeature.geometry().geometry() ) )
125+
QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
126+
featureGeomA->transform( crstA );
127+
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->reducedTolerance );
128+
129+
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
130+
featureGeomB->transform( crstB );
131+
132+
if ( !geomEngineA->overlaps( *featureGeomB ) )
107133
{
108-
delete geomEngine;
134+
delete geomEngineA;
135+
delete featureGeomA;
136+
delete featureGeomB;
109137
error->setObsolete();
110138
return;
111139
}
112-
QgsAbstractGeometry *interGeom = geomEngine->intersection( otherFeature.geometry().geometry(), &errMsg );
113-
delete geomEngine;
140+
QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB, &errMsg );
114141
if ( !interGeom )
115142
{
143+
delete geomEngineA;
144+
delete featureGeomA;
145+
delete featureGeomB;
116146
error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) );
117147
return;
118148
}
@@ -132,6 +162,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
132162
if ( !interPart || interPart->isEmpty() )
133163
{
134164
delete interGeom;
165+
delete geomEngineA;
166+
delete featureGeomA;
167+
delete featureGeomB;
135168
error->setObsolete();
136169
return;
137170
}
@@ -143,9 +176,7 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
143176
}
144177
else if ( method == Subtract )
145178
{
146-
geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->reducedTolerance );
147-
QgsAbstractGeometry *diff1 = geomEngine->difference( *interPart, &errMsg );
148-
delete geomEngine;
179+
QgsAbstractGeometry *diff1 = geomEngineA->difference( *interPart, &errMsg );
149180
if ( !diff1 || diff1->isEmpty() )
150181
{
151182
delete diff1;
@@ -155,10 +186,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
155186
{
156187
QgsGeometryCheckerUtils::filter1DTypes( diff1 );
157188
}
158-
QgsGeometry otherFeatureGeom = otherFeature.geometry();
159-
QgsGeometryEngine *otherGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( otherFeatureGeom.geometry(), mContext->reducedTolerance );
160-
QgsAbstractGeometry *diff2 = otherGeomEngine->difference( *interPart, &errMsg );
161-
delete otherGeomEngine;
189+
QgsGeometryEngine *geomEngineB = QgsGeometryCheckerUtils::createGeomEngine( featureGeomB, mContext->reducedTolerance );
190+
QgsAbstractGeometry *diff2 = geomEngineB->difference( *interPart, &errMsg );
191+
delete geomEngineB;
162192
if ( !diff2 || diff2->isEmpty() )
163193
{
164194
delete diff2;
@@ -178,19 +208,19 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
178208
{
179209
if ( shared1 < shared2 )
180210
{
181-
feature.setGeometry( QgsGeometry( diff1 ) );
211+
featureA.setGeometry( QgsGeometry( diff1 ) );
182212

183-
changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
184-
featurePool->updateFeature( feature );
213+
changes[error->layerId()][featureA.id()].append( Change( ChangeFeature, ChangeChanged ) );
214+
featurePoolA->updateFeature( featureA );
185215

186216
delete diff2;
187217
}
188218
else
189219
{
190-
otherFeature.setGeometry( QgsGeometry( diff2 ) );
220+
featureB.setGeometry( QgsGeometry( diff2 ) );
191221

192-
changes[error->layerId()][otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) );
193-
featurePool->updateFeature( otherFeature );
222+
changes[overlapError->overlappedFeature().first][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) );
223+
featurePoolB->updateFeature( featureB );
194224

195225
delete diff1;
196226
}
@@ -203,6 +233,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
203233
error->setFixFailed( tr( "Unknown method" ) );
204234
}
205235
delete interGeom;
236+
delete geomEngineA;
237+
delete featureGeomA;
238+
delete featureGeomB;
206239
}
207240

208241
QStringList QgsGeometryOverlapCheck::getResolutionMethods() const

‎src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,33 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
2626
QgsFeatureId featureId,
2727
const QgsPoint &errorLocation,
2828
const QVariant &value,
29-
QgsFeatureId otherId )
29+
const QPair<QString, QgsFeatureId> &overlappedFeature )
3030
: QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), value, ValueArea )
31-
, mOtherId( otherId )
31+
, mOverlappedFeature( overlappedFeature )
3232
{ }
33-
QgsFeatureId otherId() const { return mOtherId; }
33+
const QPair<QString, QgsFeatureId> &overlappedFeature() const { return mOverlappedFeature; }
3434

3535
bool isEqual( QgsGeometryCheckError *other ) const override
3636
{
3737
QgsGeometryOverlapCheckError *err = dynamic_cast<QgsGeometryOverlapCheckError *>( other );
3838
return err &&
3939
other->layerId() == layerId() &&
4040
other->featureId() == featureId() &&
41-
err->otherId() == otherId() &&
41+
err->overlappedFeature() == overlappedFeature() &&
4242
QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), mCheck->getContext()->reducedTolerance ) &&
4343
qAbs( value().toDouble() - other->value().toDouble() ) < mCheck->getContext()->reducedTolerance;
4444
}
4545

4646
bool closeMatch( QgsGeometryCheckError *other ) const override
4747
{
4848
QgsGeometryOverlapCheckError *err = dynamic_cast<QgsGeometryOverlapCheckError *>( other );
49-
return err && other->layerId() == layerId() && other->featureId() == featureId() && err->otherId() == otherId();
49+
return err && other->layerId() == layerId() && other->featureId() == featureId() && err->overlappedFeature() == overlappedFeature();
5050
}
5151

52-
virtual QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1" ).arg( otherId() ); }
52+
virtual QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1:%2" ).arg( mOverlappedFeature.first ).arg( mOverlappedFeature.second ); }
5353

5454
private:
55-
QgsFeatureId mOtherId;
55+
QPair<QString, QgsFeatureId> mOverlappedFeature;
5656
};
5757

5858
class QgsGeometryOverlapCheck : public QgsGeometryCheck

0 commit comments

Comments
 (0)
Please sign in to comment.