[Geometry checker] Add multi-layer support to gap check

This commit is contained in:
Sandro Mani 2017-04-04 16:49:12 +02:00
parent 23affe4fae
commit dd12b132c5
3 changed files with 124 additions and 97 deletions

View File

@ -24,6 +24,8 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
{ {
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QList<QgsAbstractGeometry *> geomList;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
// Collect geometries, build spatial index // Collect geometries, build spatial index
for ( const QString &layerId : featureIds.keys() ) for ( const QString &layerId : featureIds.keys() )
@ -34,15 +36,15 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
{ {
continue; continue;
} }
double mapToLayerUnits = featurePool->getMapToLayerUnits();
double gapAreaThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
QList<QgsAbstractGeometry *> geomList;
for ( QgsFeatureId id : featureIds[layerId] ) for ( QgsFeatureId id : featureIds[layerId] )
{ {
QgsFeature feature; QgsFeature feature;
if ( featurePool->get( id, feature ) ) if ( featurePool->get( id, feature ) )
{ {
geomList.append( feature.geometry().geometry()->clone() ); QgsAbstractGeometry *geometry = feature.geometry().geometry()->clone();
geometry->transform( t );
geomList.append( geometry );
}
} }
} }
@ -97,23 +99,32 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
// For each gap polygon which does not lie on the boundary, get neighboring polygons and add error // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart ) for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart )
{ {
QgsAbstractGeometry *geom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone(); QgsAbstractGeometry *gapGeom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone();
// Skip the gap between features and boundingbox // Skip the gap between features and boundingbox
if ( geom->boundingBox() == envelope->boundingBox() ) if ( gapGeom->boundingBox() == envelope->boundingBox() )
{ {
continue; continue;
} }
// Skip gaps above threshold // Skip gaps above threshold
if ( geom->area() > gapAreaThreshold || geom->area() < mContext->reducedTolerance ) if ( gapGeom->area() > mThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance )
{ {
continue; continue;
} }
QgsRectangle gapAreaBBox = gapGeom->boundingBox();
// Get neighboring polygons // Get neighboring polygons
QgsFeatureIds neighboringIds; QMap<QString, QgsFeatureIds> neighboringIds;
QgsRectangle gapAreaBBox = t.transform( geom->boundingBox() ); for ( const QString &layerId : featureIds.keys() )
QgsFeatureIds intersectIds = featurePool->getIntersects( geom->boundingBox() ); {
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
QgsRectangle gapAreaLayerBBox = t.transform( gapGeom->boundingBox() ); // Don't use gapAreaBBox since it is updated below
QgsFeatureIds intersectIds = featurePool->getIntersects( gapAreaLayerBBox );
QgsAbstractGeometry *gapLayerGeom = gapGeom->clone();
gapLayerGeom->transform( t );
for ( QgsFeatureId id : intersectIds ) for ( QgsFeatureId id : intersectIds )
{ {
@ -122,23 +133,24 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
{ {
continue; continue;
} }
QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *featureGeom = feature.geometry().geometry();
QgsAbstractGeometry *geom2 = featureGeom.geometry(); if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapLayerGeom, featureGeom, mContext->reducedTolerance ) > 0 )
if ( QgsGeometryCheckerUtils::sharedEdgeLength( geom, geom2, mContext->reducedTolerance ) > 0 )
{ {
neighboringIds.insert( feature.id() ); neighboringIds[layerId].insert( feature.id() );
gapAreaBBox.unionRect( t.transform( geom2->boundingBox() ) ); gapAreaLayerBBox.unionRect( featureGeom->boundingBox() );
} }
gapAreaBBox.unionRect( t.transform( gapAreaLayerBBox, QgsCoordinateTransform::ReverseTransform ) );
}
delete gapLayerGeom;
} }
if ( neighboringIds.isEmpty() ) if ( neighboringIds.isEmpty() )
{ {
delete geom; delete gapGeom;
continue; continue;
} }
// Add error // Add error
errors.append( new QgsGeometryGapCheckError( this, layerId, geom, neighboringIds, geom->area(), gapAreaBBox ) ); errors.append( new QgsGeometryGapCheckError( this, "", gapGeom, neighboringIds, gapGeom->area(), gapAreaBBox ) );
}
delete unionGeom; delete unionGeom;
delete envelope; delete envelope;
@ -172,15 +184,22 @@ void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, co
bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const
{ {
QgsFeaturePool *featurePool = mContext->featurePools[ err->layerId() ];
double maxVal = 0.; double maxVal = 0.;
QString mergeLayerId;
QgsFeature mergeFeature; QgsFeature mergeFeature;
int mergePartIdx = -1; int mergePartIdx = -1;
QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 ); QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 );
// Search for touching neighboring geometries // Search for touching neighboring geometries
for ( QgsFeatureId testId : err->neighbors() ) for ( const QString &layerId : err->neighbors().keys() )
{
QgsFeaturePool *featurePool = mContext->featurePools[ err->layerId() ];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
QgsAbstractGeometry *errLayerGeom = errGeometry->clone();
errLayerGeom->transform( t );
for ( QgsFeatureId testId : err->neighbors()[layerId] )
{ {
QgsFeature testFeature; QgsFeature testFeature;
if ( !featurePool->get( testId, testFeature ) ) if ( !featurePool->get( testId, testFeature ) )
@ -191,15 +210,18 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan
QgsAbstractGeometry *testGeom = featureGeom.geometry(); QgsAbstractGeometry *testGeom = featureGeom.geometry();
for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart ) for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
{ {
double len = QgsGeometryCheckerUtils::sharedEdgeLength( errGeometry, QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance ); double len = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom, QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
if ( len > maxVal ) if ( len > maxVal )
{ {
maxVal = len; maxVal = len;
mergeFeature = testFeature; mergeFeature = testFeature;
mergePartIdx = iPart; mergePartIdx = iPart;
mergeLayerId = layerId;
} }
} }
} }
delete errLayerGeom;
}
if ( maxVal == 0. ) if ( maxVal == 0. )
{ {
@ -207,11 +229,16 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan
} }
// Merge geometries // Merge geometries
QgsFeaturePool *featurePool = mContext->featurePools[ mergeLayerId ];
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
QgsAbstractGeometry *errLayerGeom = errGeometry->clone();
errLayerGeom->transform( t );
QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.geometry(); QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.geometry();
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errGeometry, mContext->tolerance ); QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom, mContext->tolerance );
QgsAbstractGeometry *combinedGeom = geomEngine->combine( *QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ); QgsAbstractGeometry *combinedGeom = geomEngine->combine( *QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg );
delete geomEngine; delete geomEngine;
delete errLayerGeom;
if ( !combinedGeom || combinedGeom->isEmpty() ) if ( !combinedGeom || combinedGeom->isEmpty() )
{ {
delete combinedGeom; delete combinedGeom;

View File

@ -25,7 +25,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
QgsGeometryGapCheckError( const QgsGeometryCheck *check, QgsGeometryGapCheckError( const QgsGeometryCheck *check,
const QString &layerId, const QString &layerId,
QgsAbstractGeometry *geometry, QgsAbstractGeometry *geometry,
const QgsFeatureIds &neighbors, const QMap<QString, QgsFeatureIds> &neighbors,
double area, double area,
const QgsRectangle &gapAreaBBox ) const QgsRectangle &gapAreaBBox )
: QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea ) : QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea )
@ -40,7 +40,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
} }
QgsAbstractGeometry *geometry() const override { return mGeometry->clone(); } QgsAbstractGeometry *geometry() const override { return mGeometry->clone(); }
const QgsFeatureIds &neighbors() const { return mNeighbors; } const QMap<QString, QgsFeatureIds> &neighbors() const { return mNeighbors; }
bool isEqual( QgsGeometryCheckError *other ) const override bool isEqual( QgsGeometryCheckError *other ) const override
{ {
@ -76,7 +76,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
} }
private: private:
QgsFeatureIds mNeighbors; QMap<QString, QgsFeatureIds> mNeighbors;
QgsRectangle mGapAreaBBox; QgsRectangle mGapAreaBBox;
QgsAbstractGeometry *mGeometry = nullptr; QgsAbstractGeometry *mGeometry = nullptr;
}; };

View File

@ -324,7 +324,7 @@ void QgsGeometryCheckerResultTab::highlightErrors( bool current )
for ( QTableWidgetItem *item : items ) for ( QTableWidgetItem *item : items )
{ {
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>(); QgsGeometryCheckError *error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
QgsVectorLayer *layer = mChecker->getContext()->featurePools[error->layerId()]->getLayer(); QgsVectorLayer *layer = !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->getLayer() : nullptr;
QgsAbstractGeometry *geometry = error->geometry(); QgsAbstractGeometry *geometry = error->geometry();
if ( ui.checkBoxHighlight->isChecked() && geometry ) if ( ui.checkBoxHighlight->isChecked() && geometry )