[feature][expressions] Add "length3D" function to return the 3D length

of a LineGeometry type geometry using QgsLineString::length3D(). If the geometry is not a 3D line string, it returns its 2D length.
This commit is contained in:
Andrea Giudiceandrea 2021-01-22 07:35:21 +01:00 committed by Nyall Dawson
parent 4b8baddee2
commit d7167e48d0
3 changed files with 33 additions and 1 deletions

View File

@ -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"}]
}

View File

@ -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<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" ) )

View File

@ -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()