QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 "qgssettings.h"
21#include "qgsexception.h"
24#include "qgsvectorfilewriter.h"
30#include "qgsfileutils.h"
31#include "qgsvectorlayer.h"
32#include "qgsproviderregistry.h"
33#include "qgsmeshlayer.h"
34#include "qgspluginlayer.h"
36#include "qgsrasterfilewriter.h"
37#include "qgsvectortilelayer.h"
38#include "qgspointcloudlayer.h"
39#include "qgsannotationlayer.h"
40#include <QRegularExpression>
41#include <QUuid>
42
43QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
44{
45 return compatibleMapLayers< QgsRasterLayer >( project, sort );
46}
47
48QList<QgsVectorLayer *> QgsProcessingUtils::compatibleVectorLayers( QgsProject *project, const QList<int> &geometryTypes, bool sort )
49{
50 if ( !project )
51 return QList<QgsVectorLayer *>();
52
53 QList<QgsVectorLayer *> layers;
54 const auto vectorLayers = project->layers<QgsVectorLayer *>();
55 for ( QgsVectorLayer *l : vectorLayers )
56 {
57 if ( canUseLayer( l, geometryTypes ) )
58 layers << l;
59 }
60
61 if ( sort )
62 {
63 std::sort( layers.begin(), layers.end(), []( const QgsVectorLayer * a, const QgsVectorLayer * b ) -> bool
64 {
65 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
66 } );
67 }
68 return layers;
69}
70
71QList<QgsMeshLayer *> QgsProcessingUtils::compatibleMeshLayers( QgsProject *project, bool sort )
72{
73 return compatibleMapLayers< QgsMeshLayer >( project, sort );
74}
75
76QList<QgsPluginLayer *> QgsProcessingUtils::compatiblePluginLayers( QgsProject *project, bool sort )
77{
78 return compatibleMapLayers< QgsPluginLayer >( project, sort );
79}
80
81QList<QgsPointCloudLayer *> QgsProcessingUtils::compatiblePointCloudLayers( QgsProject *project, bool sort )
82{
83 return compatibleMapLayers< QgsPointCloudLayer >( project, sort );
84}
85
86QList<QgsAnnotationLayer *> QgsProcessingUtils::compatibleAnnotationLayers( QgsProject *project, bool sort )
87{
88 // we have to defer sorting until we've added the main annotation layer too
89 QList<QgsAnnotationLayer *> res = compatibleMapLayers< QgsAnnotationLayer >( project, false );
90 if ( project )
91 res.append( project->mainAnnotationLayer() );
92
93 if ( sort )
94 {
95 std::sort( res.begin(), res.end(), []( const QgsAnnotationLayer * a, const QgsAnnotationLayer * b ) -> bool
96 {
97 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
98 } );
99 }
100
101 return res;
102}
103
104template<typename T> QList<T *> QgsProcessingUtils::compatibleMapLayers( QgsProject *project, bool sort )
105{
106 if ( !project )
107 return QList<T *>();
108
109 QList<T *> layers;
110 const auto projectLayers = project->layers<T *>();
111 for ( T *l : projectLayers )
112 {
113 if ( canUseLayer( l ) )
114 layers << l;
115 }
116
117 if ( sort )
118 {
119 std::sort( layers.begin(), layers.end(), []( const T * a, const T * b ) -> bool
120 {
121 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
122 } );
123 }
124 return layers;
125}
126
127QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project, bool sort )
128{
129 if ( !project )
130 return QList<QgsMapLayer *>();
131
132 QList<QgsMapLayer *> layers;
133
134 const auto rasterLayers = compatibleMapLayers< QgsRasterLayer >( project, false );
135 for ( QgsRasterLayer *rl : rasterLayers )
136 layers << rl;
137
138 const auto vectorLayers = compatibleVectorLayers( project, QList< int >(), false );
139 for ( QgsVectorLayer *vl : vectorLayers )
140 layers << vl;
141
142 const auto meshLayers = compatibleMapLayers< QgsMeshLayer >( project, false );
143 for ( QgsMeshLayer *ml : meshLayers )
144 layers << ml;
145
146 const auto pointCloudLayers = compatibleMapLayers< QgsPointCloudLayer >( project, false );
147 for ( QgsPointCloudLayer *pcl : pointCloudLayers )
148 layers << pcl;
149
150 const auto annotationLayers = compatibleMapLayers< QgsAnnotationLayer >( project, false );
151 for ( QgsAnnotationLayer *al : annotationLayers )
152 layers << al;
153 layers << project->mainAnnotationLayer();
154
155 const auto pluginLayers = compatibleMapLayers< QgsPluginLayer >( project, false );
156 for ( QgsPluginLayer *pl : pluginLayers )
157 layers << pl;
158
159 if ( sort )
160 {
161 std::sort( layers.begin(), layers.end(), []( const QgsMapLayer * a, const QgsMapLayer * b ) -> bool
162 {
163 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
164 } );
165 }
166 return layers;
167}
168
169QString QgsProcessingUtils::encodeProviderKeyAndUri( const QString &providerKey, const QString &uri )
170{
171 return QStringLiteral( "%1://%2" ).arg( providerKey, uri );
172}
173
174bool QgsProcessingUtils::decodeProviderKeyAndUri( const QString &string, QString &providerKey, QString &uri )
175{
176 QRegularExpression re( QStringLiteral( "^(\\w+?):\\/\\/(.+)$" ) );
177 const QRegularExpressionMatch match = re.match( string );
178 if ( !match.hasMatch() )
179 return false;
180
181 providerKey = match.captured( 1 );
182 uri = match.captured( 2 );
183
184 // double check that provider is valid
185 return QgsProviderRegistry::instance()->providerMetadata( providerKey );
186}
187
188QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store, QgsProcessingUtils::LayerHint typeHint )
189{
190 if ( !store || string.isEmpty() )
191 return nullptr;
192
193 QList< QgsMapLayer * > layers = store->mapLayers().values();
194
195 layers.erase( std::remove_if( layers.begin(), layers.end(), []( QgsMapLayer * layer )
196 {
197 switch ( layer->type() )
198 {
199 case Qgis::LayerType::Vector:
200 return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
201 case Qgis::LayerType::Raster:
202 return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
203 case Qgis::LayerType::Plugin:
204 case Qgis::LayerType::Group:
205 return true;
206 case Qgis::LayerType::Mesh:
207 return !canUseLayer( qobject_cast< QgsMeshLayer * >( layer ) );
208 case Qgis::LayerType::VectorTile:
209 return !canUseLayer( qobject_cast< QgsVectorTileLayer * >( layer ) );
210 case Qgis::LayerType::PointCloud:
211 return !canUseLayer( qobject_cast< QgsPointCloudLayer * >( layer ) );
212 case Qgis::LayerType::Annotation:
213 return !canUseLayer( qobject_cast< QgsAnnotationLayer * >( layer ) );
214 }
215 return true;
216 } ), layers.end() );
217
218 auto isCompatibleType = [typeHint]( QgsMapLayer * l ) -> bool
219 {
220 switch ( typeHint )
221 {
222 case LayerHint::UnknownType:
223 return true;
224
225 case LayerHint::Vector:
226 return l->type() == Qgis::LayerType::Vector;
227
228 case LayerHint::Raster:
229 return l->type() == Qgis::LayerType::Raster;
230
231 case LayerHint::Mesh:
232 return l->type() == Qgis::LayerType::Mesh;
233
234 case LayerHint::PointCloud:
235 return l->type() == Qgis::LayerType::PointCloud;
236
237 case LayerHint::Annotation:
238 return l->type() == Qgis::LayerType::Annotation;
239 }
240 return true;
241 };
242
243 for ( QgsMapLayer *l : std::as_const( layers ) )
244 {
245 if ( isCompatibleType( l ) && l->id() == string )
246 return l;
247 }
248 for ( QgsMapLayer *l : std::as_const( layers ) )
249 {
250 if ( isCompatibleType( l ) && l->name() == string )
251 return l;
252 }
253 for ( QgsMapLayer *l : std::as_const( layers ) )
254 {
255 if ( isCompatibleType( l ) && normalizeLayerSource( l->source() ) == normalizeLayerSource( string ) )
256 return l;
257 }
258 return nullptr;
259}
260
261QgsMapLayer *QgsProcessingUtils::loadMapLayerFromString( const QString &string, const QgsCoordinateTransformContext &transformContext, LayerHint typeHint )
262{
263 QString provider;
264 QString uri;
265 const bool useProvider = decodeProviderKeyAndUri( string, provider, uri );
266 if ( !useProvider )
267 uri = string;
268
269 QString name;
270 // for disk based sources, we use the filename to determine a layer name
271 if ( !useProvider || ( provider == QLatin1String( "ogr" ) || provider == QLatin1String( "gdal" ) || provider == QLatin1String( "mdal" ) || provider == QLatin1String( "pdal" ) || provider == QLatin1String( "ept" ) ) )
272 {
273 QStringList components = uri.split( '|' );
274 if ( components.isEmpty() )
275 return nullptr;
276
277 QFileInfo fi;
278 if ( QFileInfo::exists( uri ) )
279 fi = QFileInfo( uri );
280 else if ( QFileInfo::exists( components.at( 0 ) ) )
281 fi = QFileInfo( components.at( 0 ) );
282 else
283 return nullptr;
284 name = fi.baseName();
285 }
286 else
287 {
288 name = QgsDataSourceUri( uri ).table();
289 }
290
291 // brute force attempt to load a matching layer
292 if ( typeHint == LayerHint::UnknownType || typeHint == LayerHint::Vector )
293 {
294 QgsVectorLayer::LayerOptions options { transformContext };
295 options.loadDefaultStyle = false;
296 options.skipCrsValidation = true;
297
298 std::unique_ptr< QgsVectorLayer > layer;
299 if ( useProvider )
300 {
301 layer = std::make_unique<QgsVectorLayer>( uri, name, provider, options );
302 }
303 else
304 {
305 // fallback to ogr
306 layer = std::make_unique<QgsVectorLayer>( uri, name, QStringLiteral( "ogr" ), options );
307 }
308 if ( layer->isValid() )
309 {
310 return layer.release();
311 }
312 }
313 if ( typeHint == LayerHint::UnknownType || typeHint == LayerHint::Raster )
314 {
315 QgsRasterLayer::LayerOptions rasterOptions;
316 rasterOptions.loadDefaultStyle = false;
317 rasterOptions.skipCrsValidation = true;
318
319 std::unique_ptr< QgsRasterLayer > rasterLayer;
320 if ( useProvider )
321 {
322 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, provider, rasterOptions );
323 }
324 else
325 {
326 // fallback to gdal
327 rasterLayer = std::make_unique< QgsRasterLayer >( uri, name, QStringLiteral( "gdal" ), rasterOptions );
328 }
329
330 if ( rasterLayer->isValid() )
331 {
332 return rasterLayer.release();
333 }
334 }
335 if ( typeHint == LayerHint::UnknownType || typeHint == LayerHint::Mesh )
336 {
337 QgsMeshLayer::LayerOptions meshOptions;
338 meshOptions.skipCrsValidation = true;
339
340 std::unique_ptr< QgsMeshLayer > meshLayer;
341 if ( useProvider )
342 {
343 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, provider, meshOptions );
344 }
345 else
346 {
347 meshLayer = std::make_unique< QgsMeshLayer >( uri, name, QStringLiteral( "mdal" ), meshOptions );
348 }
349 if ( meshLayer->isValid() )
350 {
351 return meshLayer.release();
352 }
353 }
354 if ( typeHint == LayerHint::UnknownType || typeHint == LayerHint::PointCloud )
355 {
356 QgsPointCloudLayer::LayerOptions pointCloudOptions;
357 pointCloudOptions.skipCrsValidation = true;
358
359 std::unique_ptr< QgsPointCloudLayer > pointCloudLayer;
360 if ( useProvider )
361 {
362 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, provider, pointCloudOptions );
363 }
364 else
365 {
366 pointCloudLayer = std::make_unique< QgsPointCloudLayer >( uri, name, QStringLiteral( "pdal" ), pointCloudOptions );
367 }
368 if ( pointCloudLayer->isValid() )
369 {
370 return pointCloudLayer.release();
371 }
372 }
373 return nullptr;
374}
375
376QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers, LayerHint typeHint )
377{
378 if ( string.isEmpty() )
379 return nullptr;
380
381 // prefer project layers
382 if ( context.project() && typeHint == LayerHint::Annotation && string.compare( QLatin1String( "main" ), Qt::CaseInsensitive ) == 0 )
383 return context.project()->mainAnnotationLayer();
384
385 QgsMapLayer *layer = nullptr;
386 if ( auto *lProject = context.project() )
387 {
388 QgsMapLayer *layer = mapLayerFromStore( string, lProject->layerStore(), typeHint );
389 if ( layer )
390 return layer;
391 }
392
393 layer = mapLayerFromStore( string, context.temporaryLayerStore(), typeHint );
394 if ( layer )
395 return layer;
396
397 if ( !allowLoadingNewLayers )
398 return nullptr;
399
400 layer = loadMapLayerFromString( string, context.transformContext(), typeHint );
401 if ( layer )
402 {
403 context.temporaryLayerStore()->addMapLayer( layer );
404 return layer;
405 }
406 else
407 {
408 return nullptr;
409 }
410}
411
412QgsProcessingFeatureSource *QgsProcessingUtils::variantToSource( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
413{
414 QVariant val = value;
415 bool selectedFeaturesOnly = false;
416 long long featureLimit = -1;
417 bool overrideGeometryCheck = false;
419 if ( val.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) )
420 {
421 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
422 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
423 selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
424 featureLimit = fromVar.featureLimit;
425 val = fromVar.source;
426 overrideGeometryCheck = fromVar.flags & QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck;
427 geometryCheck = fromVar.geometryCheck;
428 }
429 else if ( val.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
430 {
431 // input is a QgsProcessingOutputLayerDefinition (e.g. an output from earlier in a model) - get extra properties from it
432 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
433 val = fromVar.sink;
434 }
435
436 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( val ) ) )
437 {
438 std::unique_ptr< QgsProcessingFeatureSource> source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit );
439 if ( overrideGeometryCheck )
440 source->setInvalidGeometryCheck( geometryCheck );
441 return source.release();
442 }
443
444 QString layerRef;
445 if ( val.userType() == QMetaType::type( "QgsProperty" ) )
446 {
447 layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
448 }
449 else if ( !val.isValid() || val.toString().isEmpty() )
450 {
451 // fall back to default
452 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( fallbackValue ) ) )
453 {
454 std::unique_ptr< QgsProcessingFeatureSource> source = std::make_unique< QgsProcessingFeatureSource >( layer, context, false, featureLimit );
455 if ( overrideGeometryCheck )
456 source->setInvalidGeometryCheck( geometryCheck );
457 return source.release();
458 }
459
460 layerRef = fallbackValue.toString();
461 }
462 else
463 {
464 layerRef = val.toString();
465 }
466
467 if ( layerRef.isEmpty() )
468 return nullptr;
469
470 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context, true, LayerHint::Vector ) );
471 if ( !vl )
472 return nullptr;
473
474 std::unique_ptr< QgsProcessingFeatureSource> source;
475 if ( selectedFeaturesOnly )
476 {
477 source = std::make_unique< QgsProcessingFeatureSource>( new QgsVectorLayerSelectedFeatureSource( vl ), context, true, featureLimit );
478 }
479 else
480 {
481 source = std::make_unique< QgsProcessingFeatureSource >( vl, context, false, featureLimit );
482 }
483
484 if ( overrideGeometryCheck )
485 source->setInvalidGeometryCheck( geometryCheck );
486 return source.release();
487}
488
489QgsCoordinateReferenceSystem QgsProcessingUtils::variantToCrs( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
490{
491 QVariant val = value;
492
493 if ( val.userType() == QMetaType::type( "QgsCoordinateReferenceSystem" ) )
494 {
495 // input is a QgsCoordinateReferenceSystem - done!
496 return val.value< QgsCoordinateReferenceSystem >();
497 }
498 else if ( val.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) )
499 {
500 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
501 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
502 val = fromVar.source;
503 }
504 else if ( val.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
505 {
506 // input is a QgsProcessingOutputLayerDefinition - get extra properties from it
507 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( val );
508 val = fromVar.sink;
509 }
510
511 if ( val.userType() == QMetaType::type( "QgsProperty" ) && val.value< QgsProperty >().propertyType() == QgsProperty::StaticProperty )
512 {
513 val = val.value< QgsProperty >().staticValue();
514 }
515
516 // maybe a map layer
517 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( val ) ) )
518 return layer->crs();
519
520 if ( val.userType() == QMetaType::type( "QgsProperty" ) )
521 val = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
522
523 if ( !val.isValid() )
524 {
525 // fall back to default
526 val = fallbackValue;
527 }
528
529 QString crsText = val.toString();
530 if ( crsText.isEmpty() )
531 crsText = fallbackValue.toString();
532
533 if ( crsText.isEmpty() )
535
536 // maybe special string
537 if ( context.project() && crsText.compare( QLatin1String( "ProjectCrs" ), Qt::CaseInsensitive ) == 0 )
538 return context.project()->crs();
539
540 // maybe a map layer reference
541 if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( crsText, context ) )
542 return layer->crs();
543
544 // else CRS from string
546 crs.createFromString( crsText );
547 return crs;
548}
549
550bool QgsProcessingUtils::canUseLayer( const QgsMeshLayer *layer )
551{
552 return layer && layer->dataProvider();
553}
554
555bool QgsProcessingUtils::canUseLayer( const QgsPluginLayer *layer )
556{
557 return layer && layer->isValid();
558}
559
560bool QgsProcessingUtils::canUseLayer( const QgsVectorTileLayer *layer )
561{
562 return layer && layer->isValid();
563}
564
565bool QgsProcessingUtils::canUseLayer( const QgsRasterLayer *layer )
566{
567 return layer && layer->isValid();
568}
569
570bool QgsProcessingUtils::canUseLayer( const QgsPointCloudLayer *layer )
571{
572 return layer && layer->isValid();
573}
574
575bool QgsProcessingUtils::canUseLayer( const QgsAnnotationLayer *layer )
576{
577 return layer && layer->isValid();
578}
579
580bool QgsProcessingUtils::canUseLayer( const QgsVectorLayer *layer, const QList<int> &sourceTypes )
581{
582 return layer && layer->isValid() &&
583 ( sourceTypes.isEmpty()
584 || ( sourceTypes.contains( QgsProcessing::TypeVectorPoint ) && layer->geometryType() == Qgis::GeometryType::Point )
585 || ( sourceTypes.contains( QgsProcessing::TypeVectorLine ) && layer->geometryType() == Qgis::GeometryType::Line )
586 || ( sourceTypes.contains( QgsProcessing::TypeVectorPolygon ) && layer->geometryType() == Qgis::GeometryType::Polygon )
587 || ( sourceTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) && layer->isSpatial() )
588 || sourceTypes.contains( QgsProcessing::TypeVector )
589 );
590}
591
592QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
593{
594 QString normalized = source;
595 normalized.replace( '\\', '/' );
596 return normalized.trimmed();
597}
598
599QString QgsProcessingUtils::variantToPythonLiteral( const QVariant &value )
600{
601 if ( !value.isValid() )
602 return QStringLiteral( "None" );
603
604 if ( value.userType() == QMetaType::type( "QgsProperty" ) )
605 return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
606 else if ( value.userType() == QMetaType::type( "QgsCoordinateReferenceSystem" ) )
607 {
608 if ( !value.value< QgsCoordinateReferenceSystem >().isValid() )
609 return QStringLiteral( "QgsCoordinateReferenceSystem()" );
610 else
611 return QStringLiteral( "QgsCoordinateReferenceSystem('%1')" ).arg( value.value< QgsCoordinateReferenceSystem >().authid() );
612 }
613 else if ( value.userType() == QMetaType::type( "QgsRectangle" ) )
614 {
615 QgsRectangle r = value.value<QgsRectangle>();
616 return QStringLiteral( "'%1, %3, %2, %4'" ).arg( qgsDoubleToString( r.xMinimum() ),
620 }
621 else if ( value.userType() == QMetaType::type( "QgsReferencedRectangle" ) )
622 {
624 return QStringLiteral( "'%1, %3, %2, %4 [%5]'" ).arg( qgsDoubleToString( r.xMinimum() ),
627 qgsDoubleToString( r.yMaximum() ), r.crs().authid() );
628 }
629 else if ( value.userType() == QMetaType::type( "QgsPointXY" ) )
630 {
631 QgsPointXY r = value.value<QgsPointXY>();
632 return QStringLiteral( "'%1,%2'" ).arg( qgsDoubleToString( r.x() ),
633 qgsDoubleToString( r.y() ) );
634 }
635 else if ( value.userType() == QMetaType::type( "QgsReferencedPointXY" ) )
636 {
638 return QStringLiteral( "'%1,%2 [%3]'" ).arg( qgsDoubleToString( r.x() ),
639 qgsDoubleToString( r.y() ),
640 r.crs().authid() );
641 }
642
643 switch ( value.type() )
644 {
645 case QVariant::Bool:
646 return value.toBool() ? QStringLiteral( "True" ) : QStringLiteral( "False" );
647
648 case QVariant::Double:
649 return QString::number( value.toDouble() );
650
651 case QVariant::Int:
652 case QVariant::UInt:
653 return QString::number( value.toInt() );
654
655 case QVariant::LongLong:
656 case QVariant::ULongLong:
657 return QString::number( value.toLongLong() );
658
659 case QVariant::List:
660 {
661 QStringList parts;
662 const QVariantList vl = value.toList();
663 for ( const QVariant &v : vl )
664 {
665 parts << variantToPythonLiteral( v );
666 }
667 return parts.join( ',' ).prepend( '[' ).append( ']' );
668 }
669
670 case QVariant::Map:
671 {
672 const QVariantMap map = value.toMap();
673 QStringList parts;
674 parts.reserve( map.size() );
675 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
676 {
677 parts << QStringLiteral( "%1: %2" ).arg( stringToPythonLiteral( it.key() ), variantToPythonLiteral( it.value() ) );
678 }
679 return parts.join( ',' ).prepend( '{' ).append( '}' );
680 }
681
682 case QVariant::DateTime:
683 {
684 const QDateTime dateTime = value.toDateTime();
685 return QStringLiteral( "QDateTime(QDate(%1, %2, %3), QTime(%4, %5, %6))" )
686 .arg( dateTime.date().year() )
687 .arg( dateTime.date().month() )
688 .arg( dateTime.date().day() )
689 .arg( dateTime.time().hour() )
690 .arg( dateTime.time().minute() )
691 .arg( dateTime.time().second() );
692 }
693
694 default:
695 break;
696 }
697
698 return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
699}
700
701QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
702{
703 QString s = string;
704 s.replace( '\\', QLatin1String( "\\\\" ) );
705 s.replace( '\n', QLatin1String( "\\n" ) );
706 s.replace( '\r', QLatin1String( "\\r" ) );
707 s.replace( '\t', QLatin1String( "\\t" ) );
708
709 if ( s.contains( '\'' ) && !s.contains( '\"' ) )
710 {
711 s = s.prepend( '"' ).append( '"' );
712 }
713 else
714 {
715 s.replace( '\'', QLatin1String( "\\\'" ) );
716 s = s.prepend( '\'' ).append( '\'' );
717 }
718 return s;
719}
720
721void QgsProcessingUtils::parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter, QString &extension )
722{
723 extension.clear();
724 bool matched = decodeProviderKeyAndUri( destination, providerKey, uri );
725
726 if ( !matched )
727 {
728 QRegularExpression splitRx( QStringLiteral( "^(.{3,}?):(.*)$" ) );
729 QRegularExpressionMatch match = splitRx.match( destination );
730 if ( match.hasMatch() )
731 {
732 providerKey = match.captured( 1 );
733 uri = match.captured( 2 );
734 matched = true;
735 }
736 }
737
738 if ( matched )
739 {
740 if ( providerKey == QLatin1String( "postgis" ) ) // older processing used "postgis" instead of "postgres"
741 {
742 providerKey = QStringLiteral( "postgres" );
743 }
744 if ( providerKey == QLatin1String( "ogr" ) )
745 {
746 QgsDataSourceUri dsUri( uri );
747 if ( !dsUri.database().isEmpty() )
748 {
749 if ( !dsUri.table().isEmpty() )
750 {
751 layerName = dsUri.table();
752 options.insert( QStringLiteral( "layerName" ), layerName );
753 }
754 uri = dsUri.database();
755 extension = QFileInfo( uri ).completeSuffix();
756 format = QgsVectorFileWriter::driverForExtension( extension );
757 options.insert( QStringLiteral( "driverName" ), format );
758 }
759 else
760 {
761 extension = QFileInfo( uri ).completeSuffix();
762 options.insert( QStringLiteral( "driverName" ), QgsVectorFileWriter::driverForExtension( extension ) );
763 }
764 options.insert( QStringLiteral( "update" ), true );
765 }
766 useWriter = false;
767 }
768 else
769 {
770 useWriter = true;
771 providerKey = QStringLiteral( "ogr" );
772
773 QRegularExpression splitRx( QStringLiteral( "^(.*)\\.(.*?)$" ) );
774 QRegularExpressionMatch match = splitRx.match( destination );
775 if ( match.hasMatch() )
776 {
777 extension = match.captured( 2 );
778 format = QgsVectorFileWriter::driverForExtension( extension );
779 }
780
781 if ( format.isEmpty() )
782 {
783 format = QStringLiteral( "GPKG" );
784 destination = destination + QStringLiteral( ".gpkg" );
785 }
786
787 options.insert( QStringLiteral( "driverName" ), format );
788 uri = destination;
789 }
790}
791
792QgsFeatureSink *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 )
793{
794 QVariantMap options = createOptions;
795 if ( !options.contains( QStringLiteral( "fileEncoding" ) ) )
796 {
797 // no destination encoding specified, use default
798 options.insert( QStringLiteral( "fileEncoding" ), context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding() );
799 }
800
801 if ( destination.isEmpty() || destination.startsWith( QLatin1String( "memory:" ) ) )
802 {
803 // strip "memory:" from start of destination
804 if ( destination.startsWith( QLatin1String( "memory:" ) ) )
805 destination = destination.mid( 7 );
806
807 if ( destination.isEmpty() )
808 destination = QStringLiteral( "output" );
809
810 // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
811 std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs, false ) );
812 if ( !layer || !layer->isValid() )
813 {
814 throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
815 }
816
817 layer->setCustomProperty( QStringLiteral( "OnConvertFormatRegeneratePrimaryKey" ), static_cast< bool >( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey ) );
818
819 // update destination to layer ID
820 destination = layer->id();
821
822 // this is a factory, so we need to return a proxy
823 std::unique_ptr< QgsProcessingFeatureSink > sink( new QgsProcessingFeatureSink( layer->dataProvider(), destination, context ) );
824 context.temporaryLayerStore()->addMapLayer( layer.release() );
825
826 return sink.release();
827 }
828 else
829 {
830 QString providerKey;
831 QString uri;
832 QString layerName;
833 QString format;
834 QString extension;
835 bool useWriter = false;
836 parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
837
838 QgsFields newFields = fields;
839 if ( useWriter && providerKey == QLatin1String( "ogr" ) )
840 {
841 // use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
842 // us to use any OGR format which supports feature addition
843 QString finalFileName;
844 QString finalLayerName;
846 saveOptions.fileEncoding = options.value( QStringLiteral( "fileEncoding" ) ).toString();
847 saveOptions.layerName = !layerName.isEmpty() ? layerName : options.value( QStringLiteral( "layerName" ) ).toString();
848 saveOptions.driverName = format;
849 saveOptions.datasourceOptions = !datasourceOptions.isEmpty() ? datasourceOptions : QgsVectorFileWriter::defaultDatasetOptions( format );
850 saveOptions.layerOptions = !layerOptions.isEmpty() ? layerOptions : QgsVectorFileWriter::defaultLayerOptions( format );
852 if ( remappingDefinition )
853 {
855 // sniff destination file to get correct wkb type and crs
856 std::unique_ptr< QgsVectorLayer > vl = std::make_unique< QgsVectorLayer >( destination );
857 if ( vl->isValid() )
858 {
859 remappingDefinition->setDestinationWkbType( vl->wkbType() );
860 remappingDefinition->setDestinationCrs( vl->crs() );
861 newFields = vl->fields();
862 remappingDefinition->setDestinationFields( newFields );
863 }
864 context.expressionContext().setFields( fields );
865 }
866 else
867 {
869 }
870 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( destination, newFields, geometryType, crs, context.transformContext(), saveOptions, sinkFlags, &finalFileName, &finalLayerName ) );
871 if ( writer->hasError() )
872 {
873 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
874 }
875 destination = finalFileName;
876 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
877 destination += QStringLiteral( "|layername=%1" ).arg( finalLayerName );
878
879 if ( remappingDefinition )
880 {
881 std::unique_ptr< QgsRemappingProxyFeatureSink > remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, writer.release(), true );
882 remapSink->setExpressionContext( context.expressionContext() );
883 remapSink->setTransformContext( context.transformContext() );
884 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
885 }
886 else
887 return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
888 }
889 else
890 {
891 const QgsVectorLayer::LayerOptions layerOptions { context.transformContext() };
892 if ( remappingDefinition )
893 {
894 //write to existing layer
895
896 // use destination string as layer name (eg "postgis:..." )
897 if ( !layerName.isEmpty() )
898 {
899 QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( providerKey, uri );
900 parts.insert( QStringLiteral( "layerName" ), layerName );
901 uri = QgsProviderRegistry::instance()->encodeUri( providerKey, parts );
902 }
903
904 std::unique_ptr< QgsVectorLayer > layer = std::make_unique<QgsVectorLayer>( uri, destination, providerKey, layerOptions );
905 // update destination to layer ID
906 destination = layer->id();
907 if ( layer->isValid() )
908 {
909 remappingDefinition->setDestinationWkbType( layer->wkbType() );
910 remappingDefinition->setDestinationCrs( layer->crs() );
911 remappingDefinition->setDestinationFields( layer->fields() );
912 }
913
914 std::unique_ptr< QgsRemappingProxyFeatureSink > remapSink = std::make_unique< QgsRemappingProxyFeatureSink >( *remappingDefinition, layer->dataProvider(), false );
915 context.temporaryLayerStore()->addMapLayer( layer.release() );
916 remapSink->setExpressionContext( context.expressionContext() );
917 remapSink->setTransformContext( context.transformContext() );
918 context.expressionContext().setFields( fields );
919 return new QgsProcessingFeatureSink( remapSink.release(), destination, context, true );
920 }
921 else
922 {
923 //create empty layer
924 std::unique_ptr< QgsVectorLayerExporter > exporter = std::make_unique<QgsVectorLayerExporter>( uri, providerKey, newFields, geometryType, crs, true, options, sinkFlags );
925 if ( exporter->errorCode() != Qgis::VectorExportResult::Success )
926 {
927 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
928 }
929
930 // use destination string as layer name (eg "postgis:..." )
931 if ( !layerName.isEmpty() )
932 {
933 uri += QStringLiteral( "|layername=%1" ).arg( layerName );
934 // update destination to generated URI
935 destination = uri;
936 }
937
938 return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
939 }
940 }
941 }
942}
943
944void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )
945{
946 *sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
947}
948
949
951{
952 QgsRectangle extent;
953 for ( const QgsMapLayer *layer : layers )
954 {
955 if ( !layer )
956 continue;
957
958 if ( crs.isValid() )
959 {
960 //transform layer extent to target CRS
961 QgsCoordinateTransform ct( layer->crs(), crs, context.transformContext() );
963 try
964 {
965 QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
966 extent.combineExtentWith( reprojExtent );
967 }
968 catch ( QgsCsException & )
969 {
970 // can't reproject... what to do here? hmmm?
971 // let's ignore this layer for now, but maybe we should just use the original extent?
972 }
973 }
974 else
975 {
976 extent.combineExtentWith( layer->extent() );
977 }
978
979 }
980 return extent;
981}
982
983// Deprecated
985{
986 QgsProcessingContext context;
987 return QgsProcessingUtils::combineLayerExtents( layers, crs, context );
988}
989
990QVariant QgsProcessingUtils::generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context )
991{
992 if ( !input.isValid() )
993 return QStringLiteral( "memory:%1" ).arg( id.toString() );
994
995 if ( input.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
996 {
997 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( input );
998 QVariant newSink = generateIteratingDestination( fromVar.sink, id, context );
999 fromVar.sink = QgsProperty::fromValue( newSink );
1000 return fromVar;
1001 }
1002 else if ( input.userType() == QMetaType::type( "QgsProperty" ) )
1003 {
1004 QString res = input.value< QgsProperty>().valueAsString( context.expressionContext() );
1005 return generateIteratingDestination( res, id, context );
1006 }
1007 else
1008 {
1009 QString res = input.toString();
1011 {
1012 // temporary outputs map to temporary outputs!
1014 }
1015 else if ( res.startsWith( QLatin1String( "memory:" ) ) )
1016 {
1017 return QString( res + '_' + id.toString() );
1018 }
1019 else
1020 {
1021 // assume a filename type output for now
1022 // TODO - uris?
1023 int lastIndex = res.lastIndexOf( '.' );
1024 return lastIndex >= 0 ? QString( res.left( lastIndex ) + '_' + id.toString() + res.mid( lastIndex ) ) : QString( res + '_' + id.toString() );
1025 }
1026 }
1027}
1028
1030{
1031 // we maintain a list of temporary folders -- this allows us to append additional
1032 // folders when a setting change causes the base temp folder to change, while deferring
1033 // cleanup of ALL these temp folders until session end (we can't cleanup older folders immediately,
1034 // because we don't know whether they have data in them which is still wanted)
1035 static std::vector< std::unique_ptr< QTemporaryDir > > sTempFolders;
1036 static QString sFolder;
1037 static QMutex sMutex;
1038 QMutexLocker locker( &sMutex );
1039 const QString basePath = QgsProcessing::settingsTempPath->value();
1040 if ( basePath.isEmpty() )
1041 {
1042 // default setting -- automatically create a temp folder
1043 if ( sTempFolders.empty() )
1044 {
1045 const QString templatePath = QStringLiteral( "%1/processing_XXXXXX" ).arg( QDir::tempPath() );
1046 std::unique_ptr< QTemporaryDir > tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1047 sFolder = tempFolder->path();
1048 sTempFolders.emplace_back( std::move( tempFolder ) );
1049 }
1050 }
1051 else if ( sFolder.isEmpty() || !sFolder.startsWith( basePath ) || sTempFolders.empty() )
1052 {
1053 if ( !QDir().exists( basePath ) )
1054 QDir().mkpath( basePath );
1055
1056 const QString templatePath = QStringLiteral( "%1/processing_XXXXXX" ).arg( basePath );
1057 std::unique_ptr< QTemporaryDir > tempFolder = std::make_unique< QTemporaryDir >( templatePath );
1058 sFolder = tempFolder->path();
1059 sTempFolders.emplace_back( std::move( tempFolder ) );
1060 }
1061 return sFolder;
1062}
1063
1064QString QgsProcessingUtils::generateTempFilename( const QString &basename )
1065{
1066 QString subPath = QUuid::createUuid().toString().remove( '-' ).remove( '{' ).remove( '}' );
1067 QString path = tempFolder() + '/' + subPath;
1068 if ( !QDir( path ).exists() ) //make sure the directory exists - it shouldn't, but lets be safe...
1069 {
1070 QDir tmpDir;
1071 tmpDir.mkdir( path );
1072 }
1073 return path + '/' + QgsFileUtils::stringToSafeFilename( basename );
1074}
1075
1077{
1078 auto getText = [map]( const QString & key )->QString
1079 {
1080 if ( map.contains( key ) )
1081 return map.value( key ).toString();
1082 return QString();
1083 };
1084
1085 QString s;
1086 s += QStringLiteral( "<html><body><p>" ) + getText( QStringLiteral( "ALG_DESC" ) ) + QStringLiteral( "</p>\n" );
1087
1088 QString inputs;
1089 const auto parameterDefinitions = algorithm->parameterDefinitions();
1090 for ( const QgsProcessingParameterDefinition *def : parameterDefinitions )
1091 {
1092 if ( def->flags() & QgsProcessingParameterDefinition::FlagHidden || def->isDestination() )
1093 continue;
1094
1095 if ( !getText( def->name() ).isEmpty() )
1096 {
1097 inputs += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
1098 inputs += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
1099 }
1100 }
1101 if ( !inputs.isEmpty() )
1102 s += QStringLiteral( "<h2>" ) + QObject::tr( "Input parameters" ) + QStringLiteral( "</h2>\n" ) + inputs;
1103
1104 QString outputs;
1105 const auto outputDefinitions = algorithm->outputDefinitions();
1106 for ( const QgsProcessingOutputDefinition *def : outputDefinitions )
1107 {
1108 if ( !getText( def->name() ).isEmpty() )
1109 {
1110 outputs += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
1111 outputs += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
1112 }
1113 }
1114 if ( !outputs.isEmpty() )
1115 s += QStringLiteral( "<h2>" ) + QObject::tr( "Outputs" ) + QStringLiteral( "</h2>\n" ) + outputs;
1116
1117 if ( !map.value( QStringLiteral( "EXAMPLES" ) ).toString().isEmpty() )
1118 s += QStringLiteral( "<h2>%1</h2>\n<p>%2</p>" ).arg( QObject::tr( "Examples" ), getText( QStringLiteral( "EXAMPLES" ) ) );
1119
1120 s += QLatin1String( "<br>" );
1121 if ( !map.value( QStringLiteral( "ALG_CREATOR" ) ).toString().isEmpty() )
1122 s += QStringLiteral( "<p align=\"right\">" ) + QObject::tr( "Algorithm author:" ) + QStringLiteral( " " ) + getText( QStringLiteral( "ALG_CREATOR" ) ) + QStringLiteral( "</p>" );
1123 if ( !map.value( QStringLiteral( "ALG_HELP_CREATOR" ) ).toString().isEmpty() )
1124 s += QStringLiteral( "<p align=\"right\">" ) + QObject::tr( "Help author:" ) + QStringLiteral( " " ) + getText( QStringLiteral( "ALG_HELP_CREATOR" ) ) + QStringLiteral( "</p>" );
1125 if ( !map.value( QStringLiteral( "ALG_VERSION" ) ).toString().isEmpty() )
1126 s += QStringLiteral( "<p align=\"right\">" ) + QObject::tr( "Algorithm version:" ) + QStringLiteral( " " ) + getText( QStringLiteral( "ALG_VERSION" ) ) + QStringLiteral( "</p>" );
1127
1128 s += QLatin1String( "</body></html>" );
1129 return s;
1130}
1131
1132QString convertToCompatibleFormatInternal( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName,
1133 long long featureLimit )
1134{
1135 bool requiresTranslation = false;
1136
1137 // if we are only looking for selected features then we have to export back to disk,
1138 // as we need to subset only selected features, a concept which doesn't exist outside QGIS!
1139 requiresTranslation = requiresTranslation || selectedFeaturesOnly;
1140
1141 // if we are limiting the feature count, we better export
1142 requiresTranslation = requiresTranslation || featureLimit != -1;
1143
1144 // if the data provider is NOT ogr, then we HAVE to convert. Otherwise we run into
1145 // issues with data providers like spatialite, delimited text where the format can be
1146 // opened outside of QGIS, but with potentially very different behavior!
1147 requiresTranslation = requiresTranslation || vl->providerType() != QLatin1String( "ogr" );
1148
1149 // if the layer has a feature filter set, then we HAVE to convert. Feature filters are
1150 // a purely QGIS concept.
1151 requiresTranslation = requiresTranslation || !vl->subsetString().isEmpty();
1152
1153 // if the layer opened using GDAL's virtual I/O mechanism (/vsizip/, etc.), then
1154 // we HAVE to convert as other tools may not work with it
1155 requiresTranslation = requiresTranslation || vl->source().startsWith( QLatin1String( "/vsi" ) );
1156
1157 // Check if layer is a disk based format and if so if the layer's path has a compatible filename suffix
1158 QString diskPath;
1159 if ( !requiresTranslation )
1160 {
1161 const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( vl->providerType(), vl->source() );
1162 if ( parts.contains( QStringLiteral( "path" ) ) )
1163 {
1164 diskPath = parts.value( QStringLiteral( "path" ) ).toString();
1165 QFileInfo fi( diskPath );
1166 requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
1167
1168 // if the layer name doesn't match the filename, we need to convert the layer. This method can only return
1169 // a filename, and cannot handle layernames as well as file paths
1170 const QString srcLayerName = parts.value( QStringLiteral( "layerName" ) ).toString();
1171 if ( layerName )
1172 {
1173 // differing layer names are acceptable
1174 *layerName = srcLayerName;
1175 }
1176 else
1177 {
1178 // differing layer names are NOT acceptable
1179 requiresTranslation = requiresTranslation || ( !srcLayerName.isEmpty() && srcLayerName != fi.baseName() );
1180 }
1181 }
1182 else
1183 {
1184 requiresTranslation = true; // not a disk-based format
1185 }
1186 }
1187
1188 if ( requiresTranslation )
1189 {
1190 QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat );
1191
1193 saveOptions.fileEncoding = context.defaultEncoding();
1194 saveOptions.driverName = QgsVectorFileWriter::driverForExtension( preferredFormat );
1195 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( temp, vl->fields(), vl->wkbType(), vl->crs(), context.transformContext(), saveOptions ) );
1196 QgsFeature f;
1198 if ( featureLimit != -1 )
1199 {
1200 if ( selectedFeaturesOnly )
1201 it = vl->getSelectedFeatures( QgsFeatureRequest().setLimit( featureLimit ) );
1202 else
1203 it = vl->getFeatures( QgsFeatureRequest().setLimit( featureLimit ) );
1204 }
1205 else
1206 {
1207 if ( selectedFeaturesOnly )
1208 it = vl->getSelectedFeatures( QgsFeatureRequest().setLimit( featureLimit ) );
1209 else
1210 it = vl->getFeatures();
1211 }
1212
1213 while ( it.nextFeature( f ) )
1214 {
1215 if ( feedback->isCanceled() )
1216 return QString();
1217 writer->addFeature( f, QgsFeatureSink::FastInsert );
1218 }
1219 return temp;
1220 }
1221 else
1222 {
1223 return diskPath;
1224 }
1225}
1226
1227QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long long featureLimit )
1228{
1229 return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, nullptr, featureLimit );
1230}
1231
1232QString 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 )
1233{
1234 layerName.clear();
1235 return convertToCompatibleFormatInternal( layer, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, &layerName, featureLimit );
1236}
1237
1238QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix )
1239{
1240 QgsFields outFields = fieldsA;
1241 QSet< QString > usedNames;
1242 for ( const QgsField &f : fieldsA )
1243 {
1244 usedNames.insert( f.name().toLower() );
1245 }
1246
1247 for ( const QgsField &f : fieldsB )
1248 {
1249 QgsField newField = f;
1250 newField.setName( fieldsBPrefix + f.name() );
1251 if ( usedNames.contains( newField.name().toLower() ) )
1252 {
1253 int idx = 2;
1254 QString newName = newField.name() + '_' + QString::number( idx );
1255 while ( usedNames.contains( newName.toLower() ) || fieldsB.indexOf( newName ) != -1 )
1256 {
1257 idx++;
1258 newName = newField.name() + '_' + QString::number( idx );
1259 }
1260 newField.setName( newName );
1261 outFields.append( newField );
1262 }
1263 else
1264 {
1265 outFields.append( newField );
1266 }
1267 usedNames.insert( newField.name() );
1268 }
1269
1270 return outFields;
1271}
1272
1273
1274QList<int> QgsProcessingUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
1275{
1276 QList<int> indices;
1277 if ( !fieldNames.isEmpty() )
1278 {
1279 indices.reserve( fieldNames.count() );
1280 for ( const QString &f : fieldNames )
1281 {
1282 int idx = fields.lookupField( f );
1283 if ( idx >= 0 )
1284 indices.append( idx );
1285 }
1286 }
1287 else
1288 {
1289 indices.reserve( fields.count() );
1290 for ( int i = 0; i < fields.count(); ++i )
1291 indices.append( i );
1292 }
1293 return indices;
1294}
1295
1296
1297QgsFields QgsProcessingUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
1298{
1299 QgsFields fieldsSubset;
1300 for ( int i : indices )
1301 fieldsSubset.append( fields.at( i ) );
1302 return fieldsSubset;
1303}
1304
1306{
1308 if ( setting == -1 )
1309 return QStringLiteral( "gpkg" );
1310 return QgsVectorFileWriter::supportedFormatExtensions().value( setting, QStringLiteral( "gpkg" ) );
1311}
1312
1314{
1316 if ( setting == -1 )
1317 return QStringLiteral( "tif" );
1318 return QgsRasterFileWriter::supportedFormatExtensions().value( setting, QStringLiteral( "tif" ) );
1319}
1320
1322{
1323 return QStringLiteral( "las" );
1324}
1325
1326QVariantMap QgsProcessingUtils::removePointerValuesFromMap( const QVariantMap &map )
1327{
1328 auto layerPointerToString = []( QgsMapLayer * layer ) -> QString
1329 {
1330 if ( layer && layer->providerType() == QLatin1String( "memory" ) )
1331 return layer->id();
1332 else if ( layer )
1333 return layer->source();
1334 else
1335 return QString();
1336 };
1337
1338 auto cleanPointerValues = [&layerPointerToString]( const QVariant & value ) -> QVariant
1339 {
1340 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( value.value< QObject * >() ) )
1341 {
1342 // don't store pointers in maps for long-term storage
1343 return layerPointerToString( layer );
1344 }
1345 else if ( value.userType() == QMetaType::type( "QPointer< QgsMapLayer >" ) )
1346 {
1347 // don't store pointers in maps for long-term storage
1348 return layerPointerToString( value.value< QPointer< QgsMapLayer > >().data() );
1349 }
1350 else
1351 {
1352 return value;
1353 }
1354 };
1355
1356 QVariantMap res;
1357 for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
1358 {
1359 if ( it->type() == QVariant::Map )
1360 {
1361 res.insert( it.key(), removePointerValuesFromMap( it.value().toMap() ) );
1362 }
1363 else if ( it->type() == QVariant::List )
1364 {
1365 QVariantList dest;
1366 const QVariantList source = it.value().toList();
1367 dest.reserve( source.size() );
1368 for ( const QVariant &v : source )
1369 {
1370 dest.append( cleanPointerValues( v ) );
1371 }
1372 res.insert( it.key(), dest );
1373 }
1374 else
1375 {
1376 res.insert( it.key(), cleanPointerValues( it.value() ) );
1377 }
1378 }
1379 return res;
1380}
1381
1382QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantMap &parameters, bool &ok, QString &error )
1383{
1384 QVariantMap output;
1385 ok = true;
1386 for ( auto it = parameters.constBegin(); it != parameters.constEnd(); ++it )
1387 {
1388 if ( it.value().type() == QVariant::Map )
1389 {
1390 const QVariantMap value = it.value().toMap();
1391 if ( value.value( QStringLiteral( "type" ) ).toString() == QLatin1String( "data_defined" ) )
1392 {
1393 const QString expression = value.value( QStringLiteral( "expression" ) ).toString();
1394 const QString field = value.value( QStringLiteral( "field" ) ).toString();
1395 if ( !expression.isEmpty() )
1396 {
1397 output.insert( it.key(), QgsProperty::fromExpression( expression ) );
1398 }
1399 else if ( !field.isEmpty() )
1400 {
1401 output.insert( it.key(), QgsProperty::fromField( field ) );
1402 }
1403 else
1404 {
1405 ok = false;
1406 error = QObject::tr( "Invalid data defined parameter for %1, requires 'expression' or 'field' values." ).arg( it.key() );
1407 }
1408 }
1409 else
1410 {
1411 output.insert( it.key(), it.value() );
1412 }
1413 }
1414 else if ( it.value().type() == QVariant::String )
1415 {
1416 const QString stringValue = it.value().toString();
1417
1418 if ( stringValue.startsWith( QLatin1String( "field:" ) ) )
1419 {
1420 output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) );
1421 }
1422 else if ( stringValue.startsWith( QLatin1String( "expression:" ) ) )
1423 {
1424 output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) );
1425 }
1426 else
1427 {
1428 output.insert( it.key(), it.value() );
1429 }
1430 }
1431 else
1432 {
1433 output.insert( it.key(), it.value() );
1434 }
1435 }
1436 return output;
1437}
1438
1439//
1440// QgsProcessingFeatureSource
1441//
1442
1443QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource, long long featureLimit )
1444 : mSource( originalSource )
1445 , mOwnsSource( ownsOriginalSource )
1446 , mInvalidGeometryCheck( QgsWkbTypes::geometryType( mSource->wkbType() ) == Qgis::GeometryType::Point
1447 ? QgsFeatureRequest::GeometryNoCheck // never run geometry validity checks for point layers!
1448 : context.invalidGeometryCheck() )
1449 , mInvalidGeometryCallback( context.invalidGeometryCallback( originalSource ) )
1450 , mTransformErrorCallback( context.transformErrorCallback() )
1451 , mInvalidGeometryCallbackSkip( context.defaultInvalidGeometryCallbackForCheck( QgsFeatureRequest::GeometrySkipInvalid, originalSource ) )
1452 , mInvalidGeometryCallbackAbort( context.defaultInvalidGeometryCallbackForCheck( QgsFeatureRequest::GeometryAbortOnInvalid, originalSource ) )
1453 , mFeatureLimit( featureLimit )
1454{}
1455
1457{
1458 if ( mOwnsSource )
1459 delete mSource;
1460}
1461
1463{
1464 QgsFeatureRequest req( request );
1465 req.setTransformErrorCallback( mTransformErrorCallback );
1466
1467 if ( flags & FlagSkipGeometryValidityChecks )
1469 else
1470 {
1471 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1472 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1473 }
1474
1475 if ( mFeatureLimit != -1 && req.limit() != -1 )
1476 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1477 else if ( mFeatureLimit != -1 )
1478 req.setLimit( mFeatureLimit );
1479
1480 return mSource->getFeatures( req );
1481}
1482
1484{
1485 FeatureAvailability sourceAvailability = mSource->hasFeatures();
1486 if ( sourceAvailability == NoFeaturesAvailable )
1487 return NoFeaturesAvailable; // never going to be features if underlying source has no features
1488 else if ( mInvalidGeometryCheck == QgsFeatureRequest::GeometryNoCheck )
1489 return sourceAvailability;
1490 else
1491 // we don't know... source has features, but these may be filtered out by invalid geometry check
1493}
1494
1496{
1497 QgsFeatureRequest req( request );
1498 req.setInvalidGeometryCheck( mInvalidGeometryCheck );
1499 req.setInvalidGeometryCallback( mInvalidGeometryCallback );
1500 req.setTransformErrorCallback( mTransformErrorCallback );
1501
1502 if ( mFeatureLimit != -1 && req.limit() != -1 )
1503 req.setLimit( std::min( static_cast< long long >( req.limit() ), mFeatureLimit ) );
1504 else if ( mFeatureLimit != -1 )
1505 req.setLimit( mFeatureLimit );
1506
1507 return mSource->getFeatures( req );
1508}
1509
1511{
1512 return mSource->sourceCrs();
1513}
1514
1516{
1517 return mSource->fields();
1518}
1519
1521{
1522 return mSource->wkbType();
1523}
1524
1526{
1527 if ( mFeatureLimit == -1 )
1528 return mSource->featureCount();
1529 else
1530 return std::min( mFeatureLimit, mSource->featureCount() );
1531}
1532
1534{
1535 return mSource->sourceName();
1536
1537}
1538
1539QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
1540{
1541 return mSource->uniqueValues( fieldIndex, limit );
1542}
1543
1544QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
1545{
1546 return mSource->minimumValue( fieldIndex );
1547}
1548
1549QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
1550{
1551 return mSource->maximumValue( fieldIndex );
1552}
1553
1555{
1556 return mSource->sourceExtent();
1557}
1558
1560{
1561 return mSource->allFeatureIds();
1562}
1563
1565{
1566 return mSource->hasSpatialIndex();
1567}
1568
1570{
1571 QgsExpressionContextScope *expressionContextScope = nullptr;
1572 QgsExpressionContextScopeGenerator *generator = dynamic_cast<QgsExpressionContextScopeGenerator *>( mSource );
1573 if ( generator )
1574 {
1575 expressionContextScope = generator->createExpressionContextScope();
1576 }
1577 return expressionContextScope;
1578}
1579
1581{
1582 mInvalidGeometryCheck = method;
1583 switch ( mInvalidGeometryCheck )
1584 {
1586 mInvalidGeometryCallback = nullptr;
1587 break;
1588
1590 mInvalidGeometryCallback = mInvalidGeometryCallbackSkip;
1591 break;
1592
1594 mInvalidGeometryCallback = mInvalidGeometryCallbackAbort;
1595 break;
1596
1597 }
1598}
1599
1600
1601//
1602// QgsProcessingFeatureSink
1603//
1604QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
1605 : QgsProxyFeatureSink( originalSink )
1606 , mContext( context )
1607 , mSinkName( sinkName )
1608 , mOwnsSink( ownsOriginalSink )
1609{}
1610
1612{
1613 if ( mOwnsSink )
1614 delete destinationSink();
1615}
1616
1617bool QgsProcessingFeatureSink::addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags )
1618{
1619 bool result = QgsProxyFeatureSink::addFeature( feature, flags );
1620 if ( !result && mContext.feedback() )
1621 {
1622 const QString error = lastError();
1623 if ( !error.isEmpty() )
1624 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1: %2" ).arg( mSinkName, error ) );
1625 else
1626 mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
1627 }
1628 return result;
1629}
1630
1631bool QgsProcessingFeatureSink::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags )
1632{
1633 bool result = QgsProxyFeatureSink::addFeatures( features, flags );
1634 if ( !result && mContext.feedback() )
1635 {
1636 const QString error = lastError();
1637 if ( !error.isEmpty() )
1638 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1: %2", nullptr, features.count() ).arg( mSinkName, error ) );
1639 else
1640 mContext.feedback()->reportError( QObject::tr( "%n feature(s) could not be written to %1", nullptr, features.count() ).arg( mSinkName ) );
1641 }
1642 return result;
1643}
1644
1645bool QgsProcessingFeatureSink::addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags )
1646{
1647 bool result = QgsProxyFeatureSink::addFeatures( iterator, flags );
1648 if ( !result && mContext.feedback() )
1649 {
1650 const QString error = lastError();
1651 if ( !error.isEmpty() )
1652 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1: %2" ).arg( mSinkName, error ) );
1653 else
1654 mContext.feedback()->reportError( QObject::tr( "Features could not be written to %1" ).arg( mSinkName ) );
1655 }
1656 return result;
1657}
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:55
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:155
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.
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
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 SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Class for storing the component parts of a RDBMS data source URI (e.g.
QString table() const
Returns the table 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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
InvalidGeometryCheck
Handling of features with invalid geometries.
@ GeometryNoCheck
No invalid geometry checking.
@ GeometryAbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
@ GeometrySkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
long long limit() const
Returns the maximum number of features to request, or -1 if no limit set.
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 & setInvalidGeometryCheck(InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
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.
@ 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...
An interface for objects which provide features via a getFeatures method.
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
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 QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
SpatialIndexPresence
Enumeration of spatial index presence states.
virtual Qgis::WkbType wkbType() const =0
Returns the geometry type for features returned by this source.
virtual FeatureAvailability hasFeatures() const
Determines if there are any features available in the source.
FeatureAvailability
Possible return value for hasFeatures() to determine if a source is empty.
@ FeaturesMaybeAvailable
There may be features available in this source.
@ NoFeaturesAvailable
There are certainly no features available in this 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 QString sourceName() const =0
Returns a friendly display name for 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.
virtual SpatialIndexPresence hasSpatialIndex() const
Returns an enum value representing the presence of a valid spatial index on the source,...
virtual QgsRectangle sourceExtent() const
Returns the extent of all geometries from the source.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:52
QString name
Definition: qgsfield.h:61
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:186
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
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:73
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:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
bool isValid
Definition: qgsmaplayer.h:81
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.
Definition: qgsmeshlayer.h:100
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:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
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.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
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.
Flags flags
Flags which dictate source behavior.
bool selectedFeaturesOnly
true if only selected features in the source should be used by algorithms.
QgsFeatureRequest::InvalidGeometryCheck geometryCheck
Geometry check method to apply to this source.
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...
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.
QgsFeatureSource::FeatureAvailability hasFeatures() const override
Determines if there are any features available in the source.
QVariant maximumValue(int fieldIndex) const override
Returns the maximum value for an attribute column or an invalid variant in case of error.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request, Flags flags) const
Returns an iterator for the features in the source, respecting the supplied feature flags.
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.
@ FlagSkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
QString sourceName() const override
Returns a friendly display name for the source.
SpatialIndexPresence hasSpatialIndex() const override
Returns an enum value representing the presence of a valid spatial index on the source,...
QgsFeatureIds allFeatureIds() const override
Returns a list of all feature IDs for features present in the source.
QgsProcessingFeatureSource(QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource=false, long long featureLimit=-1)
Constructor for QgsProcessingFeatureSource, accepting an original feature source originalSource and p...
QgsFields fields() const override
Returns the fields associated with features in the source.
void setInvalidGeometryCheck(QgsFeatureRequest::InvalidGeometryCheck method)
Overrides the default geometry check method for 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.
@ FlagHidden
Parameter is hidden and should not be shown to users.
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)
Converts a source vector layer to a file path of a vector layer of compatible format.
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 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()
static QString tempFolder()
Returns a session specific processing temporary folder for use in processing algorithms.
LayerHint
Layer type hints.
@ Annotation
Annotation layer type, since QGIS 3.22.
@ Vector
Vector layer type.
@ 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 generateTempFilename(const QString &basename)
Returns a temporary filename for a given file, putting it into a temporary folder (creating that fold...
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 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)
Converts a source vector layer to a file path and layer name of a vector layer of compatible format.
static QgsRectangle combineLayerExtents(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context)
Combines the extent of several map layers.
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 void createFeatureSinkPython(QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap()) SIP_THROW(QgsProcessingException)
Creates a feature sink ready for adding features.
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 QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType)
Interprets a string as a map layer within the supplied context.
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 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 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 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 const QgsSettingsEntryInteger * settingsDefaultOutputRasterLayerExt
Settings entry default output raster layer ext.
static const QgsSettingsEntryInteger * settingsDefaultOutputVectorLayerExt
Settings entry default output vector layer ext.
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.
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:51
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:52
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:55
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:50
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:49
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:105
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.
Definition: qgsproject.h:1194
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:110
A store for object properties.
Definition: qgsproperty.h:230
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:237
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
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.
Type propertyType() const
Returns the property type.
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.
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.
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
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.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings 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.
QgsVectorFileWriter::SymbologyExport 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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QString subsetString
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:43
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
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3448
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:920
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
const QgsField & field
Definition: qgsfield.h:501
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 QgsCoordinateReferenceSystem & crs
Setting options for loading mesh layers.
Definition: qgsmeshlayer.h:108
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
Definition: qgsmeshlayer.h:142
Setting options for loading point cloud layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
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 vector layers.