Skip to content

Commit

Permalink
QgsPolygonV2 tests and fixes
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
nyalldawson committed Dec 11, 2015
1 parent d2bf8d8 commit 233f67b
Show file tree
Hide file tree
Showing 16 changed files with 574 additions and 5 deletions.
6 changes: 6 additions & 0 deletions python/core/geometry/qgsabstractgeometryv2.sip
Expand Up @@ -201,6 +201,12 @@ class QgsAbstractGeometryV2
*/
virtual bool dropMValue() = 0;

/** Converts the geometry to a specified type.
* @returns true if conversion was successful
* @note added in QGIS 2.14
*/
virtual bool convertTo( QgsWKBTypes::Type type );

protected:

/** Updates the geometry type based on whether sub geometries contain z or m values.
Expand Down
2 changes: 1 addition & 1 deletion python/core/geometry/qgscurvepolygonv2.sip
Expand Up @@ -43,7 +43,7 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
void setExteriorRing( QgsCurveV2* ring /Transfer/ );
/** Sets interior rings (takes ownership)*/
void setInteriorRings( const QList<QgsCurveV2*>& rings );
void addInteriorRing( QgsCurveV2* ring /Transfer/ );
virtual void addInteriorRing( QgsCurveV2* ring /Transfer/ );
/** Removes ring. Exterior ring is 0, first interior ring 1, ...*/
bool removeInteriorRing( int nr );

Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgslinestringv2.sip
Expand Up @@ -159,4 +159,6 @@ class QgsLineStringV2: public QgsCurveV2
virtual bool dropZValue();
virtual bool dropMValue();

virtual bool convertTo( QgsWKBTypes::Type type );

};
5 changes: 5 additions & 0 deletions python/core/geometry/qgspointv2.sip
Expand Up @@ -119,12 +119,16 @@ class QgsPointV2: public QgsAbstractGeometryV2
void setY( double y );

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

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

};
4 changes: 4 additions & 0 deletions python/core/geometry/qgspolygonv2.sip
Expand Up @@ -5,6 +5,8 @@ class QgsPolygonV2: public QgsCurvePolygonV2
%End

public:
QgsPolygonV2();

virtual QString geometryType() const;
virtual QgsPolygonV2* clone() const;

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

QgsPolygonV2* surfaceToPolygon() const;

void addInteriorRing( QgsCurveV2* ring /Transfer/ );
};
37 changes: 37 additions & 0 deletions src/core/geometry/qgsabstractgeometryv2.cpp
Expand Up @@ -88,6 +88,12 @@ void QgsAbstractGeometryV2::setZMTypeFromSubGeometry( const QgsAbstractGeometryV
mWkbType = QgsWKBTypes::LineString25D;
return;
}
else if ( baseGeomType == QgsWKBTypes::Polygon &&
( subgeom->wkbType() == QgsWKBTypes::Point25D || subgeom->wkbType() == QgsWKBTypes::LineString25D ) )
{
mWkbType = QgsWKBTypes::Polygon25D;
return;
}

bool hasZ = subgeom->is3D();
bool hasM = subgeom->isMeasure();
Expand Down Expand Up @@ -237,6 +243,37 @@ QgsPointV2 QgsAbstractGeometryV2::centroid() const
}
}

bool QgsAbstractGeometryV2::convertTo( QgsWKBTypes::Type type )
{
if ( type == mWkbType )
return true;

if ( QgsWKBTypes::flatType( type ) != QgsWKBTypes::flatType( mWkbType ) )
return false;

bool needZ = QgsWKBTypes::hasZ( type );
bool needM = QgsWKBTypes::hasM( type );
if ( !needZ )
{
dropZValue();
}
else if ( !is3D() )
{
addZValue();
}

if ( !needM )
{
dropMValue();
}
else if ( !isMeasure() )
{
addMValue();
}

return true;
}

bool QgsAbstractGeometryV2::isEmpty() const
{
QgsVertexId vId; QgsPointV2 vertex;
Expand Down
6 changes: 6 additions & 0 deletions src/core/geometry/qgsabstractgeometryv2.h
Expand Up @@ -340,6 +340,12 @@ class CORE_EXPORT QgsAbstractGeometryV2
*/
virtual bool dropMValue() = 0;

/** Converts the geometry to a specified type.
* @returns true if conversion was successful
* @note added in QGIS 2.14
*/
virtual bool convertTo( QgsWKBTypes::Type type );

protected:
QgsWKBTypes::Type mWkbType;
mutable QgsRectangle mBoundingBox;
Expand Down
29 changes: 26 additions & 3 deletions src/core/geometry/qgscurvepolygonv2.cpp
Expand Up @@ -378,6 +378,9 @@ double QgsCurvePolygonV2::area() const

double QgsCurvePolygonV2::perimeter() const
{
if ( !mExteriorRing )
return 0.0;

//sum perimeter of rings
double perimeter = mExteriorRing->length();
QList<QgsCurveV2*>::const_iterator ringIt = mInteriorRings.constBegin();
Expand Down Expand Up @@ -440,7 +443,7 @@ QgsCurveV2* QgsCurvePolygonV2::exteriorRing() const

QgsCurveV2* QgsCurvePolygonV2::interiorRing( int i ) const
{
if ( i >= mInteriorRings.size() )
if ( i < 0 || i >= mInteriorRings.size() )
{
return 0;
}
Expand Down Expand Up @@ -470,17 +473,37 @@ void QgsCurvePolygonV2::setExteriorRing( QgsCurveV2* ring )
void QgsCurvePolygonV2::setInteriorRings( const QList<QgsCurveV2*>& rings )
{
qDeleteAll( mInteriorRings );
mInteriorRings = rings;
mInteriorRings.clear();

//add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
Q_FOREACH ( QgsCurveV2* ring, rings )
{
addInteriorRing( ring );
}
}

void QgsCurvePolygonV2::addInteriorRing( QgsCurveV2* ring )
{
if ( !ring )
return;

//ensure dimensionality of ring matches curve polygon
if ( !is3D() )
ring->dropZValue();
else if ( !ring->is3D() )
ring->addZValue();

if ( !isMeasure() )
ring->dropMValue();
else if ( !ring->isMeasure() )
ring->addMValue();

mInteriorRings.append( ring );
}

bool QgsCurvePolygonV2::removeInteriorRing( int nr )
{
if ( nr >= mInteriorRings.size() )
if ( nr < 0 || nr >= mInteriorRings.size() )
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscurvepolygonv2.h
Expand Up @@ -70,7 +70,7 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
/** Sets all interior rings (takes ownership)*/
void setInteriorRings( const QList<QgsCurveV2*>& rings );
/** Adds an interior ring to the geometry (takes ownership)*/
void addInteriorRing( QgsCurveV2* ring );
virtual void addInteriorRing( QgsCurveV2* ring );
/** Removes ring. Exterior ring is 0, first interior ring 1, ...*/
bool removeInteriorRing( int nr );

Expand Down
19 changes: 19 additions & 0 deletions src/core/geometry/qgslinestringv2.cpp
Expand Up @@ -944,3 +944,22 @@ bool QgsLineStringV2::dropMValue()
mM.clear();
return true;
}

bool QgsLineStringV2::convertTo( QgsWKBTypes::Type type )
{
if ( type == mWkbType )
return true;

if ( type == QgsWKBTypes::LineString25D )
{
//special handling required for conversion to LineString25D
dropMValue();
addZValue();
mWkbType = QgsWKBTypes::LineString25D;
return true;
}
else
{
return QgsCurveV2::convertTo( type );
}
}
2 changes: 2 additions & 0 deletions src/core/geometry/qgslinestringv2.h
Expand Up @@ -186,6 +186,8 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
virtual bool dropZValue() override;
virtual bool dropMValue() override;

bool convertTo( QgsWKBTypes::Type type ) override;

private:
QVector<double> mX;
QVector<double> mY;
Expand Down
30 changes: 30 additions & 0 deletions src/core/geometry/qgspointv2.cpp
Expand Up @@ -380,3 +380,33 @@ bool QgsPointV2::dropMValue()
mM = 0.0;
return true;
}

bool QgsPointV2::convertTo( QgsWKBTypes::Type type )
{
if ( type == mWkbType )
return true;

switch ( type )
{
case QgsWKBTypes::Point:
mZ = 0.0;
mM = 0.0;
mWkbType = type;
return true;
case QgsWKBTypes::PointZ:
case QgsWKBTypes::Point25D:
mM = 0.0;
mWkbType = type;
return true;
case QgsWKBTypes::PointM:
mZ = 0.0;
mWkbType = type;
return true;
case QgsWKBTypes::PointZM:
mWkbType = type;
return true;
default:
return false;
}
return false;
}
5 changes: 5 additions & 0 deletions src/core/geometry/qgspointv2.h
Expand Up @@ -130,12 +130,16 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
void setY( double y ) { mY = y; mBoundingBox = QgsRectangle(); }

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

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

private:
double mX;
Expand Down
32 changes: 32 additions & 0 deletions src/core/geometry/qgspolygonv2.cpp
Expand Up @@ -21,6 +21,12 @@
#include "qgslinestringv2.h"
#include "qgswkbptr.h"

QgsPolygonV2::QgsPolygonV2()
: QgsCurvePolygonV2()
{
mWkbType = QgsWKBTypes::Polygon;
}

QgsPolygonV2* QgsPolygonV2::clone() const
{
return new QgsPolygonV2( *this );
Expand Down Expand Up @@ -102,9 +108,35 @@ unsigned char* QgsPolygonV2::asWkb( int& binarySize ) const
curve->points( pts );
QgsGeometryUtils::pointsToWKB( wkb, pts, curve->is3D(), curve->isMeasure() );
}

return geomPtr;
}

void QgsPolygonV2::addInteriorRing( QgsCurveV2* ring )
{
if ( !ring )
return;

if ( ring->hasCurvedSegments() )
{
//can't add a curved ring to a QgsPolygonV2
QgsLineStringV2* segmented = ring->curveToLine();
delete ring;
ring = segmented;
}

if ( mWkbType == QgsWKBTypes::Polygon25D )
{
ring->convertTo( QgsWKBTypes::LineString25D );
mInteriorRings.append( ring );
}
else
{
QgsCurvePolygonV2::addInteriorRing( ring );
}
}


QgsPolygonV2* QgsPolygonV2::surfaceToPolygon() const
{
return clone();
Expand Down
4 changes: 4 additions & 0 deletions src/core/geometry/qgspolygonv2.h
Expand Up @@ -29,6 +29,8 @@
class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygonV2
{
public:
QgsPolygonV2();

virtual QString geometryType() const override { return "Polygon"; }
virtual QgsPolygonV2* clone() const override;

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

QgsPolygonV2* surfaceToPolygon() const override;

void addInteriorRing( QgsCurveV2* ring ) override;
};
#endif // QGSPOLYGONV2_H

0 comments on commit 233f67b

Please sign in to comment.