Skip to content

Commit

Permalink
QgsGeometryValidator: Fix validation of LineString
Browse files Browse the repository at this point in the history
The algorithm used seems to be false on some cases.
Prefers the one from QgsGeometryUtils.

Fixes #54022
  • Loading branch information
lbartoletti committed Feb 7, 2024
1 parent b9a4769 commit 8eb06dc
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 46 deletions.
55 changes: 9 additions & 46 deletions src/core/qgsgeometryvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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++;
}
}
}
Expand Down
53 changes: 53 additions & 0 deletions tests/src/python/test_qgsgeometryvalidator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))")
Expand Down

0 comments on commit 8eb06dc

Please sign in to comment.