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