QGIS API Documentation 4.1.0-Master (26185ffb827)
Loading...
Searching...
No Matches
qgsprocessingutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingutils.cpp
3 ------------------------
4 begin : April 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsprocessingutils.h"
19
20#include "qgsannotationlayer.h"
21#include "qgsexception.h"
23#include "qgsfileutils.h"
25#include "qgsmeshlayer.h"
26#include "qgspluginlayer.h"
27#include "qgspointcloudlayer.h"
31#include "qgsproject.h"
32#include "qgsproviderregistry.h"
33#include "qgsrasterfilewriter.h"
35#include "qgstiledscenelayer.h"
36#include "qgsvectorfilewriter.h"
37#include "qgsvectorlayer.h"
40#include "qgsvectortilelayer.h"
41
42#include <QImageWriter>
43#include <QRegularExpression>
44#include <QString>
45#include <QTextCodec>
46#include <QUuid>
47
48#include "moc_qgsprocessingutils.cpp"
49
50using namespace Qt::StringLiterals;
51
52QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
53{
54 return compatibleMapLayers< QgsRasterLayer >( project, sort );
55}
56
57QList<QgsVectorLayer *> QgsProcessingUtils::compatibleVectorLayers( QgsProject *project, const QList<int> &geometryTypes, bool sort )
58{
59 if ( !project )
60 return QList<QgsVectorLayer *>();
61
62 QList<QgsVectorLayer *> layers;
63 const auto vectorLayers = project->layers<QgsVectorLayer *>();
64 for ( QgsVectorLayer *l : vectorLayers )
65 {
66 if ( canUseLayer( l, geometryTypes ) )
67 layers << l;
68 }
69
70 if ( sort )
71 {
72 std::sort( layers.begin(), layers.end(), []( const QgsVectorLayer *a, const QgsVectorLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
73 }
74 return layers;
75}
76
77QList<QgsMeshLayer *> QgsProcessingUtils::compatibleMeshLayers( QgsProject *project, bool sort )
78{
79 return compatibleMapLayers< QgsMeshLayer >( project, sort );
80}
81
82QList<QgsPluginLayer *> QgsProcessingUtils::compatiblePluginLayers( QgsProject *project, bool sort )
83{
84 return compatibleMapLayers< QgsPluginLayer >( project, sort );
85}
86
87QList<QgsPointCloudLayer *> QgsProcessingUtils::compatiblePointCloudLayers( QgsProject *project, bool sort )
88{
89 return compatibleMapLayers< QgsPointCloudLayer >( project, sort );
90}
91
92QList<QgsAnnotationLayer *> QgsProcessingUtils::compatibleAnnotationLayers( QgsProject *project, bool sort )
93{
94 // we have to defer sorting until we've added the main annotation layer too
95 QList<QgsAnnotationLayer *> res = compatibleMapLayers< QgsAnnotationLayer >( project, false );
96 if ( project )
97 res.append( project->mainAnnotationLayer() );
98
99 if ( sort )
100 {
101 std::sort( res.begin(), res.end(), []( const QgsAnnotationLayer *a, const QgsAnnotationLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
102 }
103
104 return res;
105}
106
107QList<QgsVectorTileLayer *> QgsProcessingUtils::compatibleVectorTileLayers( QgsProject *project, bool sort )
108{
109 return compatibleMapLayers< QgsVectorTileLayer >( project, sort );
110}
111
112QList<QgsTiledSceneLayer *> QgsProcessingUtils::compatibleTiledSceneLayers( QgsProject *project, bool sort )
113{
114 return compatibleMapLayers< QgsTiledSceneLayer >( project, sort );
115}
116
117template<typename T> QList<T *> QgsProcessingUtils::compatibleMapLayers( QgsProject *project, bool sort )
118{
119 if ( !project )
120 return QList<T *>();
121
122 QList<T *> layers;
123 const auto projectLayers = project->layers<T *>();
124 for ( T *l : projectLayers )
125 {
126 if ( canUseLayer( l ) )
127 layers << l;
128 }
129
130 if ( sort )
131 {
132 std::sort( layers.begin(), layers.end(), []( const T *a, const T *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
133 }
134 return layers;
135}
136
137QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project, bool sort )
138{
139 if ( !project )
140 return QList<QgsMapLayer *>();
141
142 QList<QgsMapLayer *> layers;
143
144 const auto rasterLayers = compatibleMapLayers< QgsRasterLayer >( project, false );
145 for ( QgsRasterLayer *rl : rasterLayers )
146 layers << rl;
147
148 const auto vectorLayers = compatibleVectorLayers( project, QList< int >(), false );
149 for ( QgsVectorLayer *vl : vectorLayers )
150 layers << vl;
151
152 const auto meshLayers = compatibleMapLayers< QgsMeshLayer >( project, false );
153 for ( QgsMeshLayer *ml : meshLayers )
154 layers << ml;
155
156 const auto pointCloudLayers = compatibleMapLayers< QgsPointCloudLayer >( project, false );
157 for ( QgsPointCloudLayer *pcl : pointCloudLayers )
158 layers << pcl;
159
160 const auto annotationLayers = compatibleMapLayers< QgsAnnotationLayer >( project, false );
161 for ( QgsAnnotationLayer *al : annotationLayers )
162 layers << al;
163 layers << project->mainAnnotationLayer();
164
165 const auto vectorTileLayers = compatibleMapLayers< QgsVectorTileLayer >( project, false );
166 for ( QgsVectorTileLayer *vtl : vectorTileLayers )
167 layers << vtl;
168
169 const auto tiledSceneLayers = compatibleMapLayers< QgsTiledSceneLayer >( project, false );
170 for ( QgsTiledSceneLayer *tml : tiledSceneLayers )
171 layers << tml;
172
173 const auto pluginLayers = compatibleMapLayers< QgsPluginLayer >( project, false );
174 for ( QgsPluginLayer *pl : pluginLayers )
175 layers << pl;
176
177 if ( sort )
178 {
179 std::sort( layers.begin(), layers.end(), []( const QgsMapLayer *a, const QgsMapLayer *b ) -> bool { return QString::localeAwareCompare( a->name(), b->name() ) < 0; } );
180 }
181 return layers;
182}
183
184QString QgsProcessingUtils::encodeProviderKeyAndUri( const QString &providerKey, const QString &uri )
185{
186 return u"%1://%2"_s.arg( providerKey, uri );
187}
188
189bool QgsProcessingUtils::decodeProviderKeyAndUri( const QString &string, QString &providerKey, QString &uri )
190{
191 const thread_local QRegularExpression re( u"^(\\w+?):\\/\\/(.+)$"_s );
192 const QRegularExpressionMatch match = re.match( string );
193 if ( !match.hasMatch() )
194 return false;
195
196 providerKey = match.captured( 1 );
197 uri = match.captured( 2 );
198
199 // double check that provider is valid
200 return QgsProviderRegistry::instance()->providerMetadata( providerKey );
201}
202
203QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store, QgsProcessingUtils::LayerHint typeHint )
204{
205 if ( !store || string.isEmpty() )
206 return nullptr;
207
208 QList< QgsMapLayer * > layers = store->mapLayers().values();
209
210 layers.erase(
211 std::remove_if(
212 layers.begin(),
213 layers.end(),
214 []( QgsMapLayer *layer ) {
215 switch ( layer->type() )
216 {
217 case Qgis::LayerType::Vector:
218 return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
219 case Qgis::LayerType::Raster:
220 return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
221 case Qgis::LayerType::Plugin:
222 case Qgis::LayerType::Group:
223 return true;
224 case Qgis::LayerType::Mesh:
225 return !canUseLayer( qobject_cast< QgsMeshLayer * >( layer ) );
226 case Qgis::LayerType::VectorTile:
227 return !canUseLayer( qobject_cast< QgsVectorTileLayer * >( layer ) );
228 case Qgis::LayerType::TiledScene:
229 return !canUseLayer( qobject_cast< QgsTiledSceneLayer * >( layer ) );
230 case Qgis::LayerType::PointCloud:
231 return !canUseLayer( qobject_cast< QgsPointCloudLayer * >( layer ) );
232 case Qgis::LayerType::Annotation:
233 return !canUseLayer( qobject_cast< QgsAnnotationLayer * >( layer ) );
234 }
235 return true;
236 }
237 ),
238 layers.end()
239 );
240
241 auto isCompatibleType = [typeHint]( QgsMapLayer *l ) -> bool {
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( u"layerName"_s ).toString();
305
306 if ( !layerName.isEmpty() )
307 {
308 name = layerName;
309 }
310 else if ( const QString path = parts.value( u"path"_s ).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, u"ogr"_s, 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, u"gdal"_s, 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, u"mdal"_s, 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( u"pdal"_s );
462 if ( pdalProvider )
463 {
464 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, u"pdal"_s, 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( "main"_L1, 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( "main"_L1, 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( "ProjectCrs"_L1, 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( "gdal"_L1, Qt::CaseInsensitive ) == 0 || provider.compare( "ogr"_L1, Qt::CaseInsensitive ) == 0 || provider.compare( "mdal"_L1, Qt::CaseInsensitive ) == 0 )
763 return source;
764
765 return u"%1://%2"_s.arg( provider, source );
766 }
767
768 if ( layer->type() == Qgis::LayerType::Annotation && layerName.compare( "main"_L1, Qt::CaseInsensitive ) == 0 )
769 {
770 return layerName;
771 }
772
773 return layer->id();
774}
775
776QString QgsProcessingUtils::variantToPythonLiteral( const QVariant &value )
777{
778 if ( !value.isValid() )
779 return u"None"_s;
780
781 if ( value.userType() == qMetaTypeId<QgsProperty>() )
782 return u"QgsProperty.fromExpression('%1')"_s.arg( value.value< QgsProperty >().asExpression() );
783 else if ( value.userType() == qMetaTypeId<QgsCoordinateReferenceSystem>() )
784 {
785 if ( !value.value< QgsCoordinateReferenceSystem >().isValid() )
786 return u"QgsCoordinateReferenceSystem()"_s;
787 else
788 return u"QgsCoordinateReferenceSystem('%1')"_s.arg( value.value< QgsCoordinateReferenceSystem >().authid() );
789 }
790 else if ( value.userType() == qMetaTypeId<QgsRectangle>() )
791 {
792 QgsRectangle r = value.value<QgsRectangle>();
793 return u"'%1, %3, %2, %4'"_s.arg( qgsDoubleToString( r.xMinimum() ), qgsDoubleToString( r.yMinimum() ), qgsDoubleToString( r.xMaximum() ), qgsDoubleToString( r.yMaximum() ) );
794 }
795 else if ( value.userType() == qMetaTypeId<QgsReferencedRectangle>() )
796 {
798 return u"'%1, %3, %2, %4 [%5]'"_s.arg( qgsDoubleToString( r.xMinimum() ), qgsDoubleToString( r.yMinimum() ), qgsDoubleToString( r.xMaximum() ), qgsDoubleToString( r.yMaximum() ), r.crs().authid() );
799 }
800 else if ( value.userType() == qMetaTypeId<QgsPointXY>() )
801 {
802 QgsPointXY r = value.value<QgsPointXY>();
803 return u"'%1,%2'"_s.arg( qgsDoubleToString( r.x() ), qgsDoubleToString( r.y() ) );
804 }
805 else if ( value.userType() == qMetaTypeId<QgsReferencedPointXY>() )
806 {
808 return u"'%1,%2 [%3]'"_s.arg( qgsDoubleToString( r.x() ), qgsDoubleToString( r.y() ), r.crs().authid() );
809 }
810
811 switch ( value.userType() )
812 {
813 case QMetaType::Type::Bool:
814 return value.toBool() ? u"True"_s : u"False"_s;
815
816 case QMetaType::Type::Double:
817 return QString::number( value.toDouble() );
818
819 case QMetaType::Type::Int:
820 case QMetaType::Type::UInt:
821 return QString::number( value.toInt() );
822
823 case QMetaType::Type::LongLong:
824 case QMetaType::Type::ULongLong:
825 return QString::number( value.toLongLong() );
826
827 case QMetaType::Type::QVariantList:
828 {
829 QStringList parts;
830 const QVariantList vl = value.toList();
831 for ( const QVariant &v : vl )
832 {
833 parts << variantToPythonLiteral( v );
834 }
835 return parts.join( ',' ).prepend( '[' ).append( ']' );
836 }
837
838 case QMetaType::Type::QVariantMap:
839 {
840 const QVariantMap map = value.toMap();
841 QStringList parts;
842 parts.reserve( map.size() );
843 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
844 {
845 parts << u"%1: %2"_s.arg( stringToPythonLiteral( it.key() ), variantToPythonLiteral( it.value() ) );
846 }
847 return parts.join( ',' ).prepend( '{' ).append( '}' );
848 }
849
850 case QMetaType::Type::QDateTime:
851 {
852 const QDateTime dateTime = value.toDateTime();
853 return u"QDateTime(QDate(%1, %2, %3), QTime(%4, %5, %6))"_s.arg( dateTime.date().year() )
854 .arg( dateTime.date().month() )
855 .arg( dateTime.date().day() )
856 .arg( dateTime.time().hour() )
857 .arg( dateTime.time().minute() )
858 .arg( dateTime.time().second() );
859 }
860
861 default:
862 break;
863 }
864
865 return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
866}
867
868QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
869{
870 QString s = string;
871 s.replace( '\\', "\\\\"_L1 );
872 s.replace( '\n', "\\n"_L1 );
873 s.replace( '\r', "\\r"_L1 );
874 s.replace( '\t', "\\t"_L1 );
875
876 if ( s.contains( '\'' ) && !s.contains( '\"' ) )
877 {
878 s = s.prepend( '"' ).append( '"' );
879 }
880 else
881 {
882 s.replace( '\'', "\\\'"_L1 );
883 s = s.prepend( '\'' ).append( '\'' );
884 }
885 return s;
886}
887
888void QgsProcessingUtils::parseDestinationString(
889 QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter, QString &extension
890)
891{
892 extension.clear();
893 bool matched = decodeProviderKeyAndUri( destination, providerKey, uri );
894
895 if ( !matched )
896 {
897 const thread_local QRegularExpression splitRx( u"^(.{3,}?):(.*)$"_s );
898 QRegularExpressionMatch match = splitRx.match( destination );
899 if ( match.hasMatch() )
900 {
901 providerKey = match.captured( 1 );
902 uri = match.captured( 2 );
903 matched = true;
904 }
905 }
906
907 if ( matched )
908 {
909 if ( providerKey == "postgis"_L1 ) // older processing used "postgis" instead of "postgres"
910 {
911 providerKey = u"postgres"_s;
912 }
913 if ( providerKey == "ogr"_L1 )
914 {
915 QgsDataSourceUri dsUri( uri );
916 if ( !dsUri.database().isEmpty() )
917 {
918 if ( !dsUri.table().isEmpty() )
919 {
920 layerName = dsUri.table();
921 options.insert( u"layerName"_s, layerName );
922 }
923 uri = dsUri.database();
924 extension = QFileInfo( uri ).completeSuffix();
925 format = QgsVectorFileWriter::driverForExtension( extension );
926 options.insert( u"driverName"_s, format );
927 }
928 else
929 {
930 extension = QFileInfo( uri ).completeSuffix();
931 options.insert( u"driverName"_s, QgsVectorFileWriter::driverForExtension( extension ) );
932 }
933 options.insert( u"update"_s, true );
934 }
935 useWriter = false;
936 }
937 else
938 {
939 useWriter = true;
940 providerKey = u"ogr"_s;
941
942 const thread_local QRegularExpression splitRx( u"^(.*)\\.(.*?)$"_s );
943 QRegularExpressionMatch match = splitRx.match( destination );
944 if ( match.hasMatch() )
945 {
946 extension = match.captured( 2 );
947 format = QgsVectorFileWriter::driverForExtension( extension );
948 }
949
950 if ( format.isEmpty() )
951 {
952 format = u"GPKG"_s;
953 destination = destination + u".gpkg"_s;
954 }
955
956 options.insert( u"driverName"_s, format );
957 uri = destination;
958 }
959}
960
962 QString &destination,
963 QgsProcessingContext &context,
964 const QgsFields &fields,
965 Qgis::WkbType geometryType,
967 const QVariantMap &createOptions,
968 const QStringList &datasourceOptions,
969 const QStringList &layerOptions,
971 QgsRemappingSinkDefinition *remappingDefinition
972)
973{
974 QVariantMap options = createOptions;
975 if ( !options.contains( u"fileEncoding"_s ) )
976 {
977 // no destination encoding specified, use default
978 options.insert( u"fileEncoding"_s, context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding() );
979 }
980
981 if ( destination.isEmpty() || destination.startsWith( "memory:"_L1 ) )
982 {
983 // strip "memory:" from start of destination
984 if ( destination.startsWith( "memory:"_L1 ) )
985 destination = destination.mid( 7 );
986
987 if ( destination.isEmpty() )
988 destination = u"output"_s;
989
990 // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
991 std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs, false ) );
992 if ( !layer || !layer->isValid() )
993 {
994 throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
995 }
996
997 if ( QgsProcessingFeedback *feedback = context.feedback() )
998 {
999 for ( const QgsField &field : fields )
1000 {
1001 // TODO -- support these!
1002 if ( !field.alias().isEmpty() )
1003 feedback->pushWarning( QObject::tr( "%1: Aliases are not compatible with scratch layers" ).arg( field.name() ) );
1004 if ( !field.alias().isEmpty() )
1005 feedback->pushWarning( QObject::tr( "%1: Comments are not compatible with scratch layers" ).arg( field.name() ) );
1006 }
1007 }
1008
1009 layer->setCustomProperty( u"OnConvertFormatRegeneratePrimaryKey"_s, static_cast< bool >( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey ) );
1010
1011 // update destination to layer ID
1012 destination = layer->id();
1013
1014 // this is a factory, so we need to return a proxy
1015 auto sink = std::make_unique<QgsProcessingFeatureSink>( layer->dataProvider(), destination, context );
1016 context.temporaryLayerStore()->addMapLayer( layer.release() );
1017
1018 return sink.release();
1019 }
1020 else
1021 {
1022 QString providerKey;
1023 QString uri;
1024 QString layerName;
1025 QString format;
1026 QString extension;
1027 bool useWriter = false;
1028 parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1029
1030 QgsFields newFields = fields;
1031 if ( useWriter && providerKey == "ogr"_L1 )
1032 {
1033 // use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
1034 // us to use any OGR format which supports feature addition
1035 QString finalFileName;
1036 QString finalLayerName;
1038 saveOptions.fileEncoding = options.value( u"fileEncoding"_s ).toString();
1039 saveOptions.layerName = !layerName.isEmpty() ? layerName : options.value( u"layerName"_s ).toString();
1040 saveOptions.driverName = format;
1041 saveOptions.datasourceOptions = !datasourceOptions.isEmpty() ? datasourceOptions : QgsVectorFileWriter::defaultDatasetOptions( format );
1042 saveOptions.layerOptions = !layerOptions.isEmpty() ? layerOptions : QgsVectorFileWriter::defaultLayerOptions( format );
1044 if ( remappingDefinition )
1045 {
1047 // sniff destination file to get correct wkb type and crs
1048 auto vl = std::make_unique< QgsVectorLayer >( destination );
1049 if ( vl->isValid() )
1050 {
1051 remappingDefinition->setDestinationWkbType( vl->wkbType() );
1052 remappingDefinition->setDestinationCrs( vl->crs() );
1053 newFields = vl->fields();
1054 remappingDefinition->setDestinationFields( newFields );
1055 }
1056 context.expressionContext().setFields( fields );
1057 }
1058 else
1059 {
1061 }
1062 std::unique_ptr< QgsVectorFileWriter > writer(
1063 QgsVectorFileWriter::create( destination, newFields, geometryType, crs, context.transformContext(), saveOptions, sinkFlags, &finalFileName, &finalLayerName )
1064 );
1065 if ( writer->hasError() )
1066 {
1067 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
1068 }
1069
1070 if ( QgsProcessingFeedback *feedback = context.feedback() )
1071 {
1072 for ( const QgsField &field : fields )
1073 {
1074 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
1075 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1076 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
1077 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
1078 }
1079 }
1080
1081 destination = finalFileName;
1082 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
1083 destination += u"|layername=%1"_s.arg( finalLayerName );
1084
1085 if ( remappingDefinition )
1086 {
1087 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, writer.release(), true );
1088 remapSink->setExpressionContext( context.expressionContext() );
1089 remapSink->setTransformContext( context.transformContext() );
1090 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1091 }
1092 else
1093 return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
1094 }
1095 else
1096 {
1097 const QgsVectorLayer::LayerOptions layerOptions { context.transformContext() };
1098 if ( remappingDefinition )
1099 {
1100 //write to existing layer
1101
1102 // use destination string as layer name (eg "postgis:..." )
1103 if ( !layerName.isEmpty() )
1104 {
1105 QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( providerKey, uri );
1106 parts.insert( u"layerName"_s, layerName );
1107 uri = QgsProviderRegistry::instance()->encodeUri( providerKey, parts );
1108 }
1109
1110 auto layer = std::make_unique<QgsVectorLayer>( uri, destination, providerKey, layerOptions );
1111 // update destination to layer ID
1112 destination = layer->id();
1113 if ( layer->isValid() )
1114 {
1115 remappingDefinition->setDestinationWkbType( layer->wkbType() );
1116 remappingDefinition->setDestinationCrs( layer->crs() );
1117 remappingDefinition->setDestinationFields( layer->fields() );
1118 }
1119
1120 if ( QgsProcessingFeedback *feedback = context.feedback() )
1121 {
1124 for ( const QgsField &field : fields )
1125 {
1126 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1127 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1128 if ( !field.alias().isEmpty() && !( capabilities & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1129 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1130 }
1131 }
1132
1133 auto remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, layer->dataProvider(), false );
1134 context.temporaryLayerStore()->addMapLayer( layer.release() );
1135 remapSink->setExpressionContext( context.expressionContext() );
1136 remapSink->setTransformContext( context.transformContext() );
1137 context.expressionContext().setFields( fields );
1138 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
1139 }
1140 else
1141 {
1142 //create empty layer
1143 auto exporter = std::make_unique<QgsVectorLayerExporter>( uri, providerKey, newFields, geometryType, crs, true, options, sinkFlags );
1144 if ( exporter->errorCode() != Qgis::VectorExportResult::Success )
1145 {
1146 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
1147 }
1148
1149 // use destination string as layer name (eg "postgis:..." )
1150 if ( !layerName.isEmpty() )
1151 {
1152 uri += u"|layername=%1"_s.arg( layerName );
1153 // update destination to generated URI
1154 destination = uri;
1155 }
1156
1157 if ( QgsProcessingFeedback *feedback = context.feedback() )
1158 {
1159 for ( const QgsField &field : fields )
1160 {
1161 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditAlias ) )
1162 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1163 if ( !field.alias().isEmpty() && !( exporter->attributeEditCapabilities() & Qgis::VectorDataProviderAttributeEditCapability::EditComment ) )
1164 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by the %2 provider" ).arg( field.name(), providerKey ) );
1165 }
1166 }
1167
1168 return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
1169 }
1170 }
1171 }
1172}
1173
1175 QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options
1176)
1177{
1178 *sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
1179}
1180
1181
1183{
1184 QgsRectangle extent;
1185 for ( const QgsMapLayer *layer : layers )
1186 {
1187 if ( !layer )
1188 continue;
1189
1190 if ( crs.isValid() )
1191 {
1192 //transform layer extent to target CRS
1193 QgsCoordinateTransform ct( layer->crs(), crs, context.transformContext() );
1195 try
1196 {
1197 QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
1198 extent.combineExtentWith( reprojExtent );
1199 }
1200 catch ( QgsCsException & )
1201 {
1202 // can't reproject... what to do here? hmmm?
1203 // let's ignore this layer for now, but maybe we should just use the original extent?
1204 }
1205 }
1206 else
1207 {
1208 extent.combineExtentWith( layer->extent() );
1209 }
1210 }
1211 return extent;
1212}
1213
1214// Deprecated
1216{
1217 QgsProcessingContext context;
1218 return QgsProcessingUtils::combineLayerExtents( layers, crs, context );
1219}
1220
1221QVariant QgsProcessingUtils::generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context )
1222{
1223 if ( !input.isValid() )
1224 return u"memory:%1"_s.arg( id.toString() );
1225
1226 if ( input.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1227 {
1228 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( input );
1229 QVariant newSink = generateIteratingDestination( fromVar.sink, id, context );
1230 fromVar.sink = QgsProperty::fromValue( newSink );
1231 return fromVar;
1232 }
1233 else if ( input.userType() == qMetaTypeId<QgsProperty>() )
1234 {
1235 QString res = input.value< QgsProperty>().valueAsString( context.expressionContext() );
1236 return generateIteratingDestination( res, id, context );
1237 }
1238 else
1239 {
1240 QString res = input.toString();
1242 {
1243 // temporary outputs map to temporary outputs!
1245 }
1246 else if ( res.startsWith( "memory:"_L1 ) )
1247 {
1248 return QString( res + '_' + id.toString() );
1249 }
1250 else
1251 {
1252 // assume a filename type output for now
1253 // TODO - uris?
1254 int lastIndex = res.lastIndexOf( '.' );
1255 return lastIndex >= 0 ? QString( res.left( lastIndex ) + '_' + id.toString() + res.mid( lastIndex ) ) : QString( res + '_' + id.toString() );
1256 }
1257 }
1258}
1259
1261{
1262 // we maintain a list of temporary folders -- this allows us to append additional
1263 // folders when a setting change causes the base temp folder to change, while deferring
1264 // cleanup of ALL these temp folders until session end (we can't cleanup older folders immediately,
1265 // because we don't know whether they have data in them which is still wanted)
1266 static std::vector< std::unique_ptr< QTemporaryDir > > sTempFolders;
1267 static QString sFolder;
1268 static QMutex sMutex;
1269 QMutexLocker locker( &sMutex );
1270 QString basePath;
1271
1272 if ( context )
1273 basePath = context->temporaryFolder();
1274 if ( basePath.isEmpty() )
1275 basePath = QgsProcessing::settingsTempPath->value();
1276
1277 if ( basePath.isEmpty() )
1278 {
1279 // default setting -- automatically create a temp folder
1280 if ( sTempFolders.empty() )
1281 {
1282 const QString templatePath = u"%1/processing_XXXXXX"_s.arg( QDir::tempPath() );
1283 auto tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1284 sFolder = tempFolder->path();
1285 sTempFolders.emplace_back( std::move( tempFolder ) );
1286 }
1287 }
1288 else if ( sFolder.isEmpty() || !sFolder.startsWith( basePath ) || sTempFolders.empty() )
1289 {
1290 if ( !QDir().exists( basePath ) )
1291 QDir().mkpath( basePath );
1292
1293 const QString templatePath = u"%1/processing_XXXXXX"_s.arg( basePath );
1294 auto tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1295 sFolder = tempFolder->path();
1296 sTempFolders.emplace_back( std::move( tempFolder ) );
1297 }
1298 return sFolder;
1299}
1300
1301QString QgsProcessingUtils::generateTempFilename( const QString &basename, const QgsProcessingContext *context )
1302{
1303 QString subPath = QUuid::createUuid().toString().remove( '-' ).remove( '{' ).remove( '}' );
1304 QString path = tempFolder( context ) + '/' + subPath;
1305 if ( !QDir( path ).exists() ) //make sure the directory exists - it shouldn't, but lets be safe...
1306 {
1307 QDir tmpDir;
1308 tmpDir.mkdir( path );
1309 }
1310 return path + '/' + QgsFileUtils::stringToSafeFilename( basename );
1311}
1312
1314{
1315 auto getText = [map]( const QString &key ) -> QString {
1316 if ( map.contains( key ) )
1317 return map.value( key ).toString();
1318 return QString();
1319 };
1320
1321 QString s;
1322 s += u"<html><body><p>"_s + getText( u"ALG_DESC"_s ) + u"</p>\n"_s;
1323
1324 QString inputs;
1325 const auto parameterDefinitions = algorithm->parameterDefinitions();
1326 for ( const QgsProcessingParameterDefinition *def : parameterDefinitions )
1327 {
1328 if ( def->flags() & Qgis::ProcessingParameterFlag::Hidden || def->isDestination() )
1329 continue;
1330
1331 if ( !getText( def->name() ).isEmpty() )
1332 {
1333 inputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1334 inputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1335 }
1336 }
1337 if ( !inputs.isEmpty() )
1338 s += u"<h2>"_s + QObject::tr( "Input parameters" ) + u"</h2>\n"_s + inputs;
1339
1340 QString outputs;
1341 const auto outputDefinitions = algorithm->outputDefinitions();
1342 for ( const QgsProcessingOutputDefinition *def : outputDefinitions )
1343 {
1344 if ( !getText( def->name() ).isEmpty() )
1345 {
1346 outputs += u"<h3>"_s + def->description() + u"</h3>\n"_s;
1347 outputs += u"<p>"_s + getText( def->name() ) + u"</p>\n"_s;
1348 }
1349 }
1350 if ( !outputs.isEmpty() )
1351 s += u"<h2>"_s + QObject::tr( "Outputs" ) + u"</h2>\n"_s + outputs;
1352
1353 if ( !map.value( u"EXAMPLES"_s ).toString().isEmpty() )
1354 s += u"<h2>%1</h2>\n<p>%2</p>"_s.arg( QObject::tr( "Examples" ), getText( u"EXAMPLES"_s ) );
1355
1356 s += "<br>"_L1;
1357 if ( !map.value( u"ALG_CREATOR"_s ).toString().isEmpty() )
1358 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm author:" ) + u" "_s + getText( u"ALG_CREATOR"_s ) + u"</p>"_s;
1359 if ( !map.value( u"ALG_HELP_CREATOR"_s ).toString().isEmpty() )
1360 s += u"<p align=\"right\">"_s + QObject::tr( "Help author:" ) + u" "_s + getText( u"ALG_HELP_CREATOR"_s ) + u"</p>"_s;
1361 if ( !map.value( u"ALG_VERSION"_s ).toString().isEmpty() )
1362 s += u"<p align=\"right\">"_s + QObject::tr( "Algorithm version:" ) + u" "_s + getText( u"ALG_VERSION"_s ) + u"</p>"_s;
1363
1364 s += "</body></html>"_L1;
1365 return s;
1366}
1367
1369{
1370 int index = 0;
1371 for ( const QgsProcessingParameterDefinition *def : algorithm->parameterDefinitions() )
1372 {
1373 if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 )
1374 return index;
1375 index++;
1376 }
1377 return -1;
1378}
1379
1381{
1382 int index = 0;
1383 for ( const QgsProcessingOutputDefinition *def : algorithm->outputDefinitions() )
1384 {
1385 if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 )
1386 return index;
1387 index++;
1388 }
1389 return -1;
1390}
1391
1393 const QgsVectorLayer *vl,
1394 bool selectedFeaturesOnly,
1395 const QString &baseName,
1396 const QStringList &compatibleFormats,
1397 const QString &preferredFormat,
1398 QgsProcessingContext &context,
1399 QgsProcessingFeedback *feedback,
1400 QString *layerName,
1401 long long featureLimit,
1402 const QString &filterExpression,
1403 bool renameFid
1404)
1405{
1406 bool requiresTranslation = false;
1407
1408 // if we are only looking for selected features then we have to export back to disk,
1409 // as we need to subset only selected features, a concept which doesn't exist outside QGIS!
1410 requiresTranslation = requiresTranslation || selectedFeaturesOnly;
1411
1412 // if we are limiting the feature count, we better export
1413 requiresTranslation = requiresTranslation || featureLimit != -1 || !filterExpression.isEmpty();
1414
1415 // if the data provider is NOT ogr, then we HAVE to convert. Otherwise we run into
1416 // issues with data providers like spatialite, delimited text where the format can be
1417 // opened outside of QGIS, but with potentially very different behavior!
1418 requiresTranslation = requiresTranslation || vl->providerType() != "ogr"_L1;
1419
1420 // if the layer has a feature filter set, then we HAVE to convert. Feature filters are
1421 // a purely QGIS concept.
1422 requiresTranslation = requiresTranslation || !vl->subsetString().isEmpty();
1423
1424 // if the layer opened using GDAL's virtual I/O mechanism (/vsizip/, etc.), then
1425 // we HAVE to convert as other tools may not work with it
1426 requiresTranslation = requiresTranslation || vl->source().startsWith( "/vsi"_L1 );
1427
1428 // Check if layer is a disk based format and if so if the layer's path has a compatible filename suffix
1429 QString diskPath;
1430 if ( !requiresTranslation )
1431 {
1432 const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( vl->providerType(), vl->source() );
1433 if ( parts.contains( u"path"_s ) )
1434 {
1435 diskPath = parts.value( u"path"_s ).toString();
1436 QFileInfo fi( diskPath );
1437 requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
1438
1439 // if the layer name doesn't match the filename, we need to convert the layer. This method can only return
1440 // a filename, and cannot handle layernames as well as file paths
1441 const QString srcLayerName = parts.value( u"layerName"_s ).toString();
1442 if ( layerName )
1443 {
1444 // differing layer names are acceptable
1445 *layerName = srcLayerName;
1446 }
1447 else
1448 {
1449 // differing layer names are NOT acceptable
1450 requiresTranslation = requiresTranslation || ( !srcLayerName.isEmpty() && srcLayerName != fi.baseName() );
1451 }
1452 }
1453 else
1454 {
1455 requiresTranslation = true; // not a disk-based format
1456 }
1457 }
1458
1459 if ( requiresTranslation )
1460 {
1461 QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat, &context );
1462
1464 saveOptions.fileEncoding = context.defaultEncoding();
1465 saveOptions.driverName = QgsVectorFileWriter::driverForExtension( preferredFormat );
1466 QgsFields fields = vl->fields();
1467 if ( renameFid )
1468 {
1469 const int fidIndex = fields.lookupField( u"fid"_s );
1470 if ( fidIndex >= 0 )
1471 fields.rename( fidIndex, u"OLD_FID"_s );
1472 }
1473 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( temp, fields, vl->wkbType(), vl->crs(), context.transformContext(), saveOptions ) );
1474 QgsFeature f;
1476 QgsFeatureRequest request;
1477 if ( featureLimit != -1 )
1478 {
1479 request.setLimit( featureLimit );
1480 }
1481 if ( !filterExpression.isEmpty() )
1482 {
1483 request.setFilterExpression( filterExpression );
1484 }
1485
1486 if ( selectedFeaturesOnly )
1487 it = vl->getSelectedFeatures( std::move( request ) );
1488 else
1489 it = vl->getFeatures( request );
1490
1491 constexpr int maxErrors { 10 };
1492 unsigned long errorCounter { 0 };
1493 while ( it.nextFeature( f ) )
1494 {
1495 if ( feedback && feedback->isCanceled() )
1496 return QString();
1497
1498 if ( !writer->addFeature( f, QgsFeatureSink::FastInsert ) && feedback )
1499 {
1500 const QString errorMessage = writer->errorMessage();
1501 if ( !renameFid && saveOptions.driverName == "GPKG"_L1 && errorMessage.contains( "fid", Qt::CaseInsensitive ) )
1502 {
1503 // try again, dropping the FID field
1504 feedback->reportError( QObject::tr( "Cannot store existing FID values in temporary GeoPackage layer, these will be moved to \"OLD_FID\" instead." ), false );
1505 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, layerName, featureLimit, filterExpression, true );
1506 }
1507
1508 QString errorText;
1509 if ( errorCounter++ < maxErrors )
1510 {
1511 errorText = QObject::tr( "Error writing feature # %1 to output layer: %2" ).arg( QString::number( f.id() ), errorMessage );
1512
1513 feedback->reportError( errorText );
1514 }
1515 }
1516 }
1517 if ( errorCounter >= maxErrors )
1518 {
1519 feedback->reportError( QObject::tr( "There were %1 errors writing features, only the first %2 have been reported." ).arg( QString::number( errorCounter ), QString::number( maxErrors ) ) );
1520 }
1521 return temp;
1522 }
1523 else
1524 {
1525 return diskPath;
1526 }
1527}
1528
1530 const QgsVectorLayer *vl,
1531 bool selectedFeaturesOnly,
1532 const QString &baseName,
1533 const QStringList &compatibleFormats,
1534 const QString &preferredFormat,
1535 QgsProcessingContext &context,
1536 QgsProcessingFeedback *feedback,
1537 long long featureLimit,
1538 const QString &filterExpression
1539)
1540{
1541 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, nullptr, featureLimit, filterExpression, false );
1542}
1543
1545 const QgsVectorLayer *layer,
1546 bool selectedFeaturesOnly,
1547 const QString &baseName,
1548 const QStringList &compatibleFormats,
1549 const QString &preferredFormat,
1550 QgsProcessingContext &context,
1551 QgsProcessingFeedback *feedback,
1552 QString &layerName,
1553 long long featureLimit,
1554 const QString &filterExpression
1555)
1556{
1557 layerName.clear();
1558 return convertToCompatibleFormatInternal( layer, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, &layerName, featureLimit, filterExpression, false );
1559}
1560
1561QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix )
1562{
1563 QgsFields outFields = fieldsA;
1564 QSet< QString > usedNames;
1565 for ( const QgsField &f : fieldsA )
1566 {
1567 usedNames.insert( f.name().toLower() );
1568 }
1569
1570 for ( const QgsField &f : fieldsB )
1571 {
1572 QgsField newField = f;
1573 newField.setName( fieldsBPrefix + f.name() );
1574 if ( usedNames.contains( newField.name().toLower() ) )
1575 {
1576 int idx = 2;
1577 QString newName = newField.name() + '_' + QString::number( idx );
1578 while ( usedNames.contains( newName.toLower() ) || fieldsB.indexOf( newName ) != -1 )
1579 {
1580 idx++;
1581 newName = newField.name() + '_' + QString::number( idx );
1582 }
1583 newField.setName( newName );
1584 outFields.append( newField );
1585 }
1586 else
1587 {
1588 outFields.append( newField );
1589 }
1590 usedNames.insert( newField.name() );
1591 }
1592
1593 return outFields;
1594}
1595
1596QList<int> QgsProcessingUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
1597{
1598 QList<int> indices;
1599 if ( !fieldNames.isEmpty() )
1600 {
1601 indices.reserve( fieldNames.count() );
1602 for ( const QString &f : fieldNames )
1603 {
1604 int idx = fields.lookupField( f );
1605 if ( idx >= 0 )
1606 indices.append( idx );
1607 }
1608 }
1609 else
1610 {
1611 indices.reserve( fields.count() );
1612 for ( int i = 0; i < fields.count(); ++i )
1613 indices.append( i );
1614 }
1615 return indices;
1616}
1617
1618QgsFields QgsProcessingUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
1619{
1620 QgsFields fieldsSubset;
1621 for ( int i : indices )
1622 fieldsSubset.append( fields.at( i ) );
1623 return fieldsSubset;
1624}
1625
1627{
1628 QString setting = QgsProcessing::settingsDefaultOutputVectorLayerExt->value().trimmed();
1629 if ( setting.isEmpty() )
1630 return u"gpkg"_s;
1631
1632 if ( setting.startsWith( '.' ) )
1633 setting = setting.mid( 1 );
1634
1635 const QStringList supportedFormats = QgsVectorFileWriter::supportedFormatExtensions();
1636 if ( !supportedFormats.contains( setting, Qt::CaseInsensitive ) )
1637 return u"gpkg"_s;
1638
1639 return setting;
1640}
1641
1643{
1644 QString setting = QgsProcessing::settingsDefaultOutputRasterLayerFormat->value().trimmed();
1645 if ( setting.isEmpty() )
1646 return u"GTiff"_s;
1647
1648 const QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersFormats = QgsRasterFileWriter::supportedFiltersAndFormats();
1649 for ( const QgsRasterFileWriter::FilterFormatDetails &detail : std::as_const( supportedFiltersFormats ) )
1650 {
1651 if ( detail.driverName.compare( setting, Qt::CaseInsensitive ) == 0 )
1652 return detail.driverName;
1653 }
1654
1655 return u"GTiff"_s;
1656}
1657
1659{
1660 QString format = defaultRasterFormat();
1661 QStringList extensions = QgsRasterFileWriter::extensionsForFormat( format );
1662 if ( !extensions.isEmpty() )
1663 return extensions[0];
1664
1665 return u"tif"_s;
1666}
1667
1669{
1670 return u"las"_s;
1671}
1672
1674{
1675 return u"mbtiles"_s;
1676}
1677
1678QVariantMap QgsProcessingUtils::removePointerValuesFromMap( const QVariantMap &map )
1679{
1680 auto layerPointerToString = []( QgsMapLayer *layer ) -> QString {
1681 if ( layer && layer->providerType() == "memory"_L1 )
1682 return layer->id();
1683 else if ( layer )
1684 return layer->source();
1685 else
1686 return QString();
1687 };
1688
1689 auto cleanPointerValues = [&layerPointerToString]( const QVariant &value ) -> QVariant {
1690 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( value.value< QObject * >() ) )
1691 {
1692 // don't store pointers in maps for long-term storage
1693 return layerPointerToString( layer );
1694 }
1695 else if ( value.userType() == QMetaType::type( "QPointer< QgsMapLayer >" ) )
1696 {
1697 // don't store pointers in maps for long-term storage
1698 return layerPointerToString( value.value< QPointer< QgsMapLayer > >().data() );
1699 }
1700 else
1701 {
1702 return value;
1703 }
1704 };
1705
1706 QVariantMap res;
1707 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
1708 {
1709 if ( it->userType() == QMetaType::Type::QVariantMap )
1710 {
1711 res.insert( it.key(), removePointerValuesFromMap( it.value().toMap() ) );
1712 }
1713 else if ( it->userType() == QMetaType::Type::QVariantList )
1714 {
1715 QVariantList dest;
1716 const QVariantList source = it.value().toList();
1717 dest.reserve( source.size() );
1718 for ( const QVariant &v : source )
1719 {
1720 dest.append( cleanPointerValues( v ) );
1721 }
1722 res.insert( it.key(), dest );
1723 }
1724 else
1725 {
1726 res.insert( it.key(), cleanPointerValues( it.value() ) );
1727 }
1728 }
1729 return res;
1730}
1731
1732QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantMap &parameters, bool &ok, QString &error )
1733{
1734 QVariantMap output;
1735 ok = true;
1736 for ( auto it = parameters.constBegin(); it != parameters.constEnd(); ++it )
1737 {
1738 if ( it.value().userType() == QMetaType::Type::QVariantMap )
1739 {
1740 const QVariantMap value = it.value().toMap();
1741 if ( value.value( u"type"_s ).toString() == "data_defined"_L1 )
1742 {
1743 const QString expression = value.value( u"expression"_s ).toString();
1744 const QString field = value.value( u"field"_s ).toString();
1745 if ( !expression.isEmpty() )
1746 {
1747 output.insert( it.key(), QgsProperty::fromExpression( expression ) );
1748 }
1749 else if ( !field.isEmpty() )
1750 {
1751 output.insert( it.key(), QgsProperty::fromField( field ) );
1752 }
1753 else
1754 {
1755 ok = false;
1756 error = QObject::tr( "Invalid data defined parameter for %1, requires 'expression' or 'field' values." ).arg( it.key() );
1757 }
1758 }
1759 else
1760 {
1761 output.insert( it.key(), it.value() );
1762 }
1763 }
1764 else if ( it.value().userType() == QMetaType::Type::QString )
1765 {
1766 const QString stringValue = it.value().toString();
1767
1768 if ( stringValue.startsWith( "field:"_L1 ) )
1769 {
1770 output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) );
1771 }
1772 else if ( stringValue.startsWith( "expression:"_L1 ) )
1773 {
1774 output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) );
1775 }
1776 else
1777 {
1778 output.insert( it.key(), it.value() );
1779 }
1780 }
1781 else
1782 {
1783 output.insert( it.key(), it.value() );
1784 }
1785 }
1786 return output;
1787}
1788
1789QString QgsProcessingUtils::resolveDefaultEncoding( const QString &defaultEncoding )
1790{
1791 if ( !QTextCodec::availableCodecs().contains( defaultEncoding.toLatin1() ) )
1792 {
1793 const QString systemCodec = QTextCodec::codecForLocale()->name();
1794 if ( !systemCodec.isEmpty() )
1795 {
1796 return systemCodec;
1797 }
1798 return QString( "UTF-8" );
1799 }
1800
1801 return defaultEncoding;
1802}
1803
1805{
1806 const QList<QByteArray> supportedFormats = QImageWriter::supportedImageFormats();
1807 QStringList formats;
1808 formats.reserve( supportedFormats.size() );
1809
1810 for ( const QByteArray &format : supportedFormats )
1811 {
1812 if ( format == "svg" )
1813 {
1814 continue;
1815 }
1816 formats.append( QString::fromUtf8( format ).toUpper() );
1817 }
1818
1819 std::sort( formats.begin(), formats.end(), []( const QString &a, const QString &b ) -> bool {
1820 if ( a == "PNG"_L1 )
1821 {
1822 return true;
1823 }
1824 if ( b == "PNG"_L1 )
1825 {
1826 return false;
1827 }
1828 return a.localeAwareCompare( b ) < 0;
1829 } );
1830
1831 return formats;
1832}
1833
1835{
1836 const QStringList formats = supportedImageFormats();
1837 QStringList fileFilters;
1838 fileFilters.reserve( formats.size() );
1839
1840 for ( const QString &format : formats )
1841 {
1842 const QString longName = format + QObject::tr( " format" );
1843 const QString glob = u"*."_s + format;
1844
1845 fileFilters.append( u"%1 (%2 %3)"_s.arg( longName, glob.toLower(), glob ) );
1846 }
1847
1848 return fileFilters.join( ";;"_L1 );
1849}
1850
1851//
1852// QgsProcessingFeatureSource
1853//
1854
1855QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource, long long featureLimit, const QString &filterExpression )
1856 : mSource( originalSource )
1857 , mOwnsSource( ownsOriginalSource )
1858 , mSourceCrs( mSource->sourceCrs() )
1859 , mSourceFields( mSource->fields() )
1860 , mSourceWkbType( mSource->wkbType() )
1861 , mSourceName( mSource->sourceName() )
1862 , mSourceExtent( mSource->sourceExtent() )
1863 , mSourceSpatialIndexPresence( mSource->hasSpatialIndex() )
1864 , mInvalidGeometryCheck(
1865 QgsWkbTypes::geometryType( mSource->wkbType() ) == Qgis::GeometryType::Point ? Qgis::InvalidGeometryCheck::NoCheck // never run geometry validity checks for point layers!
1866 : context.invalidGeometryCheck()
1867 )
1868 , mInvalidGeometryCallback( context.invalidGeometryCallback( originalSource ) )
1869 , mTransformErrorCallback( context.transformErrorCallback() )
1870 , mInvalidGeometryCallbackSkip( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::SkipInvalid, originalSource ) )
1871 , mInvalidGeometryCallbackAbort( context.defaultInvalidGeometryCallbackForCheck( Qgis::InvalidGeometryCheck::AbortOnInvalid, originalSource ) )
1872 , mFeatureLimit( featureLimit )
1873 , mFilterExpression( filterExpression )
1874{}
1875
1877{
1878 if ( mOwnsSource )
1879 delete mSource;
1880}
1881
1883{
1884 QgsFeatureRequest req( request );
1885 req.setTransformErrorCallback( mTransformErrorCallback );
1886
1889 else
1890 {
1891 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1892 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1893 }
1894
1895 if ( mFeatureLimit != -1 && req.limit() != -1 )
1896 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1897 else if ( mFeatureLimit != -1 )
1898 req.setLimit( mFeatureLimit );
1899
1900 if ( !mFilterExpression.isEmpty() )
1901 req.combineFilterExpression( mFilterExpression );
1902
1903 return mSource->getFeatures( req );
1904}
1905
1907{
1908 Qgis::FeatureAvailability sourceAvailability = mSource->hasFeatures();
1909 if ( sourceAvailability == Qgis::FeatureAvailability::NoFeaturesAvailable )
1910 return Qgis::FeatureAvailability::NoFeaturesAvailable; // never going to be features if underlying source has no features
1911 else if ( mInvalidGeometryCheck == Qgis::InvalidGeometryCheck::NoCheck && mFilterExpression.isEmpty() )
1912 return sourceAvailability;
1913 else
1914 // we don't know... source has features, but these may be filtered out by invalid geometry check or filter expression
1916}
1917
1919{
1920 QgsFeatureRequest req( request );
1921 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1922 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1923 req.setTransformErrorCallback( mTransformErrorCallback );
1924
1925 if ( mFeatureLimit != -1 && req.limit() != -1 )
1926 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1927 else if ( mFeatureLimit != -1 )
1928 req.setLimit( mFeatureLimit );
1929
1930 if ( !mFilterExpression.isEmpty() )
1931 req.combineFilterExpression( mFilterExpression );
1932
1933 return mSource->getFeatures( req );
1934}
1935
1940
1942{
1943 return mSourceFields;
1944}
1945
1947{
1948 return mSourceWkbType;
1949}
1950
1952{
1953 if ( !mFilterExpression.isEmpty() )
1954 return static_cast< int >( Qgis::FeatureCountState::UnknownCount );
1955
1956 if ( mFeatureLimit == -1 )
1957 return mSource->featureCount();
1958 else
1959 return std::min( mFeatureLimit, mSource->featureCount() );
1960}
1961
1963{
1964 return mSourceName;
1965}
1966
1967QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
1968{
1969 if ( mFilterExpression.isEmpty() )
1970 return mSource->uniqueValues( fieldIndex, limit );
1971
1972 // inefficient method when filter expression in use
1973 // TODO QGIS 5.0 -- add filter expression to virtual ::uniqueValues function
1974 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
1975 return QSet<QVariant>();
1976
1979 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
1980 req.setFilterExpression( mFilterExpression );
1981
1982 QSet<QVariant> values;
1983 QgsFeatureIterator it = getFeatures( req );
1984 QgsFeature f;
1985 while ( it.nextFeature( f ) )
1986 {
1987 values.insert( f.attribute( fieldIndex ) );
1988 if ( limit > 0 && values.size() >= limit )
1989 return values;
1990 }
1991 return values;
1992}
1993
1994QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
1995{
1996 if ( mFilterExpression.isEmpty() )
1997 return mSource->minimumValue( fieldIndex );
1998
1999 // inefficient method when filter expression in use
2000 // TODO QGIS 5.0 -- add filter expression to virtual ::minimumValue function
2001 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
2002 return QVariant();
2003
2006 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
2007
2008 QVariant min;
2009 QgsFeatureIterator it = getFeatures( req );
2010 QgsFeature f;
2011 while ( it.nextFeature( f ) )
2012 {
2013 const QVariant v = f.attribute( fieldIndex );
2014 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantLessThan( v, min ) || QgsVariantUtils::isNull( min ) ) )
2015 {
2016 min = v;
2017 }
2018 }
2019 return min;
2020}
2021
2022QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
2023{
2024 if ( mFilterExpression.isEmpty() )
2025 return mSource->maximumValue( fieldIndex );
2026
2027 // inefficient method when filter expression in use
2028 // TODO QGIS 5.0 -- add filter expression to virtual ::maximumValue function
2029 if ( fieldIndex < 0 || fieldIndex >= fields().count() )
2030 return QVariant();
2031
2034 req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );
2035
2036 QVariant max;
2037 QgsFeatureIterator it = getFeatures( req );
2038 QgsFeature f;
2039 while ( it.nextFeature( f ) )
2040 {
2041 const QVariant v = f.attribute( fieldIndex );
2042 if ( !QgsVariantUtils::isNull( v ) && ( qgsVariantGreaterThan( v, max ) || QgsVariantUtils::isNull( max ) ) )
2043 {
2044 max = v;
2045 }
2046 }
2047 return max;
2048}
2049
2051{
2052 return mSourceExtent;
2053}
2054
2056{
2057 if ( mFilterExpression.isEmpty() )
2058 return mSource->allFeatureIds();
2059
2060 QgsFeatureIterator fit = getFeatures( QgsFeatureRequest().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setNoAttributes().setFilterExpression( mFilterExpression ) );
2061
2062 QgsFeatureIds ids;
2063
2064 QgsFeature fet;
2065 while ( fit.nextFeature( fet ) )
2066 {
2067 ids << fet.id();
2068 }
2069
2070 return ids;
2071}
2072
2074{
2075 return mSourceSpatialIndexPresence;
2076}
2077
2079{
2080 QgsExpressionContextScope *expressionContextScope = nullptr;
2081 QgsExpressionContextScopeGenerator *generator = dynamic_cast<QgsExpressionContextScopeGenerator *>( mSource );
2082 if ( generator )
2083 {
2084 expressionContextScope = generator->createExpressionContextScope();
2085 }
2086 return expressionContextScope;
2087}
2088
2090{
2091 mInvalidGeometryCheck = method;
2092 switch ( mInvalidGeometryCheck )
2093 {
2095 mInvalidGeometryCallback = nullptr;
2096 break;
2097
2099 mInvalidGeometryCallback = mInvalidGeometryCallbackSkip;
2100 break;
2101
2103 mInvalidGeometryCallback = mInvalidGeometryCallbackAbort;
2104 break;
2105 }
2106}
2107
2109{
2110 return mInvalidGeometryCheck;
2111}
2112
2113
2114//
2115// QgsProcessingFeatureSink
2116//
2117QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
2118 : QgsProxyFeatureSink( originalSink )
2119 , mContext( context )
2120 , mSinkName( sinkName )
2121 , mOwnsSink( ownsOriginalSink )
2122{}
2123
2125{
2126 if ( !flushBuffer() && mContext.feedback() )
2127 {
2128 mContext.feedback()->reportError( lastError() );
2129 }
2130
2131 if ( mOwnsSink )
2132 {
2133 delete mSink;
2134 mSink = nullptr;
2135 }
2136}
2137
2139{
2140 if ( !flushBuffer() )
2141 {
2143 }
2144}
2145
2147{
2148 bool result = QgsProxyFeatureSink::addFeature( feature, flags );
2149 if ( !result && mContext.feedback() )
2150 {
2151 const QString error = lastError();
2152 if ( !error.isEmpty() )
2153 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1: %2" ).arg( mSinkName, error ) );
2154 else
2155 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
2156 }
2157 return result;
2158}
2159
2161{
2162 bool result = QgsProxyFeatureSink::addFeatures( features, flags );
2163 if ( !result && mContext.feedback() )
2164 {
2165 const QString error = lastError();
2166 if ( !error.isEmpty() )
2167 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1: %2", nullptr, features.count() ).arg( mSinkName, error ) );
2168 else
2169 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1", nullptr, features.count() ).arg( mSinkName ) );
2170 }
2171 return result;
2172}
2173
2175{
2176 bool result = QgsProxyFeatureSink::addFeatures( iterator, flags );
2177 if ( !result && mContext.feedback() )
2178 {
2179 const QString error = lastError();
2180 if ( !error.isEmpty() )
2181 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1: %2" ).arg( mSinkName, error ) );
2182 else
2183 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1" ).arg( mSinkName ) );
2184 }
2185 return result;
2186}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3720
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3714
@ VectorPoint
Vector point layers.
Definition qgis.h:3715
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3717
@ VectorLine
Vector line layers.
Definition qgis.h:3716
@ FieldComments
Writer can support field comments.
Definition qgis.h:1142
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1141
SpatialIndexPresence
Enumeration of spatial index presence states.
Definition qgis.h:585
@ Success
No errors were encountered.
Definition qgis.h:1120
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2329
@ Static
Static property.
Definition qgis.h:711
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
FeatureAvailability
Possible return value for QgsFeatureSource::hasFeatures() to determine if a source is empty.
Definition qgis.h:604
@ FeaturesMaybeAvailable
There may be features available in this source.
Definition qgis.h:607
@ NoFeaturesAvailable
There are certainly no features available in this source.
Definition qgis.h:605
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
@ EditComment
Allows editing comments.
Definition qgis.h:619
QFlags< VectorDataProviderAttributeEditCapability > VectorDataProviderAttributeEditCapabilities
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:629
InvalidGeometryCheck
Methods for handling of features with invalid geometries.
Definition qgis.h:2372
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2373
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2375
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2374
@ OverrideDefaultGeometryCheck
If set, the default geometry check method (as dictated by QgsProcessingContext) will be overridden fo...
Definition qgis.h:3869
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3895
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3948
@ NoSymbology
Export only data.
Definition qgis.h:6160
QFlags< ProcessingFeatureSourceFlag > ProcessingFeatureSourceFlags
Flags which control how QgsProcessingFeatureSource fetches features.
Definition qgis.h:3906
Represents a map layer containing a set of georeferenced annotations, e.g.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
QString table() const
Returns the table name stored in the URI.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
QString database() const
Returns the database name stored in the URI.
Abstract interface for generating an expression context scope.
virtual QgsExpressionContextScope * createExpressionContextScope() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
QgsFeatureRequest & combineFilterExpression(const QString &expression)
Modifies the existing filter expression to add an additional expression filter.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setInvalidGeometryCheck(Qgis::InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setInvalidGeometryCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering an invalid geometry and invalidGeometryCheck() is s...
QgsFeatureRequest & setTransformErrorCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering a transform error when iterating features and a des...
An interface for objects which accept features via addFeature(s) methods.
QFlags< SinkFlag > SinkFlags
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
QFlags< Flag > Flags
An interface for objects which provide features via a getFeatures method.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFeatureId id
Definition qgsfeature.h:63
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:224
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all layers by layer ID.
QgsMapLayer * addMapLayer(QgsMapLayer *layer, bool takeOwnership=true)
Add a layer to the store.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QString id
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:93
virtual void setTransformContext(const QgsCoordinateTransformContext &transformContext)=0
Sets the coordinate transform context to transformContext.
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Base class for plugin layers.
Represents a map layer supporting display of point clouds.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
QgsProcessingFeedback * feedback()
Returns the associated feedback object.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution.
QString temporaryFolder() const
Returns the (optional) temporary folder to use when running algorithms.
Custom exception class for processing related exceptions.
QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
void finalize() override
Finalizes the sink, flushing any buffered features to the destination.
QgsProcessingFeatureSink(QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink=false)
Constructor for QgsProcessingFeatureSink, accepting an original feature sink originalSink and process...
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
Encapsulates settings relating to a feature source input to a processing algorithm.
bool selectedFeaturesOnly
true if only selected features in the source should be used by algorithms.
Qgis::InvalidGeometryCheck geometryCheck
Geometry check method to apply to this source.
Qgis::ProcessingFeatureSourceDefinitionFlags flags
Flags which dictate source behavior.
long long featureLimit
If set to a value > 0, places a limit on the maximum number of features which will be read from the s...
QString filterExpression
Optional expression filter to use for filtering features which will be read from the source.
QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource,...
QgsRectangle sourceExtent() const override
Returns the extent of all geometries from the source.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Returns the set of unique values contained within the specified fieldIndex from this source.
QgsExpressionContextScope * createExpressionContextScope() const
Returns an expression context scope suitable for this source.
QgsProcessingFeatureSource(QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource=false, long long featureLimit=-1, const QString &filterExpression=QString())
Constructor for QgsProcessingFeatureSource, accepting an original feature source originalSource and p...
void setInvalidGeometryCheck(Qgis::InvalidGeometryCheck method)
Overrides the default geometry check method for the source.
Qgis::InvalidGeometryCheck invalidGeometryCheck() const
Returns the geometry check method for the source.
QVariant maximumValue(int fieldIndex) const override
Returns the maximum value for an attribute column or an invalid variant in case of error.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
Qgis::WkbType wkbType() const override
Returns the geometry type for features returned by this source.
QVariant minimumValue(int fieldIndex) const override
Returns the minimum value for an attribute column or an invalid variant in case of error.
long long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request, Qgis::ProcessingFeatureSourceFlags flags) const
Returns an iterator for the features in the source, respecting the supplied feature flags.
Qgis::FeatureAvailability hasFeatures() const override
Determines if there are any features available in the source.
QString sourceName() const override
Returns a friendly display name for the source.
QgsFeatureIds allFeatureIds() const override
Returns a list of all feature IDs for features present in the source.
Qgis::SpatialIndexPresence hasSpatialIndex() const override
Returns an enum value representing the presence of a valid spatial index on the source,...
QgsFields fields() const override
Returns the fields associated with features in the source.
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
Base class for the definition of processing outputs.
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm.
QgsProperty sink
Sink/layer definition.
Base class for the definition of processing parameters.
static QList< QgsTiledSceneLayer * > compatibleTiledSceneLayers(QgsProject *project, bool sort=true)
Returns a list of tiled scene layers from a project which are compatible with the processing framewor...
static QString stringToPythonLiteral(const QString &string)
Converts a string to a Python string literal.
static QString defaultVectorExtension()
Returns the default vector extension to use, in the absence of all other constraints (e....
static QVariant generateIteratingDestination(const QVariant &input, const QVariant &id, QgsProcessingContext &context)
Converts an input parameter value for use in source iterating mode, where one individual sink is crea...
static QgsFields indicesToFields(const QList< int > &indices, const QgsFields &fields)
Returns a subset of fields based on the indices of desired fields.
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
static QString layerToStringIdentifier(const QgsMapLayer *layer, const QString &layerName=QString())
Returns a string representation of the source for a layer.
static QVariantMap preprocessQgisProcessParameters(const QVariantMap &parameters, bool &ok, QString &error)
Pre-processes a set of parameter values for the qgis_process command.
static QList< QgsAnnotationLayer * > compatibleAnnotationLayers(QgsProject *project, bool sort=true)
Returns a list of annotation layers from a project which are compatible with the processing framework...
static QString generateTempFilename(const QString &basename, const QgsProcessingContext *context=nullptr)
Returns a temporary filename for a given file, putting it into a temporary folder (creating that fold...
static QString normalizeLayerSource(const QString &source)
Normalizes a layer source string for safe comparison across different operating system environments.
static QString formatHelpMapAsHtml(const QVariantMap &map, const QgsProcessingAlgorithm *algorithm)
Returns a HTML formatted version of the help text encoded in a variant map for a specified algorithm.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri().
LayerHint
Layer type hints.
@ TiledScene
Tiled scene layer type, since QGIS 3.34.
@ Annotation
Annotation layer type, since QGIS 3.22.
@ VectorTile
Vector tile layer type, since QGIS 3.32.
@ Mesh
Mesh layer type, since QGIS 3.6.
@ PointCloud
Point cloud layer type, since QGIS 3.22.
static int parameterDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the parameter with matching name for a specified algorithm.
static QString defaultRasterFormat()
Returns the default raster format to use, in the absence of all other constraints (e....
static QgsProcessingFeatureSource * variantToSource(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a new feature source.
static QList< QgsRasterLayer * > compatibleRasterLayers(QgsProject *project, bool sort=true)
Returns a list of raster layers from a project which are compatible with the processing framework.
static int outputDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the output matching name for a specified algorithm.
static 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 QStringList supportedImageFormats()
Returns a list of image format extensions supported by QImageWriter.
static QString supportedImageFileFilters()
Returns a file filter string of all supported image formats, suitable for use in file picker dialogs.
static QList< QgsMeshLayer * > compatibleMeshLayers(QgsProject *project, bool sort=true)
Returns a list of mesh layers from a project which are compatible with the processing framework.
static QString tempFolder(const QgsProcessingContext *context=nullptr)
Returns a session specific processing temporary folder for use in processing algorithms.
static const QgsSettingsEntryString * settingsDefaultOutputRasterLayerFormat
Settings entry default output raster layer format.
QFlags< LayerOptionsFlag > LayerOptionsFlags
static const QgsSettingsEntryString * settingsTempPath
Settings entry temp path.
static const QString TEMPORARY_OUTPUT
Constant used to indicate that a Processing algorithm output should be a temporary layer/file.
static const QgsSettingsEntryString * settingsDefaultOutputVectorLayerExt
Settings entry default output vector layer ext.
@ SkipIndexGeneration
Do not generate index when creating a layer. Makes sense only for point cloud layers.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:120
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
Qgis::PropertyType propertyType() const
Returns the property type.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
QString key() const
This returns the unique key associated with the provider.
virtual QList< Qgis::LayerType > supportedLayerTypes() const
Returns a list of the map layer types supported by the provider.
virtual QVariantMap decodeUri(const QString &uri) const
Breaks a provider data source URI into its component paths (e.g.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProvidersForUri(const QString &uri) const
Returns the details for the preferred provider(s) for opening the specified uri.
QString encodeUri(const QString &providerKey, const QVariantMap &parts)
Reassembles a provider data source URI from its component paths (e.g.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
bool flushBuffer() override
Flushes any internal buffer which may exist in the sink, causing any buffered features to be added to...
QgsFeatureSink * mSink
Underlying destination sink.
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
QgsProxyFeatureSink(QgsFeatureSink *sink)
Constructs a new QgsProxyFeatureSink which forwards features onto a destination sink.
static QStringList extensionsForFormat(const QString &format)
Returns a list of known file extensions for the given GDAL driver format.
static QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersAndFormats(RasterFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and GDAL format key as second ele...
Represents a raster layer.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsPointXY with associated coordinate reference system.
A QgsRectangle with associated coordinate reference system.
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.
void setDestinationCrs(const QgsCoordinateReferenceSystem &destination)
Sets the destination crs used for reprojecting incoming features to the sink's destination CRS.
void setDestinationWkbType(Qgis::WkbType type)
Sets the WKB geometry type for the destination.
void setDestinationFields(const QgsFields &fields)
Sets the fields for the destination sink.
Represents a map layer supporting display of tiled scene objects.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
virtual Qgis::VectorDataProviderAttributeEditCapabilities attributeEditCapabilities() const
Returns the provider's supported attribute editing capabilities.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
QString layerName
Layer name. If let empty, it will be derived from the filename.
QStringList layerOptions
List of OGR layer creation options.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QStringList datasourceOptions
List of OGR data source creation options.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
QgsFeatureSource subclass for the selected features from a QgsVectorLayer.
Represents a vector layer which manages a vector based dataset.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsRectangle extent() const final
Returns the extent of the layer.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
Implements a map layer that is dedicated to rendering of vector tiles.
Handles storage of information regarding WKB types and their properties.
Definition qgswkbtypes.h:42
@ UnknownCount
Provider returned an unknown feature count.
Definition qgis.h:574
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition qgis.cpp:601
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7175
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
QList< int > QgsAttributeList
Definition qgsfield.h:30
QString convertToCompatibleFormatInternal(const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName, long long featureLimit, const QString &filterExpression, bool renameFid)
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool skipIndexGeneration
Set to true if point cloud index generation should be skipped.
Details of available filters and formats.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
Setting options for loading vector layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Set to true if the default layer style should be loaded.