Skip to content

Commit

Permalink
[Geometry checker] Initial multi-layer support
Browse files Browse the repository at this point in the history
  • Loading branch information
manisandro committed Oct 23, 2017
1 parent e3fc73f commit 31cc65d
Show file tree
Hide file tree
Showing 51 changed files with 1,488 additions and 1,086 deletions.
1 change: 1 addition & 0 deletions src/plugins/geometry_checker/CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ SET (geometrychecker_SRCS
checks/qgsgeometrysegmentlengthcheck.cpp
checks/qgsgeometryselfcontactcheck.cpp
checks/qgsgeometryselfintersectioncheck.cpp
checks/qgsgeometrysliverpolygoncheck.cpp
checks/qgsgeometrytypecheck.cpp
ui/qgsgeometrycheckerdialog.cpp
ui/qgsgeometrycheckersetuptab.cpp
Expand Down
81 changes: 44 additions & 37 deletions src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp
Expand Up @@ -17,61 +17,68 @@
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"

void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
for ( const QString &layerId : featureIds.keys() )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) )
{
continue;
}
QgsGeometry g = feature.geometry();
const QgsAbstractGeometry *geom = g.geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
for ( QgsFeatureId featureid : featureIds[layerId] )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !getFeaturePool( layerId )->get( featureid, feature ) )
{
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
// Less than three points, no angles to check
if ( nVerts < 3 )
{
continue;
}
for ( int iVert = 0; iVert < nVerts; ++iVert )
continue;
}
QgsGeometry g = feature.geometry();
const QgsAbstractGeometry *geom = g.geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
QgsVector v21, v23;
try
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing );
// Less than three points, no angles to check
if ( nVerts < 3 )
{
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
}
catch ( const QgsException & )
{
// Zero length vectors
continue;
}

double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
if ( angle < mMinAngle )
for ( int iVert = 0; iVert < nVerts; ++iVert )
{
errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
QgsVector v21, v23;
try
{
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized();
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized();
}
catch ( const QgsException & )
{
// Zero length vectors
continue;
}

double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
if ( angle < mMinAngle )
{
errors.append( new QgsGeometryCheckError( this, layerId, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
}
}
}
}
}
}
}

void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
Expand Down Expand Up @@ -132,15 +139,15 @@ void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method,
}
else
{
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance()
&& QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) &&
geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2
{
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) );
}
feature.setGeometry( g );
mFeaturePool->updateFeature( feature );
getFeaturePool( error->layerId() )->updateFeature( feature );
error->setFixed( method );
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h
Expand Up @@ -23,12 +23,12 @@ class QgsGeometryAngleCheck : public QgsGeometryCheck
Q_OBJECT

public:
QgsGeometryAngleCheck( QgsFeaturePool *featurePool, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, featurePool )
, mMinAngle( minAngle )
QgsGeometryAngleCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools )
, mMinAngle( minAngle )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override;
void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override;
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Minimal angle" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); }
Expand Down
81 changes: 48 additions & 33 deletions src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp
Expand Up @@ -18,47 +18,54 @@
#include "qgsgeometryareacheck.h"
#include "../utils/qgsfeaturepool.h"

void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
for ( const QString &layerId : featureIds.keys() )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) )
{
continue;
}

QgsGeometry g = feature.geometry();
QgsAbstractGeometry *geom = g.geometry();
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
for ( QgsFeatureId featureid : featureIds[layerId] )
{
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( geom );
for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i )
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !getFeaturePool( layerId )->get( featureid, feature ) )
{
double value;
if ( checkThreshold( multiGeom->geometryN( i ), value ) )
continue;
}

QgsGeometry g = feature.geometry();
QgsAbstractGeometry *geom = g.geometry();
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
{
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( geom );
for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i )
{
errors.append( new QgsGeometryCheckError( this, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) );
double value;
if ( checkThreshold( layerId, multiGeom->geometryN( i ), value ) )
{
errors.append( new QgsGeometryCheckError( this, layerId, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) );
}
}
}
}
else
{
double value;
if ( checkThreshold( geom, value ) )
else
{
errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) );
double value;
if ( checkThreshold( layerId, geom, value ) )
{
errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) );
}
}
}
}
}

void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
Expand All @@ -78,7 +85,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
{
double value;
if ( !checkThreshold( static_cast<QgsGeometryCollection *>( geom )->geometryN( vidx.part ), value ) )
if ( !checkThreshold( error->layerId(), static_cast<QgsGeometryCollection *>( geom )->geometryN( vidx.part ), value ) )
{
error->setObsolete();
return;
Expand All @@ -87,7 +94,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
else
{
double value;
if ( !checkThreshold( geom, value ) )
if ( !checkThreshold( error->layerId(), geom, value ) )
{
error->setObsolete();
return;
Expand All @@ -101,13 +108,13 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
}
else if ( method == Delete )
{
deleteFeatureGeometryPart( feature, vidx.part, changes );
deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes );
error->setFixed( method );
}
else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
{
QString errMsg;
if ( mergeWithNeighbor( feature, vidx.part, method, mergeAttributeIndex, changes, errMsg ) )
if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) )
{
error->setFixed( method );
}
Expand All @@ -122,20 +129,28 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i
}
}

bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
bool QgsGeometryAreaCheck::checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const
{
value = geom->area();
double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits();
double threshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
return value < threshold;
}

bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
{
double maxVal = 0.;
QgsFeature mergeFeature;
int mergePartIdx = -1;
bool matchFound = false;
QgsGeometry g = feature.geometry();;
QgsGeometry g = feature.geometry();
QgsAbstractGeometry *geom = g.geometry();

// Search for touching neighboring geometries
Q_FOREACH ( QgsFeatureId testId, mFeaturePool->getIntersects( g.boundingBox() ) )
for ( QgsFeatureId testId : getFeaturePool( layerId )->getIntersects( g.boundingBox() ) )
{
QgsFeature testFeature;
if ( !mFeaturePool->get( testId, testFeature ) )
if ( !getFeaturePool( layerId )->get( testId, testFeature ) )
{
continue;
}
Expand Down Expand Up @@ -210,9 +225,9 @@ bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx,
{
--mergePartIdx;
}
replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes );
replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes );
// Remove polygon from source geometry
deleteFeatureGeometryPart( feature, partIdx, changes );
deleteFeatureGeometryPart( layerId, feature, partIdx, changes );

return true;
}
Expand Down
16 changes: 8 additions & 8 deletions src/plugins/geometry_checker/checks/qgsgeometryareacheck.h
Expand Up @@ -25,23 +25,23 @@ class QgsGeometryAreaCheck : public QgsGeometryCheck
Q_OBJECT

public:
QgsGeometryAreaCheck( QgsFeaturePool *featurePool, double threshold )
: QgsGeometryCheck( FeatureCheck, featurePool )
, mThreshold( threshold )
QgsGeometryAreaCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double thresholdMapUnits )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools )
, mThresholdMapUnits( thresholdMapUnits )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override;
void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override;
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Minimal area" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryAreaCheck" ); }
private:
enum ResolutionMethod { MergeLongestEdge, MergeLargestArea, MergeIdenticalAttribute, Delete, NoChange };

bool mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const;
virtual bool checkThreshold( const QgsAbstractGeometry *geom, double &value ) const { value = geom->area(); return value < mThreshold; }
virtual bool checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const;
bool mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const;

protected:
double mThreshold;
double mThresholdMapUnits;
};

#endif // QGS_GEOMETRY_AREA_CHECK_H

0 comments on commit 31cc65d

Please sign in to comment.