QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgsgeometryutils.h"
22#include "qgsgeometry.h"
23#include "qgsgeos.h"
24#include "qgsmultisurface.h"
25#include "qgsproject.h"
26#include "qgsvectorlayer.h"
27#include <limits>
28
30{
31 if ( !ring )
32 {
34 }
35
36 QVector< QgsCurvePolygon * > polygonList;
37 QgsCurvePolygon *curvePoly = qgsgeometry_cast< QgsCurvePolygon * >( geom );
38 QgsGeometryCollection *multiGeom = qgsgeometry_cast< QgsGeometryCollection * >( geom );
39 if ( curvePoly )
40 {
41 polygonList.append( curvePoly );
42 }
43 else if ( multiGeom )
44 {
45 polygonList.reserve( multiGeom->numGeometries() );
46 for ( int i = 0; i < multiGeom->numGeometries(); ++i )
47 {
48 polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) );
49 }
50 }
51 else
52 {
53 return Qgis::GeometryOperationResult::InvalidInputGeometryType; //not polygon / multipolygon;
54 }
55
56 //ring must be closed
57 if ( !ring->isClosed() )
58 {
60 }
61 else if ( !ring->isRing() )
62 {
64 }
65
66 std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
67 ringGeom->prepareGeometry();
68
69 //for each polygon, test if inside outer ring and no intersection with other interior ring
70 QVector< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin();
71 for ( ; polyIter != polygonList.constEnd(); ++polyIter )
72 {
73 if ( ringGeom->within( *polyIter ) )
74 {
75 //check if disjoint with other interior rings
76 const int nInnerRings = ( *polyIter )->numInteriorRings();
77 for ( int i = 0; i < nInnerRings; ++i )
78 {
79 if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
80 {
82 }
83 }
84
85 //make sure dimensionality of ring matches geometry
86 if ( QgsWkbTypes::hasZ( geom->wkbType() ) )
87 ring->addZValue( 0 );
88 if ( QgsWkbTypes::hasM( geom->wkbType() ) )
89 ring->addMValue( 0 );
90
91 ( *polyIter )->addInteriorRing( ring.release() );
93 }
94 }
95 return Qgis::GeometryOperationResult::AddRingNotInExistingFeature; //not contained in any outer ring
96}
97
98Qgis::GeometryOperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part )
99{
100 if ( !geom )
101 {
103 }
104
105 if ( !part )
106 {
108 }
109
110 //multitype?
111 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
112 if ( !geomCollection )
113 {
115 }
116
117 bool added = false;
120 {
121 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() );
122
123 if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
124 {
125 std::unique_ptr<QgsCurvePolygon> poly;
127 {
128 poly = std::make_unique< QgsPolygon >();
129 }
130 else
131 {
132 poly = std::make_unique< QgsCurvePolygon >();
133 }
134 poly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( part.release() ) );
135 added = geomCollection->addGeometry( poly.release() );
136 }
137 else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon
138 || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Triangle
139 || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::CurvePolygon )
140 {
141 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( part.get() ) )
142 {
143 if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon && curvePolygon->hasCurvedSegments() )
144 {
145 //need to segmentize part as multipolygon does not support curves
146 std::unique_ptr< QgsCurvePolygon > polygon( curvePolygon->toPolygon() );
147 part = std::move( polygon );
148 }
149 added = geomCollection->addGeometry( qgsgeometry_cast<QgsCurvePolygon *>( part.release() ) );
150 }
151 else
152 {
153 added = geomCollection->addGeometry( part.release() );
154 }
155 }
156 else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon
157 || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiSurface )
158 {
159 std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
160
161 int i;
162 const int n = geomCollection->numGeometries();
163 for ( i = 0; i < parts->numGeometries(); i++ )
164 {
165 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
166 break;
167 }
168
169 added = i == parts->numGeometries();
170 if ( !added )
171 {
172 while ( geomCollection->numGeometries() > n )
173 geomCollection->removeGeometry( n );
175 }
176 }
177 else
178 {
180 }
181 }
184 {
185 if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiLineString
186 || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiCurve )
187 {
188 std::unique_ptr<QgsGeometryCollection> parts( qgsgeometry_cast<QgsGeometryCollection *>( part.release() ) );
189
190 int i;
191 const int n = geomCollection->numGeometries();
192 for ( i = 0; i < parts->numGeometries(); i++ )
193 {
194 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
195 break;
196 }
197
198 added = i == parts->numGeometries();
199 if ( !added )
200 {
201 while ( geomCollection->numGeometries() > n )
202 geomCollection->removeGeometry( n );
204 }
205 }
206 else
207 {
208 if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ) )
209 {
210 if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiLineString && curve->hasCurvedSegments() )
211 {
212 //need to segmentize part as multilinestring does not support curves
213 std::unique_ptr< QgsCurve > line( curve->segmentize() );
214 part = std::move( line );
215 }
216 added = geomCollection->addGeometry( qgsgeometry_cast<QgsCurve *>( part.release() ) );
217 }
218 else
219 {
220 added = geomCollection->addGeometry( part.release() );
221 }
222 }
223 }
224 else
225 {
226 added = geomCollection->addGeometry( part.release() );
227 }
229}
230
231bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
232{
233 if ( !geom || partNum < 0 )
234 {
235 return false;
236 }
237
238 if ( ringNum < 1 ) //cannot remove exterior ring
239 {
240 return false;
241 }
242
243 QgsAbstractGeometry *g = geom;
244 QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
245 if ( c )
246 {
247 g = c->geometryN( partNum );
248 }
249 else if ( partNum > 0 )
250 {
251 //part num specified, but not a multi part geometry type
252 return false;
253 }
254
255 QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g );
256 if ( !cpoly )
257 {
258 return false;
259 }
260
261 return cpoly->removeInteriorRing( ringNum - 1 );
262}
263
265{
266 if ( !geom )
267 {
268 return false;
269 }
270
271 QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
272 if ( !c )
273 {
274 return false;
275 }
276
277 return c->removeGeometry( partNum );
278}
279
280std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom,
281 const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
282 bool &haveInvalidGeometry,
283 const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures
284 )
285{
286
287 haveInvalidGeometry = false;
288 std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
289 if ( !geomEngine )
290 {
291 return nullptr;
292 }
293 const QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType();
294
295
296 //check if g has polygon type
297 if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry )
298 {
299 return nullptr;
300 }
301
302 if ( avoidIntersectionsLayers.isEmpty() )
303 return nullptr; //no intersections stored in project does not mean error
304
305 QVector< QgsGeometry > nearGeometries;
306
307 //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
308 for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers )
309 {
310 QgsFeatureIds ignoreIds;
311 const QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer );
312 if ( ignoreIt != ignoreFeatures.constEnd() )
313 ignoreIds = ignoreIt.value();
314
315 QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
317 .setNoAttributes() );
318 QgsFeature f;
319 while ( fi.nextFeature( f ) )
320 {
321 if ( ignoreIds.contains( f.id() ) )
322 continue;
323
324 if ( !f.hasGeometry() )
325 continue;
326
327 if ( !f.geometry().isGeosValid() )
328 haveInvalidGeometry = true;
329
330 nearGeometries << f.geometry();
331 }
332 }
333
334 if ( nearGeometries.isEmpty() )
335 {
336 return nullptr;
337 }
338
339 const std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) );
340 if ( !combinedGeometries )
341 {
342 return nullptr;
343 }
344
345 std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) );
346
347 return diffGeom;
348}
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:955
@ 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.
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
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:36
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isClosed() const SIP_HOLDGIL
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
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:233
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the 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.
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.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
Represents a vector layer which manages a vector based data sets.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
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