Skip to content

Commit

Permalink
[feature][expressions] Add "length3D" function to return the 3D length
Browse files Browse the repository at this point in the history
of a LineGeometry type geometry using QgsLineString::length3D(). If the geometry is not a 3D line string, it returns its 2D length.
  • Loading branch information
agiudiceandrea authored and nyalldawson committed Feb 24, 2021
1 parent 4b8badd commit d7167e4
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
8 changes: 8 additions & 0 deletions 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"}]
}
11 changes: 11 additions & 0 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -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 )
Expand Down Expand Up @@ -6428,6 +6438,7 @@ const QList<QgsExpressionFunction *> &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" ) )
Expand Down
15 changes: 14 additions & 1 deletion tests/src/core/testqgsexpression.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit d7167e4

Please sign in to comment.