Skip to content
Permalink
Browse files

Lazily initialize QgsDistanceArea in expressions

This is barely ever used (only when the $perimeter, $length or $area
functions are used in an expression), and is expensive to construct.

We've been merrily creating them EVERY time we prepare an expression
resulting in a lot of wasted effort. By lazily creating it only
if needed we save a bunch of wasted processing and ultimately get
MUCH faster expression preparation.

(especially noticable when animating vector layers using data defined
symbol settings or categorized/graduated/rule based renderers)
  • Loading branch information
nyalldawson committed May 7, 2020
1 parent 753a684 commit e9bc4832493792db05fd872f77b92e6428f587b2
Showing with 17 additions and 9 deletions.
  1. +13 −9 src/core/expression/qgsexpression.cpp
  2. +4 −0 src/core/expression/qgsexpression_p.h
@@ -272,15 +272,11 @@ void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
// Set the geometry calculator from the context if it has not been set by setGeomCalculator()
if ( context && ! d->mCalc )
{
QString ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
QgsCoordinateReferenceSystem crs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
QgsCoordinateTransformContext tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
if ( crs.isValid() )
{
d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
d->mCalc->setEllipsoid( ellipsoid.isEmpty() ? geoNone() : ellipsoid );
d->mCalc->setSourceCrs( crs, tContext );
}
// actually don't do it right away, cos it's expensive to create and only a very small number of expression
// functions actually require it. Let's lazily construct it when needed
d->mDaEllipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
d->mDaCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
d->mDaTransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
}

// Set the distance units from the context if it has not been set by setDistanceUnits()
@@ -397,6 +393,14 @@ QString QgsExpression::dump() const

QgsDistanceArea *QgsExpression::geomCalculator()
{
if ( !d->mCalc && d->mDaCrs.isValid() )
{
// calculator IS required, so initialize it now...
d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
d->mCalc->setEllipsoid( d->mDaEllipsoid.isEmpty() ? geoNone() : d->mDaEllipsoid );
d->mCalc->setSourceCrs( d->mDaCrs, d->mDaTransformContext );
}

return d->mCalc.get();
}

@@ -67,6 +67,10 @@ class QgsExpressionPrivate

QString mExp;

QString mDaEllipsoid;
QgsCoordinateReferenceSystem mDaCrs;
QgsCoordinateTransformContext mDaTransformContext;

std::shared_ptr<QgsDistanceArea> mCalc;
QgsUnitTypes::DistanceUnit mDistanceUnit = QgsUnitTypes::DistanceUnknownUnit;
QgsUnitTypes::AreaUnit mAreaUnit = QgsUnitTypes::AreaUnknownUnit;

0 comments on commit e9bc483

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