diff --git a/src/plugins/geometry_checker/CMakeLists.txt b/src/plugins/geometry_checker/CMakeLists.txt index 2baba325bcb..5d11875cb9a 100644 --- a/src/plugins/geometry_checker/CMakeLists.txt +++ b/src/plugins/geometry_checker/CMakeLists.txt @@ -15,6 +15,7 @@ SET (geometrychecker_SRCS checks/qgsgeometrycheck.cpp checks/qgsgeometrygapcheck.cpp checks/qgsgeometryholecheck.cpp + checks/qgsgeometrylineintersectioncheck.cpp checks/qgsgeometrymultipartcheck.cpp checks/qgsgeometrycontainedcheck.cpp checks/qgsgeometryoverlapcheck.cpp @@ -53,6 +54,7 @@ SET (geometrychecker_MOC_HDRS checks/qgsgeometryduplicatenodescheck.h checks/qgsgeometrygapcheck.h checks/qgsgeometryholecheck.h + checks/qgsgeometrylineintersectioncheck.h checks/qgsgeometrymultipartcheck.h checks/qgsgeometrycontainedcheck.h checks/qgsgeometryoverlapcheck.h diff --git a/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.cpp new file mode 100644 index 00000000000..7f81a03dc4a --- /dev/null +++ b/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + qgsgeometrylineintersectioncheck.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 "qgsgeometrylineintersectioncheck.h" +#include "qgslinestring.h" + +void QgsGeometryLineIntersectionCheck::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, true ); + 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 whether the line intersects with any other lines + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, featureIds.keys(), line->boundingBox(), {QgsWkbTypes::LineGeometry} ); + for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) + { + if ( checkFeature.feature().id() == layerFeature.feature().id() ) + { + // Skip current feature + 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; + } + QgsPoint inter; + if ( QgsGeometryCheckerUtils::linesIntersect( line, testLine, mContext->tolerance, inter ) ) + { + errors.append( new QgsGeometryCheckError( this, layerFeature, inter ) ); + } + } + } + } + } +} + +void QgsGeometryLineIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +{ + if ( method == NoChange ) + { + error->setFixed( method ); + } + else + { + error->setFixFailed( tr( "Unknown method" ) ); + } +} + +QStringList QgsGeometryLineIntersectionCheck::getResolutionMethods() const +{ + static QStringList methods = QStringList() << tr( "No action" ); + return methods; +} diff --git a/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.h b/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.h new file mode 100644 index 00000000000..226a82b0438 --- /dev/null +++ b/src/plugins/geometry_checker/checks/qgsgeometrylineintersectioncheck.h @@ -0,0 +1,38 @@ +/*************************************************************************** + qgsgeometrylineintersectioncheck.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 QGSGEOMETRYLINEINTERSECTIONCHECK_H +#define QGSGEOMETRYLINEINTERSECTIONCHECK_H + +#include "qgsgeometrycheck.h" + +class QgsGeometryLineIntersectionCheck : public QgsGeometryCheck +{ + Q_OBJECT + + public: + QgsGeometryLineIntersectionCheck( 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( "Intersection" ); } + QString errorName() const override { return QStringLiteral( "QgsGeometryLineIntersectionCheck" ); } + private: + enum ResolutionMethod { NoChange }; +}; + +#endif // QGSGEOMETRYLINEINTERSECTIONCHECK_H diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp index 0a4f242a26a..f349a0968b6 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp @@ -25,6 +25,7 @@ #include "checks/qgsgeometryduplicatenodescheck.h" #include "checks/qgsgeometrygapcheck.h" #include "checks/qgsgeometryholecheck.h" +#include "checks/qgsgeometrylineintersectioncheck.h" #include "checks/qgsgeometrymultipartcheck.h" #include "checks/qgsgeometryoverlapcheck.h" #include "checks/qgsgeometrypointcoveredbylinecheck.h" @@ -299,6 +300,35 @@ REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT void QgsGeometryCheckFactoryT::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const +{ + ui.checkLineIntersection->setChecked( QgsSettings().value( sSettingsGroup + "checkLineIntersection" ).toBool() ); +} + +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int /*nPolygon*/ ) const +{ + ui.checkLineIntersection->setEnabled( nLineString > 0 ); + return ui.checkLineIntersection->isEnabled(); +} + +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +{ + QgsSettings().setValue( sSettingsGroup + "checkLineIntersection", ui.checkLineIntersection->isChecked() ); + if ( ui.checkLineIntersection->isEnabled() && ui.checkLineIntersection->isChecked() ) + { + return new QgsGeometryLineIntersectionCheck( context ); + } + else + { + return nullptr; + } +} + +REGISTER_QGS_GEOMETRY_CHECK_FACTORY( QgsGeometryCheckFactoryT ) + +/////////////////////////////////////////////////////////////////////////////// + + template<> void QgsGeometryCheckFactoryT::restorePrevious( Ui::QgsGeometryCheckerSetupTab &ui ) const { ui.checkBoxMultipart->setChecked( QgsSettings().value( sSettingsGroup + "checkMultipart" ).toBool() ); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui index d4dbd9d0b5d..e67588cf610 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui @@ -43,7 +43,7 @@ 0 0 626 - 895 + 918 @@ -511,6 +511,13 @@ + + + + Lines must not intersect + + + @@ -535,7 +542,7 @@ - + <i>Note: Topology checks are performed in the current map CRS.</i> @@ -552,7 +559,7 @@ - + Points must be covered by lines diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index a285715c846..950bfbf8a92 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -222,6 +222,25 @@ namespace QgsGeometryCheckerUtils return false; } + bool linesIntersect( const QgsLineString *line1, const QgsLineString *line2, double tol, QgsPoint &inter ) + { + for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i ) + { + for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j ) + { + QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) ); + QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); + QgsPoint q1 = line1->vertexAt( QgsVertexId( 0, 0, j ) ); + QgsPoint q2 = line1->vertexAt( QgsVertexId( 0, 0, j + 1 ) ); + if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, 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 5ad0f3d7f4f..07c81a6a610 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h @@ -128,6 +128,8 @@ namespace QgsGeometryCheckerUtils bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false ); + bool linesIntersect( const QgsLineString *line1, const QgsLineString *line2, double tol, QgsPoint &inter ); + double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ); /**