QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsoverlayutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsoverlayutils.cpp
3 ---------------------
4 Date : April 2018
5 Copyright : (C) 2018 by Martin Dobias
6 Email : wonder dot sk at gmail 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 "qgsoverlayutils.h"
17
18#include "qgsgeometryengine.h"
19#include "qgsfeature.h"
20#include "qgsfeaturerequest.h"
21#include "qgsfeaturesource.h"
23
25
26bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, Qgis::GeometryType geometryType, SanitizeFlags flags )
27{
28 if ( geom.isNull() )
29 {
30 // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
31 throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: intersection failed." ), geom.lastError() ) );
32 }
33
34 // Intersection of geometries may give use also geometries we do not want in our results.
35 // For example, two square polygons touching at the corner have a point as the intersection, but no area.
36 // In other cases we may get a mixture of geometries in the output - we want to keep only the expected types.
38 {
39 // try to filter out irrelevant parts with different geometry type than what we want
40 geom.convertGeometryCollectionToSubclass( geometryType );
41 if ( geom.isEmpty() )
42 return false;
43 }
44
45 if ( QgsWkbTypes::geometryType( geom.wkbType() ) != geometryType )
46 {
47 // we can't make use of this resulting geometry
48 return false;
49 }
50
51 if ( geometryType != Qgis::GeometryType::Point
52 || !( flags & SanitizeFlag::DontPromotePointGeometryToMultiPoint ) )
53 {
54 // some data providers are picky about the geometries we pass to them: we can't add single-part geometries
55 // when we promised multi-part geometries, so ensure we have the right type
56 geom.convertToMultiType();
57 }
58
59 return true;
60}
61
62
64static bool sanitizeDifferenceResult( QgsGeometry &geom, Qgis::GeometryType geometryType, QgsOverlayUtils::SanitizeFlags flags )
65{
66 if ( geom.isNull() )
67 {
68 // TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
69 throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: difference failed." ), geom.lastError() ) );
70 }
71
72 //fix geometry collections
74 {
75 // try to filter out irrelevant parts with different geometry type than what we want
76 geom.convertGeometryCollectionToSubclass( geometryType );
77 }
78
79
80 // if geomB covers the whole source geometry, we get an empty geometry collection
81 if ( geom.isEmpty() )
82 return false;
83
84 if ( geometryType != Qgis::GeometryType::Point
85 || !( flags & QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint ) )
86 {
87 // some data providers are picky about the geometries we pass to them: we can't add single-part geometries
88 // when we promised multi-part geometries, so ensure we have the right type
89 geom.convertToMultiType();
90 }
91
92 return true;
93}
94
95
96static QString writeFeatureError()
97{
98 return QObject::tr( "Could not write feature" );
99}
100
101void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, QgsOverlayUtils::DifferenceOutput outputAttrs, const QgsGeometryParameters &parameters, SanitizeFlags flags )
102{
104 QgsFeatureRequest requestB;
105 requestB.setNoAttributes();
106 if ( outputAttrs != OutputBA )
107 requestB.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
108
109 double step = sourceB.featureCount() > 0 ? 100.0 / static_cast< double >( sourceB.featureCount() ) : 1;
110 long long i = 0;
111 QgsFeatureIterator fi = sourceB.getFeatures( requestB );
112
113 feedback->setProgressText( QObject::tr( "Creating spatial index" ) );
114 const QgsSpatialIndex indexB( fi, [&]( const QgsFeature & )->bool
115 {
116 i++;
117 if ( feedback->isCanceled() )
118 return false;
119
120 feedback->setProgress( static_cast< double >( i ) * step );
121
122 return true;
123 } );
124
125 if ( feedback->isCanceled() )
126 return;
127
128 const int fieldsCountA = sourceA.fields().count();
129 const int fieldsCountB = sourceB.fields().count();
130 QgsAttributes attrs;
131 attrs.resize( outputAttrs == OutputA ? fieldsCountA : ( fieldsCountA + fieldsCountB ) );
132
133 if ( totalCount == 0 )
134 totalCount = 1; // avoid division by zero
135
136 feedback->setProgressText( QObject::tr( "Calculating difference" ) );
137
138 QgsFeature featA;
139 QgsFeatureRequest requestA;
140 requestA.setInvalidGeometryCheck( context.invalidGeometryCheck() );
141 if ( outputAttrs == OutputBA )
142 requestA.setDestinationCrs( sourceB.sourceCrs(), context.transformContext() );
143 QgsFeatureIterator fitA = sourceA.getFeatures( requestA );
144 while ( fitA.nextFeature( featA ) )
145 {
146 if ( feedback->isCanceled() )
147 break;
148
149 if ( featA.hasGeometry() )
150 {
151 QgsGeometry geom( featA.geometry() );
152 const QgsFeatureIds intersects = qgis::listToSet( indexB.intersects( geom.boundingBox() ) );
153
154 QgsFeatureRequest request;
155 request.setFilterFids( intersects );
156 request.setNoAttributes();
157 if ( outputAttrs != OutputBA )
158 request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
159
160 std::unique_ptr< QgsGeometryEngine > engine;
161 if ( !intersects.isEmpty() )
162 {
163 // use prepared geometries for faster intersection tests
164 engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) );
165 engine->prepareGeometry();
166 }
167
168 QVector<QgsGeometry> geometriesB;
169 QgsFeature featB;
170 QgsFeatureIterator fitB = sourceB.getFeatures( request );
171 while ( fitB.nextFeature( featB ) )
172 {
173 if ( feedback->isCanceled() )
174 break;
175
176 if ( engine->intersects( featB.geometry().constGet() ) )
177 geometriesB << featB.geometry();
178 }
179
180 if ( !geometriesB.isEmpty() )
181 {
182 const QgsGeometry geomB = QgsGeometry::unaryUnion( geometriesB, parameters );
183 if ( !geomB.lastError().isEmpty() )
184 {
185 // This may happen if input geometries from a layer do not line up well (for example polygons
186 // that are nearly touching each other, but there is a very tiny overlap or gap at one of the edges).
187 // It is possible to get rid of this issue in two steps:
188 // 1. snap geometries with a small tolerance (e.g. 1cm) using QgsGeometrySnapperSingleSource
189 // 2. fix geometries (removes polygons collapsed to lines etc.) using MakeValid
190 throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: unary union failed." ), geomB.lastError() ) );
191 }
192 geom = geom.difference( geomB, parameters );
193 }
194
195 if ( !geom.isNull() && !sanitizeDifferenceResult( geom, geometryType, flags ) )
196 continue;
197
198 const QgsAttributes attrsA( featA.attributes() );
199 switch ( outputAttrs )
200 {
201 case OutputA:
202 attrs = attrsA;
203 break;
204 case OutputAB:
205 for ( int i = 0; i < fieldsCountA; ++i )
206 attrs[i] = attrsA[i];
207 break;
208 case OutputBA:
209 for ( int i = 0; i < fieldsCountA; ++i )
210 attrs[i + fieldsCountB] = attrsA[i];
211 break;
212 }
213
214 QgsFeature outFeat;
215 outFeat.setGeometry( geom );
216 outFeat.setAttributes( attrs );
217 if ( !sink.addFeature( outFeat, QgsFeatureSink::FastInsert ) )
218 throw QgsProcessingException( writeFeatureError() );
219 }
220 else
221 {
222 // TODO: should we write out features that do not have geometry?
223 if ( !sink.addFeature( featA, QgsFeatureSink::FastInsert ) )
224 throw QgsProcessingException( writeFeatureError() );
225 }
226
227 ++count;
228 feedback->setProgress( count / static_cast< double >( totalCount ) * 100. );
229 }
230}
231
232
233void QgsOverlayUtils::intersection( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, const QList<int> &fieldIndicesA, const QList<int> &fieldIndicesB, const QgsGeometryParameters &parameters )
234{
236 const int attrCount = fieldIndicesA.count() + fieldIndicesB.count();
237
238 QgsFeatureRequest request;
239 request.setNoAttributes();
240 request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
241
242 QgsFeature outFeat;
243
244 double step = sourceB.featureCount() > 0 ? 100.0 / static_cast< double >( sourceB.featureCount() ) : 1;
245 long long i = 0;
246 QgsFeatureIterator fi = sourceB.getFeatures( request );
247 feedback->setProgressText( QObject::tr( "Creating spatial index" ) );
248 const QgsSpatialIndex indexB( fi, [&]( const QgsFeature & )->bool
249 {
250 i++;
251 if ( feedback->isCanceled() )
252 return false;
253
254 feedback->setProgress( static_cast< double >( i ) * step );
255
256 return true;
257 } );
258
259 if ( feedback->isCanceled() )
260 return;
261
262 if ( totalCount == 0 )
263 totalCount = 1; // avoid division by zero
264
265 feedback->setProgressText( QObject::tr( "Calculating intersection" ) );
266
267 QgsFeature featA;
268 QgsFeatureIterator fitA = sourceA.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) );
269 while ( fitA.nextFeature( featA ) )
270 {
271 if ( feedback->isCanceled() )
272 break;
273
274 if ( !featA.hasGeometry() )
275 continue;
276
277 const QgsGeometry geom( featA.geometry() );
278 const QgsFeatureIds intersects = qgis::listToSet( indexB.intersects( geom.boundingBox() ) );
279
280 QgsFeatureRequest request;
281 request.setFilterFids( intersects );
282 request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
283 request.setSubsetOfAttributes( fieldIndicesB );
284
285 std::unique_ptr< QgsGeometryEngine > engine;
286 if ( !intersects.isEmpty() )
287 {
288 // use prepared geometries for faster intersection tests
289 engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) );
290 engine->prepareGeometry();
291 }
292
293 QgsAttributes outAttributes( attrCount );
294 const QgsAttributes attrsA( featA.attributes() );
295 for ( int i = 0; i < fieldIndicesA.count(); ++i )
296 outAttributes[i] = attrsA[fieldIndicesA[i]];
297
298 QgsFeature featB;
299 QgsFeatureIterator fitB = sourceB.getFeatures( request );
300 while ( fitB.nextFeature( featB ) )
301 {
302 if ( feedback->isCanceled() )
303 break;
304
305 const QgsGeometry tmpGeom( featB.geometry() );
306 if ( !engine->intersects( tmpGeom.constGet() ) )
307 continue;
308
309 QgsGeometry intGeom = geom.intersection( tmpGeom, parameters );
310 if ( !sanitizeIntersectionResult( intGeom, geometryType ) )
311 continue;
312
313 const QgsAttributes attrsB( featB.attributes() );
314 for ( int i = 0; i < fieldIndicesB.count(); ++i )
315 outAttributes[fieldIndicesA.count() + i] = attrsB[fieldIndicesB[i]];
316
317 outFeat.setGeometry( intGeom );
318 outFeat.setAttributes( outAttributes );
319 if ( !sink.addFeature( outFeat, QgsFeatureSink::FastInsert ) )
320 throw QgsProcessingException( writeFeatureError() );
321 }
322
323 ++count;
324 feedback->setProgress( count / static_cast<double >( totalCount ) * 100. );
325 }
326}
327
328void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, const QgsGeometryParameters &parameters, SanitizeFlags flags )
329{
330 long count = 0;
331 const long totalCount = source.featureCount();
332 if ( totalCount == 0 )
333 return; // nothing to do here
334
335 QgsFeatureId newFid = -1;
336
338
339 QgsFeatureRequest requestOnlyGeoms;
340 requestOnlyGeoms.setNoAttributes();
341
342 QgsFeatureRequest requestOnlyAttrs;
343 requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry );
344
345 QgsFeatureRequest requestOnlyIds;
346 requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry );
347 requestOnlyIds.setNoAttributes();
348
349 // make a set of used feature IDs so that we do not try to reuse them for newly added features
350 QgsFeature f;
351 QSet<QgsFeatureId> fids;
352 QgsFeatureIterator it = source.getFeatures( requestOnlyIds );
353 while ( it.nextFeature( f ) )
354 {
355 if ( feedback->isCanceled() )
356 return;
357
358 fids.insert( f.id() );
359 }
360
361 QHash<QgsFeatureId, QgsGeometry> geometries;
362 QgsSpatialIndex index;
363 QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds; // which features overlap a particular area
364
365 // resolve intersections
366
367 it = source.getFeatures( requestOnlyGeoms );
368 while ( it.nextFeature( f ) )
369 {
370 if ( feedback->isCanceled() )
371 return;
372
373 const QgsFeatureId fid1 = f.id();
374 QgsGeometry g1 = f.geometry();
375 std::unique_ptr< QgsGeometryEngine > g1engine;
376
377 geometries.insert( fid1, g1 );
378 index.addFeature( f );
379
380 const QgsRectangle bbox( f.geometry().boundingBox() );
381 const QList<QgsFeatureId> ids = index.intersects( bbox );
382 for ( const QgsFeatureId fid2 : ids )
383 {
384 if ( fid1 == fid2 )
385 continue;
386
387 if ( !g1engine )
388 {
389 // use prepared geometries for faster intersection tests
390 g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) );
391 g1engine->prepareGeometry();
392 }
393
394 const QgsGeometry g2 = geometries.value( fid2 );
395 if ( !g1engine->intersects( g2.constGet() ) )
396 continue;
397
398 QgsGeometry geomIntersection = g1.intersection( g2, parameters );
399 if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) )
400 continue;
401
402 //
403 // add intersection geometry
404 //
405
406 // figure out new fid
407 while ( fids.contains( newFid ) )
408 --newFid;
409 fids.insert( newFid );
410
411 geometries.insert( newFid, geomIntersection );
412 QgsFeature fx( newFid );
413 fx.setGeometry( geomIntersection );
414
415 index.addFeature( fx );
416
417 // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly
418 // created geometries - in such case we need to retrieve original IDs
419 QList<QgsFeatureId> lst;
420 if ( intersectingIds.contains( fid1 ) )
421 lst << intersectingIds.value( fid1 );
422 else
423 lst << fid1;
424 if ( intersectingIds.contains( fid2 ) )
425 lst << intersectingIds.value( fid2 );
426 else
427 lst << fid2;
428 intersectingIds.insert( newFid, lst );
429
430 //
431 // update f1
432 //
433
434 QgsGeometry g12 = g1.difference( g2, parameters );
435
436 index.deleteFeature( f );
437 geometries.remove( fid1 );
438
439 if ( sanitizeDifferenceResult( g12, geometryType, flags ) )
440 {
441 geometries.insert( fid1, g12 );
442
443 QgsFeature f1x( fid1 );
444 f1x.setGeometry( g12 );
445 index.addFeature( f1x );
446 }
447
448 //
449 // update f2
450 //
451
452 QgsGeometry g21 = g2.difference( g1, parameters );
453
454 QgsFeature f2old( fid2 );
455 f2old.setGeometry( g2 );
456 index.deleteFeature( f2old );
457
458 geometries.remove( fid2 );
459
460 if ( sanitizeDifferenceResult( g21, geometryType, flags ) )
461 {
462 geometries.insert( fid2, g21 );
463
464 QgsFeature f2x( fid2 );
465 f2x.setGeometry( g21 );
466 index.addFeature( f2x );
467 }
468
469 // update our temporary copy of the geometry to what is left from it
470 g1 = g12;
471 g1engine.reset();
472 }
473
474 ++count;
475 feedback->setProgress( count / static_cast< double >( totalCount ) * 100. );
476 }
477 if ( feedback->isCanceled() )
478 return;
479
480 // release some memory of structures we don't need anymore
481
482 fids.clear();
483 index = QgsSpatialIndex();
484
485 // load attributes
486
487 QHash<QgsFeatureId, QgsAttributes> attributesHash;
488 it = source.getFeatures( requestOnlyAttrs );
489 while ( it.nextFeature( f ) )
490 {
491 if ( feedback->isCanceled() )
492 return;
493
494 attributesHash.insert( f.id(), f.attributes() );
495 }
496
497 // store stuff in the sink
498
499 for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i )
500 {
501 if ( feedback->isCanceled() )
502 return;
503
504 QgsFeature outFeature( i.key() );
505 outFeature.setGeometry( i.value() );
506
507 if ( intersectingIds.contains( i.key() ) )
508 {
509 const QList<QgsFeatureId> ids = intersectingIds.value( i.key() );
510 for ( const QgsFeatureId id : ids )
511 {
512 outFeature.setAttributes( attributesHash.value( id ) );
513 if ( !sink.addFeature( outFeature, QgsFeatureSink::FastInsert ) )
514 throw QgsProcessingException( writeFeatureError() );
515 }
516 }
517 else
518 {
519 outFeature.setAttributes( attributesHash.value( i.key() ) );
520 if ( !sink.addFeature( outFeature, QgsFeatureSink::FastInsert ) )
521 throw QgsProcessingException( writeFeatureError() );
522 }
523 }
524}
525
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:228
@ GeometryCollection
GeometryCollection.
A vector of attributes.
Definition: qgsattributes.h:59
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 & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setInvalidGeometryCheck(InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
An interface for objects which accept features via addFeature(s) methods.
virtual bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags())
Adds a single feature to the sink.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
An interface for objects which provide features via a getFeatures method.
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
virtual QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
virtual Qgis::WkbType wkbType() const =0
Returns the geometry type for features returned by this source.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
virtual long long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
Encapsulates parameters under which a geometry operation is performed.
Definition: qgsgeometry.h:111
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const
Returns the behavior used for checking invalid geometries in input layers.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
A spatial index for QgsFeature objects.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a feature to the index.
bool deleteFeature(const QgsFeature &feature)
Removes a feature from the index.
static Qgis::GeometryType geometryType(Qgis::WkbType type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:865
static Qgis::WkbType multiType(Qgis::WkbType type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:201
static Qgis::WkbType flatType(Qgis::WkbType type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:629
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28