QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsprocessingmodelalgorithm.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprocessingmodelalgorithm.cpp
3  ------------------------------
4  begin : June 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 
19 #include "qgsprocessingregistry.h"
20 #include "qgsprocessingfeedback.h"
21 #include "qgsprocessingutils.h"
22 #include "qgis.h"
23 #include "qgsxmlutils.h"
24 #include "qgsexception.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsstringutils.h"
27 #include "qgsapplication.h"
31 
32 #include <QFile>
33 #include <QTextStream>
34 #include <QRegularExpression>
36 
37 QgsProcessingModelAlgorithm::QgsProcessingModelAlgorithm( const QString &name, const QString &group, const QString &groupId )
38  : mModelName( name.isEmpty() ? QObject::tr( "model" ) : name )
39  , mModelGroup( group )
40  , mModelGroupId( groupId )
41 {}
42 
43 void QgsProcessingModelAlgorithm::initAlgorithm( const QVariantMap & )
44 {
45 }
46 
47 QString QgsProcessingModelAlgorithm::name() const
48 {
49  return mModelName;
50 }
51 
52 QString QgsProcessingModelAlgorithm::displayName() const
53 {
54  return mModelName;
55 }
56 
57 QString QgsProcessingModelAlgorithm::group() const
58 {
59  return mModelGroup;
60 }
61 
62 QString QgsProcessingModelAlgorithm::groupId() const
63 {
64  return mModelGroupId;
65 }
66 
67 QIcon QgsProcessingModelAlgorithm::icon() const
68 {
69  return QgsApplication::getThemeIcon( QStringLiteral( "/processingModel.svg" ) );
70 }
71 
72 QString QgsProcessingModelAlgorithm::svgIconPath() const
73 {
74  return QgsApplication::iconPath( QStringLiteral( "processingModel.svg" ) );
75 }
76 
77 QString QgsProcessingModelAlgorithm::shortHelpString() const
78 {
79  if ( mHelpContent.empty() )
80  return QString();
81 
82  return QgsProcessingUtils::formatHelpMapAsHtml( mHelpContent, this );
83 }
84 
85 QString QgsProcessingModelAlgorithm::shortDescription() const
86 {
87  return mHelpContent.value( QStringLiteral( "SHORT_DESCRIPTION" ) ).toString();
88 }
89 
90 QString QgsProcessingModelAlgorithm::helpUrl() const
91 {
92  return mHelpContent.value( QStringLiteral( "HELP_URL" ) ).toString();
93 }
94 
95 QgsProcessingAlgorithm::Flags QgsProcessingModelAlgorithm::flags() const
96 {
97  // TODO - check child algorithms, if they all support threading, then the model supports threading...
99 }
100 
101 QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const QgsProcessingModelChildAlgorithm &child, const QVariantMap &modelParameters, const QVariantMap &results, const QgsExpressionContext &expressionContext, QString &error ) const
102 {
103  error.clear();
104  auto evaluateSources = [ =, &error ]( const QgsProcessingParameterDefinition * def )->QVariant
105  {
106  const QgsProcessingModelChildParameterSources paramSources = child.parameterSources().value( def->name() );
107 
108  QString expressionText;
109  QVariantList paramParts;
110  for ( const QgsProcessingModelChildParameterSource &source : paramSources )
111  {
112  switch ( source.source() )
113  {
114  case QgsProcessingModelChildParameterSource::StaticValue:
115  paramParts << source.staticValue();
116  break;
117 
118  case QgsProcessingModelChildParameterSource::ModelParameter:
119  paramParts << modelParameters.value( source.parameterName() );
120  break;
121 
122  case QgsProcessingModelChildParameterSource::ChildOutput:
123  {
124  QVariantMap linkedChildResults = results.value( source.outputChildId() ).toMap();
125  paramParts << linkedChildResults.value( source.outputName() );
126  break;
127  }
128 
129  case QgsProcessingModelChildParameterSource::Expression:
130  {
131  QgsExpression exp( source.expression() );
132  paramParts << exp.evaluate( &expressionContext );
133  if ( exp.hasEvalError() )
134  {
135  error = QObject::tr( "Could not evaluate expression for parameter %1 for %2: %3" ).arg( def->name(), child.description(), exp.evalErrorString() );
136  }
137  break;
138  }
139  case QgsProcessingModelChildParameterSource::ExpressionText:
140  {
141  expressionText = QgsExpression::replaceExpressionText( source.expressionText(), &expressionContext );
142  break;
143  }
144 
145  case QgsProcessingModelChildParameterSource::ModelOutput:
146  break;
147  }
148  }
149 
150  if ( ! expressionText.isEmpty() )
151  {
152  return expressionText;
153  }
154  else if ( paramParts.count() == 1 )
155  return paramParts.at( 0 );
156  else
157  return paramParts;
158  };
159 
160 
161  QVariantMap childParams;
162  const auto constParameterDefinitions = child.algorithm()->parameterDefinitions();
163  for ( const QgsProcessingParameterDefinition *def : constParameterDefinitions )
164  {
165  if ( !def->isDestination() )
166  {
167  if ( !child.parameterSources().contains( def->name() ) )
168  continue; // use default value
169 
170  const QVariant value = evaluateSources( def );
171  childParams.insert( def->name(), value );
172  }
173  else
174  {
175  const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
176 
177  // is destination linked to one of the final outputs from this model?
178  bool isFinalOutput = false;
179  QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
180  QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
181  for ( ; outputIt != outputs.constEnd(); ++outputIt )
182  {
183  if ( outputIt->childOutputName() == destParam->name() )
184  {
185  QString paramName = child.childId() + ':' + outputIt.key();
186  if ( modelParameters.contains( paramName ) )
187  {
188  QVariant value = modelParameters.value( paramName );
189  if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
190  {
191  // make sure layout output name is correctly set
192  QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
193  fromVar.destinationName = outputIt.key();
194  value = QVariant::fromValue( fromVar );
195  }
196 
197  childParams.insert( destParam->name(), value );
198  }
199  isFinalOutput = true;
200  break;
201  }
202  }
203 
204  bool hasExplicitDefinition = false;
205  if ( !isFinalOutput && child.parameterSources().contains( def->name() ) )
206  {
207  // explicitly defined source for output
208  const QVariant value = evaluateSources( def );
209  if ( value.isValid() )
210  {
211  childParams.insert( def->name(), value );
212  hasExplicitDefinition = true;
213  }
214  }
215 
216  if ( !isFinalOutput && !hasExplicitDefinition )
217  {
218  // output is temporary
219 
220  // check whether it's optional, and if so - is it required?
221  bool required = true;
223  {
224  required = childOutputIsRequired( child.childId(), destParam->name() );
225  }
226 
227  // not optional, or required elsewhere in model
228  if ( required )
229  childParams.insert( destParam->name(), destParam->generateTemporaryDestination() );
230  }
231  }
232  }
233  return childParams;
234 }
235 
236 bool QgsProcessingModelAlgorithm::childOutputIsRequired( const QString &childId, const QString &outputName ) const
237 {
238  // look through all child algs
239  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
240  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
241  {
242  if ( childIt->childId() == childId || !childIt->isActive() )
243  continue;
244 
245  // look through all sources for child
246  QMap<QString, QgsProcessingModelChildParameterSources> candidateChildParams = childIt->parameterSources();
247  QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator childParamIt = candidateChildParams.constBegin();
248  for ( ; childParamIt != candidateChildParams.constEnd(); ++childParamIt )
249  {
250  const auto constValue = childParamIt.value();
251  for ( const QgsProcessingModelChildParameterSource &source : constValue )
252  {
253  if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
254  && source.outputChildId() == childId
255  && source.outputName() == outputName )
256  {
257  return true;
258  }
259  }
260  }
261  }
262  return false;
263 }
264 
265 QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
266 {
267  QSet< QString > toExecute;
268  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
269  QSet< QString > broken;
270  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
271  {
272  if ( childIt->isActive() )
273  {
274  if ( childIt->algorithm() )
275  toExecute.insert( childIt->childId() );
276  else
277  broken.insert( childIt->childId() );
278  }
279  }
280 
281  if ( !broken.empty() )
282  throw QgsProcessingException( QCoreApplication::translate( "QgsProcessingModelAlgorithm", "Cannot run model, the following algorithms are not available on this system: %1" ).arg( broken.values().join( QLatin1String( ", " ) ) ) );
283 
284  QElapsedTimer totalTime;
285  totalTime.start();
286 
287  QgsProcessingMultiStepFeedback modelFeedback( toExecute.count(), feedback );
288  QgsExpressionContext baseContext = createExpressionContext( parameters, context );
289 
290  QVariantMap childResults;
291  QVariantMap childInputs;
292 
293  const bool verboseLog = context.logLevel() == QgsProcessingContext::Verbose;
294 
295  QVariantMap finalResults;
296  QSet< QString > executed;
297  bool executedAlg = true;
298  while ( executedAlg && executed.count() < toExecute.count() )
299  {
300  executedAlg = false;
301  for ( const QString &childId : std::as_const( toExecute ) )
302  {
303  if ( feedback && feedback->isCanceled() )
304  break;
305 
306  if ( executed.contains( childId ) )
307  continue;
308 
309  bool canExecute = true;
310  const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
311  for ( const QString &dependency : dependencies )
312  {
313  if ( !executed.contains( dependency ) )
314  {
315  canExecute = false;
316  break;
317  }
318  }
319 
320  if ( !canExecute )
321  continue;
322 
323  executedAlg = true;
324 
325  const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
326  std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
327 
328  const bool skipGenericLogging = !verboseLog || childAlg->flags() & QgsProcessingAlgorithm::FlagSkipGenericModelLogging;
329  if ( feedback && !skipGenericLogging )
330  feedback->pushDebugInfo( QObject::tr( "Prepare algorithm: %1" ).arg( childId ) );
331 
332  QgsExpressionContext expContext = baseContext;
333  expContext << QgsExpressionContextUtils::processingAlgorithmScope( child.algorithm(), parameters, context )
334  << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
335  context.setExpressionContext( expContext );
336 
337  QString error;
338  QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error );
339  if ( !error.isEmpty() )
340  throw QgsProcessingException( error );
341 
342  if ( feedback && !skipGenericLogging )
343  feedback->setProgressText( QObject::tr( "Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
344 
345  childInputs.insert( childId, childParams );
346  QStringList params;
347  for ( auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
348  {
349  params << QStringLiteral( "%1: %2" ).arg( childParamIt.key(),
350  child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
351  }
352 
353  if ( feedback && !skipGenericLogging )
354  {
355  feedback->pushInfo( QObject::tr( "Input Parameters:" ) );
356  feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( params.join( QLatin1String( ", " ) ) ) );
357  }
358 
359  QElapsedTimer childTime;
360  childTime.start();
361 
362  bool ok = false;
363  QVariantMap results = childAlg->run( childParams, context, &modelFeedback, &ok, child.configuration() );
364  if ( !ok )
365  {
366  const QString error = ( childAlg->flags() & QgsProcessingAlgorithm::FlagCustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
367  throw QgsProcessingException( error );
368  }
369  childResults.insert( childId, results );
370 
371  // look through child alg's outputs to determine whether any of these should be copied
372  // to the final model outputs
373  QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
374  QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
375  for ( ; outputIt != outputs.constEnd(); ++outputIt )
376  {
377  finalResults.insert( childId + ':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
378  }
379 
380  executed.insert( childId );
381 
382  std::function< void( const QString &, const QString & )> pruneAlgorithmBranchRecursive;
383  pruneAlgorithmBranchRecursive = [&]( const QString & id, const QString &branch = QString() )
384  {
385  const QSet<QString> toPrune = dependentChildAlgorithms( id, branch );
386  for ( const QString &targetId : toPrune )
387  {
388  if ( executed.contains( targetId ) )
389  continue;
390 
391  executed.insert( targetId );
392  pruneAlgorithmBranchRecursive( targetId, branch );
393  }
394  };
395 
396  // prune remaining algorithms if they are dependent on a branch from this child which didn't eventuate
397  const QgsProcessingOutputDefinitions outputDefs = childAlg->outputDefinitions();
398  for ( const QgsProcessingOutputDefinition *outputDef : outputDefs )
399  {
400  if ( outputDef->type() == QgsProcessingOutputConditionalBranch::typeName() && !results.value( outputDef->name() ).toBool() )
401  {
402  pruneAlgorithmBranchRecursive( childId, outputDef->name() );
403  }
404  }
405 
407  {
408  // check if any dependent algorithms should be canceled based on the outputs of this algorithm run
409  // first find all direct dependencies of this algorithm by looking through all remaining child algorithms
410  for ( const QString &candidateId : std::as_const( toExecute ) )
411  {
412  if ( executed.contains( candidateId ) )
413  continue;
414 
415  // a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
416  // algorithm's outputs
417  const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
418  const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
419  QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
420  bool pruned = false;
421  for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
422  {
423  for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
424  {
425  if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
426  {
427  // ok, this one is dependent on the current alg. Did we get a value for it?
428  if ( !results.contains( source.outputName() ) )
429  {
430  // oh no, nothing returned for this parameter. Gotta trim the branch back!
431  pruned = true;
432  // skip the dependent alg..
433  executed.insert( candidateId );
434  //... and everything which depends on it
435  pruneAlgorithmBranchRecursive( candidateId, QString() );
436  break;
437  }
438  }
439  }
440  if ( pruned )
441  break;
442  }
443  }
444  }
445 
446  childAlg.reset( nullptr );
447  modelFeedback.setCurrentStep( executed.count() );
448  if ( feedback && !skipGenericLogging )
449  feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
450  }
451 
452  if ( feedback && feedback->isCanceled() )
453  break;
454  }
455  if ( feedback )
456  feedback->pushDebugInfo( QObject::tr( "Model processed OK. Executed %n algorithm(s) total in %1 s.", nullptr, executed.count() ).arg( totalTime.elapsed() / 1000.0 ) );
457 
458  mResults = finalResults;
459  mResults.insert( QStringLiteral( "CHILD_RESULTS" ), childResults );
460  mResults.insert( QStringLiteral( "CHILD_INPUTS" ), childInputs );
461  return mResults;
462 }
463 
464 QString QgsProcessingModelAlgorithm::sourceFilePath() const
465 {
466  return mSourceFile;
467 }
468 
469 void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
470 {
471  mSourceFile = sourceFile;
472 }
473 
474 bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath() const
475 {
476  if ( mSourceFile.isEmpty() )
477  return false;
478 
479  const QFileInfo fi( mSourceFile );
480  return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
481 }
482 
483 QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const int indentSize ) const
484 {
485  QStringList fileDocString;
486  fileDocString << QStringLiteral( "\"\"\"" );
487  fileDocString << QStringLiteral( "Model exported as python." );
488  fileDocString << QStringLiteral( "Name : %1" ).arg( displayName() );
489  fileDocString << QStringLiteral( "Group : %1" ).arg( group() );
490  fileDocString << QStringLiteral( "With QGIS : %1" ).arg( Qgis::versionInt() );
491  fileDocString << QStringLiteral( "\"\"\"" );
492  fileDocString << QString();
493 
494  QStringList lines;
495  QString indent = QString( ' ' ).repeated( indentSize );
496  QString currentIndent;
497 
498  QMap< QString, QString> friendlyChildNames;
499  QMap< QString, QString> friendlyOutputNames;
500  auto safeName = []( const QString & name, bool capitalize )->QString
501  {
502  QString n = name.toLower().trimmed();
503  QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
504  n.replace( rx, QString() );
505  QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
506  n.replace( rx2, QString() );
507  if ( !capitalize )
508  n = n.replace( ' ', '_' );
510  };
511 
512  auto uniqueSafeName = [ &safeName ]( const QString & name, bool capitalize, const QMap< QString, QString > &friendlyNames )->QString
513  {
514  const QString base = safeName( name, capitalize );
515  QString candidate = base;
516  int i = 1;
517  while ( friendlyNames.contains( candidate ) )
518  {
519  i++;
520  candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
521  }
522  return candidate;
523  };
524 
525  const QString algorithmClassName = safeName( name(), true );
526 
527  QSet< QString > toExecute;
528  for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
529  {
530  if ( childIt->isActive() && childIt->algorithm() )
531  {
532  toExecute.insert( childIt->childId() );
533  friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
534  }
535  }
536  const int totalSteps = toExecute.count();
537 
538  QStringList importLines; // not a set - we need regular ordering
539  switch ( outputType )
540  {
542  {
543  // add specific parameter type imports
544  const auto params = parameterDefinitions();
545  importLines.reserve( params.count() + 3 );
546  importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
547  importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
548  importLines << QStringLiteral( "from qgis.core import QgsProcessingMultiStepFeedback" );
549 
550  bool hasAdvancedParams = false;
551  for ( const QgsProcessingParameterDefinition *def : params )
552  {
554  hasAdvancedParams = true;
555 
556  const QString importString = QgsApplication::processingRegistry()->parameterType( def->type() )->pythonImportString();
557  if ( !importString.isEmpty() && !importLines.contains( importString ) )
558  importLines << importString;
559  }
560 
561  if ( hasAdvancedParams )
562  importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
563 
564  lines << QStringLiteral( "import processing" );
565  lines << QString() << QString();
566 
567  lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
568  lines << QString();
569 
570  // initAlgorithm, parameter definitions
571  lines << indent + QStringLiteral( "def initAlgorithm(self, config=None):" );
572  if ( params.empty() )
573  {
574  lines << indent + indent + QStringLiteral( "pass" );
575  }
576  else
577  {
578  lines.reserve( lines.size() + params.size() );
579  for ( const QgsProcessingParameterDefinition *def : params )
580  {
581  std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
582 
583  if ( defClone->isDestination() )
584  {
585  const QString &friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
586  friendlyOutputNames.insert( defClone->name(), friendlyName );
587  defClone->setName( friendlyName );
588  }
589  else
590  {
591  if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
592  lines << indent + indent + QStringLiteral( "# %1" ).arg( mParameterComponents.value( defClone->name() ).comment()->description() );
593  }
594 
595  if ( defClone->flags() & QgsProcessingParameterDefinition::FlagAdvanced )
596  {
597  lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
598  lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
599  lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
600  }
601  else
602  {
603  lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
604  }
605  }
606  }
607 
608  lines << QString();
609  lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, model_feedback):" );
610  currentIndent = indent + indent;
611 
612  lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
613  lines << currentIndent + QStringLiteral( "# overall progress through the model" );
614  lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
615  break;
616  }
617 #if 0
618  case Script:
619  {
620  QgsStringMap params;
621  QgsProcessingContext context;
622  QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
623  for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
624  {
625  QString name = paramIt.value().parameterName();
626  if ( parameterDefinition( name ) )
627  {
628  // TODO - generic value to string method
629  params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
630  }
631  }
632 
633  if ( !params.isEmpty() )
634  {
635  lines << QStringLiteral( "parameters = {" );
636  for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
637  {
638  lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
639  }
640  lines << QStringLiteral( "}" )
641  << QString();
642  }
643 
644  lines << QStringLiteral( "context = QgsProcessingContext()" )
645  << QStringLiteral( "context.setProject(QgsProject.instance())" )
646  << QStringLiteral( "feedback = QgsProcessingFeedback()" )
647  << QString();
648 
649  break;
650  }
651 #endif
652 
653  }
654 
655  lines << currentIndent + QStringLiteral( "results = {}" );
656  lines << currentIndent + QStringLiteral( "outputs = {}" );
657  lines << QString();
658 
659  QSet< QString > executed;
660  bool executedAlg = true;
661  int currentStep = 0;
662  while ( executedAlg && executed.count() < toExecute.count() )
663  {
664  executedAlg = false;
665  const auto constToExecute = toExecute;
666  for ( const QString &childId : constToExecute )
667  {
668  if ( executed.contains( childId ) )
669  continue;
670 
671  bool canExecute = true;
672  const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
673  for ( const QString &dependency : constDependsOnChildAlgorithms )
674  {
675  if ( !executed.contains( dependency ) )
676  {
677  canExecute = false;
678  break;
679  }
680  }
681 
682  if ( !canExecute )
683  continue;
684 
685  executedAlg = true;
686 
687  const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
688 
689  // fill in temporary outputs
690  const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
691  QgsStringMap childParams;
692  for ( const QgsProcessingParameterDefinition *def : childDefs )
693  {
694  if ( def->isDestination() )
695  {
696  const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
697 
698  // is destination linked to one of the final outputs from this model?
699  bool isFinalOutput = false;
700  QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
701  QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
702  for ( ; outputIt != outputs.constEnd(); ++outputIt )
703  {
704  if ( outputIt->childOutputName() == destParam->name() )
705  {
706  QString paramName = child.childId() + ':' + outputIt.key();
707  paramName = friendlyOutputNames.value( paramName, paramName );
708  childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
709  isFinalOutput = true;
710  break;
711  }
712  }
713 
714  if ( !isFinalOutput )
715  {
716  // output is temporary
717 
718  // check whether it's optional, and if so - is it required?
719  bool required = true;
721  {
722  required = childOutputIsRequired( child.childId(), destParam->name() );
723  }
724 
725  // not optional, or required elsewhere in model
726  if ( required )
727  {
728 
729  childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
730  }
731  }
732  }
733  }
734 
735  lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
736  currentStep++;
737  if ( currentStep < totalSteps )
738  {
739  lines << QString();
740  lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
741  lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
742  lines << currentIndent + indent + QStringLiteral( "return {}" );
743  lines << QString();
744  }
745  executed.insert( childId );
746  }
747  }
748 
749  switch ( outputType )
750  {
752  lines << currentIndent + QStringLiteral( "return results" );
753  lines << QString();
754 
755  // name, displayName
756  lines << indent + QStringLiteral( "def name(self):" );
757  lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
758  lines << QString();
759  lines << indent + QStringLiteral( "def displayName(self):" );
760  lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
761  lines << QString();
762 
763  // group, groupId
764  lines << indent + QStringLiteral( "def group(self):" );
765  lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
766  lines << QString();
767  lines << indent + QStringLiteral( "def groupId(self):" );
768  lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
769  lines << QString();
770 
771  // help
772  if ( !shortHelpString().isEmpty() )
773  {
774  lines << indent + QStringLiteral( "def shortHelpString(self):" );
775  lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
776  lines << QString();
777  }
778  if ( !helpUrl().isEmpty() )
779  {
780  lines << indent + QStringLiteral( "def helpUrl(self):" );
781  lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
782  lines << QString();
783  }
784 
785  // createInstance
786  lines << indent + QStringLiteral( "def createInstance(self):" );
787  lines << indent + indent + QStringLiteral( "return %1()" ).arg( algorithmClassName );
788 
789  // additional import lines
790  static QMap< QString, QString > sAdditionalImports
791  {
792  { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
793  { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
794  { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
795  { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
796  { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
797  { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
798  { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
799  { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
800  { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
801  { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
802  { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
803  { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
804  { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
805  { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
806  { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
807  { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
808  { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
809  { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
810  { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
811  { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
812  };
813 
814  for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
815  {
816  if ( importLines.contains( it.value() ) )
817  {
818  // already got this import
819  continue;
820  }
821 
822  bool found = false;
823  for ( const QString &line : std::as_const( lines ) )
824  {
825  if ( line.contains( it.key() ) )
826  {
827  found = true;
828  break;
829  }
830  }
831  if ( found )
832  {
833  importLines << it.value();
834  }
835  }
836 
837  lines = fileDocString + importLines + lines;
838  break;
839  }
840 
841  lines << QString();
842 
843  return lines;
844 }
845 
846 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
847 {
848  QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
849 
850  auto safeName = []( const QString & name )->QString
851  {
852  QString s = name;
853  return s.replace( QRegularExpression( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) ), QStringLiteral( "_" ) );
854  };
855 
856  // "static"/single value sources
857  QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
883  QStringList() << QgsProcessingOutputNumber::typeName()
886 
887  for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
888  {
889  QString name;
890  QVariant value;
891  QString description;
892  switch ( source.source() )
893  {
894  case QgsProcessingModelChildParameterSource::ModelParameter:
895  {
896  name = source.parameterName();
897  value = modelParameters.value( source.parameterName() );
898  description = parameterDefinition( source.parameterName() )->description();
899  break;
900  }
901  case QgsProcessingModelChildParameterSource::ChildOutput:
902  {
903  const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
904  name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
905  source.outputChildId() : child.description(), source.outputName() );
906  if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
907  {
908  description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
909  child.description() );
910  }
911  value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
912  break;
913  }
914 
915  case QgsProcessingModelChildParameterSource::Expression:
916  case QgsProcessingModelChildParameterSource::ExpressionText:
917  case QgsProcessingModelChildParameterSource::StaticValue:
918  case QgsProcessingModelChildParameterSource::ModelOutput:
919  continue;
920  }
921  variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
922  }
923 
924  // layer sources
925  sources = availableSourcesForChild( childId, QStringList()
931 
932  for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
933  {
934  QString name;
935  QVariant value;
936  QString description;
937 
938  switch ( source.source() )
939  {
940  case QgsProcessingModelChildParameterSource::ModelParameter:
941  {
942  name = source.parameterName();
943  value = modelParameters.value( source.parameterName() );
944  description = parameterDefinition( source.parameterName() )->description();
945  break;
946  }
947  case QgsProcessingModelChildParameterSource::ChildOutput:
948  {
949  const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
950  name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
951  source.outputChildId() : child.description(), source.outputName() );
952  value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
953  if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
954  {
955  description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
956  child.description() );
957  }
958  break;
959  }
960 
961  case QgsProcessingModelChildParameterSource::Expression:
962  case QgsProcessingModelChildParameterSource::ExpressionText:
963  case QgsProcessingModelChildParameterSource::StaticValue:
964  case QgsProcessingModelChildParameterSource::ModelOutput:
965  continue;
966 
967  }
968 
969  if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
970  {
971  QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
972  value = fromVar.sink;
973  if ( value.canConvert<QgsProperty>() )
974  {
975  value = value.value< QgsProperty >().valueAsString( context.expressionContext() );
976  }
977  }
978  QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
979  if ( !layer )
980  layer = QgsProcessingUtils::mapLayerFromString( value.toString(), context );
981 
982  variables.insert( safeName( name ), VariableDefinition( QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ), source, description ) );
983  variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
984  variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
985  variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
986  variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
987  }
988 
989  sources = availableSourcesForChild( childId, QStringList()
991  for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
992  {
993  QString name;
994  QVariant value;
995  QString description;
996 
997  switch ( source.source() )
998  {
999  case QgsProcessingModelChildParameterSource::ModelParameter:
1000  {
1001  name = source.parameterName();
1002  value = modelParameters.value( source.parameterName() );
1003  description = parameterDefinition( source.parameterName() )->description();
1004  break;
1005  }
1006  case QgsProcessingModelChildParameterSource::ChildOutput:
1007  {
1008  const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1009  name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1010  source.outputChildId() : child.description(), source.outputName() );
1011  value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1012  if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1013  {
1014  description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1015  child.description() );
1016  }
1017  break;
1018  }
1019 
1020  case QgsProcessingModelChildParameterSource::Expression:
1021  case QgsProcessingModelChildParameterSource::ExpressionText:
1022  case QgsProcessingModelChildParameterSource::StaticValue:
1023  case QgsProcessingModelChildParameterSource::ModelOutput:
1024  continue;
1025 
1026  }
1027 
1028  QgsFeatureSource *featureSource = nullptr;
1029  if ( value.canConvert<QgsProcessingFeatureSourceDefinition>() )
1030  {
1031  QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1032  value = fromVar.source;
1033  }
1034  else if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
1035  {
1036  QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1037  value = fromVar.sink;
1038  if ( value.canConvert<QgsProperty>() )
1039  {
1040  value = value.value< QgsProperty >().valueAsString( context.expressionContext() );
1041  }
1042  }
1043  if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1044  {
1045  featureSource = layer;
1046  }
1047  if ( !featureSource )
1048  {
1049  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1050  featureSource = vl;
1051  }
1052 
1053  variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1054  variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1055  variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1056  variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1057  variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1058  }
1059 
1060  return variables;
1061 }
1062 
1063 QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1064 {
1065  std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QStringLiteral( "algorithm_inputs" ) ) );
1066  QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, context, modelParameters, results );
1067  QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1068  for ( ; varIt != variables.constEnd(); ++varIt )
1069  {
1070  scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1071  }
1072  return scope.release();
1073 }
1074 
1075 QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1076 {
1077  QgsProcessingModelChildParameterSources sources;
1078 
1079  // first look through model parameters
1080  QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1081  for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1082  {
1083  const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1084  if ( !def )
1085  continue;
1086 
1087  if ( parameterTypes.contains( def->type() ) )
1088  {
1089  if ( !dataTypes.isEmpty() )
1090  {
1091  if ( def->type() == QgsProcessingParameterField::typeName() )
1092  {
1093  const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1094  if ( !( dataTypes.contains( fieldDef->dataType() ) || fieldDef->dataType() == QgsProcessingParameterField::Any ) )
1095  {
1096  continue;
1097  }
1098  }
1100  {
1101  const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1102  if ( !sourceDef )
1103  continue;
1104 
1105  bool ok = sourceDef->dataTypes().isEmpty();
1106  const auto constDataTypes = sourceDef->dataTypes();
1107  for ( int type : constDataTypes )
1108  {
1109  if ( dataTypes.contains( type ) || type == QgsProcessing::TypeMapLayer || type == QgsProcessing::TypeVector || type == QgsProcessing::TypeVectorAnyGeometry )
1110  {
1111  ok = true;
1112  break;
1113  }
1114  }
1115  if ( dataTypes.contains( QgsProcessing::TypeMapLayer ) || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) )
1116  ok = true;
1117 
1118  if ( !ok )
1119  continue;
1120  }
1121  }
1122  sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1123  }
1124  }
1125 
1126  QSet< QString > dependents;
1127  if ( !childId.isEmpty() )
1128  {
1129  dependents = dependentChildAlgorithms( childId );
1130  dependents << childId;
1131  }
1132 
1133  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1134  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1135  {
1136  if ( dependents.contains( childIt->childId() ) )
1137  continue;
1138 
1139  const QgsProcessingAlgorithm *alg = childIt->algorithm();
1140  if ( !alg )
1141  continue;
1142 
1143  const auto constOutputDefinitions = alg->outputDefinitions();
1144  for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1145  {
1146  if ( outputTypes.contains( out->type() ) )
1147  {
1148  if ( !dataTypes.isEmpty() )
1149  {
1150  if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1151  {
1152  const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1153 
1154  if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1155  {
1156  //unacceptable output
1157  continue;
1158  }
1159  }
1160  }
1161  sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1162  }
1163  }
1164  }
1165 
1166  return sources;
1167 }
1168 
1169 QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1170 {
1171  return mHelpContent;
1172 }
1173 
1174 void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1175 {
1176  mHelpContent = helpContent;
1177 }
1178 
1179 void QgsProcessingModelAlgorithm::setName( const QString &name )
1180 {
1181  mModelName = name;
1182 }
1183 
1184 void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1185 {
1186  mModelGroup = group;
1187 }
1188 
1189 bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1190 {
1191  issues.clear();
1192  bool res = true;
1193 
1194  if ( mChildAlgorithms.empty() )
1195  {
1196  res = false;
1197  issues << QObject::tr( "Model does not contain any algorithms" );
1198  }
1199 
1200  for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1201  {
1202  QStringList childIssues;
1203  res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1204 
1205  for ( const QString &issue : std::as_const( childIssues ) )
1206  {
1207  issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1208  }
1209  }
1210  return res;
1211 }
1212 
1213 QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1214 {
1215  return mChildAlgorithms;
1216 }
1217 
1218 void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1219 {
1220  mParameterComponents = parameterComponents;
1221 }
1222 
1223 void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1224 {
1225  mParameterComponents.insert( component.parameterName(), component );
1226 }
1227 
1228 QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1229 {
1230  if ( !mParameterComponents.contains( name ) )
1231  {
1232  QgsProcessingModelParameter &component = mParameterComponents[ name ];
1233  component.setParameterName( name );
1234  return component;
1235  }
1236  return mParameterComponents[ name ];
1237 }
1238 
1239 QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1240 {
1241  QList< QgsProcessingModelParameter > res;
1242  QSet< QString > found;
1243  for ( const QString &parameter : mParameterOrder )
1244  {
1245  if ( mParameterComponents.contains( parameter ) )
1246  {
1247  res << mParameterComponents.value( parameter );
1248  found << parameter;
1249  }
1250  }
1251 
1252  // add any missing ones to end of list
1253  for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1254  {
1255  if ( !found.contains( it.key() ) )
1256  {
1257  res << it.value();
1258  }
1259  }
1260  return res;
1261 }
1262 
1263 void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1264 {
1265  mParameterOrder = order;
1266 }
1267 
1268 void QgsProcessingModelAlgorithm::updateDestinationParameters()
1269 {
1270  //delete existing destination parameters
1271  QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1272  while ( it.hasNext() )
1273  {
1274  const QgsProcessingParameterDefinition *def = it.next();
1275  if ( def->isDestination() )
1276  {
1277  delete def;
1278  it.remove();
1279  }
1280  }
1281  // also delete outputs
1282  qDeleteAll( mOutputs );
1283  mOutputs.clear();
1284 
1285  // rebuild
1286  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1287  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1288  {
1289  QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1290  QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1291  for ( ; outputIt != outputs.constEnd(); ++outputIt )
1292  {
1293  if ( !childIt->isActive() || !childIt->algorithm() )
1294  continue;
1295 
1296  // child algorithm has a destination parameter set, copy it to the model
1297  const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1298  if ( !source )
1299  continue;
1300 
1301  std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1302  // Even if an output was hidden in a child algorithm, we want to show it here for the final
1303  // outputs.
1304  param->setFlags( param->flags() & ~QgsProcessingParameterDefinition::FlagHidden );
1305  if ( outputIt->isMandatory() )
1306  param->setFlags( param->flags() & ~QgsProcessingParameterDefinition::FlagOptional );
1307  param->setName( outputIt->childId() + ':' + outputIt->name() );
1308  param->setDescription( outputIt->description() );
1309  param->setDefaultValue( outputIt->defaultValue() );
1310 
1311  QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1312  if ( addParameter( param.release() ) && newDestParam )
1313  {
1314  if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1315  {
1316  // we need to copy the constraints given by the provider which creates this output across
1317  // and replace those which have been set to match the model provider's constraints
1318  newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1319  newDestParam->mOriginalProvider = provider;
1320  }
1321  }
1322  }
1323  }
1324 }
1325 
1326 void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1327 {
1328  mGroupBoxes.insert( groupBox.uuid(), groupBox );
1329 }
1330 
1331 QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1332 {
1333  return mGroupBoxes.values();
1334 }
1335 
1336 void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1337 {
1338  mGroupBoxes.remove( uuid );
1339 }
1340 
1341 QVariant QgsProcessingModelAlgorithm::toVariant() const
1342 {
1343  QVariantMap map;
1344  map.insert( QStringLiteral( "model_name" ), mModelName );
1345  map.insert( QStringLiteral( "model_group" ), mModelGroup );
1346  map.insert( QStringLiteral( "help" ), mHelpContent );
1347 
1348  QVariantMap childMap;
1349  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1350  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1351  {
1352  childMap.insert( childIt.key(), childIt.value().toVariant() );
1353  }
1354  map.insert( QStringLiteral( "children" ), childMap );
1355 
1356  QVariantMap paramMap;
1357  QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1358  for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1359  {
1360  paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1361  }
1362  map.insert( QStringLiteral( "parameters" ), paramMap );
1363 
1364  QVariantMap paramDefMap;
1365  for ( const QgsProcessingParameterDefinition *def : mParameters )
1366  {
1367  paramDefMap.insert( def->name(), def->toVariantMap() );
1368  }
1369  map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1370 
1371  QVariantList groupBoxDefs;
1372  for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1373  {
1374  groupBoxDefs.append( it.value().toVariant() );
1375  }
1376  map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1377 
1378  map.insert( QStringLiteral( "modelVariables" ), mVariables );
1379 
1380  map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1381 
1382  map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1383 
1384  return map;
1385 }
1386 
1387 bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1388 {
1389  QVariantMap map = model.toMap();
1390 
1391  mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1392  mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1393  mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1394  mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1395 
1396  mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1397  mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1398 
1399  mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1400 
1401  mChildAlgorithms.clear();
1402  QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1403  QVariantMap::const_iterator childIt = childMap.constBegin();
1404  for ( ; childIt != childMap.constEnd(); ++childIt )
1405  {
1406  QgsProcessingModelChildAlgorithm child;
1407  // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1408  // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1409  // with no way for users to repair them
1410  if ( !child.loadVariant( childIt.value() ) )
1411  continue;
1412 
1413  mChildAlgorithms.insert( child.childId(), child );
1414  }
1415 
1416  mParameterComponents.clear();
1417  QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1418  QVariantMap::const_iterator paramIt = paramMap.constBegin();
1419  for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1420  {
1421  QgsProcessingModelParameter param;
1422  if ( !param.loadVariant( paramIt.value().toMap() ) )
1423  return false;
1424 
1425  mParameterComponents.insert( param.parameterName(), param );
1426  }
1427 
1428  qDeleteAll( mParameters );
1429  mParameters.clear();
1430  QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1431 
1432  auto addParam = [ = ]( const QVariant & value )
1433  {
1434  std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1435  // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1436  // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1437  // with no way for users to repair them
1438  if ( param )
1439  {
1440  if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1441  return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1442 
1443  // set parameter help from help content
1444  param->setHelp( mHelpContent.value( param->name() ).toString() );
1445 
1446  // add parameter
1447  addParameter( param.release() );
1448  }
1449  else
1450  {
1451  QVariantMap map = value.toMap();
1452  QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1453  QString name = map.value( QStringLiteral( "name" ) ).toString();
1454 
1455  QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1456  }
1457  };
1458 
1459  QSet< QString > loadedParams;
1460  // first add parameters respecting mParameterOrder
1461  for ( const QString &name : std::as_const( mParameterOrder ) )
1462  {
1463  if ( paramDefMap.contains( name ) )
1464  {
1465  addParam( paramDefMap.value( name ) );
1466  loadedParams << name;
1467  }
1468  }
1469  // then load any remaining parameters
1470  QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1471  for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1472  {
1473  if ( !loadedParams.contains( paramDefIt.key() ) )
1474  addParam( paramDefIt.value() );
1475  }
1476 
1477  mGroupBoxes.clear();
1478  const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1479  for ( const QVariant &groupBoxDef : groupBoxList )
1480  {
1481  QgsProcessingModelGroupBox groupBox;
1482  groupBox.loadVariant( groupBoxDef.toMap() );
1483  mGroupBoxes.insert( groupBox.uuid(), groupBox );
1484  }
1485 
1486  updateDestinationParameters();
1487 
1488  return true;
1489 }
1490 
1491 bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, QgsProcessing::SourceType outputType )
1492 {
1493  // This method is intended to be "permissive" rather than "restrictive".
1494  // I.e. we only reject outputs which we know can NEVER be acceptable, but
1495  // if there's doubt then we default to returning true.
1496  return ( acceptableDataTypes.empty()
1497  || acceptableDataTypes.contains( outputType )
1498  || outputType == QgsProcessing::TypeMapLayer
1499  || outputType == QgsProcessing::TypeVector
1500  || outputType == QgsProcessing::TypeVectorAnyGeometry
1501  || acceptableDataTypes.contains( QgsProcessing::TypeVector )
1502  || acceptableDataTypes.contains( QgsProcessing::TypeMapLayer )
1503  || ( acceptableDataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) && ( outputType == QgsProcessing::TypeVectorPoint ||
1504  outputType == QgsProcessing::TypeVectorLine ||
1505  outputType == QgsProcessing::TypeVectorPolygon ) ) );
1506 }
1507 
1508 void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1509 {
1510  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1511  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1512  {
1513  if ( !childIt->algorithm() )
1514  childIt->reattach();
1515  }
1516 }
1517 
1518 bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1519 {
1520  QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1521  QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1522  doc.appendChild( elem );
1523 
1524  QFile file( path );
1525  if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1526  {
1527  QTextStream stream( &file );
1528  doc.save( stream, 2 );
1529  file.close();
1530  return true;
1531  }
1532  return false;
1533 }
1534 
1535 bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1536 {
1537  QDomDocument doc;
1538 
1539  QFile file( path );
1540  if ( file.open( QFile::ReadOnly ) )
1541  {
1542  if ( !doc.setContent( &file ) )
1543  return false;
1544 
1545  file.close();
1546  }
1547  else
1548  {
1549  return false;
1550  }
1551 
1552  QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1553  return loadVariant( props );
1554 }
1555 
1556 void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1557 {
1558  mChildAlgorithms = childAlgorithms;
1559  updateDestinationParameters();
1560 }
1561 
1562 void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1563 {
1564  mChildAlgorithms.insert( algorithm.childId(), algorithm );
1565  updateDestinationParameters();
1566 }
1567 
1568 QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1569 {
1570  if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1571  algorithm.generateChildId( *this );
1572 
1573  mChildAlgorithms.insert( algorithm.childId(), algorithm );
1574  updateDestinationParameters();
1575  return algorithm.childId();
1576 }
1577 
1578 QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1579 {
1580  return mChildAlgorithms[ childId ];
1581 }
1582 
1583 bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1584 {
1585  if ( !dependentChildAlgorithms( id ).isEmpty() )
1586  return false;
1587 
1588  mChildAlgorithms.remove( id );
1589  updateDestinationParameters();
1590  return true;
1591 }
1592 
1593 void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1594 {
1595  const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1596  for ( const QString &child : constDependentChildAlgorithms )
1597  {
1598  childAlgorithm( child ).setActive( false );
1599  }
1600  childAlgorithm( id ).setActive( false );
1601  updateDestinationParameters();
1602 }
1603 
1604 bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1605 {
1606  const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1607  for ( const QString &child : constDependsOnChildAlgorithms )
1608  {
1609  if ( !childAlgorithm( child ).isActive() )
1610  return false;
1611  }
1612  childAlgorithm( id ).setActive( true );
1613  updateDestinationParameters();
1614  return true;
1615 }
1616 
1617 void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1618 {
1619  if ( addParameter( definition ) )
1620  mParameterComponents.insert( definition->name(), component );
1621 }
1622 
1623 void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
1624 {
1625  removeParameter( definition->name() );
1626  addParameter( definition );
1627 }
1628 
1629 void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
1630 {
1631  removeParameter( name );
1632  mParameterComponents.remove( name );
1633 }
1634 
1635 bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
1636 {
1637  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1638  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1639  {
1640  // check whether child requires this parameter
1641  QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1642  QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1643  for ( ; paramIt != childParams.constEnd(); ++paramIt )
1644  {
1645  const auto constValue = paramIt.value();
1646  for ( const QgsProcessingModelChildParameterSource &source : constValue )
1647  {
1648  if ( source.source() == QgsProcessingModelChildParameterSource::ModelParameter
1649  && source.parameterName() == name )
1650  {
1651  return true;
1652  }
1653  }
1654  }
1655  }
1656  return false;
1657 }
1658 
1659 bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
1660 {
1661  const auto constMParameters = mParameters;
1662  for ( const QgsProcessingParameterDefinition *def : constMParameters )
1663  {
1664  if ( def->name() == name )
1665  continue;
1666 
1667  if ( def->dependsOnOtherParameters().contains( name ) )
1668  return true;
1669  }
1670  return false;
1671 }
1672 
1673 QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
1674 {
1675  return mParameterComponents;
1676 }
1677 
1678 void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
1679 {
1680  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1681  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1682  {
1683  if ( depends.contains( childIt->childId() ) )
1684  continue;
1685 
1686  // does alg have a direct dependency on this child?
1687  const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
1688  bool hasDependency = false;
1689  for ( const QgsProcessingModelChildDependency &dep : constDependencies )
1690  {
1691  if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
1692  {
1693  hasDependency = true;
1694  break;
1695  }
1696  }
1697 
1698  if ( hasDependency )
1699  {
1700  depends.insert( childIt->childId() );
1701  dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
1702  continue;
1703  }
1704 
1705  // check whether child requires any outputs from the target alg
1706  QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1707  QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1708  for ( ; paramIt != childParams.constEnd(); ++paramIt )
1709  {
1710  const auto constValue = paramIt.value();
1711  for ( const QgsProcessingModelChildParameterSource &source : constValue )
1712  {
1713  if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
1714  && source.outputChildId() == childId )
1715  {
1716  depends.insert( childIt->childId() );
1717  dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
1718  break;
1719  }
1720  }
1721  }
1722  }
1723 }
1724 
1725 QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
1726 {
1727  QSet< QString > algs;
1728 
1729  // temporarily insert the target child algorithm to avoid
1730  // unnecessarily recursion though it
1731  algs.insert( childId );
1732 
1733  dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
1734 
1735  // remove temporary target alg
1736  algs.remove( childId );
1737 
1738  return algs;
1739 }
1740 
1741 
1742 void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
1743 {
1744  const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
1745 
1746  // add direct dependencies
1747  const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
1748  for ( const QgsProcessingModelChildDependency &val : constDependencies )
1749  {
1750  if ( !depends.contains( val.childId ) )
1751  {
1752  depends.insert( val.childId );
1753  dependsOnChildAlgorithmsRecursive( val.childId, depends );
1754  }
1755  }
1756 
1757  // check through parameter dependencies
1758  QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
1759  QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1760  for ( ; paramIt != childParams.constEnd(); ++paramIt )
1761  {
1762  const auto constValue = paramIt.value();
1763  for ( const QgsProcessingModelChildParameterSource &source : constValue )
1764  {
1765  if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && !depends.contains( source.outputChildId() ) )
1766  {
1767  depends.insert( source.outputChildId() );
1768  dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
1769  }
1770  }
1771  }
1772 }
1773 
1774 QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
1775 {
1776  QSet< QString > algs;
1777 
1778  // temporarily insert the target child algorithm to avoid
1779  // unnecessarily recursion though it
1780  algs.insert( childId );
1781 
1782  dependsOnChildAlgorithmsRecursive( childId, algs );
1783 
1784  // remove temporary target alg
1785  algs.remove( childId );
1786 
1787  return algs;
1788 }
1789 
1790 QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
1791 {
1792  QSet< QString > dependent;
1793  if ( !childId.isEmpty() )
1794  {
1795  dependent.unite( dependentChildAlgorithms( childId ) );
1796  dependent.insert( childId );
1797  }
1798 
1799  QList<QgsProcessingModelChildDependency> res;
1800  for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1801  {
1802  if ( !dependent.contains( it->childId() ) )
1803  {
1804  // check first if algorithm provides output branches
1805  bool hasBranches = false;
1806  if ( it->algorithm() )
1807  {
1808  const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
1809  for ( const QgsProcessingOutputDefinition *def : defs )
1810  {
1812  {
1813  hasBranches = true;
1814  QgsProcessingModelChildDependency alg;
1815  alg.childId = it->childId();
1816  alg.conditionalBranch = def->name();
1817  res << alg;
1818  }
1819  }
1820  }
1821 
1822  if ( !hasBranches )
1823  {
1824  QgsProcessingModelChildDependency alg;
1825  alg.childId = it->childId();
1826  res << alg;
1827  }
1828  }
1829  }
1830  return res;
1831 }
1832 
1833 bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
1834 {
1835  issues.clear();
1836  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
1837  if ( childIt != mChildAlgorithms.constEnd() )
1838  {
1839  if ( !childIt->algorithm() )
1840  {
1841  issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
1842  return false;
1843  }
1844  bool res = true;
1845 
1846  // loop through child algorithm parameters and check that they are all valid
1847  const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
1848  for ( const QgsProcessingParameterDefinition *def : defs )
1849  {
1850  if ( childIt->parameterSources().contains( def->name() ) )
1851  {
1852  // is the value acceptable?
1853  const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
1854  for ( const QgsProcessingModelChildParameterSource &source : sources )
1855  {
1856  switch ( source.source() )
1857  {
1858  case QgsProcessingModelChildParameterSource::StaticValue:
1859  if ( !def->checkValueIsAcceptable( source.staticValue() ) )
1860  {
1861  res = false;
1862  issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
1863  }
1864  break;
1865 
1866  case QgsProcessingModelChildParameterSource::ModelParameter:
1867  if ( !parameterComponents().contains( source.parameterName() ) )
1868  {
1869  res = false;
1870  issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
1871  }
1872  break;
1873 
1874  case QgsProcessingModelChildParameterSource::ChildOutput:
1875  if ( !childAlgorithms().contains( source.outputChildId() ) )
1876  {
1877  res = false;
1878  issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
1879  }
1880  break;
1881 
1882  case QgsProcessingModelChildParameterSource::Expression:
1883  case QgsProcessingModelChildParameterSource::ExpressionText:
1884  case QgsProcessingModelChildParameterSource::ModelOutput:
1885  break;
1886  }
1887  }
1888  }
1889  else
1890  {
1891  // not specified. Is it optional?
1892 
1893  // ignore destination parameters -- they shouldn't ever be mandatory
1894  if ( def->isDestination() )
1895  continue;
1896 
1898  {
1899  res = false;
1900  issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
1901  }
1902  }
1903  }
1904 
1905  return res;
1906  }
1907  else
1908  {
1909  issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
1910  return false;
1911  }
1912 }
1913 
1914 bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
1915 {
1916  reattachAlgorithms();
1917  QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1918  for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1919  {
1920  if ( !childIt->algorithm() )
1921  {
1922  if ( errorMessage )
1923  {
1924  *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
1925  }
1926  return false;
1927  }
1928  }
1929  return true;
1930 }
1931 
1932 QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
1933 {
1934  if ( mSourceFile.isEmpty() )
1935  return QString(); // temporary model - can't run as python command
1936 
1937  return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
1938 }
1939 
1940 QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
1941 {
1942  QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
1943  res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
1944  return res;
1945 }
1946 
1947 QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
1948 {
1949  QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
1950  alg->loadVariant( toVariant() );
1951  alg->setProvider( provider() );
1952  alg->setSourceFilePath( sourceFilePath() );
1953  return alg;
1954 }
1955 
1956 QVariantMap QgsProcessingModelAlgorithm::variables() const
1957 {
1958  return mVariables;
1959 }
1960 
1961 void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
1962 {
1963  mVariables = variables;
1964 }
1965 
1966 QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
1967 {
1968  return mDesignerParameterValues;
1969 }
1970 
@ UpperCamelCase
Convert the string to upper camel case. Note that this method does not unaccent characters.
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition: qgis.cpp:282
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * processingModelAlgorithmScope(const QgsProcessingModelAlgorithm *model, const QVariantMap &parameters, QgsProcessingContext &context)
Creates a new scope which contains variables and functions relating to a processing model algorithm,...
static QgsExpressionContextScope * processingAlgorithmScope(const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context)
Creates a new scope which contains variables and functions relating to a processing algorithm,...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
An interface for objects which provide features via a getFeatures method.
virtual QgsRectangle sourceExtent() const
Returns the extent of all geometries from the source.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Base class for all map layer types.
Definition: qgsmaplayer.h:73
virtual QgsRectangle extent() const
Returns the extent of the layer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Abstract base class for processing algorithms.
QgsProcessingOutputDefinitions outputDefinitions() const
Returns an ordered list of output definitions utilized by the algorithm.
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
@ FlagNoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
@ FlagCustomException
Algorithm raises custom exception notices, don't use the standard ones.
@ FlagSkipGenericModelLogging
When running as part of a model, the generic algorithm setup and results logging should be skipped.
@ FlagPruneModelBranchesBasedOnAlgorithmResults
Algorithm results will cause remaining model branches to be pruned based on the results of running th...
virtual QgsExpressionContext createExpressionContext(const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source=nullptr) const
Creates an expression context relating to the algorithm.
const QgsProcessingParameterDefinition * parameterDefinition(const QString &name) const
Returns a matching parameter by name.
virtual QString asPythonCommand(const QVariantMap &parameters, QgsProcessingContext &context) const
Returns a Python command string which can be executed to run the algorithm using the specified parame...
QgsProcessingProvider * provider() const
Returns the provider to which this algorithm belongs.
Contains information about the context in which a processing algorithm is executed.
@ Verbose
Verbose logging.
QgsExpressionContext & expressionContext()
Returns the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
LogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
Base class for all parameter definitions which represent file or layer destinations,...
virtual QString generateTemporaryDestination() const
Generates a temporary destination value for this parameter.
void setSupportsNonFileBasedOutput(bool supportsNonFileBasedOutput)
Sets whether the destination parameter supports non filed-based outputs, such as memory layers or dir...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Encapsulates settings relating to a feature source input to a processing algorithm.
QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource,...
Base class for providing feedback from a processing algorithm.
virtual void pushCommandInfo(const QString &info)
Pushes an informational message containing a command from the algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
Processing feedback object for multi-step operations.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
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.
QString destinationName
Name to use for sink if it's to be loaded into a destination project.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
A vector layer output for processing algorithms.
static QString typeName()
Returns the type name for the output class.
QgsProcessing::SourceType dataType() const
Returns the layer type for the output layer.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
Base class for the definition of processing parameters.
QgsProcessingAlgorithm * algorithm() const
Returns a pointer to the algorithm which owns this parameter.
virtual QgsProcessingParameterDefinition * clone() const =0
Creates a clone of the parameter definition.
virtual bool isDestination() const
Returns true if this parameter represents a file or layer destination, e.g.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
@ FlagHidden
Parameter is hidden and should not be shown to users.
virtual QString type() const =0
Unique parameter type name.
Flags flags() const
Returns any flags associated with the parameter.
virtual QVariantMap toVariantMap() const
Saves this parameter to a QVariantMap.
QString name() const
Returns the name of the parameter.
virtual QStringList dependsOnOtherParameters() const
Returns a list of other parameter names on which this parameter is dependent (e.g.
virtual bool checkValueIsAcceptable(const QVariant &input, QgsProcessingContext *context=nullptr) const
Checks whether the specified input value is acceptable for the parameter.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A vector layer or feature source field parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
DataType dataType() const
Returns the acceptable data type for the field.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
Can be inherited by parameters which require limits to their acceptable data types.
QList< int > dataTypes() const
Returns the geometry types for sources acceptable by the parameter.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
virtual QString pythonImportString() const
Returns a valid Python import string for importing the corresponding parameter type,...
static QString typeName()
Returns the type name for the parameter class.
static QgsProcessingParameterDefinition * parameterFromVariantMap(const QVariantMap &map)
Creates a new QgsProcessingParameterDefinition using the configuration from a supplied variant map.
Abstract base class for processing providers.
const QgsProcessingAlgorithm * algorithm(const QString &name) const
Returns the matching algorithm by name, or nullptr if no matching algorithm is contained by this prov...
QgsProcessingParameterType * parameterType(const QString &id) const
Returns the parameter type registered for id.
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.
@ Vector
Vector layer type.
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.
PythonOutputType
Available Python output types.
Definition: qgsprocessing.h:63
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
Definition: qgsprocessing.h:64
SourceType
Data source types enum.
Definition: qgsprocessing.h:46
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:50
@ TypeMapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
Definition: qgsprocessing.h:47
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:51
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:49
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:48
A store for object properties.
Definition: qgsproperty.h:231
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...
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
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
Represents a vector layer which manages a vector based data sets.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
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
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2026
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2133
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Single variable definition for use within a QgsExpressionContextScope.