QGIS API Documentation  3.27.0-Master (0e23467727)
qgsgeometryeditutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometryeditutils.cpp
3  -------------------------------------------------------------------
4 Date : 21 Jan 2015
5 Copyright : (C) 2015 by Marco Hugentobler
6 email : 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 
16 #include "qgsgeometryeditutils.h"
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 
98 Qgis::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 
231 bool 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 
280 std::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:902
@ 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:223
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Geometry collection.
int numGeometries() const SIP_HOLDGIL
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.
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.
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