diff --git a/src/core/qgsgeometryvalidator.cpp b/src/core/qgsgeometryvalidator.cpp index c7db2bf37d56..afb673f3a227 100644 --- a/src/core/qgsgeometryvalidator.cpp +++ b/src/core/qgsgeometryvalidator.cpp @@ -16,6 +16,7 @@ email : jef at norbit dot de #include "qgis.h" #include "qgsgeometryvalidator.h" #include "qgsgeometry.h" +#include "qgsgeometryutils.h" #include "qgslogger.h" #include "qgsgeos.h" #include "qgsgeometrycollection.h" @@ -187,59 +188,21 @@ void QgsGeometryValidator::validatePolyline( int i, const QgsLineString *line, b line = noDupes.get(); } + // segment Intersection for ( int j = 0; !mStop && j < line->numPoints() - 3; j++ ) { - const double xAtJ = line->xAt( j ); - const double yAtJ = line->yAt( j ); - const QgsVector v( line->xAt( j + 1 ) - xAtJ, line->yAt( j + 1 ) - yAtJ ); - const double vl = v.length(); - const int n = ( j == 0 && ring ) ? line->numPoints() - 2 : line->numPoints() - 1; - for ( int k = j + 2; !mStop && k < n; k++ ) { - const double xAtK = line->xAt( k ); - const double yAtK = line->yAt( k ); - - const QgsVector w( line->xAt( k + 1 ) - xAtK, line->yAt( k + 1 ) - yAtK ); - - double sX; - double sY; - if ( !intersectLines( xAtJ, yAtJ, v, xAtK, yAtK, w, sX, sY ) ) - continue; - - double d = 0.0; - try + QgsPoint intersectionPoint; + bool isIntersection = false; + if ( QgsGeometryUtils::segmentIntersection( line->pointN( j ), line->pointN( j + 1 ), line->pointN( k ), line->pointN( k + 1 ), intersectionPoint, isIntersection ) ) { - d = -distLine2Point( xAtJ, yAtJ, v.perpVector(), sX, sY ); - } - catch ( QgsException &e ) - { - Q_UNUSED( e ) - QgsDebugError( "Error validating: " + e.what() ); - continue; - } - if ( d < 0 || d > vl ) - continue; - - try - { - d = -distLine2Point( xAtK, yAtK, w.perpVector(), sX, sY ); - } - catch ( QgsException &e ) - { - Q_UNUSED( e ) - QgsDebugError( "Error validating: " + e.what() ); - continue; + const QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4, %5" ).arg( j ).arg( k ).arg( i ).arg( intersectionPoint.x() ).arg( intersectionPoint.y() ); + QgsDebugMsgLevel( msg, 2 ); + emit errorFound( QgsGeometry::Error( msg, QgsPointXY( intersectionPoint.x(), intersectionPoint.y() ) ) ); + mErrorCount++; } - - if ( d <= 0 || d >= w.length() ) - continue; - - const QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4, %5" ).arg( j ).arg( k ).arg( i ).arg( sX ).arg( sY ); - QgsDebugMsgLevel( msg, 2 ); - emit errorFound( QgsGeometry::Error( msg, QgsPointXY( sX, sY ) ) ); - mErrorCount++; } } } diff --git a/tests/src/python/test_qgsgeometryvalidator.py b/tests/src/python/test_qgsgeometryvalidator.py index d71c3737bc90..99f0008219d0 100644 --- a/tests/src/python/test_qgsgeometryvalidator.py +++ b/tests/src/python/test_qgsgeometryvalidator.py @@ -74,6 +74,59 @@ def test_linestring_duplicate_nodes(self): self.assertEqual(spy[2][0].where(), QgsPointXY(1, 1)) self.assertEqual(spy[2][0].what(), 'line 1 contains 3 duplicate node(s) starting at vertex 1') + def test_linestring_intersections(self): + # no intersections + g = QgsGeometry.fromWkt("LineString (0 0, 10 0, 10 10, 0 10)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 0) + + # accept closed linestring + g = QgsGeometry.fromWkt("LineString (0 0, 10 0, 10 10, 0 10, 0 0)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 0) + + # accept segment on segment + # well, this case is ambiguous + g = QgsGeometry.fromWkt("LineString (0 0, 10 0, 10 10, 0 10, 10 10)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 0) + + # self-intersection + g = QgsGeometry.fromWkt("LineString (0 0, 10 10, 0 10, 10 0)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 1) + self.assertEqual(spy[0][0].where(), QgsPointXY(5, 5)) + self.assertEqual(spy[0][0].what(), 'segments 0 and 2 of line 0 intersect at 5, 5') + + # tests issue #54022 + # Test number 1 + g = QgsGeometry.fromWkt("LineString (10.516394879668999 59.73620343022049894, 10.51672588102520045 59.73642831668259845, 10.51671297289430029 59.73629479296509004, 10.516394879668999 59.73620343022049894)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 0) + + # Test number 2 + g = QgsGeometry.fromWkt("LINESTRING (10.516394879668999 59.73620343022049894, 10.51672588102520045 59.73642831668259845, 10.51671297289430029 59.73619479296509004, 10.516394879668999 59.73620343022049894)") + validator = QgsGeometryValidator(g) + spy = QSignalSpy(validator.errorFound) + validator.run() + + self.assertEqual(len(spy), 0) + def test_ring_intersections(self): # no intersections g = QgsGeometry.fromWkt("Polygon ((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 5 1, 1 9, 1 1), (6 9, 2 9, 6 1, 6 9))")