Skip to content

Commit

Permalink
Add QgsGeometryMissingVertexCheck
Browse files Browse the repository at this point in the history
A topology check for missing vertices on polygon boundaries of neighbouring polygons.
  • Loading branch information
m-kuhn committed Sep 24, 2018
1 parent 39e1f89 commit 6f0881e
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -148,6 +148,7 @@ SET(QGIS_ANALYSIS_SRCS
vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp
vector/geometry_checker/qgsgeometrygapcheck.cpp
vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp
vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp
vector/geometry_checker/qgsgeometryholecheck.cpp
vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp
vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp
Expand Down Expand Up @@ -275,6 +276,7 @@ SET(QGIS_ANALYSIS_HDRS
vector/geometry_checker/qgsgeometryduplicatenodescheck.h
vector/geometry_checker/qgsgeometryfollowboundariescheck.h
vector/geometry_checker/qgsgeometrygapcheck.h
vector/geometry_checker/qgsgeometrymissingvertexcheck.h
vector/geometry_checker/qgsgeometryholecheck.h
vector/geometry_checker/qgsgeometrylineintersectioncheck.h
vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h
Expand Down
119 changes: 119 additions & 0 deletions src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp
@@ -0,0 +1,119 @@
/***************************************************************************
qgsgeometrymissingvertexcheck.cpp
---------------------
begin : September 2018
copyright : (C) 2018 Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsgeometrymissingvertexcheck.h"

#include "qgsfeaturepool.h"
#include "qgsgeometrycollection.h"
#include "qgscurvepolygon.h"
#include "qgscurve.h"
#include "qgslinestring.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryutils.h"

void QgsGeometryMissingVertexCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
Q_UNUSED( messages );
if ( progressCounter )
progressCounter->fetchAndAddRelaxed( 1 );

QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;

QgsFeaturePool *featurePool = mContext->featurePools.value( featureIds.firstKey() );

const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, nullptr, mContext, true );

for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();

if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( geom ) )
{
processPolygon( polygon, featurePool, errors, layerFeature );
}
else if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
{
const int numGeometries = collection->numGeometries();
for ( int i = 0; i < numGeometries; ++i )
{
if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( collection->geometryN( i ) ) )
{
processPolygon( polygon, featurePool, errors, layerFeature );
}
}
}
}
}

void QgsGeometryMissingVertexCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
{
Q_UNUSED( mergeAttributeIndices );
Q_UNUSED( changes );
if ( method == NoChange )
{
error->setFixed( method );
}
}

QStringList QgsGeometryMissingVertexCheck::resolutionMethods() const
{
static QStringList methods = QStringList() << tr( "No action" );
return methods;
}

void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const
{
std::unique_ptr<QgsGeometryCollection> boundaries = qgis::make_unique<QgsGeometryCollection>();

std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->exteriorRing(), mContext->tolerance );
boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );

const int numRings = polygon->numInteriorRings();
for ( int i = 0; i < numRings; ++i )
{
geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->exteriorRing(), mContext->tolerance );
boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
}

geomEngine = QgsGeometryCheckerUtils::createGeomEngine( boundaries.get(), mContext->tolerance );

const QgsFeatureIds fids = featurePool->getIntersects( boundaries->boundingBox() );

QgsFeature feature;
for ( QgsFeatureId fid : fids )
{
if ( fid == layerFeature.feature().id() )
continue;

if ( featurePool->getFeature( fid, feature ) )
{
QgsVertexIterator vertexIterator = feature.geometry().vertices();
while ( vertexIterator.hasNext() )
{
const QgsPoint &pt = vertexIterator.next();
if ( geomEngine->intersects( &pt ) )
{
QgsVertexId vertexId;
QgsPoint closestVertex = QgsGeometryUtils::closestVertex( *polygon, pt, vertexId );

if ( closestVertex.distance( pt ) > mContext->tolerance )
{
errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( pt ) ) );
}
}
}
}
}
}
@@ -0,0 +1,52 @@
/***************************************************************************
qgsgeometrymissingvertexcheck.h
---------------------
begin : September 2018
copyright : (C) 2018 Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#define SIP_NO_FILE

#ifndef QGSGEOMETRYMISSINGVERTEXCHECK_H
#define QGSGEOMETRYMISSINGVERTEXCHECK_H

#include "qgsgeometrycheck.h"

class QgsCurvePolygon;

/**
* \ingroup analysis
*
* A topology check for missing vertices.
* Any vertex which is on the border of another polygon but no corresponding vertex
* can be found on the other polygon will be reported as an error.
*
* \since QGIS 3.4
*/
class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck
{
public:
explicit QgsGeometryMissingVertexCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( LayerCheck, {QgsWkbTypes::PolygonGeometry}, context )
{}
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 resolutionMethods() const override;
QString errorDescription() const override { return tr( "Missing Vertex" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryMissingVertexCheck" ); }

enum ResolutionMethod { NoChange };

private:
void processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const;
};

#endif // QGSGEOMETRYMISSINGVERTEXCHECK_

0 comments on commit 6f0881e

Please sign in to comment.