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