QGIS API Documentation  3.6.0-Noosa (5873452)
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 );
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 QgsGeometry::OperationResult::InvalidInputGeometryType; //not polygon / multipolygon;
54  }
55 
56  //ring must be closed
57  if ( !ring->isClosed() )
58  {
59  return QgsGeometry::OperationResult::AddRingNotClosed;
60  }
61  else if ( !ring->isRing() )
62  {
63  return QgsGeometry::OperationResult::AddRingNotValid;
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  int nInnerRings = ( *polyIter )->numInteriorRings();
77  for ( int i = 0; i < nInnerRings; ++i )
78  {
79  if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
80  {
81  return QgsGeometry::OperationResult::AddRingCrossesExistingRings;
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() );
92  return QgsGeometry::OperationResult::Success; //success
93  }
94  }
95  return QgsGeometry::OperationResult::AddRingNotInExistingFeature; //not contained in any outer ring
96 }
97 
98 QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part )
99 {
100  if ( !geom )
101  {
102  return QgsGeometry::OperationResult::InvalidBaseGeometry;
103  }
104 
105  if ( !part )
106  {
107  return QgsGeometry::OperationResult::InvalidInputGeometryType;
108  }
109 
110  //multitype?
111  QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
112  if ( !geomCollection )
113  {
114  return QgsGeometry::OperationResult::AddPartNotMultiGeometry;
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 = qgis::make_unique< QgsPolygon >();
129  }
130  else
131  {
132  poly = qgis::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 );
138  added = geomCollection->addGeometry( poly.release() );
139  }
140  else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon
141  || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::CurvePolygon )
142  {
143  added = geomCollection->addGeometry( part.release() );
144  }
145  else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon
146  || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiSurface )
147  {
148  std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
149 
150  int i;
151  int n = geomCollection->numGeometries();
152  for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ )
153  ;
154 
155  added = i == parts->numGeometries();
156  if ( !added )
157  {
158  while ( geomCollection->numGeometries() > n )
159  geomCollection->removeGeometry( n );
160  return QgsGeometry::OperationResult::InvalidInputGeometryType;
161  }
162  }
163  else
164  {
165  return QgsGeometry::OperationResult::InvalidInputGeometryType;
166  }
167  }
168  else
169  {
170  added = geomCollection->addGeometry( part.release() );
171  }
172  return added ? QgsGeometry::Success : QgsGeometry::OperationResult::InvalidInputGeometryType;
173 }
174 
175 bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )
176 {
177  if ( !geom || partNum < 0 )
178  {
179  return false;
180  }
181 
182  if ( ringNum < 1 ) //cannot remove exterior ring
183  {
184  return false;
185  }
186 
187  QgsAbstractGeometry *g = geom;
189  if ( c )
190  {
191  g = c->geometryN( partNum );
192  }
193  else if ( partNum > 0 )
194  {
195  //part num specified, but not a multi part geometry type
196  return false;
197  }
198 
200  if ( !cpoly )
201  {
202  return false;
203  }
204 
205  return cpoly->removeInteriorRing( ringNum - 1 );
206 }
207 
209 {
210  if ( !geom )
211  {
212  return false;
213  }
214 
216  if ( !c )
217  {
218  return false;
219  }
220 
221  return c->removeGeometry( partNum );
222 }
223 
224 std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom,
225  const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
226  const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
227 {
228  std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) );
229  if ( !geomEngine )
230  {
231  return nullptr;
232  }
233  QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType();
234 
235 
236  //check if g has polygon type
237  if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry )
238  {
239  return nullptr;
240  }
241 
242  if ( avoidIntersectionsLayers.isEmpty() )
243  return nullptr; //no intersections stored in project does not mean error
244 
245  QVector< QgsGeometry > nearGeometries;
246 
247  //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
248  for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers )
249  {
250  QgsFeatureIds ignoreIds;
251  QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer );
252  if ( ignoreIt != ignoreFeatures.constEnd() )
253  ignoreIds = ignoreIt.value();
254 
255  QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() )
257  .setNoAttributes() );
258  QgsFeature f;
259  while ( fi.nextFeature( f ) )
260  {
261  if ( ignoreIds.contains( f.id() ) )
262  continue;
263 
264  if ( !f.hasGeometry() )
265  continue;
266 
267  nearGeometries << f.geometry();
268  }
269  }
270 
271  if ( nearGeometries.isEmpty() )
272  {
273  return nullptr;
274  }
275 
276  std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) );
277  if ( !combinedGeometries )
278  {
279  return nullptr;
280  }
281 
282  std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) );
283 
284  return diffGeom;
285 }
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
Use exact geometry intersection (slower) instead of bounding boxes.
Curve polygon geometry type.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:770
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:118
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
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
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
Geometry collection.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:665
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer *> &avoidIntersectionsLayers, 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...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
Abstract base class for all geometries.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:39
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
static QgsGeometry::OperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition: qgsgeometry.h:123
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:820
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
QgsGeometry geometry
Definition: qgsfeature.h:67
bool nextFeature(QgsFeature &f)
Operation succeeded.
Definition: qgsgeometry.h:120
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:429
virtual int numPoints() const =0
Returns the number of points in the curve.
static QgsGeometry::OperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.