Skip to content

Commit

Permalink
[Geometry checker] Initial multilayer support for duplicate check
Browse files Browse the repository at this point in the history
  • Loading branch information
manisandro committed Oct 23, 2017
1 parent 3d8ffcb commit 55cf125
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 55 deletions.
131 changes: 83 additions & 48 deletions src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.cpp
Expand Up @@ -13,6 +13,7 @@
* *
***************************************************************************/

#include "qgscrscache.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryduplicatecheck.h"
#include "qgsspatialindex.h"
Expand All @@ -22,65 +23,88 @@
void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
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;
}
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();
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), mContext->tolerance );

QList<QgsFeatureId> duplicates;
QgsFeatureIds ids = featurePool->getIntersects( featureGeom.geometry()->boundingBox() );
for ( QgsFeatureId id : ids )
QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
featureGeomA->transform( crstA );
QgsGeometryEngine *geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->tolerance );
QgsRectangle bboxA = featureGeomA->boundingBox();

QMap<QString, QList<QgsFeatureId>> duplicates;

for ( int j = i; j < n; ++j )
{
// > : only report overlaps once
if ( id >= featureid )
{
continue;
}
QgsFeature testFeature;
if ( !featurePool->get( id, testFeature ) )
QString layerIdB = layerIds[j];
QgsFeaturePool *featurePoolB = mContext->featurePools[ layerIdA ];
if ( !getCompatibility( featurePoolB->getLayer()->geometryType() ) )
{
continue;
}
QString errMsg;
QgsAbstractGeometry *diffGeom = geomEngine->symDifference( *testFeature.geometry().geometry(), &errMsg );
if ( diffGeom && diffGeom->area() < mContext->tolerance )
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );

QgsFeatureIds idsB = featurePoolB->getIntersects( crstB.transform( bboxA, QgsCoordinateTransform::ReverseTransform ) );
for ( QgsFeatureId featureIdB : idsB )
{
duplicates.append( id );
// > : 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;
QgsAbstractGeometry *diffGeom = geomEngineA->symDifference( *featureGeomB, &errMsg );
if ( diffGeom && diffGeom->area() < mContext->tolerance )
{
duplicates[layerIdB].append( featureIdB );
}
else if ( !diffGeom )
{
messages.append( tr( "Duplicate check between features %1:%2 and %3:%4 %5" ).arg( layerIdA ).arg( featureA.id() ).arg( layerIdB ).arg( featureB.id() ).arg( errMsg ) );
}
delete diffGeom;
delete featureGeomB;
}
else if ( !diffGeom )
if ( !duplicates.isEmpty() )
{
messages.append( tr( "Duplicate check between features %1 and %2: %3" ).arg( feature.id() ).arg( testFeature.id() ).arg( errMsg ) );
errors.append( new QgsGeometryDuplicateCheckError( this, layerIdA, featureIdA, featureA.geometry().geometry()->centroid(), duplicates ) );
}
delete diffGeom;
delete geomEngineA;
delete featureGeomA;
}
if ( !duplicates.isEmpty() )
{
std::sort( duplicates.begin(), duplicates.end() );
errors.append( new QgsGeometryDuplicateCheckError( this, layerId, featureid, feature.geometry().geometry()->centroid(), duplicates ) );
}
delete geomEngine;

}
}
}

void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeature feature;
if ( !featurePool->get( error->featureId(), feature ) )
QgsFeaturePool *featurePoolA = mContext->featurePools[ error->layerId() ];
QgsFeature featureA;
if ( !featurePoolA->get( error->featureId(), featureA ) )
{
error->setObsolete();
return;
Expand All @@ -92,27 +116,38 @@ void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int meth
}
else if ( method == RemoveDuplicates )
{
QgsGeometry featureGeom = feature.geometry();
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), mContext->tolerance );
QgsCoordinateTransform crstA = QgsCoordinateTransformCache::instance()->transform( featurePoolA->getLayer()->crs().authid(), mContext->mapCrs );
QgsAbstractGeometry *featureGeomA = featureA.geometry().geometry()->clone();
featureGeomA->transform( crstA );
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeomA, mContext->tolerance );

QgsGeometryDuplicateCheckError *duplicateError = static_cast<QgsGeometryDuplicateCheckError *>( error );
for ( QgsFeatureId id : duplicateError->duplicates() )
for ( const QString &layerIdB : duplicateError->duplicates().keys() )
{
QgsFeature testFeature;
if ( !featurePool->get( id, testFeature ) )
QgsFeaturePool *featurePoolB = mContext->featurePools[ layerIdB ];
QgsCoordinateTransform crstB = QgsCoordinateTransformCache::instance()->transform( featurePoolB->getLayer()->crs().authid(), mContext->mapCrs );
for ( QgsFeatureId idB : duplicateError->duplicates()[layerIdB] )
{
continue;
}
QgsAbstractGeometry *diffGeom = geomEngine->symDifference( *testFeature.geometry().geometry() );
if ( diffGeom && diffGeom->area() < mContext->tolerance )
{
featurePool->deleteFeature( testFeature );
changes[error->layerId()][id].append( Change( ChangeFeature, ChangeRemoved ) );
}
QgsFeature featureB;
if ( !featurePoolB->get( idB, featureB ) )
{
continue;
}
QgsAbstractGeometry *featureGeomB = featureB.geometry().geometry()->clone();
featureGeomB->transform( crstB );
QgsAbstractGeometry *diffGeom = geomEngine->symDifference( *featureGeomB );
if ( diffGeom && diffGeom->area() < mContext->tolerance )
{
featurePoolB->deleteFeature( featureB );
changes[layerIdB][idB].append( Change( ChangeFeature, ChangeRemoved ) );
}

delete diffGeom;
delete diffGeom;
delete featureGeomB;
}
}
delete geomEngine;
delete featureGeomA;
error->setFixed( method );
}
else
Expand Down
20 changes: 13 additions & 7 deletions src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.h
Expand Up @@ -25,11 +25,11 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError
const QString &layerId,
QgsFeatureId featureId,
const QgsPoint &errorLocation,
const QList<QgsFeatureId> &duplicates )
const QMap<QString, QList<QgsFeatureId>> &duplicates )
: QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) )
, mDuplicates( duplicates )
{ }
QList<QgsFeatureId> duplicates() const { return mDuplicates; }
QMap<QString, QList<QgsFeatureId>> duplicates() const { return mDuplicates; }

bool isEqual( QgsGeometryCheckError *other ) const override
{
Expand All @@ -41,16 +41,22 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError
}

private:
QList<QgsFeatureId> mDuplicates;
QMap<QString, QList<QgsFeatureId>> mDuplicates;

static inline QString duplicatesString( const QList<QgsFeatureId> &duplicates )
static inline QString duplicatesString( const QMap<QString, QList<QgsFeatureId>> &duplicates )
{
QStringList str;
for ( QgsFeatureId id : duplicates )
for ( const QString &layerId : duplicates.keys() )
{
str.append( QString::number( id ) );
str.append( layerId + ": " );
QStringList ids;
for ( QgsFeatureId id : duplicates[layerId] )
{
ids.append( QString::number( id ) );
}
str.append( ids.join( ", " ) );
}
return str.join( QStringLiteral( ", " ) );
return str.join( QStringLiteral( "; " ) );
}
};

Expand Down

0 comments on commit 55cf125

Please sign in to comment.