1
0
mirror of https://github.com/qgis/QGIS.git synced 2025-04-08 00:05:32 -04:00

[Geometry checker] Initial multilayer support for overlap check

This commit is contained in:
Sandro Mani 2017-04-05 15:05:39 +02:00
parent dd12b132c5
commit 3d8ffcb184
2 changed files with 101 additions and 68 deletions
src/plugins/geometry_checker/checks

@ -13,53 +13,69 @@
* * * *
***************************************************************************/ ***************************************************************************/
#include "qgscrscache.h"
#include "qgsgeometryengine.h" #include "qgsgeometryengine.h"
#include "qgsgeometryoverlapcheck.h" #include "qgsgeometryoverlapcheck.h"
#include "../utils/qgsfeaturepool.h" #include "../utils/qgsfeaturepool.h"
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{ {
double overlapThreshold = mThresholdMapUnits;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
for ( const QString &layerId : featureIds.keys() ) QList<QString> layerIds = featureIds.keys();
for ( int i = 0, n = layerIds.length(); i < n; ++i )
{ {
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; QString layerIdA = layerIds[i];
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) ) QgsFeaturePool *featurePoolA = mContext->featurePools[ layerIdA ];
if ( !getCompatibility( featurePoolA->getLayer()->geometryType() ) )
{ {
continue; continue;
} }
double mapToLayerUnits = featurePool->getMapToLayerUnits(); QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
double overlapThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
for ( QgsFeatureId featureid : featureIds[layerId] ) for ( QgsFeatureId featureIdA : featureIds[layerIdA] )
{ {
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature; QgsFeature featureA;
if ( !featurePool->get( featureid, feature ) ) if ( !featurePoolA->get( featureIdA, featureA ) )
{
continue;
}
QgsGeometry featureGeom = feature.geometry();
QgsAbstractGeometry *geom = featureGeom.geometry();
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->tolerance );
QgsFeatureIds ids = featurePool->getIntersects( feature.geometry().boundingBox() );
for ( QgsFeatureId otherid : ids )
{
// >= : only report overlaps once
if ( otherid >= featureid )
{ {
continue; continue;
} }
QgsFeature otherFeature; QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
if ( !featurePool->get( otherid, otherFeature ) ) featureGeomA->transform( crstA );
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->tolerance );
QgsRectangle bboxA = featureGeomA->boundingBox();
for ( int j = i; j < n; ++j )
{
QString layerIdB = layerIds[j];
QgsFeaturePool *featurePoolB = mContext->featurePools[ layerIdA ];
if ( !getCompatibility( featurePoolB->getLayer()->geometryType() ) )
{ {
continue; continue;
} }
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );
QgsFeatureIds idsB = featurePoolB->getIntersects( crstB.transform( bboxA, QgsCoordinateTransform::ReverseTransform ) );
for ( QgsFeatureId featureIdB : idsB )
{
// > : only report overlaps within same layer once
if ( layerIdA == layerIdB && featureIdB >= featureIdA )
{
continue;
}
QgsFeature featureB;
if ( !featurePoolB->get( featureIdB, featureB ) )
{
continue;
}
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
featureGeomB->transform( crstB );
QString errMsg; QString errMsg;
if ( geomEngine->overlaps( *otherFeature.geometry().geometry(), &errMsg ) ) if ( geomEngineA->overlaps( *featureGeomB, &errMsg ) )
{ {
QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() ); QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB );
if ( interGeom && !interGeom->isEmpty() ) if ( interGeom && !interGeom->isEmpty() )
{ {
QgsGeometryCheckerUtils::filter1DTypes( interGeom ); QgsGeometryCheckerUtils::filter1DTypes( interGeom );
@ -68,18 +84,21 @@ void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &err
double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area(); double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
if ( area > mContext->reducedTolerance && area < overlapThreshold ) if ( area > mContext->reducedTolerance && area < overlapThreshold )
{ {
errors.append( new QgsGeometryOverlapCheckError( this, layerId, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) ); errors.append( new QgsGeometryOverlapCheckError( this, layerIdA, featureIdA, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, qMakePair( layerIdB, featureIdB ) ) );
} }
} }
} }
else if ( !errMsg.isEmpty() ) else if ( !errMsg.isEmpty() )
{ {
messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) ); messages.append( tr( "Overlap check between features %1:%2 and %3:%4 %5" ).arg( layerIdA ).arg( featureIdA ).arg( layerIdB ).arg( featureIdB ).arg( errMsg ) );
} }
delete interGeom; delete interGeom;
} }
delete featureGeomB;
} }
delete geomEngine; }
delete geomEngineA;
delete featureGeomA;
} }
} }
} }
@ -89,30 +108,41 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
QString errMsg; QString errMsg;
QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error ); QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error );
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeaturePool *featurePoolA = mContext->featurePools[ overlapError->layerId() ];
QgsFeature feature; QgsFeaturePool *featurePoolB = mContext->featurePools[ overlapError->overlappedFeature().first ];
QgsFeature otherFeature; QgsFeature featureA;
if ( !featurePool->get( error->featureId(), feature ) || QgsFeature featureB;
!featurePool->get( overlapError->otherId(), otherFeature ) ) if ( !featurePoolA->get( overlapError->featureId(), featureA ) ||
!featurePoolB->get( overlapError->overlappedFeature().second, featureB ) )
{ {
error->setObsolete(); error->setObsolete();
return; return;
} }
QgsGeometry featureGeom = feature.geometry(); QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
QgsAbstractGeometry *geom = featureGeom.geometry(); QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->tolerance );
// Check if error still applies // Check if error still applies
if ( !geomEngine->overlaps( otherFeature.geometry().geometry() ) ) QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
featureGeomA->transform( crstA );
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->reducedTolerance );
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
featureGeomB->transform( crstB );
if ( !geomEngineA->overlaps( *featureGeomB ) )
{ {
delete geomEngine; delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
error->setObsolete(); error->setObsolete();
return; return;
} }
QgsAbstractGeometry *interGeom = geomEngine->intersection( otherFeature.geometry().geometry(), &errMsg ); QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB, &errMsg );
delete geomEngine;
if ( !interGeom ) if ( !interGeom )
{ {
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) ); error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) );
return; return;
} }
@ -132,6 +162,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
if ( !interPart || interPart->isEmpty() ) if ( !interPart || interPart->isEmpty() )
{ {
delete interGeom; delete interGeom;
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
error->setObsolete(); error->setObsolete();
return; return;
} }
@ -143,9 +176,7 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
} }
else if ( method == Subtract ) else if ( method == Subtract )
{ {
geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->reducedTolerance ); QgsAbstractGeometry *diff1 = geomEngineA->difference( *interPart, &errMsg );
QgsAbstractGeometry *diff1 = geomEngine->difference( *interPart, &errMsg );
delete geomEngine;
if ( !diff1 || diff1->isEmpty() ) if ( !diff1 || diff1->isEmpty() )
{ {
delete diff1; delete diff1;
@ -155,10 +186,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
{ {
QgsGeometryCheckerUtils::filter1DTypes( diff1 ); QgsGeometryCheckerUtils::filter1DTypes( diff1 );
} }
QgsGeometry otherFeatureGeom = otherFeature.geometry(); QgsGeometryEngine *geomEngineB = QgsGeometryCheckerUtils::createGeomEngine( featureGeomB, mContext->reducedTolerance );
QgsGeometryEngine *otherGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( otherFeatureGeom.geometry(), mContext->reducedTolerance ); QgsAbstractGeometry *diff2 = geomEngineB->difference( *interPart, &errMsg );
QgsAbstractGeometry *diff2 = otherGeomEngine->difference( *interPart, &errMsg ); delete geomEngineB;
delete otherGeomEngine;
if ( !diff2 || diff2->isEmpty() ) if ( !diff2 || diff2->isEmpty() )
{ {
delete diff2; delete diff2;
@ -178,19 +208,19 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
{ {
if ( shared1 < shared2 ) if ( shared1 < shared2 )
{ {
feature.setGeometry( QgsGeometry( diff1 ) ); featureA.setGeometry( QgsGeometry( diff1 ) );
changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); changes[error->layerId()][featureA.id()].append( Change( ChangeFeature, ChangeChanged ) );
featurePool->updateFeature( feature ); featurePoolA->updateFeature( featureA );
delete diff2; delete diff2;
} }
else else
{ {
otherFeature.setGeometry( QgsGeometry( diff2 ) ); featureB.setGeometry( QgsGeometry( diff2 ) );
changes[error->layerId()][otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) ); changes[overlapError->overlappedFeature().first][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) );
featurePool->updateFeature( otherFeature ); featurePoolB->updateFeature( featureB );
delete diff1; delete diff1;
} }
@ -203,6 +233,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
error->setFixFailed( tr( "Unknown method" ) ); error->setFixFailed( tr( "Unknown method" ) );
} }
delete interGeom; delete interGeom;
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
} }
QStringList QgsGeometryOverlapCheck::getResolutionMethods() const QStringList QgsGeometryOverlapCheck::getResolutionMethods() const

@ -26,11 +26,11 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
QgsFeatureId featureId, QgsFeatureId featureId,
const QgsPoint &errorLocation, const QgsPoint &errorLocation,
const QVariant &value, const QVariant &value,
QgsFeatureId otherId ) const QPair<QString, QgsFeatureId> &overlappedFeature )
: QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), value, ValueArea ) : QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), value, ValueArea )
, mOtherId( otherId ) , mOverlappedFeature( overlappedFeature )
{ } { }
QgsFeatureId otherId() const { return mOtherId; } const QPair<QString, QgsFeatureId> &overlappedFeature() const { return mOverlappedFeature; }
bool isEqual( QgsGeometryCheckError *other ) const override bool isEqual( QgsGeometryCheckError *other ) const override
{ {
@ -38,7 +38,7 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
return err && return err &&
other->layerId() == layerId() && other->layerId() == layerId() &&
other->featureId() == featureId() && other->featureId() == featureId() &&
err->otherId() == otherId() && err->overlappedFeature() == overlappedFeature() &&
QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), mCheck->getContext()->reducedTolerance ) && QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), mCheck->getContext()->reducedTolerance ) &&
qAbs( value().toDouble() - other->value().toDouble() ) < mCheck->getContext()->reducedTolerance; qAbs( value().toDouble() - other->value().toDouble() ) < mCheck->getContext()->reducedTolerance;
} }
@ -46,13 +46,13 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
bool closeMatch( QgsGeometryCheckError *other ) const override bool closeMatch( QgsGeometryCheckError *other ) const override
{ {
QgsGeometryOverlapCheckError *err = dynamic_cast<QgsGeometryOverlapCheckError *>( other ); QgsGeometryOverlapCheckError *err = dynamic_cast<QgsGeometryOverlapCheckError *>( other );
return err && other->layerId() == layerId() && other->featureId() == featureId() && err->otherId() == otherId(); return err && other->layerId() == layerId() && other->featureId() == featureId() && err->overlappedFeature() == overlappedFeature();
} }
virtual QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1" ).arg( otherId() ); } virtual QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1:%2" ).arg( mOverlappedFeature.first ).arg( mOverlappedFeature.second ); }
private: private:
QgsFeatureId mOtherId; QPair<QString, QgsFeatureId> mOverlappedFeature;
}; };
class QgsGeometryOverlapCheck : public QgsGeometryCheck class QgsGeometryOverlapCheck : public QgsGeometryCheck