Skip to content
Permalink
Browse files

Lazy construct expression context for memory provider feature sources

It's not free to calculate, and is only used when
iterating over a memory layer with a subset string set

Results in a big speed up to scripts which fire off many individual
feature requests to a memory provider layer
  • Loading branch information
nyalldawson committed Oct 12, 2020
1 parent 5c8013d commit 1d2bb41752c51e6c9b84124a1ce1599d3d42b41c
@@ -47,7 +47,7 @@ QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *sour
if ( !mSource->mSubsetString.isEmpty() )
{
mSubsetExpression = qgis::make_unique< QgsExpression >( mSource->mSubsetString );
mSubsetExpression->prepare( &mSource->mExpressionContext );
mSubsetExpression->prepare( mSource->expressionContext() );
}

if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
@@ -63,7 +63,7 @@ QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *sour
{
mUsingFeatureIdList = true;
mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect );
QgsDebugMsg( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ) );
QgsDebugMsgLevel( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ), 2 );
}
else if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
{
@@ -138,8 +138,8 @@ bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature &feature )

if ( hasFeature && mSubsetExpression )
{
mSource->mExpressionContext.setFeature( candidate );
if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
mSource->expressionContext()->setFeature( candidate );
if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() )
hasFeature = false;
}

@@ -198,8 +198,8 @@ bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature &feature )

if ( mSubsetExpression )
{
mSource->mExpressionContext.setFeature( *mSelectIterator );
if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
mSource->expressionContext()->setFeature( *mSelectIterator );
if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() )
hasFeature = false;
}

@@ -257,14 +257,26 @@ QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p )
, mSubsetString( p->mSubsetString )
, mCrs( p->mCrs )
{
mExpressionContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() );
mExpressionContext.setFields( mFields );
}

QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request )
{
return QgsFeatureIterator( new QgsMemoryFeatureIterator( this, false, request ) );
}

QgsExpressionContext *QgsMemoryFeatureSource::expressionContext()
{
// lazy construct expression context -- it's not free to calculate, and is only used when
// iterating over a memory layer with a subset string set
if ( !mExpressionContext )
{
mExpressionContext = qgis::make_unique< QgsExpressionContext >(
QList<QgsExpressionContextScope *>()
<< QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
mExpressionContext->setFields( mFields );
}
return mExpressionContext.get();
}

///@endcond PRIVATE
@@ -38,12 +38,14 @@ class QgsMemoryFeatureSource final: public QgsAbstractFeatureSource

QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override;

QgsExpressionContext *expressionContext();

private:
QgsFields mFields;
QgsFeatureMap mFeatures;
std::unique_ptr< QgsSpatialIndex > mSpatialIndex;
QString mSubsetString;
QgsExpressionContext mExpressionContext;
std::unique_ptr< QgsExpressionContext > mExpressionContext;
QgsCoordinateReferenceSystem mCrs;

friend class QgsMemoryFeatureIterator;

0 comments on commit 1d2bb41

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