QGIS API Documentation  3.21.0-Master (56b4176581)
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 *
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() ) )
88  if ( QgsWkbTypes::hasM( geom->wkbType() ) )
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
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  // Ownership is still with part, curve points to the same object and is transferred
135  // to poly here.
136  part.release();
137  poly->setExteriorRing( curve );
139  }
140  else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon
141  || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Triangle
142  || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::CurvePolygon )
143  {
145  }
146  else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon
147  || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiSurface )
148  {
149  std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
150
151  int i;
152  const int n = geomCollection->numGeometries();
153  for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ )
154  ;
155
156  added = i == parts->numGeometries();
158  {
159  while ( geomCollection->numGeometries() > n )
160  geomCollection->removeGeometry( n );
162  }
163  }
164  else
165  {
167  }
168  }
169  else
170  {
172  }
174 }
175
176 bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
177 {
178  if ( !geom || partNum < 0 )
179  {
180  return false;
181  }
182
183  if ( ringNum < 1 ) //cannot remove exterior ring
184  {
185  return false;
186  }
187
188  QgsAbstractGeometry *g = geom;
189  QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
190  if ( c )
191  {
192  g = c->geometryN( partNum );
193  }
194  else if ( partNum > 0 )
195  {
196  //part num specified, but not a multi part geometry type
197  return false;
198  }
199
200  QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g );
201  if ( !cpoly )
202  {
203  return false;
204  }
205
206  return cpoly->removeInteriorRing( ringNum - 1 );
207 }
208
210 {
211  if ( !geom )
212  {
213  return false;
214  }
215
216  QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom );
217  if ( !c )
218  {
219  return false;
220  }
221
222  return c->removeGeometry( partNum );
223 }
224
225 std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom,
226  const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
227  bool &haveInvalidGeometry,
228  const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures
229  )
230 {
231
232  haveInvalidGeometry = false;
233  std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
234  if ( !geomEngine )
235  {
236  return nullptr;
237  }
238  const QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType();
239
240
241  //check if g has polygon type
242  if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry )
243  {
244  return nullptr;
245  }
246
247  if ( avoidIntersectionsLayers.isEmpty() )
248  return nullptr; //no intersections stored in project does not mean error
249
250  QVector< QgsGeometry > nearGeometries;
251
252  //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
253  for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers )
254  {
255  QgsFeatureIds ignoreIds;
256  const QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer );
257  if ( ignoreIt != ignoreFeatures.constEnd() )
258  ignoreIds = ignoreIt.value();
259
260  QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
262  .setNoAttributes() );
263  QgsFeature f;
264  while ( fi.nextFeature( f ) )
265  {
266  if ( ignoreIds.contains( f.id() ) )
267  continue;
268
269  if ( !f.hasGeometry() )
270  continue;
271
272  if ( !f.geometry().isGeosValid() )
273  haveInvalidGeometry = true;
274
275  nearGeometries << f.geometry();
276  }
277  }
278
279  if ( nearGeometries.isEmpty() )
280  {
281  return nullptr;
282  }
283
284  const std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) );
285  if ( !combinedGeometries )
286  {
287  return nullptr;
288  }
289
290  std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) );
291
292  return diffGeom;
293 }
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:636
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
The input ring doesn't have any existing ring to fit into.
The input ring crosses existing rings (it is not disjoint)
The source geometry is not multi.
The input ring is not closed.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
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:52
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:205
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.
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:938
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
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:702
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
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