QGIS API Documentation  3.2.0-Bonn (bc43194)
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"
22 #include "qgsprocessingcontext.h"
23 #include "qgsvectorlayerexporter.h"
24 #include "qgsvectorfilewriter.h"
25 #include "qgsmemoryproviderutils.h"
27 #include "qgsprocessingalgorithm.h"
30 #include "qgsfileutils.h"
31 
32 QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
33 {
34  if ( !project )
35  return QList<QgsRasterLayer *>();
36 
37  QList<QgsRasterLayer *> layers;
38  Q_FOREACH ( QgsRasterLayer *l, project->layers<QgsRasterLayer *>() )
39  {
40  if ( canUseLayer( l ) )
41  layers << l;
42  }
43 
44  if ( sort )
45  {
46  std::sort( layers.begin(), layers.end(), []( const QgsRasterLayer * a, const QgsRasterLayer * b ) -> bool
47  {
48  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
49  } );
50  }
51  return layers;
52 }
53 
54 QList<QgsVectorLayer *> QgsProcessingUtils::compatibleVectorLayers( QgsProject *project, const QList<int> &geometryTypes, bool sort )
55 {
56  if ( !project )
57  return QList<QgsVectorLayer *>();
58 
59  QList<QgsVectorLayer *> layers;
60  Q_FOREACH ( QgsVectorLayer *l, project->layers<QgsVectorLayer *>() )
61  {
62  if ( canUseLayer( l, geometryTypes ) )
63  layers << l;
64  }
65 
66  if ( sort )
67  {
68  std::sort( layers.begin(), layers.end(), []( const QgsVectorLayer * a, const QgsVectorLayer * b ) -> bool
69  {
70  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
71  } );
72  }
73  return layers;
74 }
75 
76 QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project, bool sort )
77 {
78  if ( !project )
79  return QList<QgsMapLayer *>();
80 
81  QList<QgsMapLayer *> layers;
82  Q_FOREACH ( QgsRasterLayer *rl, compatibleRasterLayers( project, false ) )
83  layers << rl;
84  Q_FOREACH ( QgsVectorLayer *vl, compatibleVectorLayers( project, QList< int >(), false ) )
85  layers << vl;
86 
87  if ( sort )
88  {
89  std::sort( layers.begin(), layers.end(), []( const QgsMapLayer * a, const QgsMapLayer * b ) -> bool
90  {
91  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
92  } );
93  }
94  return layers;
95 }
96 
97 QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store )
98 {
99  if ( !store || string.isEmpty() )
100  return nullptr;
101 
102  QList< QgsMapLayer * > layers = store->mapLayers().values();
103 
104  layers.erase( std::remove_if( layers.begin(), layers.end(), []( QgsMapLayer * layer )
105  {
106  switch ( layer->type() )
107  {
109  return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
111  return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
113  return true;
115  return false;
116  }
117  return true;
118  } ), layers.end() );
119 
120  Q_FOREACH ( QgsMapLayer *l, layers )
121  {
122  if ( l->id() == string )
123  return l;
124  }
125  Q_FOREACH ( QgsMapLayer *l, layers )
126  {
127  if ( l->name() == string )
128  return l;
129  }
130  Q_FOREACH ( QgsMapLayer *l, layers )
131  {
132  if ( normalizeLayerSource( l->source() ) == normalizeLayerSource( string ) )
133  return l;
134  }
135  return nullptr;
136 }
137 
139 class ProjectionSettingRestorer
140 {
141  public:
142 
143  ProjectionSettingRestorer()
144  {
145  QgsSettings settings;
146  previousSetting = settings.value( QStringLiteral( "/Projections/defaultBehavior" ) ).toString();
147  settings.setValue( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "useProject" ) );
148  }
149 
150  ~ProjectionSettingRestorer()
151  {
152  QgsSettings settings;
153  settings.setValue( QStringLiteral( "/Projections/defaultBehavior" ), previousSetting );
154  }
155 
156  QString previousSetting;
157 };
159 
160 QgsMapLayer *QgsProcessingUtils::loadMapLayerFromString( const QString &string )
161 {
162  QStringList components = string.split( '|' );
163  if ( components.isEmpty() )
164  return nullptr;
165 
166  QFileInfo fi;
167  if ( QFileInfo::exists( string ) )
168  fi = QFileInfo( string );
169  else if ( QFileInfo::exists( components.at( 0 ) ) )
170  fi = QFileInfo( components.at( 0 ) );
171  else
172  return nullptr;
173 
174  // TODO - remove when there is a cleaner way to block the unknown projection dialog!
175  ProjectionSettingRestorer restorer;
176  ( void )restorer; // no warnings
177 
178  QString name = fi.baseName();
179 
180  // brute force attempt to load a matching layer
182  options.loadDefaultStyle = false;
183  std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( string, name, QStringLiteral( "ogr" ), options ) );
184  if ( layer->isValid() )
185  {
186  return layer.release();
187  }
188  QgsRasterLayer::LayerOptions rasterOptions;
189  rasterOptions.loadDefaultStyle = false;
190  std::unique_ptr< QgsRasterLayer > rasterLayer( new QgsRasterLayer( string, name, QStringLiteral( "gdal" ), rasterOptions ) );
191  if ( rasterLayer->isValid() )
192  {
193  return rasterLayer.release();
194  }
195  return nullptr;
196 }
197 
198 QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers )
199 {
200  if ( string.isEmpty() )
201  return nullptr;
202 
203  // prefer project layers
204  QgsMapLayer *layer = nullptr;
205  if ( context.project() )
206  {
207  QgsMapLayer *layer = mapLayerFromStore( string, context.project()->layerStore() );
208  if ( layer )
209  return layer;
210  }
211 
212  layer = mapLayerFromStore( string, context.temporaryLayerStore() );
213  if ( layer )
214  return layer;
215 
216  if ( !allowLoadingNewLayers )
217  return nullptr;
218 
219  layer = loadMapLayerFromString( string );
220  if ( layer )
221  {
222  context.temporaryLayerStore()->addMapLayer( layer );
223  return layer;
224  }
225  else
226  {
227  return nullptr;
228  }
229 }
230 
231 QgsProcessingFeatureSource *QgsProcessingUtils::variantToSource( const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue )
232 {
233  QVariant val = value;
234  bool selectedFeaturesOnly = false;
235  if ( val.canConvert<QgsProcessingFeatureSourceDefinition>() )
236  {
237  // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
239  selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
240  val = fromVar.source;
241  }
242 
243  if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( val ) ) )
244  {
245  return new QgsProcessingFeatureSource( layer, context );
246  }
247 
248  QString layerRef;
249  if ( val.canConvert<QgsProperty>() )
250  {
251  layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), fallbackValue.toString() );
252  }
253  else if ( !val.isValid() || val.toString().isEmpty() )
254  {
255  // fall back to default
256  if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( fallbackValue ) ) )
257  {
258  return new QgsProcessingFeatureSource( layer, context );
259  }
260 
261  layerRef = fallbackValue.toString();
262  }
263  else
264  {
265  layerRef = val.toString();
266  }
267 
268  if ( layerRef.isEmpty() )
269  return nullptr;
270 
271  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context ) );
272  if ( !vl )
273  return nullptr;
274 
275  if ( selectedFeaturesOnly )
276  {
277  return new QgsProcessingFeatureSource( new QgsVectorLayerSelectedFeatureSource( vl ), context, true );
278  }
279  else
280  {
281  return new QgsProcessingFeatureSource( vl, context );
282  }
283 }
284 
285 bool QgsProcessingUtils::canUseLayer( const QgsRasterLayer *layer )
286 {
287  // only gdal file-based layers
288  return layer && layer->providerType() == QStringLiteral( "gdal" );
289 }
290 
291 bool QgsProcessingUtils::canUseLayer( const QgsVectorLayer *layer, const QList<int> &sourceTypes )
292 {
293  return layer &&
294  ( sourceTypes.isEmpty()
295  || ( sourceTypes.contains( QgsProcessing::TypeVectorPoint ) && layer->geometryType() == QgsWkbTypes::PointGeometry )
296  || ( sourceTypes.contains( QgsProcessing::TypeVectorLine ) && layer->geometryType() == QgsWkbTypes::LineGeometry )
297  || ( sourceTypes.contains( QgsProcessing::TypeVectorPolygon ) && layer->geometryType() == QgsWkbTypes::PolygonGeometry )
298  || ( sourceTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) && layer->isSpatial() )
299  || sourceTypes.contains( QgsProcessing::TypeVector )
300  );
301 }
302 
303 QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
304 {
305  QString normalized = source;
306  normalized.replace( '\\', '/' );
307  return normalized.trimmed();
308 }
309 
310 QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
311 {
312  QString s = string;
313  s.replace( '"', QStringLiteral( "\\\"" ) );
314  s.replace( '\'', QStringLiteral( "\\\'" ) );
315  s = s.prepend( '\'' ).append( '\'' );
316  return s;
317 }
318 
319 void QgsProcessingUtils::parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter )
320 {
321  QRegularExpression splitRx( QStringLiteral( "^(.{3,}?):(.*)$" ) );
322  QRegularExpressionMatch match = splitRx.match( destination );
323  if ( match.hasMatch() )
324  {
325  providerKey = match.captured( 1 );
326  if ( providerKey == QStringLiteral( "postgis" ) ) // older processing used "postgis" instead of "postgres"
327  {
328  providerKey = QStringLiteral( "postgres" );
329  }
330  uri = match.captured( 2 );
331  if ( providerKey == QLatin1String( "ogr" ) )
332  {
333  QgsDataSourceUri dsUri( uri );
334  if ( !dsUri.database().isEmpty() )
335  {
336  if ( !dsUri.table().isEmpty() )
337  {
338  layerName = dsUri.table();
339  options.insert( QStringLiteral( "layerName" ), layerName );
340  }
341  uri = dsUri.database();
342  }
343  options.insert( QStringLiteral( "update" ), true );
344  }
345  useWriter = false;
346  }
347  else
348  {
349  useWriter = true;
350  providerKey = QStringLiteral( "ogr" );
351  QRegularExpression splitRx( QStringLiteral( "^(.*)\\.(.*?)$" ) );
352  QRegularExpressionMatch match = splitRx.match( destination );
353  QString extension;
354  if ( match.hasMatch() )
355  {
356  extension = match.captured( 2 );
357  format = QgsVectorFileWriter::driverForExtension( extension );
358  }
359 
360  if ( format.isEmpty() )
361  {
362  format = QStringLiteral( "GPKG" );
363  destination = destination + QStringLiteral( ".gpkg" );
364  }
365 
366  options.insert( QStringLiteral( "driverName" ), format );
367  uri = destination;
368  }
369 }
370 
371 QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions )
372 {
373  QVariantMap options = createOptions;
374  if ( !options.contains( QStringLiteral( "fileEncoding" ) ) )
375  {
376  // no destination encoding specified, use default
377  options.insert( QStringLiteral( "fileEncoding" ), context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding() );
378  }
379 
380  if ( destination.isEmpty() || destination.startsWith( QStringLiteral( "memory:" ) ) )
381  {
382  // strip "memory:" from start of destination
383  if ( destination.startsWith( QStringLiteral( "memory:" ) ) )
384  destination = destination.mid( 7 );
385 
386  if ( destination.isEmpty() )
387  destination = QStringLiteral( "output" );
388 
389  // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
390  std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ) );
391  if ( !layer || !layer->isValid() )
392  {
393  throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
394  }
395 
396  // update destination to layer ID
397  destination = layer->id();
398 
399  // this is a factory, so we need to return a proxy
400  std::unique_ptr< QgsProcessingFeatureSink > sink( new QgsProcessingFeatureSink( layer->dataProvider(), destination, context ) );
401  context.temporaryLayerStore()->addMapLayer( layer.release() );
402 
403  return sink.release();
404  }
405  else
406  {
407  QString providerKey;
408  QString uri;
409  QString layerName;
410  QString format;
411  bool useWriter = false;
412  parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter );
413 
414  if ( useWriter && providerKey == QLatin1String( "ogr" ) )
415  {
416  // use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
417  // us to use any OGR format which supports feature addition
418  QString finalFileName;
419  std::unique_ptr< QgsVectorFileWriter > writer = qgis::make_unique< QgsVectorFileWriter >( destination, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
420  QgsVectorFileWriter::defaultLayerOptions( format ), &finalFileName );
421 
422  if ( writer->hasError() )
423  {
424  throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
425  }
426  destination = finalFileName;
427  return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
428  }
429  else
430  {
431  //create empty layer
432  std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, true, options ) );
433  if ( exporter->errorCode() )
434  {
435  throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
436  }
437 
438  // use destination string as layer name (eg "postgis:..." )
439  if ( !layerName.isEmpty() )
440  uri += QStringLiteral( "|layername=%1" ).arg( layerName );
441  std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( uri, destination, providerKey ) );
442  // update destination to layer ID
443  destination = layer->id();
444 
445  context.temporaryLayerStore()->addMapLayer( layer.release() );
446  return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
447  }
448  }
449  return nullptr;
450 }
451 
452 void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )
453 {
454  *sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
455 }
456 
457 
459 {
460  QgsRectangle extent;
461  Q_FOREACH ( QgsMapLayer *layer, layers )
462  {
463  if ( !layer )
464  continue;
465 
466  if ( crs.isValid() )
467  {
468  //transform layer extent to target CRS
470  QgsCoordinateTransform ct( layer->crs(), crs );
472  try
473  {
474  QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
475  extent.combineExtentWith( reprojExtent );
476  }
477  catch ( QgsCsException & )
478  {
479  // can't reproject... what to do here? hmmm?
480  // let's ignore this layer for now, but maybe we should just use the original extent?
481  }
482  }
483  else
484  {
485  extent.combineExtentWith( layer->extent() );
486  }
487 
488  }
489  return extent;
490 }
491 
492 QVariant QgsProcessingUtils::generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context )
493 {
494  if ( !input.isValid() )
495  return QStringLiteral( "memory:%1" ).arg( id.toString() );
496 
497  if ( input.canConvert<QgsProcessingOutputLayerDefinition>() )
498  {
500  QVariant newSink = generateIteratingDestination( fromVar.sink, id, context );
501  fromVar.sink = QgsProperty::fromValue( newSink );
502  return fromVar;
503  }
504  else if ( input.canConvert<QgsProperty>() )
505  {
506  QString res = input.value< QgsProperty>().valueAsString( context.expressionContext() );
507  return generateIteratingDestination( res, id, context );
508  }
509  else
510  {
511  QString res = input.toString();
512  if ( res.startsWith( QStringLiteral( "memory:" ) ) )
513  {
514  return res + '_' + id.toString();
515  }
516  else
517  {
518  // assume a filename type output for now
519  // TODO - uris?
520  int lastIndex = res.lastIndexOf( '.' );
521  return res.left( lastIndex ) + '_' + id.toString() + res.mid( lastIndex );
522  }
523  }
524 }
525 
527 {
528  static QString sFolder;
529  static QMutex sMutex;
530  sMutex.lock();
531  if ( sFolder.isEmpty() )
532  {
533  QString subPath = QUuid::createUuid().toString().remove( '-' ).remove( '{' ).remove( '}' );
534  sFolder = QDir::tempPath() + QStringLiteral( "/processing_" ) + subPath;
535  if ( !QDir( sFolder ).exists() )
536  QDir().mkpath( sFolder );
537  }
538  sMutex.unlock();
539  return sFolder;
540 }
541 
542 QString QgsProcessingUtils::generateTempFilename( const QString &basename )
543 {
544  QString subPath = QUuid::createUuid().toString().remove( '-' ).remove( '{' ).remove( '}' );
545  QString path = tempFolder() + '/' + subPath;
546  if ( !QDir( path ).exists() ) //make sure the directory exists - it shouldn't, but lets be safe...
547  {
548  QDir tmpDir;
549  tmpDir.mkdir( path );
550  }
551  return path + '/' + QgsFileUtils::stringToSafeFilename( basename );
552 }
553 
555 {
556  auto getText = [map]( const QString & key )->QString
557  {
558  if ( map.contains( key ) )
559  return map.value( key ).toString();
560  return QString();
561  };
562 
563  QString s = QObject::tr( "<html><body><h2>Algorithm description</h2>\n" );
564  s += QStringLiteral( "<p>" ) + getText( QStringLiteral( "ALG_DESC" ) ) + QStringLiteral( "</p>\n" );
565  s += QObject::tr( "<h2>Input parameters</h2>\n" );
566 
567  Q_FOREACH ( const QgsProcessingParameterDefinition *def, algorithm->parameterDefinitions() )
568  {
569  s += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
570  s += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
571  }
572  s += QObject::tr( "<h2>Outputs</h2>\n" );
573  Q_FOREACH ( const QgsProcessingOutputDefinition *def, algorithm->outputDefinitions() )
574  {
575  s += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
576  s += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
577  }
578  s += QLatin1String( "<br>" );
579  s += QObject::tr( "<p align=\"right\">Algorithm author: %1</p>" ).arg( getText( QStringLiteral( "ALG_CREATOR" ) ) );
580  s += QObject::tr( "<p align=\"right\">Help author: %1</p>" ).arg( getText( QStringLiteral( "ALG_HELP_CREATOR" ) ) );
581  s += QObject::tr( "<p align=\"right\">Algorithm version: %1</p>" ).arg( getText( QStringLiteral( "ALG_VERSION" ) ) );
582  s += QStringLiteral( "</body></html>" );
583  return s;
584 }
585 
586 QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
587 {
588  bool requiresTranslation = selectedFeaturesOnly;
589  if ( !selectedFeaturesOnly )
590  {
591  QFileInfo fi( vl->source() );
592  requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
593  }
594 
595  if ( requiresTranslation )
596  {
597  QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat );
598 
599  QgsVectorFileWriter writer( temp, context.defaultEncoding(),
600  vl->fields(), vl->wkbType(), vl->crs(), QgsVectorFileWriter::driverForExtension( preferredFormat ) );
601  QgsFeature f;
603  if ( selectedFeaturesOnly )
604  it = vl->getSelectedFeatures();
605  else
606  it = vl->getFeatures();
607 
608  while ( it.nextFeature( f ) )
609  {
610  if ( feedback->isCanceled() )
611  return QString();
612  writer.addFeature( f, QgsFeatureSink::FastInsert );
613  }
614  return temp;
615  }
616  else
617  {
618  return vl->source();
619  }
620 }
621 
623 {
624  QgsFields outFields = fieldsA;
625  QSet< QString > usedNames;
626  for ( const QgsField &f : fieldsA )
627  {
628  usedNames.insert( f.name().toLower() );
629  }
630 
631  for ( const QgsField &f : fieldsB )
632  {
633  if ( usedNames.contains( f.name().toLower() ) )
634  {
635  int idx = 2;
636  QString newName = f.name() + '_' + QString::number( idx );
637  while ( usedNames.contains( newName.toLower() ) )
638  {
639  idx++;
640  newName = f.name() + '_' + QString::number( idx );
641  }
642  QgsField newField = f;
643  newField.setName( newName );
644  outFields.append( newField );
645  usedNames.insert( newName.toLower() );
646  }
647  else
648  {
649  usedNames.insert( f.name().toLower() );
650  outFields.append( f );
651  }
652  }
653 
654  return outFields;
655 }
656 
657 
658 QList<int> QgsProcessingUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
659 {
660  QList<int> indices;
661  if ( !fieldNames.isEmpty() )
662  {
663  indices.reserve( fieldNames.count() );
664  for ( const QString &f : fieldNames )
665  {
666  int idx = fields.lookupField( f );
667  if ( idx >= 0 )
668  indices.append( idx );
669  }
670  }
671  else
672  {
673  indices.reserve( fields.count() );
674  for ( int i = 0; i < fields.count(); ++i )
675  indices.append( i );
676  }
677  return indices;
678 }
679 
680 
681 QgsFields QgsProcessingUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
682 {
683  QgsFields fieldsSubset;
684  for ( int i : indices )
685  fieldsSubset.append( fields.at( i ) );
686  return fieldsSubset;
687 }
688 
689 
690 //
691 // QgsProcessingFeatureSource
692 //
693 
694 QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource )
695  : mSource( originalSource )
696  , mOwnsSource( ownsOriginalSource )
697  , mInvalidGeometryCheck( context.invalidGeometryCheck() )
698  , mInvalidGeometryCallback( context.invalidGeometryCallback() )
699  , mTransformErrorCallback( context.transformErrorCallback() )
700 {}
701 
703 {
704  if ( mOwnsSource )
705  delete mSource;
706 }
707 
709 {
710  QgsFeatureRequest req( request );
711  req.setTransformErrorCallback( mTransformErrorCallback );
712 
713  if ( flags & FlagSkipGeometryValidityChecks )
715  else
716  {
717  req.setInvalidGeometryCheck( mInvalidGeometryCheck );
718  req.setInvalidGeometryCallback( mInvalidGeometryCallback );
719  }
720 
721  return mSource->getFeatures( req );
722 }
723 
725 {
726  QgsFeatureRequest req( request );
727  req.setInvalidGeometryCheck( mInvalidGeometryCheck );
728  req.setInvalidGeometryCallback( mInvalidGeometryCallback );
729  req.setTransformErrorCallback( mTransformErrorCallback );
730  return mSource->getFeatures( req );
731 }
732 
734 {
735  return mSource->sourceCrs();
736 }
737 
739 {
740  return mSource->fields();
741 }
742 
744 {
745  return mSource->wkbType();
746 }
747 
749 {
750  return mSource->featureCount();
751 }
752 
754 {
755  return mSource->sourceName();
756 
757 }
758 
759 QSet<QVariant> QgsProcessingFeatureSource::uniqueValues( int fieldIndex, int limit ) const
760 {
761  return mSource->uniqueValues( fieldIndex, limit );
762 }
763 
764 QVariant QgsProcessingFeatureSource::minimumValue( int fieldIndex ) const
765 {
766  return mSource->minimumValue( fieldIndex );
767 }
768 
769 QVariant QgsProcessingFeatureSource::maximumValue( int fieldIndex ) const
770 {
771  return mSource->maximumValue( fieldIndex );
772 }
773 
775 {
776  return mSource->sourceExtent();
777 }
778 
780 {
781  return mSource->allFeatureIds();
782 }
783 
785 {
786  QgsExpressionContextScope *expressionContextScope = nullptr;
787  QgsExpressionContextScopeGenerator *generator = dynamic_cast<QgsExpressionContextScopeGenerator *>( mSource );
788  if ( generator )
789  {
790  expressionContextScope = generator->createExpressionContextScope();
791  }
792  return expressionContextScope;
793 }
794 
795 
796 //
797 // QgsProcessingFeatureSink
798 //
799 QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
800  : QgsProxyFeatureSink( originalSink )
801  , mContext( context )
802  , mSinkName( sinkName )
803  , mOwnsSink( ownsOriginalSink )
804 {}
805 
807 {
808  if ( mOwnsSink )
809  delete destinationSink();
810 }
811 
812 bool QgsProcessingFeatureSink::addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags )
813 {
814  bool result = QgsProxyFeatureSink::addFeature( feature, flags );
815  if ( !result )
816  mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
817  return result;
818 }
819 
820 bool QgsProcessingFeatureSink::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags )
821 {
822  bool result = QgsProxyFeatureSink::addFeatures( features, flags );
823  if ( !result )
824  mContext.feedback()->reportError( QObject::tr( "%1 feature(s) could not be written to %2" ).arg( features.count() ).arg( mSinkName ) );
825  return result;
826 }
827 
828 bool QgsProcessingFeatureSink::addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags )
829 {
830  bool result = !QgsProxyFeatureSink::addFeatures( iterator, flags );
831  if ( !result )
832  mContext.feedback()->reportError( QObject::tr( "Features could not be written to %2" ).arg( mSinkName ) );
833  return result;
834 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
QgsProperty sink
Sink/layer definition.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true)
Interprets a string as a map layer within the supplied context.
Wrapper for iterator of features from vector data provider or vector layer.
virtual QgsRectangle sourceExtent() const
Returns the extent of all geometries from the source.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
A rectangle specified with double values.
Definition: qgsrectangle.h:40
QgsWkbTypes::Type wkbType() const override
Returns the geometry type for features returned by this source.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
QString table() const
Returns the table.
QString description() const
Returns the description for the output.
QgsFeatureRequest & setInvalidGeometryCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering an invalid geometry and invalidGeometryCheck() is s...
Base class for providing feedback from a processing algorithm.
QgsProcessingParameterDefinitions parameterDefinitions() const
Returns an ordered list of parameter definitions utilized by the algorithm.
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
Encapsulates settings relating to a feature sink or output raster layer for a processing algorithm...
static void createFeatureSinkPython(QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap()) SIP_THROW(QgsProcessingException)
Creates a feature sink ready for adding features.
A simple feature sink which proxies feature addition on to another feature sink.
static QgsRectangle combineLayerExtents(const QList< QgsMapLayer *> &layers, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Combines the extent of several map layers.
QgsFeatureRequest & setInvalidGeometryCheck(InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
QgsProcessingFeatureSource(QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource=false)
Constructor for QgsProcessingFeatureSource, accepting an original feature source originalSource and p...
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Setting options for loading vector layers.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:544
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:549
QString name() const
Returns the name of the output.
QgsFeatureIds allFeatureIds() const override
Returns a list of all feature IDs for features present in the source.
virtual QgsWkbTypes::Type wkbType() const =0
Returns the geometry type for features returned by this source.
An interface for objects which accept features via addFeature(s) methods.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:538
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 stringToPythonLiteral(const QString &string)
Converts a string to a Python string literal.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource, modifying results according to the settings in a QgsProcessingContext.
Container of fields for a vector layer.
Definition: qgsfields.h:42
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:135
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request, Flags flags) const
Returns an iterator for the features in the source, respecting the supplied feature flags...
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...
A convenience class for writing vector files to disk.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
QVariant maximumValue(int fieldIndex) const override
Returns the maximum value for an attribute column or an invalid variant in case of error...
static QString convertToCompatibleFormat(const QgsVectorLayer *layer, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback)
Converts a source vector layer to a file path to a vector layer of compatible format.
QgsRectangle sourceExtent() const override
Returns the extent of all geometries from the source.
static QString normalizeLayerSource(const QString &source)
Normalizes a layer source string for safe comparison across different operating system environments...
Abstract base class for processing algorithms.
int count() const
Returns number of items.
Definition: qgsfields.cpp:115
QgsMapLayerStore * layerStore()
Returns a pointer to the project&#39;s internal layer store.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
bool selectedFeaturesOnly
True if only selected features in the source should be used by algorithms.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
Added in 3.2.
Definition: qgsmaplayer.h:107
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Returns the set of unique values contained within the specified fieldIndex from this source...
QgsProcessingFeedback * feedback()
Returns the associated feedback object.
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
virtual QVariant minimumValue(int fieldIndex) const
Returns the minimum value for an attribute column or an invalid variant in case of error...
QgsFields fields() const override
Returns the fields associated with features in the source.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution...
QgsProperty source
Source definition.
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap())
Creates a feature sink ready for adding features.
QgsFields fields() const override
Returns the list of fields of this layer.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
QVariant minimumValue(int fieldIndex) const override
Returns the minimum value for an attribute column or an invalid variant in case of error...
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB)
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an &#39;...
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
QgsCoordinateReferenceSystem sourceCrs() const override
Returns the coordinate reference system for features in the source.
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
QgsWkbTypes::Type wkbType() const override
Returns the WKBType or WKBUnknown in case of error.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
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...
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
Reads and writes project states.
Definition: qgsproject.h:85
Vector polygon layers.
Definition: qgsprocessing.h:50
No invalid geometry checking.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsProcessingFeatureSource * variantToSource(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a new feature source.
A store for object properties.
Definition: qgsproperty.h:229
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
virtual QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const
Returns the set of unique values contained within the specified fieldIndex from this source...
QgsExpressionContext & expressionContext()
Returns the expression context.
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...
A convenience class for exporting vector layers to a destination data provider.
static QString tempFolder()
Returns a session specific processing temporary folder for use in processing algorithms.
virtual QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
QString name() const
Returns the name of the parameter.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
QgsExpressionContextScope * createExpressionContextScope() const
Returns an expression context scope suitable for this source.
Encapsulates settings relating to a feature source input to a processing algorithm.
QgsProcessingOutputDefinitions outputDefinitions() const
Returns an ordered list of output definitions utilized by the algorithm.
static QString generateTempFilename(const QString &basename)
Returns a temporary filename for a given file, putting it into a temporary folder (creating that fold...
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:352
Base class for the definition of processing outputs.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:539
QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
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 indicesToFields(const QList< int > &indices, const QgsFields &fields)
Returns a subset of fields based on the indices of desired fields.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:723
virtual QgsExpressionContextScope * createExpressionContextScope() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all layers by layer ID.
QgsFeatureSource subclass for the selected features from a QgsVectorLayer.
QgsMapLayer * addMapLayer(QgsMapLayer *layer, bool takeOwnership=true)
Add a layer to the store.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Vector point layers.
Definition: qgsprocessing.h:48
Abstract interface for generating an expression context scope.
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
An interface for objects which provide features via a getFeatures method.
QString source() const
Returns the source for the layer.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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...
QString sourceName() const override
Returns a friendly display name for the source.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
This class represents a coordinate reference system (CRS).
Base class for the definition of processing parameters.
Vector line layers.
Definition: qgsprocessing.h:49
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:53
Class for doing transforms between two map coordinate systems.
QString name
Definition: qgsmaplayer.h:65
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, QgsWkbTypes::Type geometryType=QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Creates a new memory layer using the specified parameters.
virtual QVariant maximumValue(int fieldIndex) const
Returns the maximum value for an attribute column or an invalid variant in case of error...
bool nextFeature(QgsFeature &f)
QgsFeatureSink * destinationSink()
Returns the destination QgsFeatureSink which the proxy will forward features to.
virtual QString sourceName() const =0
Returns a friendly display name for the source.
long featureCount() const override
Returns the number of features contained in the source, or -1 if the feature count is unknown...
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
Represents a vector layer which manages a vector based data sets.
virtual QgsFeatureIds allFeatureIds() const
Returns a list of all feature IDs for features present in the source.
Contains information about the context in which a processing algorithm is executed.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
QString description() const
Returns the description for the parameter.
QString database() const
Returns the database.
Any vector layer with geometry.
Definition: qgsprocessing.h:47
Setting options for loading raster layers.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
virtual long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown...
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QgsProcessingFeatureSink(QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink=false)
Constructor for QgsProcessingFeatureSink, accepting an original feature sink originalSink and process...