Skip to content

Commit

Permalink
GEOMETRY: fix regression on split multipart
Browse files Browse the repository at this point in the history
Fix #54155
  • Loading branch information
elpaso authored and nyalldawson committed Sep 29, 2023
1 parent 8a3ee8a commit b235eee
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 12 deletions.
33 changes: 21 additions & 12 deletions src/core/geometry/qgsgeos.cpp
Expand Up @@ -1042,12 +1042,12 @@ geos::unique_ptr QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) co
QgsMultiCurve lines;

//For each part
for ( int i = 0; i < multiCurve->numGeometries(); ++i )
for ( int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
{
const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( i ) );
const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( geometryIndex ) );
if ( !line )
{
const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( multiCurve->geometryN( i ) );
const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( multiCurve->geometryN( geometryIndex ) );
line = curve->curveToLine();
}
if ( !line )
Expand All @@ -1056,14 +1056,23 @@ geos::unique_ptr QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) co
}
// we gather the intersection points and their distance from previous node grouped by segment
QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
for ( int p = 0; p < splitPoints->numGeometries(); ++p )
for ( int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
{
const QgsPoint *intersectionPoint = splitPoints->pointN( p );
const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );

QgsPoint segmentPoint2D;
QgsVertexId nextVertex;
// With closestSegment we only get a 2D point so we need to interpolate if we
// don't want to lose Z data
line->closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );

// The intersection might belong to another part, skip it
// Note: cannot test for equality because of Z
if ( intersectionPoint->x() != segmentPoint2D.x() || intersectionPoint->y() != segmentPoint2D.y() )
{
continue;
}

const QgsLineString segment = QgsLineString( line->pointN( nextVertex.vertex - 1 ), line->pointN( nextVertex.vertex ) );
const double distance = segmentPoint2D.distance( line->pointN( nextVertex.vertex - 1 ) );

Expand All @@ -1089,25 +1098,25 @@ geos::unique_ptr QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) co
QgsLineString newLine;
int nVertices = line->numPoints();
QgsPoint splitPoint;
for ( int j = 0; j < nVertices; ++j )
for ( int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
{
QgsPoint currentPoint = line->pointN( j );
QgsPoint currentPoint = line->pointN( vertexIndex );
newLine.addVertex( currentPoint );
if ( pointMap.contains( j ) )
if ( pointMap.contains( vertexIndex ) )
{
// For each intersecting point
for ( int k = 0; k < pointMap[ j ].size(); ++k )
for ( int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
{
splitPoint = pointMap[ j ][k].second;
splitPoint = pointMap[ vertexIndex ][k].second;
if ( splitPoint == currentPoint )
{
lines.addGeometry( newLine.clone() );
newLine = QgsLineString();
newLine.addVertex( currentPoint );
}
else if ( splitPoint == line->pointN( j + 1 ) )
else if ( splitPoint == line->pointN( vertexIndex + 1 ) )
{
newLine.addVertex( line->pointN( j + 1 ) );
newLine.addVertex( line->pointN( vertexIndex + 1 ) );
lines.addGeometry( newLine.clone() );
newLine = QgsLineString();
}
Expand Down
10 changes: 10 additions & 0 deletions tests/src/core/geometry/testqgsgeometry.cpp
Expand Up @@ -2403,6 +2403,16 @@ void TestQgsGeometry::splitGeometry()
QCOMPARE( g2.splitGeometry( QgsPointSequence() << QgsPoint( -63290.25259721936890855, -79165.28533450335089583 ) << QgsPoint( -63290.25259721936890855, -79160.28533450335089583 ), newGeoms, false, testPoints ), Qgis::GeometryOperationResult::Success );
QCOMPARE( newGeoms.count(), 1 );
QCOMPARE( newGeoms[0].asWkt( 17 ), QStringLiteral( "LineString (-63290.25259721937618451 -79162.78533450335089583, -63290.25259721936890855 -79162.78533450335089583)" ) );

// Should not split the first part - https://github.com/qgis/QGIS/issues/54155
g2 = QgsGeometry::fromWkt( "MultiLinestring((0 1, 1 0),(0 2, 2 0))" );
testPoints.clear();
newGeoms.clear();
QCOMPARE( g2.splitGeometry( QgsPointSequence() << QgsPoint( 0.8, 0.8 ) << QgsPoint( 1.2, 1.2 ), newGeoms, false, testPoints, false ), Qgis::GeometryOperationResult::Success );
QCOMPARE( newGeoms.count(), 3 );
QCOMPARE( newGeoms[0].asWkt( 0 ), QStringLiteral( "MultiLineString ((0 2, 1 1))" ) );
QCOMPARE( newGeoms[1].asWkt( 0 ), QStringLiteral( "MultiLineString ((1 1, 2 0))" ) );
QCOMPARE( newGeoms[2].asWkt( 0 ), QStringLiteral( "MultiLineString ((0 1, 1 0))" ) );
}

void TestQgsGeometry::snappedToGrid()
Expand Down
10 changes: 10 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Expand Up @@ -7363,6 +7363,16 @@ def testSplitGeometry(self):
self.assertGeometriesEqual(square, r2)
self.assertGeometriesEqual(parts[0], r1)

multilinestring = QgsGeometry.fromWkt("MultiLinestring((0 1, 1 0),(0 2, 2 0))")
blade = QgsCompoundCurve()
blade.addCurve(QgsLineString([QgsPointXY(0.8, 0.8), QgsPointXY(1.2, 1.2)]))
result, splitted, _ = multilinestring.splitGeometry(blade, False, False, False)
self.assertEqual(result, Qgis.GeometryOperationResult.Success)
self.assertEqual(len(splitted), 3)
self.assertTrue(compareWkt(splitted[0].asWkt(), 'MultiLineString ((0 2, 1 1))'))
self.assertTrue(compareWkt(splitted[1].asWkt(), 'MultiLineString ((1 1, 2 0))'))
self.assertTrue(compareWkt(splitted[2].asWkt(), 'MultiLineString ((0 1, 1 0))'))


if __name__ == '__main__':
unittest.main()

0 comments on commit b235eee

Please sign in to comment.