QGIS API Documentation 3.99.0-Master (d270888f95f)
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 "qgsexception.h"
23#include "qgsfileutils.h"
25#include "qgsmeshlayer.h"
26#include "qgspluginlayer.h"
27#include "qgspointcloudlayer.h"
31#include "qgsproject.h"
32#include "qgsproviderregistry.h"
33#include "qgsrasterfilewriter.h"
35#include "qgstiledscenelayer.h"
36#include "qgsvectorfilewriter.h"
37#include "qgsvectorlayer.h"
40#include "qgsvectortilelayer.h"
41
42#include <QRegularExpression>
43#include <QString>
44#include <QTextCodec>
45#include <QUuid>
46
47#include "moc_qgsprocessingutils.cpp"
48
49using namespace Qt::StringLiterals;
50
51QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
52{
53 return compatibleMapLayers< QgsRasterLayer >( project, sort );
54}
55
56QList<QgsVectorLayer *> QgsProcessingUtils::compatibleVectorLayers( QgsProject *project, const QList<int> &geometryTypes, bool sort )
57{
58 if ( !project )
59 return QList<QgsVectorLayer *>();
60
61 QList<QgsVectorLayer *> layers;
62 const auto vectorLayers = project->layers<QgsVectorLayer *>();
63 for ( QgsVectorLayer *l : vectorLayers )
64 {
65 if ( canUseLayer( l, geometryTypes ) )
66 layers << l;
67 }
68
69 if ( sort )
70 {
71 std::sort( layers.begin(), layers.end(), []( const QgsVectorLayer * a, const QgsVectorLayer * b ) -> bool
72 {
73 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
74 } );
75 }
76 return layers;
77}
78
79QList<QgsMeshLayer *> QgsProcessingUtils::compatibleMeshLayers( QgsProject *project, bool sort )
80{
81 return compatibleMapLayers< QgsMeshLayer >( project, sort );
82}
83
84QList<QgsPluginLayer *> QgsProcessingUtils::compatiblePluginLayers( QgsProject *project, bool sort )
85{
86 return compatibleMapLayers< QgsPluginLayer >( project, sort );
87}
88
89QList<QgsPointCloudLayer *> QgsProcessingUtils::compatiblePointCloudLayers( QgsProject *project, bool sort )
90{
91 return compatibleMapLayers< QgsPointCloudLayer >( project, sort );
92}
93
94QList<QgsAnnotationLayer *> QgsProcessingUtils::compatibleAnnotationLayers( QgsProject *project, bool sort )
95{
96 // we have to defer sorting until we've added the main annotation layer too
97 QList<QgsAnnotationLayer *> res = compatibleMapLayers< QgsAnnotationLayer >( project, false );
98 if ( project )
99 res.append( project->mainAnnotationLayer() );
100
101 if ( sort )
102 {
103 std::sort( res.begin(), res.end(), []( const QgsAnnotationLayer * a, const QgsAnnotationLayer * b ) -> bool
104 {
105 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
106 } );
107 }
108
109 return res;
110}
111
112QList<QgsVectorTileLayer *> QgsProcessingUtils::compatibleVectorTileLayers( QgsProject *project, bool sort )
113{
114 return compatibleMapLayers< QgsVectorTileLayer >( project, sort );
115}
116
117QList<QgsTiledSceneLayer *> QgsProcessingUtils::compatibleTiledSceneLayers( QgsProject *project, bool sort )
118{
119 return compatibleMapLayers< QgsTiledSceneLayer >( project, sort );
120}
121
122template<typename T> QList<T *> QgsProcessingUtils::compatibleMapLayers( QgsProject *project, bool sort )
123{
124 if ( !project )
125 return QList<T *>();
126
127 QList<T *> layers;
128 const auto projectLayers = project->layers<T *>();
129 for ( T *l : projectLayers )
130 {
131 if ( canUseLayer( l ) )
132 layers << l;
133 }
134
135 if ( sort )
136 {
137 std::sort( layers.begin(), layers.end(), []( const T * a, const T * b ) -> bool
138 {
139 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
140 } );
141 }
142 return layers;
143}
144
145QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project, bool sort )
146{
147 if ( !project )
148 return QList<QgsMapLayer *>();
149
150 QList<QgsMapLayer *> layers;
151
152 const auto rasterLayers = compatibleMapLayers< QgsRasterLayer >( project, false );
153 for ( QgsRasterLayer *rl : rasterLayers )
154 layers << rl;
155
156 const auto vectorLayers = compatibleVectorLayers( project, QList< int >(), false );
157 for ( QgsVectorLayer *vl : vectorLayers )
158 layers << vl;
159
160 const auto meshLayers = compatibleMapLayers< QgsMeshLayer >( project, false );
161 for ( QgsMeshLayer *ml : meshLayers )
162 layers << ml;
163
164 const auto pointCloudLayers = compatibleMapLayers< QgsPointCloudLayer >( project, false );
165 for ( QgsPointCloudLayer *pcl : pointCloudLayers )
166 layers << pcl;
167
168 const auto annotationLayers = compatibleMapLayers< QgsAnnotationLayer >( project, false );
169 for ( QgsAnnotationLayer *al : annotationLayers )
170 layers << al;
171 layers << project->mainAnnotationLayer();
172
173 const auto vectorTileLayers = compatibleMapLayers< QgsVectorTileLayer >( project, false );
174 for ( QgsVectorTileLayer *vtl : vectorTileLayers )
175 layers << vtl;
176
177 const auto tiledSceneLayers = compatibleMapLayers< QgsTiledSceneLayer >( project, false );
178 for ( QgsTiledSceneLayer *tml : tiledSceneLayers )
179 layers << tml;
180
181 const auto pluginLayers = compatibleMapLayers< QgsPluginLayer >( project, false );
182 for ( QgsPluginLayer *pl : pluginLayers )
183 layers << pl;
184
185 if ( sort )
186 {
187 std::sort( layers.begin(), layers.end(), []( const QgsMapLayer * a, const QgsMapLayer * b ) -> bool
188 {
189 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
190 } );
191 }
192 return layers;
193}
194
195QString QgsProcessingUtils::encodeProviderKeyAndUri( const QString &providerKey, const QString &uri )
196{
197 return u"%1://%2"_s.arg( providerKey, uri );
198}
199
200bool QgsProcessingUtils::decodeProviderKeyAndUri( const QString &string, QString &providerKey, QString &uri )
201{
202 const thread_local QRegularExpression re( u"^(\\w+?):\\/\\/(.+)$"_s );
203 const QRegularExpressionMatch match = re.match( string );
204 if ( !match.hasMatch() )
205 return false;
206
207 providerKey = match.captured( 1 );
208 uri = match.captured( 2 );
209
210 // double check that provider is valid
211 return QgsProviderRegistry::instance()->providerMetadata( providerKey );
212}
213
214QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store, QgsProcessingUtils::LayerHint typeHint )
215{
216 if ( !store || string.isEmpty() )
217 return nullptr;
218
219 QList< QgsMapLayer * > layers = store->mapLayers().values();
220
221 layers.erase( std::remove_if( layers.begin(), layers.end(), []( QgsMapLayer * layer )
222 {
223 switch ( layer->type() )
224 {
225 case Qgis::LayerType::Vector:
226 return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
227 case Qgis::LayerType::Raster:
228 return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
229 case Qgis::LayerType::Plugin:
230 case Qgis::LayerType::Group:
231 return true;
232 case Qgis::LayerType::Mesh:
233 return !canUseLayer( qobject_cast< QgsMeshLayer * >( layer ) );
234 case Qgis::LayerType::VectorTile:
235 return !canUseLayer( qobject_cast< QgsVectorTileLayer * >( layer ) );
236 case Qgis::LayerType::TiledScene:
237 return !canUseLayer( qobject_cast< QgsTiledSceneLayer * >( layer ) );
238 case Qgis::LayerType::PointCloud:
239 return !canUseLayer( qobject_cast< QgsPointCloudLayer * >( layer ) );
240 case Qgis::LayerType::Annotation:
241 return !canUseLayer( qobject_cast< QgsAnnotationLayer * >( layer ) );
242 }
243 return true;
244 } ), layers.end() );
245
246 auto isCompatibleType = [typeHint]( QgsMapLayer * l ) -> bool
247 {
248 switch ( typeHint )
249 {
250 case LayerHint::UnknownType:
251 return true;
252
253 case LayerHint::Vector:
254 return l->type() == Qgis::LayerType::Vector;
255
256 case LayerHint::Raster:
257 return l->type() == Qgis::LayerType::Raster;
258
259 case LayerHint::Mesh:
260 return l->type() == Qgis::LayerType::Mesh;
261
262 case LayerHint::PointCloud:
263 return l->type() == Qgis::LayerType::PointCloud;
264
265 case LayerHint::Annotation:
266 return l->type() == Qgis::LayerType::Annotation;
267
268 case LayerHint::VectorTile:
269 return l->type() == Qgis::LayerType::VectorTile;
270
271 case LayerHint::TiledScene:
272 return l->type() == Qgis::LayerType::TiledScene;
273 }
274 return true;
275 };
276
277 for ( QgsMapLayer *l : std::as_const( layers ) )
278 {
279 if ( isCompatibleType( l ) && l->id() == string )
280 return l;
281 }
282 for ( QgsMapLayer *l : std::as_const( layers ) )
283 {
284 if ( isCompatibleType( l ) && l->name() == string )
285 return l;
286 }
287 for ( QgsMapLayer *l : std::as_const( layers ) )
288 {
289 if ( isCompatibleType( l ) && normalizeLayerSource( l->source() ) == normalizeLayerSource( string ) )
290 return l;
291 }
292 return nullptr;
293}
294
295QgsMapLayer *QgsProcessingUtils::loadMapLayerFromString( const QString &string, const QgsCoordinateTransformContext &transformContext, LayerHint typeHint, QgsProcessing::LayerOptionsFlags flags )
296{
297 QString provider;
298 QString uri;
299 const bool useProvider = decodeProviderKeyAndUri( string, provider, uri );
300 if ( !useProvider )
301 uri = string;
302
303 QString name;
304
305 const QgsProviderMetadata *providerMetadata = useProvider ? QgsProviderRegistry::instance()->providerMetadata( provider ) : nullptr;
306 if ( providerMetadata )
307 {
308 // use the uri parts to determine a suitable layer name
309 const QVariantMap parts = providerMetadata->decodeUri( uri );
310 const QString layerName = parts.value( u"layerName"_s ).toString();
311
312 if ( !layerName.isEmpty() )
313 {
314 name = layerName;
315 }
316 else if ( const QString path = parts.value( u"path"_s ).toString(); !path.isEmpty() )
317 {
318 name = QFileInfo( path ).baseName();
319 }
320 }
321 else
322 {
323 const QStringList components = uri.split( '|' );
324 if ( components.isEmpty() )
325 return nullptr;
326
327 if ( QFileInfo fi( components.at( 0 ) ); fi.isFile() )
328 name = fi.baseName();
329 else
330 name = QFileInfo( uri ).baseName();
331 }
332
333 if ( name.isEmpty() )
334 {
335 name = QgsDataSourceUri( uri ).table();
336 }
337 if ( name.isEmpty() )
338 {
339 name = uri;
340 }
341
342 QList< Qgis::LayerType > candidateTypes;
343 switch ( typeHint )
344 {
346 {
347 if ( providerMetadata )
348 {
349 // refine the type hint based on what the provider supports
350 candidateTypes = providerMetadata->supportedLayerTypes();
351 }
352 break;
353 }
355 candidateTypes.append( Qgis::LayerType::Vector );
356 break;
358 candidateTypes.append( Qgis::LayerType::Raster );
359 break;
360 case LayerHint::Mesh:
361 candidateTypes.append( Qgis::LayerType::Mesh );
362 break;
364 candidateTypes.append( Qgis::LayerType::PointCloud );
365 break;
367 candidateTypes.append( Qgis::LayerType::Annotation );
368 break;
370 candidateTypes.append( Qgis::LayerType::VectorTile );
371 break;
373 candidateTypes.append( Qgis::LayerType::TiledScene );
374 break;
375 }
376
377 // brute force attempt to load a matching layer
378 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Vector ) )
379 {
380 QgsVectorLayer::LayerOptions options { transformContext };
381 options.loadDefaultStyle = false;
382 options.skipCrsValidation = true;
383
384 std::unique_ptr< QgsVectorLayer > layer;
385 if ( providerMetadata )
386 {
387 layer = std::make_unique<QgsVectorLayer>( uri, name, providerMetadata->key(), options );
388 }
389 else
390 {
391 // fallback to ogr
392 layer = std::make_unique<QgsVectorLayer>( uri, name, u"ogr"_s, options );
393 }
394 if ( layer->isValid() )
395 {
396 return layer.release();
397 }
398 }
399 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Raster ) )
400 {
401 QgsRasterLayer::LayerOptions rasterOptions;
402 rasterOptions.loadDefaultStyle = false;
403 rasterOptions.skipCrsValidation = true;
404
405 std::unique_ptr< QgsRasterLayer > rasterLayer;
406 if ( providerMetadata )
407 {
408 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, providerMetadata->key(), rasterOptions );
409 }
410 else
411 {
412 // fallback to gdal
413 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, u"gdal"_s, rasterOptions );
414 }
415
416 if ( rasterLayer->isValid() )
417 {
418 return rasterLayer.release();
419 }
420 }
421 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::Mesh ) )
422 {
423 QgsMeshLayer::LayerOptions meshOptions;
424 meshOptions.skipCrsValidation = true;
425
426 std::unique_ptr< QgsMeshLayer > meshLayer;
427 if ( providerMetadata )
428 {
429 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, providerMetadata->key(), meshOptions );
430 }
431 else
432 {
433 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, u"mdal"_s, meshOptions );
434 }
435 if ( meshLayer->isValid() )
436 {
437 return meshLayer.release();
438 }
439 }
440 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::PointCloud ) )
441 {
442 QgsPointCloudLayer::LayerOptions pointCloudOptions;
443 pointCloudOptions.skipCrsValidation = true;
444
446 {
447 pointCloudOptions.skipIndexGeneration = true;
448 }
449
450 std::unique_ptr< QgsPointCloudLayer > pointCloudLayer;
451 if ( providerMetadata )
452 {
453 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, providerMetadata->key(), pointCloudOptions );
454 }
455 else
456 {
457 const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( uri );
458 if ( !preferredProviders.empty() )
459 {
460 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, preferredProviders.at( 0 ).metadata()->key(), pointCloudOptions );
461 }
462 else
463 {
464 // pdal provider can read ascii files but it is not exposed by the provider to
465 // prevent automatic loading of tabular ascii files.
466 // Try to open the file with pdal provider.
467 QgsProviderMetadata *pdalProvider = QgsProviderRegistry::instance()->providerMetadata( u"pdal"_s );
468 if ( pdalProvider )
469 {
470 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, u"pdal"_s, pointCloudOptions );
471 }
472 }
473 }
474 if ( pointCloudLayer && pointCloudLayer->isValid() )
475 {
476 return pointCloudLayer.release();
477 }
478 }
479 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::VectorTile ) )
480 {
481 QgsDataSourceUri dsUri;
482 dsUri.setParam( "type", "mbtiles" );
483 dsUri.setParam( "url", uri );
484
485 std::unique_ptr< QgsVectorTileLayer > tileLayer;
486 tileLayer = std::make_unique< QgsVectorTileLayer >( dsUri.encodedUri(), name );
487
488 if ( tileLayer->isValid() )
489 {
490 return tileLayer.release();
491 }
492 }
493 if ( candidateTypes.empty() || candidateTypes.contains( Qgis::LayerType::TiledScene ) )
494 {
495 QgsTiledSceneLayer::LayerOptions tiledSceneOptions;
496 tiledSceneOptions.skipCrsValidation = true;
497
498 std::unique_ptr< QgsTiledSceneLayer > tiledSceneLayer;
499 if ( providerMetadata )
500 {
501 tiledSceneLayer = std::make_unique< QgsTiledSceneLayer >( uri, name, providerMetadata->key(), tiledSceneOptions );
502 }
503 else
504 {
505 const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( uri );
506 if ( !preferredProviders.empty() )
507 {
508 tiledSceneLayer = std::make_unique< QgsTiledSceneLayer >( uri, name, preferredProviders.at( 0 ).metadata()->key(), tiledSceneOptions );
509 }
510 }
511 if ( tiledSceneLayer && tiledSceneLayer->isValid() )
512 {
513 return tiledSceneLayer.release();
514 }
515 }
516 return nullptr;
517}
518
519QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers, LayerHint typeHint, QgsProcessing::LayerOptionsFlags flags )
520{
521 if ( string.isEmpty() )
522 return nullptr;
523
524 // prefer project layers
525 if ( context.project() && ( typeHint == LayerHint::Annotation ) && string.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
526 return context.project()->mainAnnotationLayer();
527
528 QgsMapLayer *layer = nullptr;
529 if ( auto *lProject = context.project() )
530 {
531 QgsMapLayer *layer = mapLayerFromStore( string, lProject->layerStore(), typeHint );
532 if ( layer )
533 return layer;
534 }
535
536 layer = mapLayerFromStore( string, context.temporaryLayerStore(), typeHint );
537 if ( layer )
538 return layer;
539
540 // check main annotation layer
541 if ( context.project() && ( typeHint == LayerHint::UnknownType ) && string.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
542 return context.project()->mainAnnotationLayer();
543
544
545 if ( !allowLoadingNewLayers )
546 return nullptr;
547
548 layer = loadMapLayerFromString( string, context.transformContext(), typeHint, flags );
549 if ( layer )
550 {
551 context.temporaryLayerStore()->addMapLayer( layer );
552 return layer;
553 }
554 else
555 {
556 return nullptr;
557 }
558}
559
560QgsProcessingFeatureSource *QgsProcessingUtils::variantToSource( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
561{
562 QVariant val = value;
563 bool selectedFeaturesOnly = false;
564 long long featureLimit = -1;
565 QString filterExpression;
566 bool overrideGeometryCheck = false;
568 if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
569 {
570 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
571 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
572 selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
573 featureLimit = fromVar.featureLimit;
574 filterExpression = fromVar.filterExpression;
575 val = fromVar.source;
577 geometryCheck = fromVar.geometryCheck;
578 }
579 else if ( val.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
580 {
581 // input is a QgsProcessingOutputLayerDefinition (e.g. an output from earlier in a model) - get extra properties from it
582 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
583 val = fromVar.sink;
584 }
585
586 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( val ) ) )
587 {
588 auto source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit, filterExpression );
589 if ( overrideGeometryCheck )
590 source->setInvalidGeometryCheck( geometryCheck );
591 return source.release();
592 }
593
594 QString layerRef;
595 if ( val.userType() == qMetaTypeId<QgsProperty>() )
596 {
597 layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
598 }
599 else if ( !val.isValid() || val.toString().isEmpty() )
600 {
601 // fall back to default
602 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( fallbackValue ) ) )
603 {
604 auto source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit, filterExpression );
605 if ( overrideGeometryCheck )
606 source->setInvalidGeometryCheck( geometryCheck );
607 return source.release();
608 }
609
610 layerRef = fallbackValue.toString();
611 }
612 else
613 {
614 layerRef = val.toString();
615 }
616
617 if ( layerRef.isEmpty() )
618 return nullptr;
619
620 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context, true, LayerHint::Vector ) );
621 if ( !vl )
622 return nullptr;
623
624 std::unique_ptr< QgsProcessingFeatureSource> source;
625 if ( selectedFeaturesOnly )
626 {
627 source = std::make_unique< QgsProcessingFeatureSource>( new QgsVectorLayerSelectedFeatureSource( vl ), context, true, featureLimit, filterExpression );
628 }
629 else
630 {
631 source = std::make_unique< QgsProcessingFeatureSource >( vl, context, false, featureLimit, filterExpression );
632 }
633
634 if ( overrideGeometryCheck )
635 source->setInvalidGeometryCheck( geometryCheck );
636 return source.release();
637}
638
639QgsCoordinateReferenceSystem QgsProcessingUtils::variantToCrs( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
640{
641 QVariant val = value;
642
643 if ( val.userType() == qMetaTypeId<QgsCoordinateReferenceSystem>() )
644 {
645 // input is a QgsCoordinateReferenceSystem - done!
646 return val.value< QgsCoordinateReferenceSystem >();
647 }
648 else if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
649 {
650 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
651 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
652 val = fromVar.source;
653 }
654 else if ( val.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
655 {
656 // input is a QgsProcessingOutputLayerDefinition - get extra properties from it
657 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
658 val = fromVar.sink;
659 }
660
661 if ( val.userType() == qMetaTypeId<QgsProperty>() && val.value< QgsProperty >().propertyType() == Qgis::PropertyType::Static )
662 {
663 val = val.value< QgsProperty >().staticValue();
664 }
665
666 // maybe a map layer
667 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( val ) ) )
668 return layer->crs();
669
670 if ( val.userType() == qMetaTypeId<QgsProperty>() )
671 val = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
672
673 if ( !val.isValid() )
674 {
675 // fall back to default
676 val = fallbackValue;
677 }
678
679 QString crsText = val.toString();
680 if ( crsText.isEmpty() )
681 crsText = fallbackValue.toString();
682
683 if ( crsText.isEmpty() )
685
686 // maybe special string
687 if ( context.project() && crsText.compare( "ProjectCrs"_L1, Qt::CaseInsensitive ) == 0 )
688 return context.project()->crs();
689
690 // else CRS from string
691 const QgsCoordinateReferenceSystem crs( crsText );
692 if ( crs.isValid() )
693 return crs;
694
695 // maybe a map layer reference
696 if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( crsText, context ) )
697 return layer->crs();
698
699 // no luck!
701}
702
703bool QgsProcessingUtils::canUseLayer( const QgsMeshLayer *layer )
704{
705 return layer && layer->dataProvider();
706}
707
708bool QgsProcessingUtils::canUseLayer( const QgsPluginLayer *layer )
709{
710 return layer && layer->isValid();
711}
712
713bool QgsProcessingUtils::canUseLayer( const QgsVectorTileLayer *layer )
714{
715 return layer && layer->isValid();
716}
717
718bool QgsProcessingUtils::canUseLayer( const QgsRasterLayer *layer )
719{
720 return layer && layer->isValid();
721}
722
723bool QgsProcessingUtils::canUseLayer( const QgsPointCloudLayer *layer )
724{
725 return layer && layer->isValid();
726}
727
728bool QgsProcessingUtils::canUseLayer( const QgsAnnotationLayer *layer )
729{
730 return layer && layer->isValid();
731}
732
733bool QgsProcessingUtils::canUseLayer( const QgsTiledSceneLayer *layer )
734{
735 return layer && layer->isValid();
736}
737
738bool QgsProcessingUtils::canUseLayer( const QgsVectorLayer *layer, const QList<int> &sourceTypes )
739{
740 return layer && layer->isValid() &&
741 ( sourceTypes.isEmpty()
742 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) && layer->geometryType() == Qgis::GeometryType::Point )
743 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) && layer->geometryType() == Qgis::GeometryType::Line )
744 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) && layer->geometryType() == Qgis::GeometryType::Polygon )
745 || ( sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && layer->isSpatial() )
746 || sourceTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
747 );
748}
749
750QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
751{
752 QString normalized = source;
753 normalized.replace( '\\', '/' );
754 return normalized.trimmed();
755}
756
757QString QgsProcessingUtils::layerToStringIdentifier( const QgsMapLayer *layer, const QString &layerName )
758{
759 if ( !layer )
760 return QString();
761
762 const QString source = QgsProcessingUtils::normalizeLayerSource( layer->source() );
763 if ( !source.isEmpty() )
764 {
765 const QString provider = layer->providerType();
766 // don't prepend provider type for these exceptional providers -- we assume them
767 // by default if the provider type is excluded. See logic in QgsProcessingUtils::loadMapLayerFromString
768 if ( provider.compare( "gdal"_L1, Qt::CaseInsensitive ) == 0
769 || provider.compare( "ogr"_L1, Qt::CaseInsensitive ) == 0
770 || provider.compare( "mdal"_L1, Qt::CaseInsensitive ) == 0 )
771 return source;
772
773 return u"%1://%2"_s.arg( provider, source );
774 }
775
776 if ( layer->type() == Qgis::LayerType::Annotation && layerName.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
777 {
778 return layerName;
779 }
780
781 return layer->id();
782}
783
784QString QgsProcessingUtils::variantToPythonLiteral( const QVariant &value )
785{
786 if ( !value.isValid() )
787 return u"None"_s;
788
789 if ( value.userType() == qMetaTypeId<QgsProperty>() )
790 return u"QgsProperty.fromExpression('%1')"_s.arg( value.value< QgsProperty >().asExpression() );
791 else if ( value.userType() == qMetaTypeId<QgsCoordinateReferenceSystem>() )
792 {
793 if ( !value.value< QgsCoordinateReferenceSystem >().isValid() )
794 return u"QgsCoordinateReferenceSystem()"_s;
795 else
796 return u"QgsCoordinateReferenceSystem('%1')"_s.arg( value.value< QgsCoordinateReferenceSystem >().authid() );
797 }
798 else if ( value.userType() == qMetaTypeId<QgsRectangle>() )
799 {
800 QgsRectangle r = value.value<QgsRectangle>();
801 return u"'%1, %3, %2, %4'"_s.arg( qgsDoubleToString( r.xMinimum() ),
805 }
806 else if ( value.userType() == qMetaTypeId<QgsReferencedRectangle>() )
807 {
809 return u"'%1, %3, %2, %4 [%5]'"_s.arg( qgsDoubleToString( r.xMinimum() ),
812 qgsDoubleToString( r.yMaximum() ), r.crs().authid() );
813 }
814 else if ( value.userType() == qMetaTypeId<QgsPointXY>() )
815 {
816 QgsPointXY r = value.value<QgsPointXY>();
817 return u"'%1,%2'"_s.arg( qgsDoubleToString( r.x() ),
818 qgsDoubleToString( r.y() ) );
819 }
820 else if ( value.userType() == qMetaTypeId<QgsReferencedPointXY>() )
821 {
823 return u"'%1,%2 [%3]'"_s.arg( qgsDoubleToString( r.x() ),
824 qgsDoubleToString( r.y() ),
825 r.crs().authid() );
826 }
827
828 switch ( value.userType() )
829 {
830 case QMetaType::Type::Bool:
831 return value.toBool() ? u"True"_s : u"False"_s;
832
833 case QMetaType::Type::Double:
834 return QString::number( value.toDouble() );
835
836 case QMetaType::Type::Int:
837 case QMetaType::Type::UInt:
838 return QString::number( value.toInt() );
839
840 case QMetaType::Type::LongLong:
841 case QMetaType::Type::ULongLong:
842 return QString::number( value.toLongLong() );
843
844 case QMetaType::Type::QVariantList:
845 {
846 QStringList parts;
847 const QVariantList vl = value.toList();
848 for ( const QVariant &v : vl )
849 {
850 parts << variantToPythonLiteral( v );
851 }
852 return parts.join( ',' ).prepend( '[' ).append( ']' );
853 }
854
855 case QMetaType::Type::QVariantMap:
856 {
857 const QVariantMap map = value.toMap();
858 QStringList parts;
859 parts.reserve( map.size() );
860 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
861 {
862 parts << u"%1: %2"_s.arg( stringToPythonLiteral( it.key() ), variantToPythonLiteral( it.value() ) );
863 }
864 return parts.join( ',' ).prepend( '{' ).append( '}' );
865 }
866
867 case QMetaType::Type::QDateTime:
868 {
869 const QDateTime dateTime = value.toDateTime();
870 return u"QDateTime(QDate(%1, %2, %3), QTime(%4, %5, %6))"_s
871 .arg( dateTime.date().year() )
872 .arg( dateTime.date().month() )
873 .arg( dateTime.date().day() )
874 .arg( dateTime.time().hour() )
875 .arg( dateTime.time().minute() )
876 .arg( dateTime.time().second() );
877 }
878
879 default:
880 break;
881 }
882
883 return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
884}
885
886QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
887{
888 QString s = string;
889 s.replace( '\\', "\\\\"_L1 );
890 s.replace( '\n', "\\n"_L1 );
891 s.replace( '\r', "\\r"_L1 );
892 s.replace( '\t', "\\t"_L1 );
893
894 if ( s.contains( '\'' ) && !s.contains( '\"' ) )
895 {
896 s = s.prepend( '"' ).append( '"' );
897 }
898 else
899 {
900 s.replace( '\'', "\\\'"_L1 );
901 s = s.prepend( '\'' ).append( '\'' );
902 }
903 return s;
904}
905
906void QgsProcessingUtils::parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter, QString &extension )
907{
908 extension.clear();
909 bool matched = decodeProviderKeyAndUri( destination, providerKey, uri );
910
911 if ( !matched )
912 {
913 const thread_local QRegularExpression splitRx( u"^(.{3,}?):(.*)$"_s );
914 QRegularExpressionMatch match = splitRx.match( destination );
915 if ( match.hasMatch() )
916 {
917 providerKey = match.captured( 1 );
918 uri = match.captured( 2 );
919 matched = true;
920 }
921 }
922
923 if ( matched )
924 {
925 if ( providerKey == "postgis"_L1 ) // older processing used "postgis" instead of "postgres"
926 {
927 providerKey = u"postgres"_s;
928 }
929 if ( providerKey == "ogr"_L1 )
930 {
931 QgsDataSourceUri dsUri( uri );
932 if ( !dsUri.database().isEmpty() )
933 {
934 if ( !dsUri.table().isEmpty() )
935 {
936 layerName = dsUri.table();
937 options.insert( u"layerName"_s, layerName );
938 }
939 uri = dsUri.database();
940 extension = QFileInfo( uri ).completeSuffix();
941 format = QgsVectorFileWriter::driverForExtension( extension );
942 options.insert( u"driverName"_s, format );
943 }
944 else
945 {
946 extension = QFileInfo( uri ).completeSuffix();
947 options.insert( u"driverName"_s, QgsVectorFileWriter::driverForExtension( extension ) );
948 }
949 options.insert( u"update"_s, true );
950 }
951 useWriter = false;
952 }
953 else
954 {
955 useWriter = true;
956 providerKey = u"ogr"_s;
957
958 const thread_local QRegularExpression splitRx( u"^(.*)\\.(.*?)$"_s );
959 QRegularExpressionMatch match = splitRx.match( destination );
960 if ( match.hasMatch() )
961 {
962 extension = match.captured( 2 );
963 format = QgsVectorFileWriter::driverForExtension( extension );
964 }
965
966 if ( format.isEmpty() )
967 {
968 format = u"GPKG"_s;
969 destination = destination + u".gpkg"_s;
970 }
971
972 options.insert( u"driverName"_s, format );
973 uri = destination;
974 }
975}
976
977QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions, const QStringList &datasourceOptions, const QStringList &layerOptions, QgsFeatureSink::SinkFlags sinkFlags, QgsRemappingSinkDefinition *remappingDefinition )
978{
979 QVariantMap options = createOptions;
980 if ( !options.contains( u"fileEncoding"_s ) )
981 {
982 // no destination encoding specified, use default
983 options.insert( u"fileEncoding"_s, context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding() );
984 }
985
986 if ( destination.isEmpty() || destination.startsWith( "memory:"_L1 ) )
987 {
988 // strip "memory:" from start of destination
989 if ( destination.startsWith( "memory:"_L1 ) )
990 destination = destination.mid( 7 );
991
992 if ( destination.isEmpty() )
993 destination = u"output"_s;
994
995 // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
996 std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs, false ) );
997 if ( !layer || !layer->isValid() )
998 {
999 throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
1000 }
1001
1002 if ( QgsProcessingFeedback *feedback = context.feedback() )
1003 {
1004 for ( const QgsField &field : fields )
1005 {
1006 // TODO -- support these!
1007 if ( !field.alias().isEmpty() )
1008 feedback->pushWarning( QObject::tr( "%1: Aliases are not compatible with scratch layers" ).arg( field.name() ) );
1009 if ( !field.alias().isEmpty() )
1010 feedback->pushWarning( QObject::tr( "%1: Comments are not compatible with scratch layers" ).arg( field.name() ) );
1011 }
1012 }
1013
1014 layer->setCustomProperty( u"OnConvertFormatRegeneratePrimaryKey"_s, static_cast< bool >( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey ) );
1015
1016 // update destination to layer ID
1017 destination = layer->id();
1018
1019 // this is a factory, so we need to return a proxy
1020 auto sink = std::make_unique<QgsProcessingFeatureSink>( layer->dataProvider(), destination, context );
1021 context.temporaryLayerStore()->addMapLayer( layer.release() );
1022
1023 return sink.release();
1024 }
1025 else
1026 {
1027 QString providerKey;
1028 QString uri;
1029 QString layerName;
1030 QString format;
1031 QString extension;
1032 bool useWriter = false;
1033 parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1034
1035 QgsFields newFields = fields;
1036 if ( useWriter && providerKey == "ogr"_L1 )
1037 {
1038 // use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
1039 // us to use any OGR format which supports feature addition
1040 QString finalFileName;
1041 QString finalLayerName;
1043 saveOptions.fileEncoding = options.value( u"fileEncoding"_s ).toString();
1044 saveOptions.layerName = !layerName.isEmpty() ? layerName : options.value( u"layerName"_s ).toString();
1045 saveOptions.driverName = format;
1046 saveOptions.datasourceOptions = !datasourceOptions.isEmpty() ? datasourceOptions : QgsVectorFileWriter::defaultDatasetOptions( format );
1047 saveOptions.layerOptions = !layerOptions.isEmpty() ? layerOptions : QgsVectorFileWriter::defaultLayerOptions( format );
1049 if ( remappingDefinition )
1050 {
1052 // sniff destination file to get correct wkb type and crs
1053 auto vl = std::make_unique< QgsVectorLayer >( destination );
1054 if ( vl->isValid() )
1055 {
1056 remappingDefinition->setDestinationWkbType( vl->wkbType() );
1057 remappingDefinition->setDestinationCrs( vl->crs() );
1058 newFields = vl->fields();
1059 remappingDefinition->setDestinationFields( newFields );
1060 }
1061 context.expressionContext().setFields( fields );
1062 }
1063 else
1064 {
1066 }
1067 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( destination, newFields, geometryType, crs, context.transformContext(), saveOptions, sinkFlags, &finalFileName, &finalLayerName ) );
1068 if ( writer->hasError() )
1069 {
1070 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
1071 }
1072
1073 if ( QgsProcessingFeedback *feedback = context.feedback() )
1074 {
1075 for ( const QgsField &field : fields )
1076 {
1077 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
1078 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1079 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
1080 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1081 }
1082 }
1083
1084 destination = finalFileName;
1085 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
1086 destination += u"|layername=%1"_s.arg( finalLayerName );
1087
1088 if ( remappingDefinition )
1089 {
1090 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, writer.release(), true );
1091 remapSink->setExpressionContext( context.expressionContext() );
1092 remapSink->setTransformContext( context.transformContext() );
1093 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1094 }
1095 else
1096 return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
1097 }
1098 else
1099 {
1100 const QgsVectorLayer::LayerOptions layerOptions { context.transformContext() };
1101 if ( remappingDefinition )
1102 {
1103 //write to existing layer
1104
1105 // use destination string as layer name (eg "postgis:..." )
1106 if ( !layerName.isEmpty() )
1107 {
1108 QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( providerKey, uri );
1109 parts.insert( u"layerName"_s, layerName );
1110 uri = QgsProviderRegistry::instance()->encodeUri( providerKey, parts );
1111 }
1112
1113 auto layer = std::make_unique<QgsVectorLayer>( uri, destination, providerKey, layerOptions );
1114 // update destination to layer ID
1115 destination = layer->id();
1116 if ( layer->isValid() )
1117 {
1118 remappingDefinition->setDestinationWkbType( layer->wkbType() );
1119 remappingDefinition->setDestinationCrs( layer->crs() );
1120 remappingDefinition->setDestinationFields( layer->fields() );
1121 }
1122
1123 if ( QgsProcessingFeedback *feedback = context.feedback() )
1124 {
1126 for ( const QgsField &field : fields )
1127 {
1128 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1129 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1130 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1131 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1132 }
1133 }
1134
1135 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, layer->dataProvider(), false );
1136 context.temporaryLayerStore()->addMapLayer( layer.release() );
1137 remapSink->setExpressionContext( context.expressionContext() );
1138 remapSink->setTransformContext( context.transformContext() );
1139 context.expressionContext().setFields( fields );
1140 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1141 }
1142 else
1143 {
1144 //create empty layer
1145 auto exporter = std::make_unique<QgsVectorLayerExporter>( uri, providerKey, newFields, geometryType, crs, true, options, sinkFlags );
1146 if ( exporter->errorCode() != Qgis::VectorExportResult::Success )
1147 {
1148 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
1149 }
1150
1151 // use destination string as layer name (eg "postgis:..." )
1152 if ( !layerName.isEmpty() )
1153 {
1154 uri += u"|layername=%1"_s.arg( layerName );
1155 // update destination to generated URI
1156 destination = uri;
1157 }
1158
1159 if ( QgsProcessingFeedback *feedback = context.feedback() )
1160 {
1161 for ( const QgsField &field : fields )
1162 {
1163 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1164 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1165 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1166 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1167 }
1168 }
1169
1170 return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
1171 }
1172 }
1173 }
1174}
1175
1176void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )
1177{
1178 *sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
1179}
1180
1181
1183{
1184 QgsRectangle extent;
1185 for ( const QgsMapLayer *layer : layers )
1186 {
1187 if ( !layer )
1188 continue;
1189
1190 if ( crs.isValid() )
1191 {
1192 //transform layer extent to target CRS
1193 QgsCoordinateTransform ct( layer->crs(), crs, context.transformContext() );
1195 try
1196 {
1197 QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
1198 extent.combineExtentWith( reprojExtent );
1199 }
1200 catch ( QgsCsException & )
1201 {
1202 // can't reproject... what to do here? hmmm?
1203 // let's ignore this layer for now, but maybe we should just use the original extent?
1204 }
1205 }
1206 else
1207 {
1208 extent.combineExtentWith( layer->extent() );
1209 }
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 {
1318 if ( map.contains( key ) )
1319 return map.value( key ).toString();
1320 return QString();
1321 };
1322
1323 QString s;
1324 s += u"<html><body><p>"_s + getText( u"ALG_DESC"_s ) + u"</p>\n"_s;
1325
1326 QString inputs;
1327 const auto parameterDefinitions = algorithm->parameterDefinitions();
1328 for ( const QgsProcessingParameterDefinition *def : parameterDefinitions )
1329 {
1330 if ( def->flags() & Qgis::ProcessingParameterFlag::Hidden || def->isDestination() )
1331 continue;
1332
1333 if ( !getText( def->name() ).isEmpty() )
1334 {
1335 inputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1336 inputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1337 }
1338 }
1339 if ( !inputs.isEmpty() )
1340 s += u"<h2>"_s + QObject::tr( "Input parameters" ) + u"</h2>\n"_s + inputs;
1341
1342 QString outputs;
1343 const auto outputDefinitions = algorithm->outputDefinitions();
1344 for ( const QgsProcessingOutputDefinition *def : outputDefinitions )
1345 {
1346 if ( !getText( def->name() ).isEmpty() )
1347 {
1348 outputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1349 outputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1350 }
1351 }
1352 if ( !outputs.isEmpty() )
1353 s += u"<h2>"_s + QObject::tr( "Outputs" ) + u"</h2>\n"_s + outputs;
1354
1355 if ( !map.value( u"EXAMPLES"_s ).toString().isEmpty() )
1356 s += u"<h2>%1</h2>\n<p>%2</p>"_s.arg( QObject::tr( "Examples" ), getText( u"EXAMPLES"_s ) );
1357
1358 s += "<br>"_L1;
1359 if ( !map.value( u"ALG_CREATOR"_s ).toString().isEmpty() )
1360 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm author:" ) + u" "_s + getText( u"ALG_CREATOR"_s ) + u"</p>"_s;
1361 if ( !map.value( u"ALG_HELP_CREATOR"_s ).toString().isEmpty() )
1362 s += u"<p align=\"right\">"_s + QObject::tr( "Help author:" ) + u" "_s + getText( u"ALG_HELP_CREATOR"_s ) + u"</p>"_s;
1363 if ( !map.value( u"ALG_VERSION"_s ).toString().isEmpty() )
1364 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm version:" ) + u" "_s + getText( u"ALG_VERSION"_s ) + u"</p>"_s;
1365
1366 s += "</body></html>"_L1;
1367 return s;
1368}
1369
1371{
1372 int index = 0;
1373 for ( const QgsProcessingOutputDefinition *def : algorithm->outputDefinitions() )
1374 {
1375 if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 )
1376 return index;
1377 index++;
1378 }
1379 return -1;
1380}
1381
1382QString convertToCompatibleFormatInternal( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName,
1383 long long featureLimit, const QString &filterExpression, bool renameFid )
1384{
1385 bool requiresTranslation = false;
1386
1387 // if we are only looking for selected features then we have to export back to disk,
1388 // as we need to subset only selected features, a concept which doesn't exist outside QGIS!
1389 requiresTranslation = requiresTranslation || selectedFeaturesOnly;
1390
1391 // if we are limiting the feature count, we better export
1392 requiresTranslation = requiresTranslation || featureLimit != -1 || !filterExpression.isEmpty();
1393
1394 // if the data provider is NOT ogr, then we HAVE to convert. Otherwise we run into
1395 // issues with data providers like spatialite, delimited text where the format can be
1396 // opened outside of QGIS, but with potentially very different behavior!
1397 requiresTranslation = requiresTranslation || vl->providerType() != "ogr"_L1;
1398
1399 // if the layer has a feature filter set, then we HAVE to convert. Feature filters are
1400 // a purely QGIS concept.
1401 requiresTranslation = requiresTranslation || !vl->subsetString().isEmpty();
1402
1403 // if the layer opened using GDAL's virtual I/O mechanism (/vsizip/, etc.), then
1404 // we HAVE to convert as other tools may not work with it
1405 requiresTranslation = requiresTranslation || vl->source().startsWith( "/vsi"_L1 );
1406
1407 // Check if layer is a disk based format and if so if the layer's path has a compatible filename suffix
1408 QString diskPath;
1409 if ( !requiresTranslation )
1410 {
1411 const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( vl->providerType(), vl->source() );
1412 if ( parts.contains( u"path"_s ) )
1413 {
1414 diskPath = parts.value( u"path"_s ).toString();
1415 QFileInfo fi( diskPath );
1416 requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
1417
1418 // if the layer name doesn't match the filename, we need to convert the layer. This method can only return
1419 // a filename, and cannot handle layernames as well as file paths
1420 const QString srcLayerName = parts.value( u"layerName"_s ).toString();
1421 if ( layerName )
1422 {
1423 // differing layer names are acceptable
1424 *layerName = srcLayerName;
1425 }
1426 else
1427 {
1428 // differing layer names are NOT acceptable
1429 requiresTranslation = requiresTranslation || ( !srcLayerName.isEmpty() && srcLayerName != fi.baseName() );
1430 }
1431 }
1432 else
1433 {
1434 requiresTranslation = true; // not a disk-based format
1435 }
1436 }
1437
1438 if ( requiresTranslation )
1439 {
1440 QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat, &context );
1441
1443 saveOptions.fileEncoding = context.defaultEncoding();
1444 saveOptions.driverName = QgsVectorFileWriter::driverForExtension( preferredFormat );
1445 QgsFields fields = vl->fields();
1446 if ( renameFid )
1447 {
1448 const int fidIndex = fields.lookupField( u"fid"_s );
1449 if ( fidIndex >= 0 )
1450 fields.rename( fidIndex, u"OLD_FID"_s );
1451 }
1452 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( temp, fields, vl->wkbType(), vl->crs(), context.transformContext(), saveOptions ) );
1453 QgsFeature f;
1455 QgsFeatureRequest request;
1456 if ( featureLimit != -1 )
1457 {
1458 request.setLimit( featureLimit );
1459 }
1460 if ( !filterExpression.isEmpty() )
1461 {
1462 request.setFilterExpression( filterExpression );
1463 }
1464
1465 if ( selectedFeaturesOnly )
1466 it = vl->getSelectedFeatures( std::move( request ) );
1467 else
1468 it = vl->getFeatures( request );
1469
1470 constexpr int maxErrors { 10 };
1471 unsigned long errorCounter { 0 };
1472 while ( it.nextFeature( f ) )
1473 {
1474 if ( feedback && feedback->isCanceled() )
1475 return QString();
1476
1477 if ( !writer->addFeature( f, QgsFeatureSink::FastInsert ) && feedback )
1478 {
1479 const QString errorMessage = writer->errorMessage();
1480 if ( !renameFid && saveOptions.driverName == "GPKG"_L1 && errorMessage.contains( "fid", Qt::CaseInsensitive ) )
1481 {
1482 // try again, dropping the FID field
1483 feedback->reportError( QObject::tr( "Cannot store existing FID values in temporary GeoPackage layer, these will be moved to \"OLD_FID\" instead." ), false );
1484 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, layerName,
1485 featureLimit, filterExpression, true );
1486 }
1487
1488 QString errorText;
1489 if ( errorCounter++ < maxErrors )
1490 {
1491 errorText = QObject::tr( "Error writing feature # %1 to output layer: %2" ).arg( QString::number( f.id() ), errorMessage );
1492
1493 feedback->reportError( errorText );
1494 }
1495 }
1496 }
1497 if ( errorCounter >= maxErrors )
1498 {
1499 feedback->reportError( QObject::tr( "There were %1 errors writing features, only the first %2 have been reported." ).arg( QString::number( errorCounter ), QString::number( maxErrors ) ) );
1500 }
1501 return temp;
1502 }
1503 else
1504 {
1505 return diskPath;
1506 }
1507}
1508
1509QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long long featureLimit, const QString &filterExpression )
1510{
1511 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, nullptr, featureLimit, filterExpression, false );
1512}
1513
1514QString QgsProcessingUtils::convertToCompatibleFormatAndLayerName( const QgsVectorLayer *layer, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString &layerName, long long featureLimit, const QString &filterExpression )
1515{
1516 layerName.clear();
1517 return convertToCompatibleFormatInternal( layer, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, &layerName, featureLimit, filterExpression, false );
1518}
1519
1520QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix )
1521{
1522 QgsFields outFields = fieldsA;
1523 QSet< QString > usedNames;
1524 for ( const QgsField &f : fieldsA )
1525 {
1526 usedNames.insert( f.name().toLower() );
1527 }
1528
1529 for ( const QgsField &f : fieldsB )
1530 {
1531 QgsField newField = f;
1532 newField.setName( fieldsBPrefix + f.name() );
1533 if ( usedNames.contains( newField.name().toLower() ) )
1534 {
1535 int idx = 2;
1536 QString newName = newField.name() + '_' + QString::number( idx );
1537 while ( usedNames.contains( newName.toLower() ) || fieldsB.indexOf( newName ) != -1 )
1538 {
1539 idx++;
1540 newName = newField.name() + '_' + QString::number( idx );
1541 }
1542 newField.setName( newName );
1543 outFields.append( newField );
1544 }
1545 else
1546 {
1547 outFields.append( newField );
1548 }
1549 usedNames.insert( newField.name() );
1550 }
1551
1552 return outFields;
1553}
1554
1555
1556QList<int> QgsProcessingUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
1557{
1558 QList<int> indices;
1559 if ( !fieldNames.isEmpty() )
1560 {
1561 indices.reserve( fieldNames.count() );
1562 for ( const QString &f : fieldNames )
1563 {
1564 int idx = fields.lookupField( f );
1565 if ( idx >= 0 )
1566 indices.append( idx );
1567 }
1568 }
1569 else
1570 {
1571 indices.reserve( fields.count() );
1572 for ( int i = 0; i < fields.count(); ++i )
1573 indices.append( i );
1574 }
1575 return indices;
1576}
1577
1578
1579QgsFields QgsProcessingUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
1580{
1581 QgsFields fieldsSubset;
1582 for ( int i : indices )
1583 fieldsSubset.append( fields.at( i ) );
1584 return fieldsSubset;
1585}
1586
1588{
1589 QString setting = QgsProcessing::settingsDefaultOutputVectorLayerExt->value().trimmed();
1590 if ( setting.isEmpty() )
1591 return u"gpkg"_s;
1592
1593 if ( setting.startsWith( '.' ) )
1594 setting = setting.mid( 1 );
1595
1596 const QStringList supportedFormats = QgsVectorFileWriter::supportedFormatExtensions();
1597 if ( !supportedFormats.contains( setting, Qt::CaseInsensitive ) )
1598 return u"gpkg"_s;
1599
1600 return setting;
1601}
1602
1604{
1605 QString setting = QgsProcessing::settingsDefaultOutputRasterLayerFormat->value().trimmed();
1606 if ( setting.isEmpty() )
1607 return u"GTiff"_s;
1608
1609 const QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersFormats =
1611 for ( const QgsRasterFileWriter::FilterFormatDetails &detail : std::as_const( supportedFiltersFormats ) )
1612 {
1613 if ( detail.driverName.compare( setting, Qt::CaseInsensitive ) == 0 )
1614 return detail.driverName;
1615 }
1616
1617 return u"GTiff"_s;
1618}
1619
1621{
1622 QString format = defaultRasterFormat();
1623 QStringList extensions = QgsRasterFileWriter::extensionsForFormat( format );
1624 if ( !extensions.isEmpty() )
1625 return extensions[0];
1626
1627 return u"tif"_s;
1628}
1629
1631{
1632 return u"las"_s;
1633}
1634
1636{
1637 return u"mbtiles"_s;
1638}
1639
1640QVariantMap QgsProcessingUtils::removePointerValuesFromMap( const QVariantMap &map )
1641{
1642 auto layerPointerToString = []( QgsMapLayer * layer ) -> QString
1643 {
1644 if ( layer && layer->providerType() == "memory"_L1 )
1645 return layer->id();
1646 else if ( layer )
1647 return layer->source();
1648 else
1649 return QString();
1650 };
1651
1652 auto cleanPointerValues = [&layerPointerToString]( const QVariant & value ) -> QVariant
1653 {
1654 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( value.value< QObject * >() ) )
1655 {
1656 // don't store pointers in maps for long-term storage
1657 return layerPointerToString( layer );
1658 }
1659 else if ( value.userType() == QMetaType::type( "QPointer< QgsMapLayer >" ) )
1660 {
1661 // don't store pointers in maps for long-term storage
1662 return layerPointerToString( value.value< QPointer< QgsMapLayer > >().data() );
1663 }
1664 else
1665 {
1666 return value;
1667 }
1668 };
1669
1670 QVariantMap res;
1671 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
1672 {
1673 if ( it->userType() == QMetaType::Type::QVariantMap )
1674 {
1675 res.insert( it.key(), removePointerValuesFromMap( it.value().toMap() ) );
1676 }
1677 else if ( it->userType() == QMetaType::Type::QVariantList )
1678 {
1679 QVariantList dest;
1680 const QVariantList source = it.value().toList();
1681 dest.reserve( source.size() );
1682 for ( const QVariant &v : source )
1683 {
1684 dest.append( cleanPointerValues( v ) );
1685 }
1686 res.insert( it.key(), dest );
1687 }
1688 else
1689 {
1690 res.insert( it.key(), cleanPointerValues( it.value() ) );
1691 }
1692 }
1693 return res;
1694}
1695
1696QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantMap &parameters, bool &ok, QString &error )
1697{
1698 QVariantMap output;
1699 ok = true;
1700 for ( auto it = parameters.constBegin(); it != parameters.constEnd(); ++it )
1701 {
1702 if ( it.value().userType() == QMetaType::Type::QVariantMap )
1703 {
1704 const QVariantMap value = it.value().toMap();
1705 if ( value.value( u"type"_s ).toString() == "data_defined"_L1 )
1706 {
1707 const QString expression = value.value( u"expression"_s ).toString();
1708 const QString field = value.value( u"field"_s ).toString();
1709 if ( !expression.isEmpty() )
1710 {
1711 output.insert( it.key(), QgsProperty::fromExpression( expression ) );
1712 }
1713 else if ( !field.isEmpty() )
1714 {
1715 output.insert( it.key(), QgsProperty::fromField( field ) );
1716 }
1717 else
1718 {
1719 ok = false;
1720 error = QObject::tr( "Invalid data defined parameter for %1, requires 'expression' or 'field' values." ).arg( it.key() );
1721 }
1722 }
1723 else
1724 {
1725 output.insert( it.key(), it.value() );
1726 }
1727 }
1728 else if ( it.value().userType() == QMetaType::Type::QString )
1729 {
1730 const QString stringValue = it.value().toString();
1731
1732 if ( stringValue.startsWith( "field:"_L1 ) )
1733 {
1734 output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) );
1735 }
1736 else if ( stringValue.startsWith( "expression:"_L1 ) )
1737 {
1738 output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) );
1739 }
1740 else
1741 {
1742 output.insert( it.key(), it.value() );
1743 }
1744 }
1745 else
1746 {
1747 output.insert( it.key(), it.value() );
1748 }
1749 }
1750 return output;
1751}
1752
1753QString QgsProcessingUtils::resolveDefaultEncoding( const QString &defaultEncoding )
1754{
1755 if ( ! QTextCodec::availableCodecs().contains( defaultEncoding.toLatin1() ) )
1756 {
1757 const QString systemCodec = QTextCodec::codecForLocale()->name();
1758 if ( ! systemCodec.isEmpty() )
1759 {
1760 return systemCodec;
1761 }
1762 return QString( "UTF-8" );
1763 }
1764
1765 return defaultEncoding;
1766}
1767
1768//
1769// QgsProcessingFeatureSource
1770//
1771
1772QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource, long long featureLimit, const QString &filterExpression )
1773 : mSource( originalSource )
1774 , mOwnsSource( ownsOriginalSource )
1775 , mSourceCrs( mSource->sourceCrs() )
1776 , mSourceFields( mSource->fields() )
1777 , mSourceWkbType( mSource->wkbType() )
1778 , mSourceName( mSource->sourceName() )
1779 , mSourceExtent( mSource->sourceExtent() )
1780 , mSourceSpatialIndexPresence( mSource->hasSpatialIndex() )
1781 , mInvalidGeometryCheck( QgsWkbTypes::geometryType( mSource->wkbType() ) == Qgis::GeometryType::Point
1782 ? Qgis::InvalidGeometryCheck::NoCheck // never run geometry validity checks for point layers!
1783 : context.invalidGeometryCheck() )
1784 , mInvalidGeometryCallback( context.invalidGeometryCallback( originalSource ) )
1785 , mTransformErrorCallback( context.transformErrorCallback() )
1786 , mInvalidGeometryCallbackSkip( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::SkipInvalid, originalSource ) )
1787 , mInvalidGeometryCallbackAbort( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::AbortOnInvalid, originalSource ) )
1788 , mFeatureLimit( featureLimit )
1789 , mFilterExpression( filterExpression )
1790{}
1791
1793{
1794 if ( mOwnsSource )
1795 delete mSource;
1796}
1797
1799{
1800 QgsFeatureRequest req( request );
1801 req.setTransformErrorCallback( mTransformErrorCallback );
1802
1805 else
1806 {
1807 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1808 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1809 }
1810
1811 if ( mFeatureLimit != -1 && req.limit() != -1 )
1812 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1813 else if ( mFeatureLimit != -1 )
1814 req.setLimit( mFeatureLimit );
1815
1816 if ( !mFilterExpression.isEmpty() )
1817 req.combineFilterExpression( mFilterExpression );
1818
1819 return mSource->getFeatures( req );
1820}
1821
1823{
1824 Qgis::FeatureAvailability sourceAvailability = mSource->hasFeatures();
1825 if ( sourceAvailability == Qgis::FeatureAvailability::NoFeaturesAvailable )
1826 return Qgis::FeatureAvailability::NoFeaturesAvailable; // never going to be features if underlying source has no features
1827 else if ( mInvalidGeometryCheck == Qgis::InvalidGeometryCheck::NoCheck && mFilterExpression.isEmpty() )
1828 return sourceAvailability;
1829 else
1830 // we don't know... source has features, but these may be filtered out by invalid geometry check or filter expression
1832}
1833
1835{
1836 QgsFeatureRequest req( request );
1837 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1838 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1839 req.setTransformErrorCallback( mTransformErrorCallback );
1840
1841 if ( mFeatureLimit != -1 && req.limit() != -1 )
1842 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1843 else if ( mFeatureLimit != -1 )
1844 req.setLimit( mFeatureLimit );
1845
1846 if ( !mFilterExpression.isEmpty() )
1847 req.combineFilterExpression( mFilterExpression );
1848
1849 return mSource->getFeatures( req );
1850}
1851
1856
1858{
1859 return mSourceFields;
1860}
1861
1863{
1864 return mSourceWkbType;
1865}
1866
1868{
1869 if ( !mFilterExpression.isEmpty() )
1870 return static_cast< int >( Qgis::FeatureCountState::UnknownCount );
1871
1872 if ( mFeatureLimit == -1 )
1873 return mSource->featureCount();
1874 else
1875 return std::min( mFeatureLimit, mSource->featureCount() );
1876}
1877
1879{
1880 return mSourceName;
1881}
1882
1883QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
1884{
1885 if ( mFilterExpression.isEmpty() )
1886 return mSource->uniqueValues( fieldIndex, limit );
1887
1888 // inefficient method when filter expression in use
1889 // TODO QGIS 5.0 -- add filter expression to virtual ::uniqueValues function
1890 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
1891 return QSet<QVariant>();
1892
1895 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
1896 req.setFilterExpression( mFilterExpression );
1897
1898 QSet<QVariant> values;
1899 QgsFeatureIterator it = getFeatures( req );
1900 QgsFeature f;
1901 while ( it.nextFeature( f ) )
1902 {
1903 values.insert( f.attribute( fieldIndex ) );
1904 if ( limit > 0 && values.size() >= limit )
1905 return values;
1906 }
1907 return values;
1908}
1909
1910QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
1911{
1912 if ( mFilterExpression.isEmpty() )
1913 return mSource->minimumValue( fieldIndex );
1914
1915 // inefficient method when filter expression in use
1916 // TODO QGIS 5.0 -- add filter expression to virtual ::minimumValue function
1917 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
1918 return QVariant();
1919
1922 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
1923
1924 QVariant min;
1925 QgsFeatureIterator it = getFeatures( req );
1926 QgsFeature f;
1927 while ( it.nextFeature( f ) )
1928 {
1929 const QVariant v = f.attribute( fieldIndex );
1930 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantLessThan( v, min ) || QgsVariantUtils::isNull( min ) ) )
1931 {
1932 min = v;
1933 }
1934 }
1935 return min;
1936}
1937
1938QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
1939{
1940 if ( mFilterExpression.isEmpty() )
1941 return mSource->maximumValue( fieldIndex );
1942
1943 // inefficient method when filter expression in use
1944 // TODO QGIS 5.0 -- add filter expression to virtual ::maximumValue function
1945 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
1946 return QVariant();
1947
1950 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
1951
1952 QVariant max;
1953 QgsFeatureIterator it = getFeatures( req );
1954 QgsFeature f;
1955 while ( it.nextFeature( f ) )
1956 {
1957 const QVariant v = f.attribute( fieldIndex );
1958 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantGreaterThan( v, max ) || QgsVariantUtils::isNull( max ) ) )
1959 {
1960 max = v;
1961 }
1962 }
1963 return max;
1964}
1965
1967{
1968 return mSourceExtent;
1969}
1970
1972{
1973 if ( mFilterExpression.isEmpty() )
1974 return mSource->allFeatureIds();
1975
1978 .setNoAttributes()
1979 .setFilterExpression( mFilterExpression ) );
1980
1981 QgsFeatureIds ids;
1982
1983 QgsFeature fet;
1984 while ( fit.nextFeature( fet ) )
1985 {
1986 ids << fet.id();
1987 }
1988
1989 return ids;
1990}
1991
1993{
1994 return mSourceSpatialIndexPresence;
1995}
1996
1998{
1999 QgsExpressionContextScope *expressionContextScope = nullptr;
2000 QgsExpressionContextScopeGenerator *generator = dynamic_cast<QgsExpressionContextScopeGenerator *>( mSource );
2001 if ( generator )
2002 {
2003 expressionContextScope = generator->createExpressionContextScope();
2004 }
2005 return expressionContextScope;
2006}
2007
2009{
2010 mInvalidGeometryCheck = method;
2011 switch ( mInvalidGeometryCheck )
2012 {
2014 mInvalidGeometryCallback = nullptr;
2015 break;
2016
2018 mInvalidGeometryCallback = mInvalidGeometryCallbackSkip;
2019 break;
2020
2022 mInvalidGeometryCallback = mInvalidGeometryCallbackAbort;
2023 break;
2024
2025 }
2026}
2027
2029{
2030 return mInvalidGeometryCheck;
2031}
2032
2033
2034//
2035// QgsProcessingFeatureSink
2036//
2037QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
2038 : QgsProxyFeatureSink( originalSink )
2039 , mContext( context )
2040 , mSinkName( sinkName )
2041 , mOwnsSink( ownsOriginalSink )
2042{}
2043
2045{
2046 if ( !flushBuffer() && mContext.feedback() )
2047 {
2048 mContext.feedback()->reportError( lastError() );
2049 }
2050
2051 if ( mOwnsSink )
2052 {
2053 delete mSink;
2054 mSink = nullptr;
2055 }
2056}
2057
2059{
2060 if ( !flushBuffer() )
2061 {
2063 }
2064}
2065
2067{
2068 bool result = QgsProxyFeatureSink::addFeature( feature, flags );
2069 if ( !result && mContext.feedback() )
2070 {
2071 const QString error = lastError();
2072 if ( !error.isEmpty() )
2073 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1: %2" ).arg( mSinkName, error ) );
2074 else
2075 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
2076 }
2077 return result;
2078}
2079
2081{
2082 bool result = QgsProxyFeatureSink::addFeatures( features, flags );
2083 if ( !result && mContext.feedback() )
2084 {
2085 const QString error = lastError();
2086 if ( !error.isEmpty() )
2087 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1: %2", nullptr, features.count() ).arg( mSinkName, error ) );
2088 else
2089 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1", nullptr, features.count() ).arg( mSinkName ) );
2090 }
2091 return result;
2092}
2093
2095{
2096 bool result = QgsProxyFeatureSink::addFeatures( iterator, flags );
2097 if ( !result && mContext.feedback() )
2098 {
2099 const QString error = lastError();
2100 if ( !error.isEmpty() )
2101 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1: %2" ).arg( mSinkName, error ) );
2102 else
2103 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1" ).arg( mSinkName ) );
2104 }
2105 return result;
2106}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3598
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3592
@ VectorPoint
Vector point layers.
Definition qgis.h:3593
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3595
@ VectorLine
Vector line layers.
Definition qgis.h:3594
@ FieldComments
Writer can support field comments.
Definition qgis.h:1094
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1093
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:577
@ Success
No errors were encountered.
Definition qgis.h:1072
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
@ Static
Static property.
Definition qgis.h:703
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
FeatureAvailability
Possible return value for QgsFeatureSource::hasFeatures() to determine if a source is empty.
Definition qgis.h:596
@ FeaturesMaybeAvailable
There may be features available in this source.
Definition qgis.h:599
@ NoFeaturesAvailable
There are certainly no features available in this source.
Definition qgis.h:597
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
@ EditComment
Allows editing comments.
Definition qgis.h:611
QFlags< VectorDataProviderAttributeEditCapability > VectorDataProviderAttributeEditCapabilities
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:621
InvalidGeometryCheck
Methods for handling of features with invalid geometries.
Definition qgis.h:2296
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2297
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2299
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2298
@ OverrideDefaultGeometryCheck
If set, the default geometry check method (as dictated by QgsProcessingContext) will be overridden fo...
Definition qgis.h:3746
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3770
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3823
@ NoSymbology
Export only data.
Definition qgis.h:5824
QFlags< ProcessingFeatureSourceFlag > ProcessingFeatureSourceFlags
Flags which control how QgsProcessingFeatureSource fetches features.
Definition qgis.h:3781
Represents a map layer containing a set of georeferenced annotations, e.g.
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:68
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:55
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:233
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:76
int count
Definition qgsfields.h:50
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 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 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 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 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 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 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:112
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:118
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.
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:566
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:6817
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.