QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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 if ( geomEngine->isEqual( diffGeom.get() ) )
347 {
348 return nullptr;
349 }
350
351 return diffGeom;
352}
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
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
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
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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.
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)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Type
The WKB type describes the number of dimensions a geometry has.
Definition qgswkbtypes.h:70
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
static Type flatType(Type type)
Returns the flat type for a WKB type.
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