QGIS API Documentation 4.3.0-Master (ffcfc20b9b4)
Loading...
Searching...
No Matches
qgsalgorithmdetectdatasetchanges.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmdetectdatasetchanges.cpp
3 -----------------------------------------
4 begin : December 2019
5 copyright : (C) 2019 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsgeometryfactory.h"
21#include "qgsspatialindex.h"
22#include "qgsvectorlayer.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29
30QString QgsDetectVectorChangesAlgorithm::name() const
31{
32 return u"detectvectorchanges"_s;
33}
34
35QString QgsDetectVectorChangesAlgorithm::displayName() const
36{
37 return QObject::tr( "Detect dataset changes" );
38}
39
40QStringList QgsDetectVectorChangesAlgorithm::tags() const
41{
42 return QObject::tr( "added,dropped,new,deleted,features,geometries,difference,delta,revised,original,version" ).split( ',' );
43}
44
45QString QgsDetectVectorChangesAlgorithm::group() const
46{
47 return QObject::tr( "Vector general" );
48}
49
50QString QgsDetectVectorChangesAlgorithm::groupId() const
51{
52 return u"vectorgeneral"_s;
53}
54
55void QgsDetectVectorChangesAlgorithm::initAlgorithm( const QVariantMap & )
56{
57 addParameter( new QgsProcessingParameterFeatureSource( u"ORIGINAL"_s, QObject::tr( "Original layer" ) ) );
58 addParameter( new QgsProcessingParameterFeatureSource( u"REVISED"_s, QObject::tr( "Revised layer" ) ) );
59
60 auto compareAttributesParam = std::make_unique<
61 QgsProcessingParameterField>( u"COMPARE_ATTRIBUTES"_s, QObject::tr( "Attributes to consider for match (or none to compare geometry only)" ), QVariant(), u"ORIGINAL"_s, Qgis::ProcessingFieldParameterDataType::Any, true, true );
62 compareAttributesParam->setDefaultToAllFields( true );
63 addParameter( compareAttributesParam.release() );
64
65 std::unique_ptr<QgsProcessingParameterDefinition> matchTypeParam = std::make_unique<
66 QgsProcessingParameterEnum>( u"MATCH_TYPE"_s, QObject::tr( "Geometry comparison behavior" ), QStringList() << QObject::tr( "Exact Match" ) << QObject::tr( "Tolerant Match (Topological Equality)" ), false, 1 );
67 matchTypeParam->setFlags( matchTypeParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
68 addParameter( matchTypeParam.release() );
69
70 addParameter( new QgsProcessingParameterFeatureSink( u"UNCHANGED"_s, QObject::tr( "Unchanged features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, true ) );
71 addParameter( new QgsProcessingParameterFeatureSink( u"ADDED"_s, QObject::tr( "Added features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, true ) );
72 addParameter( new QgsProcessingParameterFeatureSink( u"DELETED"_s, QObject::tr( "Deleted features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, true ) );
73
74 addOutput( new QgsProcessingOutputNumber( u"UNCHANGED_COUNT"_s, QObject::tr( "Count of unchanged features" ) ) );
75 addOutput( new QgsProcessingOutputNumber( u"ADDED_COUNT"_s, QObject::tr( "Count of features added in revised layer" ) ) );
76 addOutput( new QgsProcessingOutputNumber( u"DELETED_COUNT"_s, QObject::tr( "Count of features deleted from original layer" ) ) );
77}
78
79QString QgsDetectVectorChangesAlgorithm::shortHelpString() const
80{
81 return QObject::tr(
82 "This algorithm compares two vector layers, and determines which features are unchanged, added or deleted between "
83 "the two. It is designed for comparing two different versions of the same dataset.\n\n"
84 "When comparing features, the original and revised feature geometries will be compared against each other. Depending "
85 "on the Geometry Comparison Behavior setting, the comparison will either be made using an exact comparison (where "
86 "geometries must be an exact match for each other, including the order and count of vertices) or a topological "
87 "comparison only (where geometries are considered equal if all of their component edges overlap. E.g. "
88 "lines with the same vertex locations but opposite direction will be considered equal by this method). If the topological "
89 "comparison is selected then any z or m values present in the geometries will not be compared.\n\n"
90 "By default, the algorithm compares all attributes from the original and revised features. If the Attributes to Consider for Match "
91 "parameter is changed, then only the selected attributes will be compared (e.g. allowing users to ignore a timestamp or ID field "
92 "which is expected to change between the revisions).\n\n"
93 "If any features in the original or revised layers do not have an associated geometry, then care must be taken to ensure "
94 "that these features have a unique set of attributes selected for comparison. If this condition is not met, warnings will be "
95 "raised and the resultant outputs may be misleading.\n\n"
96 "The algorithm outputs three layers, one containing all features which are considered to be unchanged between the revisions, "
97 "one containing features deleted from the original layer which are not present in the revised layer, and one containing features "
98 "added to the revised layer which are not present in the original layer."
99 );
100}
101
102QString QgsDetectVectorChangesAlgorithm::shortDescription() const
103{
104 return QObject::tr( "Calculates features which are unchanged, added or deleted between two dataset versions." );
105}
106
107QgsDetectVectorChangesAlgorithm *QgsDetectVectorChangesAlgorithm::createInstance() const
108{
109 return new QgsDetectVectorChangesAlgorithm();
110}
111
112bool QgsDetectVectorChangesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
113{
114 mOriginal.reset( parameterAsSource( parameters, u"ORIGINAL"_s, context ) );
115 if ( !mOriginal )
116 throw QgsProcessingException( invalidSourceError( parameters, u"ORIGINAL"_s ) );
117
118 mRevised.reset( parameterAsSource( parameters, u"REVISED"_s, context ) );
119 if ( !mRevised )
120 throw QgsProcessingException( invalidSourceError( parameters, u"REVISED"_s ) );
121
122 mMatchType = static_cast<GeometryMatchType>( parameterAsEnum( parameters, u"MATCH_TYPE"_s, context ) );
123
124 switch ( mMatchType )
125 {
126 case Exact:
127 if ( mOriginal->wkbType() != mRevised->wkbType() )
129 QObject::tr( "Geometry type of revised layer (%1) does not match the original layer (%2). Consider using the \"Tolerant Match\" option instead." )
130 .arg( QgsWkbTypes::displayString( mRevised->wkbType() ), QgsWkbTypes::displayString( mOriginal->wkbType() ) )
131 );
132 break;
133
134 case Topological:
135 if ( QgsWkbTypes::geometryType( mOriginal->wkbType() ) != QgsWkbTypes::geometryType( mRevised->wkbType() ) )
137 QObject::tr( "Geometry type of revised layer (%1) does not match the original layer (%2)" )
139 );
140 break;
141 }
142
143 if ( mOriginal->sourceCrs() != mRevised->sourceCrs() )
144 feedback->reportError(
145 QObject::tr( "CRS for revised layer (%1) does not match the original layer (%2) - reprojection accuracy may affect geometry matching" )
146 .arg( mOriginal->sourceCrs().userFriendlyIdentifier(), mRevised->sourceCrs().userFriendlyIdentifier() ),
147 false
148 );
149
150 mFieldsToCompare = parameterAsStrings( parameters, u"COMPARE_ATTRIBUTES"_s, context );
151 mOriginalFieldsToCompareIndices.reserve( mFieldsToCompare.size() );
152 mRevisedFieldsToCompareIndices.reserve( mFieldsToCompare.size() );
153 QStringList missingOriginalFields;
154 QStringList missingRevisedFields;
155 for ( const QString &field : mFieldsToCompare )
156 {
157 const int originalIndex = mOriginal->fields().lookupField( field );
158 mOriginalFieldsToCompareIndices.append( originalIndex );
159 if ( originalIndex < 0 )
160 missingOriginalFields << field;
161
162 const int revisedIndex = mRevised->fields().lookupField( field );
163 if ( revisedIndex < 0 )
164 missingRevisedFields << field;
165 mRevisedFieldsToCompareIndices.append( revisedIndex );
166 }
167
168 if ( !missingOriginalFields.empty() )
169 throw QgsProcessingException( QObject::tr( "Original layer missing selected comparison attributes: %1" ).arg( missingOriginalFields.join( ',' ) ) );
170 if ( !missingRevisedFields.empty() )
171 throw QgsProcessingException( QObject::tr( "Revised layer missing selected comparison attributes: %1" ).arg( missingRevisedFields.join( ',' ) ) );
172
173 return true;
174}
175
176QVariantMap QgsDetectVectorChangesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
177{
178 QGS_MARK_ALGORITHM_SOURCE
179
180 QString unchangedDestId;
181 std::unique_ptr<QgsFeatureSink> unchangedSink( parameterAsSink( parameters, u"UNCHANGED"_s, context, unchangedDestId, mOriginal->fields(), mOriginal->wkbType(), mOriginal->sourceCrs() ) );
182 if ( !unchangedSink && parameters.value( u"UNCHANGED"_s ).isValid() )
183 throw QgsProcessingException( invalidSinkError( parameters, u"UNCHANGED"_s ) );
184
185 QString addedDestId;
186 std::unique_ptr<QgsFeatureSink> addedSink( parameterAsSink( parameters, u"ADDED"_s, context, addedDestId, mRevised->fields(), mRevised->wkbType(), mRevised->sourceCrs() ) );
187 if ( !addedSink && parameters.value( u"ADDED"_s ).isValid() )
188 throw QgsProcessingException( invalidSinkError( parameters, u"ADDED"_s ) );
189
190 QString deletedDestId;
191 std::unique_ptr<QgsFeatureSink> deletedSink( parameterAsSink( parameters, u"DELETED"_s, context, deletedDestId, mOriginal->fields(), mOriginal->wkbType(), mOriginal->sourceCrs() ) );
192 if ( !deletedSink && parameters.value( u"DELETED"_s ).isValid() )
193 throw QgsProcessingException( invalidSinkError( parameters, u"DELETED"_s ) );
194
195 // first iteration: we loop through the entire original layer, building up a spatial index of ALL original geometries
196 // and collecting the original geometries themselves along with the attributes to compare
197 QgsFeatureRequest request;
198 request.setSubsetOfAttributes( mOriginalFieldsToCompareIndices );
199
200 QgsFeatureIterator it = mOriginal->getFeatures( request );
201
202 double step = mOriginal->featureCount() > 0 ? 100.0 / mOriginal->featureCount() : 0;
203 QHash<QgsFeatureId, QgsGeometry> originalGeometries;
204 QHash<QgsFeatureId, QgsAttributes> originalAttributes;
205 QHash<QgsAttributes, QgsFeatureId> originalNullGeometryAttributes;
206 QHash<QgsAttributes, QgsFeatureId> originalEmptyGeometryAttributes;
207 long current = 0;
208
209 QgsAttributes attrs;
210 attrs.resize( mFieldsToCompare.size() );
211
212 const QgsSpatialIndex index( it, [&]( const QgsFeature &f ) -> bool {
213 if ( feedback->isCanceled() )
214 return false;
215
216 if ( !mFieldsToCompare.empty() )
217 {
218 int idx = 0;
219 for ( const int field : mOriginalFieldsToCompareIndices )
220 {
221 attrs[idx++] = f.attributes().at( field );
222 }
223 originalAttributes.insert( f.id(), attrs );
224 }
225
226 if ( f.hasGeometry() && !f.geometry().isEmpty() )
227 {
228 originalGeometries.insert( f.id(), f.geometry() );
229 }
230 else if ( f.hasGeometry() && f.geometry().isEmpty() )
231 {
232 auto emptyGeomIt = originalEmptyGeometryAttributes.constFind( attrs );
233 if ( emptyGeomIt != originalEmptyGeometryAttributes.constEnd() )
234 {
235 feedback->reportError(
236 QObject::tr(
237 "A non-unique set of comparison attributes was found for "
238 "one or more features with EMPTY geometries - results may be misleading (features %1 and %2)"
239 )
240 .arg( f.id() )
241 .arg( emptyGeomIt.value() )
242 );
243 }
244 else
245 {
246 originalEmptyGeometryAttributes.insert( attrs, f.id() );
247 }
248 }
249 else // no geometry
250 {
251 if ( originalNullGeometryAttributes.contains( attrs ) )
252 {
253 feedback->reportError(
254 QObject::tr(
255 "A non-unique set of comparison attributes was found for "
256 "one or more features without geometries - results may be misleading (features %1 and %2)"
257 )
258 .arg( f.id() )
259 .arg( originalNullGeometryAttributes.value( attrs ) )
260 );
261 }
262 else
263 {
264 originalNullGeometryAttributes.insert( attrs, f.id() );
265 }
266 }
267
268 // overall this loop takes about 10% of time
269 current++;
270 feedback->setProgress( 0.10 * current * step );
271 return true;
272 } );
273
274 QSet<QgsFeatureId> unchangedOriginalIds;
275 QSet<QgsFeatureId> addedRevisedIds;
276 current = 0;
277
278 // second iteration: we loop through ALL revised features, checking whether each is a match for a geometry from the
279 // original set. If so, check if the feature is unchanged. If there's no match with the original features, we mark it as an "added" feature
280 step = mRevised->featureCount() > 0 ? 100.0 / mRevised->featureCount() : 0;
281 QgsFeatureRequest revisedRequest = QgsFeatureRequest().setDestinationCrs( mOriginal->sourceCrs(), context.transformContext() );
282 revisedRequest.setSubsetOfAttributes( mRevisedFieldsToCompareIndices );
283 it = mRevised->getFeatures( revisedRequest );
284 QgsFeature revisedFeature;
285 while ( it.nextFeature( revisedFeature ) )
286 {
287 if ( feedback->isCanceled() )
288 break;
289
290 int idx = 0;
291 for ( const int field : mRevisedFieldsToCompareIndices )
292 {
293 attrs[idx++] = revisedFeature.attributes().at( field );
294 }
295
296 bool matched = false;
297
298 if ( !revisedFeature.hasGeometry() )
299 {
300 if ( originalNullGeometryAttributes.contains( attrs ) )
301 {
302 // found a match for feature
303 unchangedOriginalIds.insert( originalNullGeometryAttributes.value( attrs ) );
304 matched = true;
305 }
306 }
307 else if ( revisedFeature.hasGeometry() && revisedFeature.geometry().isEmpty() )
308 {
309 auto emptyIt = originalEmptyGeometryAttributes.constFind( attrs );
310 if ( emptyIt != originalEmptyGeometryAttributes.constEnd() )
311 {
312 // found a match for feature
313 unchangedOriginalIds.insert( emptyIt.value() );
314 matched = true;
315 }
316 }
317 else // revised feature has non-empty geometry
318 {
319 // can we match this feature?
320 const QList<QgsFeatureId> candidates = index.intersects( revisedFeature.geometry().boundingBox() );
321
322 // lazy evaluate -- there may be NO candidates!
323 QgsGeometry revised;
324
325 for ( const QgsFeatureId candidateId : candidates )
326 {
327 if ( unchangedOriginalIds.contains( candidateId ) )
328 {
329 // already matched this original feature
330 continue;
331 }
332
333 // attribute comparison is faster to do first, if desired
334 if ( !mFieldsToCompare.empty() )
335 {
336 if ( attrs != originalAttributes[candidateId] )
337 {
338 // attributes don't match, so candidates is not a match
339 continue;
340 }
341 }
342
343 QgsGeometry original = originalGeometries.value( candidateId );
344 // lazy evaluation
345 if ( revised.isNull() )
346 {
347 revised = revisedFeature.geometry();
348 // drop z/m if not wanted for match
349 switch ( mMatchType )
350 {
351 case Topological:
352 {
353 revised.get()->dropMValue();
354 revised.get()->dropZValue();
355 original.get()->dropMValue();
356 original.get()->dropZValue();
357 break;
358 }
359
360 case Exact:
361 break;
362 }
363 }
364
365 bool geometryMatch = false;
366 switch ( mMatchType )
367 {
368 case Topological:
369 {
370 geometryMatch = revised.isTopologicallyEqual( original );
371 break;
372 }
373
374 case Exact:
375 geometryMatch = revised.isExactlyEqual( original );
376 break;
377 }
378
379 if ( geometryMatch )
380 {
381 // candidate is a match for feature
382 unchangedOriginalIds.insert( candidateId );
383 matched = true;
384 break;
385 }
386 }
387 }
388
389 if ( !matched )
390 {
391 // new feature
392 addedRevisedIds.insert( revisedFeature.id() );
393 }
394
395 current++;
396 feedback->setProgress( 0.70 * current * step + 10 ); // takes about 70% of time
397 }
398
399 // third iteration: iterate back over the original features, and direct them to the appropriate sink.
400 // If they were marked as unchanged during the second iteration, we put them in the unchanged sink. Otherwise
401 // they are placed into the deleted sink.
402 step = mOriginal->featureCount() > 0 ? 100.0 / mOriginal->featureCount() : 0;
403
405 it = mOriginal->getFeatures( request );
406 current = 0;
407 long deleted = 0;
408 QgsFeature f;
409 QgsGeometry g;
410 QList<QgsFeatureId> emptyGeometryIds = originalEmptyGeometryAttributes.values();
411
412 while ( it.nextFeature( f ) )
413 {
414 if ( feedback->isCanceled() )
415 break;
416
417 // attempt to use already fetched geometry or use Null/Empty geometry
418 g = originalGeometries.value( f.id(), QgsGeometry() );
419 if ( g.isNull() && emptyGeometryIds.contains( f.id() ) )
420 {
421 g = QgsGeometry( QgsGeometryFactory::geomFromWkbType( mOriginal->wkbType() ) );
422 }
423 f.setGeometry( g );
424
425 if ( unchangedOriginalIds.contains( f.id() ) )
426 {
427 // unchanged
428 if ( unchangedSink )
429 {
430 if ( !unchangedSink->addFeature( f, QgsFeatureSink::FastInsert ) )
431 throw QgsProcessingException( writeFeatureError( unchangedSink.get(), parameters, u"UNCHANGED"_s ) );
432 else
433 feedback->featureAddedToSink( u"UNCHANGED"_s );
434 }
435 }
436 else
437 {
438 // deleted feature
439 if ( deletedSink )
440 {
441 if ( !deletedSink->addFeature( f, QgsFeatureSink::FastInsert ) )
442 throw QgsProcessingException( writeFeatureError( deletedSink.get(), parameters, u"DELETED"_s ) );
443 else
444 feedback->featureAddedToSink( u"DELETED"_s );
445 }
446 deleted++;
447 }
448
449 current++;
450 feedback->setProgress( 0.10 * current * step + 80 ); // takes about 10% of time
451 }
452
453 // forth iteration: collect all added features and add them to the added sink
454 // NOTE: while we could potentially do this as part of the second iteration and save some time, we instead
455 // do this here using a brand new request because the second iteration
456 // is fetching reprojected features and we ideally want geometries from the revised layer's actual CRS only here!
457 // also, the second iteration is only fetching the actual attributes used in the comparison, whereas we want
458 // to include all attributes in the "added" output
459 if ( addedSink )
460 {
461 step = addedRevisedIds.size() > 0 ? 100.0 / addedRevisedIds.size() : 0;
462 it = mRevised->getFeatures( QgsFeatureRequest().setFilterFids( addedRevisedIds ) );
463 current = 0;
464 while ( it.nextFeature( f ) )
465 {
466 if ( feedback->isCanceled() )
467 break;
468
469 // added feature
470 if ( !addedSink->addFeature( f, QgsFeatureSink::FastInsert ) )
471 throw QgsProcessingException( writeFeatureError( addedSink.get(), parameters, u"ADDED"_s ) );
472 else
473 feedback->featureAddedToSink( u"ADDED"_s );
474
475 current++;
476 feedback->setProgress( 0.10 * current * step + 90 ); // takes about 10% of time
477 }
478 }
479 feedback->setProgress( 100 );
480
481 feedback->pushInfo( QObject::tr( "%n feature(s) unchanged", nullptr, unchangedOriginalIds.size() ) );
482 feedback->pushInfo( QObject::tr( "%n feature(s) added", nullptr, addedRevisedIds.size() ) );
483 feedback->pushInfo( QObject::tr( "%n feature(s) deleted", nullptr, deleted ) );
484
485 if ( unchangedSink )
486 {
487 unchangedSink->finalize();
488 feedback->featureSinkFinalized( u"UNCHANGED"_s );
489 }
490 if ( addedSink )
491 {
492 addedSink->finalize();
493 feedback->featureSinkFinalized( u"ADDED"_s );
494 }
495 if ( deletedSink )
496 {
497 deletedSink->finalize();
498 feedback->featureSinkFinalized( u"DELETED"_s );
499 }
500
501 QVariantMap outputs;
502 outputs.insert( u"UNCHANGED"_s, unchangedDestId );
503 outputs.insert( u"ADDED"_s, addedDestId );
504 outputs.insert( u"DELETED"_s, deletedDestId );
505 outputs.insert( u"UNCHANGED_COUNT"_s, static_cast<long long>( unchangedOriginalIds.size() ) );
506 outputs.insert( u"ADDED_COUNT"_s, static_cast<long long>( addedRevisedIds.size() ) );
507 outputs.insert( u"DELETED_COUNT"_s, static_cast<long long>( deleted ) );
508
509 return outputs;
510}
511
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3749
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2360
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3982
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
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 & 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.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
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:64
QgsFeatureId id
Definition qgsfeature.h:63
QgsGeometry geometry
Definition qgsfeature.h:66
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
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(Qgis::WkbType t)
Returns empty geometry from wkb type.
A geometry is the spatial representation of a feature.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
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.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A numeric output for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
void setDefaultToAllFields(bool enabled)
Sets whether a parameter which allows multiple selections (see allowMultiple()) should automatically ...
A spatial index for QgsFeature objects.
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 Q_INVOKABLE QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features