Skip to content

Commit 6f9cf1b

Browse files
authored
Merge pull request #4147 from nyalldawson/exp_feature
Optimise expression context storage/retrieval of features
2 parents 18b9f40 + 4790035 commit 6f9cf1b

File tree

6 files changed

+86
-37
lines changed

6 files changed

+86
-37
lines changed

doc/api_break.dox

+6
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,12 @@ QgsExpressionItem {#qgis_api_break_3_0_QgsExpressionItem}
992992
- CustomSortRole was renamed to CUSTOM_SORT_ROLE
993993
- ItemTypeRole was renamed to ITEM_TYPE_ROLE
994994

995+
QgsExpressionContext {#qgis_api_break_3_0_QgsExpressionContext}
996+
--------------------
997+
998+
- EXPR_FEATURE was removed. Use the direct feature manipulation methods feature(), hasFeature() and setFeature() instead
999+
1000+
9951001
QgsExpressionContextUtils {#qgis_api_break_3_0_QgsExpressionContextUtils}
9961002
-------------------------
9971003

python/core/qgsexpressioncontext.sip

+7-18
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,16 @@ class QgsExpressionContextScope
189189
*/
190190
QStringList functionNames() const;
191191

192-
/** Adds a function to the scope.
193-
* @param name function name
194-
* @param function function to insert. Ownership is transferred to the scope.
195-
* @see addVariable()
196-
*/
197192
void addFunction( const QString& name, QgsScopedExpressionFunction* function /Transfer/ );
198193

199-
/** Convenience function for setting a feature for the scope. Any existing
200-
* feature set by the scope will be overwritten.
201-
* @param feature feature for scope
202-
*/
194+
bool hasFeature() const;
195+
196+
QgsFeature feature() const;
197+
203198
void setFeature( const QgsFeature& feature );
204199

205-
/** Convenience function for setting a fields for the scope. Any existing
206-
* fields set by the scope will be overwritten.
207-
* @param fields fields for scope
208-
*/
200+
void removeFeature();
201+
209202
void setFields( const QgsFields& fields );
210203
};
211204

@@ -414,9 +407,7 @@ class QgsExpressionContext
414407
*/
415408
void setFeature( const QgsFeature& feature );
416409

417-
/** Convenience function for retrieving the feature for the context, if set.
418-
* @see setFeature
419-
*/
410+
bool hasFeature() const;
420411
QgsFeature feature() const;
421412

422413
/** Convenience function for setting a fields for the context. The fields
@@ -481,8 +472,6 @@ class QgsExpressionContext
481472

482473
//! Inbuilt variable name for fields storage
483474
static const QString EXPR_FIELDS;
484-
//! Inbuilt variable name for feature storage
485-
static const QString EXPR_FEATURE;
486475
//! Inbuilt variable name for value original value variable
487476
static const QString EXPR_ORIGINAL_VALUE;
488477
//! Inbuilt variable name for symbol color variable

src/core/qgsexpression.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ static QgsFeature getFeature( const QVariant& value, QgsExpression* parent )
318318
return 0;
319319
}
320320

321-
#define FEAT_FROM_CONTEXT(c, f) if (!(c) || !(c)->hasVariable(QgsExpressionContext::EXPR_FEATURE)) return QVariant(); \
322-
QgsFeature f = qvariant_cast<QgsFeature>( (c)->variable( QgsExpressionContext::EXPR_FEATURE ) );
321+
#define FEAT_FROM_CONTEXT(c, f) if (!(c) || !(c)->hasFeature() ) return QVariant(); \
322+
QgsFeature f = ( c )->feature();
323323

324324
static QgsExpression::Node* getNode( const QVariant& value, QgsExpression* parent )
325325
{
@@ -5495,7 +5495,7 @@ QVariant QgsExpression::NodeColumnRef::eval( QgsExpression *parent, const QgsExp
54955495
}
54965496
}
54975497

5498-
if ( context && context->hasVariable( QgsExpressionContext::EXPR_FEATURE ) )
5498+
if ( context && context->hasFeature() )
54995499
{
55005500
QgsFeature feature = context->feature();
55015501
if ( index >= 0 )

src/core/qgsexpressioncontext.cpp

+23-6
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434

3535

3636
const QString QgsExpressionContext::EXPR_FIELDS( QStringLiteral( "_fields_" ) );
37-
const QString QgsExpressionContext::EXPR_FEATURE( QStringLiteral( "_feature_" ) );
3837
const QString QgsExpressionContext::EXPR_ORIGINAL_VALUE( QStringLiteral( "value" ) );
3938
const QString QgsExpressionContext::EXPR_SYMBOL_COLOR( QStringLiteral( "symbol_color" ) );
4039
const QString QgsExpressionContext::EXPR_SYMBOL_ANGLE( QStringLiteral( "symbol_angle" ) );
@@ -58,6 +57,8 @@ QgsExpressionContextScope::QgsExpressionContextScope( const QString& name )
5857
QgsExpressionContextScope::QgsExpressionContextScope( const QgsExpressionContextScope& other )
5958
: mName( other.mName )
6059
, mVariables( other.mVariables )
60+
, mHasFeature( other.mHasFeature )
61+
, mFeature( other.mFeature )
6162
{
6263
QHash<QString, QgsScopedExpressionFunction* >::const_iterator it = other.mFunctions.constBegin();
6364
for ( ; it != other.mFunctions.constEnd(); ++it )
@@ -70,6 +71,8 @@ QgsExpressionContextScope& QgsExpressionContextScope::operator=( const QgsExpres
7071
{
7172
mName = other.mName;
7273
mVariables = other.mVariables;
74+
mHasFeature = other.mHasFeature;
75+
mFeature = other.mFeature;
7376

7477
qDeleteAll( mFunctions );
7578
mFunctions.clear();
@@ -196,10 +199,6 @@ void QgsExpressionContextScope::addFunction( const QString& name, QgsScopedExpre
196199
mFunctions.insert( name, function );
197200
}
198201

199-
void QgsExpressionContextScope::setFeature( const QgsFeature &feature )
200-
{
201-
addVariable( StaticVariable( QgsExpressionContext::EXPR_FEATURE, QVariant::fromValue( feature ), true ) );
202-
}
203202

204203
void QgsExpressionContextScope::setFields( const QgsFields &fields )
205204
{
@@ -468,9 +467,27 @@ void QgsExpressionContext::setFeature( const QgsFeature &feature )
468467
mStack.last()->setFeature( feature );
469468
}
470469

470+
bool QgsExpressionContext::hasFeature() const
471+
{
472+
Q_FOREACH ( const QgsExpressionContextScope* scope, mStack )
473+
{
474+
if ( scope->hasFeature() )
475+
return true;
476+
}
477+
return false;
478+
}
479+
471480
QgsFeature QgsExpressionContext::feature() const
472481
{
473-
return qvariant_cast<QgsFeature>( variable( QgsExpressionContext::EXPR_FEATURE ) );
482+
//iterate through stack backwards, so that higher priority variables take precedence
483+
QList< QgsExpressionContextScope* >::const_iterator it = mStack.constEnd();
484+
while ( it != mStack.constBegin() )
485+
{
486+
--it;
487+
if (( *it )->hasFeature() )
488+
return ( *it )->feature();
489+
}
490+
return QgsFeature();
474491
}
475492

476493
void QgsExpressionContext::setFields( const QgsFields &fields )

src/core/qgsexpressioncontext.h

+36-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <QString>
2222
#include <QStringList>
2323
#include <QSet>
24+
#include "qgsfeature.h"
2425
#include "qgsexpression.h"
2526

2627
class QgsExpression;
@@ -250,11 +251,36 @@ class CORE_EXPORT QgsExpressionContextScope
250251
*/
251252
void addFunction( const QString& name, QgsScopedExpressionFunction* function );
252253

254+
/**
255+
* Returns true if the scope has a feature associated with it.
256+
* @note added in QGIS 3.0
257+
* @see feature()
258+
*/
259+
bool hasFeature() const { return mHasFeature; }
260+
261+
/**
262+
* Sets the feature associated with the scope.
263+
* @see setFeature()
264+
* @see hasFeature()
265+
* @note added in QGIS 3.0
266+
*/
267+
QgsFeature feature() const { return mFeature; }
268+
253269
/** Convenience function for setting a feature for the scope. Any existing
254270
* feature set by the scope will be overwritten.
255271
* @param feature feature for scope
272+
* @see removeFeature()
273+
* @see feature()
256274
*/
257-
void setFeature( const QgsFeature& feature );
275+
void setFeature( const QgsFeature& feature ) { mHasFeature = true; mFeature = feature; }
276+
277+
/**
278+
* Removes any feature associated with the scope.
279+
* @note added in QGIS 3.0
280+
* @see setFeature()
281+
* @see hasFeature()
282+
*/
283+
void removeFeature() { mHasFeature = false; mFeature = QgsFeature(); }
258284

259285
/** Convenience function for setting a fields for the scope. Any existing
260286
* fields set by the scope will be overwritten.
@@ -266,6 +292,8 @@ class CORE_EXPORT QgsExpressionContextScope
266292
QString mName;
267293
QHash<QString, StaticVariable> mVariables;
268294
QHash<QString, QgsScopedExpressionFunction* > mFunctions;
295+
bool mHasFeature = false;
296+
QgsFeature mFeature;
269297

270298
bool variableNameSort( const QString &a, const QString &b );
271299
};
@@ -474,6 +502,13 @@ class CORE_EXPORT QgsExpressionContext
474502
*/
475503
void setFeature( const QgsFeature& feature );
476504

505+
/**
506+
* Returns true if the context has a feature associated with it.
507+
* @note added in QGIS 3.0
508+
* @see feature()
509+
*/
510+
bool hasFeature() const;
511+
477512
/** Convenience function for retrieving the feature for the context, if set.
478513
* @see setFeature
479514
*/
@@ -541,8 +576,6 @@ class CORE_EXPORT QgsExpressionContext
541576

542577
//! Inbuilt variable name for fields storage
543578
static const QString EXPR_FIELDS;
544-
//! Inbuilt variable name for feature storage
545-
static const QString EXPR_FEATURE;
546579
//! Inbuilt variable name for value original value variable
547580
static const QString EXPR_ORIGINAL_VALUE;
548581
//! Inbuilt variable name for symbol color variable

tests/src/core/testqgsexpressioncontext.cpp

+11-7
Original file line numberDiff line numberDiff line change
@@ -432,27 +432,31 @@ void TestQgsExpressionContext::evaluate()
432432
void TestQgsExpressionContext::setFeature()
433433
{
434434
QgsFeature feature( 50LL );
435+
feature.setValid( true );
435436
QgsExpressionContextScope scope;
436437
scope.setFeature( feature );
437-
QVERIFY( scope.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
438-
QCOMPARE(( qvariant_cast<QgsFeature>( scope.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
438+
QVERIFY( scope.hasFeature() );
439+
QCOMPARE( scope.feature().id(), 50LL );
440+
scope.removeFeature();
441+
QVERIFY( !scope.hasFeature() );
442+
QVERIFY( !scope.feature().isValid() );
439443

440444
//test setting a feature in a context with no scopes
441445
QgsExpressionContext emptyContext;
442446
QVERIFY( !emptyContext.feature().isValid() );
443447
emptyContext.setFeature( feature );
444448
//setFeature should have created a scope
445449
QCOMPARE( emptyContext.scopeCount(), 1 );
446-
QVERIFY( emptyContext.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
447-
QCOMPARE(( qvariant_cast<QgsFeature>( emptyContext.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
450+
QVERIFY( emptyContext.feature().isValid() );
451+
QCOMPARE( emptyContext.feature().id(), 50LL );
448452
QCOMPARE( emptyContext.feature().id(), 50LL );
449453

450454
QgsExpressionContext contextWithScope;
451455
contextWithScope << new QgsExpressionContextScope();
452456
contextWithScope.setFeature( feature );
453457
QCOMPARE( contextWithScope.scopeCount(), 1 );
454-
QVERIFY( contextWithScope.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
455-
QCOMPARE(( qvariant_cast<QgsFeature>( contextWithScope.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
458+
QVERIFY( contextWithScope.feature().isValid() );
459+
QCOMPARE( contextWithScope.feature().id(), 50LL );
456460
QCOMPARE( contextWithScope.feature().id(), 50LL );
457461
}
458462

@@ -652,7 +656,7 @@ void TestQgsExpressionContext::featureBasedContext()
652656

653657
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, fields );
654658

655-
QgsFeature evalFeature = qvariant_cast<QgsFeature>( context.variable( QStringLiteral( "_feature_" ) ) );
659+
QgsFeature evalFeature = context.feature();
656660
QgsFields evalFields = qvariant_cast<QgsFields>( context.variable( QStringLiteral( "_fields_" ) ) );
657661
QCOMPARE( evalFeature.attributes(), f.attributes() );
658662
QCOMPARE( evalFields, fields );

0 commit comments

Comments
 (0)