Skip to content
Permalink
Browse files

Fix $length in labels (fixes #19355)

Use the project expression scope to access project
parameters (ellipsoid and distance/area units)
  • Loading branch information
Hugo Mercier
Hugo Mercier committed Feb 14, 2019
1 parent 88afe75 commit 809196693c597eb86d1fceaec2e1ce483900adb1
@@ -185,6 +185,7 @@ Write the context's state to application settings.




/************************************************************************
* This file has been generated automatically from *
* *
@@ -102,6 +102,7 @@ void QgsExpression::setExpression( const QString &expression )
d->mRootNode = ::parseExpression( expression, d->mParserErrorString, d->mParserErrors );
d->mEvalErrorString = QString();
d->mExp = expression;
d->mIsPrepared = false;
}

QString QgsExpression::expression() const
@@ -313,13 +314,25 @@ bool QgsExpression::needsGeometry() const
return d->mRootNode->needsGeometry();
}

void QgsExpression::initGeomCalculator()
void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
{
if ( d->mCalc )
return;

// Use planimetric as default
d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
if ( ! d->mCalc )
d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
QString ellipsoid = context->variable( "project_ellipsoid" ).toString();
QString distanceUnitsStr = context->variable( "project_distance_units" ).toString();
QString areaUnitsStr = context->variable( "project_area_units" ).toString();
QgsCoordinateReferenceSystem crs = context->variable( "_layer_crs" ).value<QgsCoordinateReferenceSystem>();
QgsCoordinateTransformContext tContext = context->variable( "_project_transform_context" ).value<QgsCoordinateTransformContext>();

d->mCalc->setEllipsoid( ellipsoid.isEmpty() ? GEO_NONE : ellipsoid );
if ( ! distanceUnitsStr.isEmpty() )
setDistanceUnits( QgsUnitTypes::stringToDistanceUnit( distanceUnitsStr ) );
if ( ! areaUnitsStr.isEmpty() )
setAreaUnits( QgsUnitTypes::stringToAreaUnit( areaUnitsStr ) );
if ( crs.isValid() )
{
d->mCalc->setSourceCrs( crs, tContext );
}
}

void QgsExpression::detach()
@@ -361,6 +374,8 @@ bool QgsExpression::prepare( const QgsExpressionContext *context )
return false;
}

initGeomCalculator( context );
d->mIsPrepared = true;
return d->mRootNode->prepare( this, context );
}

@@ -385,6 +400,12 @@ QVariant QgsExpression::evaluate( const QgsExpressionContext *context )
return QVariant();
}

if ( ! d->mIsPrepared )
{
qWarning( "QgsExpression::evaluate() called on an expression not yet prepared !" );
if ( ! prepare( context ) )
return QVariant();
}
return d->mRootNode->eval( this, context );
}

@@ -619,7 +619,7 @@ class CORE_EXPORT QgsExpression
#endif

private:
void initGeomCalculator();
void initGeomCalculator( const QgsExpressionContext *context );

struct HelpArg SIP_SKIP
{
@@ -257,6 +257,10 @@ QgsExpressionContextScope *QgsExpressionContextUtils::projectScope( const QgsPro
QgsCoordinateReferenceSystem projectCrs = project->crs();
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj4(), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), project->ellipsoid(), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( project->distanceUnits() ), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( project->areaUnits() ), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( project->transformContext() ), true, true ) );

// metadata
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), project->metadata().author(), true, true ) );
@@ -335,6 +339,7 @@ QgsExpressionContextScope *QgsExpressionContextUtils::layerScope( const QgsMapLa

scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_name" ), layer->name(), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_id" ), layer->id(), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_layer_crs" ), QVariant::fromValue<QgsCoordinateReferenceSystem>( layer->crs() ), true, true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer" ), QVariant::fromValue<QgsWeakMapLayerPointer >( QgsWeakMapLayerPointer( const_cast<QgsMapLayer *>( layer ) ) ), true, true ) );

const QgsVectorLayer *vLayer = qobject_cast< const QgsVectorLayer * >( layer );
@@ -22,6 +22,7 @@
#include "qgis_sip.h"
#include "qgsdatumtransform.h"

#include <QMetaType>
#include <QExplicitlySharedDataPointer>
class QgsCoordinateReferenceSystem;
class QgsReadWriteContext;
@@ -275,6 +276,8 @@ class CORE_EXPORT QgsCoordinateTransformContext

};

Q_DECLARE_METATYPE( QgsCoordinateTransformContext )

#endif // QGSCOORDINATETRANSFORMCONTEXT_H


@@ -70,6 +70,9 @@ class QgsExpressionPrivate
std::shared_ptr<QgsDistanceArea> mCalc;
QgsUnitTypes::DistanceUnit mDistanceUnit = QgsUnitTypes::DistanceUnknownUnit;
QgsUnitTypes::AreaUnit mAreaUnit = QgsUnitTypes::AreaUnknownUnit;

//! Whether prepare() has been called before evaluate()
bool mIsPrepared = false;
};
///@endcond

@@ -1421,6 +1421,7 @@ void TestQgsLayoutItem::itemVariablesFunction()
map->setId( QStringLiteral( "Map_id" ) );

c = l.createExpressionContext();
e.prepare( &c );
r = e.evaluate( &c );
QGSCOMPARENEAR( r.toDouble(), 184764103, 100 );

@@ -261,12 +261,15 @@ void TestQgsMapSettings::testIsLayerVisible()
QCOMPARE( r.toBool(), false );

QgsProject::instance()->removeMapLayer( vlA );
e.prepare( &context );
r = e.evaluate( &context );
QCOMPARE( r.toBool(), false ); // layer is deleted
e2.prepare( &context );
r = e2.evaluate( &context );
QCOMPARE( r.toBool(), true ); // layer still exists

QgsProject::instance()->removeMapLayer( vlB );
e2.prepare( &context );
r = e2.evaluate( &context );
QCOMPARE( r.toBool(), false ); // layer is deleted

@@ -23,7 +23,8 @@
from qgis.PyQt.QtCore import Qt, QPointF, QSizeF
from qgis.PyQt.QtGui import QFont

from qgis.core import QgsLabelingEngineSettings, QgsPalLayerSettings, QgsUnitTypes, QgsTextBackgroundSettings
from qgis.core import QgsLabelingEngineSettings, QgsPalLayerSettings, QgsUnitTypes, QgsTextBackgroundSettings, QgsProject, QgsExpressionContextUtils, QgsExpressionContext
from qgis.core import QgsCoordinateReferenceSystem

from utilities import svgSymbolsPath

@@ -308,6 +309,24 @@ def test_curved_placement_below(self):
self.lyr.placementFlags = QgsPalLayerSettings.BelowLine | QgsPalLayerSettings.MapOrientation
self.checkTest()

def test_length_expression(self):
# compare length using the ellipsoid in kms and the planimetric distance in meters
self.lyr.fieldName = "round($length,5) || ' - ' || round(length($geometry),2)"
self.lyr.isExpression = True

QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:32613"))
QgsProject.instance().setEllipsoid("WGS84")
QgsProject.instance().setDistanceUnits(QgsUnitTypes.DistanceKilometers)

ctxt = QgsExpressionContext()
ctxt.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
ctxt.appendScope(QgsExpressionContextUtils.layerScope(self.layer))
self._TestMapSettings.setExpressionContext(ctxt)

self.lyr.placement = QgsPalLayerSettings.Curved
self.lyr.placementFlags = QgsPalLayerSettings.AboveLine | QgsPalLayerSettings.MapOrientation
self.checkTest()

# noinspection PyPep8Naming


Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 8091966

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