QGIS API Documentation 3.99.0-Master (752b475928d)
Loading...
Searching...
No Matches
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
20#include "qgis.h"
21#include "qgsapplication.h"
22#include "qgsexception.h"
24#include "qgsmessagelog.h"
29#include "qgsprocessingutils.h"
30#include "qgsstringutils.h"
31#include "qgsvectorlayer.h"
32#include "qgsxmlutils.h"
33
34#include <QFile>
35#include <QRegularExpression>
36#include <QTextStream>
37
38#include "moc_qgsprocessingmodelalgorithm.cpp"
39
41
42QgsProcessingModelAlgorithm::QgsProcessingModelAlgorithm( const QString &name, const QString &group, const QString &groupId )
43 : mModelName( name.isEmpty() ? QObject::tr( "model" ) : name )
44 , mModelGroup( group )
45 , mModelGroupId( groupId )
46{}
47
48void QgsProcessingModelAlgorithm::initAlgorithm( const QVariantMap & )
49{
50}
51
52Qgis::ProcessingAlgorithmFlags QgsProcessingModelAlgorithm::flags() const
53{
55
56 // don't force algorithm attachment here, that's potentially too expensive
57 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
58 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
59 {
60 if ( childIt->algorithm() && childIt->algorithm()->flags().testFlag( Qgis::ProcessingAlgorithmFlag::SecurityRisk ) )
61 {
62 // security risk flag propagates from child algorithms to model
64 }
65 }
66 return res;
67}
68
69QString QgsProcessingModelAlgorithm::name() const
70{
71 return mModelName;
72}
73
74QString QgsProcessingModelAlgorithm::displayName() const
75{
76 return mModelName;
77}
78
79QString QgsProcessingModelAlgorithm::group() const
80{
81 return mModelGroup;
82}
83
84QString QgsProcessingModelAlgorithm::groupId() const
85{
86 return mModelGroupId;
87}
88
89QIcon QgsProcessingModelAlgorithm::icon() const
90{
91 return QgsApplication::getThemeIcon( QStringLiteral( "/processingModel.svg" ) );
92}
93
94QString QgsProcessingModelAlgorithm::svgIconPath() const
95{
96 return QgsApplication::iconPath( QStringLiteral( "processingModel.svg" ) );
97}
98
99QString QgsProcessingModelAlgorithm::shortHelpString() const
100{
101 if ( mHelpContent.empty() )
102 return QString();
103
104 return QgsProcessingUtils::formatHelpMapAsHtml( mHelpContent, this );
105}
106
107QString QgsProcessingModelAlgorithm::shortDescription() const
108{
109 return mHelpContent.value( QStringLiteral( "SHORT_DESCRIPTION" ) ).toString();
110}
111
112QString QgsProcessingModelAlgorithm::helpUrl() const
113{
114 return mHelpContent.value( QStringLiteral( "HELP_URL" ) ).toString();
115}
116
117QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const QgsProcessingModelChildAlgorithm &child, const QVariantMap &modelParameters, const QVariantMap &results, const QgsExpressionContext &expressionContext, QString &error, const QgsProcessingContext *context ) const
118{
119 error.clear();
120 auto evaluateSources = [&child, &modelParameters, &results, &error, &expressionContext]( const QgsProcessingParameterDefinition * def )->QVariant
121 {
122 const QgsProcessingModelChildParameterSources paramSources = child.parameterSources().value( def->name() );
123
124 QString expressionText;
125 QVariantList paramParts;
126 for ( const QgsProcessingModelChildParameterSource &source : paramSources )
127 {
128 switch ( source.source() )
129 {
131 paramParts << source.staticValue();
132 break;
133
135 paramParts << modelParameters.value( source.parameterName() );
136 break;
137
139 {
140 QVariantMap linkedChildResults = results.value( source.outputChildId() ).toMap();
141 paramParts << linkedChildResults.value( source.outputName() );
142 break;
143 }
144
146 {
147 QgsExpression exp( source.expression() );
148 paramParts << exp.evaluate( &expressionContext );
149 if ( exp.hasEvalError() )
150 {
151 error = QObject::tr( "Could not evaluate expression for parameter %1 for %2: %3" ).arg( def->name(), child.description(), exp.evalErrorString() );
152 }
153 break;
154 }
156 {
157 expressionText = QgsExpression::replaceExpressionText( source.expressionText(), &expressionContext );
158 break;
159 }
160
162 break;
163 }
164 }
165
166 if ( ! expressionText.isEmpty() )
167 {
168 return expressionText;
169 }
170 else if ( paramParts.count() == 1 )
171 return paramParts.at( 0 );
172 else
173 return paramParts;
174 };
175
176
177 QVariantMap childParams;
178 const QList< const QgsProcessingParameterDefinition * > childParameterDefinitions = child.algorithm()->parameterDefinitions();
179 for ( const QgsProcessingParameterDefinition *def : childParameterDefinitions )
180 {
181 if ( !def->isDestination() )
182 {
183 if ( !child.parameterSources().contains( def->name() ) )
184 continue; // use default value
185
186 const QVariant value = evaluateSources( def );
187 childParams.insert( def->name(), value );
188 }
189 else
190 {
191 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
192
193 // is destination linked to one of the final outputs from this model?
194 bool isFinalOutput = false;
195 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
196 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
197 for ( ; outputIt != outputs.constEnd(); ++outputIt )
198 {
199 if ( outputIt->childOutputName() == destParam->name() )
200 {
201 QString paramName = child.childId() + ':' + outputIt.key();
202 bool foundParam = false;
203 QVariant value;
204
205 // if parameter was specified using child_id:child_name directly, take that
206 if ( modelParameters.contains( paramName ) )
207 {
208 value = modelParameters.value( paramName );
209 foundParam = true;
210 }
211
212 // ...otherwise we need to find the corresponding model parameter which matches this output
213 if ( !foundParam )
214 {
215 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
216 {
217 if ( modelParameters.contains( modelParam->name() ) )
218 {
219 value = modelParameters.value( modelParam->name() );
220 foundParam = true;
221 }
222 }
223 }
224
225 if ( foundParam )
226 {
227 if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
228 {
229 // make sure layer output name is correctly set
230 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
231 fromVar.destinationName = outputIt.key();
232 value = QVariant::fromValue( fromVar );
233 }
234
235 childParams.insert( destParam->name(), value );
236 }
237 isFinalOutput = true;
238 break;
239 }
240 }
241
242 bool hasExplicitDefinition = false;
243 if ( !isFinalOutput && child.parameterSources().contains( def->name() ) )
244 {
245 // explicitly defined source for output
246 const QVariant value = evaluateSources( def );
247 if ( value.isValid() )
248 {
249 childParams.insert( def->name(), value );
250 hasExplicitDefinition = true;
251 }
252 }
253
254 if ( !isFinalOutput && !hasExplicitDefinition )
255 {
256 // output is temporary
257
258 // check whether it's optional, and if so - is it required?
259 bool required = true;
261 {
262 required = childOutputIsRequired( child.childId(), destParam->name() );
263 }
264
265 // not optional, or required elsewhere in model
266 if ( required )
267 childParams.insert( destParam->name(), destParam->generateTemporaryDestination( context ) );
268 }
269 }
270 }
271 return childParams;
272}
273
274const QgsProcessingParameterDefinition *QgsProcessingModelAlgorithm::modelParameterFromChildIdAndOutputName( const QString &childId, const QString &childOutputName ) const
275{
276 for ( const QgsProcessingParameterDefinition *definition : mParameters )
277 {
278 if ( !definition->isDestination() )
279 continue;
280
281 const QString modelChildId = definition->metadata().value( QStringLiteral( "_modelChildId" ) ).toString();
282 const QString modelOutputName = definition->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
283
284 if ( modelChildId == childId && modelOutputName == childOutputName )
285 return definition;
286 }
287 return nullptr;
288}
289
290bool QgsProcessingModelAlgorithm::childOutputIsRequired( const QString &childId, const QString &outputName ) const
291{
292 // look through all child algs
293 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
294 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
295 {
296 if ( childIt->childId() == childId || !childIt->isActive() )
297 continue;
298
299 // look through all sources for child
300 QMap<QString, QgsProcessingModelChildParameterSources> candidateChildParams = childIt->parameterSources();
301 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator childParamIt = candidateChildParams.constBegin();
302 for ( ; childParamIt != candidateChildParams.constEnd(); ++childParamIt )
303 {
304 const auto constValue = childParamIt.value();
305 for ( const QgsProcessingModelChildParameterSource &source : constValue )
306 {
308 && source.outputChildId() == childId
309 && source.outputName() == outputName )
310 {
311 return true;
312 }
313 }
314 }
315 }
316 return false;
317}
318
319QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
320{
321 QSet< QString > toExecute;
322 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
323 QSet< QString > broken;
324 const QSet<QString> childSubset = context.modelInitialRunConfig() ? context.modelInitialRunConfig()->childAlgorithmSubset() : QSet<QString>();
325 const bool useSubsetOfChildren = !childSubset.empty();
326 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
327 {
328 if ( childIt->isActive() && ( !useSubsetOfChildren || childSubset.contains( childIt->childId() ) ) )
329 {
330 if ( childIt->algorithm() )
331 toExecute.insert( childIt->childId() );
332 else
333 broken.insert( childIt->childId() );
334 }
335 }
336
337 if ( !broken.empty() )
338 throw QgsProcessingException( QCoreApplication::translate( "QgsProcessingModelAlgorithm", "Cannot run model, the following algorithms are not available on this system: %1" ).arg( qgsSetJoin( broken, QLatin1String( ", " ) ) ) );
339
340 QElapsedTimer totalTime;
341 totalTime.start();
342
343 QgsProcessingMultiStepFeedback modelFeedback( toExecute.count(), feedback );
344 QgsExpressionContext baseContext = createExpressionContext( parameters, context );
345
346 QVariantMap &childInputs = context.modelResult().rawChildInputs();
347 QVariantMap &childResults = context.modelResult().rawChildOutputs();
348 QSet< QString > &executed = context.modelResult().executedChildIds();
349 QMap< QString, QgsProcessingModelChildAlgorithmResult > &contextChildResults = context.modelResult().childResults();
350
351 // start with initial configuration from the context's model configuration (allowing us to
352 // resume execution using a previous state)
354 {
355 childInputs = config->initialChildInputs();
356 childResults = config->initialChildOutputs();
357 executed = config->previouslyExecutedChildAlgorithms();
358 // discard the model config, this should only be used when running the top level model
359 context.setModelInitialRunConfig( nullptr );
360 }
361 if ( useSubsetOfChildren )
362 {
363 executed.subtract( childSubset );
364 }
365
366 QVariantMap finalResults;
367
368 bool executedAlg = true;
369 int previousHtmlLogLength = feedback ? feedback->htmlLog().length() : 0;
370 int countExecuted = 0;
371 while ( executedAlg && countExecuted < toExecute.count() )
372 {
373 executedAlg = false;
374 for ( const QString &childId : std::as_const( toExecute ) )
375 {
376 if ( feedback && feedback->isCanceled() )
377 break;
378
379 if ( executed.contains( childId ) )
380 {
381 continue;
382 }
383
384 bool canExecute = true;
385 const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
386 for ( const QString &dependency : dependencies )
387 {
388 if ( !executed.contains( dependency ) )
389 {
390 canExecute = false;
391 break;
392 }
393 }
394
395 if ( !canExecute )
396 {
397 continue;
398 }
399
400 executedAlg = true;
401
402 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
403 std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
404
405 bool skipGenericLogging = true;
406 switch ( context.logLevel() )
407 {
409 // at default log level we skip all the generic logs about prepare steps, step inputs and outputs
410 skipGenericLogging = true;
411 break;
413 // at verbose log level we include all the generic logs about prepare steps, step inputs and outputs
414 // UNLESS the algorithm specifically tells to skip these (eg raise warning steps and other special cases)
415 skipGenericLogging = childAlg->flags() & Qgis::ProcessingAlgorithmFlag::SkipGenericModelLogging;
416 break;
418 // at model debug log level we'll show all the generic logs for step preparation, inputs and outputs
419 // for every child algorithm
420 skipGenericLogging = false;
421 break;
422 }
423
424 if ( feedback && !skipGenericLogging )
425 feedback->pushDebugInfo( QObject::tr( "Prepare algorithm: %1" ).arg( childId ) );
426
427 QgsExpressionContext expContext = baseContext;
428 expContext << QgsExpressionContextUtils::processingAlgorithmScope( child.algorithm(), parameters, context )
429 << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
430 context.setExpressionContext( expContext );
431
432 QString error;
433 QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error, &context );
434 if ( !error.isEmpty() )
435 throw QgsProcessingException( error );
436
437 if ( feedback && !skipGenericLogging )
438 feedback->setProgressText( QObject::tr( "Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
439
441
442 const QVariantMap thisChildParams = QgsProcessingUtils::removePointerValuesFromMap( childParams );
443 childInputs.insert( childId, thisChildParams );
444 childResult.setInputs( thisChildParams );
445
446 QStringList params;
447 for ( auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
448 {
449 params << QStringLiteral( "%1: %2" ).arg( childParamIt.key(),
450 child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
451 }
452
453 if ( feedback && !skipGenericLogging )
454 {
455 feedback->pushInfo( QObject::tr( "Input Parameters:" ) );
456 feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( params.join( QLatin1String( ", " ) ) ) );
457 }
458
459 QElapsedTimer childTime;
460 childTime.start();
461
462 QVariantMap outerScopeChildInputs = childInputs;
463 QVariantMap outerScopePrevChildResults = childResults;
464 QSet< QString > outerScopeExecuted = executed;
465 QMap< QString, QgsProcessingModelChildAlgorithmResult > outerScopeContextChildResult = contextChildResults;
466 if ( dynamic_cast< QgsProcessingModelAlgorithm * >( childAlg.get() ) )
467 {
468 // don't allow results from outer models to "leak" into child models
469 childInputs.clear();
470 childResults.clear();
471 executed.clear();
472 contextChildResults.clear();
473 }
474
475 bool ok = false;
476
477 QThread *modelThread = QThread::currentThread();
478
479 auto prepareOnMainThread = [modelThread, &ok, &childAlg, &childParams, &context, &modelFeedback]
480 {
481 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->prepare() must be run on the main thread" );
482 ok = childAlg->prepare( childParams, context, &modelFeedback );
483 context.pushToThread( modelThread );
484 };
485
486 // Make sure we only run prepare steps on the main thread!
487 if ( modelThread == qApp->thread() )
488 ok = childAlg->prepare( childParams, context, &modelFeedback );
489 else
490 {
491 context.pushToThread( qApp->thread() );
492// silence false positive leak warning
493#ifndef __clang_analyzer__
494 QMetaObject::invokeMethod( qApp, prepareOnMainThread, Qt::BlockingQueuedConnection );
495#endif
496 }
497
498 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
499
500 if ( !ok )
501 {
502 const QString error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
503 throw QgsProcessingException( error );
504 }
505
506 QVariantMap results;
507
508 bool runResult = false;
509 try
510 {
511 if ( ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::NoThreading ) && ( QThread::currentThread() != qApp->thread() ) )
512 {
513 // child algorithm run step must be called on main thread
514 auto runOnMainThread = [modelThread, &context, &modelFeedback, &results, &childAlg, &childParams]
515 {
516 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->runPrepared() must be run on the main thread" );
517 results = childAlg->runPrepared( childParams, context, &modelFeedback );
518 context.pushToThread( modelThread );
519 };
520
521 if ( feedback && !skipGenericLogging && modelThread != qApp->thread() )
522 feedback->pushWarning( QObject::tr( "Algorithm “%1” cannot be run in a background thread, switching to main thread for this step" ).arg( childAlg->displayName() ) );
523
524 context.pushToThread( qApp->thread() );
525// silence false positive leak warning
526#ifndef __clang_analyzer__
527 QMetaObject::invokeMethod( qApp, runOnMainThread, Qt::BlockingQueuedConnection );
528#endif
529 }
530 else
531 {
532 // safe to run on model thread
533 results = childAlg->runPrepared( childParams, context, &modelFeedback );
534 }
535 runResult = true;
537 }
538 catch ( QgsProcessingException &e )
539 {
540 error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? e.what() : QObject::tr( "Error encountered while running %1: %2" ).arg( child.description(), e.what() );
542 }
543
544 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
545
546 QVariantMap ppRes;
547 auto postProcessOnMainThread = [modelThread, &ppRes, &childAlg, &context, &modelFeedback, runResult]
548 {
549 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->postProcess() must be run on the main thread" );
550 ppRes = childAlg->postProcess( context, &modelFeedback, runResult );
551 context.pushToThread( modelThread );
552 };
553
554 // Make sure we only run postProcess steps on the main thread!
555 if ( modelThread == qApp->thread() )
556 ppRes = childAlg->postProcess( context, &modelFeedback, runResult );
557 else
558 {
559 context.pushToThread( qApp->thread() );
560// silence false positive leak warning
561#ifndef __clang_analyzer__
562 QMetaObject::invokeMethod( qApp, postProcessOnMainThread, Qt::BlockingQueuedConnection );
563#endif
564 }
565
566 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
567
568 if ( !ppRes.isEmpty() )
569 results = ppRes;
570
571 if ( dynamic_cast< QgsProcessingModelAlgorithm * >( childAlg.get() ) )
572 {
573 childInputs = outerScopeChildInputs;
574 childResults = outerScopePrevChildResults;
575 executed = outerScopeExecuted;
576 contextChildResults = outerScopeContextChildResult;
577 }
578
579 childResults.insert( childId, results );
580 childResult.setOutputs( results );
581
582 if ( runResult )
583 {
584 if ( feedback && !skipGenericLogging )
585 {
586 const QVariantMap displayOutputs = QgsProcessingUtils::removePointerValuesFromMap( results );
587 QStringList formattedOutputs;
588 for ( auto displayOutputIt = displayOutputs.constBegin(); displayOutputIt != displayOutputs.constEnd(); ++displayOutputIt )
589 {
590 formattedOutputs << QStringLiteral( "%1: %2" ).arg( displayOutputIt.key(),
591 QgsProcessingUtils::variantToPythonLiteral( displayOutputIt.value() ) );;
592 }
593 feedback->pushInfo( QObject::tr( "Results:" ) );
594 feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( formattedOutputs.join( QLatin1String( ", " ) ) ) );
595 }
596
597 // look through child alg's outputs to determine whether any of these should be copied
598 // to the final model outputs
599 const QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
600 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
601 {
602 const int outputSortKey = mOutputOrder.indexOf( QStringLiteral( "%1:%2" ).arg( childId, outputIt->childOutputName() ) );
603 switch ( mInternalVersion )
604 {
605 case QgsProcessingModelAlgorithm::InternalVersion::Version1:
606 finalResults.insert( childId + ':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
607 break;
608 case QgsProcessingModelAlgorithm::InternalVersion::Version2:
609 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
610 {
611 finalResults.insert( modelParam->name(), results.value( outputIt->childOutputName() ) );
612 }
613 break;
614 }
615
616 const QString outputLayer = results.value( outputIt->childOutputName() ).toString();
617 if ( !outputLayer.isEmpty() && context.willLoadLayerOnCompletion( outputLayer ) )
618 {
619 QgsProcessingContext::LayerDetails &details = context.layerToLoadOnCompletionDetails( outputLayer );
620 details.groupName = mOutputGroup;
621 if ( outputSortKey > 0 )
622 details.layerSortKey = outputSortKey;
623 }
624 }
625
626 executed.insert( childId );
627
628 std::function< void( const QString &, const QString & )> pruneAlgorithmBranchRecursive;
629 pruneAlgorithmBranchRecursive = [&]( const QString & id, const QString &branch = QString() )
630 {
631 const QSet<QString> toPrune = dependentChildAlgorithms( id, branch );
632 for ( const QString &targetId : toPrune )
633 {
634 if ( executed.contains( targetId ) )
635 continue;
636
637 executed.insert( targetId );
638 pruneAlgorithmBranchRecursive( targetId, branch );
639 }
640 };
641
642 // prune remaining algorithms if they are dependent on a branch from this child which didn't eventuate
643 const QgsProcessingOutputDefinitions outputDefs = childAlg->outputDefinitions();
644 for ( const QgsProcessingOutputDefinition *outputDef : outputDefs )
645 {
646 if ( outputDef->type() == QgsProcessingOutputConditionalBranch::typeName() && !results.value( outputDef->name() ).toBool() )
647 {
648 pruneAlgorithmBranchRecursive( childId, outputDef->name() );
649 }
650 }
651
653 {
654 // check if any dependent algorithms should be canceled based on the outputs of this algorithm run
655 // first find all direct dependencies of this algorithm by looking through all remaining child algorithms
656 for ( const QString &candidateId : std::as_const( toExecute ) )
657 {
658 if ( executed.contains( candidateId ) )
659 continue;
660
661 // a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
662 // algorithm's outputs
663 const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
664 const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
665 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
666 bool pruned = false;
667 for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
668 {
669 for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
670 {
671 if ( source.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
672 {
673 // ok, this one is dependent on the current alg. Did we get a value for it?
674 if ( !results.contains( source.outputName() ) )
675 {
676 // oh no, nothing returned for this parameter. Gotta trim the branch back!
677 pruned = true;
678 // skip the dependent alg..
679 executed.insert( candidateId );
680 //... and everything which depends on it
681 pruneAlgorithmBranchRecursive( candidateId, QString() );
682 break;
683 }
684 }
685 }
686 if ( pruned )
687 break;
688 }
689 }
690 }
691
692 childAlg.reset( nullptr );
693 countExecuted++;
694 modelFeedback.setCurrentStep( countExecuted );
695 if ( feedback && !skipGenericLogging )
696 {
697 feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
698 }
699 }
700
701 // trim out just the portion of the overall log which relates to this child
702 const QString thisAlgorithmHtmlLog = feedback ? feedback->htmlLog().mid( previousHtmlLogLength ) : QString();
703 previousHtmlLogLength = feedback ? feedback->htmlLog().length() : 0;
704
705 if ( !runResult )
706 {
707 const QString formattedException = QStringLiteral( "<span style=\"color:red\">%1</span><br/>" ).arg( error.toHtmlEscaped() ).replace( '\n', QLatin1String( "<br>" ) );
708 const QString formattedRunTime = QStringLiteral( "<span style=\"color:red\">%1</span><br/>" ).arg( QObject::tr( "Failed after %1 s." ).arg( childTime.elapsed() / 1000.0 ).toHtmlEscaped() ).replace( '\n', QLatin1String( "<br>" ) );
709
710 childResult.setHtmlLog( thisAlgorithmHtmlLog + formattedException + formattedRunTime );
711 context.modelResult().childResults().insert( childId, childResult );
712
713 throw QgsProcessingException( error );
714 }
715 else
716 {
717 childResult.setHtmlLog( thisAlgorithmHtmlLog );
718 context.modelResult().childResults().insert( childId, childResult );
719 }
720 }
721
722 if ( feedback && feedback->isCanceled() )
723 break;
724 }
725 if ( feedback )
726 feedback->pushDebugInfo( QObject::tr( "Model processed OK. Executed %n algorithm(s) total in %1 s.", nullptr, countExecuted ).arg( static_cast< double >( totalTime.elapsed() ) / 1000.0 ) );
727
728 mResults = finalResults;
729 mResults.insert( QStringLiteral( "CHILD_RESULTS" ), childResults );
730 mResults.insert( QStringLiteral( "CHILD_INPUTS" ), childInputs );
731 return mResults;
732}
733
734QString QgsProcessingModelAlgorithm::sourceFilePath() const
735{
736 return mSourceFile;
737}
738
739void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
740{
741 mSourceFile = sourceFile;
742}
743
744bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath() const
745{
746 if ( mSourceFile.isEmpty() )
747 return false;
748
749 const QFileInfo fi( mSourceFile );
750 return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
751}
752
753QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const int indentSize ) const
754{
755 QStringList fileDocString;
756 fileDocString << QStringLiteral( "\"\"\"" );
757 fileDocString << QStringLiteral( "Model exported as python." );
758 fileDocString << QStringLiteral( "Name : %1" ).arg( displayName() );
759 fileDocString << QStringLiteral( "Group : %1" ).arg( group() );
760 fileDocString << QStringLiteral( "With QGIS : %1" ).arg( Qgis::versionInt() );
761 fileDocString << QStringLiteral( "\"\"\"" );
762 fileDocString << QString();
763
764 QStringList lines;
765 QString indent = QString( ' ' ).repeated( indentSize );
766 QString currentIndent;
767
768 QMap< QString, QString> friendlyChildNames;
769 QMap< QString, QString> friendlyOutputNames;
770 auto uniqueSafeName = []( const QString & name, bool capitalize, const QMap< QString, QString > &friendlyNames )->QString
771 {
772 const QString base = safeName( name, capitalize );
773 QString candidate = base;
774 int i = 1;
775 // iterate over friendlyNames value to find candidate
776 while ( std::find( friendlyNames.cbegin(), friendlyNames.cend(), candidate ) != friendlyNames.cend() )
777 {
778 i++;
779 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
780 }
781 return candidate;
782 };
783
784 const QString algorithmClassName = safeName( name(), true );
785
786 QSet< QString > toExecute;
787 for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
788 {
789 if ( childIt->isActive() && childIt->algorithm() )
790 {
791 toExecute.insert( childIt->childId() );
792 friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
793 }
794 }
795 const int totalSteps = toExecute.count();
796
797 QStringList importLines; // not a set - we need regular ordering
798 switch ( outputType )
799 {
801 {
802 // add specific parameter type imports
803 const auto params = parameterDefinitions();
804 importLines.reserve( params.count() + 6 );
805 importLines << QStringLiteral( "from typing import Any, Optional" );
806 importLines << QString();
807 importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
808 importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
809 importLines << QStringLiteral( "from qgis.core import QgsProcessingContext" );
810 importLines << QStringLiteral( "from qgis.core import QgsProcessingFeedback, QgsProcessingMultiStepFeedback" );
811
812 bool hasAdvancedParams = false;
813 for ( const QgsProcessingParameterDefinition *def : params )
814 {
815 if ( def->flags() & Qgis::ProcessingParameterFlag::Advanced )
816 hasAdvancedParams = true;
817
818 if ( const QgsProcessingParameterType *type = QgsApplication::processingRegistry()->parameterType( def->type() ) )
819 {
820 const QString importString = type->pythonImportString();
821 if ( !importString.isEmpty() && !importLines.contains( importString ) )
822 importLines << importString;
823 }
824 }
825
826 if ( hasAdvancedParams )
827 importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
828
829 lines << QStringLiteral( "from qgis import processing" );
830 lines << QString() << QString();
831
832 lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
833 lines << QString();
834
835 // initAlgorithm, parameter definitions
836 lines << indent + QStringLiteral( "def initAlgorithm(self, config: Optional[dict[str, Any]] = None):" );
837 if ( params.empty() )
838 {
839 lines << indent + indent + QStringLiteral( "pass" );
840 }
841 else
842 {
843 lines.reserve( lines.size() + params.size() );
844 for ( const QgsProcessingParameterDefinition *def : params )
845 {
846 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
847
848 if ( defClone->isDestination() )
849 {
850 const QString uniqueChildName = defClone->metadata().value( QStringLiteral( "_modelChildId" ) ).toString() + ':' + defClone->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
851 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
852 friendlyOutputNames.insert( uniqueChildName, friendlyName );
853 defClone->setName( friendlyName );
854 }
855 else
856 {
857 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
858 {
859 const QStringList parts = mParameterComponents.value( defClone->name() ).comment()->description().split( QStringLiteral( "\n" ) );
860 for ( const QString &part : parts )
861 {
862 lines << indent + indent + QStringLiteral( "# %1" ).arg( part );
863 }
864 }
865 }
866
867 if ( defClone->flags() & Qgis::ProcessingParameterFlag::Advanced )
868 {
869 lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
870 lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
871 lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
872 }
873 else
874 {
875 lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
876 }
877 }
878 }
879
880 lines << QString();
881 lines << indent + QStringLiteral( "def processAlgorithm(self, parameters: dict[str, Any], context: QgsProcessingContext, model_feedback: QgsProcessingFeedback) -> dict[str, Any]:" );
882 currentIndent = indent + indent;
883
884 lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
885 lines << currentIndent + QStringLiteral( "# overall progress through the model" );
886 lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
887 break;
888 }
889#if 0
890 case Script:
891 {
892 QgsStringMap params;
893 QgsProcessingContext context;
894 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
895 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
896 {
897 QString name = paramIt.value().parameterName();
898 if ( parameterDefinition( name ) )
899 {
900 // TODO - generic value to string method
901 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
902 }
903 }
904
905 if ( !params.isEmpty() )
906 {
907 lines << QStringLiteral( "parameters = {" );
908 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
909 {
910 lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
911 }
912 lines << QStringLiteral( "}" )
913 << QString();
914 }
915
916 lines << QStringLiteral( "context = QgsProcessingContext()" )
917 << QStringLiteral( "context.setProject(QgsProject.instance())" )
918 << QStringLiteral( "feedback = QgsProcessingFeedback()" )
919 << QString();
920
921 break;
922 }
923#endif
924
925 }
926
927 lines << currentIndent + QStringLiteral( "results = {}" );
928 lines << currentIndent + QStringLiteral( "outputs = {}" );
929 lines << QString();
930
931 QSet< QString > executed;
932 bool executedAlg = true;
933 int currentStep = 0;
934 while ( executedAlg && executed.count() < toExecute.count() )
935 {
936 executedAlg = false;
937 const auto constToExecute = toExecute;
938 for ( const QString &childId : constToExecute )
939 {
940 if ( executed.contains( childId ) )
941 continue;
942
943 bool canExecute = true;
944 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
945 for ( const QString &dependency : constDependsOnChildAlgorithms )
946 {
947 if ( !executed.contains( dependency ) )
948 {
949 canExecute = false;
950 break;
951 }
952 }
953
954 if ( !canExecute )
955 continue;
956
957 executedAlg = true;
958
959 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
960
961 // fill in temporary outputs
962 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
963 QgsStringMap childParams;
964 for ( const QgsProcessingParameterDefinition *def : childDefs )
965 {
966 if ( def->isDestination() )
967 {
968 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
969
970 // is destination linked to one of the final outputs from this model?
971 bool isFinalOutput = false;
972 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
973 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
974 for ( ; outputIt != outputs.constEnd(); ++outputIt )
975 {
976 if ( outputIt->childOutputName() == destParam->name() )
977 {
978 QString paramName = child.childId() + ':' + outputIt.key();
979 paramName = friendlyOutputNames.value( paramName, paramName );
980 childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
981 isFinalOutput = true;
982 break;
983 }
984 }
985
986 if ( !isFinalOutput )
987 {
988 // output is temporary
989
990 // check whether it's optional, and if so - is it required?
991 bool required = true;
993 {
994 required = childOutputIsRequired( child.childId(), destParam->name() );
995 }
996
997 // not optional, or required elsewhere in model
998 if ( required )
999 {
1000 childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
1001 }
1002 }
1003 }
1004 }
1005
1006 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
1007 currentStep++;
1008 if ( currentStep < totalSteps )
1009 {
1010 lines << QString();
1011 lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
1012 lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
1013 lines << currentIndent + indent + QStringLiteral( "return {}" );
1014 lines << QString();
1015 }
1016 executed.insert( childId );
1017 }
1018 }
1019
1020 switch ( outputType )
1021 {
1023 lines << currentIndent + QStringLiteral( "return results" );
1024 lines << QString();
1025
1026 // name, displayName
1027 lines << indent + QStringLiteral( "def name(self) -> str:" );
1028 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
1029 lines << QString();
1030 lines << indent + QStringLiteral( "def displayName(self) -> str:" );
1031 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
1032 lines << QString();
1033
1034 // group, groupId
1035 lines << indent + QStringLiteral( "def group(self) -> str:" );
1036 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
1037 lines << QString();
1038 lines << indent + QStringLiteral( "def groupId(self) -> str:" );
1039 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
1040 lines << QString();
1041
1042 // help
1043 if ( !shortHelpString().isEmpty() )
1044 {
1045 lines << indent + QStringLiteral( "def shortHelpString(self) -> str:" );
1046 lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
1047 lines << QString();
1048 }
1049 if ( !helpUrl().isEmpty() )
1050 {
1051 lines << indent + QStringLiteral( "def helpUrl(self) -> str:" );
1052 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
1053 lines << QString();
1054 }
1055
1056 // createInstance
1057 lines << indent + QStringLiteral( "def createInstance(self):" );
1058 lines << indent + indent + QStringLiteral( "return self.__class__()" );
1059
1060 // additional import lines
1061 static QMap< QString, QString > sAdditionalImports
1062 {
1063 { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
1064 { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
1065 { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
1066 { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
1067 { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
1068 { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
1069 { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
1070 { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
1071 { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
1072 { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
1073 { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
1074 { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
1075 { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
1076 { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
1077 { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
1078 { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
1079 { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
1080 { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
1081 { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
1082 { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
1083 };
1084
1085 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
1086 {
1087 if ( importLines.contains( it.value() ) )
1088 {
1089 // already got this import
1090 continue;
1091 }
1092
1093 bool found = false;
1094 for ( const QString &line : std::as_const( lines ) )
1095 {
1096 if ( line.contains( it.key() ) )
1097 {
1098 found = true;
1099 break;
1100 }
1101 }
1102 if ( found )
1103 {
1104 importLines << it.value();
1105 }
1106 }
1107
1108 lines = fileDocString + importLines + lines;
1109 break;
1110 }
1111
1112 lines << QString();
1113
1114 return lines;
1115}
1116
1117QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext *context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1118{
1119 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
1120
1121 auto safeName = []( const QString & name )->QString
1122 {
1123 QString s = name;
1124 const thread_local QRegularExpression safeNameRe( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) );
1125 return s.replace( safeNameRe, QStringLiteral( "_" ) );
1126 };
1127
1128 // "static"/single value sources
1129 QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
1157 QStringList() << QgsProcessingOutputNumber::typeName()
1161
1162 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1163 {
1164 QString name;
1165 QVariant value;
1166 QString description;
1167 switch ( source.source() )
1168 {
1170 {
1171 name = source.parameterName();
1172 value = modelParameters.value( source.parameterName() );
1173 description = parameterDefinition( source.parameterName() )->description();
1174 break;
1175 }
1177 {
1178 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1179 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1180 source.outputChildId() : child.description(), source.outputName() );
1181 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1182 {
1183 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1184 child.description() );
1185 }
1186 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1187 break;
1188 }
1189
1194 continue;
1195 }
1196 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1197 }
1198
1199 // layer sources
1200 sources = availableSourcesForChild( childId, QStringList()
1206
1207 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1208 {
1209 QString name;
1210 QVariant value;
1211 QString description;
1212
1213 switch ( source.source() )
1214 {
1216 {
1217 name = source.parameterName();
1218 value = modelParameters.value( source.parameterName() );
1219 description = parameterDefinition( source.parameterName() )->description();
1220 break;
1221 }
1223 {
1224 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1225 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1226 source.outputChildId() : child.description(), source.outputName() );
1227 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1228 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1229 {
1230 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1231 child.description() );
1232 }
1233 break;
1234 }
1235
1240 continue;
1241
1242 }
1243
1244 if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1245 {
1246 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1247 value = fromVar.sink;
1248 if ( value.userType() == qMetaTypeId<QgsProperty>() && context )
1249 {
1250 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1251 }
1252 }
1253 QgsMapLayer *layer = nullptr;
1254 if ( context )
1255 {
1256 layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1257 if ( !layer )
1258 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), *context );
1259 }
1260
1261 variables.insert( safeName( name ), VariableDefinition( layer ? QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ) : QVariant(), source, description ) );
1262 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1263 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1264 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1265 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1266 }
1267
1268 sources = availableSourcesForChild( childId, QStringList()
1270 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1271 {
1272 QString name;
1273 QVariant value;
1274 QString description;
1275
1276 switch ( source.source() )
1277 {
1279 {
1280 name = source.parameterName();
1281 value = modelParameters.value( source.parameterName() );
1282 description = parameterDefinition( source.parameterName() )->description();
1283 break;
1284 }
1286 {
1287 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1288 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1289 source.outputChildId() : child.description(), source.outputName() );
1290 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1291 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1292 {
1293 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1294 child.description() );
1295 }
1296 break;
1297 }
1298
1303 continue;
1304
1305 }
1306
1307 QgsFeatureSource *featureSource = nullptr;
1308 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
1309 {
1310 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1311 value = fromVar.source;
1312 }
1313 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1314 {
1315 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1316 value = fromVar.sink;
1317 if ( context && value.userType() == qMetaTypeId<QgsProperty>() )
1318 {
1319 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1320 }
1321 }
1322 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1323 {
1324 featureSource = layer;
1325 }
1326 if ( context && !featureSource )
1327 {
1328 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), *context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1329 featureSource = vl;
1330 }
1331
1332 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1333 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1334 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1335 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1336 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1337 }
1338
1339 return variables;
1340}
1341
1342QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1343{
1344 auto scope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "algorithm_inputs" ) );
1345 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, &context, modelParameters, results );
1346 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1347 for ( ; varIt != variables.constEnd(); ++varIt )
1348 {
1349 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1350 }
1351 return scope.release();
1352}
1353
1354QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QgsProcessingParameterDefinition *param ) const
1355{
1357 if ( !paramType )
1358 return QgsProcessingModelChildParameterSources();
1359 return availableSourcesForChild( childId, paramType->acceptedParameterTypes(), paramType->acceptedOutputTypes(), paramType->acceptedDataTypes( param ) );
1360}
1361
1362QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1363{
1364 QgsProcessingModelChildParameterSources sources;
1365
1366 // first look through model parameters
1367 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1368 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1369 {
1370 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1371 if ( !def )
1372 continue;
1373
1374 if ( parameterTypes.contains( def->type() ) )
1375 {
1376 if ( !dataTypes.isEmpty() )
1377 {
1379 {
1380 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1381 if ( !( dataTypes.contains( static_cast< int >( fieldDef->dataType() ) ) || fieldDef->dataType() == Qgis::ProcessingFieldParameterDataType::Any ) )
1382 {
1383 continue;
1384 }
1385 }
1387 {
1388 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1389 if ( !sourceDef )
1390 continue;
1391
1392 bool ok = sourceDef->dataTypes().isEmpty();
1393 const auto constDataTypes = sourceDef->dataTypes();
1394 for ( int type : constDataTypes )
1395 {
1396 if ( dataTypes.contains( type ) || type == static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) || type == static_cast< int >( Qgis::ProcessingSourceType::Vector ) || type == static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) )
1397 {
1398 ok = true;
1399 break;
1400 }
1401 }
1402 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) )
1403 ok = true;
1404
1405 if ( !ok )
1406 continue;
1407 }
1408 }
1409 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1410 }
1411 }
1412
1413 QSet< QString > dependents;
1414 if ( !childId.isEmpty() )
1415 {
1416 dependents = dependentChildAlgorithms( childId );
1417 dependents << childId;
1418 }
1419
1420 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1421 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1422 {
1423 if ( dependents.contains( childIt->childId() ) )
1424 continue;
1425
1426 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1427 if ( !alg )
1428 continue;
1429
1430 const auto constOutputDefinitions = alg->outputDefinitions();
1431 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1432 {
1433 if ( outputTypes.contains( out->type() ) )
1434 {
1435 if ( !dataTypes.isEmpty() )
1436 {
1437 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1438 {
1439 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1440
1441 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1442 {
1443 //unacceptable output
1444 continue;
1445 }
1446 }
1447 }
1448 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1449 }
1450 }
1451 }
1452
1453 return sources;
1454}
1455
1456QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1457{
1458 return mHelpContent;
1459}
1460
1461void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1462{
1463 mHelpContent = helpContent;
1464}
1465
1466void QgsProcessingModelAlgorithm::setName( const QString &name )
1467{
1468 mModelName = name;
1469}
1470
1471void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1472{
1473 mModelGroup = group;
1474}
1475
1476bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1477{
1478 issues.clear();
1479 bool res = true;
1480
1481 if ( mChildAlgorithms.empty() )
1482 {
1483 res = false;
1484 issues << QObject::tr( "Model does not contain any algorithms" );
1485 }
1486
1487 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1488 {
1489 QStringList childIssues;
1490 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1491
1492 for ( const QString &issue : std::as_const( childIssues ) )
1493 {
1494 issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1495 }
1496 }
1497 return res;
1498}
1499
1500QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1501{
1502 return mChildAlgorithms;
1503}
1504
1505void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1506{
1507 mParameterComponents = parameterComponents;
1508}
1509
1510void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1511{
1512 mParameterComponents.insert( component.parameterName(), component );
1513}
1514
1515QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1516{
1517 if ( !mParameterComponents.contains( name ) )
1518 {
1519 QgsProcessingModelParameter &component = mParameterComponents[ name ];
1520 component.setParameterName( name );
1521 return component;
1522 }
1523 return mParameterComponents[ name ];
1524}
1525
1526QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1527{
1528 QList< QgsProcessingModelParameter > res;
1529 QSet< QString > found;
1530 for ( const QString &parameter : mParameterOrder )
1531 {
1532 if ( mParameterComponents.contains( parameter ) )
1533 {
1534 res << mParameterComponents.value( parameter );
1535 found << parameter;
1536 }
1537 }
1538
1539 // add any missing ones to end of list
1540 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1541 {
1542 if ( !found.contains( it.key() ) )
1543 {
1544 res << it.value();
1545 }
1546 }
1547 return res;
1548}
1549
1550void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1551{
1552 mParameterOrder = order;
1553}
1554
1555QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
1556{
1557 QList< QgsProcessingModelOutput > res;
1558 QSet< QString > found;
1559
1560 for ( const QString &output : mOutputOrder )
1561 {
1562 bool foundOutput = false;
1563 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1564 {
1565 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1566 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1567 {
1568 if ( output == QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) )
1569 {
1570 res << outputIt.value();
1571 foundOutput = true;
1572 found.insert( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) );
1573 }
1574 }
1575 if ( foundOutput )
1576 break;
1577 }
1578 }
1579
1580 // add any missing ones to end of list
1581 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1582 {
1583 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1584 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1585 {
1586 if ( !found.contains( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) ) )
1587 {
1588 res << outputIt.value();
1589 }
1590 }
1591 }
1592
1593 return res;
1594}
1595
1596void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
1597{
1598 mOutputOrder = order;
1599}
1600
1601QString QgsProcessingModelAlgorithm::outputGroup() const
1602{
1603 return mOutputGroup;
1604}
1605
1606void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
1607{
1608 mOutputGroup = group;
1609}
1610
1611void QgsProcessingModelAlgorithm::updateDestinationParameters()
1612{
1613 //delete existing destination parameters
1614 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1615 while ( it.hasNext() )
1616 {
1617 const QgsProcessingParameterDefinition *def = it.next();
1618 if ( def->isDestination() )
1619 {
1620 delete def;
1621 it.remove();
1622 }
1623 }
1624 // also delete outputs
1625 qDeleteAll( mOutputs );
1626 mOutputs.clear();
1627
1628 // rebuild
1629 QSet< QString > usedFriendlyNames;
1630 auto uniqueSafeName = [&usedFriendlyNames ]( const QString & name )->QString
1631 {
1632 const QString base = safeName( name, false );
1633 QString candidate = base;
1634 int i = 1;
1635 while ( usedFriendlyNames.contains( candidate ) )
1636 {
1637 i++;
1638 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
1639 }
1640 usedFriendlyNames.insert( candidate );
1641 return candidate;
1642 };
1643
1644 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1645 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1646 {
1647 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1648 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1649 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1650 {
1651 if ( !childIt->isActive() || !childIt->algorithm() )
1652 continue;
1653
1654 // child algorithm has a destination parameter set, copy it to the model
1655 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1656 if ( !source )
1657 continue;
1658
1659 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1660 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1661 // outputs.
1662 param->setFlags( param->flags() & ~ static_cast< int >( Qgis::ProcessingParameterFlag::Hidden ) );
1663 if ( outputIt->isMandatory() )
1664 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Optional ) );
1665 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1666 {
1667 QString friendlyName = uniqueSafeName( outputIt->description() );
1668 param->setName( friendlyName );
1669 }
1670 else
1671 {
1672 param->setName( outputIt->childId() + ':' + outputIt->name() );
1673 }
1674 // add some metadata so we can easily link this parameter back to the child source
1675 param->metadata().insert( QStringLiteral( "_modelChildId" ), outputIt->childId() );
1676 param->metadata().insert( QStringLiteral( "_modelChildOutputName" ), outputIt->name() );
1677 param->metadata().insert( QStringLiteral( "_modelChildProvider" ), childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1678
1679 param->setDescription( outputIt->description() );
1680 param->setDefaultValue( outputIt->defaultValue() );
1681
1682 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1683 if ( addParameter( param.release() ) && newDestParam )
1684 {
1685 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1686 {
1687 // we need to copy the constraints given by the provider which creates this output across
1688 // and replace those which have been set to match the model provider's constraints
1689 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1690 newDestParam->mOriginalProvider = provider;
1691 }
1692 }
1693 }
1694 }
1695}
1696
1697void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1698{
1699 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1700}
1701
1702QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1703{
1704 return mGroupBoxes.values();
1705}
1706
1707void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1708{
1709 mGroupBoxes.remove( uuid );
1710}
1711
1712QVariant QgsProcessingModelAlgorithm::toVariant() const
1713{
1714 QVariantMap map;
1715 map.insert( QStringLiteral( "model_name" ), mModelName );
1716 map.insert( QStringLiteral( "model_group" ), mModelGroup );
1717 map.insert( QStringLiteral( "help" ), mHelpContent );
1718 map.insert( QStringLiteral( "internal_version" ), qgsEnumValueToKey( mInternalVersion ) );
1719
1720 QVariantMap childMap;
1721 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1722 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1723 {
1724 childMap.insert( childIt.key(), childIt.value().toVariant() );
1725 }
1726 map.insert( QStringLiteral( "children" ), childMap );
1727
1728 QVariantMap paramMap;
1729 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1730 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1731 {
1732 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1733 }
1734 map.insert( QStringLiteral( "parameters" ), paramMap );
1735
1736 QVariantMap paramDefMap;
1737 for ( const QgsProcessingParameterDefinition *def : mParameters )
1738 {
1739 paramDefMap.insert( def->name(), def->toVariantMap() );
1740 }
1741 map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1742
1743 QVariantList groupBoxDefs;
1744 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1745 {
1746 groupBoxDefs.append( it.value().toVariant() );
1747 }
1748 map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1749
1750 map.insert( QStringLiteral( "modelVariables" ), mVariables );
1751
1752 map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1753
1754 map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1755 map.insert( QStringLiteral( "outputOrder" ), mOutputOrder );
1756 map.insert( QStringLiteral( "outputGroup" ), mOutputGroup );
1757
1758 return map;
1759}
1760
1761bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1762{
1763 QVariantMap map = model.toMap();
1764
1765 mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1766 mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1767 mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1768 mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1769
1770 mInternalVersion = qgsEnumKeyToValue( map.value( QStringLiteral( "internal_version" ) ).toString(), InternalVersion::Version1 );
1771
1772 mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1773 mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1774
1775 mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1776 mOutputOrder = map.value( QStringLiteral( "outputOrder" ) ).toStringList();
1777 mOutputGroup = map.value( QStringLiteral( "outputGroup" ) ).toString();
1778
1779 mChildAlgorithms.clear();
1780 QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1781 QVariantMap::const_iterator childIt = childMap.constBegin();
1782 for ( ; childIt != childMap.constEnd(); ++childIt )
1783 {
1784 QgsProcessingModelChildAlgorithm child;
1785 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1786 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1787 // with no way for users to repair them
1788 if ( !child.loadVariant( childIt.value() ) )
1789 continue;
1790
1791 mChildAlgorithms.insert( child.childId(), child );
1792 }
1793
1794 mParameterComponents.clear();
1795 QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1796 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1797 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1798 {
1799 QgsProcessingModelParameter param;
1800 if ( !param.loadVariant( paramIt.value().toMap() ) )
1801 return false;
1802
1803 mParameterComponents.insert( param.parameterName(), param );
1804 }
1805
1806 qDeleteAll( mParameters );
1807 mParameters.clear();
1808 QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1809
1810 auto addParam = [this]( const QVariant & value )
1811 {
1812 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1813 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1814 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1815 // with no way for users to repair them
1816 if ( param )
1817 {
1818 if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1819 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1820
1821 // set parameter help from help content
1822 param->setHelp( mHelpContent.value( param->name() ).toString() );
1823
1824 // add parameter
1825 addParameter( param.release() );
1826 }
1827 else
1828 {
1829 QVariantMap map = value.toMap();
1830 QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1831 QString name = map.value( QStringLiteral( "name" ) ).toString();
1832
1833 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1834 }
1835 };
1836
1837 QSet< QString > loadedParams;
1838 // first add parameters respecting mParameterOrder
1839 for ( const QString &name : std::as_const( mParameterOrder ) )
1840 {
1841 if ( paramDefMap.contains( name ) )
1842 {
1843 addParam( paramDefMap.value( name ) );
1844 loadedParams << name;
1845 }
1846 }
1847 // then load any remaining parameters
1848 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1849 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1850 {
1851 if ( !loadedParams.contains( paramDefIt.key() ) )
1852 addParam( paramDefIt.value() );
1853 }
1854
1855 mGroupBoxes.clear();
1856 const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1857 for ( const QVariant &groupBoxDef : groupBoxList )
1858 {
1859 QgsProcessingModelGroupBox groupBox;
1860 groupBox.loadVariant( groupBoxDef.toMap() );
1861 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1862 }
1863
1864 updateDestinationParameters();
1865
1866 return true;
1867}
1868
1869bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType )
1870{
1871 // This method is intended to be "permissive" rather than "restrictive".
1872 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1873 // if there's doubt then we default to returning true.
1874 return ( acceptableDataTypes.empty()
1875 || acceptableDataTypes.contains( static_cast< int >( outputType ) )
1877 || outputType == Qgis::ProcessingSourceType::Vector
1879 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1880 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1881 || ( acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && ( outputType == Qgis::ProcessingSourceType::VectorPoint ||
1884}
1885
1886void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1887{
1888 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1889 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1890 {
1891 if ( !childIt->algorithm() )
1892 childIt->reattach();
1893 }
1894}
1895
1896bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1897{
1898 QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1899 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1900 doc.appendChild( elem );
1901
1902 QFile file( path );
1903 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1904 {
1905 QTextStream stream( &file );
1906 doc.save( stream, 2 );
1907 file.close();
1908 return true;
1909 }
1910 return false;
1911}
1912
1913bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1914{
1915 QDomDocument doc;
1916
1917 QFile file( path );
1918 if ( file.open( QFile::ReadOnly ) )
1919 {
1920 if ( !doc.setContent( &file ) )
1921 return false;
1922
1923 file.close();
1924 }
1925 else
1926 {
1927 return false;
1928 }
1929
1930 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1931 return loadVariant( props );
1932}
1933
1934void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1935{
1936 mChildAlgorithms = childAlgorithms;
1937 updateDestinationParameters();
1938}
1939
1940void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1941{
1942 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1943 updateDestinationParameters();
1944}
1945
1946QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1947{
1948 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1949 algorithm.generateChildId( *this );
1950
1951 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1952 updateDestinationParameters();
1953 return algorithm.childId();
1954}
1955
1956QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1957{
1958 return mChildAlgorithms[ childId ];
1959}
1960
1961bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1962{
1963 if ( !dependentChildAlgorithms( id ).isEmpty() )
1964 return false;
1965
1966 mChildAlgorithms.remove( id );
1967 updateDestinationParameters();
1968 return true;
1969}
1970
1971void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1972{
1973 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1974 for ( const QString &child : constDependentChildAlgorithms )
1975 {
1976 childAlgorithm( child ).setActive( false );
1977 }
1978 childAlgorithm( id ).setActive( false );
1979 updateDestinationParameters();
1980}
1981
1982bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1983{
1984 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1985 for ( const QString &child : constDependsOnChildAlgorithms )
1986 {
1987 if ( !childAlgorithm( child ).isActive() )
1988 return false;
1989 }
1990 childAlgorithm( id ).setActive( true );
1991 updateDestinationParameters();
1992 return true;
1993}
1994
1995void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1996{
1997 if ( addParameter( definition ) )
1998 mParameterComponents.insert( definition->name(), component );
1999}
2000
2001void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
2002{
2003 removeParameter( definition->name() );
2004 addParameter( definition );
2005}
2006
2007void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
2008{
2009 removeParameter( name );
2010 mParameterComponents.remove( name );
2011}
2012
2013void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
2014{
2015 QgsProcessingContext context;
2016 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
2017
2018 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString & expressionString ) -> std::tuple< bool, QString >
2019 {
2020 QgsExpression expression( expressionString );
2021 expression.prepare( &expressionContext );
2022 QSet<QString> variables = expression.referencedVariables();
2023 if ( variables.contains( oldName ) )
2024 {
2025 QString newExpression = expressionString;
2026 newExpression.replace( QStringLiteral( "@%1" ).arg( oldName ), QStringLiteral( "@%2" ).arg( newName ) );
2027 return { true, newExpression };
2028 }
2029 return { false, QString() };
2030 };
2031
2032 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
2033 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
2034 {
2035 bool changed = false;
2036 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2037 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
2038 for ( ; paramIt != childParams.end(); ++paramIt )
2039 {
2040 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
2041 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
2042 {
2043 switch ( valueIt->source() )
2044 {
2046 {
2047 if ( valueIt->parameterName() == oldName )
2048 {
2049 valueIt->setParameterName( newName );
2050 changed = true;
2051 }
2052 break;
2053 }
2054
2056 {
2057 bool updatedExpression = false;
2058 QString newExpression;
2059 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
2060 if ( updatedExpression )
2061 {
2062 valueIt->setExpression( newExpression );
2063 changed = true;
2064 }
2065 break;
2066 }
2067
2069 {
2070 if ( valueIt->staticValue().userType() == qMetaTypeId<QgsProperty>() )
2071 {
2072 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
2073 if ( property.propertyType() == Qgis::PropertyType::Expression )
2074 {
2075 bool updatedExpression = false;
2076 QString newExpression;
2077 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
2078 if ( updatedExpression )
2079 {
2080 property.setExpressionString( newExpression );
2081 valueIt->setStaticValue( property );
2082 changed = true;
2083 }
2084 }
2085 }
2086 break;
2087 }
2088
2092 break;
2093 }
2094 }
2095 }
2096 if ( changed )
2097 childIt->setParameterSources( childParams );
2098 }
2099}
2100
2101bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
2102{
2103 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2104 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2105 {
2106 // check whether child requires this parameter
2107 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2108 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2109 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2110 {
2111 const auto constValue = paramIt.value();
2112 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2113 {
2115 && source.parameterName() == name )
2116 {
2117 return true;
2118 }
2119 }
2120 }
2121 }
2122 return false;
2123}
2124
2125bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
2126{
2127 const auto constMParameters = mParameters;
2128 for ( const QgsProcessingParameterDefinition *def : constMParameters )
2129 {
2130 if ( def->name() == name )
2131 continue;
2132
2133 if ( def->dependsOnOtherParameters().contains( name ) )
2134 return true;
2135 }
2136 return false;
2137}
2138
2139QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
2140{
2141 return mParameterComponents;
2142}
2143
2144void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
2145{
2146 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2147 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2148 {
2149 if ( depends.contains( childIt->childId() ) )
2150 continue;
2151
2152 // does alg have a direct dependency on this child?
2153 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
2154 bool hasDependency = false;
2155 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
2156 {
2157 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
2158 {
2159 hasDependency = true;
2160 break;
2161 }
2162 }
2163
2164 if ( hasDependency )
2165 {
2166 depends.insert( childIt->childId() );
2167 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2168 continue;
2169 }
2170
2171 // check whether child requires any outputs from the target alg
2172 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2173 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2174 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2175 {
2176 const auto constValue = paramIt.value();
2177 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2178 {
2180 && source.outputChildId() == childId )
2181 {
2182 depends.insert( childIt->childId() );
2183 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2184 break;
2185 }
2186 }
2187 }
2188 }
2189}
2190
2191QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
2192{
2193 QSet< QString > algs;
2194
2195 // temporarily insert the target child algorithm to avoid
2196 // unnecessarily recursion though it
2197 algs.insert( childId );
2198
2199 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
2200
2201 // remove temporary target alg
2202 algs.remove( childId );
2203
2204 return algs;
2205}
2206
2207
2208void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
2209{
2210 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
2211
2212 // add direct dependencies
2213 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
2214 for ( const QgsProcessingModelChildDependency &val : constDependencies )
2215 {
2216 if ( !depends.contains( val.childId ) )
2217 {
2218 depends.insert( val.childId );
2219 dependsOnChildAlgorithmsRecursive( val.childId, depends );
2220 }
2221 }
2222
2223 // check through parameter dependencies
2224 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
2225 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2226 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2227 {
2228 const auto constValue = paramIt.value();
2229 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2230 {
2231 switch ( source.source() )
2232 {
2234 if ( !depends.contains( source.outputChildId() ) )
2235 {
2236 depends.insert( source.outputChildId() );
2237 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
2238 }
2239 break;
2240
2242 {
2243 const QgsExpression exp( source.expression() );
2244 const QSet<QString> vars = exp.referencedVariables();
2245 if ( vars.empty() )
2246 break;
2247
2248 // find the source of referenced variables and check if it's another child algorithm
2249 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> availableVariables = variablesForChildAlgorithm( childId );
2250 for ( auto childVarIt = availableVariables.constBegin(); childVarIt != availableVariables.constEnd(); ++childVarIt )
2251 {
2252 // we're only looking here for variables coming from other child algorithm outputs
2253 if ( childVarIt->source.source() != Qgis::ProcessingModelChildParameterSource::ChildOutput )
2254 continue;
2255
2256 if ( !vars.contains( childVarIt.key() ) || depends.contains( childVarIt->source.outputChildId() ) )
2257 continue;
2258
2259 // this variable is required for the child's expression, so the corresponding algorithm must be run first
2260 depends.insert( childVarIt->source.outputChildId() );
2261 dependsOnChildAlgorithmsRecursive( childVarIt->source.outputChildId(), depends );
2262 }
2263 break;
2264 }
2265
2270 break;
2271 }
2272 }
2273 }
2274}
2275
2276QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2277{
2278 QSet< QString > algs;
2279
2280 // temporarily insert the target child algorithm to avoid
2281 // unnecessarily recursion though it
2282 algs.insert( childId );
2283
2284 dependsOnChildAlgorithmsRecursive( childId, algs );
2285
2286 // remove temporary target alg
2287 algs.remove( childId );
2288
2289 return algs;
2290}
2291
2292QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2293{
2294 QSet< QString > dependent;
2295 if ( !childId.isEmpty() )
2296 {
2297 dependent.unite( dependentChildAlgorithms( childId ) );
2298 dependent.insert( childId );
2299 }
2300
2301 QList<QgsProcessingModelChildDependency> res;
2302 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2303 {
2304 if ( !dependent.contains( it->childId() ) )
2305 {
2306 // check first if algorithm provides output branches
2307 bool hasBranches = false;
2308 if ( it->algorithm() )
2309 {
2310 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2311 for ( const QgsProcessingOutputDefinition *def : defs )
2312 {
2314 {
2315 hasBranches = true;
2316 QgsProcessingModelChildDependency alg;
2317 alg.childId = it->childId();
2318 alg.conditionalBranch = def->name();
2319 res << alg;
2320 }
2321 }
2322 }
2323
2324 if ( !hasBranches )
2325 {
2326 QgsProcessingModelChildDependency alg;
2327 alg.childId = it->childId();
2328 res << alg;
2329 }
2330 }
2331 }
2332 return res;
2333}
2334
2335bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2336{
2337 issues.clear();
2338 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2339 if ( childIt != mChildAlgorithms.constEnd() )
2340 {
2341 if ( !childIt->algorithm() )
2342 {
2343 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2344 return false;
2345 }
2346 bool res = true;
2347
2348 // loop through child algorithm parameters and check that they are all valid
2349 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2350 for ( const QgsProcessingParameterDefinition *def : defs )
2351 {
2352 if ( childIt->parameterSources().contains( def->name() ) )
2353 {
2354 // is the value acceptable?
2355 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2356 for ( const QgsProcessingModelChildParameterSource &source : sources )
2357 {
2358 switch ( source.source() )
2359 {
2361 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2362 {
2363 res = false;
2364 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2365 }
2366 break;
2367
2369 if ( !parameterComponents().contains( source.parameterName() ) )
2370 {
2371 res = false;
2372 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2373 }
2374 break;
2375
2377 if ( !childAlgorithms().contains( source.outputChildId() ) )
2378 {
2379 res = false;
2380 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2381 }
2382 break;
2383
2387 break;
2388 }
2389 }
2390 }
2391 else
2392 {
2393 // not specified. Is it optional?
2394
2395 // ignore destination parameters -- they shouldn't ever be mandatory
2396 if ( def->isDestination() )
2397 continue;
2398
2399 if ( !def->checkValueIsAcceptable( QVariant() ) )
2400 {
2401 res = false;
2402 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2403 }
2404 }
2405 }
2406
2407 return res;
2408 }
2409 else
2410 {
2411 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2412 return false;
2413 }
2414}
2415
2416bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2417{
2418 reattachAlgorithms();
2419 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2420 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2421 {
2422 if ( !childIt->algorithm() )
2423 {
2424 if ( errorMessage )
2425 {
2426 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2427 }
2428 return false;
2429 }
2430 }
2431 return true;
2432}
2433
2434QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2435{
2436 if ( mSourceFile.isEmpty() )
2437 return QString(); // temporary model - can't run as python command
2438
2439 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2440}
2441
2442QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2443{
2444 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2445 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2446 return res;
2447}
2448
2449QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2450{
2451 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2452 alg->loadVariant( toVariant() );
2453 alg->setProvider( provider() );
2454 alg->setSourceFilePath( sourceFilePath() );
2455 return alg;
2456}
2457
2458QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2459{
2460 QString n = name.toLower().trimmed();
2461 const thread_local QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
2462 n.replace( rx, QString() );
2463 const thread_local QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
2464 n.replace( rx2, QString() );
2465 if ( !capitalize )
2466 n = n.replace( ' ', '_' );
2468}
2469
2470QVariantMap QgsProcessingModelAlgorithm::variables() const
2471{
2472 return mVariables;
2473}
2474
2475void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2476{
2477 mVariables = variables;
2478}
2479
2480QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2481{
2482 return mDesignerParameterValues;
2483}
2484
ProcessingSourceType
Processing data source types.
Definition qgis.h:3531
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3539
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
Definition qgis.h:3532
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3533
@ VectorPoint
Vector point layers.
Definition qgis.h:3534
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ VectorLine
Vector line layers.
Definition qgis.h:3535
@ Success
Child was successfully executed.
Definition qgis.h:3865
@ Failed
Child encountered an error while executing.
Definition qgis.h:3866
@ Expression
Expression based property.
Definition qgis.h:686
@ UpperCamelCase
Convert the string to upper camel case. Note that this method does not unaccent characters.
Definition qgis.h:3396
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3609
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition qgis.cpp:682
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
Definition qgis.h:3852
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:3853
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:3849
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:3848
@ StaticValue
Parameter value is a static value.
Definition qgis.h:3850
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:3851
@ SkipGenericModelLogging
When running as part of a model, the generic algorithm setup and results logging should be skipped.
Definition qgis.h:3594
@ CustomException
Algorithm raises custom exception notices, don't use the standard ones.
Definition qgis.h:3592
@ NoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
Definition qgis.h:3588
@ PruneModelBranchesBasedOnAlgorithmResults
Algorithm results will cause remaining model branches to be pruned based on the results of running th...
Definition qgis.h:3593
@ SecurityRisk
The algorithm represents a potential security risk if executed with untrusted inputs.
Definition qgis.h:3597
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3764
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3763
@ Optional
Parameter is optional.
Definition qgis.h:3765
@ DefaultLevel
Default logging level.
Definition qgis.h:3657
@ Verbose
Verbose logging.
Definition qgis.h:3658
@ ModelDebug
Model debug level logging. Includes verbose logging and other outputs useful for debugging models.
Definition qgis.h:3659
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.
QString what() const
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...
Handles 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
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
Base class for all map layer types.
Definition qgsmaplayer.h:80
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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 Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
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.
Details for layers to load into projects.
int layerSortKey
Optional sorting key for sorting output layers when loading them into a project.
QString groupName
Optional name for a layer tree group under which to place the layer when loading it into a project.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsProcessingModelResult modelResult() const
Returns the model results, populated when the context is used to run a model algorithm.
QgsProcessingModelInitialRunConfig * modelInitialRunConfig()
Returns a reference to the model initial run configuration, used to run a model algorithm.
Qgis::ProcessingLogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
void setModelInitialRunConfig(std::unique_ptr< QgsProcessingModelInitialRunConfig > config)
Sets the model initial run configuration, used to run a model algorithm.
Base class for all parameter definitions which represent file or layer destinations,...
virtual QString generateTemporaryDestination(const QgsProcessingContext *context=nullptr) 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.
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 pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual QString htmlLog() const
Returns the HTML formatted contents of the log, which contains all messages pushed to the feedback ob...
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.
Encapsulates the results of running a child algorithm within a model.
void setOutputs(const QVariantMap &outputs)
Sets the outputs generated by child algorithm.
void setExecutionStatus(Qgis::ProcessingModelChildAlgorithmExecutionStatus status)
Sets the status of executing the child algorithm.
void setInputs(const QVariantMap &inputs)
Sets the inputs used for the child algorithm.
void setHtmlLog(const QString &log)
Sets the HTML formatted contents of logged messages which occurred while running the child.
Configuration settings which control how a Processing model is executed.
QSet< QString > childAlgorithmSubset() const
Returns the subset of child algorithms to run (by child ID).
QVariantMap & rawChildOutputs()
Returns a reference to the map of raw child algorithm outputs.
QVariantMap & rawChildInputs()
Returns a reference to the map of raw child algorithm inputs.
QSet< QString > & executedChildIds()
Returns a reference to the set of child algorithm IDs which were executed during the model execution.
QMap< QString, QgsProcessingModelChildAlgorithmResult > childResults() const
Returns the map of child algorithm results.
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.
static QString typeName()
Returns the type name for the output class.
A vector layer output for processing algorithms.
Qgis::ProcessingSourceType dataType() const
Returns the layer type for the output layer.
static QString typeName()
Returns the type name for the output 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.
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.
void setDefaultValue(const QVariant &value)
Sets the default value for the parameter.
QgsProcessingAlgorithm * algorithm() const
Returns a pointer to the algorithm which owns this parameter.
void setFlags(Qgis::ProcessingParameterFlags flags)
Sets the flags associated with the parameter.
QVariantMap metadata() const
Returns the parameter's freeform metadata.
virtual bool isDestination() const
Returns true if this parameter represents a file or layer destination, e.g.
void setDescription(const QString &description)
Sets the description for the parameter.
void setName(const QString &name)
Sets the name of the parameter.
virtual QgsProcessingParameterDefinition * clone() const =0
Creates a clone of the parameter definition.
virtual QString type() const =0
Unique parameter type name.
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.
Qgis::ProcessingParameterFlags flags() const
Returns any flags associated with the parameter.
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.
Qgis::ProcessingFieldParameterDataType 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.
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.
Makes metadata of processing parameters available.
virtual QStringList acceptedOutputTypes() const =0
Returns a list of compatible Processing output types for inputs for this parameter type.
virtual QList< int > acceptedDataTypes(const QgsProcessingParameterDefinition *parameter) const
Returns a list of compatible Processing data types for inputs for this parameter type for the specifi...
virtual QStringList acceptedParameterTypes() const =0
Returns a list of compatible Processing parameter types for inputs for this parameter type.
static QString typeName()
Returns the type name for the parameter class.
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.
static QString variantToPythonLiteral(const QVariant &value)
Converts a variant to a Python literal.
static QVariantMap removePointerValuesFromMap(const QVariantMap &map)
Removes any raw pointer values from an input map, replacing them with appropriate string values where...
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
PythonOutputType
Available Python output types.
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
A store for object properties.
QVariant staticValue() const
Returns the current static value for the property.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
Represents a vector layer which manages a vector based dataset.
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
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6817
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
QString qgsSetJoin(const QSet< T > &set, const QString &separator)
Joins all the set values into a single string with each element separated by the given separator.
Definition qgis.h:6698
QMap< QString, QString > QgsStringMap
Definition qgis.h:7132
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Single variable definition for use within a QgsExpressionContextScope.