Skip to content

Commit

Permalink
If no geomCalculator is set for QgsExpression, perform cartesian
Browse files Browse the repository at this point in the history
calculations for $length, $area, $perimeter (refs #13209)

ie, the default is now to use planimeteric calculations unless
a geomCalculator has been explicitly set
  • Loading branch information
nyalldawson committed Feb 12, 2016
1 parent 6bbe3b9 commit 229ef29
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 12 deletions.
14 changes: 10 additions & 4 deletions python/core/qgsexpression.sip
Expand Up @@ -136,12 +136,18 @@ class QgsExpression
//! expression() instead.
QString dump() const;

//! Return calculator used for distance and area calculations
//! (used by internal functions)
/** Return calculator used for distance and area calculations
* (used by $length, $area and $perimeter functions only)
* @see setGeomCalculator()
*/
QgsDistanceArea *geomCalculator();

//! Sets the geometry calculator used in evaluation of expressions,
// instead of the default.
/** 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
* cartesian methods (ie no ellipsoidal calculations).
* @see geomCalculator()
*/
void setGeomCalculator( const QgsDistanceArea &calc );

/** This function currently replaces each expression between [% and %]
Expand Down
28 changes: 24 additions & 4 deletions src/core/qgsexpression.cpp
Expand Up @@ -1658,7 +1658,14 @@ static QVariant fcnGeomArea( const QVariantList&, const QgsExpressionContext* co
FEAT_FROM_CONTEXT( context, f );
ENSURE_GEOM_TYPE( f, g, QGis::Polygon );
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measureArea( f.constGeometry() ) );
if ( calc )
{
return QVariant( calc->measureArea( f.constGeometry() ) );
}
else
{
return QVariant( f.constGeometry()->area() );
}
}

static QVariant fcnArea( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
Expand All @@ -1676,15 +1683,29 @@ static QVariant fcnGeomLength( const QVariantList&, const QgsExpressionContext*
FEAT_FROM_CONTEXT( context, f );
ENSURE_GEOM_TYPE( f, g, QGis::Line );
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measureLength( f.constGeometry() ) );
if ( calc )
{
return QVariant( calc->measureLength( f.constGeometry() ) );
}
else
{
return QVariant( f.constGeometry()->length() );
}
}

static QVariant fcnGeomPerimeter( const QVariantList&, const QgsExpressionContext* context, QgsExpression* parent )
{
FEAT_FROM_CONTEXT( context, f );
ENSURE_GEOM_TYPE( f, g, QGis::Polygon );
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measurePerimeter( f.constGeometry() ) );
if ( calc )
{
return QVariant( calc->measurePerimeter( f.constGeometry() ) );
}
else
{
return f.constGeometry()->isEmpty() ? QVariant( 0 ) : QVariant( f.constGeometry()->geometry()->perimeter() );
}
}

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

QgsDistanceArea* QgsExpression::geomCalculator()
{
initGeomCalculator();
return d->mCalc.data();
}

Expand Down
15 changes: 11 additions & 4 deletions src/core/qgsexpression.h
Expand Up @@ -275,12 +275,19 @@ class CORE_EXPORT QgsExpression
//! expression() instead.
QString dump() const;

//! Return calculator used for distance and area calculations
//! (used by internal functions)
/** Return calculator used for distance and area calculations
* (used by $length, $area and $perimeter functions only)
* @see setGeomCalculator()
*/
QgsDistanceArea* geomCalculator();

//! Sets the geometry calculator used in evaluation of expressions,
//! instead of the default.
/** 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
* cartesian methods (ie no ellipsoidal calculations).
* @see geomCalculator()
*/
//TODO QGIS 3.0 change calc to a pointer, so that calculator can be cleared by passing nullptr
void setGeomCalculator( const QgsDistanceArea &calc );

/** This function currently replaces each expression between [% and %]
Expand Down
54 changes: 54 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -29,6 +29,7 @@
#include "qgsvectorlayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsvectordataprovider.h"
#include "qgsdistancearea.h"

static void _parseAndEvalExpr( int arg )
{
Expand Down Expand Up @@ -1296,6 +1297,59 @@ class TestQgsExpression: public QObject

}

void geom_calculator()
{
//test calculations with and without geometry calculator set
QgsDistanceArea da;
da.setSourceAuthId( "EPSG:3111" );
da.setEllipsoid( "WGS84" );
da.setEllipsoidalMode( true );

QgsFeature feat;
QgsPolyline polygonRing3111;
polygonRing3111 << QgsPoint( 2484588, 2425722 ) << QgsPoint( 2482767, 2398853 ) << QgsPoint( 2520109, 2397715 ) << QgsPoint( 2520792, 2425494 ) << QgsPoint( 2484588, 2425722 );
QgsPolygon polygon3111;
polygon3111 << polygonRing3111;
feat.setGeometry( QgsGeometry::fromPolygon( polygon3111 ) );
QgsExpressionContext context;
context.setFeature( feat );

// test area without geomCalculator
QgsExpression expArea( "$area" );
QVariant vArea = expArea.evaluate( &context );
QCOMPARE( vArea.toDouble(), 1005640568.0 );

// test area with geomCalculator
expArea.setGeomCalculator( da );
vArea = expArea.evaluate( &context );
QVERIFY( qgsDoubleNear( vArea.toDouble(), 1009089816.617, 0.001 ) );

// test perimeter without geomCalculator
QgsExpression expPerimeter( "$perimeter" );
QVariant vPerimeter = expPerimeter.evaluate( &context );
QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), 128282.086, 0.001 ) );

// test perimeter with geomCalculator
expPerimeter.setGeomCalculator( da );
vPerimeter = expPerimeter.evaluate( &context );
QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), 128289.074, 0.001 ) );

// test length without geomCalculator
QgsPolyline line3111;
line3111 << QgsPoint( 2484588, 2425722 ) << QgsPoint( 2482767, 2398853 );
feat.setGeometry( QgsGeometry::fromPolyline( line3111 ) );
context.setFeature( feat );

QgsExpression expLength( "$length" );
QVariant vLength = expLength.evaluate( &context );
QVERIFY( qgsDoubleNear( vLength.toDouble(), 26930.637, 0.001 ) );

// test length with geomCalculator
expLength.setGeomCalculator( da );
vLength = expLength.evaluate( &context );
QVERIFY( qgsDoubleNear( vLength.toDouble(), 26932.156, 0.001 ) );
}

void eval_geometry_wkt()
{
QgsPolyline polyline, polygon_ring;
Expand Down

0 comments on commit 229ef29

Please sign in to comment.