[FEATURE] New expression function "attributes"

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.
This commit is contained in:
Nyall Dawson 2019-06-27 18:40:30 +10:00
parent 99d6524407
commit 018ca7c447
3 changed files with 70 additions and 1 deletions

View File

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

View File

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

View File

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