Skip to content

Commit

Permalink
Add QgsSingleGeometryCheck
Browse files Browse the repository at this point in the history
This class is a new subclass of QgsGeometryCheck that implements an interface that works on isolated geometries.
It has no dependency on a context, apart from a configuration that can be passed in. This makes it suitable
for checks that only work on in-memory information like self-intersection and other isValid checks.

For reference the classes QgsGeometrySelfIntersectionCheck, QgsGeometryMultipartCheck, QgsGeometryTypeCheck
and QgsGeometrySelfContactCheck have been ported to this interface.
  • Loading branch information
m-kuhn committed Sep 17, 2018
1 parent 39e1f89 commit ed86d12
Show file tree
Hide file tree
Showing 12 changed files with 413 additions and 162 deletions.
2 changes: 2 additions & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -139,6 +139,7 @@ SET(QGIS_ANALYSIS_SRCS
vector/geometry_checker/qgsgeometryanglecheck.cpp
vector/geometry_checker/qgsgeometryareacheck.cpp
vector/geometry_checker/qgsgeometrycheck.cpp
vector/geometry_checker/qgssinglegeometrycheck.cpp
vector/geometry_checker/qgsgeometrychecker.cpp
vector/geometry_checker/qgsgeometrycheckerutils.cpp
vector/geometry_checker/qgsgeometrycontainedcheck.cpp
Expand Down Expand Up @@ -268,6 +269,7 @@ SET(QGIS_ANALYSIS_HDRS
vector/geometry_checker/qgsgeometryareacheck.h
vector/geometry_checker/qgsgeometrychecker.h
vector/geometry_checker/qgsgeometrycheck.h
vector/geometry_checker/qgssinglegeometrycheck.h
vector/geometry_checker/qgsgeometrycontainedcheck.h
vector/geometry_checker/qgsgeometrydanglecheck.h
vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h
Expand Down
19 changes: 9 additions & 10 deletions src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp
Expand Up @@ -16,19 +16,18 @@
#include "qgsgeometrymultipartcheck.h"
#include "qgsfeaturepool.h"

void QgsGeometryMultipartCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
Q_UNUSED( configuration )
QList<QgsSingleGeometryCheckError *> errors;

const QgsAbstractGeometry *geom = geometry.constGet();
QgsWkbTypes::Type type = geom->wkbType();
if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) )
{
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
QgsWkbTypes::Type type = geom->wkbType();
if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) )
{
errors.append( new QgsGeometryCheckError( this, layerFeature, geom->centroid() ) );
}
errors.append( new QgsSingleGeometryCheckError( this, geometry, geom->centroid() ) );
}
return errors;
}

void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
Expand Down
Expand Up @@ -18,14 +18,14 @@
#ifndef QGS_GEOMETRY_MULTIPART_CHECK_H
#define QGS_GEOMETRY_MULTIPART_CHECK_H

#include "qgsgeometrycheck.h"
#include "qgssinglegeometrycheck.h"

class ANALYSIS_EXPORT QgsGeometryMultipartCheck : public QgsGeometryCheck
class ANALYSIS_EXPORT QgsGeometryMultipartCheck : public QgsSingleGeometryCheck
{
public:
explicit QgsGeometryMultipartCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
: QgsSingleGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) 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( "Multipart object with only one feature" ); }
Expand Down
Expand Up @@ -17,70 +17,67 @@
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"

void QgsGeometrySelfContactCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
// Test for self-contacts
int n = geom->vertexCount( iPart, iRing );
bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );
// Test for self-contacts
int n = geom->vertexCount( iPart, iRing );
bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );

// Geometry ring without duplicate nodes
QVector<int> vtxMap;
QVector<QgsPoint> ring;
vtxMap.append( 0 );
ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
for ( int i = 1; i < n; ++i )
{
QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > mContext->tolerance * mContext->tolerance )
{
vtxMap.append( i );
ring.append( p );
}
}
while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < mContext->tolerance * mContext->tolerance )
{
vtxMap.pop_back();
ring.pop_back();
}
if ( isClosed )
// Geometry ring without duplicate nodes
QVector<int> vtxMap;
QVector<QgsPoint> ring;
vtxMap.append( 0 );
ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
for ( int i = 1; i < n; ++i )
{
QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > mContext->tolerance * mContext->tolerance )
{
vtxMap.append( n - 1 );
ring.append( ring.front() );
vtxMap.append( i );
ring.append( p );
}
n = ring.size();
}
while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < mContext->tolerance * mContext->tolerance )
{
vtxMap.pop_back();
ring.pop_back();
}
if ( isClosed )
{
vtxMap.append( n - 1 );
ring.append( ring.front() );
}
n = ring.size();

// For each vertex, check whether it lies on a segment
for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
// For each vertex, check whether it lies on a segment
for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
{
const QgsPoint &p = ring[iVert];
for ( int i = 0, j = 1; j < n; i = j++ )
{
const QgsPoint &p = ring[iVert];
for ( int i = 0, j = 1; j < n; i = j++ )
if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
{
continue;
}
const QgsPoint &si = ring[i];
const QgsPoint &sj = ring[j];
QgsPoint q = QgsGeometryUtils::projectPointOnSegment( p, si, sj );
if ( QgsGeometryUtils::sqrDistance2D( p, q ) < mContext->tolerance * mContext->tolerance )
{
if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
{
continue;
}
const QgsPoint &si = ring[i];
const QgsPoint &sj = ring[j];
QgsPoint q = QgsGeometryUtils::projectPointOnSegment( p, si, sj );
if ( QgsGeometryUtils::sqrDistance2D( p, q ) < mContext->tolerance * mContext->tolerance )
{
errors.append( new QgsGeometryCheckError( this, layerFeature, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
break; // No need to report same contact on different segments multiple times
}
errors.append( new QgsSingleGeometryCheckError( this, geometry, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
break; // No need to report same contact on different segments multiple times
}
}
}
}
}
return errors;
}

void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
Expand Down
Expand Up @@ -18,14 +18,14 @@
#ifndef QGS_GEOMETRY_SELFCONTACT_CHECK_H
#define QGS_GEOMETRY_SELFCONTACT_CHECK_H

#include "qgsgeometrycheck.h"
#include "qgssinglegeometrycheck.h"

class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsGeometryCheck
class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsSingleGeometryCheck
{
public:
QgsGeometrySelfContactCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
: QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes & ) const override;
QStringList resolutionMethods() const override;
QString errorDescription() const override { return tr( "Self contact" ); }
Expand Down
Expand Up @@ -22,62 +22,48 @@
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"

bool QgsGeometrySelfIntersectionCheckError::isEqual( QgsGeometryCheckError *other ) const
bool QgsGeometrySelfIntersectionCheckError::isEqual( const QgsSingleGeometryCheckError *other ) const
{
return QgsGeometryCheckError::isEqual( other ) &&
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment1 == intersection().segment1 &&
static_cast<QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment2 == intersection().segment2;
return QgsSingleGeometryCheckError::isEqual( other ) &&
static_cast<const QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment1 == intersection().segment1 &&
static_cast<const QgsGeometrySelfIntersectionCheckError *>( other )->intersection().segment2 == intersection().segment2;
}

bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QgsGeometryCheck::Changes &changes )
bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QList<QgsGeometryCheck::Change> &changes )
{
if ( !QgsGeometryCheckError::handleChanges( changes ) )
{
if ( !QgsSingleGeometryCheckError::handleChanges( changes ) )
return false;
}
for ( const QgsGeometryCheck::Change &change : changes[layerId()].value( featureId() ) )

for ( const QgsGeometryCheck::Change &change : changes )
{
if ( change.vidx.vertex == mInter.segment1 ||
change.vidx.vertex == mInter.segment1 + 1 ||
change.vidx.vertex == mInter.segment2 ||
change.vidx.vertex == mInter.segment2 + 1 )
if ( change.vidx.vertex == mIntersection.segment1 ||
change.vidx.vertex == mIntersection.segment1 + 1 ||
change.vidx.vertex == mIntersection.segment2 ||
change.vidx.vertex == mIntersection.segment2 + 1 )
{
return false;
}
else if ( change.vidx.vertex >= 0 )
{
if ( change.vidx.vertex < mInter.segment1 )
if ( change.vidx.vertex < mIntersection.segment1 )
{
mInter.segment1 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
mIntersection.segment1 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
if ( change.vidx.vertex < mInter.segment2 )
if ( change.vidx.vertex < mIntersection.segment2 )
{
mInter.segment2 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
mIntersection.segment2 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
}
}
return true;
}


void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometrySelfIntersectionCheckError::update( const QgsSingleGeometryCheckError *other )
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::selfIntersections( geom, iPart, iRing, mContext->tolerance ) )
{
errors.append( new QgsGeometrySelfIntersectionCheckError( this, layerFeature, inter.point, QgsVertexId( iPart, iRing ), inter ) );
}
}
}
}
QgsSingleGeometryCheckError::update( other );
// Static cast since this should only get called if isEqual == true
const QgsGeometrySelfIntersectionCheckError *err = static_cast<const QgsGeometrySelfIntersectionCheckError *>( other );
mIntersection.point = err->mIntersection.point;
}

void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
Expand All @@ -89,9 +75,11 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i
error->setObsolete();
return;
}

QgsGeometry featureGeom = feature.geometry();
QgsAbstractGeometry *geom = featureGeom.get();
QgsVertexId vidx = error->vidx();

const QgsVertexId vidx = error->vidx();

// Check if ring still exists
if ( !vidx.isValid( geom ) )
Expand All @@ -100,7 +88,8 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i
return;
}

const QgsGeometryUtils::SelfIntersection &inter = static_cast<QgsGeometrySelfIntersectionCheckError *>( error )->intersection();
const QgsGeometryCheckErrorSingle *singleError = static_cast<const QgsGeometryCheckErrorSingle *>( error );
const QgsGeometryUtils::SelfIntersection &inter = static_cast<const QgsGeometrySelfIntersectionCheckError *>( singleError->singleError() )->intersection();
// Check if error still applies
bool ringIsClosed = false;
int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring, &ringIsClosed );
Expand Down Expand Up @@ -325,3 +314,22 @@ QStringList QgsGeometrySelfIntersectionCheck::resolutionMethods() const
<< tr( "No action" );
return methods;
}

QList<QgsSingleGeometryCheckError *> QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
{
Q_UNUSED( configuration )

QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::selfIntersections( geom, iPart, iRing, mContext->tolerance ) )
{
errors.append( new QgsGeometrySelfIntersectionCheckError( this, geometry, inter.point, QgsVertexId( iPart, iRing ), inter ) );
}
}
}
return errors;
}

0 comments on commit ed86d12

Please sign in to comment.