QGIS API Documentation 3.99.0-Master (a8882ad4560)
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"
28#include "qgstriangle.h"
30#include "qgsvectorlayer.h"
31
33{
34 if ( !ring )
35 {
37 }
38
39 QVector< QgsCurvePolygon * > polygonList;
43
44 if ( curvePoly )
45 {
46 polygonList.append( curvePoly );
47 }
48 else if ( polySurface )
49 {
50 // PolyhedralSurface: collect all patches (which are QgsPolygon* that inherit from QgsCurvePolygon)
51 polygonList.reserve( polySurface->numPatches() );
52 for ( int i = 0; i < polySurface->numPatches(); ++i )
53 {
54 polygonList.append( polySurface->patchN( i ) );
55 }
56 }
57 else if ( multiGeom )
58 {
59 polygonList.reserve( multiGeom->numGeometries() );
60 for ( int i = 0; i < multiGeom->numGeometries(); ++i )
61 {
62 polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) );
63 }
64 }
65 else
66 {
67 return Qgis::GeometryOperationResult::InvalidInputGeometryType; //not polygon / multipolygon / polyhedral surface;
68 }
69
70 //ring must be closed
71 if ( !ring->isClosed() )
72 {
74 }
75 else if ( !ring->isRing() )
76 {
78 }
79
80 std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
81 ringGeom->prepareGeometry();
82
83 //for each polygon, test if inside outer ring and no intersection with other interior ring
84 QVector< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin();
85 for ( ; polyIter != polygonList.constEnd(); ++polyIter )
86 {
87 if ( ringGeom->within( *polyIter ) )
88 {
89 //check if disjoint with other interior rings
90 const int nInnerRings = ( *polyIter )->numInteriorRings();
91 for ( int i = 0; i < nInnerRings; ++i )
92 {
93 if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
94 {
96 }
97 }
98
99 //make sure dimensionality of ring matches geometry
100 if ( QgsWkbTypes::hasZ( geom->wkbType() ) )
101 ring->addZValue( 0 );
102 if ( QgsWkbTypes::hasM( geom->wkbType() ) )
103 ring->addMValue( 0 );
104
105 ( *polyIter )->addInteriorRing( ring.release() );
107 }
108 }
109 return Qgis::GeometryOperationResult::AddRingNotInExistingFeature; //not contained in any outer ring
110}
111
112Qgis::GeometryOperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part )
113{
114 if ( !geom )
115 {
117 }
118
119 if ( !part )
120 {
122 }
123
124 // Handle TIN - add triangle as patch
126 {
128 if ( !tin )
129 {
131 }
132
133 // Part can be a Triangle or a Polygon (that must be a triangle)
134 if ( QgsTriangle *triangle = qgsgeometry_cast<QgsTriangle *>( part.get() ) )
135 {
136 tin->addPatch( triangle->clone() );
138 }
139 else if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( part.get() ) )
140 {
141 // Validate that the polygon can be converted to a triangle (3 vertices + closing point)
142 if ( polygon->exteriorRing() && polygon->exteriorRing()->numPoints() == 4 )
143 {
144 auto triangle = std::make_unique<QgsTriangle>();
145 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
146 if ( !triangle->isEmpty() )
147 {
148 tin->addPatch( triangle.release() );
150 }
151 }
153 }
154 else if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ) )
155 {
156 // A closed curve with exactly 4 points (3 vertices + closing)
157 if ( curve->isClosed() && curve->numPoints() == 4 )
158 {
159 auto triangle = std::make_unique<QgsTriangle>();
160 triangle->setExteriorRing( curve->clone() );
161 if ( !triangle->isEmpty() )
162 {
163 tin->addPatch( triangle.release() );
165 }
166 }
168 }
170 }
171
172 // Handle PolyhedralSurface - add polygon as patch
174 {
176 if ( !polySurface )
177 {
179 }
180
181 // Part can be a Polygon or a CurvePolygon
182 if ( QgsPolygon *polygon = qgsgeometry_cast<QgsPolygon *>( part.get() ) )
183 {
184 polySurface->addPatch( polygon->clone() );
186 }
187 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( part.get() ) )
188 {
189 // Convert curve polygon to polygon
190 std::unique_ptr<QgsPolygon> polygon( curvePolygon->toPolygon() );
191 polySurface->addPatch( polygon.release() );
193 }
194 else if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ) )
195 {
196 // A closed curve becomes a polygon patch
197 if ( curve->isClosed() && curve->numPoints() >= 4 )
198 {
199 auto polygon = std::make_unique<QgsPolygon>();
200 polygon->setExteriorRing( curve->clone() );
201 polySurface->addPatch( polygon.release() );
203 }
205 }
207 }
208
209 //multitype?
211 if ( !geomCollection )
212 {
214 }
215
216 bool added = false;
219 {
220 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() );
221
222 if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
223 {
224 std::unique_ptr<QgsCurvePolygon> poly;
226 {
227 poly = std::make_unique< QgsPolygon >();
228 }
229 else
230 {
231 poly = std::make_unique< QgsCurvePolygon >();
232 }
233 poly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( part.release() ) );
234 added = geomCollection->addGeometry( poly.release() );
235 }
236 else if ( QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::Polygon
237 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::Triangle
238 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::CurvePolygon )
239 {
240 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( part.get() ) )
241 {
242 if ( QgsWkbTypes::flatType( geom->wkbType() ) == Qgis::WkbType::MultiPolygon && curvePolygon->hasCurvedSegments() )
243 {
244 //need to segmentize part as multipolygon does not support curves
245 std::unique_ptr< QgsCurvePolygon > polygon( curvePolygon->toPolygon() );
246 part = std::move( polygon );
247 }
248 added = geomCollection->addGeometry( qgsgeometry_cast<QgsCurvePolygon *>( part.release() ) );
249 }
250 else
251 {
252 added = geomCollection->addGeometry( part.release() );
253 }
254 }
255 else if ( QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiPolygon
256 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiSurface )
257 {
258 std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
259
260 int i;
261 const int n = geomCollection->numGeometries();
262 for ( i = 0; i < parts->numGeometries(); i++ )
263 {
264 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
265 break;
266 }
267
268 added = i == parts->numGeometries();
269 if ( !added )
270 {
271 while ( geomCollection->numGeometries() > n )
272 geomCollection->removeGeometry( n );
274 }
275 }
276 else
277 {
279 }
280 }
283 {
285 || QgsWkbTypes::flatType( part->wkbType() ) == Qgis::WkbType::MultiCurve )
286 {
287 std::unique_ptr<QgsGeometryCollection> parts( qgsgeometry_cast<QgsGeometryCollection *>( part.release() ) );
288
289 int i;
290 const int n = geomCollection->numGeometries();
291 for ( i = 0; i < parts->numGeometries(); i++ )
292 {
293 if ( !geomCollection->addGeometry( parts->geometryN( i )->clone() ) )
294 break;
295 }
296
297 added = i == parts->numGeometries();
298 if ( !added )
299 {
300 while ( geomCollection->numGeometries() > n )
301 geomCollection->removeGeometry( n );
303 }
304 }
305 else
306 {
307 if ( QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ) )
308 {
309 if ( QgsWkbTypes::flatType( geom->wkbType() ) == Qgis::WkbType::MultiLineString && curve->hasCurvedSegments() )
310 {
311 //need to segmentize part as multilinestring does not support curves
312 std::unique_ptr< QgsCurve > line( curve->segmentize() );
313 part = std::move( line );
314 }
315 added = geomCollection->addGeometry( qgis::down_cast<QgsCurve *>( part.release() ) );
316 }
317 else
318 {
319 added = geomCollection->addGeometry( part.release() );
320 }
321 }
322 }
323 else
324 {
325 added = geomCollection->addGeometry( part.release() );
326 }
328}
329
330bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
331{
332 if ( !geom || partNum < 0 )
333 {
334 return false;
335 }
336
337 if ( ringNum < 1 ) //cannot remove exterior ring
338 {
339 return false;
340 }
341
342 QgsAbstractGeometry *g = geom;
344 if ( c )
345 {
346 g = c->geometryN( partNum );
347 }
348 else if ( partNum > 0 )
349 {
350 //part num specified, but not a multi part geometry type
351 return false;
352 }
353
355 if ( !cpoly )
356 {
357 return false;
358 }
359
360 return cpoly->removeInteriorRing( ringNum - 1 );
361}
362
364{
365 if ( !geom )
366 {
367 return false;
368 }
369
371 if ( !c )
372 {
373 return false;
374 }
375
376 return c->removeGeometry( partNum );
377}
378
379std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom,
380 const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
381 bool &haveInvalidGeometry,
382 const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures
383 )
384{
385
386 haveInvalidGeometry = false;
387 std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
388 if ( !geomEngine )
389 {
390 return nullptr;
391 }
392 const Qgis::WkbType geomTypeBeforeModification = geom.wkbType();
393
394
395 //check if g has polygon type
396 if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != Qgis::GeometryType::Polygon )
397 {
398 return nullptr;
399 }
400
401 if ( avoidIntersectionsLayers.isEmpty() )
402 return nullptr; //no intersections stored in project does not mean error
403
404 QVector< QgsGeometry > nearGeometries;
405
406 //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
407 for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers )
408 {
409 QgsFeatureIds ignoreIds;
410 const QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer );
411 if ( ignoreIt != ignoreFeatures.constEnd() )
412 ignoreIds = ignoreIt.value();
413
414 QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
416 .setNoAttributes() );
417 QgsFeature f;
418 while ( fi.nextFeature( f ) )
419 {
420 if ( ignoreIds.contains( f.id() ) )
421 continue;
422
423 if ( !f.hasGeometry() )
424 continue;
425
426 if ( !f.geometry().isGeosValid() )
427 haveInvalidGeometry = true;
428
429 nearGeometries << f.geometry();
430 }
431 }
432
433 if ( nearGeometries.isEmpty() )
434 {
435 return nullptr;
436 }
437
438 const std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) );
439 if ( !combinedGeometries )
440 {
441 return nullptr;
442 }
443
444 std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) );
445 if ( !diffGeom || geomEngine->isEqual( diffGeom.get() ) )
446 {
447 return nullptr;
448 }
449
450 if ( QgsWkbTypes::isMultiType( geomTypeBeforeModification ) && QgsWkbTypes::isSingleType( diffGeom->wkbType() ) )
451 {
452 QgsGeometry geom( std::move( diffGeom ) );
453 geom.convertToMultiType();
454 diffGeom.reset( geom.constGet()->clone() );
455 }
456
457 return diffGeom;
458}
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2088
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2092
@ Success
Operation succeeded.
Definition qgis.h:2089
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2104
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2103
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2099
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2101
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2091
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2102
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2244
@ Polygon
Polygons.
Definition qgis.h:368
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ LineString
LineString.
Definition qgis.h:283
@ TIN
TIN.
Definition qgis.h:296
@ Polygon
Polygon.
Definition qgis.h:284
@ MultiPolygon
MultiPolygon.
Definition qgis.h:288
@ Triangle
Triangle.
Definition qgis.h:285
@ MultiLineString
MultiLineString.
Definition qgis.h:287
@ MultiCurve
MultiCurve.
Definition qgis.h:293
@ CurvePolygon
CurvePolygon.
Definition qgis.h:292
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:295
@ MultiSurface
MultiSurface.
Definition qgis.h:294
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...
Polygon geometry type.
Definition qgspolygon.h:33
Polyhedral surface geometry type.
int numPatches() const
Returns the number of patches contained with the polyhedral surface.
virtual void addPatch(QgsPolygon *patch)
Adds a patch to the geometry, transferring ownership to the polyhedral surface.
const QgsPolygon * patchN(int i) const
Retrieves a patch from the polyhedral surface.
Triangle geometry type.
Definition qgstriangle.h:33
Triangulated surface geometry type.
void addPatch(QgsPolygon *patch) override
Adds a patch to the geometry, transferring ownership to the polyhedral surface.
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