QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsgeometryeditutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometryeditutils.cpp
3 -------------------------------------------------------------------
4Date : 21 Jan 2015
5Copyright : (C) 2015 by Marco Hugentobler
6email : marco.hugentobler at sourcepole dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgsfeatureiterator.h"
18#include "qgscurve.h"
19#include "qgscurvepolygon.h"
20#include "qgspolygon.h"
21#include "qgsgeometry.h"
22#include "qgsgeometryengine.h"
23#include "qgsvectorlayer.h"
25#include <limits>
26
28{
29 if ( !ring )
30 {
32 }
33
34 QVector< QgsCurvePolygon * > polygonList;
35 QgsCurvePolygon *curvePoly = qgsgeometry_cast< QgsCurvePolygon * >( geom );
36 QgsGeometryCollection *multiGeom = qgsgeometry_cast< QgsGeometryCollection * >( geom );
37 if ( curvePoly )
38 {
39 polygonList.append( curvePoly );
40 }
41 else if ( multiGeom )
42 {
43 polygonList.reserve( multiGeom->numGeometries() );
44 for ( int i = 0; i < multiGeom->numGeometries(); ++i )
45 {
46 polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) );
47 }
48 }
49 else
50 {
51 return Qgis::GeometryOperationResult::InvalidInputGeometryType; //not polygon / multipolygon;
52 }
53
54 //ring must be closed
55 if ( !ring->isClosed() )
56 {
58 }
59 else if ( !ring->isRing() )
60 {
62 }
63
64 std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
65 ringGeom->prepareGeometry();
66
67 //for each polygon, test if inside outer ring and no intersection with other interior ring
68 QVector< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin();
69 for ( ; polyIter != polygonList.constEnd(); ++polyIter )
70 {
71 if ( ringGeom->within( *polyIter ) )
72 {
73 //check if disjoint with other interior rings
74 const int nInnerRings = ( *polyIter )->numInteriorRings();
75 for ( int i = 0; i < nInnerRings; ++i )
76 {
77 if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
78 {
80 }
81 }
82
83 //make sure dimensionality of ring matches geometry
84 if ( QgsWkbTypes::hasZ( geom->wkbType() ) )
85 ring->addZValue( 0 );
86 if ( QgsWkbTypes::hasM( geom->wkbType() ) )
87 ring->addMValue( 0 );
88
89 ( *polyIter )->addInteriorRing( ring.release() );
91 }
92 }
93 return Qgis::GeometryOperationResult::AddRingNotInExistingFeature; //not contained in any outer ring
94}
95
96Qgis::GeometryOperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part )
97{
98 if ( !geom )
99 {
101 }
102
103 if ( !part )
104 {
106 }
107
108 //multitype?
109 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
110 if ( !geomCollection )
111 {
113 }
114
115 bool added = false;
118 {
119 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() );
120
121 if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
122 {
123 std::unique_ptr<QgsCurvePolygon> poly;
125 {
126 poly = std::make_unique< QgsPolygon >();
127 }
128 else
129 {
130 poly = std::make_unique< QgsCurvePolygon >();
131 }
132 poly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( part.release() ) );
133 added = geomCollection->addGeometry( poly.release() );
134 }
135 else if ( QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::Polygon
136 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::Triangle
137 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::CurvePolygon )
138 {
139 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( part.get() ) )
140 {
141 if ( QgsWkbTypes::flatType( geom->wkbType() ) == Qgis::WkbType::MultiPolygon && curvePolygon->hasCurvedSegments() )
142 {
143 //need to segmentize part as multipolygon does not support curves
144 std::unique_ptr< QgsCurvePolygon > polygon( curvePolygon->toPolygon() );
145 part = std::move( polygon );
146 }
147 added = geomCollection->addGeometry( qgsgeometry_cast<QgsCurvePolygon *>( part.release() ) );
148 }
149 else
150 {
151 added = geomCollection->addGeometry( part.release() );
152 }
153 }
154 else if ( QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiPolygon
155 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiSurface )
156 {
157 std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
158
159 int i;
160 const int n = geomCollection->numGeometries();
161 for ( i = 0; i < parts->numGeometries(); i++ )
162 {
163 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
164 break;
165 }
166
167 added = i == parts->numGeometries();
168 if ( !added )
169 {
170 while ( geomCollection->numGeometries() > n )
171 geomCollection->removeGeometry( n );
173 }
174 }
175 else
176 {
178 }
179 }
182 {
184 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiCurve )
185 {
186 std::unique_ptr<QgsGeometryCollection> parts( qgsgeometry_cast<QgsGeometryCollection *>( part.release() ) );
187
188 int i;
189 const int n = geomCollection->numGeometries();
190 for ( i = 0; i < parts->numGeometries(); i++ )
191 {
192 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
193 break;
194 }
195
196 added = i == parts->numGeometries();
197 if ( !added )
198 {
199 while ( geomCollection->numGeometries() > n )
200 geomCollection->removeGeometry( n );
202 }
203 }
204 else
205 {
206 if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ) )
207 {
208 if ( QgsWkbTypes::flatType( geom->wkbType() ) == Qgis::WkbType::MultiLineString && curve->hasCurvedSegments() )
209 {
210 //need to segmentize part as multilinestring does not support curves
211 std::unique_ptr< QgsCurve > line( curve->segmentize() );
212 part = std::move( line );
213 }
214 added = geomCollection->addGeometry( qgsgeometry_cast<QgsCurve *>( part.release() ) );
215 }
216 else
217 {
218 added = geomCollection->addGeometry( part.release() );
219 }
220 }
221 }
222 else
223 {
224 added = geomCollection->addGeometry( part.release() );
225 }
227}
228
229bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
230{
231 if ( !geom || partNum < 0 )
232 {
233 return false;
234 }
235
236 if ( ringNum < 1 ) //cannot remove exterior ring
237 {
238 return false;
239 }
240
241 QgsAbstractGeometry *g = geom;
242 QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
243 if ( c )
244 {
245 g = c->geometryN( partNum );
246 }
247 else if ( partNum > 0 )
248 {
249 //part num specified, but not a multi part geometry type
250 return false;
251 }
252
253 QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g );
254 if ( !cpoly )
255 {
256 return false;
257 }
258
259 return cpoly->removeInteriorRing( ringNum - 1 );
260}
261
263{
264 if ( !geom )
265 {
266 return false;
267 }
268
269 QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
270 if ( !c )
271 {
272 return false;
273 }
274
275 return c->removeGeometry( partNum );
276}
277
278std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom,
279 const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
280 bool &haveInvalidGeometry,
281 const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures
282 )
283{
284
285 haveInvalidGeometry = false;
286 std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
287 if ( !geomEngine )
288 {
289 return nullptr;
290 }
291 const Qgis::WkbType geomTypeBeforeModification = geom.wkbType();
292
293
294 //check if g has polygon type
295 if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != Qgis::GeometryType::Polygon )
296 {
297 return nullptr;
298 }
299
300 if ( avoidIntersectionsLayers.isEmpty() )
301 return nullptr; //no intersections stored in project does not mean error
302
303 QVector< QgsGeometry > nearGeometries;
304
305 //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
306 for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers )
307 {
308 QgsFeatureIds ignoreIds;
309 const QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer );
310 if ( ignoreIt != ignoreFeatures.constEnd() )
311 ignoreIds = ignoreIt.value();
312
313 QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
315 .setNoAttributes() );
316 QgsFeature f;
317 while ( fi.nextFeature( f ) )
318 {
319 if ( ignoreIds.contains( f.id() ) )
320 continue;
321
322 if ( !f.hasGeometry() )
323 continue;
324
325 if ( !f.geometry().isGeosValid() )
326 haveInvalidGeometry = true;
327
328 nearGeometries << f.geometry();
329 }
330 }
331
332 if ( nearGeometries.isEmpty() )
333 {
334 return nullptr;
335 }
336
337 const std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) );
338 if ( !combinedGeometries )
339 {
340 return nullptr;
341 }
342
343 std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) );
344 if ( !diffGeom || geomEngine->isEqual( diffGeom.get() ) )
345 {
346 return nullptr;
347 }
348
349 if ( QgsWkbTypes::isMultiType( geomTypeBeforeModification ) && QgsWkbTypes::isSingleType( diffGeom->wkbType() ) )
350 {
351 QgsGeometry geom( std::move( diffGeom ) );
352 geom.convertToMultiType();
353 diffGeom.reset( geom.constGet()->clone() );
354 }
355
356 return diffGeom;
357}
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:1615
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint)
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ AddRingNotClosed
The input ring is not closed.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ AddRingNotValid
The input ring is not valid.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ LineString
LineString.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ Triangle
Triangle.
@ MultiLineString
MultiLineString.
@ MultiCurve
MultiCurve.
@ CurvePolygon
CurvePolygon.
@ MultiSurface
MultiSurface.
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Curve polygon geometry type.
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Geometry collection.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static Qgis::GeometryOperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer * > &avoidIntersectionsLayers, bool &haveInvalidGeometry, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Alters a geometry so that it avoids intersections with features from all open vector layers.
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Represents a vector layer which manages a vector based data sets.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:862
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:758
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
static bool isSingleType(Qgis::WkbType type)
Returns true if the WKB type is a single type.
Definition: qgswkbtypes.h:748
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37