Skip to content

Commit bc73c56

Browse files
authored
Merge pull request #3341 from nyalldawson/boundary
New method for calculating geometry boundary
2 parents d3af8a1 + 1a4ceb1 commit bc73c56

34 files changed

+542
-2
lines changed

python/core/geometry/qgsabstractgeometryv2.sip

+7
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ class QgsAbstractGeometryV2
130130
*/
131131
bool isMeasure() const;
132132

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

135142
/** Sets the geometry from a WKB string.

python/core/geometry/qgscurvepolygonv2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
2929
virtual double area() const;
3030
virtual double perimeter() const;
3131
QgsPolygonV2* surfaceToPolygon() const;
32+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
33+
3234

3335
//curve polygon interface
3436
int numInteriorRings() const;

python/core/geometry/qgscurvev2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class QgsCurveV2: public QgsAbstractGeometryV2
7575
*/
7676
virtual QgsCurveV2* reversed() const = 0 /Factory/;
7777

78+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
79+
7880
/** Returns a geometry without curves. Caller takes ownership
7981
* @param tolerance segmentation tolerance
8082
* @param toleranceType maximum segmentation angle or maximum difference between approximation and curve*/

python/core/geometry/qgsgeometrycollectionv2.sip

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
3030
virtual int dimension() const;
3131
virtual QString geometryType() const;
3232
virtual void clear();
33+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
3334

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

python/core/geometry/qgsmulticurvev2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ class QgsMultiCurveV2: public QgsGeometryCollectionV2
2626
* @note added in QGIS 2.14
2727
*/
2828
QgsMultiCurveV2* reversed() const /Factory/;
29+
30+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
2931
};

python/core/geometry/qgsmultipointv2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class QgsMultiPointV2: public QgsGeometryCollectionV2
2020
/** Adds a geometry and takes ownership. Returns true in case of success*/
2121
virtual bool addGeometry( QgsAbstractGeometryV2* g );
2222

23+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
24+
2325
protected:
2426

2527
virtual bool wktOmitChildType() const;

python/core/geometry/qgsmultipolygonv2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class QgsMultiPolygonV2: public QgsMultiSurfaceV2
2424
@return the converted geometry. Caller takes ownership*/
2525
QgsAbstractGeometryV2* toCurveType() const /Factory/;
2626

27+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
28+
2729
protected:
2830

2931
virtual bool wktOmitChildType() const;

python/core/geometry/qgsmultisurfacev2.sip

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class QgsMultiSurfaceV2: public QgsGeometryCollectionV2
2020
/** Adds a geometry and takes ownership. Returns true in case of success*/
2121
virtual bool addGeometry( QgsAbstractGeometryV2* g );
2222

23+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
24+
2325
/** Returns a geometry without curves. Caller takes ownership*/
2426
QgsAbstractGeometryV2* segmentize() const /Factory/;
2527
};

python/core/geometry/qgspointv2.sip

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class QgsPointV2: public QgsAbstractGeometryV2
157157
bool transformZ = false );
158158
void transform( const QTransform& t );
159159
virtual QList< QList< QList< QgsPointV2 > > > coordinateSequence() const;
160+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
160161

161162
//low-level editing
162163
virtual bool insertVertex( QgsVertexId position, const QgsPointV2& vertex );

python/core/geometry/qgspolygonv2.sip

+1
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ class QgsPolygonV2: public QgsCurvePolygonV2
3535
//overridden to handle LineString25D rings
3636
virtual void setExteriorRing( QgsCurveV2* ring /Transfer/ );
3737

38+
virtual QgsAbstractGeometryV2* boundary() const /Factory/;
3839
};

resources/function_help/json/boundary

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "boundary",
3+
"type": "function",
4+
"description":"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. Some geometry types do not have a defined boundary, eg points or geometry collections, and will return null.",
5+
"arguments": [ {"arg":"geometry","description":"a geometry"} ],
6+
"examples": [ { "expression":"geom_to_wkt(boundary(geom_from_wkt('Polygon((1 1, 0 0, -1 1, 1 1))')))", "returns":"'LineString(1 1,0 0,-1 1,1 1)'"}]
7+
}
8+

src/core/geometry/qgsabstractgeometryv2.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,16 @@ class CORE_EXPORT QgsAbstractGeometryV2
111111
virtual bool isValid() const = 0;
112112
virtual QgsMultiPointV2* locateAlong() const = 0;
113113
virtual QgsMultiCurveV2* locateBetween() const = 0;
114-
virtual QgsCurveV2* boundary() const = 0;
115114
virtual QgsRectangle envelope() const = 0;
116115
#endif
117116

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

120126
/** Sets the geometry from a WKB string.

src/core/geometry/qgscurvepolygonv2.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "qgslinestringv2.h"
2424
#include "qgspolygonv2.h"
2525
#include "qgswkbptr.h"
26+
#include "qgsmulticurvev2.h"
2627
#include <QPainter>
2728
#include <QPainterPath>
2829

@@ -420,6 +421,25 @@ QgsPolygonV2* QgsCurvePolygonV2::surfaceToPolygon() const
420421
return polygon;
421422
}
422423

424+
QgsAbstractGeometryV2* QgsCurvePolygonV2::boundary() const
425+
{
426+
if ( mInteriorRings.isEmpty() )
427+
{
428+
return mExteriorRing->clone();
429+
}
430+
else
431+
{
432+
QgsMultiCurveV2* multiCurve = new QgsMultiCurveV2();
433+
multiCurve->addGeometry( mExteriorRing->clone() );
434+
int nInteriorRings = mInteriorRings.size();
435+
for ( int i = 0; i < nInteriorRings; ++i )
436+
{
437+
multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
438+
}
439+
return multiCurve;
440+
}
441+
}
442+
423443
QgsPolygonV2* QgsCurvePolygonV2::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
424444
{
425445
if ( !mExteriorRing )

src/core/geometry/qgscurvepolygonv2.h

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
5555
virtual double area() const override;
5656
virtual double perimeter() const override;
5757
QgsPolygonV2* surfaceToPolygon() const override;
58+
virtual QgsAbstractGeometryV2* boundary() const override;
5859

5960
//curve polygon interface
6061
int numInteriorRings() const;

src/core/geometry/qgscurvev2.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "qgscurvev2.h"
1919
#include "qgslinestringv2.h"
2020
#include "qgspointv2.h"
21+
#include "qgsmultipointv2.h"
2122

2223
QgsCurveV2::QgsCurveV2(): QgsAbstractGeometryV2()
2324
{}
@@ -80,6 +81,20 @@ bool QgsCurveV2::nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const
8081
return pointAt( id.vertex, vertex, id.type );
8182
}
8283

84+
QgsAbstractGeometryV2* QgsCurveV2::boundary() const
85+
{
86+
if ( isEmpty() )
87+
return nullptr;
88+
89+
if ( isClosed() )
90+
return nullptr;
91+
92+
QgsMultiPointV2* multiPoint = new QgsMultiPointV2();
93+
multiPoint->addGeometry( new QgsPointV2( startPoint() ) );
94+
multiPoint->addGeometry( new QgsPointV2( endPoint() ) );
95+
return multiPoint;
96+
}
97+
8398
QgsCurveV2* QgsCurveV2::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
8499
{
85100
return curveToLine( tolerance, toleranceType );

src/core/geometry/qgscurvev2.h

+2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class CORE_EXPORT QgsCurveV2: public QgsAbstractGeometryV2
102102
*/
103103
virtual QgsCurveV2* reversed() const = 0;
104104

105+
virtual QgsAbstractGeometryV2* boundary() const override;
106+
105107
/** Returns a geometry without curves. Caller takes ownership
106108
* @param tolerance segmentation tolerance
107109
* @param toleranceType maximum segmentation angle or maximum difference between approximation and curve*/

src/core/geometry/qgsgeometrycollectionv2.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ void QgsGeometryCollectionV2::clear()
7575
clearCache(); //set bounding box invalid
7676
}
7777

78+
QgsAbstractGeometryV2*QgsGeometryCollectionV2::boundary() const
79+
{
80+
return nullptr;
81+
}
82+
7883
int QgsGeometryCollectionV2::numGeometries() const
7984
{
8085
return mGeometries.size();

src/core/geometry/qgsgeometrycollectionv2.h

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class CORE_EXPORT QgsGeometryCollectionV2: public QgsAbstractGeometryV2
5454
virtual int dimension() const override;
5555
virtual QString geometryType() const override { return "GeometryCollection"; }
5656
virtual void clear() override;
57+
virtual QgsAbstractGeometryV2* boundary() const override;
5758

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

src/core/geometry/qgsmulticurvev2.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
2020
#include "qgscompoundcurvev2.h"
2121
#include "qgsgeometryutils.h"
2222
#include "qgslinestringv2.h"
23+
#include "qgsmultipointv2.h"
2324

2425
QgsMultiCurveV2::QgsMultiCurveV2()
2526
: QgsGeometryCollectionV2()
@@ -125,3 +126,25 @@ QgsMultiCurveV2* QgsMultiCurveV2::reversed() const
125126
}
126127
return reversedMultiCurve;
127128
}
129+
130+
QgsAbstractGeometryV2* QgsMultiCurveV2::boundary() const
131+
{
132+
QgsMultiPointV2* multiPoint = new QgsMultiPointV2();
133+
for ( int i = 0; i < mGeometries.size(); ++i )
134+
{
135+
if ( QgsCurveV2* curve = dynamic_cast<QgsCurveV2*>( mGeometries.at( i ) ) )
136+
{
137+
if ( !curve->isClosed() )
138+
{
139+
multiPoint->addGeometry( new QgsPointV2( curve->startPoint() ) );
140+
multiPoint->addGeometry( new QgsPointV2( curve->endPoint() ) );
141+
}
142+
}
143+
}
144+
if ( multiPoint->numGeometries() == 0 )
145+
{
146+
delete multiPoint;
147+
return nullptr;
148+
}
149+
return multiPoint;
150+
}

src/core/geometry/qgsmulticurvev2.h

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class CORE_EXPORT QgsMultiCurveV2: public QgsGeometryCollectionV2
4747
* @note added in QGIS 2.14
4848
*/
4949
QgsMultiCurveV2* reversed() const;
50+
51+
virtual QgsAbstractGeometryV2* boundary() const override;
52+
5053
};
5154

5255
#endif // QGSMULTICURVEV2_H

src/core/geometry/qgsmultilinestringv2.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,4 @@ QgsAbstractGeometryV2* QgsMultiLineStringV2::toCurveType() const
118118
}
119119
return multiCurve;
120120
}
121+

src/core/geometry/qgsmultipointv2.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,8 @@ bool QgsMultiPointV2::addGeometry( QgsAbstractGeometryV2* g )
105105
setZMTypeFromSubGeometry( g, QgsWKBTypes::MultiPoint );
106106
return QgsGeometryCollectionV2::addGeometry( g );
107107
}
108+
109+
QgsAbstractGeometryV2* QgsMultiPointV2::boundary() const
110+
{
111+
return nullptr;
112+
}

src/core/geometry/qgsmultipointv2.h

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class CORE_EXPORT QgsMultiPointV2: public QgsGeometryCollectionV2
4444
/** Adds a geometry and takes ownership. Returns true in case of success*/
4545
virtual bool addGeometry( QgsAbstractGeometryV2* g ) override;
4646

47+
virtual QgsAbstractGeometryV2* boundary() const override;
48+
4749
protected:
4850

4951
virtual bool wktOmitChildType() const override { return true; }

src/core/geometry/qgsmultipolygonv2.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
2020
#include "qgslinestringv2.h"
2121
#include "qgspolygonv2.h"
2222
#include "qgscurvepolygonv2.h"
23+
#include "qgsmultilinestringv2.h"
2324

2425
QgsMultiPolygonV2::QgsMultiPolygonV2()
2526
: QgsMultiSurfaceV2()
@@ -133,3 +134,38 @@ QgsAbstractGeometryV2* QgsMultiPolygonV2::toCurveType() const
133134
}
134135
return multiSurface;
135136
}
137+
138+
QgsAbstractGeometryV2* QgsMultiPolygonV2::boundary() const
139+
{
140+
QgsMultiLineStringV2* multiLine = new QgsMultiLineStringV2();
141+
for ( int i = 0; i < mGeometries.size(); ++i )
142+
{
143+
if ( QgsPolygonV2* polygon = dynamic_cast<QgsPolygonV2*>( mGeometries.at( i ) ) )
144+
{
145+
QgsAbstractGeometryV2* polygonBoundary = polygon->boundary();
146+
147+
if ( QgsLineStringV2* lineStringBoundary = dynamic_cast< QgsLineStringV2* >( polygonBoundary ) )
148+
{
149+
multiLine->addGeometry( lineStringBoundary );
150+
}
151+
else if ( QgsMultiLineStringV2* multiLineStringBoundary = dynamic_cast< QgsMultiLineStringV2* >( polygonBoundary ) )
152+
{
153+
for ( int j = 0; j < multiLineStringBoundary->numGeometries(); ++j )
154+
{
155+
multiLine->addGeometry( multiLineStringBoundary->geometryN( j )->clone() );
156+
}
157+
delete multiLineStringBoundary;
158+
}
159+
else
160+
{
161+
delete polygonBoundary;
162+
}
163+
}
164+
}
165+
if ( multiLine->numGeometries() == 0 )
166+
{
167+
delete multiLine;
168+
return nullptr;
169+
}
170+
return multiLine;
171+
}

src/core/geometry/qgsmultipolygonv2.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@ class CORE_EXPORT QgsMultiPolygonV2: public QgsMultiSurfaceV2
4040
QDomElement asGML3( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const override;
4141
QString asJSON( int precision = 17 ) const override;
4242

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

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

50+
virtual QgsAbstractGeometryV2* boundary() const override;
51+
5152
protected:
5253

5354
virtual bool wktOmitChildType() const override { return true; }

src/core/geometry/qgsmultisurfacev2.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ email : marco.hugentobler at sourcepole dot com
2121
#include "qgslinestringv2.h"
2222
#include "qgspolygonv2.h"
2323
#include "qgscurvepolygonv2.h"
24+
#include "qgsmulticurvev2.h"
2425

2526
QgsMultiSurfaceV2::QgsMultiSurfaceV2()
2627
: QgsGeometryCollectionV2()
@@ -132,3 +133,21 @@ bool QgsMultiSurfaceV2::addGeometry( QgsAbstractGeometryV2* g )
132133
setZMTypeFromSubGeometry( g, QgsWKBTypes::MultiSurface );
133134
return QgsGeometryCollectionV2::addGeometry( g );
134135
}
136+
137+
QgsAbstractGeometryV2* QgsMultiSurfaceV2::boundary() const
138+
{
139+
QgsMultiCurveV2* multiCurve = new QgsMultiCurveV2();
140+
for ( int i = 0; i < mGeometries.size(); ++i )
141+
{
142+
if ( QgsSurfaceV2* surface = dynamic_cast<QgsSurfaceV2*>( mGeometries.at( i ) ) )
143+
{
144+
multiCurve->addGeometry( surface->boundary() );
145+
}
146+
}
147+
if ( multiCurve->numGeometries() == 0 )
148+
{
149+
delete multiCurve;
150+
return nullptr;
151+
}
152+
return multiCurve;
153+
}

src/core/geometry/qgsmultisurfacev2.h

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class CORE_EXPORT QgsMultiSurfaceV2: public QgsGeometryCollectionV2
4343

4444
/** Adds a geometry and takes ownership. Returns true in case of success*/
4545
virtual bool addGeometry( QgsAbstractGeometryV2* g ) override;
46+
47+
virtual QgsAbstractGeometryV2* boundary() const override;
4648
};
4749

4850
#endif // QGSMULTISURFACEV2_H

src/core/geometry/qgspointv2.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ QgsCoordinateSequenceV2 QgsPointV2::coordinateSequence() const
281281
return cs;
282282
}
283283

284+
QgsAbstractGeometryV2* QgsPointV2::boundary() const
285+
{
286+
return nullptr;
287+
}
288+
284289
/***************************************************************************
285290
* This class is considered CRITICAL and any change MUST be accompanied with
286291
* full unit tests.

0 commit comments

Comments
 (0)