Skip to content

Commit 229ef29

Browse files
committed
If no geomCalculator is set for QgsExpression, perform cartesian
calculations for $length, $area, $perimeter (refs #13209) ie, the default is now to use planimeteric calculations unless a geomCalculator has been explicitly set
1 parent 6bbe3b9 commit 229ef29

File tree

4 files changed

+99
-12
lines changed

4 files changed

+99
-12
lines changed

python/core/qgsexpression.sip

+10-4
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,18 @@ class QgsExpression
136136
//! expression() instead.
137137
QString dump() const;
138138

139-
//! Return calculator used for distance and area calculations
140-
//! (used by internal functions)
139+
/** Return calculator used for distance and area calculations
140+
* (used by $length, $area and $perimeter functions only)
141+
* @see setGeomCalculator()
142+
*/
141143
QgsDistanceArea *geomCalculator();
142144

143-
//! Sets the geometry calculator used in evaluation of expressions,
144-
// instead of the default.
145+
/** Sets the geometry calculator used for distance and area calculations in expressions.
146+
* (used by $length, $area and $perimeter functions only). By default, no geometry
147+
* calculator is set and all distance and area calculations are performed using simple
148+
* cartesian methods (ie no ellipsoidal calculations).
149+
* @see geomCalculator()
150+
*/
145151
void setGeomCalculator( const QgsDistanceArea &calc );
146152

147153
/** This function currently replaces each expression between [% and %]

src/core/qgsexpression.cpp

+24-4
Original file line numberDiff line numberDiff line change
@@ -1658,7 +1658,14 @@ static QVariant fcnGeomArea( const QVariantList&, const QgsExpressionContext* co
16581658
FEAT_FROM_CONTEXT( context, f );
16591659
ENSURE_GEOM_TYPE( f, g, QGis::Polygon );
16601660
QgsDistanceArea* calc = parent->geomCalculator();
1661-
return QVariant( calc->measureArea( f.constGeometry() ) );
1661+
if ( calc )
1662+
{
1663+
return QVariant( calc->measureArea( f.constGeometry() ) );
1664+
}
1665+
else
1666+
{
1667+
return QVariant( f.constGeometry()->area() );
1668+
}
16621669
}
16631670

16641671
static QVariant fcnArea( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
@@ -1676,15 +1683,29 @@ static QVariant fcnGeomLength( const QVariantList&, const QgsExpressionContext*
16761683
FEAT_FROM_CONTEXT( context, f );
16771684
ENSURE_GEOM_TYPE( f, g, QGis::Line );
16781685
QgsDistanceArea* calc = parent->geomCalculator();
1679-
return QVariant( calc->measureLength( f.constGeometry() ) );
1686+
if ( calc )
1687+
{
1688+
return QVariant( calc->measureLength( f.constGeometry() ) );
1689+
}
1690+
else
1691+
{
1692+
return QVariant( f.constGeometry()->length() );
1693+
}
16801694
}
16811695

16821696
static QVariant fcnGeomPerimeter( const QVariantList&, const QgsExpressionContext* context, QgsExpression* parent )
16831697
{
16841698
FEAT_FROM_CONTEXT( context, f );
16851699
ENSURE_GEOM_TYPE( f, g, QGis::Polygon );
16861700
QgsDistanceArea* calc = parent->geomCalculator();
1687-
return QVariant( calc->measurePerimeter( f.constGeometry() ) );
1701+
if ( calc )
1702+
{
1703+
return QVariant( calc->measurePerimeter( f.constGeometry() ) );
1704+
}
1705+
else
1706+
{
1707+
return f.constGeometry()->isEmpty() ? QVariant( 0 ) : QVariant( f.constGeometry()->geometry()->perimeter() );
1708+
}
16881709
}
16891710

16901711
static QVariant fcnPerimeter( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
@@ -3387,7 +3408,6 @@ QString QgsExpression::dump() const
33873408

33883409
QgsDistanceArea* QgsExpression::geomCalculator()
33893410
{
3390-
initGeomCalculator();
33913411
return d->mCalc.data();
33923412
}
33933413

src/core/qgsexpression.h

+11-4
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,19 @@ class CORE_EXPORT QgsExpression
275275
//! expression() instead.
276276
QString dump() const;
277277

278-
//! Return calculator used for distance and area calculations
279-
//! (used by internal functions)
278+
/** Return calculator used for distance and area calculations
279+
* (used by $length, $area and $perimeter functions only)
280+
* @see setGeomCalculator()
281+
*/
280282
QgsDistanceArea* geomCalculator();
281283

282-
//! Sets the geometry calculator used in evaluation of expressions,
283-
//! instead of the default.
284+
/** Sets the geometry calculator used for distance and area calculations in expressions.
285+
* (used by $length, $area and $perimeter functions only). By default, no geometry
286+
* calculator is set and all distance and area calculations are performed using simple
287+
* cartesian methods (ie no ellipsoidal calculations).
288+
* @see geomCalculator()
289+
*/
290+
//TODO QGIS 3.0 change calc to a pointer, so that calculator can be cleared by passing nullptr
284291
void setGeomCalculator( const QgsDistanceArea &calc );
285292

286293
/** This function currently replaces each expression between [% and %]

tests/src/core/testqgsexpression.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "qgsvectorlayer.h"
3030
#include "qgsmaplayerregistry.h"
3131
#include "qgsvectordataprovider.h"
32+
#include "qgsdistancearea.h"
3233

3334
static void _parseAndEvalExpr( int arg )
3435
{
@@ -1296,6 +1297,59 @@ class TestQgsExpression: public QObject
12961297

12971298
}
12981299

1300+
void geom_calculator()
1301+
{
1302+
//test calculations with and without geometry calculator set
1303+
QgsDistanceArea da;
1304+
da.setSourceAuthId( "EPSG:3111" );
1305+
da.setEllipsoid( "WGS84" );
1306+
da.setEllipsoidalMode( true );
1307+
1308+
QgsFeature feat;
1309+
QgsPolyline polygonRing3111;
1310+
polygonRing3111 << QgsPoint( 2484588, 2425722 ) << QgsPoint( 2482767, 2398853 ) << QgsPoint( 2520109, 2397715 ) << QgsPoint( 2520792, 2425494 ) << QgsPoint( 2484588, 2425722 );
1311+
QgsPolygon polygon3111;
1312+
polygon3111 << polygonRing3111;
1313+
feat.setGeometry( QgsGeometry::fromPolygon( polygon3111 ) );
1314+
QgsExpressionContext context;
1315+
context.setFeature( feat );
1316+
1317+
// test area without geomCalculator
1318+
QgsExpression expArea( "$area" );
1319+
QVariant vArea = expArea.evaluate( &context );
1320+
QCOMPARE( vArea.toDouble(), 1005640568.0 );
1321+
1322+
// test area with geomCalculator
1323+
expArea.setGeomCalculator( da );
1324+
vArea = expArea.evaluate( &context );
1325+
QVERIFY( qgsDoubleNear( vArea.toDouble(), 1009089816.617, 0.001 ) );
1326+
1327+
// test perimeter without geomCalculator
1328+
QgsExpression expPerimeter( "$perimeter" );
1329+
QVariant vPerimeter = expPerimeter.evaluate( &context );
1330+
QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), 128282.086, 0.001 ) );
1331+
1332+
// test perimeter with geomCalculator
1333+
expPerimeter.setGeomCalculator( da );
1334+
vPerimeter = expPerimeter.evaluate( &context );
1335+
QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), 128289.074, 0.001 ) );
1336+
1337+
// test length without geomCalculator
1338+
QgsPolyline line3111;
1339+
line3111 << QgsPoint( 2484588, 2425722 ) << QgsPoint( 2482767, 2398853 );
1340+
feat.setGeometry( QgsGeometry::fromPolyline( line3111 ) );
1341+
context.setFeature( feat );
1342+
1343+
QgsExpression expLength( "$length" );
1344+
QVariant vLength = expLength.evaluate( &context );
1345+
QVERIFY( qgsDoubleNear( vLength.toDouble(), 26930.637, 0.001 ) );
1346+
1347+
// test length with geomCalculator
1348+
expLength.setGeomCalculator( da );
1349+
vLength = expLength.evaluate( &context );
1350+
QVERIFY( qgsDoubleNear( vLength.toDouble(), 26932.156, 0.001 ) );
1351+
}
1352+
12991353
void eval_geometry_wkt()
13001354
{
13011355
QgsPolyline polyline, polygon_ring;

0 commit comments

Comments
 (0)