Skip to content

Commit 2e1d2d1

Browse files
committed
Fix calculation of area/length of mixed geometry collections
1 parent 725f973 commit 2e1d2d1

File tree

8 files changed

+137
-47
lines changed

8 files changed

+137
-47
lines changed

python/core/qgsdistancearea.sip

+23-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,29 @@ class QgsDistanceArea
5555
//! returns ellipsoid's inverse flattening
5656
double ellipsoidInverseFlattening() const;
5757

58-
//! general measurement (line distance or polygon area)
59-
double measure( const QgsGeometry* geometry ) const;
58+
/** General measurement (line distance or polygon area)
59+
* @deprecated use measureArea() or measureLength() methods instead, as this method
60+
* is unpredictable for geometry collections
61+
*/
62+
double measure( const QgsGeometry* geometry ) const /Deprecated/;
63+
64+
/** Measures the area of a geometry.
65+
* @param geometry geometry to measure
66+
* @returns area of geometry. For geometry collections, non surface geometries will be ignored
67+
* @note added in QGIS 2.12
68+
* @see measureLength()
69+
* @see measurePerimeter()
70+
*/
71+
double measureArea( const QgsGeometry* geometry ) const;
72+
73+
/** Measures the length of a geometry.
74+
* @param geometry geometry to measure
75+
* @returns length of geometry. For geometry collections, non curve geometries will be ignored
76+
* @note added in QGIS 2.12
77+
* @see measureArea()
78+
* @see measurePerimeter()
79+
*/
80+
double measureLength( const QgsGeometry* geometry ) const;
6081

6182
//! measures perimeter of polygon
6283
double measurePerimeter( const QgsGeometry* geometry ) const;

src/analysis/vector/qgsgeometryanalyzer.cpp

+2-29
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ QList<double> QgsGeometryAnalyzer::simpleMeasure( QgsGeometry* mpGeometry )
345345
else
346346
{
347347
QgsDistanceArea measure;
348-
list.append( measure.measure( mpGeometry ) );
348+
list.append( measure.measureArea( mpGeometry ) );
349349
if ( mpGeometry->type() == QGis::Polygon )
350350
{
351351
perim = perimeterMeasure( mpGeometry, measure );
@@ -357,34 +357,7 @@ QList<double> QgsGeometryAnalyzer::simpleMeasure( QgsGeometry* mpGeometry )
357357

358358
double QgsGeometryAnalyzer::perimeterMeasure( QgsGeometry* geometry, QgsDistanceArea& measure )
359359
{
360-
double value = 0.00;
361-
if ( geometry->isMultipart() )
362-
{
363-
QgsMultiPolygon poly = geometry->asMultiPolygon();
364-
QgsMultiPolygon::iterator it;
365-
QgsPolygon::iterator jt;
366-
for ( it = poly.begin(); it != poly.end(); ++it )
367-
{
368-
for ( jt = it->begin(); jt != it->end(); ++jt )
369-
{
370-
QgsGeometry* geom = QgsGeometry::fromPolyline( *jt );
371-
value = value + measure.measure( geom );
372-
delete geom;
373-
}
374-
}
375-
}
376-
else
377-
{
378-
QgsPolygon::iterator jt;
379-
QgsPolygon poly = geometry->asPolygon();
380-
for ( jt = poly.begin(); jt != poly.end(); ++jt )
381-
{
382-
QgsGeometry* geom = QgsGeometry::fromPolyline( *jt );
383-
value = value + measure.measure( geom );
384-
delete geom;
385-
}
386-
}
387-
return value;
360+
return measure.measurePerimeter( geometry );
388361
}
389362

390363
bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName,

src/analysis/vector/qgstransectsample.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ int QgsTransectSample::createSample( QProgressDialog* pd )
259259
}
260260

261261
//cancel if length of lineClipStratum is too small
262-
double transectLength = distanceArea.measure( lineClipStratum );
262+
double transectLength = distanceArea.measureLength( lineClipStratum );
263263
if ( transectLength < mMinTransectLength )
264264
{
265265
delete lineFarAwayGeom; delete lineClipStratum;

src/core/qgsdistancearea.cpp

+31-6
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ bool QgsDistanceArea::setEllipsoid( double semiMajor, double semiMinor )
263263
return true;
264264
}
265265

266-
double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2 ) const
266+
double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2, MeasureType type ) const
267267
{
268268
if ( !geomV2 )
269269
{
@@ -276,10 +276,16 @@ double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2 ) const
276276
return 0.0;
277277
}
278278

279+
MeasureType measureType = type;
280+
if ( measureType == Default )
281+
{
282+
measureType = ( geomDimension == 1 ? Length : Area );
283+
}
284+
279285
if ( !mEllipsoidalMode || mEllipsoid == GEO_NONE )
280286
{
281287
//no transform required
282-
if ( geomDimension == 1 )
288+
if ( measureType == Length )
283289
{
284290
return geomV2->length();
285291
}
@@ -297,14 +303,12 @@ double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2 ) const
297303
double sum = 0;
298304
for ( int i = 0; i < collection->numGeometries(); ++i )
299305
{
300-
//hmm... this is a bit broken. What if a collection consists of both lines and polygons?
301-
//the sum will be a mash of both areas and lengths
302-
sum += measure( collection->geometryN( i ) );
306+
sum += measure( collection->geometryN( i ), measureType );
303307
}
304308
return sum;
305309
}
306310

307-
if ( geomDimension == 1 )
311+
if ( measureType == Length )
308312
{
309313
const QgsCurveV2* curve = dynamic_cast<const QgsCurveV2*>( geomV2 );
310314
if ( !curve )
@@ -320,6 +324,9 @@ double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2 ) const
320324
else
321325
{
322326
const QgsSurfaceV2* surface = dynamic_cast<const QgsSurfaceV2*>( geomV2 );
327+
if ( !surface )
328+
return 0.0;
329+
323330
QgsPolygonV2* polygon = surface->surfaceToPolygon();
324331

325332
double area = 0;
@@ -346,6 +353,24 @@ double QgsDistanceArea::measure( const QgsGeometry *geometry ) const
346353
return measure( geomV2 );
347354
}
348355

356+
double QgsDistanceArea::measureArea( const QgsGeometry* geometry ) const
357+
{
358+
if ( !geometry )
359+
return 0.0;
360+
361+
const QgsAbstractGeometryV2* geomV2 = geometry->geometry();
362+
return measure( geomV2, Area );
363+
}
364+
365+
double QgsDistanceArea::measureLength( const QgsGeometry* geometry ) const
366+
{
367+
if ( !geometry )
368+
return 0.0;
369+
370+
const QgsAbstractGeometryV2* geomV2 = geometry->geometry();
371+
return measure( geomV2, Length );
372+
}
373+
349374
double QgsDistanceArea::measurePerimeter( const QgsGeometry* geometry ) const
350375
{
351376
if ( !geometry )

src/core/qgsdistancearea.h

+32-3
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,29 @@ class CORE_EXPORT QgsDistanceArea
8888
//! returns ellipsoid's inverse flattening
8989
double ellipsoidInverseFlattening() const { return mInvFlattening; }
9090

91-
//! general measurement (line distance or polygon area)
92-
double measure( const QgsGeometry* geometry ) const;
91+
/** General measurement (line distance or polygon area)
92+
* @deprecated use measureArea() or measureLength() methods instead, as this method
93+
* is unpredictable for geometry collections
94+
*/
95+
Q_DECL_DEPRECATED double measure( const QgsGeometry* geometry ) const;
96+
97+
/** Measures the area of a geometry.
98+
* @param geometry geometry to measure
99+
* @returns area of geometry. For geometry collections, non surface geometries will be ignored
100+
* @note added in QGIS 2.12
101+
* @see measureLength()
102+
* @see measurePerimeter()
103+
*/
104+
double measureArea( const QgsGeometry* geometry ) const;
105+
106+
/** Measures the length of a geometry.
107+
* @param geometry geometry to measure
108+
* @returns length of geometry. For geometry collections, non curve geometries will be ignored
109+
* @note added in QGIS 2.12
110+
* @see measureArea()
111+
* @see measurePerimeter()
112+
*/
113+
double measureLength( const QgsGeometry* geometry ) const;
93114

94115
//! measures perimeter of polygon
95116
double measurePerimeter( const QgsGeometry *geometry ) const;
@@ -151,6 +172,14 @@ class CORE_EXPORT QgsDistanceArea
151172
void computeAreaInit();
152173

153174
private:
175+
176+
enum MeasureType
177+
{
178+
Default,
179+
Area,
180+
Length
181+
};
182+
154183
//! Copy helper
155184
void _copy( const QgsDistanceArea & origDA );
156185

@@ -171,7 +200,7 @@ class CORE_EXPORT QgsDistanceArea
171200
double getQ( double x ) const;
172201
double getQbar( double x ) const;
173202

174-
double measure( const QgsAbstractGeometryV2* geomV2 ) const;
203+
double measure( const QgsAbstractGeometryV2* geomV2, MeasureType type = Default ) const;
175204
double measureLine( const QgsCurveV2* curve ) const;
176205
double measurePolygon( const QgsCurveV2* curve ) const;
177206

src/core/qgsexpression.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,7 @@ static QVariant fcnGeomArea( const QVariantList&, const QgsExpressionContext* co
13831383
FEAT_FROM_CONTEXT( context, f );
13841384
ENSURE_GEOM_TYPE( f, g, QGis::Polygon );
13851385
QgsDistanceArea* calc = parent->geomCalculator();
1386-
return QVariant( calc->measure( f.constGeometry() ) );
1386+
return QVariant( calc->measureArea( f.constGeometry() ) );
13871387
}
13881388

13891389
static QVariant fcnArea( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
@@ -1401,7 +1401,7 @@ static QVariant fcnGeomLength( const QVariantList&, const QgsExpressionContext*
14011401
FEAT_FROM_CONTEXT( context, f );
14021402
ENSURE_GEOM_TYPE( f, g, QGis::Line );
14031403
QgsDistanceArea* calc = parent->geomCalculator();
1404-
return QVariant( calc->measure( f.constGeometry() ) );
1404+
return QVariant( calc->measureLength( f.constGeometry() ) );
14051405
}
14061406

14071407
static QVariant fcnGeomPerimeter( const QVariantList&, const QgsExpressionContext* context, QgsExpression* parent )

src/gui/qgsmaptoolidentify.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( QgsFeatur
308308
if ( geometryType == QGis::Line )
309309
{
310310
const QgsPolyline &pline = feature->constGeometry()->asPolyline();
311-
double dist = calc.measure( feature->constGeometry() );
311+
double dist = calc.measureLength( feature->constGeometry() );
312312
QGis::UnitType myDisplayUnits;
313313
convertMeasurement( calc, dist, myDisplayUnits, false );
314314
QString str = calc.textUnit( dist, 3, myDisplayUnits, false ); // dist and myDisplayUnits are out params
@@ -332,7 +332,7 @@ QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( QgsFeatur
332332
}
333333
else if ( geometryType == QGis::Polygon )
334334
{
335-
double area = calc.measure( feature->constGeometry() );
335+
double area = calc.measureArea( feature->constGeometry() );
336336
double perimeter = calc.measurePerimeter( feature->constGeometry() );
337337
QGis::UnitType myDisplayUnits;
338338
convertMeasurement( calc, area, myDisplayUnits, true ); // area and myDisplayUnits are out params

tests/src/core/testqgsdistancearea.cpp

+44-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class TestQgsDistanceArea: public QObject
3737
void test_distances();
3838
void unit_conversions();
3939
void regression13601();
40+
void collections();
4041
};
4142

4243
void TestQgsDistanceArea::initTestCase()
@@ -175,8 +176,49 @@ void TestQgsDistanceArea::regression13601()
175176
calc.setEllipsoidalMode( true );
176177
calc.setEllipsoid( "NONE" );
177178
calc.setSourceCrs( 1108L );
178-
QgsGeometry geom( QgsGeometryFactory::geomFromWkt("Polygon ((252000 1389000, 265000 1389000, 265000 1385000, 252000 1385000, 252000 1389000))") );
179-
QVERIFY( qgsDoubleNear( calc.measure( &geom ), 52000000, 0.0001 ) );
179+
QgsGeometry geom( QgsGeometryFactory::geomFromWkt( "Polygon ((252000 1389000, 265000 1389000, 265000 1385000, 252000 1385000, 252000 1389000))" ) );
180+
QVERIFY( qgsDoubleNear( calc.measureArea( &geom ), 52000000, 0.0001 ) );
181+
}
182+
183+
void TestQgsDistanceArea::collections()
184+
{
185+
Q_NOWARN_DEPRECATED_PUSH
186+
//test measuring for collections
187+
QgsDistanceArea myDa;
188+
myDa.setSourceAuthId( "EPSG:4030" );
189+
myDa.setEllipsoidalMode( true );
190+
myDa.setEllipsoid( "WGS84" );
191+
192+
//collection of lines, should be sum of line length
193+
QgsGeometry lines( QgsGeometryFactory::geomFromWkt( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70) )" ) );
194+
double result = myDa.measure( &lines ); //should measure length
195+
QVERIFY( qgsDoubleNear( result, 12006159, 1 ) );
196+
result = myDa.measureLength( &lines );
197+
QVERIFY( qgsDoubleNear( result, 12006159, 1 ) );
198+
result = myDa.measureArea( &lines );
199+
QVERIFY( qgsDoubleNear( result, 0 ) );
200+
201+
//collection of polygons
202+
QgsGeometry polys( QgsGeometryFactory::geomFromWkt( "GeometryCollection( Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) );
203+
result = myDa.measure( &polys ); //should meaure area
204+
QVERIFY( qgsDoubleNear( result, 670434859475, 1 ) );
205+
result = myDa.measureArea( &polys );
206+
QVERIFY( qgsDoubleNear( result, 670434859475, 1 ) );
207+
result = myDa.measureLength( &polys );
208+
QVERIFY( qgsDoubleNear( result, 0 ) );
209+
210+
//mixed collection
211+
QgsGeometry mixed( QgsGeometryFactory::geomFromWkt( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70), Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) );
212+
result = myDa.measure( &mixed ); //should measure area
213+
QVERIFY( qgsDoubleNear( result, 670434859475, 1 ) );
214+
//measure area specifically
215+
result = myDa.measureArea( &mixed );
216+
QVERIFY( qgsDoubleNear( result, 670434859475, 1 ) );
217+
//measure length
218+
result = myDa.measureLength( &mixed );
219+
QVERIFY( qgsDoubleNear( result, 12006159, 1 ) );
220+
221+
Q_NOWARN_DEPRECATED_POP
180222
}
181223

182224
QTEST_MAIN( TestQgsDistanceArea )

0 commit comments

Comments
 (0)