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