Skip to content

Commit a0d6412

Browse files
committed
Fix incorrect area calculation in corner cases (fix #16820)
In certain circumstances very proximal nodes could cause instability in the ellipsoidal area calculation. Port (slightly tweaked) fix from grass changeset 71167 for same issue, and add a unit test (cherry-picked from d850393)
1 parent fda97b2 commit a0d6412

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

src/core/qgsdistancearea.cpp

+29-4
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,14 @@ double QgsDistanceArea::computePolygonArea( const QList<QgsPoint>& points ) cons
897897
double Qbar1, Qbar2;
898898
double area;
899899

900+
/* GRASS comment: threshold for dy, should be between 1e-4 and 1e-7
901+
* QGIS note: while the grass comment states that thres should be between 1e-4->1e-7,
902+
* a value of 1e-7 caused TestQgsDistanceArea::regression14675() to regress
903+
* The maximum threshold possible which permits regression14675() to pass
904+
* was found to be ~0.7e-7.
905+
*/
906+
const double thresh = 0.7e-7;
907+
900908
QgsDebugMsgLevel( "Ellipsoid: " + mEllipsoid, 3 );
901909
if (( ! mEllipsoidalMode ) || ( mEllipsoid == GEO_NONE ) )
902910
{
@@ -927,11 +935,28 @@ double QgsDistanceArea::computePolygonArea( const QList<QgsPoint>& points ) cons
927935
x1 += m_TwoPI;
928936

929937
dx = x2 - x1;
930-
area += dx * ( m_Qp - getQ( y2 ) );
931-
932938
dy = y2 - y1;
933-
if ( !qgsDoubleNear( dy, 0.0 ) )
934-
area += dx * getQ( y2 ) - ( dx / dy ) * ( Qbar2 - Qbar1 );
939+
if ( qAbs( dy ) > thresh )
940+
{
941+
/* account for different latitudes y1, y2 */
942+
area += dx * ( m_Qp - ( Qbar2 - Qbar1 ) / dy );
943+
}
944+
else
945+
{
946+
/* latitudes y1, y2 are (nearly) identical */
947+
948+
/* if y2 becomes similar to y1, i.e. y2 -> y1
949+
* Qbar2 - Qbar1 -> 0 and dy -> 0
950+
* (Qbar2 - Qbar1) / dy -> ?
951+
* (Qbar2 - Qbar1) / dy should approach Q((y1 + y2) / 2)
952+
* Metz 2017
953+
*/
954+
area += dx * ( m_Qp - getQ( y2 ) );
955+
956+
/* original:
957+
* area += dx * getQ( y2 ) - ( dx / dy ) * ( Qbar2 - Qbar1 );
958+
*/
959+
}
935960
}
936961
if (( area *= m_AE ) < 0.0 )
937962
area = -area;

tests/src/core/testqgsdistancearea.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class TestQgsDistanceArea: public QObject
4545
void measureAreaAndUnits();
4646
void emptyPolygon();
4747
void regression14675();
48+
void regression16820();
4849

4950
};
5051

@@ -370,6 +371,16 @@ void TestQgsDistanceArea::regression14675()
370371
QGSCOMPARENEAR( calc.measureArea( &geom ), 0.83301, 0.02 );
371372
}
372373

374+
void TestQgsDistanceArea::regression16820()
375+
{
376+
QgsDistanceArea calc;
377+
calc.setEllipsoid( "WGS84" );
378+
calc.setSourceCrs( QgsCoordinateReferenceSystem( "EPSG:32634" ) );
379+
QgsGeometry geom( QgsGeometryFactory::geomFromWkt( "Polygon ((110250.54038314701756462 5084495.57398066483438015, 110243.46975068224128336 5084507.17200060561299324, 110251.23908144699817058 5084506.68309532757848501, 110251.2394439501222223 5084506.68307251576334238, 110250.54048078990308568 5084495.57553235255181789, 110250.54038314701756462 5084495.57398066483438015))" ) );
380+
//lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats
381+
QGSCOMPARENEAR( calc.measureArea( &geom ), 43.183369, 0.2 );
382+
}
383+
373384
QTEST_MAIN( TestQgsDistanceArea )
374385
#include "testqgsdistancearea.moc"
375386

0 commit comments

Comments
 (0)