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