Skip to content
Permalink
Browse files

add maptip, expression display and eval_template expressions

  • Loading branch information
Gustry authored and nyalldawson committed Nov 4, 2019
1 parent 6a0ae06 commit e7556c4ea66f980b8f003de28c7ec3b01cced211
@@ -0,0 +1,39 @@
{
"name": "display_expression",
"type": "function",
"description": "Returns the display expression for a given feature in a layer. If called with no parameters, it evaluates the current feature. The expression is evaluated by default.",
"arguments": [
{
"arg": "feature",
"optional": true,
"default": "current feature",
"description": "The feature which should be evaluated."
},
{
"arg": "layer",
"optional": true,
"default": "current layer",
"description": "The layer (or its id or name)."
},
{
"arg": "evaluate",
"description": "If the expression must be evaluated. If false, the expression will be returned as a string literal only (which could potentially be later evaluated using the 'eval' function).",
"optional": true,
"default": "true"
}
],
"examples": [
{
"expression": "display_expression()",
"returns": "The display expression of the current feature."
},
{
"expression": "display_expression($currentfeature)",
"returns": "The display expression for a given feature."
},
{
"expression": "display_expression('a_layer_id', $currentfeature, 'False')",
"returns": "The display expression of the current feature not evaluated."
}
]
}
@@ -0,0 +1,13 @@
{
"name": "eval_template",
"type": "function",
"description": "Evaluates a template which is passed in a string. Useful to expand dynamic parameters passed as context variables or fields.",
"arguments": [{
"arg": "template",
"description": "a template string"
}],
"examples": [{
"expression": "eval_template('QGIS [% upper(\\\\'rocks\\\\') %]')",
"returns": "QGIS ROCKS"
}]
}
@@ -0,0 +1,39 @@
{
"name": "maptip",
"type": "function",
"description": "Returns the maptip for a given feature in a layer. If called with no parameters, it evaluates the current feature. The maptip is evaluated by default.",
"arguments": [
{
"arg": "feature",
"optional": true,
"default": "current feature",
"description": "The feature which should be evaluated."
},
{
"arg": "layer",
"optional": true,
"default": "current layer",
"description": "The layer (or its id or name)."
},
{
"arg": "evaluate",
"description": "If the expression must be evaluated. If false, the expression will be returned as a string literal only (which could potentially be later evaluated using the 'eval_template' function).",
"optional": true,
"default": "true"
}
],
"examples": [
{
"expression": "maptip()",
"returns": "The maptip of the current feature."
},
{
"expression": "maptip($currentFeature)",
"returns": "The maptip of the current feature."
},
{
"expression": "maptip('a_layer_id', $currentFeature, 'False')",
"returns": "The maptip of the current feature not evaluated."
}
]
}
@@ -272,6 +272,12 @@ static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionC
return context->variable( name );
}

static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
return QgsExpression::replaceExpressionText( templateString, context );
}

static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
if ( !context )
@@ -1513,6 +1519,96 @@ static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionCo
return result;
}

static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
{
QgsVectorLayer *layer = nullptr;
QgsFeature feature;
bool evaluate = true;

if ( values.isEmpty() )
{
feature = context->feature();
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
}
else if ( values.size() == 1 )
{
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
}
else if ( values.size() == 2 )
{
layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
}
else if ( values.size() == 3 )
{
layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
evaluate = values.value( 2 ).toBool();
}
else
{
if ( isMaptip )
{
parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %1 given." ).arg( values.length() ) );
}
else
{
parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %1 given." ).arg( values.length() ) );
}
return QVariant();
}

if ( !layer )
{
parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
return QVariant( );
}

if ( !feature.isValid() )
{
parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
return QVariant( );
}

if ( ! evaluate )
{
if ( isMaptip )
{
return layer->mapTipTemplate();
}
else
{
return layer->displayExpression();
}
}

QgsExpressionContext subContext( *context );
subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
subContext.setFeature( feature );

if ( isMaptip )
{
return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
}
else
{
QgsExpression exp( layer->displayExpression() );
exp.prepare( &subContext );
return exp.evaluate( &subContext ).toString();
}
}

static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
}

static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
}

static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsVectorLayer *layer = nullptr;
@@ -5903,6 +5999,30 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );

QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
QStringLiteral( "maptip" ),
-1,
fcnFeatureMaptip,
QStringLiteral( "Record and Attributes" ),
QString(),
false,
QSet<QString>()
);
maptipFunc->setIsStatic( false );
functions << maptipFunc;

QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction(
QStringLiteral( "display_expression" ),
-1,
fcnFeatureDisplayExpression,
QStringLiteral( "Record and Attributes" ),
QString(),
false,
QSet<QString>()
);
displayFunc->setIsStatic( false );
functions << displayFunc;

QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
QStringLiteral( "is_selected" ),
-1,
@@ -6015,6 +6135,9 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()

functions
<< varFunction;

functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );

QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
evalFunc->setIsStaticFunction(
[]( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
@@ -94,6 +94,8 @@ class TestQgsExpression: public QObject
mPointsLayer->setAttributionUrl( QStringLiteral( "attribution url" ) );
mPointsLayer->setMinimumScale( 500 );
mPointsLayer->setMaximumScale( 1000 );
mPointsLayer->setMapTipTemplate( QStringLiteral( "Maptip with class = [% \"Class\" %]" ) );
mPointsLayer->setDisplayExpression( QStringLiteral( "'Display expression with class = ' || \"Class\"" ) );

mPointsLayerMetadata = new QgsVectorLayer( pointFileInfo.filePath(),
pointFileInfo.completeBaseName() + "_metadata", QStringLiteral( "ogr" ) );
@@ -1411,6 +1413,11 @@ class TestQgsExpression: public QObject
QTest::newRow( "right associativity" ) << "(2^3)^2" << false << QVariant( 64. );
QTest::newRow( "left associativity" ) << "1-(2-1)" << false << QVariant( 0 );

// eval_template tests
QTest::newRow( "eval_template" ) << QStringLiteral( "eval_template(\'this is a [% \\'template\\' || \\'!\\' %]\')" ) << false << QVariant( "this is a template!" );
QTest::newRow( "eval_template string" ) << QStringLiteral( "eval_template('string')" ) << false << QVariant( "string" );
QTest::newRow( "eval_template expression" ) << QStringLiteral( "eval_template('a' || ' string')" ) << false << QVariant( "a string" );

// layer_property tests
QTest::newRow( "layer_property no layer" ) << "layer_property('','title')" << false << QVariant();
QTest::newRow( "layer_property bad layer" ) << "layer_property('bad','title')" << false << QVariant();
@@ -2015,6 +2022,63 @@ class TestQgsExpression: public QObject
QTest::newRow( "group by with null value" ) << "sum(\"col1\", \"col4\")" << false << QVariant( 8 );
}

void maptip_display_data()
{
QTest::addColumn<QString>( "string" );
QTest::addColumn<QgsFeature>( "feature" );
QTest::addColumn<QgsVectorLayer *>( "layer" );
QTest::addColumn<bool>( "evalError" );
QTest::addColumn<QVariant>( "result" );

QgsFeature firstFeature = mPointsLayer->getFeature( 1 );
QgsVectorLayer *noLayer = nullptr;

QTest::newRow( "display not evaluated" ) << QStringLiteral( "display_expression(@layer_id, $currentfeature, False)" ) << firstFeature << mPointsLayer << false << QVariant( "'Display expression with class = ' || \"Class\"" );
QTest::newRow( "display wrong layer" ) << QStringLiteral( "display_expression()" ) << firstFeature << noLayer << true << QVariant();
QTest::newRow( "display wrong feature" ) << QStringLiteral( "display_expression()" ) << QgsFeature() << mPointsLayer << true << QVariant();

QTest::newRow( "maptip wrong feature" ) << QStringLiteral( "maptip()" ) << QgsFeature() << mPointsLayer << true << QVariant();
QTest::newRow( "maptip wrong layer" ) << QStringLiteral( "maptip()" ) << firstFeature << noLayer << true << QVariant();
QTest::newRow( "maptip not evaluated" ) << QStringLiteral( "maptip(@layer_id, $currentfeature, False)" ) << firstFeature << mPointsLayer << false << QVariant( "Maptip with class = [% \"Class\" %]" );

QTest::newRow( "maptip with 2 params" ) << QStringLiteral( "maptip(@layer_id, $currentfeature)" ) << firstFeature << mPointsLayer << false << QVariant( "Maptip with class = Biplane" );
QTest::newRow( "maptip with 1 param" ) << QStringLiteral( "maptip($currentfeature)" ) << firstFeature << mPointsLayer << false << QVariant( "Maptip with class = Biplane" );
QTest::newRow( "maptip with 0 param" ) << QStringLiteral( "maptip()" ) << firstFeature << mPointsLayer << false << QVariant( "Maptip with class = Biplane" );

QTest::newRow( "display with 2 params" ) << QStringLiteral( "display_expression(@layer_id, $currentfeature)" ) << firstFeature << mPointsLayer << false << QVariant( "Display expression with class = Biplane" );
QTest::newRow( "display with 1 param" ) << QStringLiteral( "display_expression($currentfeature)" ) << firstFeature << mPointsLayer << false << QVariant( "Display expression with class = Biplane" );
QTest::newRow( "display with 0 param" ) << QStringLiteral( "display_expression()" ) << firstFeature << mPointsLayer << false << QVariant( "Display expression with class = Biplane" );
}

void maptip_display()
{
QFETCH( QString, string );
QFETCH( QgsFeature, feature );
QFETCH( QgsVectorLayer *, layer );
QFETCH( bool, evalError );
QFETCH( QVariant, result );

QgsExpressionContext context;
context.appendScope( QgsExpressionContextUtils::globalScope() );
context.appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
if ( layer )
{
//layer->setDisplayExpression( QStringLiteral( "Display expression with class = ' || Class" ) );
context.appendScope( QgsExpressionContextUtils::layerScope( layer ) );
}
context.setFeature( feature );

QgsExpression exp( string );
exp.prepare( &context );

if ( exp.hasParserError() )
qDebug() << exp.parserErrorString();
QCOMPARE( exp.hasParserError(), false );
QVariant res = exp.evaluate( &context );
QCOMPARE( exp.hasEvalError(), evalError );
QCOMPARE( res.toString(), result.toString() );
}

void selection()
{
QFETCH( QgsFeatureIds, selectedFeatures );

0 comments on commit e7556c4

Please sign in to comment.
You can’t perform that action at this time.