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