QGIS API Documentation 4.3.0-Master (0cfde48c85b)
Loading...
Searching...
No Matches
qgsprocessingutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingutils.cpp
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#include "qgsprocessingutils.h"
19
20#include "qgsannotationlayer.h"
21#include "qgsapplication.h"
22#include "qgsexception.h"
24#include "qgsfileutils.h"
26#include "qgsmeshlayer.h"
27#include "qgspluginlayer.h"
28#include "qgspointcloudlayer.h"
32#include "qgsproject.h"
33#include "qgsproviderregistry.h"
34#include "qgsrasterfilewriter.h"
36#include "qgstiledscenelayer.h"
37#include "qgsvectorfilewriter.h"
38#include "qgsvectorlayer.h"
41#include "qgsvectortilelayer.h"
42
43#include <QImageWriter>
44#include <QRegularExpression>
45#include <QString>
46#include <QTextCodec>
47#include <QUuid>
48
49#include "moc_qgsprocessingutils.cpp"
50
51using namespace Qt::StringLiterals;
52
53QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
54{
55 return compatibleMapLayers< QgsRasterLayer >( project, sort );
56}
57
58QList<QgsVectorLayer *> QgsProcessingUtils::compatibleVectorLayers( QgsProject *project, const QList<int> &geometryTypes, bool sort )
59{
60 if ( !project )
61 return QList<QgsVectorLayer *>();
62
63 QList<QgsVectorLayer *> layers;
64 const auto vectorLayers = project->layers<QgsVectorLayer *>();
65 for ( QgsVectorLayer *l : vectorLayers )
66 {
67 if ( canUseLayer( l, geometryTypes ) )
68 layers << l;
69 }
70
71 if ( sort )
72 {
73 std::sort( layers.begin(), layers.end(), []( const QgsVectorLayer *a, const QgsVectorLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
74 }
75 return layers;
76}
77
78QList<QgsMeshLayer *> QgsProcessingUtils::compatibleMeshLayers( QgsProject *project, bool sort )
79{
80 return compatibleMapLayers< QgsMeshLayer >( project, sort );
81}
82
83QList<QgsPluginLayer *> QgsProcessingUtils::compatiblePluginLayers( QgsProject *project, bool sort )
84{
85 return compatibleMapLayers< QgsPluginLayer >( project, sort );
86}
87
88QList<QgsPointCloudLayer *> QgsProcessingUtils::compatiblePointCloudLayers( QgsProject *project, bool sort )
89{
90 return compatibleMapLayers< QgsPointCloudLayer >( project, sort );
91}
92
93QList<QgsAnnotationLayer *> QgsProcessingUtils::compatibleAnnotationLayers( QgsProject *project, bool sort )
94{
95 // we have to defer sorting until we've added the main annotation layer too
96 QList<QgsAnnotationLayer *> res = compatibleMapLayers< QgsAnnotationLayer >( project, false );
97 if ( project )
98 res.append( project->mainAnnotationLayer() );
99
100 if ( sort )
101 {
102 std::sort( res.begin(), res.end(), []( const QgsAnnotationLayer *a, const QgsAnnotationLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
103 }
104
105 return res;
106}
107
108QList<QgsVectorTileLayer *> QgsProcessingUtils::compatibleVectorTileLayers( QgsProject *project, bool sort )
109{
110 return compatibleMapLayers< QgsVectorTileLayer >( project, sort );
111}
112
113QList<QgsTiledSceneLayer *> QgsProcessingUtils::compatibleTiledSceneLayers( QgsProject *project, bool sort )
114{
115 return compatibleMapLayers< QgsTiledSceneLayer >( project, sort );
116}
117
118template<typename T> QList<T *> QgsProcessingUtils::compatibleMapLayers( QgsProject *project, bool sort )
119{
120 if ( !project )
121 return QList<T *>();
122
123 QList<T *> layers;
124 const auto projectLayers = project->layers<T *>();
125 for ( T *l : projectLayers )
126 {
127 if ( canUseLayer( l ) )
128 layers << l;
129 }
130
131 if ( sort )
132 {
133 std::sort( layers.begin(), layers.end(), []( const T *a, const T *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
134 }
135 return layers;
136}
137
138QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project, bool sort )
139{
140 if ( !project )
141 return QList<QgsMapLayer *>();
142
143 QList<QgsMapLayer *> layers;
144
145 const auto rasterLayers = compatibleMapLayers< QgsRasterLayer >( project, false );
146 for ( QgsRasterLayer *rl : rasterLayers )
147 layers << rl;
148
149 const auto vectorLayers = compatibleVectorLayers( project, QList< int >(), false );
150 for ( QgsVectorLayer *vl : vectorLayers )
151 layers << vl;
152
153 const auto meshLayers = compatibleMapLayers< QgsMeshLayer >( project, false );
154 for ( QgsMeshLayer *ml : meshLayers )
155 layers << ml;
156
157 const auto pointCloudLayers = compatibleMapLayers< QgsPointCloudLayer >( project, false );
158 for ( QgsPointCloudLayer *pcl : pointCloudLayers )
159 layers << pcl;
160
161 const auto annotationLayers = compatibleMapLayers< QgsAnnotationLayer >( project, false );
162 for ( QgsAnnotationLayer *al : annotationLayers )
163 layers << al;
164 layers << project->mainAnnotationLayer();
165
166 const auto vectorTileLayers = compatibleMapLayers< QgsVectorTileLayer >( project, false );
167 for ( QgsVectorTileLayer *vtl : vectorTileLayers )
168 layers << vtl;
169
170 const auto tiledSceneLayers = compatibleMapLayers< QgsTiledSceneLayer >( project, false );
171 for ( QgsTiledSceneLayer *tml : tiledSceneLayers )
172 layers << tml;
173
174 const auto pluginLayers = compatibleMapLayers< QgsPluginLayer >( project, false );
175 for ( QgsPluginLayer *pl : pluginLayers )
176 layers << pl;
177
178 if ( sort )
179 {
180 std::sort( layers.begin(), layers.end(), []( const QgsMapLayer *a, const QgsMapLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
181 }
182 return layers;
183}
184
185QString QgsProcessingUtils::encodeProviderKeyAndUri( const QString &providerKey, const QString &uri )
186{
187 return u"%1://%2"_s.arg( providerKey, uri );
188}
189
190bool QgsProcessingUtils::decodeProviderKeyAndUri( const QString &string, QString &providerKey, QString &uri )
191{
192 const thread_local QRegularExpression re( u"^(\\w+?):\\/\\/(.+)$"_s );
193 const QRegularExpressionMatch match = re.match( string );
194 if ( !match.hasMatch() )
195 return false;
196
197 providerKey = match.captured( 1 );
198 uri = match.captured( 2 );
199
200 // double check that provider is valid
201 return QgsProviderRegistry::instance()->providerMetadata( providerKey );
202}
203
204QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store, QgsProcessingUtils::LayerHint typeHint )
205{
206 if ( !store || string.isEmpty() )
207 return nullptr;
208
209 QList< QgsMapLayer * > layers = store->mapLayers().values();
210
211 layers.erase(
212 std::remove_if(
213 layers.begin(),
214 layers.end(),
215 []( QgsMapLayer *layer ) {
216 switch ( layer->type() )
217 {
218 case Qgis::LayerType::Vector:
219 return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
220 case Qgis::LayerType::Raster:
221 return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
222 case Qgis::LayerType::Plugin:
223 case Qgis::LayerType::Group:
224 return true;
225 case Qgis::LayerType::Mesh:
226 return !canUseLayer( qobject_cast< QgsMeshLayer * >( layer ) );
227 case Qgis::LayerType::VectorTile:
228 return !canUseLayer( qobject_cast< QgsVectorTileLayer * >( layer ) );
229 case Qgis::LayerType::TiledScene:
230 return !canUseLayer( qobject_cast< QgsTiledSceneLayer * >( layer ) );
231 case Qgis::LayerType::PointCloud:
232 return !canUseLayer( qobject_cast< QgsPointCloudLayer * >( layer ) );
233 case Qgis::LayerType::Annotation:
234 return !canUseLayer( qobject_cast< QgsAnnotationLayer * >( layer ) );
235 }
236 return true;
237 }
238 ),
239 layers.end()
240 );
241
242 auto isCompatibleType = [typeHint]( QgsMapLayer *l ) -> bool {
243 switch ( typeHint )
244 {
245 case LayerHint::UnknownType:
246 return true;
247
248 case LayerHint::Vector:
249 return l->type() == Qgis::LayerType::Vector;
250
251 case LayerHint::Raster:
252 return l->type() == Qgis::LayerType::Raster;
253
254 case LayerHint::Mesh:
255 return l->type() == Qgis::LayerType::Mesh;
256
257 case LayerHint::PointCloud:
258 return l->type() == Qgis::LayerType::PointCloud;
259
260 case LayerHint::Annotation:
261 return l->type() == Qgis::LayerType::Annotation;
262
263 case LayerHint::VectorTile:
264 return l->type() == Qgis::LayerType::VectorTile;
265
266 case LayerHint::TiledScene:
267 return l->type() == Qgis::LayerType::TiledScene;
268 }
269 return true;
270 };
271
272 for ( QgsMapLayer *l : std::as_const( layers ) )
273 {
274 if ( isCompatibleType( l ) && l->id() == string )
275 return l;
276 }
277 for ( QgsMapLayer *l : std::as_const( layers ) )
278 {
279 if ( isCompatibleType( l ) && l->name() == string )
280 return l;
281 }
282 for ( QgsMapLayer *l : std::as_const( layers ) )
283 {
284 if ( isCompatibleType( l ) && normalizeLayerSource( l->source() ) == normalizeLayerSource( string ) )
285 return l;
286 }
287 return nullptr;
288}
289
290QgsMapLayer *QgsProcessingUtils::loadMapLayerFromString( const QString &string, const QgsCoordinateTransformContext &transformContext, LayerHint typeHint, QgsProcessing::LayerOptionsFlags flags )
291{
292 QString provider;
293 QString uri;
294 const bool useProvider = decodeProviderKeyAndUri( string, provider, uri );
295 if ( !useProvider )
296 uri = string;
297
298 QString name;
299
300 const QgsProviderMetadata *providerMetadata = useProvider ? QgsProviderRegistry::instance()->providerMetadata( provider ) : nullptr;
301 if ( providerMetadata )
302 {
303 // use the uri parts to determine a suitable layer name
304 const QVariantMap parts = providerMetadata->decodeUri( uri );
305 const QString layerName = parts.value( u"layerName"_s ).toString();
306
307 if ( !layerName.isEmpty() )
308 {
309 name = layerName;
310 }
311 else if ( const QString path = parts.value( u"path"_s ).toString(); !path.isEmpty() )
312 {
313 name = QFileInfo( path ).baseName();
314 }
315 }
316 else
317 {
318 const QStringList components = uri.split( '|' );
319 if ( components.isEmpty() )
320 return nullptr;
321
322 if ( QFileInfo fi( components.at( 0 ) ); fi.isFile() )
323 name = fi.baseName();
324 else
325 name = QFileInfo( uri ).baseName();
326 }
327
328 if ( name.isEmpty() )
329 {
330 name = QgsDataSourceUri( uri ).table();
331 }
332 if ( name.isEmpty() )
333 {
334 name = uri;
335 }
336
337 QList< Qgis::LayerType > candidateTypes;
338 switch ( typeHint )
339 {
341 {
342 if ( providerMetadata )
343 {
344 // refine the type hint based on what the provider supports
345 candidateTypes = providerMetadata->supportedLayerTypes();
346 }
347 break;
348 }
350 candidateTypes.append( Qgis::LayerType::Vector );
351 break;
353 candidateTypes.append( Qgis::LayerType::Raster );
354 break;
355 case LayerHint::Mesh:
356 candidateTypes.append( Qgis::LayerType::Mesh );
357 break;
359 candidateTypes.append( Qgis::LayerType::PointCloud );
360 break;
362 candidateTypes.append( Qgis::LayerType::Annotation );
363 break;
365 candidateTypes.append( Qgis::LayerType::VectorTile );
366 break;
368 candidateTypes.append( Qgis::LayerType::TiledScene );
369 break;
370 }
371
372 // brute force attempt to load a matching layer
373 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Vector ) )
374 {
375 QgsVectorLayer::LayerOptions options { transformContext };
376 options.loadDefaultStyle = false;
377 options.skipCrsValidation = true;
378
379 std::unique_ptr< QgsVectorLayer > layer;
380 if ( providerMetadata )
381 {
382 layer = std::make_unique<QgsVectorLayer>( uri, name, providerMetadata->key(), options );
383 }
384 else
385 {
386 // fallback to ogr
387 layer = std::make_unique<QgsVectorLayer>( uri, name, u"ogr"_s, options );
388 }
389 if ( layer->isValid() )
390 {
391 return layer.release();
392 }
393 }
394 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Raster ) )
395 {
396 QgsRasterLayer::LayerOptions rasterOptions;
397 rasterOptions.loadDefaultStyle = false;
398 rasterOptions.skipCrsValidation = true;
399
400 std::unique_ptr< QgsRasterLayer > rasterLayer;
401 if ( providerMetadata )
402 {
403 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, providerMetadata->key(), rasterOptions );
404 }
405 else
406 {
407 // fallback to gdal
408 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, u"gdal"_s, rasterOptions );
409 }
410
411 if ( rasterLayer->isValid() )
412 {
413 return rasterLayer.release();
414 }
415 }
416 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Mesh ) )
417 {
418 QgsMeshLayer::LayerOptions meshOptions;
419 meshOptions.skipCrsValidation = true;
420
421 std::unique_ptr< QgsMeshLayer > meshLayer;
422 if ( providerMetadata )
423 {
424 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, providerMetadata->key(), meshOptions );
425 }
426 else
427 {
428 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, u"mdal"_s, meshOptions );
429 }
430 if ( meshLayer->isValid() )
431 {
432 return meshLayer.release();
433 }
434 }
435 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::PointCloud ) )
436 {
437 QgsPointCloudLayer::LayerOptions pointCloudOptions;
438 pointCloudOptions.skipCrsValidation = true;
439
441 {
442 pointCloudOptions.skipIndexGeneration = true;
443 }
444
445 std::unique_ptr< QgsPointCloudLayer > pointCloudLayer;
446 if ( providerMetadata )
447 {
448 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, providerMetadata->key(), pointCloudOptions );
449 }
450 else
451 {
452 const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( uri );
453 if ( !preferredProviders.empty() )
454 {
455 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, preferredProviders.at( 0 ).metadata()->key(), pointCloudOptions );
456 }
457 else
458 {
459 // pdal provider can read ascii files but it is not exposed by the provider to
460 // prevent automatic loading of tabular ascii files.
461 // Try to open the file with pdal provider.
462 QgsProviderMetadata *pdalProvider = QgsProviderRegistry::instance()->providerMetadata( u"pdal"_s );
463 if ( pdalProvider )
464 {
465 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, u"pdal"_s, pointCloudOptions );
466 }
467 }
468 }
469 if ( pointCloudLayer && pointCloudLayer->isValid() )
470 {
471 return pointCloudLayer.release();
472 }
473 }
474 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::VectorTile ) )
475 {
476 QgsDataSourceUri dsUri;
477 dsUri.setParam( "type", "mbtiles" );
478 dsUri.setParam( "url", uri );
479
480 std::unique_ptr< QgsVectorTileLayer > tileLayer;
481 tileLayer = std::make_unique< QgsVectorTileLayer >( dsUri.encodedUri(), name );
482
483 if ( tileLayer->isValid() )
484 {
485 return tileLayer.release();
486 }
487 }
488 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::TiledScene ) )
489 {
490 QgsTiledSceneLayer::LayerOptions tiledSceneOptions;
491 tiledSceneOptions.skipCrsValidation = true;
492
493 std::unique_ptr< QgsTiledSceneLayer > tiledSceneLayer;
494 if ( providerMetadata )
495 {
496 tiledSceneLayer = std::make_unique< QgsTiledSceneLayer >( uri, name, providerMetadata->key(), tiledSceneOptions );
497 }
498 else
499 {
500 const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( uri );
501 if ( !preferredProviders.empty() )
502 {
503 tiledSceneLayer = std::make_unique< QgsTiledSceneLayer >( uri, name, preferredProviders.at( 0 ).metadata()->key(), tiledSceneOptions );
504 }
505 }
506 if ( tiledSceneLayer && tiledSceneLayer->isValid() )
507 {
508 return tiledSceneLayer.release();
509 }
510 }
511 return nullptr;
512}
513
514QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers, LayerHint typeHint, QgsProcessing::LayerOptionsFlags flags )
515{
516 if ( string.isEmpty() )
517 return nullptr;
518
519 // prefer project layers
520 if ( context.project() && ( typeHint == LayerHint::Annotation ) && string.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
521 return context.project()->mainAnnotationLayer();
522
523 QgsMapLayer *layer = nullptr;
524 if ( auto *lProject = context.project() )
525 {
526 QgsMapLayer *layer = mapLayerFromStore( string, lProject->layerStore(), typeHint );
527 if ( layer )
528 return layer;
529 }
530
531 layer = mapLayerFromStore( string, context.temporaryLayerStore(), typeHint );
532 if ( layer )
533 return layer;
534
535 // check main annotation layer
536 if ( context.project() && ( typeHint == LayerHint::UnknownType ) && string.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
537 return context.project()->mainAnnotationLayer();
538
539
540 if ( !allowLoadingNewLayers )
541 return nullptr;
542
543 layer = loadMapLayerFromString( string, context.transformContext(), typeHint, flags );
544 if ( layer )
545 {
546 context.temporaryLayerStore()->addMapLayer( layer );
547 return layer;
548 }
549 else
550 {
551 return nullptr;
552 }
553}
554
555QgsProcessingFeatureSource *QgsProcessingUtils::variantToSource( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
556{
557 QVariant val = value;
558 bool selectedFeaturesOnly = false;
559 long long featureLimit = -1;
560 QString filterExpression;
561 bool overrideGeometryCheck = false;
563 if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
564 {
565 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
566 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
567 selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
568 featureLimit = fromVar.featureLimit;
569 filterExpression = fromVar.filterExpression;
570 val = fromVar.source;
572 geometryCheck = fromVar.geometryCheck;
573 }
574 else if ( val.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
575 {
576 // input is a QgsProcessingOutputLayerDefinition (e.g. an output from earlier in a model) - get extra properties from it
577 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
578 val = fromVar.sink;
579 }
580
581 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( val ) ) )
582 {
583 auto source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit, filterExpression );
584 if ( overrideGeometryCheck )
585 source->setInvalidGeometryCheck( geometryCheck );
586 return source.release();
587 }
588
589 QString layerRef;
590 if ( val.userType() == qMetaTypeId<QgsProperty>() )
591 {
592 layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
593 }
594 else if ( !val.isValid() || val.toString().isEmpty() )
595 {
596 // fall back to default
597 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( fallbackValue ) ) )
598 {
599 auto source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit, filterExpression );
600 if ( overrideGeometryCheck )
601 source->setInvalidGeometryCheck( geometryCheck );
602 return source.release();
603 }
604
605 layerRef = fallbackValue.toString();
606 }
607 else
608 {
609 layerRef = val.toString();
610 }
611
612 if ( layerRef.isEmpty() )
613 return nullptr;
614
615 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context, true, LayerHint::Vector ) );
616 if ( !vl )
617 return nullptr;
618
619 std::unique_ptr< QgsProcessingFeatureSource> source;
620 if ( selectedFeaturesOnly )
621 {
622 source = std::make_unique< QgsProcessingFeatureSource>( new QgsVectorLayerSelectedFeatureSource( vl ), context, true, featureLimit, filterExpression );
623 }
624 else
625 {
626 source = std::make_unique< QgsProcessingFeatureSource >( vl, context, false, featureLimit, filterExpression );
627 }
628
629 if ( overrideGeometryCheck )
630 source->setInvalidGeometryCheck( geometryCheck );
631 return source.release();
632}
633
634QgsCoordinateReferenceSystem QgsProcessingUtils::variantToCrs( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
635{
636 QVariant val = value;
637
638 if ( val.userType() == qMetaTypeId<QgsCoordinateReferenceSystem>() )
639 {
640 // input is a QgsCoordinateReferenceSystem - done!
641 return val.value< QgsCoordinateReferenceSystem >();
642 }
643 else if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
644 {
645 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
646 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
647 val = fromVar.source;
648 }
649 else if ( val.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
650 {
651 // input is a QgsProcessingOutputLayerDefinition - get extra properties from it
652 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
653 val = fromVar.sink;
654 }
655
656 if ( val.userType() == qMetaTypeId<QgsProperty>() && val.value< QgsProperty >().propertyType() == Qgis::PropertyType::Static )
657 {
658 val = val.value< QgsProperty >().staticValue();
659 }
660
661 // maybe a map layer
662 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( val ) ) )
663 return layer->crs();
664
665 if ( val.userType() == qMetaTypeId<QgsProperty>() )
666 val = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
667
668 if ( !val.isValid() )
669 {
670 // fall back to default
671 val = fallbackValue;
672 }
673
674 QString crsText = val.toString();
675 if ( crsText.isEmpty() )
676 crsText = fallbackValue.toString();
677
678 if ( crsText.isEmpty() )
680
681 // maybe special string
682 if ( context.project() && crsText.compare( "ProjectCrs"_L1, Qt::CaseInsensitive ) == 0 )
683 return context.project()->crs();
684
685 // else CRS from string
686 const QgsCoordinateReferenceSystem crs( crsText );
687 if ( crs.isValid() )
688 return crs;
689
690 // maybe a map layer reference
691 if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( crsText, context ) )
692 return layer->crs();
693
694 // no luck!
696}
697
698bool QgsProcessingUtils::canUseLayer( const QgsMeshLayer *layer )
699{
700 return layer && layer->dataProvider();
701}
702
703bool QgsProcessingUtils::canUseLayer( const QgsPluginLayer *layer )
704{
705 return layer && layer->isValid();
706}
707
708bool QgsProcessingUtils::canUseLayer( const QgsVectorTileLayer *layer )
709{
710 return layer && layer->isValid();
711}
712
713bool QgsProcessingUtils::canUseLayer( const QgsRasterLayer *layer )
714{
715 return layer && layer->isValid();
716}
717
718bool QgsProcessingUtils::canUseLayer( const QgsPointCloudLayer *layer )
719{
720 return layer && layer->isValid();
721}
722
723bool QgsProcessingUtils::canUseLayer( const QgsAnnotationLayer *layer )
724{
725 return layer && layer->isValid();
726}
727
728bool QgsProcessingUtils::canUseLayer( const QgsTiledSceneLayer *layer )
729{
730 return layer && layer->isValid();
731}
732
733bool QgsProcessingUtils::canUseLayer( const QgsVectorLayer *layer, const QList<int> &sourceTypes )
734{
735 return layer && layer->isValid() &&
736 ( sourceTypes.isEmpty()
737 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) && layer->geometryType() == Qgis::GeometryType::Point )
738 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) && layer->geometryType() == Qgis::GeometryType::Line )
739 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) && layer->geometryType() == Qgis::GeometryType::Polygon )
740 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && layer->isSpatial() )
741 || sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
742 );
743}
744
745QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
746{
747 QString normalized = source;
748 normalized.replace( '\\', '/' );
749 return normalized.trimmed();
750}
751
752QString QgsProcessingUtils::layerToStringIdentifier( const QgsMapLayer *layer, const QString &layerName )
753{
754 if ( !layer )
755 return QString();
756
757 const QString source = QgsProcessingUtils::normalizeLayerSource( layer->source() );
758 if ( !source.isEmpty() )
759 {
760 const QString provider = layer->providerType();
761 // don't prepend provider type for these exceptional providers -- we assume them
762 // by default if the provider type is excluded. See logic in QgsProcessingUtils::loadMapLayerFromString
763 if ( provider.compare( "gdal"_L1, Qt::CaseInsensitive ) == 0 || provider.compare( "ogr"_L1, Qt::CaseInsensitive ) == 0 || provider.compare( "mdal"_L1, Qt::CaseInsensitive ) == 0 )
764 return source;
765
766 return u"%1://%2"_s.arg( provider, source );
767 }
768
769 if ( layer->type() == Qgis::LayerType::Annotation && layerName.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
770 {
771 return layerName;
772 }
773
774 return layer->id();
775}
776
777QString QgsProcessingUtils::variantToPythonLiteral( const QVariant &value )
778{
779 if ( !value.isValid() )
780 return u"None"_s;
781
782 if ( value.userType() == qMetaTypeId<QgsProperty>() )
783 return u"QgsProperty.fromExpression('%1')"_s.arg( value.value< QgsProperty >().asExpression() );
784 else if ( value.userType() == qMetaTypeId<QgsCoordinateReferenceSystem>() )
785 {
786 if ( !value.value< QgsCoordinateReferenceSystem >().isValid() )
787 return u"QgsCoordinateReferenceSystem()"_s;
788 else
789 return u"QgsCoordinateReferenceSystem('%1')"_s.arg( value.value< QgsCoordinateReferenceSystem >().authid() );
790 }
791 else if ( value.userType() == qMetaTypeId<QgsRectangle>() )
792 {
793 QgsRectangle r = value.value<QgsRectangle>();
794 return u"'%1, %3, %2, %4'"_s.arg( qgsDoubleToString( r.xMinimum() ), qgsDoubleToString( r.yMinimum() ), qgsDoubleToString( r.xMaximum() ), qgsDoubleToString( r.yMaximum() ) );
795 }
796 else if ( value.userType() == qMetaTypeId<QgsReferencedRectangle>() )
797 {
799 return u"'%1, %3, %2, %4 [%5]'"_s.arg( qgsDoubleToString( r.xMinimum() ), qgsDoubleToString( r.yMinimum() ), qgsDoubleToString( r.xMaximum() ), qgsDoubleToString( r.yMaximum() ), r.crs().authid() );
800 }
801 else if ( value.userType() == qMetaTypeId<QgsPointXY>() )
802 {
803 QgsPointXY r = value.value<QgsPointXY>();
804 return u"'%1,%2'"_s.arg( qgsDoubleToString( r.x() ), qgsDoubleToString( r.y() ) );
805 }
806 else if ( value.userType() == qMetaTypeId<QgsReferencedPointXY>() )
807 {
809 return u"'%1,%2 [%3]'"_s.arg( qgsDoubleToString( r.x() ), qgsDoubleToString( r.y() ), r.crs().authid() );
810 }
811
812 switch ( value.userType() )
813 {
814 case QMetaType::Type::Bool:
815 return value.toBool() ? u"True"_s : u"False"_s;
816
817 case QMetaType::Type::Double:
818 return QString::number( value.toDouble() );
819
820 case QMetaType::Type::Int:
821 case QMetaType::Type::UInt:
822 return QString::number( value.toInt() );
823
824 case QMetaType::Type::LongLong:
825 case QMetaType::Type::ULongLong:
826 return QString::number( value.toLongLong() );
827
828 case QMetaType::Type::QVariantList:
829 {
830 QStringList parts;
831 const QVariantList vl = value.toList();
832 for ( const QVariant &v : vl )
833 {
834 parts << variantToPythonLiteral( v );
835 }
836 return parts.join( ',' ).prepend( '[' ).append( ']' );
837 }
838
839 case QMetaType::Type::QVariantMap:
840 {
841 const QVariantMap map = value.toMap();
842 QStringList parts;
843 parts.reserve( map.size() );
844 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
845 {
846 parts << u"%1: %2"_s.arg( stringToPythonLiteral( it.key() ), variantToPythonLiteral( it.value() ) );
847 }
848 return parts.join( ',' ).prepend( '{' ).append( '}' );
849 }
850
851 case QMetaType::Type::QDateTime:
852 {
853 const QDateTime dateTime = value.toDateTime();
854 return u"QDateTime(QDate(%1, %2, %3), QTime(%4, %5, %6))"_s.arg( dateTime.date().year() )
855 .arg( dateTime.date().month() )
856 .arg( dateTime.date().day() )
857 .arg( dateTime.time().hour() )
858 .arg( dateTime.time().minute() )
859 .arg( dateTime.time().second() );
860 }
861
862 default:
863 break;
864 }
865
866 return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
867}
868
869QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
870{
871 QString s = string;
872 s.replace( '\\', "\\\\"_L1 );
873 s.replace( '\n', "\\n"_L1 );
874 s.replace( '\r', "\\r"_L1 );
875 s.replace( '\t', "\\t"_L1 );
876
877 if ( s.contains( '\'' ) && !s.contains( '\"' ) )
878 {
879 s = s.prepend( '"' ).append( '"' );
880 }
881 else
882 {
883 s.replace( '\'', "\\\'"_L1 );
884 s = s.prepend( '\'' ).append( '\'' );
885 }
886 return s;
887}
888
889void QgsProcessingUtils::parseDestinationString(
890 QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter, QString &extension
891)
892{
893 extension.clear();
894 bool matched = decodeProviderKeyAndUri( destination, providerKey, uri );
895
896 if ( !matched )
897 {
898 const thread_local QRegularExpression splitRx( u"^(.{3,}?):(.*)$"_s );
899 QRegularExpressionMatch match = splitRx.match( destination );
900 if ( match.hasMatch() )
901 {
902 providerKey = match.captured( 1 );
903 uri = match.captured( 2 );
904 matched = true;
905 }
906 }
907
908 if ( matched )
909 {
910 if ( providerKey == "postgis"_L1 ) // older processing used "postgis" instead of "postgres"
911 {
912 providerKey = u"postgres"_s;
913 }
914 if ( providerKey == "ogr"_L1 )
915 {
916 QgsDataSourceUri dsUri( uri );
917 if ( !dsUri.database().isEmpty() )
918 {
919 if ( !dsUri.table().isEmpty() )
920 {
921 layerName = dsUri.table();
922 options.insert( u"layerName"_s, layerName );
923 }
924 uri = dsUri.database();
925 extension = QFileInfo( uri ).completeSuffix();
926 format = QgsVectorFileWriter::driverForExtension( extension );
927 options.insert( u"driverName"_s, format );
928 }
929 else
930 {
931 extension = QFileInfo( uri ).completeSuffix();
932 options.insert( u"driverName"_s, QgsVectorFileWriter::driverForExtension( extension ) );
933 }
934 options.insert( u"update"_s, true );
935 }
936 useWriter = false;
937 }
938 else
939 {
940 useWriter = true;
941 providerKey = u"ogr"_s;
942
943 const thread_local QRegularExpression splitRx( u"^(.*)\\.(.*?)$"_s );
944 QRegularExpressionMatch match = splitRx.match( destination );
945 if ( match.hasMatch() )
946 {
947 extension = match.captured( 2 );
948 format = QgsVectorFileWriter::driverForExtension( extension );
949 }
950
951 if ( format.isEmpty() )
952 {
953 format = u"GPKG"_s;
954 destination = destination + u".gpkg"_s;
955 }
956
957 options.insert( u"driverName"_s, format );
958 uri = destination;
959 }
960}
961
963 QString &destination,
964 QgsProcessingContext &context,
965 const QgsFields &fields,
966 Qgis::WkbType geometryType,
968 const QVariantMap &createOptions,
969 const QStringList &datasourceOptions,
970 const QStringList &layerOptions,
972 QgsRemappingSinkDefinition *remappingDefinition
973)
974{
975 QVariantMap options = createOptions;
976 if ( !options.contains( u"fileEncoding"_s ) )
977 {
978 // no destination encoding specified, use default
979 options.insert( u"fileEncoding"_s, context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding() );
980 }
981
982 if ( destination.isEmpty() || destination.startsWith( "memory:"_L1 ) )
983 {
984 // strip "memory:" from start of destination
985 if ( destination.startsWith( "memory:"_L1 ) )
986 destination = destination.mid( 7 );
987
988 if ( destination.isEmpty() )
989 destination = u"output"_s;
990
991 // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
992 std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs, false ) );
993 if ( !layer || !layer->isValid() )
994 {
995 throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
996 }
997
998 if ( QgsProcessingFeedback *feedback = context.feedback() )
999 {
1000 for ( const QgsField &field : fields )
1001 {
1002 // TODO -- support these!
1003 if ( !field.alias().isEmpty() )
1004 feedback->pushWarning( QObject::tr( "%1: Aliases are not compatible with scratch layers" ).arg( field.name() ) );
1005 if ( !field.alias().isEmpty() )
1006 feedback->pushWarning( QObject::tr( "%1: Comments are not compatible with scratch layers" ).arg( field.name() ) );
1007 }
1008 }
1009
1010 layer->setCustomProperty( u"OnConvertFormatRegeneratePrimaryKey"_s, static_cast< bool >( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey ) );
1011
1012 // update destination to layer ID
1013 destination = layer->id();
1014
1015 // this is a factory, so we need to return a proxy
1016 auto sink = std::make_unique<QgsProcessingFeatureSink>( layer->dataProvider(), destination, context );
1017 context.temporaryLayerStore()->addMapLayer( layer.release() );
1018
1019 return sink.release();
1020 }
1021 else
1022 {
1023 QString providerKey;
1024 QString uri;
1025 QString layerName;
1026 QString format;
1027 QString extension;
1028 bool useWriter = false;
1029 parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1030
1031 QgsFields newFields = fields;
1032 if ( useWriter && providerKey == "ogr"_L1 )
1033 {
1034 // use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
1035 // us to use any OGR format which supports feature addition
1036 QString finalFileName;
1037 QString finalLayerName;
1039 saveOptions.fileEncoding = options.value( u"fileEncoding"_s ).toString();
1040 saveOptions.layerName = !layerName.isEmpty() ? layerName : options.value( u"layerName"_s ).toString();
1041 saveOptions.driverName = format;
1042 saveOptions.datasourceOptions = !datasourceOptions.isEmpty() ? datasourceOptions : QgsVectorFileWriter::defaultDatasetOptions( format );
1043 saveOptions.layerOptions = !layerOptions.isEmpty() ? layerOptions : QgsVectorFileWriter::defaultLayerOptions( format );
1045 if ( remappingDefinition )
1046 {
1048 // sniff destination file to get correct wkb type and crs
1049 auto vl = std::make_unique< QgsVectorLayer >( destination );
1050 if ( vl->isValid() )
1051 {
1052 remappingDefinition->setDestinationWkbType( vl->wkbType() );
1053 remappingDefinition->setDestinationCrs( vl->crs() );
1054 newFields = vl->fields();
1055 remappingDefinition->setDestinationFields( newFields );
1056 }
1057 context.expressionContext().setFields( fields );
1058 }
1059 else
1060 {
1062 }
1063 std::unique_ptr< QgsVectorFileWriter > writer(
1064 QgsVectorFileWriter::create( destination, newFields, geometryType, crs, context.transformContext(), saveOptions, sinkFlags, &finalFileName, &finalLayerName )
1065 );
1066 if ( writer->hasError() )
1067 {
1068 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
1069 }
1070
1071 if ( QgsProcessingFeedback *feedback = context.feedback() )
1072 {
1073 for ( const QgsField &field : fields )
1074 {
1075 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
1076 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1077 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
1078 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1079 }
1080 }
1081
1082 destination = finalFileName;
1083 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
1084 destination += u"|layername=%1"_s.arg( finalLayerName );
1085
1086 if ( remappingDefinition )
1087 {
1088 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, writer.release(), true );
1089 remapSink->setExpressionContext( context.expressionContext() );
1090 remapSink->setTransformContext( context.transformContext() );
1091 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1092 }
1093 else
1094 return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
1095 }
1096 else
1097 {
1098 const QgsVectorLayer::LayerOptions layerOptions { context.transformContext() };
1099 if ( remappingDefinition )
1100 {
1101 //write to existing layer
1102
1103 // use destination string as layer name (eg "postgis:..." )
1104 if ( !layerName.isEmpty() )
1105 {
1106 QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( providerKey, uri );
1107 parts.insert( u"layerName"_s, layerName );
1108 uri = QgsProviderRegistry::instance()->encodeUri( providerKey, parts );
1109 }
1110
1111 auto layer = std::make_unique<QgsVectorLayer>( uri, destination, providerKey, layerOptions );
1112 // update destination to layer ID
1113 destination = layer->id();
1114 if ( layer->isValid() )
1115 {
1116 remappingDefinition->setDestinationWkbType( layer->wkbType() );
1117 remappingDefinition->setDestinationCrs( layer->crs() );
1118 remappingDefinition->setDestinationFields( layer->fields() );
1119 }
1120
1121 if ( QgsProcessingFeedback *feedback = context.feedback() )
1122 {
1125 for ( const QgsField &field : fields )
1126 {
1127 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1128 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1129 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1130 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1131 }
1132 }
1133
1134 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, layer->dataProvider(), false );
1135 context.temporaryLayerStore()->addMapLayer( layer.release() );
1136 remapSink->setExpressionContext( context.expressionContext() );
1137 remapSink->setTransformContext( context.transformContext() );
1138 context.expressionContext().setFields( fields );
1139 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1140 }
1141 else
1142 {
1143 //create empty layer
1144 auto exporter = std::make_unique<QgsVectorLayerExporter>( uri, providerKey, newFields, geometryType, crs, true, options, sinkFlags );
1145 if ( exporter->errorCode() != Qgis::VectorExportResult::Success )
1146 {
1147 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
1148 }
1149
1150 // use destination string as layer name (eg "postgis:..." )
1151 if ( !layerName.isEmpty() )
1152 {
1153 uri += u"|layername=%1"_s.arg( layerName );
1154 // update destination to generated URI
1155 destination = uri;
1156 }
1157
1158 if ( QgsProcessingFeedback *feedback = context.feedback() )
1159 {
1160 for ( const QgsField &field : fields )
1161 {
1162 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1163 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1164 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1165 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1166 }
1167 }
1168
1169 return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
1170 }
1171 }
1172 }
1173}
1174
1176 QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options
1177)
1178{
1179 *sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
1180}
1181
1182
1184{
1185 QgsRectangle extent;
1186 for ( const QgsMapLayer *layer : layers )
1187 {
1188 if ( !layer )
1189 continue;
1190
1191 if ( crs.isValid() )
1192 {
1193 //transform layer extent to target CRS
1194 QgsCoordinateTransform ct( layer->crs(), crs, context.transformContext() );
1196 try
1197 {
1198 QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
1199 extent.combineExtentWith( reprojExtent );
1200 }
1201 catch ( QgsCsException & )
1202 {
1203 // can't reproject... what to do here? hmmm?
1204 // let's ignore this layer for now, but maybe we should just use the original extent?
1205 }
1206 }
1207 else
1208 {
1209 extent.combineExtentWith( layer->extent() );
1210 }
1211 }
1212 return extent;
1213}
1214
1215// Deprecated
1217{
1218 QgsProcessingContext context;
1219 return QgsProcessingUtils::combineLayerExtents( layers, crs, context );
1220}
1221
1222QVariant QgsProcessingUtils::generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context )
1223{
1224 if ( !input.isValid() )
1225 return u"memory:%1"_s.arg( id.toString() );
1226
1227 if ( input.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1228 {
1229 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( input );
1230 QVariant newSink = generateIteratingDestination( fromVar.sink, id, context );
1231 fromVar.sink = QgsProperty::fromValue( newSink );
1232 return fromVar;
1233 }
1234 else if ( input.userType() == qMetaTypeId<QgsProperty>() )
1235 {
1236 QString res = input.value< QgsProperty>().valueAsString( context.expressionContext() );
1237 return generateIteratingDestination( res, id, context );
1238 }
1239 else
1240 {
1241 QString res = input.toString();
1243 {
1244 // temporary outputs map to temporary outputs!
1246 }
1247 else if ( res.startsWith( "memory:"_L1 ) )
1248 {
1249 return QString( res + '_' + id.toString() );
1250 }
1251 else
1252 {
1253 // assume a filename type output for now
1254 // TODO - uris?
1255 int lastIndex = res.lastIndexOf( '.' );
1256 return lastIndex >= 0 ? QString( res.left( lastIndex ) + '_' + id.toString() + res.mid( lastIndex ) ) : QString( res + '_' + id.toString() );
1257 }
1258 }
1259}
1260
1262{
1263 // we maintain a list of temporary folders -- this allows us to append additional
1264 // folders when a setting change causes the base temp folder to change, while deferring
1265 // cleanup of ALL these temp folders until session end (we can't cleanup older folders immediately,
1266 // because we don't know whether they have data in them which is still wanted)
1267 static std::vector< std::unique_ptr< QTemporaryDir > > sTempFolders;
1268 static QString sFolder;
1269 static QMutex sMutex;
1270 QMutexLocker locker( &sMutex );
1271 QString basePath;
1272
1273 if ( context )
1274 basePath = context->temporaryFolder();
1275 if ( basePath.isEmpty() )
1276 basePath = QgsProcessing::settingsTempPath->value();
1277
1278 if ( basePath.isEmpty() )
1279 {
1280 // default setting -- automatically create a temp folder
1281 if ( sTempFolders.empty() )
1282 {
1283 const QString templatePath = u"%1/processing_XXXXXX"_s.arg( QDir::tempPath() );
1284 auto tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1285 sFolder = tempFolder->path();
1286 sTempFolders.emplace_back( std::move( tempFolder ) );
1287 }
1288 }
1289 else if ( sFolder.isEmpty() || !sFolder.startsWith( basePath ) || sTempFolders.empty() )
1290 {
1291 if ( !QDir().exists( basePath ) )
1292 QDir().mkpath( basePath );
1293
1294 const QString templatePath = u"%1/processing_XXXXXX"_s.arg( basePath );
1295 auto tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1296 sFolder = tempFolder->path();
1297 sTempFolders.emplace_back( std::move( tempFolder ) );
1298 }
1299 return sFolder;
1300}
1301
1302QString QgsProcessingUtils::generateTempFilename( const QString &basename, const QgsProcessingContext *context )
1303{
1304 QString subPath = QUuid::createUuid().toString().remove( '-' ).remove( '{' ).remove( '}' );
1305 QString path = tempFolder( context ) + '/' + subPath;
1306 if ( !QDir( path ).exists() ) //make sure the directory exists - it shouldn't, but lets be safe...
1307 {
1308 QDir tmpDir;
1309 tmpDir.mkdir( path );
1310 }
1311 return path + '/' + QgsFileUtils::stringToSafeFilename( basename );
1312}
1313
1315{
1316 auto getText = [map]( const QString &key ) -> QString {
1317 if ( map.contains( key ) )
1318 return map.value( key ).toString();
1319 return QString();
1320 };
1321
1322 QString s;
1323 s += u"<html><body><p>"_s + getText( u"ALG_DESC"_s ) + u"</p>\n"_s;
1324
1325 QString inputs;
1326 const auto parameterDefinitions = algorithm->parameterDefinitions();
1327 for ( const QgsProcessingParameterDefinition *def : parameterDefinitions )
1328 {
1329 if ( def->flags() & Qgis::ProcessingParameterFlag::Hidden || def->isDestination() )
1330 continue;
1331
1332 if ( !getText( def->name() ).isEmpty() )
1333 {
1334 inputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1335 inputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1336 }
1337 }
1338 if ( !inputs.isEmpty() )
1339 s += u"<h2>"_s + QObject::tr( "Input parameters" ) + u"</h2>\n"_s + inputs;
1340
1341 QString outputs;
1342 const auto outputDefinitions = algorithm->outputDefinitions();
1343 for ( const QgsProcessingOutputDefinition *def : outputDefinitions )
1344 {
1345 if ( !getText( def->name() ).isEmpty() )
1346 {
1347 outputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1348 outputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1349 }
1350 }
1351 if ( !outputs.isEmpty() )
1352 s += u"<h2>"_s + QObject::tr( "Outputs" ) + u"</h2>\n"_s + outputs;
1353
1354 if ( !map.value( u"EXAMPLES"_s ).toString().isEmpty() )
1355 s += u"<h2>%1</h2>\n<p>%2</p>"_s.arg( QObject::tr( "Examples" ), getText( u"EXAMPLES"_s ) );
1356
1357 s += "<br>"_L1;
1358 if ( !map.value( u"ALG_CREATOR"_s ).toString().isEmpty() )
1359 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm author:" ) + u" "_s + getText( u"ALG_CREATOR"_s ) + u"</p>"_s;
1360 if ( !map.value( u"ALG_HELP_CREATOR"_s ).toString().isEmpty() )
1361 s += u"<p align=\"right\">"_s + QObject::tr( "Help author:" ) + u" "_s + getText( u"ALG_HELP_CREATOR"_s ) + u"</p>"_s;
1362 if ( !map.value( u"ALG_VERSION"_s ).toString().isEmpty() )
1363 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm version:" ) + u" "_s + getText( u"ALG_VERSION"_s ) + u"</p>"_s;
1364
1365 s += "</body></html>"_L1;
1366 return s;
1367}
1368
1370{
1371 int index = 0;
1372 for ( const QgsProcessingParameterDefinition *def : algorithm->parameterDefinitions() )
1373 {
1374 if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 )
1375 return index;
1376 index++;
1377 }
1378 return -1;
1379}
1380
1382{
1383 int index = 0;
1384 for ( const QgsProcessingOutputDefinition *def : algorithm->outputDefinitions() )
1385 {
1386 if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 )
1387 return index;
1388 index++;
1389 }
1390 return -1;
1391}
1392
1394 const QgsVectorLayer *vl,
1395 bool selectedFeaturesOnly,
1396 const QString &baseName,
1397 const QStringList &compatibleFormats,
1398 const QString &preferredFormat,
1399 QgsProcessingContext &context,
1400 QgsProcessingFeedback *feedback,
1401 QString *layerName,
1402 long long featureLimit,
1403 const QString &filterExpression,
1404 bool renameFid
1405)
1406{
1407 bool requiresTranslation = false;
1408
1409 // if we are only looking for selected features then we have to export back to disk,
1410 // as we need to subset only selected features, a concept which doesn't exist outside QGIS!
1411 requiresTranslation = requiresTranslation || selectedFeaturesOnly;
1412
1413 // if we are limiting the feature count, we better export
1414 requiresTranslation = requiresTranslation || featureLimit != -1 || !filterExpression.isEmpty();
1415
1416 // if the data provider is NOT ogr, then we HAVE to convert. Otherwise we run into
1417 // issues with data providers like spatialite, delimited text where the format can be
1418 // opened outside of QGIS, but with potentially very different behavior!
1419 requiresTranslation = requiresTranslation || vl->providerType() != "ogr"_L1;
1420
1421 // if the layer has a feature filter set, then we HAVE to convert. Feature filters are
1422 // a purely QGIS concept.
1423 requiresTranslation = requiresTranslation || !vl->subsetString().isEmpty();
1424
1425 // if the layer opened using GDAL's virtual I/O mechanism (/vsizip/, etc.), then
1426 // we HAVE to convert as other tools may not work with it
1427 requiresTranslation = requiresTranslation || vl->source().startsWith( "/vsi"_L1 );
1428
1429 // Check if layer is a disk based format and if so if the layer's path has a compatible filename suffix
1430 QString diskPath;
1431 if ( !requiresTranslation )
1432 {
1433 const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( vl->providerType(), vl->source() );
1434 if ( parts.contains( u"path"_s ) )
1435 {
1436 diskPath = parts.value( u"path"_s ).toString();
1437 QFileInfo fi( diskPath );
1438 requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
1439
1440 // if the layer name doesn't match the filename, we need to convert the layer. This method can only return
1441 // a filename, and cannot handle layernames as well as file paths
1442 const QString srcLayerName = parts.value( u"layerName"_s ).toString();
1443 if ( layerName )
1444 {
1445 // differing layer names are acceptable
1446 *layerName = srcLayerName;
1447 }
1448 else
1449 {
1450 // differing layer names are NOT acceptable
1451 requiresTranslation = requiresTranslation || ( !srcLayerName.isEmpty() && srcLayerName != fi.baseName() );
1452 }
1453 }
1454 else
1455 {
1456 requiresTranslation = true; // not a disk-based format
1457 }
1458 }
1459
1460 if ( requiresTranslation )
1461 {
1462 QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat, &context );
1463
1465 saveOptions.fileEncoding = context.defaultEncoding();
1466 saveOptions.driverName = QgsVectorFileWriter::driverForExtension( preferredFormat );
1467 QgsFields fields = vl->fields();
1468 if ( renameFid )
1469 {
1470 const int fidIndex = fields.lookupField( u"fid"_s );
1471 if ( fidIndex >= 0 )
1472 fields.rename( fidIndex, u"OLD_FID"_s );
1473 }
1474 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( temp, fields, vl->wkbType(), vl->crs(), context.transformContext(), saveOptions ) );
1475 QgsFeature f;
1477 QgsFeatureRequest request;
1478 if ( featureLimit != -1 )
1479 {
1480 request.setLimit( featureLimit );
1481 }
1482 if ( !filterExpression.isEmpty() )
1483 {
1484 request.setFilterExpression( filterExpression );
1485 }
1486
1487 if ( selectedFeaturesOnly )
1488 it = vl->getSelectedFeatures( std::move( request ) );
1489 else
1490 it = vl->getFeatures( request );
1491
1492 constexpr int maxErrors { 10 };
1493 unsigned long errorCounter { 0 };
1494 while ( it.nextFeature( f ) )
1495 {
1496 if ( feedback && feedback->isCanceled() )
1497 return QString();
1498
1499 if ( !writer->addFeature( f, QgsFeatureSink::FastInsert ) && feedback )
1500 {
1501 const QString errorMessage = writer->errorMessage();
1502 if ( !renameFid && saveOptions.driverName == "GPKG"_L1 && errorMessage.contains( "fid", Qt::CaseInsensitive ) )
1503 {
1504 // try again, dropping the FID field
1505 feedback->reportError( QObject::tr( "Cannot store existing FID values in temporary GeoPackage layer, these will be moved to \"OLD_FID\" instead." ), false );
1506 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, layerName, featureLimit, filterExpression, true );
1507 }
1508
1509 QString errorText;
1510 if ( errorCounter++ < maxErrors )
1511 {
1512 errorText = QObject::tr( "Error writing feature # %1 to output layer: %2" ).arg( QString::number( f.id() ), errorMessage );
1513
1514 feedback->reportError( errorText );
1515 }
1516 }
1517 }
1518 if ( errorCounter >= maxErrors )
1519 {
1520 feedback->reportError( QObject::tr( "There were %1 errors writing features, only the first %2 have been reported." ).arg( QString::number( errorCounter ), QString::number( maxErrors ) ) );
1521 }
1522 return temp;
1523 }
1524 else
1525 {
1526 return diskPath;
1527 }
1528}
1529
1531 const QgsVectorLayer *vl,
1532 bool selectedFeaturesOnly,
1533 const QString &baseName,
1534 const QStringList &compatibleFormats,
1535 const QString &preferredFormat,
1536 QgsProcessingContext &context,
1537 QgsProcessingFeedback *feedback,
1538 long long featureLimit,
1539 const QString &filterExpression
1540)
1541{
1542 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, nullptr, featureLimit, filterExpression, false );
1543}
1544
1546 const QgsVectorLayer *layer,
1547 bool selectedFeaturesOnly,
1548 const QString &baseName,
1549 const QStringList &compatibleFormats,
1550 const QString &preferredFormat,
1551 QgsProcessingContext &context,
1552 QgsProcessingFeedback *feedback,
1553 QString &layerName,
1554 long long featureLimit,
1555 const QString &filterExpression
1556)
1557{
1558 layerName.clear();
1559 return convertToCompatibleFormatInternal( layer, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, &layerName, featureLimit, filterExpression, false );
1560}
1561
1562QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix )
1563{
1564 QgsFields outFields = fieldsA;
1565 QSet< QString > usedNames;
1566 for ( const QgsField &f : fieldsA )
1567 {
1568 usedNames.insert( f.name().toLower() );
1569 }
1570
1571 for ( const QgsField &f : fieldsB )
1572 {
1573 QgsField newField = f;
1574 newField.setName( fieldsBPrefix + f.name() );
1575 if ( usedNames.contains( newField.name().toLower() ) )
1576 {
1577 int idx = 2;
1578 QString newName = newField.name() + '_' + QString::number( idx );
1579 while ( usedNames.contains( newName.toLower() ) || fieldsB.indexOf( newName ) != -1 )
1580 {
1581 idx++;
1582 newName = newField.name() + '_' + QString::number( idx );
1583 }
1584 newField.setName( newName );
1585 outFields.append( newField );
1586 }
1587 else
1588 {
1589 outFields.append( newField );
1590 }
1591 usedNames.insert( newField.name() );
1592 }
1593
1594 return outFields;
1595}
1596
1597QList<int> QgsProcessingUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
1598{
1599 QList<int> indices;
1600 if ( !fieldNames.isEmpty() )
1601 {
1602 indices.reserve( fieldNames.count() );
1603 for ( const QString &f : fieldNames )
1604 {
1605 int idx = fields.lookupField( f );
1606 if ( idx >= 0 )
1607 indices.append( idx );
1608 }
1609 }
1610 else
1611 {
1612 indices.reserve( fields.count() );
1613 for ( int i = 0; i < fields.count(); ++i )
1614 indices.append( i );
1615 }
1616 return indices;
1617}
1618
1619QgsFields QgsProcessingUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
1620{
1621 QgsFields fieldsSubset;
1622 for ( int i : indices )
1623 fieldsSubset.append( fields.at( i ) );
1624 return fieldsSubset;
1625}
1626
1628{
1629 QString setting = QgsProcessing::settingsDefaultOutputVectorLayerExt->value().trimmed();
1630 if ( setting.isEmpty() )
1631 return u"gpkg"_s;
1632
1633 if ( setting.startsWith( '.' ) )
1634 setting = setting.mid( 1 );
1635
1636 const QStringList supportedFormats = QgsVectorFileWriter::supportedFormatExtensions();
1637 if ( !supportedFormats.contains( setting, Qt::CaseInsensitive ) )
1638 return u"gpkg"_s;
1639
1640 return setting;
1641}
1642
1644{
1645 QString setting = QgsProcessing::settingsDefaultOutputRasterLayerFormat->value().trimmed();
1646 if ( setting.isEmpty() )
1647 return u"GTiff"_s;
1648
1649 const QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersFormats = QgsRasterFileWriter::supportedFiltersAndFormats();
1650 for ( const QgsRasterFileWriter::FilterFormatDetails &detail : std::as_const( supportedFiltersFormats ) )
1651 {
1652 if ( detail.driverName.compare( setting, Qt::CaseInsensitive ) == 0 )
1653 return detail.driverName;
1654 }
1655
1656 return u"GTiff"_s;
1657}
1658
1660{
1661 QString format = defaultRasterFormat();
1662 QStringList extensions = QgsRasterFileWriter::extensionsForFormat( format );
1663 if ( !extensions.isEmpty() )
1664 return extensions[0];
1665
1666 return u"tif"_s;
1667}
1668
1670{
1671 return u"las"_s;
1672}
1673
1675{
1676 return u"mbtiles"_s;
1677}
1678
1679QVariantMap QgsProcessingUtils::removePointerValuesFromMap( const QVariantMap &map )
1680{
1681 auto layerPointerToString = []( QgsMapLayer *layer ) -> QString {
1682 if ( layer && layer->providerType() == "memory"_L1 )
1683 return layer->id();
1684 else if ( layer )
1685 return layer->source();
1686 else
1687 return QString();
1688 };
1689
1690 auto cleanPointerValues = [&layerPointerToString]( const QVariant &value ) -> QVariant {
1691 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( value.value< QObject * >() ) )
1692 {
1693 // don't store pointers in maps for long-term storage
1694 return layerPointerToString( layer );
1695 }
1696 else if ( value.userType() == QMetaType::type( "QPointer< QgsMapLayer >" ) )
1697 {
1698 // don't store pointers in maps for long-term storage
1699 return layerPointerToString( value.value< QPointer< QgsMapLayer > >().data() );
1700 }
1701 else
1702 {
1703 return value;
1704 }
1705 };
1706
1707 QVariantMap res;
1708 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
1709 {
1710 if ( it->userType() == QMetaType::Type::QVariantMap )
1711 {
1712 res.insert( it.key(), removePointerValuesFromMap( it.value().toMap() ) );
1713 }
1714 else if ( it->userType() == QMetaType::Type::QVariantList )
1715 {
1716 QVariantList dest;
1717 const QVariantList source = it.value().toList();
1718 dest.reserve( source.size() );
1719 for ( const QVariant &v : source )
1720 {
1721 dest.append( cleanPointerValues( v ) );
1722 }
1723 res.insert( it.key(), dest );
1724 }
1725 else
1726 {
1727 res.insert( it.key(), cleanPointerValues( it.value() ) );
1728 }
1729 }
1730 return res;
1731}
1732
1733QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantMap &parameters, bool &ok, QString &error )
1734{
1735 QVariantMap output;
1736 ok = true;
1737 for ( auto it = parameters.constBegin(); it != parameters.constEnd(); ++it )
1738 {
1739 if ( it.value().userType() == QMetaType::Type::QVariantMap )
1740 {
1741 const QVariantMap value = it.value().toMap();
1742 if ( value.value( u"type"_s ).toString() == "data_defined"_L1 )
1743 {
1744 const QString expression = value.value( u"expression"_s ).toString();
1745 const QString field = value.value( u"field"_s ).toString();
1746 if ( !expression.isEmpty() )
1747 {
1748 output.insert( it.key(), QgsProperty::fromExpression( expression ) );
1749 }
1750 else if ( !field.isEmpty() )
1751 {
1752 output.insert( it.key(), QgsProperty::fromField( field ) );
1753 }
1754 else
1755 {
1756 ok = false;
1757 error = QObject::tr( "Invalid data defined parameter for %1, requires 'expression' or 'field' values." ).arg( it.key() );
1758 }
1759 }
1760 else
1761 {
1762 output.insert( it.key(), it.value() );
1763 }
1764 }
1765 else if ( it.value().userType() == QMetaType::Type::QString )
1766 {
1767 const QString stringValue = it.value().toString();
1768
1769 if ( stringValue.startsWith( "field:"_L1 ) )
1770 {
1771 output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) );
1772 }
1773 else if ( stringValue.startsWith( "expression:"_L1 ) )
1774 {
1775 output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) );
1776 }
1777 else
1778 {
1779 output.insert( it.key(), it.value() );
1780 }
1781 }
1782 else
1783 {
1784 output.insert( it.key(), it.value() );
1785 }
1786 }
1787 return output;
1788}
1789
1790QString QgsProcessingUtils::resolveDefaultEncoding( const QString &defaultEncoding )
1791{
1792 if ( !QTextCodec::availableCodecs().contains( defaultEncoding.toLatin1() ) )
1793 {
1794 const QString systemCodec = QTextCodec::codecForLocale()->name();
1795 if ( !systemCodec.isEmpty() )
1796 {
1797 return systemCodec;
1798 }
1799 return QString( "UTF-8" );
1800 }
1801
1802 return defaultEncoding;
1803}
1804
1806{
1807 const QList<QByteArray> supportedFormats = QImageWriter::supportedImageFormats();
1808 QStringList formats;
1809 formats.reserve( supportedFormats.size() );
1810
1811 for ( const QByteArray &format : supportedFormats )
1812 {
1813 if ( format == "svg" )
1814 {
1815 continue;
1816 }
1817 formats.append( QString::fromUtf8( format ).toUpper() );
1818 }
1819
1820 std::sort( formats.begin(), formats.end(), []( const QString &a, const QString &b ) -> bool {
1821 if ( a == "PNG"_L1 )
1822 {
1823 return true;
1824 }
1825 if ( b == "PNG"_L1 )
1826 {
1827 return false;
1828 }
1829 return a.localeAwareCompare( b ) < 0;
1830 } );
1831
1832 return formats;
1833}
1834
1836{
1837 const QStringList formats = supportedImageFormats();
1838 QStringList fileFilters;
1839 fileFilters.reserve( formats.size() );
1840
1841 for ( const QString &format : formats )
1842 {
1843 const QString longName = format + QObject::tr( " format" );
1844 const QString glob = u"*."_s + format;
1845
1846 fileFilters.append( u"%1 (%2 %3)"_s.arg( longName, glob.toLower(), glob ) );
1847 }
1848
1849 return fileFilters.join( ";;"_L1 );
1850}
1851
1853{
1854 const QDir settingsDir = QDir( QgsApplication::qgisSettingsDirPath() );
1855 const QDir userDir = QDir( settingsDir.filePath( u"processing"_s ) );
1856 if ( !userDir.exists() )
1857 {
1858 userDir.mkpath( u"."_s );
1859 }
1860
1861 return userDir.absolutePath();
1862}
1863
1865{
1866 return QDir( QDir::homePath() ).absoluteFilePath( u"processing"_s );
1867}
1868
1870{
1871 QDir modelFolder = QDir( userFolder() ).filePath( u"models"_s );
1872 if ( !modelFolder.exists() )
1873 {
1874 modelFolder.mkpath( u"."_s );
1875 }
1876 return modelFolder.absolutePath();
1877}
1878
1880{
1881 // NOTE -- deliberately NOT a settings entry, we need to maintain compatibility with processing GUI
1882 // class for configuring settings for now
1883 const QString settingValue = QgsSettings().value( u"Processing/Configuration/MODELS_FOLDER"_s ).toString();
1884 if ( !settingValue.trimmed().isEmpty() )
1885 {
1886 return settingValue.split( ';' );
1887 }
1888 else
1889 {
1890 return { defaultModelFolder() };
1891 }
1892}
1893
1894//
1895// QgsProcessingFeatureSource
1896//
1897
1898QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource, long long featureLimit, const QString &filterExpression )
1899 : mSource( originalSource )
1900 , mOwnsSource( ownsOriginalSource )
1901 , mSourceCrs( mSource->sourceCrs() )
1902 , mSourceFields( mSource->fields() )
1903 , mSourceWkbType( mSource->wkbType() )
1904 , mSourceName( mSource->sourceName() )
1905 , mSourceExtent( mSource->sourceExtent() )
1906 , mSourceSpatialIndexPresence( mSource->hasSpatialIndex() )
1907 , mInvalidGeometryCheck(
1908 QgsWkbTypes::geometryType( mSource->wkbType() ) == Qgis::GeometryType::Point ? Qgis::InvalidGeometryCheck::NoCheck // never run geometry validity checks for point layers!
1909 : context.invalidGeometryCheck()
1910 )
1911 , mInvalidGeometryCallback( context.invalidGeometryCallback( originalSource ) )
1912 , mTransformErrorCallback( context.transformErrorCallback() )
1913 , mInvalidGeometryCallbackSkip( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::SkipInvalid, originalSource ) )
1914 , mInvalidGeometryCallbackAbort( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::AbortOnInvalid, originalSource ) )
1915 , mFeatureLimit( featureLimit )
1916 , mFilterExpression( filterExpression )
1917{}
1918
1920{
1921 if ( mOwnsSource )
1922 delete mSource;
1923}
1924
1926{
1927 QgsFeatureRequest req( request );
1928 req.setTransformErrorCallback( mTransformErrorCallback );
1929
1932 else
1933 {
1934 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1935 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1936 }
1937
1938 if ( mFeatureLimit != -1 && req.limit() != -1 )
1939 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1940 else if ( mFeatureLimit != -1 )
1941 req.setLimit( mFeatureLimit );
1942
1943 if ( !mFilterExpression.isEmpty() )
1944 req.combineFilterExpression( mFilterExpression );
1945
1946 return mSource->getFeatures( req );
1947}
1948
1950{
1951 Qgis::FeatureAvailability sourceAvailability = mSource->hasFeatures();
1952 if ( sourceAvailability == Qgis::FeatureAvailability::NoFeaturesAvailable )
1953 return Qgis::FeatureAvailability::NoFeaturesAvailable; // never going to be features if underlying source has no features
1954 else if ( mInvalidGeometryCheck == Qgis::InvalidGeometryCheck::NoCheck && mFilterExpression.isEmpty() )
1955 return sourceAvailability;
1956 else
1957 // we don't know... source has features, but these may be filtered out by invalid geometry check or filter expression
1959}
1960
1962{
1963 QgsFeatureRequest req( request );
1964 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1965 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1966 req.setTransformErrorCallback( mTransformErrorCallback );
1967
1968 if ( mFeatureLimit != -1 && req.limit() != -1 )
1969 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1970 else if ( mFeatureLimit != -1 )
1971 req.setLimit( mFeatureLimit );
1972
1973 if ( !mFilterExpression.isEmpty() )
1974 req.combineFilterExpression( mFilterExpression );
1975
1976 return mSource->getFeatures( req );
1977}
1978
1983
1985{
1986 return mSourceFields;
1987}
1988
1990{
1991 return mSourceWkbType;
1992}
1993
1995{
1996 if ( !mFilterExpression.isEmpty() )
1997 return static_cast< int >( Qgis::FeatureCountState::UnknownCount );
1998
1999 if ( mFeatureLimit == -1 )
2000 return mSource->featureCount();
2001 else
2002 return std::min( mFeatureLimit, mSource->featureCount() );
2003}
2004
2006{
2007 return mSourceName;
2008}
2009
2010QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
2011{
2012 if ( mFilterExpression.isEmpty() )
2013 return mSource->uniqueValues( fieldIndex, limit );
2014
2015 // inefficient method when filter expression in use
2016 // TODO QGIS 5.0 -- add filter expression to virtual ::uniqueValues function
2017 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
2018 return QSet<QVariant>();
2019
2022 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
2023 req.setFilterExpression( mFilterExpression );
2024
2025 QSet<QVariant> values;
2026 QgsFeatureIterator it = getFeatures( req );
2027 QgsFeature f;
2028 while ( it.nextFeature( f ) )
2029 {
2030 values.insert( f.attribute( fieldIndex ) );
2031 if ( limit > 0 && values.size() >= limit )
2032 return values;
2033 }
2034 return values;
2035}
2036
2037QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
2038{
2039 if ( mFilterExpression.isEmpty() )
2040 return mSource->minimumValue( fieldIndex );
2041
2042 // inefficient method when filter expression in use
2043 // TODO QGIS 5.0 -- add filter expression to virtual ::minimumValue function
2044 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
2045 return QVariant();
2046
2049 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
2050
2051 QVariant min;
2052 QgsFeatureIterator it = getFeatures( req );
2053 QgsFeature f;
2054 while ( it.nextFeature( f ) )
2055 {
2056 const QVariant v = f.attribute( fieldIndex );
2057 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantLessThan( v, min ) || QgsVariantUtils::isNull( min ) ) )
2058 {
2059 min = v;
2060 }
2061 }
2062 return min;
2063}
2064
2065QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
2066{
2067 if ( mFilterExpression.isEmpty() )
2068 return mSource->maximumValue( fieldIndex );
2069
2070 // inefficient method when filter expression in use
2071 // TODO QGIS 5.0 -- add filter expression to virtual ::maximumValue function
2072 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
2073 return QVariant();
2074
2077 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
2078
2079 QVariant max;
2080 QgsFeatureIterator it = getFeatures( req );
2081 QgsFeature f;
2082 while ( it.nextFeature( f ) )
2083 {
2084 const QVariant v = f.attribute( fieldIndex );
2085 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantGreaterThan( v, max ) || QgsVariantUtils::isNull( max ) ) )
2086 {
2087 max = v;
2088 }
2089 }
2090 return max;
2091}
2092
2094{
2095 return mSourceExtent;
2096}
2097
2099{
2100 if ( mFilterExpression.isEmpty() )
2101 return mSource->allFeatureIds();
2102
2103 QgsFeatureIterator fit = getFeatures( QgsFeatureRequest().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setNoAttributes().setFilterExpression( mFilterExpression ) );
2104
2105 QgsFeatureIds ids;
2106
2107 QgsFeature fet;
2108 while ( fit.nextFeature( fet ) )
2109 {
2110 ids << fet.id();
2111 }
2112
2113 return ids;
2114}
2115
2117{
2118 return mSourceSpatialIndexPresence;
2119}
2120
2122{
2123 QgsExpressionContextScope *expressionContextScope = nullptr;
2124 QgsExpressionContextScopeGenerator *generator = dynamic_cast<QgsExpressionContextScopeGenerator *>( mSource );
2125 if ( generator )
2126 {
2127 expressionContextScope = generator->createExpressionContextScope();
2128 }
2129 return expressionContextScope;
2130}
2131
2133{
2134 mInvalidGeometryCheck = method;
2135 switch ( mInvalidGeometryCheck )
2136 {
2138 mInvalidGeometryCallback = nullptr;
2139 break;
2140
2142 mInvalidGeometryCallback = mInvalidGeometryCallbackSkip;
2143 break;
2144
2146 mInvalidGeometryCallback = mInvalidGeometryCallbackAbort;
2147 break;
2148 }
2149}
2150
2152{
2153 return mInvalidGeometryCheck;
2154}
2155
2156
2157//
2158// QgsProcessingFeatureSink
2159//
2160QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
2161 : QgsProxyFeatureSink( originalSink )
2162 , mContext( context )
2163 , mSinkName( sinkName )
2164 , mOwnsSink( ownsOriginalSink )
2165{}
2166
2168{
2169 if ( !flushBuffer() && mContext.feedback() )
2170 {
2171 mContext.feedback()->reportError( lastError() );
2172 }
2173
2174 if ( mOwnsSink )
2175 {
2176 delete mSink;
2177 mSink = nullptr;
2178 }
2179}
2180
2182{
2183 if ( !flushBuffer() )
2184 {
2186 }
2187}
2188
2190{
2191 bool result = QgsProxyFeatureSink::addFeature( feature, flags );
2192 if ( !result && mContext.feedback() )
2193 {
2194 const QString error = lastError();
2195 if ( !error.isEmpty() )
2196 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1: %2" ).arg( mSinkName, error ) );
2197 else
2198 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
2199 }
2200 return result;
2201}
2202
2204{
2205 bool result = QgsProxyFeatureSink::addFeatures( features, flags );
2206 if ( !result && mContext.feedback() )
2207 {
2208 const QString error = lastError();
2209 if ( !error.isEmpty() )
2210 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1: %2", nullptr, features.count() ).arg( mSinkName, error ) );
2211 else
2212 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1", nullptr, features.count() ).arg( mSinkName ) );
2213 }
2214 return result;
2215}
2216
2218{
2219 bool result = QgsProxyFeatureSink::addFeatures( iterator, flags );
2220 if ( !result && mContext.feedback() )
2221 {
2222 const QString error = lastError();
2223 if ( !error.isEmpty() )
2224 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1: %2" ).arg( mSinkName, error ) );
2225 else
2226 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1" ).arg( mSinkName ) );
2227 }
2228 return result;
2229}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3777
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3771
@ VectorPoint
Vector point layers.
Definition qgis.h:3772
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3774
@ VectorLine
Vector line layers.
Definition qgis.h:3773
@ FieldComments
Writer can support field comments.
Definition qgis.h:1176
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1175
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:619
@ Success
No errors were encountered.
Definition qgis.h:1154
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
@ Static
Static property.
Definition qgis.h:745
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
FeatureAvailability
Possible return value for QgsFeatureSource::hasFeatures() to determine if a source is empty.
Definition qgis.h:638
@ FeaturesMaybeAvailable
There may be features available in this source.
Definition qgis.h:641
@ NoFeaturesAvailable
There are certainly no features available in this source.
Definition qgis.h:639
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
@ EditComment
Allows editing comments.
Definition qgis.h:653
QFlags< VectorDataProviderAttributeEditCapability > VectorDataProviderAttributeEditCapabilities
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:663
InvalidGeometryCheck
Methods for handling of features with invalid geometries.
Definition qgis.h:2423
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2424
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2426
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2425
@ OverrideDefaultGeometryCheck
If set, the default geometry check method (as dictated by QgsProcessingContext) will be overridden fo...
Definition qgis.h:3948
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3974
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:314
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:4027
@ NoSymbology
Export only data.
Definition qgis.h:6378
QFlags< ProcessingFeatureSourceFlag > ProcessingFeatureSourceFlags
Flags which control how QgsProcessingFeatureSource fetches features.
Definition qgis.h:3985
Represents a map layer containing a set of georeferenced annotations, e.g.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
QString table() const
Returns the table name stored in the URI.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
QString database() const
Returns the database name stored in the URI.
Abstract interface for generating an expression context scope.
virtual QgsExpressionContextScope * createExpressionContextScope() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
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 & setLimit(long long limit)
Set the maximum number of features to request.
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & combineFilterExpression(const QString &expression)
Modifies the existing filter expression to add an additional expression filter.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setInvalidGeometryCheck(Qgis::InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setInvalidGeometryCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering an invalid geometry and invalidGeometryCheck() is s...
QgsFeatureRequest & setTransformErrorCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering a transform error when iterating features and a des...
An interface for objects which accept features via addFeature(s) methods.
QFlags< SinkFlag > SinkFlags
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
QFlags< Flag > Flags
An interface for objects which provide features via a getFeatures method.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFeatureId id
Definition qgsfeature.h:63
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:224
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
int count
Definition qgsfields.h:49
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all layers by layer ID.
QgsMapLayer * addMapLayer(QgsMapLayer *layer, bool takeOwnership=true)
Add a layer to the store.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QString id
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:93
virtual void setTransformContext(const QgsCoordinateTransformContext &transformContext)=0
Sets the coordinate transform context to transformContext.
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Base class for plugin layers.
Represents a map layer supporting display of point clouds.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
QgsProcessingFeedback * feedback()
Returns the associated feedback object.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution.
QString temporaryFolder() const
Returns the (optional) temporary folder to use when running algorithms.
Custom exception class for processing related exceptions.
QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
void finalize() override
Finalizes the sink, flushing any buffered features to the destination.
QgsProcessingFeatureSink(QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink=false)
Constructor for QgsProcessingFeatureSink, accepting an original feature sink originalSink and process...
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
Encapsulates settings relating to a feature source input to a processing algorithm.
bool selectedFeaturesOnly
true if only selected features in the source should be used by algorithms.
Qgis::InvalidGeometryCheck geometryCheck
Geometry check method to apply to this source.
Qgis::ProcessingFeatureSourceDefinitionFlags flags
Flags which dictate source behavior.
long long featureLimit
If set to a value > 0, places a limit on the maximum number of features which will be read from the s...
QString filterExpression
Optional expression filter to use for filtering features which will be read from the source.
QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource,...
QgsRectangle sourceExtent() const override
Returns the extent of all geometries from the source.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Returns the set of unique values contained within the specified fieldIndex from this source.
QgsExpressionContextScope * createExpressionContextScope() const
Returns an expression context scope suitable for this source.
QgsProcessingFeatureSource(QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource=false, long long featureLimit=-1, const QString &filterExpression=QString())
Constructor for QgsProcessingFeatureSource, accepting an original feature source originalSource and p...
void setInvalidGeometryCheck(Qgis::InvalidGeometryCheck method)
Overrides the default geometry check method for the source.
Qgis::InvalidGeometryCheck invalidGeometryCheck() const
Returns the geometry check method for the source.
QVariant maximumValue(int fieldIndex) const override
Returns the maximum value for an attribute column or an invalid variant in case of error.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
Qgis::WkbType wkbType() const override
Returns the geometry type for features returned by this source.
QVariant minimumValue(int fieldIndex) const override
Returns the minimum value for an attribute column or an invalid variant in case of error.
long long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request, Qgis::ProcessingFeatureSourceFlags flags) const
Returns an iterator for the features in the source, respecting the supplied feature flags.
Qgis::FeatureAvailability hasFeatures() const override
Determines if there are any features available in the source.
QString sourceName() const override
Returns a friendly display name for the source.
QgsFeatureIds allFeatureIds() const override
Returns a list of all feature IDs for features present in the source.
Qgis::SpatialIndexPresence hasSpatialIndex() const override
Returns an enum value representing the presence of a valid spatial index on the source,...
QgsFields fields() const override
Returns the fields associated with features in the source.
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
Base class for the definition of processing outputs.
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm.
QgsProperty sink
Sink/layer definition.
Base class for the definition of processing parameters.
static QList< QgsTiledSceneLayer * > compatibleTiledSceneLayers(QgsProject *project, bool sort=true)
Returns a list of tiled scene layers from a project which are compatible with the processing framewor...
static QString stringToPythonLiteral(const QString &string)
Converts a string to a Python string literal.
static QString defaultVectorExtension()
Returns the default vector extension to use, in the absence of all other constraints (e....
static QVariant generateIteratingDestination(const QVariant &input, const QVariant &id, QgsProcessingContext &context)
Converts an input parameter value for use in source iterating mode, where one individual sink is crea...
static QgsFields indicesToFields(const QList< int > &indices, const QgsFields &fields)
Returns a subset of fields based on the indices of desired fields.
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
static QString layerToStringIdentifier(const QgsMapLayer *layer, const QString &layerName=QString())
Returns a string representation of the source for a layer.
static QVariantMap preprocessQgisProcessParameters(const QVariantMap &parameters, bool &ok, QString &error)
Pre-processes a set of parameter values for the qgis_process command.
static QList< QgsAnnotationLayer * > compatibleAnnotationLayers(QgsProject *project, bool sort=true)
Returns a list of annotation layers from a project which are compatible with the processing framework...
static QString generateTempFilename(const QString &basename, const QgsProcessingContext *context=nullptr)
Returns a temporary filename for a given file, putting it into a temporary folder (creating that fold...
static QString defaultModelFolder()
Returns the default location for Processing model files.
static QString normalizeLayerSource(const QString &source)
Normalizes a layer source string for safe comparison across different operating system environments.
static QString formatHelpMapAsHtml(const QVariantMap &map, const QgsProcessingAlgorithm *algorithm)
Returns a HTML formatted version of the help text encoded in a variant map for a specified algorithm.
static QString defaultOutputFolder()
Returns the default location for data files and outputs generated by Processing algorithms.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri().
LayerHint
Layer type hints.
@ TiledScene
Tiled scene layer type, since QGIS 3.34.
@ Annotation
Annotation layer type, since QGIS 3.22.
@ VectorTile
Vector tile layer type, since QGIS 3.32.
@ Mesh
Mesh layer type, since QGIS 3.6.
@ PointCloud
Point cloud layer type, since QGIS 3.22.
static int parameterDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the parameter with matching name for a specified algorithm.
static QString defaultRasterFormat()
Returns the default raster format to use, in the absence of all other constraints (e....
static QgsProcessingFeatureSource * variantToSource(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a new feature source.
static QList< QgsRasterLayer * > compatibleRasterLayers(QgsProject *project, bool sort=true)
Returns a list of raster layers from a project which are compatible with the processing framework.
static int outputDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the output matching name for a specified algorithm.
static QString userFolder()
Returns the location for Processing files stored within the user's current QGIS profile.
static QgsRectangle combineLayerExtents(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context)
Combines the extent of several map layers.
static QString resolveDefaultEncoding(const QString &defaultEncoding="System")
Returns the default encoding.
static QList< QgsPluginLayer * > compatiblePluginLayers(QgsProject *project, bool sort=true)
Returns a list of plugin layers from a project which are compatible with the processing framework.
static QString variantToPythonLiteral(const QVariant &value)
Converts a variant to a Python literal.
static QgsCoordinateReferenceSystem variantToCrs(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a coordinate reference system.
static QList< QgsVectorLayer * > compatibleVectorLayers(QgsProject *project, const QList< int > &sourceTypes=QList< int >(), bool sort=true)
Returns a list of vector layers from a project which are compatible with the processing framework.
static QVariantMap removePointerValuesFromMap(const QVariantMap &map)
Removes any raw pointer values from an input map, replacing them with appropriate string values where...
static QStringList modelFolders()
Returns the list of locations for reading Processing model files.
static bool decodeProviderKeyAndUri(const QString &string, QString &providerKey, QString &uri)
Decodes a provider key and layer uri from an encoded string, for use with encodeProviderKeyAndUri().
static void createFeatureSinkPython(QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap())
Creates a feature sink ready for adding features.
static QList< QgsVectorTileLayer * > compatibleVectorTileLayers(QgsProject *project, bool sort=true)
Returns a list of vector tile layers from a project which are compatible with the processing framewor...
static QString convertToCompatibleFormatAndLayerName(const QgsVectorLayer *layer, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString &layerName, long long featureLimit=-1, const QString &filterExpression=QString())
Converts a source vector layer to a file path and layer name of a vector layer of compatible format.
static QList< QgsMapLayer * > compatibleLayers(QgsProject *project, bool sort=true)
Returns a list of map layers from a project which are compatible with the processing framework.
static QString convertToCompatibleFormat(const QgsVectorLayer *layer, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long long featureLimit=-1, const QString &filterExpression=QString())
Converts a source vector layer to a file path of a vector layer of compatible format.
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap(), const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QgsRemappingSinkDefinition *remappingDefinition=nullptr)
Creates a feature sink ready for adding features.
static QString defaultRasterExtension()
Returns the default raster extension to use, in the absence of all other constraints (e....
static QString defaultVectorTileExtension()
Returns the default vector tile extension to use, in the absence of all other constraints (e....
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
static QString defaultPointCloudExtension()
Returns the default point cloud extension to use, in the absence of all other constraints (e....
static QList< QgsPointCloudLayer * > compatiblePointCloudLayers(QgsProject *project, bool sort=true)
Returns a list of point cloud layers from a project which are compatible with the processing framewor...
static QStringList supportedImageFormats()
Returns a list of image format extensions supported by QImageWriter.
static QString supportedImageFileFilters()
Returns a file filter string of all supported image formats, suitable for use in file picker dialogs.
static QList< QgsMeshLayer * > compatibleMeshLayers(QgsProject *project, bool sort=true)
Returns a list of mesh layers from a project which are compatible with the processing framework.
static QString tempFolder(const QgsProcessingContext *context=nullptr)
Returns a session specific processing temporary folder for use in processing algorithms.
static const QgsSettingsEntryString * settingsDefaultOutputRasterLayerFormat
Settings entry default output raster layer format.
QFlags< LayerOptionsFlag > LayerOptionsFlags
static const QgsSettingsEntryString * settingsTempPath
Settings entry temp path.
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.
static const QgsSettingsEntryString * settingsDefaultOutputVectorLayerExt
Settings entry default output vector layer ext.
@ SkipIndexGeneration
Do not generate index when creating a layer. Makes sense only for point cloud layers.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:120
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
Qgis::PropertyType propertyType() const
Returns the property type.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
QString key() const
This returns the unique key associated with the provider.
virtual QList< Qgis::LayerType > supportedLayerTypes() const
Returns a list of the map layer types supported by the provider.
virtual QVariantMap decodeUri(const QString &uri) const
Breaks a provider data source URI into its component paths (e.g.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProvidersForUri(const QString &uri) const
Returns the details for the preferred provider(s) for opening the specified uri.
QString encodeUri(const QString &providerKey, const QVariantMap &parts)
Reassembles a provider data source URI from its component paths (e.g.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
bool flushBuffer() override
Flushes any internal buffer which may exist in the sink, causing any buffered features to be added to...
QgsFeatureSink * mSink
Underlying destination sink.
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
QgsProxyFeatureSink(QgsFeatureSink *sink)
Constructs a new QgsProxyFeatureSink which forwards features onto a destination sink.
static QStringList extensionsForFormat(const QString &format)
Returns a list of known file extensions for the given GDAL driver format.
static QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersAndFormats(RasterFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and GDAL format key as second ele...
Represents a raster layer.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsPointXY with associated coordinate reference system.
A QgsRectangle with associated coordinate reference system.
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.
void setDestinationCrs(const QgsCoordinateReferenceSystem &destination)
Sets the destination crs used for reprojecting incoming features to the sink's destination CRS.
void setDestinationWkbType(Qgis::WkbType type)
Sets the WKB geometry type for the destination.
void setDestinationFields(const QgsFields &fields)
Sets the fields for the destination sink.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Represents a map layer supporting display of tiled scene objects.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
virtual Qgis::VectorDataProviderAttributeEditCapabilities attributeEditCapabilities() const
Returns the provider's supported attribute editing capabilities.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
QString layerName
Layer name. If let empty, it will be derived from the filename.
QStringList layerOptions
List of OGR layer creation options.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QStringList datasourceOptions
List of OGR data source creation options.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
QgsFeatureSource subclass for the selected features from a QgsVectorLayer.
Represents a vector layer which manages a vector based dataset.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsRectangle extent() const final
Returns the extent of the layer.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
Implements a map layer that is dedicated to rendering of vector tiles.
Handles storage of information regarding WKB types and their properties.
Definition qgswkbtypes.h:42
@ UnknownCount
Provider returned an unknown feature count.
Definition qgis.h:608
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition qgis.cpp:601
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7632
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
QList< int > QgsAttributeList
Definition qgsfield.h:30
QString convertToCompatibleFormatInternal(const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName, long long featureLimit, const QString &filterExpression, bool renameFid)
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool skipIndexGeneration
Set to true if point cloud index generation should be skipped.
Details of available filters and formats.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
Setting options for loading vector layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Set to true if the default layer style should be loaded.