diff --git a/resources/function_help/json/length3D b/resources/function_help/json/length3D new file mode 100644 index 00000000000..26a28b29dfb --- /dev/null +++ b/resources/function_help/json/length3D @@ -0,0 +1,8 @@ +{ + "name": "length3D", + "type": "function", + "groups": ["GeometryGroup"], + "description": "Calculates the 3D length of a geometry line object. If the geometry is not a 3D line object, it returns its 2D length. Calculations are always planimetric in the Spatial Reference System (SRS) of this geometry, and the units of the returned length will match the units for the SRS. This differs from the calculations performed by the $length function, which will perform ellipsoidal calculations based on the project's ellipsoid and distance unit settings.", + "arguments": [ {"arg":"geometry","description":"line geometry object"}], + "examples": [ { "expression":"length3D(geom_from_wkt('LINESTRINGZ(0 0 0, 3 0 4)'))", "returns":"5.0"}] +} diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 89f0829aeae..20ba6fa07f9 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -1357,6 +1357,16 @@ static QVariant fcnLength( const QVariantList &values, const QgsExpressionContex return QVariant( str.length() ); } +static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); + + if ( geom.type() != QgsWkbTypes::LineGeometry ) + return QVariant(); + + return QVariant( qgsgeometry_cast< const QgsLineString * >( geom.constGet() )->length3D() ); +} + static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) { if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map ) @@ -6428,6 +6438,7 @@ const QList &QgsExpression::Functions() << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index cb587751836..91099b4a0ea 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -941,6 +941,10 @@ class TestQgsExpression: public QObject QTest::newRow( "length line" ) << "length(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant( 4.0 ); QTest::newRow( "length polygon" ) << "length(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant(); QTest::newRow( "length point" ) << "length(geom_from_wkt('POINT(0 0)'))" << false << QVariant(); + QTest::newRow( "length3D lineZ" ) << "length3D(geom_from_wkt('LINESTRINGZ(0 0 0, 3 0 4)'))" << false << QVariant( 5.0 ); + QTest::newRow( "length3D line" ) << "length3D(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant( 4.0 ); + QTest::newRow( "length3D polygon" ) << "length3D(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant(); + QTest::newRow( "length3D point" ) << "length3D(geom_from_wkt('POINT(0 0)'))" << false << QVariant(); QTest::newRow( "area polygon" ) << "area(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant( 8.0 ); QTest::newRow( "area line" ) << "area(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant(); QTest::newRow( "area point" ) << "area(geom_from_wkt('POINT(0 0)'))" << false << QVariant(); @@ -2775,13 +2779,17 @@ class TestQgsExpression: public QObject void eval_geometry_calc() { QgsPolylineXY polyline, polygon_ring; + QgsPolyline polylineZ; polyline << QgsPointXY( 0, 0 ) << QgsPointXY( 10, 0 ); + polylineZ << QgsPoint( 0, 0, 0 ) << QgsPoint( 3, 0, 4 ); polygon_ring << QgsPointXY( 2, 1 ) << QgsPointXY( 10, 1 ) << QgsPointXY( 10, 6 ) << QgsPointXY( 2, 6 ) << QgsPointXY( 2, 1 ); QgsPolygonXY polygon; polygon << polygon_ring; - QgsFeature fPolygon, fPolyline; + QgsFeature fPolygon, fPolyline, fPolylineZ; QgsGeometry polylineGeom = QgsGeometry::fromPolylineXY( polyline ); fPolyline.setGeometry( polylineGeom ); + QgsGeometry polylineZGeom = QgsGeometry::fromPolyline( polylineZ ); + fPolylineZ.setGeometry( polylineZGeom ); QgsGeometry polygonGeom = QgsGeometry::fromPolygonXY( polygon ); fPolygon.setGeometry( polygonGeom ); @@ -2898,6 +2906,11 @@ class TestQgsExpression: public QObject QgsExpression exp13( QStringLiteral( "perimeter($geometry)" ) ); QVariant vPerimeterPoly = exp13.evaluate( &context ); QCOMPARE( vPerimeterPoly.toDouble(), 26.0 ); + + context.setFeature( fPolylineZ ); + QgsExpression exp14( QStringLiteral( "length3D($geometry)" ) ); + QVariant vLengthLineZ = exp14.evaluate( &context ); + QCOMPARE( vLengthLineZ.toDouble(), 5.0 ); } void geom_calculator()