QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
18#include <limits>
19
20#include "qgscurve.h"
21#include "qgscurvepolygon.h"
22#include "qgsfeatureiterator.h"
23#include "qgsgeometry.h"
25#include "qgsgeometryengine.h"
26#include "qgspolygon.h"
27#include "qgsvectorlayer.h"
28
30{
31 if ( !ring )
32 {
34 }
35
36 QVector< QgsCurvePolygon * > polygonList;
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?
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() ) == Qgis::WkbType::Polygon
138 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::Triangle
139 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::CurvePolygon )
140 {
141 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( part.get() ) )
142 {
143 if ( QgsWkbTypes::flatType( geom->wkbType() ) == Qgis::WkbType::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() ) == Qgis::WkbType::MultiPolygon
157 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::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 {
186 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::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() ) == Qgis::WkbType::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( qgis::down_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;
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
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
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 Qgis::WkbType geomTypeBeforeModification = geom.wkbType();
294
295
296 //check if g has polygon type
297 if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != Qgis::GeometryType::Polygon )
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 ( !diffGeom || geomEngine->isEqual( diffGeom.get() ) )
347 {
348 return nullptr;
349 }
350
351 if ( QgsWkbTypes::isMultiType( geomTypeBeforeModification ) && QgsWkbTypes::isSingleType( diffGeom->wkbType() ) )
352 {
353 QgsGeometry geom( std::move( diffGeom ) );
354 geom.convertToMultiType();
355 diffGeom.reset( geom.constGet()->clone() );
356 }
357
358 return diffGeom;
359}
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2042
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2046
@ Success
Operation succeeded.
Definition qgis.h:2043
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2058
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2057
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2053
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2055
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2045
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2056
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2198
@ Polygon
Polygons.
Definition qgis.h:361
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ LineString
LineString.
Definition qgis.h:280
@ Polygon
Polygon.
Definition qgis.h:281
@ MultiPolygon
MultiPolygon.
Definition qgis.h:285
@ Triangle
Triangle.
Definition qgis.h:282
@ MultiLineString
MultiLineString.
Definition qgis.h:284
@ MultiCurve
MultiCurve.
Definition qgis.h:290
@ CurvePolygon
CurvePolygon.
Definition qgis.h:289
@ MultiSurface
MultiSurface.
Definition qgis.h:291
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: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:54
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.
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:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
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.
A geometry is the spatial representation of a feature.
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, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
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 dataset.
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...
static Q_INVOKABLE bool isSingleType(Qgis::WkbType type)
Returns true if the WKB type is a single type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi 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
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds