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