From 97d5155e8a99fa712428a57f8b2bd20aada8b70d Mon Sep 17 00:00:00 2001 From: Alexander Bruy Date: Tue, 5 Sep 2023 14:20:20 +0300 Subject: [PATCH] better fix and add a test --- src/core/qgspointlocator.h | 13 +++++-- tests/src/core/testqgspointlocator.cpp | 52 ++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index a82a25ab634f..595c2087a3cc 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -287,12 +287,17 @@ class CORE_EXPORT QgsPointLocator : public QObject if ( !( geom.isNull() || geom.isEmpty() ) ) { - const QgsLineString line( geom.vertexAt( mVertexIndex ), geom.vertexAt( mVertexIndex + 1 ) ); - point = QgsGeometryUtils::closestPoint( line, QgsPoint( snappedPoint ) ); + // when snapping to a curve we need to use real geometry in order to have correct location + // of the snapped point, see https://github.com/qgis/QGIS/issues/53197. + // In other cases it is ok to use only a segment to speedup calculations. if ( QgsWkbTypes::isCurvedType( mLayer->wkbType() ) ) { - point.setX( snappedPoint.x() ); - point.setY( snappedPoint.y() ); + point = QgsGeometryUtils::closestPoint( *geom.constGet(), QgsPoint( snappedPoint ) ); + } + else + { + const QgsLineString line( geom.vertexAt( mVertexIndex ), geom.vertexAt( mVertexIndex + 1 ) ); + point = QgsGeometryUtils::closestPoint( line, QgsPoint( snappedPoint ) ); } } diff --git a/tests/src/core/testqgspointlocator.cpp b/tests/src/core/testqgspointlocator.cpp index 948b8fc4d745..eef5feac99c6 100644 --- a/tests/src/core/testqgspointlocator.cpp +++ b/tests/src/core/testqgspointlocator.cpp @@ -228,7 +228,6 @@ class TestQgsPointLocator : public QObject QCOMPARE( lst3[2].vertexIndex(), 3 ); } - void testLayerUpdates() { @@ -474,12 +473,10 @@ class TestQgsPointLocator : public QObject delete loc; } - void testEmptyLayer() { // Issue https://github.com/qgis/QGIS/issues/33449 - // Create an empty layer, add one feature and check that we can snap on this feature QgsVectorLayer layer( QStringLiteral( "Polygon" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ); QgsProject::instance()->addMapLayer( &layer ); @@ -517,6 +514,55 @@ class TestQgsPointLocator : public QObject QCOMPARE( m.vertexIndex(), 2 ); } + void testInterpolatedPoint() + { + std::unique_ptr curveLayer( new QgsVectorLayer( QStringLiteral( "CircularStringZ" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) ) ); + QgsFeature f1; + const QgsGeometry f1g = QgsGeometry::fromWkt( "CircularStringZ (0 0 0, 5 5 5, 0 10 10)" ) ; + f1.setGeometry( f1g ); + QgsFeatureList f1list; + f1list << f1; + curveLayer->dataProvider()->addFeatures( f1list ); + QVERIFY( curveLayer->dataProvider()->featureCount() == 1 ); + + QgsPointLocator loc1( curveLayer.get(), QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr ); + const QgsPointLocator::Match m1 = loc1.nearestEdge( QgsPointXY( 3, 8 ), std::numeric_limits::max() ); + QVERIFY( m1.isValid() ); + QVERIFY( m1.hasEdge() ); + QCOMPARE( m1.layer(), curveLayer.get() ); + QCOMPARE( m1.featureId(), ( QgsFeatureId )1 ); + QCOMPARE( m1.point(), QgsPointXY( 3.53553390593273775, 8.53553390593273775 ) ); + QCOMPARE( m1.distance(), 0.757359312881 ); + QCOMPARE( m1.vertexIndex(), 1 ); + QgsPointXY pt1, pt2; + m1.edgePoints( pt1, pt2 ); + QCOMPARE( pt1, QgsPointXY( 5, 5 ) ); + QCOMPARE( pt2, QgsPointXY( 0, 10 ) ); + QCOMPARE( m1.interpolatedPoint( QgsCoordinateReferenceSystem() ), QgsPoint( 3.53553390593273775, 8.53553390593273775, 7.70598050073098584 ) ); + + std::unique_ptr lineLayer( new QgsVectorLayer( QStringLiteral( "LineStringZ" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) ) ); + QgsFeature f2; + const QgsGeometry f2g = QgsGeometry::fromWkt( "LineStringZ (0 0 0, 5 5 5, 0 10 10)" ) ; + f2.setGeometry( f2g ); + QgsFeatureList f2list; + f2list << f2; + lineLayer->dataProvider()->addFeatures( f2list ); + QVERIFY( lineLayer->dataProvider()->featureCount() == 1 ); + + QgsPointLocator loc2( lineLayer.get(), QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr ); + const QgsPointLocator::Match m2 = loc2.nearestEdge( QgsPointXY( 3, 8 ), std::numeric_limits::max() ); + QVERIFY( m2.isValid() ); + QVERIFY( m2.hasEdge() ); + QCOMPARE( m2.layer(), lineLayer.get() ); + QCOMPARE( m2.featureId(), ( QgsFeatureId )1 ); + QCOMPARE( m2.point(), QgsPointXY( 2.5, 7.5 ) ); + QCOMPARE( m2.distance(), 0.707106781187 ); + QCOMPARE( m2.vertexIndex(), 1 ); + m2.edgePoints( pt1, pt2 ); + QCOMPARE( pt1, QgsPointXY( 5, 5 ) ); + QCOMPARE( pt2, QgsPointXY( 0, 10 ) ); + QCOMPARE( m2.interpolatedPoint( QgsCoordinateReferenceSystem() ), QgsPoint( 2.5, 7.5, 7.5 ) ); + } }; QGSTEST_MAIN( TestQgsPointLocator )