Skip to content

Commit

Permalink
feature: add x_at, y_at, z_at and m_at expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Koyaani committed Nov 29, 2022
1 parent ec689c5 commit d8131a0
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 36 deletions.
18 changes: 18 additions & 0 deletions resources/function_help/json/m_at
@@ -0,0 +1,18 @@
{
"name": "m_at",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Retrieves a m coordinate of the geometry, or NULL if the geometry has no m value.",
"arguments": [{
"arg": "geometry",
"description": "geometry object"
}, {
"arg": "i",
"description": "index of the vertex of the geometry (indices start at 0; negative values apply from the last index, starting at -1)"
}],
"examples": [{
"expression": "m_at(geom_from_wkt('LineStringZM(0 0 0 0, 10 10 0 5, 10 10 0 0)'), 1)",
"returns": "5"
}],
"tags": ["retrieves", "coordinate", "measure"]
}
18 changes: 18 additions & 0 deletions resources/function_help/json/x_at
@@ -0,0 +1,18 @@
{
"name": "x_at",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Retrieves a x coordinate of the geometry.",
"arguments": [{
"arg": "geometry",
"description": "geometry object"
}, {
"arg": "i",
"description": "index of the vertex of the geometry (indices start at 0; negative values apply from the last index, starting at -1)"
}],
"examples": [{
"expression": "x_at( geom_from_wkt( 'POINT(4 5)' ), 0 )",
"returns": "4"
}],
"tags": ["retrieves", "coordinate"]
}
18 changes: 18 additions & 0 deletions resources/function_help/json/y_at
@@ -0,0 +1,18 @@
{
"name": "y_at",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Retrieves a y coordinate of the geometry.",
"arguments": [{
"arg": "geometry",
"description": "geometry object"
}, {
"arg": "i",
"description": "index of the vertex of the geometry (indices start at 0; negative values apply from the last index, starting at -1)"
}],
"examples": [{
"expression": "y_at( geom_from_wkt( 'POINT(4 5)' ), 0 )",
"returns": "5"
}],
"tags": ["retrieves", "coordinate"]
}
18 changes: 18 additions & 0 deletions resources/function_help/json/z_at
@@ -0,0 +1,18 @@
{
"name": "z_at",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Retrieves a z coordinate of the geometry, or NULL if the geometry has no z value.",
"arguments": [{
"arg": "geometry",
"description": "geometry object"
}, {
"arg": "i",
"description": "index of the vertex of the geometry (indices start at 0; negative values apply from the last index, starting at -1)"
}],
"examples": [{
"expression": "z_at(geom_from_wkt('LineStringZ(0 0 0, 10 10 5, 10 10 0)'), 1)",
"returns": "5"
}],
"tags": ["retrieves", "coordinate", "3D"]
}
135 changes: 116 additions & 19 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -3778,44 +3778,134 @@ static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const Q
return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
}

static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
{
FEAT_FROM_CONTEXT( context, f )
int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
QgsGeometry g = f.geometry();
if ( g.isNull() )
if ( geom.isNull() )
return QVariant();

if ( idx < 0 )
{
idx += g.constGet()->nCoordinates();
idx += geom.constGet()->nCoordinates();
}
if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
{
parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
return QVariant();
}
return QVariant::fromValue( geom.vertexAt( idx ) );
}

// function used for the old $ style
static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
FEAT_FROM_CONTEXT( context, feature )
const QgsGeometry geom = feature.geometry();
int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );

QVariant v = pointAt( geom, idx, parent );

if ( !v.isNull() )
return QVariant( v.value<QgsPoint>().x() );
else
return QVariant();
}
static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
{
if ( values.at( 1 ).isNull() || values.at( 0 ).isNull() ) // the case where the alias x_at function is called like a $ function (x_at(i))
{
return fcnOldXat( values, f, parent, node );
}
else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (this values is at the second position)
{
return fcnOldXat( QVariantList() << values[1], f, parent, node );
}

QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ); // Should be one or 0 ???
if ( geom.isNull() )
{
return QVariant();
}

QVariant v = pointAt( geom, vertexNumber, parent );
if ( !v.isNull() )
return QVariant( v.value<QgsPoint>().x() );
else
return QVariant();
}

// function used for the old $ style
static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
FEAT_FROM_CONTEXT( context, feature )
const QgsGeometry geom = feature.geometry();
int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );

QVariant v = pointAt( geom, idx, parent );

if ( !v.isNull() )
return QVariant( v.value<QgsPoint>().y() );
else
return QVariant();
}
static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
{
if ( values.at( 1 ).isNull() ) // the case where the alias y_at function is called like a $ function (y_at(i))
{
return fcnOldYat( values, f, parent, node );
}
else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (this values is at the second position)
{
return fcnOldYat( QVariantList() << values[1], f, parent, node );
}

QgsPointXY p = g.vertexAt( idx );
return QVariant( QPointF( p.x(), p.y() ) );
QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
if ( geom.isNull() )
{
return QVariant();
}

QVariant v = pointAt( geom, vertexNumber, parent );
if ( !v.isNull() )
return QVariant( v.value<QgsPoint>().y() );
else
return QVariant();
}

static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariant v = pointAt( values, f, parent );
if ( v.type() == QVariant::PointF )
return QVariant( v.toPointF().x() );
QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
if ( geom.isNull() )
{
return QVariant();
}

QVariant v = pointAt( geom, vertexNumber, parent );
if ( !v.isNull() && v.value<QgsPoint>().is3D() )
return QVariant( v.value<QgsPoint>().z() );
else
return QVariant();
}
static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )

static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariant v = pointAt( values, f, parent );
if ( v.type() == QVariant::PointF )
return QVariant( v.toPointF().y() );
QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
if ( geom.isNull() )
{
return QVariant();
}

QVariant v = pointAt( geom, vertexNumber, parent );
if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
return QVariant( v.value<QgsPoint>().m() );
else
return QVariant();
}


static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
{
if ( !context )
Expand Down Expand Up @@ -8022,11 +8112,18 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
#endif
QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
}, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );

functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "i" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "i" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "i" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "i" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );

QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
xAtFunc->setIsStatic( false );
functions << xAtFunc;

QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) );

QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
yAtFunc->setIsStatic( false );
functions << yAtFunc;

Expand Down

0 comments on commit d8131a0

Please sign in to comment.