diff --git a/resources/function_help/json/$z b/resources/function_help/json/$z new file mode 100644 index 000000000000..e4ae9651219d --- /dev/null +++ b/resources/function_help/json/$z @@ -0,0 +1,8 @@ +{ + "name": "$z", + "type": "function", + "groups": ["GeometryGroup"], + "description": "Returns the z value of the current point feature if it is 3D. If the feature is a multipoint feature, then the z value of the first point will be returned.", + "examples": [ { "expression":"$z", "returns":"123"} + ] +} diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index d372aa72816c..2c91d6f212a2 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -2426,6 +2426,40 @@ static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, } } +static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * ) +{ + FEAT_FROM_CONTEXT( context, f ) + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PointGeometry ) + + if ( g.isEmpty() ) + return QVariant(); + + const QgsAbstractGeometry *abGeom = g.constGet(); + + if ( g.isEmpty() || !abGeom->is3D() ) + return QVariant(); + + if ( g.type() == QgsWkbTypes::PointGeometry && !g.isMultipart() ) + { + const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() ); + if ( point ) + return point->z(); + } + else if ( g.type() == QgsWkbTypes::PointGeometry && g.isMultipart() ) + { + if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) ) + { + if ( collection->numGeometries() > 0 ) + { + if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) ) + return point->z(); + } + } + } + + return QVariant(); +} + static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) { QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); @@ -6794,6 +6828,10 @@ const QList &QgsExpression::Functions() yFunc->setIsStatic( false ); functions << yFunc; + QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true ); + zFunc->setIsStatic( false ); + functions << zFunc; + QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions { { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects }, diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index a043091bc0bc..30cd8eef45e7 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -2826,12 +2826,13 @@ class TestQgsExpression: public QObject QTest::addColumn( "evalError" ); QTest::addColumn( "result" ); - QgsPointXY point( 123, 456 ); + QgsPoint point( 123, 456, 789 ); QgsPolylineXY line; line << QgsPointXY( 1, 1 ) << QgsPointXY( 4, 2 ) << QgsPointXY( 3, 1 ); - QTest::newRow( "geom x" ) << "$x" << QgsGeometry::fromPointXY( point ) << false << QVariant( 123. ); - QTest::newRow( "geom y" ) << "$y" << QgsGeometry::fromPointXY( point ) << false << QVariant( 456. ); + QTest::newRow( "geom x" ) << "$x" << QgsGeometry( std::make_unique( point ) ) << false << QVariant( 123. ); + QTest::newRow( "geom y" ) << "$y" << QgsGeometry( std::make_unique( point ) ) << false << QVariant( 456. ); + QTest::newRow( "geom z" ) << "$z" << QgsGeometry( std::make_unique( point ) ) << false << QVariant( 789. ); QTest::newRow( "geom xat" ) << "xat(-1)" << QgsGeometry::fromPolylineXY( line ) << false << QVariant( 3. ); QTest::newRow( "geom yat" ) << "yat(1)" << QgsGeometry::fromPolylineXY( line ) << false << QVariant( 2. ); QTest::newRow( "null geometry" ) << "$geometry" << QgsGeometry() << false << QVariant();