Skip to content

Commit

Permalink
[Geometry checker] Initial multilayer support for overlap check
Browse files Browse the repository at this point in the history
  • Loading branch information
manisandro committed Oct 23, 2017
1 parent dd12b13 commit 3d8ffcb
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 68 deletions.
155 changes: 94 additions & 61 deletions src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,73 +13,92 @@
* *
***************************************************************************/

#include "qgscrscache.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryoverlapcheck.h"
#include "../utils/qgsfeaturepool.h"

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;
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 ];
if ( !getCompatibility( featurePool->getLayer()->geometryType() ) )
QString layerIdA = layerIds[i];
QgsFeaturePool *featurePoolA = mContext->featurePools[ layerIdA ];
if ( !getCompatibility( featurePoolA->getLayer()->geometryType() ) )
{
continue;
}
double mapToLayerUnits = featurePool->getMapToLayerUnits();
double overlapThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
for ( QgsFeatureId featureid : featureIds[layerId] )
QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );

for ( QgsFeatureId featureIdA : featureIds[layerIdA] )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !featurePool->get( featureid, feature ) )
QgsFeature featureA;
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;
}
QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
featureGeomA->transform( crstA );
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->tolerance );
QgsRectangle bboxA = featureGeomA->boundingBox();

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

QString errMsg;
if ( geomEngine->overlaps( *otherFeature.geometry().geometry(), &errMsg ) )
QgsFeatureIds idsB = featurePoolB->getIntersects( crstB.transform( bboxA, QgsCoordinateTransform::ReverseTransform ) );
for ( QgsFeatureId featureIdB : idsB )
{
QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() );
if ( interGeom && !interGeom->isEmpty() )
// > : only report overlaps within same layer once
if ( layerIdA == layerIdB && featureIdB >= featureIdA )
{
continue;
}
QgsFeature featureB;
if ( !featurePoolB->get( featureIdB, featureB ) )
{
QgsGeometryCheckerUtils::filter1DTypes( interGeom );
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
continue;
}
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
featureGeomB->transform( crstB );
QString errMsg;
if ( geomEngineA->overlaps( *featureGeomB, &errMsg ) )
{
QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB );
if ( interGeom && !interGeom->isEmpty() )
{
double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
if ( area > mContext->reducedTolerance && area < overlapThreshold )
QgsGeometryCheckerUtils::filter1DTypes( interGeom );
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
{
errors.append( new QgsGeometryOverlapCheckError( this, layerId, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) );
double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area();
if ( area > mContext->reducedTolerance && area < overlapThreshold )
{
errors.append( new QgsGeometryOverlapCheckError( this, layerIdA, featureIdA, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, qMakePair( layerIdB, featureIdB ) ) );
}
}
}
else if ( !errMsg.isEmpty() )
{
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;
}
else if ( !errMsg.isEmpty() )
{
messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
}
delete interGeom;
delete featureGeomB;
}
}
delete geomEngine;
delete geomEngineA;
delete featureGeomA;
}
}
}
Expand All @@ -89,30 +108,41 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
QString errMsg;
QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error );

QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeature feature;
QgsFeature otherFeature;
if ( !featurePool->get( error->featureId(), feature ) ||
!featurePool->get( overlapError->otherId(), otherFeature ) )
QgsFeaturePool *featurePoolA = mContext->featurePools[ overlapError->layerId() ];
QgsFeaturePool *featurePoolB = mContext->featurePools[ overlapError->overlappedFeature().first ];
QgsFeature featureA;
QgsFeature featureB;
if ( !featurePoolA->get( overlapError->featureId(), featureA ) ||
!featurePoolB->get( overlapError->overlappedFeature().second, featureB ) )
{
error->setObsolete();
return;
}
QgsGeometry featureGeom = feature.geometry();
QgsAbstractGeometry *geom = featureGeom.geometry();
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->tolerance );
QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );

// 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();
return;
}
QgsAbstractGeometry *interGeom = geomEngine->intersection( otherFeature.geometry().geometry(), &errMsg );
delete geomEngine;
QgsAbstractGeometry *interGeom = geomEngineA->intersection( *featureGeomB, &errMsg );
if ( !interGeom )
{
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) );
return;
}
Expand All @@ -132,6 +162,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
if ( !interPart || interPart->isEmpty() )
{
delete interGeom;
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
error->setObsolete();
return;
}
Expand All @@ -143,9 +176,7 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
}
else if ( method == Subtract )
{
geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, mContext->reducedTolerance );
QgsAbstractGeometry *diff1 = geomEngine->difference( *interPart, &errMsg );
delete geomEngine;
QgsAbstractGeometry *diff1 = geomEngineA->difference( *interPart, &errMsg );
if ( !diff1 || diff1->isEmpty() )
{
delete diff1;
Expand All @@ -155,10 +186,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
{
QgsGeometryCheckerUtils::filter1DTypes( diff1 );
}
QgsGeometry otherFeatureGeom = otherFeature.geometry();
QgsGeometryEngine *otherGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( otherFeatureGeom.geometry(), mContext->reducedTolerance );
QgsAbstractGeometry *diff2 = otherGeomEngine->difference( *interPart, &errMsg );
delete otherGeomEngine;
QgsGeometryEngine *geomEngineB = QgsGeometryCheckerUtils::createGeomEngine( featureGeomB, mContext->reducedTolerance );
QgsAbstractGeometry *diff2 = geomEngineB->difference( *interPart, &errMsg );
delete geomEngineB;
if ( !diff2 || diff2->isEmpty() )
{
delete diff2;
Expand All @@ -178,19 +208,19 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
{
if ( shared1 < shared2 )
{
feature.setGeometry( QgsGeometry( diff1 ) );
featureA.setGeometry( QgsGeometry( diff1 ) );

changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
featurePool->updateFeature( feature );
changes[error->layerId()][featureA.id()].append( Change( ChangeFeature, ChangeChanged ) );
featurePoolA->updateFeature( featureA );

delete diff2;
}
else
{
otherFeature.setGeometry( QgsGeometry( diff2 ) );
featureB.setGeometry( QgsGeometry( diff2 ) );

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

delete diff1;
}
Expand All @@ -203,6 +233,9 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method
error->setFixFailed( tr( "Unknown method" ) );
}
delete interGeom;
delete geomEngineA;
delete featureGeomA;
delete featureGeomB;
}

QStringList QgsGeometryOverlapCheck::getResolutionMethods() const
Expand Down
14 changes: 7 additions & 7 deletions src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,33 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
QgsFeatureId featureId,
const QgsPoint &errorLocation,
const QVariant &value,
QgsFeatureId otherId )
const QPair<QString, QgsFeatureId> &overlappedFeature )
: 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
{
QgsGeometryOverlapCheckError *err = dynamic_cast<QgsGeometryOverlapCheckError *>( other );
return err &&
other->layerId() == layerId() &&
other->featureId() == featureId() &&
err->otherId() == otherId() &&
err->overlappedFeature() == overlappedFeature() &&
QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), mCheck->getContext()->reducedTolerance ) &&
qAbs( value().toDouble() - other->value().toDouble() ) < mCheck->getContext()->reducedTolerance;
}

bool closeMatch( QgsGeometryCheckError *other ) const override
{
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:
QgsFeatureId mOtherId;
QPair<QString, QgsFeatureId> mOverlappedFeature;
};

class QgsGeometryOverlapCheck : public QgsGeometryCheck
Expand Down

0 comments on commit 3d8ffcb

Please sign in to comment.