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