From 7fb1c5515bb9d8b335ade2bf8935a3b46c1f4a23 Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Tue, 27 Jun 2017 13:48:23 +0200 Subject: [PATCH] [Geometry checker] Add dangle check --- src/plugins/geometry_checker/CMakeLists.txt | 2 + .../checks/qgsgeometrydanglecheck.cpp | 109 ++++++++++++++++++ .../checks/qgsgeometrydanglecheck.h | 38 ++++++ .../qgsgeometrycheckfactory.cpp | 29 +++++ .../ui/qgsgeometrycheckersetuptab.ui | 9 +- .../utils/qgsgeometrycheckerutils.cpp | 16 +++ .../utils/qgsgeometrycheckerutils.h | 2 + 7 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp create mode 100644 src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h diff --git a/src/plugins/geometry_checker/CMakeLists.txt b/src/plugins/geometry_checker/CMakeLists.txt index f90c2869997..08800440531 100644 --- a/src/plugins/geometry_checker/CMakeLists.txt +++ b/src/plugins/geometry_checker/CMakeLists.txt @@ -8,6 +8,7 @@ SET (geometrychecker_SRCS qgsgeometrycheckfactory.cpp checks/qgsgeometryanglecheck.cpp checks/qgsgeometryareacheck.cpp + checks/qgsgeometrydanglecheck.cpp checks/qgsgeometrydegeneratepolygoncheck.cpp checks/qgsgeometryduplicatecheck.cpp checks/qgsgeometryduplicatenodescheck.cpp @@ -45,6 +46,7 @@ SET (geometrychecker_MOC_HDRS checks/qgsgeometryanglecheck.h checks/qgsgeometryareacheck.h checks/qgsgeometrycheck.h + checks/qgsgeometrydanglecheck.h checks/qgsgeometrydegeneratepolygoncheck.h checks/qgsgeometryduplicatecheck.h checks/qgsgeometryduplicatenodescheck.h diff --git a/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp new file mode 100644 index 00000000000..af68db50224 --- /dev/null +++ b/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + qgsgeometrydanglecheck.cpp + --------------------- + begin : June 2017 + copyright : (C) 2017 by Sandro Mani / Sourcepole AG + email : smani at sourcepole dot 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 "qgsgeometrydanglecheck.h" +#include "qgslinestring.h" + +void QgsGeometryDangleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +{ + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); + for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) + { + const QgsAbstractGeometry *geom = layerFeature.geometry(); + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + const QgsLineString *line = dynamic_cast( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) ); + if ( !line ) + { + // Should not happen + continue; + } + // Check that start and end node lie on a line + int nVerts = geom->vertexCount( iPart, 0 ); + const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, 0, 0 ) ); + const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, 0, nVerts - 1 ) ); + + bool p1touches = QgsGeometryCheckerUtils::pointOnLine( p1, line, mContext->tolerance, true ); + bool p2touches = QgsGeometryCheckerUtils::pointOnLine( p2, line, mContext->tolerance, true ); + + if ( p1touches && p2touches ) + { + // Both endpoints lie on line itself + continue; + } + + // Check whether endpoints line on another line in the layer + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, QList() << layerFeature.layer().id(), line->boundingBox(), {QgsWkbTypes::LineGeometry} ); + for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) + { + if ( checkFeature.feature().id() == layerFeature.feature().id() ) + { + // Skip current feature, it was already checked above + continue; + } + const QgsAbstractGeometry *testGeom = checkFeature.geometry(); + for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart ) + { + const QgsLineString *testLine = dynamic_cast( QgsGeometryCheckerUtils::getGeomPart( testGeom, jPart ) ); + if ( !testLine ) + { + continue; + } + p1touches = p1touches || QgsGeometryCheckerUtils::pointOnLine( p1, testLine, mContext->tolerance ); + p2touches = p2touches || QgsGeometryCheckerUtils::pointOnLine( p2, testLine, mContext->tolerance ); + if ( p1touches && p2touches ) + { + break; + } + } + if ( p1touches && p2touches ) + { + break; + } + } + if ( p1touches && p2touches ) + { + continue; + } + if ( !p1touches ) + { + errors.append( new QgsGeometryCheckError( this, layerFeature, p1, QgsVertexId( iPart, 0, 0 ) ) ); + } + if ( !p2touches ) + { + errors.append( new QgsGeometryCheckError( this, layerFeature, p2, QgsVertexId( iPart, 0, nVerts - 1 ) ) ); + } + } + } +} + +void QgsGeometryDangleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +{ + if ( method == NoChange ) + { + error->setFixed( method ); + } + else + { + error->setFixFailed( tr( "Unknown method" ) ); + } +} + +QStringList QgsGeometryDangleCheck::getResolutionMethods() const +{ + static QStringList methods = QStringList() << tr( "No action" ); + return methods; +} diff --git a/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h b/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h new file mode 100644 index 00000000000..a43af4a80c3 --- /dev/null +++ b/src/plugins/geometry_checker/checks/qgsgeometrydanglecheck.h @@ -0,0 +1,38 @@ +/*************************************************************************** + qgsgeometrydanglecheck.h + --------------------- + begin : June 2017 + copyright : (C) 2017 by Sandro Mani / Sourcepole AG + email : smani at sourcepole dot 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. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYDANGLECHECK_H +#define QGSGEOMETRYDANGLECHECK_H + +#include "qgsgeometrycheck.h" + +class QgsGeometryDangleCheck : public QgsGeometryCheck +{ + Q_OBJECT + + public: + QgsGeometryDangleCheck( QgsGeometryCheckerContext *context ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry}, context ) + {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + QStringList getResolutionMethods() const override; + QString errorDescription() const override { return tr( "Dangle" ); } + QString errorName() const override { return QStringLiteral( "QgsGeometryDangleCheck" ); } + private: + enum ResolutionMethod { NoChange }; +}; + +#endif // QGSGEOMETRYDANGLECHECK_H diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp index 167dd57b58a..1ed89a0b1cf 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp @@ -19,6 +19,7 @@ #include "checks/qgsgeometryanglecheck.h" #include "checks/qgsgeometryareacheck.h" #include "checks/qgsgeometrycontainedcheck.h" +#include "checks/qgsgeometrydanglecheck.h" #include "checks/qgsgeometrydegeneratepolygoncheck.h" #include "checks/qgsgeometryduplicatecheck.h" #include "checks/qgsgeometryduplicatenodescheck.h" @@ -126,6 +127,34 @@ REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT void QgsGeometryCheckFactoryT::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const +{ + ui.checkBoxDangle->setChecked( QgsSettings().value( sSettingsGroup + "checkDangle" ).toBool() ); +} + +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int /*nPolygon*/ ) const +{ + ui.checkBoxDangle->setEnabled( nLineString > 0 ); + return ui.checkBoxDangle->isEnabled(); +} + +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +{ + QgsSettings().setValue( sSettingsGroup + "checkDangle", ui.checkBoxDangle->isChecked() ); + if ( ui.checkBoxDangle->isEnabled() && ui.checkBoxDangle->isChecked() ) + { + return new QgsGeometryDangleCheck( context ); + } + else + { + return nullptr; + } +} + +REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT ) + +/////////////////////////////////////////////////////////////////////////////// + template<> void QgsGeometryCheckFactoryT::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const { ui.checkBoxDegeneratePolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkDegeneratePolygon" ).toBool() ); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui index 66bc5904b95..aea7dcd4e41 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui @@ -43,7 +43,7 @@ 0 0 626 - 820 + 872 @@ -280,6 +280,13 @@ + + + + Lines must not have dangles + + + diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index 69ec69c674e..46b1ab66864 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -206,6 +206,22 @@ namespace QgsGeometryCheckerUtils return nom / std::sqrt( dx * dx + dy * dy ); } + bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities ) + { + int nVerts = line->vertexCount(); + for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i ) + { + QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) ); + QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); + double dist = pointLineDist( p1, p2, 1 ); + if ( dist < tol ) + { + return true; + } + } + return false; + } + double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ) { double len = 0; diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h index 4a67e41ea85..5ad0f3d7f4f 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h @@ -126,6 +126,8 @@ namespace QgsGeometryCheckerUtils } } + bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false ); + double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ); /**