Skip to content

Commit 233f67b

Browse files
committed
QgsPolygonV2 tests and fixes
- fixes for handling Polygon25D - add QgsAbstractGeometryV2::convertTo( QgsWKBTypes::Type type ) for easy conversion between geometry types - fix crash when calculating perimeter with no exterior ring - ensure that added rings respect dimensionality of polygon (avoids issues such as polygons with z having a ring without z, or a Polygon25D with LineStringZ rings) - if a curved ring is added to a polygon then a segmentized version of the ring is used (can't have a Polygon with a CircularString ring) - when calling setInteriorRings, make sure empty rings are skipped and that all rings are converted to correct type for polygon - don't crash when requesting or removing interior ring with index < 0
1 parent d2bf8d8 commit 233f67b

16 files changed

+574
-5
lines changed

python/core/geometry/qgsabstractgeometryv2.sip

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ class QgsAbstractGeometryV2
201201
*/
202202
virtual bool dropMValue() = 0;
203203

204+
/** Converts the geometry to a specified type.
205+
* @returns true if conversion was successful
206+
* @note added in QGIS 2.14
207+
*/
208+
virtual bool convertTo( QgsWKBTypes::Type type );
209+
204210
protected:
205211

206212
/** Updates the geometry type based on whether sub geometries contain z or m values.

python/core/geometry/qgscurvepolygonv2.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
4343
void setExteriorRing( QgsCurveV2* ring /Transfer/ );
4444
/** Sets interior rings (takes ownership)*/
4545
void setInteriorRings( const QList<QgsCurveV2*>& rings );
46-
void addInteriorRing( QgsCurveV2* ring /Transfer/ );
46+
virtual void addInteriorRing( QgsCurveV2* ring /Transfer/ );
4747
/** Removes ring. Exterior ring is 0, first interior ring 1, ...*/
4848
bool removeInteriorRing( int nr );
4949

python/core/geometry/qgslinestringv2.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,6 @@ class QgsLineStringV2: public QgsCurveV2
159159
virtual bool dropZValue();
160160
virtual bool dropMValue();
161161

162+
virtual bool convertTo( QgsWKBTypes::Type type );
163+
162164
};

python/core/geometry/qgspointv2.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,16 @@ class QgsPointV2: public QgsAbstractGeometryV2
119119
void setY( double y );
120120

121121
/** Sets the point's z-coordinate.
122+
* @note calling this will have no effect if the point does not contain a z-dimension. Use addZValue() to
123+
* add a z value and force the point to have a z dimension.
122124
* @see z()
123125
* @see rz()
124126
*/
125127
void setZ( double z );
126128

127129
/** Sets the point's m-value.
130+
* @note calling this will have no effect if the point does not contain a m-dimension. Use addMValue() to
131+
* add an m value and force the point to have an m dimension.
128132
* @see m()
129133
* @see rm()
130134
*/
@@ -171,5 +175,6 @@ class QgsPointV2: public QgsAbstractGeometryV2
171175
virtual bool addMValue( double mValue = 0 );
172176
virtual bool dropZValue();
173177
virtual bool dropMValue();
178+
virtual bool convertTo( QgsWKBTypes::Type type );
174179

175180
};

python/core/geometry/qgspolygonv2.sip

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ class QgsPolygonV2: public QgsCurvePolygonV2
55
%End
66

77
public:
8+
QgsPolygonV2();
9+
810
virtual QString geometryType() const;
911
virtual QgsPolygonV2* clone() const;
1012

@@ -19,4 +21,6 @@ class QgsPolygonV2: public QgsCurvePolygonV2
1921
// inherited: QString asJSON( int precision = 17 ) const;
2022

2123
QgsPolygonV2* surfaceToPolygon() const;
24+
25+
void addInteriorRing( QgsCurveV2* ring /Transfer/ );
2226
};

src/core/geometry/qgsabstractgeometryv2.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ void QgsAbstractGeometryV2::setZMTypeFromSubGeometry( const QgsAbstractGeometryV
8888
mWkbType = QgsWKBTypes::LineString25D;
8989
return;
9090
}
91+
else if ( baseGeomType == QgsWKBTypes::Polygon &&
92+
( subgeom->wkbType() == QgsWKBTypes::Point25D || subgeom->wkbType() == QgsWKBTypes::LineString25D ) )
93+
{
94+
mWkbType = QgsWKBTypes::Polygon25D;
95+
return;
96+
}
9197

9298
bool hasZ = subgeom->is3D();
9399
bool hasM = subgeom->isMeasure();
@@ -237,6 +243,37 @@ QgsPointV2 QgsAbstractGeometryV2::centroid() const
237243
}
238244
}
239245

246+
bool QgsAbstractGeometryV2::convertTo( QgsWKBTypes::Type type )
247+
{
248+
if ( type == mWkbType )
249+
return true;
250+
251+
if ( QgsWKBTypes::flatType( type ) != QgsWKBTypes::flatType( mWkbType ) )
252+
return false;
253+
254+
bool needZ = QgsWKBTypes::hasZ( type );
255+
bool needM = QgsWKBTypes::hasM( type );
256+
if ( !needZ )
257+
{
258+
dropZValue();
259+
}
260+
else if ( !is3D() )
261+
{
262+
addZValue();
263+
}
264+
265+
if ( !needM )
266+
{
267+
dropMValue();
268+
}
269+
else if ( !isMeasure() )
270+
{
271+
addMValue();
272+
}
273+
274+
return true;
275+
}
276+
240277
bool QgsAbstractGeometryV2::isEmpty() const
241278
{
242279
QgsVertexId vId; QgsPointV2 vertex;

src/core/geometry/qgsabstractgeometryv2.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,12 @@ class CORE_EXPORT QgsAbstractGeometryV2
340340
*/
341341
virtual bool dropMValue() = 0;
342342

343+
/** Converts the geometry to a specified type.
344+
* @returns true if conversion was successful
345+
* @note added in QGIS 2.14
346+
*/
347+
virtual bool convertTo( QgsWKBTypes::Type type );
348+
343349
protected:
344350
QgsWKBTypes::Type mWkbType;
345351
mutable QgsRectangle mBoundingBox;

src/core/geometry/qgscurvepolygonv2.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ double QgsCurvePolygonV2::area() const
378378

379379
double QgsCurvePolygonV2::perimeter() const
380380
{
381+
if ( !mExteriorRing )
382+
return 0.0;
383+
381384
//sum perimeter of rings
382385
double perimeter = mExteriorRing->length();
383386
QList<QgsCurveV2*>::const_iterator ringIt = mInteriorRings.constBegin();
@@ -440,7 +443,7 @@ QgsCurveV2* QgsCurvePolygonV2::exteriorRing() const
440443

441444
QgsCurveV2* QgsCurvePolygonV2::interiorRing( int i ) const
442445
{
443-
if ( i >= mInteriorRings.size() )
446+
if ( i < 0 || i >= mInteriorRings.size() )
444447
{
445448
return 0;
446449
}
@@ -470,17 +473,37 @@ void QgsCurvePolygonV2::setExteriorRing( QgsCurveV2* ring )
470473
void QgsCurvePolygonV2::setInteriorRings( const QList<QgsCurveV2*>& rings )
471474
{
472475
qDeleteAll( mInteriorRings );
473-
mInteriorRings = rings;
476+
mInteriorRings.clear();
477+
478+
//add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
479+
Q_FOREACH ( QgsCurveV2* ring, rings )
480+
{
481+
addInteriorRing( ring );
482+
}
474483
}
475484

476485
void QgsCurvePolygonV2::addInteriorRing( QgsCurveV2* ring )
477486
{
487+
if ( !ring )
488+
return;
489+
490+
//ensure dimensionality of ring matches curve polygon
491+
if ( !is3D() )
492+
ring->dropZValue();
493+
else if ( !ring->is3D() )
494+
ring->addZValue();
495+
496+
if ( !isMeasure() )
497+
ring->dropMValue();
498+
else if ( !ring->isMeasure() )
499+
ring->addMValue();
500+
478501
mInteriorRings.append( ring );
479502
}
480503

481504
bool QgsCurvePolygonV2::removeInteriorRing( int nr )
482505
{
483-
if ( nr >= mInteriorRings.size() )
506+
if ( nr < 0 || nr >= mInteriorRings.size() )
484507
{
485508
return false;
486509
}

src/core/geometry/qgscurvepolygonv2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
7070
/** Sets all interior rings (takes ownership)*/
7171
void setInteriorRings( const QList<QgsCurveV2*>& rings );
7272
/** Adds an interior ring to the geometry (takes ownership)*/
73-
void addInteriorRing( QgsCurveV2* ring );
73+
virtual void addInteriorRing( QgsCurveV2* ring );
7474
/** Removes ring. Exterior ring is 0, first interior ring 1, ...*/
7575
bool removeInteriorRing( int nr );
7676

src/core/geometry/qgslinestringv2.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,3 +944,22 @@ bool QgsLineStringV2::dropMValue()
944944
mM.clear();
945945
return true;
946946
}
947+
948+
bool QgsLineStringV2::convertTo( QgsWKBTypes::Type type )
949+
{
950+
if ( type == mWkbType )
951+
return true;
952+
953+
if ( type == QgsWKBTypes::LineString25D )
954+
{
955+
//special handling required for conversion to LineString25D
956+
dropMValue();
957+
addZValue();
958+
mWkbType = QgsWKBTypes::LineString25D;
959+
return true;
960+
}
961+
else
962+
{
963+
return QgsCurveV2::convertTo( type );
964+
}
965+
}

src/core/geometry/qgslinestringv2.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
186186
virtual bool dropZValue() override;
187187
virtual bool dropMValue() override;
188188

189+
bool convertTo( QgsWKBTypes::Type type ) override;
190+
189191
private:
190192
QVector<double> mX;
191193
QVector<double> mY;

src/core/geometry/qgspointv2.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,33 @@ bool QgsPointV2::dropMValue()
380380
mM = 0.0;
381381
return true;
382382
}
383+
384+
bool QgsPointV2::convertTo( QgsWKBTypes::Type type )
385+
{
386+
if ( type == mWkbType )
387+
return true;
388+
389+
switch ( type )
390+
{
391+
case QgsWKBTypes::Point:
392+
mZ = 0.0;
393+
mM = 0.0;
394+
mWkbType = type;
395+
return true;
396+
case QgsWKBTypes::PointZ:
397+
case QgsWKBTypes::Point25D:
398+
mM = 0.0;
399+
mWkbType = type;
400+
return true;
401+
case QgsWKBTypes::PointM:
402+
mZ = 0.0;
403+
mWkbType = type;
404+
return true;
405+
case QgsWKBTypes::PointZM:
406+
mWkbType = type;
407+
return true;
408+
default:
409+
return false;
410+
}
411+
return false;
412+
}

src/core/geometry/qgspointv2.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,16 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
130130
void setY( double y ) { mY = y; mBoundingBox = QgsRectangle(); }
131131

132132
/** Sets the point's z-coordinate.
133+
* @note calling this will have no effect if the point does not contain a z-dimension. Use addZValue() to
134+
* add a z value and force the point to have a z dimension.
133135
* @see z()
134136
* @see rz()
135137
*/
136138
void setZ( double z ) { mZ = z; }
137139

138140
/** Sets the point's m-value.
141+
* @note calling this will have no effect if the point does not contain a m-dimension. Use addMValue() to
142+
* add a m value and force the point to have an m dimension.
139143
* @see m()
140144
* @see rm()
141145
*/
@@ -182,6 +186,7 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
182186
virtual bool addMValue( double mValue = 0 ) override;
183187
virtual bool dropZValue() override;
184188
virtual bool dropMValue() override;
189+
bool convertTo( QgsWKBTypes::Type type ) override;
185190

186191
private:
187192
double mX;

src/core/geometry/qgspolygonv2.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
#include "qgslinestringv2.h"
2222
#include "qgswkbptr.h"
2323

24+
QgsPolygonV2::QgsPolygonV2()
25+
: QgsCurvePolygonV2()
26+
{
27+
mWkbType = QgsWKBTypes::Polygon;
28+
}
29+
2430
QgsPolygonV2* QgsPolygonV2::clone() const
2531
{
2632
return new QgsPolygonV2( *this );
@@ -102,9 +108,35 @@ unsigned char* QgsPolygonV2::asWkb( int& binarySize ) const
102108
curve->points( pts );
103109
QgsGeometryUtils::pointsToWKB( wkb, pts, curve->is3D(), curve->isMeasure() );
104110
}
111+
105112
return geomPtr;
106113
}
107114

115+
void QgsPolygonV2::addInteriorRing( QgsCurveV2* ring )
116+
{
117+
if ( !ring )
118+
return;
119+
120+
if ( ring->hasCurvedSegments() )
121+
{
122+
//can't add a curved ring to a QgsPolygonV2
123+
QgsLineStringV2* segmented = ring->curveToLine();
124+
delete ring;
125+
ring = segmented;
126+
}
127+
128+
if ( mWkbType == QgsWKBTypes::Polygon25D )
129+
{
130+
ring->convertTo( QgsWKBTypes::LineString25D );
131+
mInteriorRings.append( ring );
132+
}
133+
else
134+
{
135+
QgsCurvePolygonV2::addInteriorRing( ring );
136+
}
137+
}
138+
139+
108140
QgsPolygonV2* QgsPolygonV2::surfaceToPolygon() const
109141
{
110142
return clone();

src/core/geometry/qgspolygonv2.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygonV2
3030
{
3131
public:
32+
QgsPolygonV2();
33+
3234
virtual QString geometryType() const override { return "Polygon"; }
3335
virtual QgsPolygonV2* clone() const override;
3436

@@ -43,5 +45,7 @@ class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygonV2
4345
// inherited: QString asJSON( int precision = 17 ) const;
4446

4547
QgsPolygonV2* surfaceToPolygon() const override;
48+
49+
void addInteriorRing( QgsCurveV2* ring ) override;
4650
};
4751
#endif // QGSPOLYGONV2_H

0 commit comments

Comments
 (0)