From f02602b9bbd80394414eec73f4f5688a52df91a2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 28 Mar 2018 14:48:06 +1000 Subject: [PATCH] [needs-docs] geometry smooth algorithm now also retains and smooths z/m values ...instead of just discarding them Applies to processing algorithm and expression function (and QgsGeometry::smooth method) --- python/core/geometry/qgsgeometry.sip.in | 3 ++ resources/function_help/json/smooth | 2 +- .../processing/qgsalgorithmsmooth.cpp | 4 ++- src/core/geometry/qgsgeometry.cpp | 9 +++-- src/core/geometry/qgsgeometry.h | 4 +++ tests/src/core/testqgsgeometry.cpp | 36 +++++++++++++++++++ 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/python/core/geometry/qgsgeometry.sip.in b/python/core/geometry/qgsgeometry.sip.in index bb0ad763d6f..e30c0dabfd5 100644 --- a/python/core/geometry/qgsgeometry.sip.in +++ b/python/core/geometry/qgsgeometry.sip.in @@ -1668,6 +1668,9 @@ tolerance Smooths a geometry by rounding off corners using the Chaikin algorithm. This operation roughly doubles the number of vertices in a geometry. +If input geometries contain Z or M values, these will also be smoothed and the output +geometry will retain the same dimensionality as the input geometry. + :param iterations: number of smoothing iterations to run. More iterations results in a smoother geometry :param offset: fraction of line to create new vertices along, between 0 and 1.0, diff --git a/resources/function_help/json/smooth b/resources/function_help/json/smooth index fef1ec7093a..37c9852988f 100644 --- a/resources/function_help/json/smooth +++ b/resources/function_help/json/smooth @@ -1,7 +1,7 @@ { "name": "smooth", "type": "function", - "description":"Smooths a geometry by adding extra nodes which round off corners in the geometry.", + "description":"Smooths a geometry by adding extra nodes which round off corners in the geometry. If input geometries contain Z or M values, these will also be smoothed and the output geometry will retain the same dimensionality as the input geometry.", "arguments": [ {"arg":"geometry","description":"a geometry"}, {"arg":"iterations", "optional":true,"description":"number of smoothing iterations to apply. Larger numbers result in smoother but more complex geometries."}, {"arg":"offset", "optional":true,"description":"value between 0 and 0.5 which controls how tightly the smoothed geometry follow the original geometry. Smaller values result in a tighter smoothing, larger values result in looser smoothing."}, diff --git a/src/analysis/processing/qgsalgorithmsmooth.cpp b/src/analysis/processing/qgsalgorithmsmooth.cpp index f96f00b18cb..a82c44a67b3 100644 --- a/src/analysis/processing/qgsalgorithmsmooth.cpp +++ b/src/analysis/processing/qgsalgorithmsmooth.cpp @@ -67,7 +67,9 @@ QString QgsSmoothAlgorithm::shortHelpString() const "The maximum angle parameter can be used to prevent smoothing of " "nodes with large angles. Any node where the angle of the segments to either " "side is larger than this will not be smoothed. For example, setting the maximum " - "angle to 90 degrees or lower would preserve right angles in the geometry." ); + "angle to 90 degrees or lower would preserve right angles in the geometry.\n\n" + "If input geometries contain Z or M values, these will also be smoothed and the output " + "geometry will retain the same dimensionality as the input geometry." ); } QgsSmoothAlgorithm *QgsSmoothAlgorithm::createInstance() const diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 352fbd0c685..dcb3ac42189 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -2711,9 +2711,12 @@ QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double off inline QgsPoint interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, const double offset ) { - double deltaX = p2.x() - p1.x(); - double deltaY = p2.y() - p1.y(); - return QgsPoint( p1.x() + deltaX * offset, p1.y() + deltaY * offset ); + const double _offset = 1 - offset; + return QgsPoint( p1.wkbType(), + p1.x() * _offset + p2.x() * offset, + p1.y() * _offset + p2.y() * offset, + p1.is3D() ? p1.z() * _offset + p2.z() * offset : std::numeric_limits::quiet_NaN(), + p1.isMeasure() ? p1.m() * _offset + p2.m() * offset : std::numeric_limits::quiet_NaN() ); } std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations, diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 4bc6232a19b..15d785c048e 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -1675,6 +1675,10 @@ class CORE_EXPORT QgsGeometry /** * Smooths a geometry by rounding off corners using the Chaikin algorithm. This operation * roughly doubles the number of vertices in a geometry. + * + * If input geometries contain Z or M values, these will also be smoothed and the output + * geometry will retain the same dimensionality as the input geometry. + * * \param iterations number of smoothing iterations to run. More iterations results * in a smoother geometry * \param offset fraction of line to create new vertices along, between 0 and 1.0, diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 0a8f5892ac0..d0a4fd19c03 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -15606,6 +15606,24 @@ void TestQgsGeometry::smoothCheck() << QgsPointXY( 10.0, 7.5 ) << QgsPointXY( 12.5, 10.0 ) << QgsPointXY( 20.0, 10.0 ); QVERIFY( QgsGeometry::compare( line, expectedLine ) ); + //linestringM + wkt = QStringLiteral( "LineStringM(0 0 1, 10 0 2, 10 10 6, 20 10 4)" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "LineStringM (0 0 1, 7.5 0 1.75, 10 2.5 3, 10 7.5 5, 12.5 10 5.5, 20 10 4)" ) ); + + //linestringZ + wkt = QStringLiteral( "LineStringZ(0 0 1, 10 0 2, 10 10 6, 20 10 4)" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "LineStringZ (0 0 1, 7.5 0 1.75, 10 2.5 3, 10 7.5 5, 12.5 10 5.5, 20 10 4)" ) ); + + //linestringZM + wkt = QStringLiteral( "LineStringZM(0 0 1 4, 10 0 2 8, 10 10 6 2, 20 10 4 0)" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "LineStringZM (0 0 1 4, 7.5 0 1.75 7, 10 2.5 3 6.5, 10 7.5 5 3.5, 12.5 10 5.5 1.5, 20 10 4 0)" ) ); + //linestring, with min distance wkt = QStringLiteral( "LineString(0 0, 10 0, 10 10, 15 10, 15 20)" ); geom = QgsGeometry::fromWkt( wkt ); @@ -15670,6 +15688,24 @@ void TestQgsGeometry::smoothCheck() << QgsPointXY( 2.0, 3.5 ) << QgsPointXY( 2.0, 2.5 ) << QgsPointXY( 2.5, 2.0 ) ); QVERIFY( QgsGeometry::compare( poly, expectedPolygon ) ); + //polygonM + wkt = QStringLiteral( "PolygonM ((0 0 1, 10 0 4, 10 10 6, 0 10 8, 0 0 1 ),(2 2 3, 4 2 5, 4 4 7, 2 4 9, 2 2 3))" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "PolygonM ((2.5 0 1.75, 7.5 0 3.25, 10 2.5 4.5, 10 7.5 5.5, 7.5 10 6.5, 2.5 10 7.5, 0 7.5 6.25, 0 2.5 2.75, 2.5 0 1.75),(2.5 2 3.5, 3.5 2 4.5, 4 2.5 5.5, 4 3.5 6.5, 3.5 4 7.5, 2.5 4 8.5, 2 3.5 7.5, 2 2.5 4.5, 2.5 2 3.5))" ) ); + + //polygonZ + wkt = QStringLiteral( "PolygonZ ((0 0 1, 10 0 4, 10 10 6, 0 10 8, 0 0 1 ),(2 2 3, 4 2 5, 4 4 7, 2 4 9, 2 2 3))" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "PolygonZ ((2.5 0 1.75, 7.5 0 3.25, 10 2.5 4.5, 10 7.5 5.5, 7.5 10 6.5, 2.5 10 7.5, 0 7.5 6.25, 0 2.5 2.75, 2.5 0 1.75),(2.5 2 3.5, 3.5 2 4.5, 4 2.5 5.5, 4 3.5 6.5, 3.5 4 7.5, 2.5 4 8.5, 2 3.5 7.5, 2 2.5 4.5, 2.5 2 3.5))" ) ); + + //polygonZM + wkt = QStringLiteral( "PolygonZ ((0 0 1 6, 10 0 4 2, 10 10 6 0, 0 10 8 4, 0 0 1 6 ))" ); + geom = QgsGeometry::fromWkt( wkt ); + result = geom.smooth( 1, 0.25 ); + QCOMPARE( result.asWkt(), QStringLiteral( "PolygonZM ((2.5 0 1.75 5, 7.5 0 3.25 3, 10 2.5 4.5 1.5, 10 7.5 5.5 0.5, 7.5 10 6.5 1, 2.5 10 7.5 3, 0 7.5 6.25 4.5, 0 2.5 2.75 5.5, 2.5 0 1.75 5))" ) ); + //polygon with max angle - should be unchanged wkt = QStringLiteral( "Polygon ((0 0, 10 0, 10 10, 0 10, 0 0))" ); geom = QgsGeometry::fromWkt( wkt );