1 /***************************************************************************
2                          qgsprocessingutils.h
3                          ------------------------
4     begin                : April 2017
5     copyright            : (C) 2017 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 
18 #ifndef QGSPROCESSINGUTILS_H
19 #define QGSPROCESSINGUTILS_H
20 
21 #include "qgis_core.h"
22 
23 #include "qgsrasterlayer.h"
24 #include "qgsmessagelog.h"
25 #include "qgsspatialindex.h"
26 #include "qgsprocessing.h"
27 #include "qgsfeaturesink.h"
28 #include "qgsfeaturesource.h"
29 #include "qgsproxyfeaturesink.h"
30 #include "qgsremappingproxyfeaturesink.h"
31 
32 class QgsMeshLayer;
33 class QgsProject;
34 class QgsProcessingContext;
35 class QgsMapLayerStore;
36 class QgsProcessingFeedback;
37 class QgsProcessingFeatureSource;
38 class QgsProcessingAlgorithm;
39 class QgsVectorTileLayer;
40 
41 #include <QString>
42 #include <QVariant>
43 
44 /**
45  * \class QgsProcessingUtils
46  * \ingroup core
47  * \brief Utility functions for use with processing classes.
48  * \since QGIS 3.0
49  */
50 class CORE_EXPORT QgsProcessingUtils
51 {
52   public:
53 
54     /**
55      * Returns a list of raster layers from a \a project which are compatible with the processing
56      * framework.
57      *
58      * If the \a sort argument is TRUE then the layers will be sorted by their QgsMapLayer::name()
59      * value.
60      * \see compatibleVectorLayers()
61      * \see compatibleMeshLayers()
62      * \see compatibleLayers()
63      */
64     static QList< QgsRasterLayer * > compatibleRasterLayers( QgsProject *project, bool sort = true );
65 
66     /**
67      * Returns a list of vector layers from a \a project which are compatible with the processing
68      * framework.
69      *
70      * The \a sourceTypes list should be filled with a list of QgsProcessing::SourceType values.
71      * If the \a sourceTypes list is non-empty then the layers will be sorted so that only
72      * layers with the specified source type included in the list will be returned. Leaving the \a sourceTypes
73      * list empty will cause all vector layers, regardless of their geometry type, to be returned.
74      *
75      * If the \a sort argument is TRUE then the layers will be sorted by their QgsMapLayer::name()
76      * value.
77      * \see compatibleRasterLayers()
78      * \see compatibleMeshLayers()
79      * \see compatibleLayers()
80      */
81     static QList< QgsVectorLayer * > compatibleVectorLayers( QgsProject *project,
82         const QList< int > &sourceTypes = QList< int >(),
83         bool sort = true );
84 
85     /**
86      * Returns a list of mesh layers from a \a project which are compatible with the processing
87      * framework.
88      *
89      * If the \a sort argument is TRUE then the layers will be sorted by their QgsMapLayer::name()
90      * value.
91      *
92      * \see compatibleRasterLayers()
93      * \see compatibleVectorLayers()
94      * \see compatibleLayers()
95      *
96      * \since QGIS 3.6
97      */
98     static QList<QgsMeshLayer *> compatibleMeshLayers( QgsProject *project, bool sort = true );
99 
100     /**
101      * Returns a list of map layers from a \a project which are compatible with the processing
102      * framework.
103      *
104      * If the \a sort argument is TRUE then the layers will be sorted by their QgsMapLayer::name()
105      * value.
106      * \see compatibleRasterLayers()
107      * \see compatibleVectorLayers()
108      */
109     static QList< QgsMapLayer * > compatibleLayers( QgsProject *project, bool sort = true );
110 
111     /**
112      * Encodes a provider key and layer \a uri to a single string, for use with
113      * decodeProviderKeyAndUri()
114      *
115      * \since QGIS 3.14
116      */
117     static QString encodeProviderKeyAndUri( const QString &providerKey, const QString &uri );
118 
119     /**
120      * Decodes a provider key and layer \a uri from an encoded string, for use with
121      * encodeProviderKeyAndUri()
122      *
123      * \param string encoded string, as returned by encodeProviderKeyAndUri()
124      * \param providerKey ID key for corresponding data provider
125      * \param uri decoded layer uri
126      * \returns TRUE if \a string was successfully decoded
127      *
128      * \since QGIS 3.14
129      */
130     static bool decodeProviderKeyAndUri( const QString &string, QString &providerKey SIP_OUT, QString &uri SIP_OUT );
131 
132     /**
133      * Layer type hints.
134      * \since QGIS 3.4
135      */
136     enum class LayerHint SIP_MONKEYPATCH_SCOPEENUM : int
137     {
138       UnknownType, //!< Unknown layer type
139       Vector, //!< Vector layer type
140       Raster, //!< Raster layer type
141       Mesh, //!< Mesh layer type, since QGIS 3.6
142     };
143 
144     /**
145      * Interprets a string as a map layer within the supplied \a context.
146      *
147      * The method will attempt to
148      * load a layer matching the passed \a string. E.g. if the string matches a layer ID or name
149      * within the context's project or temporary layer store then this layer will be returned.
150      * If the string is a file path and \a allowLoadingNewLayers is TRUE, then the layer at this
151      * file path will be loaded and added to the context's temporary layer store.
152      * Ownership of the layer remains with the \a context or the context's current project.
153      *
154      * The \a typeHint can be used to dictate the type of map layer expected.
155      */
156     static QgsMapLayer *mapLayerFromString( const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers = true, QgsProcessingUtils::LayerHint typeHint = QgsProcessingUtils::LayerHint::UnknownType );
157 
158     /**
159      * Converts a variant \a value to a new feature source.
160      *
161      * Sources will either be taken from \a context's active project, or loaded from external
162      * sources and stored temporarily in the \a context.
163      *
164      * The optional \a fallbackValue can be used to specify a "default" value which is used
165      * if \a value cannot be successfully converted to a source.
166      *
167      * This function creates a new object and the caller takes responsibility for deleting the returned object.
168      */
169     static QgsProcessingFeatureSource *variantToSource( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue = QVariant() ) SIP_FACTORY;
170 
171     /**
172      * Converts a variant \a value to a coordinate reference system.
173      *
174      * The optional \a fallbackValue can be used to specify a "default" value which is used
175      * if \a value cannot be successfully converted to a CRS.
176      *
177      * \since QGIS 3.12
178      */
179     static QgsCoordinateReferenceSystem variantToCrs( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue = QVariant() );
180 
181     /**
182      * Normalizes a layer \a source string for safe comparison across different
183      * operating system environments.
184      */
185     static QString normalizeLayerSource( const QString &source );
186 
187     /**
188      * Converts a variant to a Python literal.
189      *
190      * \see stringToPythonLiteral()
191      * \since QGSIS 3.6
192      */
193     static QString variantToPythonLiteral( const QVariant &value );
194 
195     /**
196      * Converts a string to a Python string literal. E.g. by replacing ' with \'.
197      *
198      * \see variantToPythonLiteral()
199      */
200     static QString stringToPythonLiteral( const QString &string );
201 
202     /**
203      * Creates a feature sink ready for adding features. The \a destination specifies a destination
204      * URI for the resultant layer. It may be updated in place to reflect the actual destination
205      * for the layer.
206      *
207      * Sink parameters such as desired \a encoding, \a fields, \a geometryType and \a crs must be specified.
208      *
209      * The \a createOptions map can be used to specify additional sink creation options, which
210      * are passed to the underlying provider when creating new layers. Known options also
211      * include 'fileEncoding', which is used to specify a file encoding to use for created
212      * files. If 'fileEncoding' is not specified, the default encoding from the \a context will be used.
213      *
214      * If a layer is created for the feature sink, the layer will automatically be added to the \a context's
215      * temporary layer store.
216      *
217      * The caller takes responsibility for deleting the returned sink.
218      */
219 #ifndef SIP_RUN
220     static QgsFeatureSink *createFeatureSink( QString &destination,
221         QgsProcessingContext &context,
222         const QgsFields &fields,
223         QgsWkbTypes::Type geometryType,
224         const QgsCoordinateReferenceSystem &crs,
225         const QVariantMap &createOptions = QVariantMap(),
226         const QStringList &datasourceOptions = QStringList(),
227         const QStringList &layerOptions = QStringList(),
228         QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(),
229         QgsRemappingSinkDefinition *remappingDefinition = nullptr ) SIP_FACTORY;
230 #endif
231 
232     /**
233      * Creates a feature sink ready for adding features. The \a destination specifies a destination
234      * URI for the resultant layer. It may be updated in place to reflect the actual destination
235      * for the layer.
236      *
237      * Sink parameters such as desired \a fields, \a geometryType and \a crs must be specified.
238      *
239      * The \a createOptions map can be used to specify additional sink creation options, which
240      * are passed to the underlying provider when creating new layers. Known options also
241      * include 'fileEncoding', which is used to specify a file encoding to use for created
242      * files. If 'fileEncoding' is not specified, the default encoding from the \a context will be used.
243      *
244      * If a layer is created for the feature sink, the layer will automatically be added to the \a context's
245      * temporary layer store.
246      *
247      * \note this version of the createFeatureSink() function has an API designed around use from the
248      * SIP bindings. c++ code should call the other createFeatureSink() version.
249      * \note available in Python bindings as createFeatureSink()
250      */
251     static void createFeatureSinkPython( QgsFeatureSink **sink SIP_OUT SIP_TRANSFERBACK, QString &destination SIP_INOUT, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions = QVariantMap() ) SIP_THROW( QgsProcessingException ) SIP_PYNAME( createFeatureSink );
252 
253 
254     /**
255      * Combines the extent of several map \a layers. If specified, the target \a crs
256      * will be used to transform the layer's extent to the desired output reference system
257      * using the specified \a context.
258      * \since QGIS 3.8
259      */
260     static QgsRectangle combineLayerExtents( const QList<QgsMapLayer *> &layers, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context );
261 
262     /**
263      * Combines the extent of several map \a layers. If specified, the target \a crs
264      * will be used to transform the layer's extent to the desired output reference system.
265      * \deprecated Use version with QgsProcessingContext argument instead
266      */
267     Q_DECL_DEPRECATED static QgsRectangle combineLayerExtents( const QList<QgsMapLayer *> &layers, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) SIP_DEPRECATED;
268 
269     /**
270      * Converts an \a input parameter value for use in source iterating mode, where one individual sink
271      * is created per input feature.
272      * The \a id parameter represents the unique ID for this output, which is embedded into the resulting
273      * parameter value.
274      */
275     static QVariant generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context );
276 
277     /**
278      * Returns a session specific processing temporary folder for use in processing algorithms.
279      * \see generateTempFilename()
280      */
281     static QString tempFolder();
282 
283     /**
284      * Returns a temporary filename for a given file, putting it into
285      * a temporary folder (creating that folder in the process),
286      * but not changing the \a basename.
287      * \see tempFolder()
288      */
289     static QString generateTempFilename( const QString &basename );
290 
291     /**
292      * Returns a HTML formatted version of the help text encoded in a variant \a map for
293      * a specified \a algorithm.
294      */
295     static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm );
296 
297     /**
298      * Converts a source vector \a layer to a file path of a vector layer of compatible format.
299      *
300      * If the specified \a layer is not of the format listed in the
301      * \a compatibleFormats argument, then the layer will first be exported to a compatible format
302      * in a temporary location using \a baseName. The function will then return the path to that temporary file.
303      *
304      * \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
305      *
306      * The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
307      * layer export is required. This defaults to shapefiles.
308      *
309      * The \a featureLimit argument can be used to specify a limit on the number of features read from the layer.
310      *
311      * When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
312      * to use convertToCompatibleFormatAndLayerName() which may avoid conversion in more situations.
313      *
314      * \see convertToCompatibleFormatAndLayerName()
315      */
316     static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
317         bool selectedFeaturesOnly,
318         const QString &baseName,
319         const QStringList &compatibleFormats,
320         const QString &preferredFormat,
321         QgsProcessingContext &context,
322         QgsProcessingFeedback *feedback, long long featureLimit = -1 );
323 
324     /**
325      * Converts a source vector \a layer to a file path and layer name of a vector layer of compatible format.
326      *
327      * If the specified \a layer is not of the format listed in the
328      * \a compatibleFormats argument, then the layer will first be exported to a compatible format
329      * in a temporary location using \a baseName. The function will then return the path to that temporary file.
330      *
331      * \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
332      *
333      * The \a featureLimit argument can be used to specify a limit on the number of features read from the layer.
334      *
335      * The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
336      * layer export is required. This defaults to shapefiles.
337      *
338      * This method should be preferred over convertToCompatibleFormat() when an algorithm is able
339      * to correctly handle files with multiple layers. Unlike convertToCompatibleFormat(), it will not force
340      * a conversion in this case and will return the target layer name in the \a layerName argument.
341      *
342      * \param layer source layer to convert (if required)
343      * \param selectedFeaturesOnly TRUE if only selected features from the layer should be used
344      * \param baseName base file name for converted layer, if required
345      * \param compatibleFormats a list of lowercase file extensions compatible with the algorithm
346      * \param preferredFormat preferred format extension to use if conversion if required
347      * \param context processing context
348      * \param feedback feedback object
349      * \param layerName will be set to the target layer name for multi-layer sources (e.g. Geopackage)
350      * \param featureLimit can be used to place a limit on the maximum number of features read from the layer
351      *
352      * \returns path to source layer, or nearly converted compatible layer
353      *
354      * \see convertToCompatibleFormat()
355      * \since QGIS 3.10
356      */
357     static QString convertToCompatibleFormatAndLayerName( const QgsVectorLayer *layer,
358         bool selectedFeaturesOnly,
359         const QString &baseName,
360         const QStringList &compatibleFormats,
361         const QString &preferredFormat,
362         QgsProcessingContext &context,
363         QgsProcessingFeedback *feedback,
364         QString &layerName SIP_OUT, long long featureLimit = -1 );
365 
366     /**
367      * Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
368      *
369      * Duplicate field names will be altered to "name_2", "name_3", etc, finding the first
370      * non-duplicate name.
371      *
372      * \note Some output file formats (e.g. shapefiles) have restrictions on the maximum
373      * length of field names, so be aware that the results of calling this method may
374      * be truncated when saving to these formats.
375      */
376     static QgsFields combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix = QString() );
377 
378     /**
379      * Returns a list of field indices parsed from the given list of field names. Unknown field names are ignored.
380      * If the list of field names is empty, it is assumed that all fields are required.
381      * \since QGIS 3.2
382      */
383     static QList<int> fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields );
384 
385     /**
386      * Returns a subset of fields based on the indices of desired fields.
387      * \since QGIS 3.2
388      */
389     static QgsFields indicesToFields( const QList<int> &indices, const QgsFields &fields );
390 
391     /**
392      * Returns the default vector extension to use, in the absence of all other constraints (e.g.
393      * provider based support for extensions).
394      *
395      * This method returns the user-set default extension from the processing settings, or
396      * a fallback value of "gpkg".
397      *
398      * \see defaultRasterExtension()
399      * \since QGIS 3.10
400      */
401     static QString defaultVectorExtension();
402 
403     /**
404      * Returns the default raster extension to use, in the absence of all other constraints (e.g.
405      * provider based support for extensions).
406      *
407      * This method returns the user-set default extension from the processing settings, or
408      * a fallback value of "tif".
409      *
410      * \see defaultVectorExtension()
411      * \since QGIS 3.10
412      */
413     static QString defaultRasterExtension();
414 
415   private:
416     static bool canUseLayer( const QgsRasterLayer *layer );
417     static bool canUseLayer( const QgsMeshLayer *layer );
418     static bool canUseLayer( const QgsVectorTileLayer *layer );
419     static bool canUseLayer( const QgsVectorLayer *layer,
420                              const QList< int > &sourceTypes = QList< int >() );
421 
422     /**
423      * Interprets a \a string as a map layer from a store.
424      *
425      * This method attempts to match a string to a store map layer, using
426      * first the layer ID, then layer names, and finally layer source.
427      * If the string matches a normalized version of any layer source
428      * for layers in the specified \a store, then those matching layers will be
429      * returned.
430      * \see mapLayerFromString()
431      */
432     static QgsMapLayer *mapLayerFromStore( const QString &string, QgsMapLayerStore *store, QgsProcessingUtils::LayerHint typeHint = QgsProcessingUtils::LayerHint::UnknownType );
433 
434     /**
435      * Interprets a string as a map layer. The method will attempt to
436      * load a layer matching the passed \a string using the given coordinate
437      * \a transformContext.
438      * E.g. if the string is a file path,
439      * then the layer at this file path will be loaded.
440      * The caller takes responsibility for deleting the returned map layer.
441      *
442      * \since QGIS 3.8
443      */
444     static QgsMapLayer *loadMapLayerFromString( const QString &string, const QgsCoordinateTransformContext &transformContext, LayerHint typeHint = LayerHint::UnknownType );
445 
446     /**
447      * Interprets a string as a map layer. The method will attempt to
448      * load a layer matching the passed \a string. E.g. if the string is a file path,
449      * then the layer at this file path will be loaded.
450      * The caller takes responsibility for deleting the returned map layer.
451      *
452      * \deprecated use mapLayerFromString() that takes QgsCoordinateTransformContext as an argument instead
453      */
454     Q_DECL_DEPRECATED static QgsMapLayer *loadMapLayerFromString( const QString &string, LayerHint typeHint = LayerHint::UnknownType ) SIP_DEPRECATED ;
455 
456     static void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter, QString &extension );
457 
458     friend class TestQgsProcessing;
459     friend class QgsProcessingProvider;
460 
461 };
462 
463 /**
464  * \class QgsProcessingFeatureSource
465  * \ingroup core
466  * \brief QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource, modifying
467  * results according to the settings in a QgsProcessingContext.
468  * \since QGIS 3.0
469  */
470 class CORE_EXPORT QgsProcessingFeatureSource : public QgsFeatureSource
471 {
472   public:
473 
474     //! Flags controlling how QgsProcessingFeatureSource fetches features
475     enum Flag
476     {
477       FlagSkipGeometryValidityChecks = 1 << 1, //!< Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always require invalid geometries, regardless of any user settings (e.g. "repair geometry" type algorithms).
478     };
479     Q_DECLARE_FLAGS( Flags, Flag )
480 
481     /**
482      * Constructor for QgsProcessingFeatureSource, accepting an original feature source \a originalSource
483      * and processing \a context.
484      * Ownership of \a originalSource is dictated by \a ownsOriginalSource. If \a ownsOriginalSource is FALSE,
485      * ownership is not transferred, and callers must ensure that \a originalSource exists for the lifetime of this object.
486      * If \a ownsOriginalSource is TRUE, then this object will take ownership of \a originalSource.
487      *
488      * If \a featureLimit is set to a value > 0, then a limit is placed on the maximum number of features which will be
489      * read from the source.
490      */
491     QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource = false,
492                                 long long featureLimit = -1 );
493 
494     ~QgsProcessingFeatureSource() override;
495 
496     /**
497      * Returns an iterator for the features in the source, respecting the supplied feature \a flags.
498      * An optional \a request can be used to optimise the returned
499      * iterator, eg by restricting the returned attributes or geometry.
500      */
501     QgsFeatureIterator getFeatures( const QgsFeatureRequest &request, Flags flags ) const;
502 
503     QgsFeatureSource::FeatureAvailability hasFeatures() const override;
504 
505     QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override;
506     QgsCoordinateReferenceSystem sourceCrs() const override;
507     QgsFields fields() const override;
508     QgsWkbTypes::Type wkbType() const override;
509     long featureCount() const override;
510     QString sourceName() const override;
511     QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const override;
512     QVariant minimumValue( int fieldIndex ) const override;
513     QVariant maximumValue( int fieldIndex ) const override;
514     QgsRectangle sourceExtent() const override;
515     QgsFeatureIds allFeatureIds() const override;
516     SpatialIndexPresence hasSpatialIndex() const override;
517 
518     /**
519      * Returns an expression context scope suitable for this source.
520      */
521     QgsExpressionContextScope *createExpressionContextScope() const SIP_FACTORY;
522 
523     /**
524      * Overrides the default geometry check method for the source.
525      *
526      * \since QGIS 3.14
527      */
528     void setInvalidGeometryCheck( QgsFeatureRequest::InvalidGeometryCheck method );
529 
530   private:
531 
532     QgsFeatureSource *mSource = nullptr;
533     bool mOwnsSource = false;
534     QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
535     std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
536     std::function< void( const QgsFeature & ) > mTransformErrorCallback;
537 
538     std::function< void( const QgsFeature & ) > mInvalidGeometryCallbackSkip;
539     std::function< void( const QgsFeature & ) > mInvalidGeometryCallbackAbort;
540 
541     long long mFeatureLimit = -1;
542 
543 };
544 
545 #ifndef SIP_RUN
546 
547 /**
548  * \class QgsProcessingFeatureSink
549  * \ingroup core
550  * \brief QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
551  * \note Not available in Python bindings.
552  * \since QGIS 3.0
553  */
554 class CORE_EXPORT QgsProcessingFeatureSink : public QgsProxyFeatureSink
555 {
556   public:
557 
558 
559     /**
560      * Constructor for QgsProcessingFeatureSink, accepting an original feature sink \a originalSink
561      * and processing \a context. Any added features are added to the \a originalSink, with feature
562      * writing errors being reports to \a context.
563      *
564      * The \a context must exist for the lifetime of this object.
565      *
566      * The \a sinkName is used to identify the destination sink when reporting errors.
567      *
568      * Ownership of \a originalSink is dictated by \a ownsOriginalSource. If \a ownsOriginalSink is FALSE,
569      * ownership is not transferred, and callers must ensure that \a originalSink exists for the lifetime of this object.
570      * If \a ownsOriginalSink is TRUE, then this object will take ownership of \a originalSink.
571      */
572     QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink = false );
573     ~QgsProcessingFeatureSink() override;
574     bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;
575     bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;
576     bool addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;
577 
578   private:
579 
580     QgsProcessingContext &mContext;
581     QString mSinkName;
582     bool mOwnsSink = false;
583 
584 };
585 #endif
586 
587 #endif // QGSPROCESSINGUTILS_H
588 
589 
590