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