Skip to content

Commit

Permalink
[FEATURE] New expression function "attributes"
Browse files Browse the repository at this point in the history
Returns a map containing all attributes from a feature, with field
names as map keys. We've got featureful, robust support for working
with maps in expressions now, so this allows rapid conversion
of all feature attributes to a map to use with these handy
functions.
  • Loading branch information
nyalldawson committed Jun 28, 2019
1 parent 99d6524 commit 018ca7c
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
14 changes: 14 additions & 0 deletions resources/function_help/json/attributes
@@ -0,0 +1,14 @@
{
"name": "attributes",
"type": "function",
"description": "Returns a map containing all attributes from a feature, with field names as map keys.",
"variants": [
{ "variant": "Variant 1",
"variant_description": "Returns a map of all attributes from the current feature.",
"examples": [ { "expression":"attributes()['name']", "returns":"value stored in 'name' attribute for the current feature"}] },
{ "variant": "Variant 2",
"variant_description": "Allows the target feature to be specified.",
"arguments": [ {"arg":"feature","description":"a feature"}],
"examples": [ { "expression":"attributes( @atlas_feature )['name']", "returns":"value stored in 'name' attribute for the current atlas feature"}] }
]
}
27 changes: 26 additions & 1 deletion src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -1394,6 +1394,7 @@ static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *co

return context->feature();
}

static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsFeature feature;
Expand All @@ -1417,6 +1418,28 @@ static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionCon
return feature.attribute( attr );
}

static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsFeature feature;
QString attr;
if ( values.size() == 0 || values.at( 0 ).isNull() )
{
feature = context->feature();
}
else
{
feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
}

const QgsFields fields = feature.fields();
QVariantMap result;
for ( int i = 0; i < fields.count(); ++i )
{
result.insert( fields.at( i ).name(), feature.attribute( i ) );
}
return result;
}

static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsVectorLayer *layer = nullptr;
Expand Down Expand Up @@ -5488,7 +5511,9 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false )
<< new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );

QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
QStringLiteral( "is_selected" ),
Expand Down
30 changes: 30 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -1602,6 +1602,36 @@ class TestQgsExpression: public QObject
QCOMPARE( v.toString(), QString( "test value" ) );
}

void eval_feature_attributes()
{
QgsFeature f( 100 );
QgsFields fields;
fields.append( QgsField( QStringLiteral( "col1" ) ) );
fields.append( QgsField( QStringLiteral( "second_column" ), QVariant::Int ) );
f.setFields( fields, true );
f.setAttribute( QStringLiteral( "col1" ), QStringLiteral( "test value" ) );
f.setAttribute( QStringLiteral( "second_column" ), 5 );
QgsExpression exp( QStringLiteral( "attributes()['col1']" ) );
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, QgsFields() );
QVariant v = exp.evaluate( &context );
QCOMPARE( v.toString(), QString( "test value" ) );
QgsExpression exp2( QStringLiteral( "attributes()['second_column']" ) );
v = exp2.evaluate( &context );
QCOMPARE( v.toInt(), 5 );

QgsExpression exp3( QStringLiteral( "attributes($currentfeature)['col1']" ) );
v = exp.evaluate( &context );
QCOMPARE( v.toString(), QString( "test value" ) );
QgsExpression exp4( QStringLiteral( "attributes($currentfeature)['second_column']" ) );
v = exp4.evaluate( &context );
QCOMPARE( v.toInt(), 5 );

QgsExpression exp5( QStringLiteral( "attributes('a')" ) );
v = exp5.evaluate( &context );
QVERIFY( v.isNull() );
QVERIFY( exp5.hasEvalError() );
}

void eval_get_feature_data()
{
QTest::addColumn<QString>( "string" );
Expand Down

0 comments on commit 018ca7c

Please sign in to comment.