Skip to content
Permalink
Browse files

Merge pull request #9192 from mhugo/backports

Backports
  • Loading branch information
Hugo Mercier
Hugo Mercier committed Feb 19, 2019
2 parents c4ef769 + 3e60181 commit db4bf524cc52c110043e1ddac548ca6a5957c443
@@ -293,8 +293,12 @@ Returns calculator used for distance and area calculations
void setGeomCalculator( const QgsDistanceArea *calc );
%Docstring
Sets the geometry calculator used for distance and area calculations in expressions.
(used by $length, $area and $perimeter functions only). By default, no geometry
calculator is set and all distance and area calculations are performed using simple
(used by $length, $area and $perimeter functions only).
If the geometry calculator is set to None (default), prepare() will read variables
from the expression context ("project_ellipsoid", "_project_transform_context" and
"_layer_crs") to build a geometry calculator.
If these variables does not exist and if setGeomCalculator() is not called,
all distance and area calculations are performed using simple
Cartesian methods (ie no ellipsoidal calculations).

:param calc: geometry calculator. Ownership is not transferred. Set to a None to force
@@ -321,6 +325,8 @@ Returns the desired distance units for calculations involving geomCalculator(),
void setDistanceUnits( QgsUnitTypes::DistanceUnit unit );
%Docstring
Sets the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
If distance units are set to QgsUnitTypes.DistanceUnknownUnit (default), prepare() will read
variables from the expression context ("project_distance_units") to determine distance units.

.. note::

@@ -351,6 +357,8 @@ Returns the desired areal units for calculations involving geomCalculator(), e.g
void setAreaUnits( QgsUnitTypes::AreaUnit unit );
%Docstring
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
If distance units are set to QgsUnitTypes.AreaUnknownUnit (default), prepare() will read
variables from the expression context ("project_distance_units") to determine distance units.

.. note::

@@ -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,37 @@ bool QgsExpression::needsGeometry() const
return d->mRootNode->needsGeometry();
}

void QgsExpression::initGeomCalculator()
void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
{
if ( d->mCalc )
return;
// 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() ? GEO_NONE : ellipsoid );
d->mCalc->setSourceCrs( crs, tContext );
}
}

// Set the distance units from the context if it has not been set by setDistanceUnits()
if ( context && distanceUnits() == QgsUnitTypes::DistanceUnknownUnit )
{
QString distanceUnitsStr = context->variable( QStringLiteral( "project_distance_units" ) ).toString();
if ( ! distanceUnitsStr.isEmpty() )
setDistanceUnits( QgsUnitTypes::stringToDistanceUnit( distanceUnitsStr ) );
}

// Use planimetric as default
d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
// Set the area units from the context if it has not been set by setAreaUnits()
if ( context && areaUnits() == QgsUnitTypes::AreaUnknownUnit )
{
QString areaUnitsStr = context->variable( QStringLiteral( "project_area_units" ) ).toString();
if ( ! areaUnitsStr.isEmpty() )
setAreaUnits( QgsUnitTypes::stringToAreaUnit( areaUnitsStr ) );
}
}

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

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

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

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

@@ -684,8 +716,10 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts.insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_short_version" ), QCoreApplication::translate( "variable_help", "Short QGIS version string." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
sVariableHelpTexts.insert( QStringLiteral( "qgis_locale" ), QCoreApplication::translate( "variable_help", "Two letter identifier for current QGIS locale." ) );
sVariableHelpTexts.insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
sVariableHelpTexts.insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );

@@ -703,6 +737,9 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts.insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
sVariableHelpTexts.insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
sVariableHelpTexts.insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
sVariableHelpTexts.insert( QStringLiteral( "project_area_units" ), QCoreApplication::translate( "variable_help", "Area unit for current project, used when calculating areas of geometries." ) );
sVariableHelpTexts.insert( QStringLiteral( "project_distance_units" ), QCoreApplication::translate( "variable_help", "Distance unit for current project, used when calculating lengths of geometries." ) );
sVariableHelpTexts.insert( QStringLiteral( "project_ellipsoid" ), QCoreApplication::translate( "variable_help", "Name of ellipsoid of current project, used when calculating geodetic areas and lengths of geometries." ) );

//layer variables
sVariableHelpTexts.insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
@@ -379,8 +379,12 @@ class CORE_EXPORT QgsExpression

/**
* Sets the geometry calculator used for distance and area calculations in expressions.
* (used by $length, $area and $perimeter functions only). By default, no geometry
* calculator is set and all distance and area calculations are performed using simple
* (used by $length, $area and $perimeter functions only).
* If the geometry calculator is set to nullptr (default), prepare() will read variables
* from the expression context ("project_ellipsoid", "_project_transform_context" and
* "_layer_crs") to build a geometry calculator.
* If these variables does not exist and if setGeomCalculator() is not called,
* all distance and area calculations are performed using simple
* Cartesian methods (ie no ellipsoidal calculations).
* \param calc geometry calculator. Ownership is not transferred. Set to a nullptr to force
* Cartesian calculations.
@@ -399,6 +403,8 @@ class CORE_EXPORT QgsExpression

/**
* Sets the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
* If distance units are set to QgsUnitTypes::DistanceUnknownUnit (default), prepare() will read
* variables from the expression context ("project_distance_units") to determine distance units.
* \note distances are only converted when a geomCalculator() has been set
* \see distanceUnits()
* \see setAreaUnits()
@@ -417,6 +423,8 @@ class CORE_EXPORT QgsExpression

/**
* Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
* If distance units are set to QgsUnitTypes::AreaUnknownUnit (default), prepare() will read
* variables from the expression context ("project_distance_units") to determine distance units.
* \note areas are only converted when a geomCalculator() has been set
* \see areaUnits()
* \see setDistanceUnits()
@@ -619,7 +627,7 @@ class CORE_EXPORT QgsExpression
#endif

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

struct HelpArg SIP_SKIP
{
@@ -1261,16 +1261,18 @@ bool QgsExpressionNodeColumnRef::prepareNode( QgsExpression *parent, const QgsEx
QgsFields fields = qvariant_cast<QgsFields>( context->variable( QgsExpressionContext::EXPR_FIELDS ) );

mIndex = fields.lookupField( mName );
if ( mIndex >= 0 )

if ( mIndex == -1 && context->hasFeature() )
{
return true;
mIndex = context->feature().fieldNameIndex( mName );
}
else

if ( mIndex == -1 )
{
parent->setEvalErrorString( tr( "Column '%1' not found" ).arg( mName ) );
mIndex = -1;
return false;
}
return true;
}

QString QgsExpressionNodeColumnRef::dump() const
@@ -22,6 +22,8 @@
#include "qgis.h"
#include "qgsdatumtransform.h"

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

};

Q_DECLARE_METATYPE( QgsCoordinateTransformContext )

#endif // QGSCOORDINATETRANSFORMCONTEXT_H


@@ -807,6 +807,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 ) );
@@ -885,6 +889,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 );
@@ -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

@@ -144,12 +144,12 @@ void QgsRangeWidgetWrapper::initWidget( QWidget *editor )
int minval = min.toInt();
if ( allowNull )
{
int stepval = step.isValid() ? step.toInt() : 1;
int newMinval = minval - stepval;
uint stepval = step.isValid() ? step.toUInt() : 1;
// make sure there is room for a new value (i.e. signed integer does not overflow)
if ( newMinval < minval )
int minvalOverflow = uint( minval ) - stepval;
if ( minvalOverflow < minval )
{
minval = newMinval;
minval = minvalOverflow;
}
mIntSpinBox->setValue( minval );
QgsSpinBox *intSpinBox( qobject_cast<QgsSpinBox *>( mIntSpinBox ) );
@@ -1420,6 +1420,7 @@ void TestQgsLayoutItem::itemVariablesFunction()
map->setId( QStringLiteral( "Map_id" ) );

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

@@ -258,12 +258,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 db4bf52

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