Skip to content

Commit

Permalink
Add method to QgsAbstractGeometryV2 for boundary
Browse files Browse the repository at this point in the history
Returns the closure of the combinatorial boundary of the
geometry (ie the topological boundary of the geometry).
For instance, a polygon geometry will have a boundary
consisting of the linestrings for each ring in the polygon.

Follows OGC / SQL/MM specs for boundary calculation

(cherry-picked from 85f37ee)
  • Loading branch information
nyalldawson committed Aug 9, 2016
1 parent 63f946e commit 240c136
Show file tree
Hide file tree
Showing 31 changed files with 514 additions and 2 deletions.
7 changes: 7 additions & 0 deletions python/core/geometry/qgsabstractgeometryv2.sip
Expand Up @@ -130,6 +130,13 @@ class QgsAbstractGeometryV2
*/
bool isMeasure() const;

/** Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the geometry).
* For instance, a polygon geometry will have a boundary consisting of the linestrings for each ring in the polygon.
* @returns boundary for geometry. May be null for some geometry types.
* @note added in QGIS 3.0
*/
virtual QgsAbstractGeometryV2* boundary() const = 0;

//import

/** Sets the geometry from a WKB string.
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgscurvepolygonv2.sip
Expand Up @@ -29,6 +29,8 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
virtual double area() const;
virtual double perimeter() const;
QgsPolygonV2* surfaceToPolygon() const;
virtual QgsAbstractGeometryV2* boundary() const /Factory/;


//curve polygon interface
int numInteriorRings() const;
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgscurvev2.sip
Expand Up @@ -75,6 +75,8 @@ class QgsCurveV2: public QgsAbstractGeometryV2
*/
virtual QgsCurveV2* reversed() const = 0 /Factory/;

virtual QgsAbstractGeometryV2* boundary() const /Factory/;

/** Returns a geometry without curves. Caller takes ownership
* @param tolerance segmentation tolerance
* @param toleranceType maximum segmentation angle or maximum difference between approximation and curve*/
Expand Down
1 change: 1 addition & 0 deletions python/core/geometry/qgsgeometrycollectionv2.sip
Expand Up @@ -30,6 +30,7 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
virtual int dimension() const;
virtual QString geometryType() const;
virtual void clear();
virtual QgsAbstractGeometryV2* boundary() const /Factory/;

/** Adds a geometry and takes ownership. Returns true in case of success.*/
virtual bool addGeometry( QgsAbstractGeometryV2* g /Transfer/ );
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgsmulticurvev2.sip
Expand Up @@ -26,4 +26,6 @@ class QgsMultiCurveV2: public QgsGeometryCollectionV2
* @note added in QGIS 2.14
*/
QgsMultiCurveV2* reversed() const /Factory/;

virtual QgsAbstractGeometryV2* boundary() const /Factory/;
};
2 changes: 2 additions & 0 deletions python/core/geometry/qgsmultipointv2.sip
Expand Up @@ -20,6 +20,8 @@ class QgsMultiPointV2: public QgsGeometryCollectionV2
/** Adds a geometry and takes ownership. Returns true in case of success*/
virtual bool addGeometry( QgsAbstractGeometryV2* g );

virtual QgsAbstractGeometryV2* boundary() const /Factory/;

protected:

virtual bool wktOmitChildType() const;
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgsmultipolygonv2.sip
Expand Up @@ -24,6 +24,8 @@ class QgsMultiPolygonV2: public QgsMultiSurfaceV2
@return the converted geometry. Caller takes ownership*/
QgsAbstractGeometryV2* toCurveType() const /Factory/;

virtual QgsAbstractGeometryV2* boundary() const /Factory/;

protected:

virtual bool wktOmitChildType() const;
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgsmultisurfacev2.sip
Expand Up @@ -20,6 +20,8 @@ class QgsMultiSurfaceV2: public QgsGeometryCollectionV2
/** Adds a geometry and takes ownership. Returns true in case of success*/
virtual bool addGeometry( QgsAbstractGeometryV2* g );

virtual QgsAbstractGeometryV2* boundary() const /Factory/;

/** Returns a geometry without curves. Caller takes ownership*/
QgsAbstractGeometryV2* segmentize() const /Factory/;
};
1 change: 1 addition & 0 deletions python/core/geometry/qgspointv2.sip
Expand Up @@ -157,6 +157,7 @@ class QgsPointV2: public QgsAbstractGeometryV2
bool transformZ = false );
void transform( const QTransform& t );
virtual QList< QList< QList< QgsPointV2 > > > coordinateSequence() const;
virtual QgsAbstractGeometryV2* boundary() const /Factory/;

//low-level editing
virtual bool insertVertex( QgsVertexId position, const QgsPointV2& vertex );
Expand Down
1 change: 1 addition & 0 deletions python/core/geometry/qgspolygonv2.sip
Expand Up @@ -35,4 +35,5 @@ class QgsPolygonV2: public QgsCurvePolygonV2
//overridden to handle LineString25D rings
virtual void setExteriorRing( QgsCurveV2* ring /Transfer/ );

virtual QgsAbstractGeometryV2* boundary() const /Factory/;
};
8 changes: 7 additions & 1 deletion src/core/geometry/qgsabstractgeometryv2.h
Expand Up @@ -110,10 +110,16 @@ class CORE_EXPORT QgsAbstractGeometryV2
virtual bool isValid() const = 0;
virtual QgsMultiPointV2* locateAlong() const = 0;
virtual QgsMultiCurveV2* locateBetween() const = 0;
virtual QgsCurveV2* boundary() const = 0;
virtual QgsRectangle envelope() const = 0;
#endif

/** Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the geometry).
* For instance, a polygon geometry will have a boundary consisting of the linestrings for each ring in the polygon.
* @returns boundary for geometry. May be null for some geometry types.
* @note added in QGIS 3.0
*/
virtual QgsAbstractGeometryV2* boundary() const = 0;

//import

/** Sets the geometry from a WKB string.
Expand Down
20 changes: 20 additions & 0 deletions src/core/geometry/qgscurvepolygonv2.cpp
Expand Up @@ -23,6 +23,7 @@
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgswkbptr.h"
#include "qgsmulticurvev2.h"
#include <QPainter>
#include <QPainterPath>

Expand Down Expand Up @@ -420,6 +421,25 @@ QgsPolygonV2* QgsCurvePolygonV2::surfaceToPolygon() const
return polygon;
}

QgsAbstractGeometryV2* QgsCurvePolygonV2::boundary() const
{
if ( mInteriorRings.isEmpty() )
{
return mExteriorRing->clone();
}
else
{
QgsMultiCurveV2* multiCurve = new QgsMultiCurveV2();
multiCurve->addGeometry( mExteriorRing->clone() );
int nInteriorRings = mInteriorRings.size();
for ( int i = 0; i < nInteriorRings; ++i )
{
multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
}
return multiCurve;
}
}

QgsPolygonV2* QgsCurvePolygonV2::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
{
if ( !mExteriorRing )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgscurvepolygonv2.h
Expand Up @@ -55,6 +55,7 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
virtual double area() const override;
virtual double perimeter() const override;
QgsPolygonV2* surfaceToPolygon() const override;
virtual QgsAbstractGeometryV2* boundary() const override;

//curve polygon interface
int numInteriorRings() const;
Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgscurvev2.cpp
Expand Up @@ -17,6 +17,8 @@

#include "qgscurvev2.h"
#include "qgslinestringv2.h"
#include "qgspointv2.h"
#include "qgsmultipointv2.h"

QgsCurveV2::QgsCurveV2(): QgsAbstractGeometryV2()
{}
Expand Down Expand Up @@ -79,6 +81,20 @@ bool QgsCurveV2::nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const
return pointAt( id.vertex, vertex, id.type );
}

QgsAbstractGeometryV2* QgsCurveV2::boundary() const
{
if ( isEmpty() )
return nullptr;

if ( isClosed() )
return nullptr;

QgsMultiPointV2* multiPoint = new QgsMultiPointV2();
multiPoint->addGeometry( new QgsPointV2( startPoint() ) );
multiPoint->addGeometry( new QgsPointV2( endPoint() ) );
return multiPoint;
}

QgsCurveV2* QgsCurveV2::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
{
return curveToLine( tolerance, toleranceType );
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscurvev2.h
Expand Up @@ -102,6 +102,8 @@ class CORE_EXPORT QgsCurveV2: public QgsAbstractGeometryV2
*/
virtual QgsCurveV2* reversed() const = 0;

virtual QgsAbstractGeometryV2* boundary() const override;

/** Returns a geometry without curves. Caller takes ownership
* @param tolerance segmentation tolerance
* @param toleranceType maximum segmentation angle or maximum difference between approximation and curve*/
Expand Down
5 changes: 5 additions & 0 deletions src/core/geometry/qgsgeometrycollectionv2.cpp
Expand Up @@ -75,6 +75,11 @@ void QgsGeometryCollectionV2::clear()
clearCache(); //set bounding box invalid
}

QgsAbstractGeometryV2*QgsGeometryCollectionV2::boundary() const
{
return nullptr;
}

int QgsGeometryCollectionV2::numGeometries() const
{
return mGeometries.size();
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeometrycollectionv2.h
Expand Up @@ -54,6 +54,7 @@ class CORE_EXPORT QgsGeometryCollectionV2: public QgsAbstractGeometryV2
virtual int dimension() const override;
virtual QString geometryType() const override { return "GeometryCollection"; }
virtual void clear() override;
virtual QgsAbstractGeometryV2* boundary() const override;

/** Adds a geometry and takes ownership. Returns true in case of success.*/
virtual bool addGeometry( QgsAbstractGeometryV2* g );
Expand Down
23 changes: 23 additions & 0 deletions src/core/geometry/qgsmulticurvev2.cpp
Expand Up @@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgscompoundcurvev2.h"
#include "qgsgeometryutils.h"
#include "qgslinestringv2.h"
#include "qgsmultipointv2.h"

QgsMultiCurveV2::QgsMultiCurveV2()
: QgsGeometryCollectionV2()
Expand Down Expand Up @@ -125,3 +126,25 @@ QgsMultiCurveV2* QgsMultiCurveV2::reversed() const
}
return reversedMultiCurve;
}

QgsAbstractGeometryV2* QgsMultiCurveV2::boundary() const
{
QgsMultiPointV2* multiPoint = new QgsMultiPointV2();
for ( int i = 0; i < mGeometries.size(); ++i )
{
if ( QgsCurveV2* curve = dynamic_cast<QgsCurveV2*>( mGeometries.at( i ) ) )
{
if ( !curve->isClosed() )
{
multiPoint->addGeometry( new QgsPointV2( curve->startPoint() ) );
multiPoint->addGeometry( new QgsPointV2( curve->endPoint() ) );
}
}
}
if ( multiPoint->numGeometries() == 0 )
{
delete multiPoint;
return nullptr;
}
return multiPoint;
}
3 changes: 3 additions & 0 deletions src/core/geometry/qgsmulticurvev2.h
Expand Up @@ -47,6 +47,9 @@ class CORE_EXPORT QgsMultiCurveV2: public QgsGeometryCollectionV2
* @note added in QGIS 2.14
*/
QgsMultiCurveV2* reversed() const;

virtual QgsAbstractGeometryV2* boundary() const override;

};

#endif // QGSMULTICURVEV2_H
1 change: 1 addition & 0 deletions src/core/geometry/qgsmultilinestringv2.cpp
Expand Up @@ -118,3 +118,4 @@ QgsAbstractGeometryV2* QgsMultiLineStringV2::toCurveType() const
}
return multiCurve;
}

5 changes: 5 additions & 0 deletions src/core/geometry/qgsmultipointv2.cpp
Expand Up @@ -105,3 +105,8 @@ bool QgsMultiPointV2::addGeometry( QgsAbstractGeometryV2* g )
setZMTypeFromSubGeometry( g, QgsWKBTypes::MultiPoint );
return QgsGeometryCollectionV2::addGeometry( g );
}

QgsAbstractGeometryV2* QgsMultiPointV2::boundary() const
{
return nullptr;
}
2 changes: 2 additions & 0 deletions src/core/geometry/qgsmultipointv2.h
Expand Up @@ -44,6 +44,8 @@ class CORE_EXPORT QgsMultiPointV2: public QgsGeometryCollectionV2
/** Adds a geometry and takes ownership. Returns true in case of success*/
virtual bool addGeometry( QgsAbstractGeometryV2* g ) override;

virtual QgsAbstractGeometryV2* boundary() const override;

protected:

virtual bool wktOmitChildType() const override { return true; }
Expand Down
36 changes: 36 additions & 0 deletions src/core/geometry/qgsmultipolygonv2.cpp
Expand Up @@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgscurvepolygonv2.h"
#include "qgsmultilinestringv2.h"

QgsMultiPolygonV2::QgsMultiPolygonV2()
: QgsMultiSurfaceV2()
Expand Down Expand Up @@ -133,3 +134,38 @@ QgsAbstractGeometryV2* QgsMultiPolygonV2::toCurveType() const
}
return multiSurface;
}

QgsAbstractGeometryV2* QgsMultiPolygonV2::boundary() const
{
QgsMultiLineStringV2* multiLine = new QgsMultiLineStringV2();
for ( int i = 0; i < mGeometries.size(); ++i )
{
if ( QgsPolygonV2* polygon = dynamic_cast<QgsPolygonV2*>( mGeometries.at( i ) ) )
{
QgsAbstractGeometryV2* polygonBoundary = polygon->boundary();

if ( QgsLineStringV2* lineStringBoundary = dynamic_cast< QgsLineStringV2* >( polygonBoundary ) )
{
multiLine->addGeometry( lineStringBoundary );
}
else if ( QgsMultiLineStringV2* multiLineStringBoundary = dynamic_cast< QgsMultiLineStringV2* >( polygonBoundary ) )
{
for ( int j = 0; j < multiLineStringBoundary->numGeometries(); ++j )
{
multiLine->addGeometry( multiLineStringBoundary->geometryN( j )->clone() );
}
delete multiLineStringBoundary;
}
else
{
delete polygonBoundary;
}
}
}
if ( multiLine->numGeometries() == 0 )
{
delete multiLine;
return nullptr;
}
return multiLine;
}
3 changes: 2 additions & 1 deletion src/core/geometry/qgsmultipolygonv2.h
Expand Up @@ -40,14 +40,15 @@ class CORE_EXPORT QgsMultiPolygonV2: public QgsMultiSurfaceV2
QDomElement asGML3( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const override;
QString asJSON( int precision = 17 ) const override;


/** Adds a geometry and takes ownership. Returns true in case of success*/
virtual bool addGeometry( QgsAbstractGeometryV2* g ) override;

/** Returns the geometry converted to the more generic curve type QgsMultiSurfaceV2
@return the converted geometry. Caller takes ownership*/
QgsAbstractGeometryV2* toCurveType() const override;

virtual QgsAbstractGeometryV2* boundary() const override;

protected:

virtual bool wktOmitChildType() const override { return true; }
Expand Down
19 changes: 19 additions & 0 deletions src/core/geometry/qgsmultisurfacev2.cpp
Expand Up @@ -21,6 +21,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgscurvepolygonv2.h"
#include "qgsmulticurvev2.h"

QgsMultiSurfaceV2::QgsMultiSurfaceV2()
: QgsGeometryCollectionV2()
Expand Down Expand Up @@ -132,3 +133,21 @@ bool QgsMultiSurfaceV2::addGeometry( QgsAbstractGeometryV2* g )
setZMTypeFromSubGeometry( g, QgsWKBTypes::MultiSurface );
return QgsGeometryCollectionV2::addGeometry( g );
}

QgsAbstractGeometryV2* QgsMultiSurfaceV2::boundary() const
{
QgsMultiCurveV2* multiCurve = new QgsMultiCurveV2();
for ( int i = 0; i < mGeometries.size(); ++i )
{
if ( QgsSurfaceV2* surface = dynamic_cast<QgsSurfaceV2*>( mGeometries.at( i ) ) )
{
multiCurve->addGeometry( surface->boundary() );
}
}
if ( multiCurve->numGeometries() == 0 )
{
delete multiCurve;
return nullptr;
}
return multiCurve;
}
2 changes: 2 additions & 0 deletions src/core/geometry/qgsmultisurfacev2.h
Expand Up @@ -43,6 +43,8 @@ class CORE_EXPORT QgsMultiSurfaceV2: public QgsGeometryCollectionV2

/** Adds a geometry and takes ownership. Returns true in case of success*/
virtual bool addGeometry( QgsAbstractGeometryV2* g ) override;

virtual QgsAbstractGeometryV2* boundary() const override;
};

#endif // QGSMULTISURFACEV2_H
5 changes: 5 additions & 0 deletions src/core/geometry/qgspointv2.cpp
Expand Up @@ -281,6 +281,11 @@ QgsCoordinateSequenceV2 QgsPointV2::coordinateSequence() const
return cs;
}

QgsAbstractGeometryV2* QgsPointV2::boundary() const
{
return nullptr;
}

/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests.
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgspointv2.h
Expand Up @@ -169,6 +169,7 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
bool transformZ = false ) override;
void transform( const QTransform& t ) override;
virtual QgsCoordinateSequenceV2 coordinateSequence() const override;
virtual QgsAbstractGeometryV2* boundary() const override;

//low-level editing
virtual bool insertVertex( QgsVertexId position, const QgsPointV2& vertex ) override { Q_UNUSED( position ); Q_UNUSED( vertex ); return false; }
Expand Down

0 comments on commit 240c136

Please sign in to comment.