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