Skip to content

Commit

Permalink
Merge pull request #7932 from m-kuhn/single_geometry_check
Browse files Browse the repository at this point in the history
Add QgsSingleGeometryCheck
  • Loading branch information
m-kuhn authored Sep 19, 2018
2 parents 3cb82a5 + a0283eb commit d129c2c
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 194 deletions.
2 changes: 2 additions & 0 deletions src/analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
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
37 changes: 37 additions & 0 deletions src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,32 @@ QgsRectangle QgsGeometryCheckError::affectedAreaBBox() const
return mGeometry.boundingBox();
}

void QgsGeometryCheckError::setFixed( int method )
{
mStatus = StatusFixed;
const QStringList methods = mCheck->resolutionMethods();
mResolutionMessage = methods[method];
}

void QgsGeometryCheckError::setFixFailed( const QString &reason )
{
mStatus = StatusFixFailed;
mResolutionMessage = reason;
}

bool QgsGeometryCheckError::isEqual( QgsGeometryCheckError *other ) const
{
return other->check() == check() &&
other->layerId() == layerId() &&
other->featureId() == featureId() &&
other->vidx() == vidx();
}

bool QgsGeometryCheckError::closeMatch( QgsGeometryCheckError * ) const
{
return false;
}

bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes &changes )
{
if ( status() == StatusObsolete )
Expand Down Expand Up @@ -277,3 +303,14 @@ void QgsGeometryCheck::deleteFeatureGeometryRing( const QString &layerId, QgsFea
deleteFeatureGeometryPart( layerId, feature, partIdx, changes );
}
}

void QgsGeometryCheckError::update( const QgsGeometryCheckError *other )
{
Q_ASSERT( mCheck == other->mCheck );
Q_ASSERT( mLayerId == other->mLayerId );
Q_ASSERT( mFeatureId == other->mFeatureId );
mErrorLocation = other->mErrorLocation;
mVidx = other->mVidx;
mValue = other->mValue;
mGeometry = other->mGeometry;
}
56 changes: 24 additions & 32 deletions src/analysis/vector/geometry_checker/qgsgeometrycheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,40 +157,32 @@ class ANALYSIS_EXPORT QgsGeometryCheckError
const QgsVertexId &vidx() const { return mVidx; }
Status status() const { return mStatus; }
QString resolutionMessage() const { return mResolutionMessage; }
void setFixed( int method )
{
mStatus = StatusFixed;
const QStringList methods = mCheck->resolutionMethods();
mResolutionMessage = methods[method];
}
void setFixFailed( const QString &reason )
{
mStatus = StatusFixFailed;
mResolutionMessage = reason;
}
void setFixed( int method );
void setFixFailed( const QString &reason );
void setObsolete() { mStatus = StatusObsolete; }
virtual bool isEqual( QgsGeometryCheckError *other ) const
{
return other->check() == check() &&
other->layerId() == layerId() &&
other->featureId() == featureId() &&
other->vidx() == vidx();
}
virtual bool closeMatch( QgsGeometryCheckError * /*other*/ ) const
{
return false;
}
virtual void update( const QgsGeometryCheckError *other )
{
Q_ASSERT( mCheck == other->mCheck );
Q_ASSERT( mLayerId == other->mLayerId );
Q_ASSERT( mFeatureId == other->mFeatureId );
mErrorLocation = other->mErrorLocation;
mVidx = other->mVidx;
mValue = other->mValue;
mGeometry = other->mGeometry;
}

/**
* Check if this error is equal to \a other.
* Is reimplemented by subclasses with additional information, comparison
* of base information is done in parent class.
*/
virtual bool isEqual( QgsGeometryCheckError *other ) const;

/**
* Check if this error is almost equal to \a other.
* If this returns true, it can be used to update existing errors after re-checking.
*/
virtual bool closeMatch( QgsGeometryCheckError * /*other*/ ) const;

/**
* Update this error with the information from \other.
* Will be used to update existing errors whenever they are re-checked.
*/
virtual void update( const QgsGeometryCheckError *other );

/**
* Apply a list of \a changes.
*/
virtual bool handleChanges( const QgsGeometryCheck::Changes &changes );

protected:
Expand Down
19 changes: 9 additions & 10 deletions src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp
Original file line number Diff line number Diff line change
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, geometry ) );
}
return errors;
}

void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,70 +17,68 @@
#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 )
Q_UNUSED( configuration )
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, QgsGeometry( p.clone() ), 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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit d129c2c

Please sign in to comment.