QGIS API Documentation 3.99.0-Master (b927df884fe)
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 // iterate over friendlyNames value to find candidate
773 while ( std::find( friendlyNames.cbegin(), friendlyNames.cend(), candidate ) != friendlyNames.cend() )
774 {
775 i++;
776 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
777 }
778 return candidate;
779 };
780
781 const QString algorithmClassName = safeName( name(), true );
782
783 QSet< QString > toExecute;
784 for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
785 {
786 if ( childIt->isActive() && childIt->algorithm() )
787 {
788 toExecute.insert( childIt->childId() );
789 friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
790 }
791 }
792 const int totalSteps = toExecute.count();
793
794 QStringList importLines; // not a set - we need regular ordering
795 switch ( outputType )
796 {
798 {
799 // add specific parameter type imports
800 const auto params = parameterDefinitions();
801 importLines.reserve( params.count() + 6 );
802 importLines << QStringLiteral( "from typing import Any, Optional" );
803 importLines << QString();
804 importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
805 importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
806 importLines << QStringLiteral( "from qgis.core import QgsProcessingContext" );
807 importLines << QStringLiteral( "from qgis.core import QgsProcessingFeedback, QgsProcessingMultiStepFeedback" );
808
809 bool hasAdvancedParams = false;
810 for ( const QgsProcessingParameterDefinition *def : params )
811 {
812 if ( def->flags() & Qgis::ProcessingParameterFlag::Advanced )
813 hasAdvancedParams = true;
814
815 const QString importString = QgsApplication::processingRegistry()->parameterType( def->type() )->pythonImportString();
816 if ( !importString.isEmpty() && !importLines.contains( importString ) )
817 importLines << importString;
818 }
819
820 if ( hasAdvancedParams )
821 importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
822
823 lines << QStringLiteral( "from qgis import processing" );
824 lines << QString() << QString();
825
826 lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
827 lines << QString();
828
829 // initAlgorithm, parameter definitions
830 lines << indent + QStringLiteral( "def initAlgorithm(self, config: Optional[dict[str, Any]] = None):" );
831 if ( params.empty() )
832 {
833 lines << indent + indent + QStringLiteral( "pass" );
834 }
835 else
836 {
837 lines.reserve( lines.size() + params.size() );
838 for ( const QgsProcessingParameterDefinition *def : params )
839 {
840 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
841
842 if ( defClone->isDestination() )
843 {
844 const QString uniqueChildName = defClone->metadata().value( QStringLiteral( "_modelChildId" ) ).toString() + ':' + defClone->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
845 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
846 friendlyOutputNames.insert( uniqueChildName, friendlyName );
847 defClone->setName( friendlyName );
848 }
849 else
850 {
851 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
852 {
853 const QStringList parts = mParameterComponents.value( defClone->name() ).comment()->description().split( QStringLiteral( "\n" ) );
854 for ( const QString &part : parts )
855 {
856 lines << indent + indent + QStringLiteral( "# %1" ).arg( part );
857 }
858 }
859 }
860
861 if ( defClone->flags() & Qgis::ProcessingParameterFlag::Advanced )
862 {
863 lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
864 lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
865 lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
866 }
867 else
868 {
869 lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
870 }
871 }
872 }
873
874 lines << QString();
875 lines << indent + QStringLiteral( "def processAlgorithm(self, parameters: dict[str, Any], context: QgsProcessingContext, model_feedback: QgsProcessingFeedback) -> dict[str, Any]:" );
876 currentIndent = indent + indent;
877
878 lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
879 lines << currentIndent + QStringLiteral( "# overall progress through the model" );
880 lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
881 break;
882 }
883#if 0
884 case Script:
885 {
886 QgsStringMap params;
887 QgsProcessingContext context;
888 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
889 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
890 {
891 QString name = paramIt.value().parameterName();
892 if ( parameterDefinition( name ) )
893 {
894 // TODO - generic value to string method
895 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
896 }
897 }
898
899 if ( !params.isEmpty() )
900 {
901 lines << QStringLiteral( "parameters = {" );
902 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
903 {
904 lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
905 }
906 lines << QStringLiteral( "}" )
907 << QString();
908 }
909
910 lines << QStringLiteral( "context = QgsProcessingContext()" )
911 << QStringLiteral( "context.setProject(QgsProject.instance())" )
912 << QStringLiteral( "feedback = QgsProcessingFeedback()" )
913 << QString();
914
915 break;
916 }
917#endif
918
919 }
920
921 lines << currentIndent + QStringLiteral( "results = {}" );
922 lines << currentIndent + QStringLiteral( "outputs = {}" );
923 lines << QString();
924
925 QSet< QString > executed;
926 bool executedAlg = true;
927 int currentStep = 0;
928 while ( executedAlg && executed.count() < toExecute.count() )
929 {
930 executedAlg = false;
931 const auto constToExecute = toExecute;
932 for ( const QString &childId : constToExecute )
933 {
934 if ( executed.contains( childId ) )
935 continue;
936
937 bool canExecute = true;
938 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
939 for ( const QString &dependency : constDependsOnChildAlgorithms )
940 {
941 if ( !executed.contains( dependency ) )
942 {
943 canExecute = false;
944 break;
945 }
946 }
947
948 if ( !canExecute )
949 continue;
950
951 executedAlg = true;
952
953 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
954
955 // fill in temporary outputs
956 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
957 QgsStringMap childParams;
958 for ( const QgsProcessingParameterDefinition *def : childDefs )
959 {
960 if ( def->isDestination() )
961 {
962 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
963
964 // is destination linked to one of the final outputs from this model?
965 bool isFinalOutput = false;
966 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
967 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
968 for ( ; outputIt != outputs.constEnd(); ++outputIt )
969 {
970 if ( outputIt->childOutputName() == destParam->name() )
971 {
972 QString paramName = child.childId() + ':' + outputIt.key();
973 paramName = friendlyOutputNames.value( paramName, paramName );
974 childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
975 isFinalOutput = true;
976 break;
977 }
978 }
979
980 if ( !isFinalOutput )
981 {
982 // output is temporary
983
984 // check whether it's optional, and if so - is it required?
985 bool required = true;
987 {
988 required = childOutputIsRequired( child.childId(), destParam->name() );
989 }
990
991 // not optional, or required elsewhere in model
992 if ( required )
993 {
994 childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
995 }
996 }
997 }
998 }
999
1000 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
1001 currentStep++;
1002 if ( currentStep < totalSteps )
1003 {
1004 lines << QString();
1005 lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
1006 lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
1007 lines << currentIndent + indent + QStringLiteral( "return {}" );
1008 lines << QString();
1009 }
1010 executed.insert( childId );
1011 }
1012 }
1013
1014 switch ( outputType )
1015 {
1017 lines << currentIndent + QStringLiteral( "return results" );
1018 lines << QString();
1019
1020 // name, displayName
1021 lines << indent + QStringLiteral( "def name(self) -> str:" );
1022 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
1023 lines << QString();
1024 lines << indent + QStringLiteral( "def displayName(self) -> str:" );
1025 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
1026 lines << QString();
1027
1028 // group, groupId
1029 lines << indent + QStringLiteral( "def group(self) -> str:" );
1030 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
1031 lines << QString();
1032 lines << indent + QStringLiteral( "def groupId(self) -> str:" );
1033 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
1034 lines << QString();
1035
1036 // help
1037 if ( !shortHelpString().isEmpty() )
1038 {
1039 lines << indent + QStringLiteral( "def shortHelpString(self) -> str:" );
1040 lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
1041 lines << QString();
1042 }
1043 if ( !helpUrl().isEmpty() )
1044 {
1045 lines << indent + QStringLiteral( "def helpUrl(self) -> str:" );
1046 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
1047 lines << QString();
1048 }
1049
1050 // createInstance
1051 lines << indent + QStringLiteral( "def createInstance(self):" );
1052 lines << indent + indent + QStringLiteral( "return self.__class__()" );
1053
1054 // additional import lines
1055 static QMap< QString, QString > sAdditionalImports
1056 {
1057 { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
1058 { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
1059 { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
1060 { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
1061 { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
1062 { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
1063 { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
1064 { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
1065 { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
1066 { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
1067 { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
1068 { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
1069 { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
1070 { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
1071 { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
1072 { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
1073 { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
1074 { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
1075 { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
1076 { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
1077 };
1078
1079 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
1080 {
1081 if ( importLines.contains( it.value() ) )
1082 {
1083 // already got this import
1084 continue;
1085 }
1086
1087 bool found = false;
1088 for ( const QString &line : std::as_const( lines ) )
1089 {
1090 if ( line.contains( it.key() ) )
1091 {
1092 found = true;
1093 break;
1094 }
1095 }
1096 if ( found )
1097 {
1098 importLines << it.value();
1099 }
1100 }
1101
1102 lines = fileDocString + importLines + lines;
1103 break;
1104 }
1105
1106 lines << QString();
1107
1108 return lines;
1109}
1110
1111QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext *context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1112{
1113 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
1114
1115 auto safeName = []( const QString & name )->QString
1116 {
1117 QString s = name;
1118 const thread_local QRegularExpression safeNameRe( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) );
1119 return s.replace( safeNameRe, QStringLiteral( "_" ) );
1120 };
1121
1122 // "static"/single value sources
1123 QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
1151 QStringList() << QgsProcessingOutputNumber::typeName()
1155
1156 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1157 {
1158 QString name;
1159 QVariant value;
1160 QString description;
1161 switch ( source.source() )
1162 {
1164 {
1165 name = source.parameterName();
1166 value = modelParameters.value( source.parameterName() );
1167 description = parameterDefinition( source.parameterName() )->description();
1168 break;
1169 }
1171 {
1172 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1173 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1174 source.outputChildId() : child.description(), source.outputName() );
1175 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1176 {
1177 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1178 child.description() );
1179 }
1180 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1181 break;
1182 }
1183
1188 continue;
1189 }
1190 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1191 }
1192
1193 // layer sources
1194 sources = availableSourcesForChild( childId, QStringList()
1200
1201 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1202 {
1203 QString name;
1204 QVariant value;
1205 QString description;
1206
1207 switch ( source.source() )
1208 {
1210 {
1211 name = source.parameterName();
1212 value = modelParameters.value( source.parameterName() );
1213 description = parameterDefinition( source.parameterName() )->description();
1214 break;
1215 }
1217 {
1218 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1219 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1220 source.outputChildId() : child.description(), source.outputName() );
1221 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1222 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1223 {
1224 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1225 child.description() );
1226 }
1227 break;
1228 }
1229
1234 continue;
1235
1236 }
1237
1238 if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1239 {
1240 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1241 value = fromVar.sink;
1242 if ( value.userType() == qMetaTypeId<QgsProperty>() && context )
1243 {
1244 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1245 }
1246 }
1247 QgsMapLayer *layer = nullptr;
1248 if ( context )
1249 {
1250 layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1251 if ( !layer )
1252 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), *context );
1253 }
1254
1255 variables.insert( safeName( name ), VariableDefinition( layer ? QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ) : QVariant(), source, description ) );
1256 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1257 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1258 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1259 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1260 }
1261
1262 sources = availableSourcesForChild( childId, QStringList()
1264 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1265 {
1266 QString name;
1267 QVariant value;
1268 QString description;
1269
1270 switch ( source.source() )
1271 {
1273 {
1274 name = source.parameterName();
1275 value = modelParameters.value( source.parameterName() );
1276 description = parameterDefinition( source.parameterName() )->description();
1277 break;
1278 }
1280 {
1281 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1282 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1283 source.outputChildId() : child.description(), source.outputName() );
1284 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1285 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1286 {
1287 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1288 child.description() );
1289 }
1290 break;
1291 }
1292
1297 continue;
1298
1299 }
1300
1301 QgsFeatureSource *featureSource = nullptr;
1302 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
1303 {
1304 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1305 value = fromVar.source;
1306 }
1307 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1308 {
1309 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1310 value = fromVar.sink;
1311 if ( context && value.userType() == qMetaTypeId<QgsProperty>() )
1312 {
1313 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1314 }
1315 }
1316 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1317 {
1318 featureSource = layer;
1319 }
1320 if ( context && !featureSource )
1321 {
1322 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), *context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1323 featureSource = vl;
1324 }
1325
1326 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1327 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1328 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1329 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1330 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1331 }
1332
1333 return variables;
1334}
1335
1336QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1337{
1338 auto scope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "algorithm_inputs" ) );
1339 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, &context, modelParameters, results );
1340 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1341 for ( ; varIt != variables.constEnd(); ++varIt )
1342 {
1343 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1344 }
1345 return scope.release();
1346}
1347
1348QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QgsProcessingParameterDefinition *param ) const
1349{
1351 if ( !paramType )
1352 return QgsProcessingModelChildParameterSources();
1353 return availableSourcesForChild( childId, paramType->acceptedParameterTypes(), paramType->acceptedOutputTypes(), paramType->acceptedDataTypes( param ) );
1354}
1355
1356QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1357{
1358 QgsProcessingModelChildParameterSources sources;
1359
1360 // first look through model parameters
1361 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1362 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1363 {
1364 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1365 if ( !def )
1366 continue;
1367
1368 if ( parameterTypes.contains( def->type() ) )
1369 {
1370 if ( !dataTypes.isEmpty() )
1371 {
1373 {
1374 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1375 if ( !( dataTypes.contains( static_cast< int >( fieldDef->dataType() ) ) || fieldDef->dataType() == Qgis::ProcessingFieldParameterDataType::Any ) )
1376 {
1377 continue;
1378 }
1379 }
1381 {
1382 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1383 if ( !sourceDef )
1384 continue;
1385
1386 bool ok = sourceDef->dataTypes().isEmpty();
1387 const auto constDataTypes = sourceDef->dataTypes();
1388 for ( int type : constDataTypes )
1389 {
1390 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 ) )
1391 {
1392 ok = true;
1393 break;
1394 }
1395 }
1396 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 ) ) )
1397 ok = true;
1398
1399 if ( !ok )
1400 continue;
1401 }
1402 }
1403 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1404 }
1405 }
1406
1407 QSet< QString > dependents;
1408 if ( !childId.isEmpty() )
1409 {
1410 dependents = dependentChildAlgorithms( childId );
1411 dependents << childId;
1412 }
1413
1414 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1415 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1416 {
1417 if ( dependents.contains( childIt->childId() ) )
1418 continue;
1419
1420 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1421 if ( !alg )
1422 continue;
1423
1424 const auto constOutputDefinitions = alg->outputDefinitions();
1425 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1426 {
1427 if ( outputTypes.contains( out->type() ) )
1428 {
1429 if ( !dataTypes.isEmpty() )
1430 {
1431 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1432 {
1433 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1434
1435 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1436 {
1437 //unacceptable output
1438 continue;
1439 }
1440 }
1441 }
1442 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1443 }
1444 }
1445 }
1446
1447 return sources;
1448}
1449
1450QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1451{
1452 return mHelpContent;
1453}
1454
1455void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1456{
1457 mHelpContent = helpContent;
1458}
1459
1460void QgsProcessingModelAlgorithm::setName( const QString &name )
1461{
1462 mModelName = name;
1463}
1464
1465void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1466{
1467 mModelGroup = group;
1468}
1469
1470bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1471{
1472 issues.clear();
1473 bool res = true;
1474
1475 if ( mChildAlgorithms.empty() )
1476 {
1477 res = false;
1478 issues << QObject::tr( "Model does not contain any algorithms" );
1479 }
1480
1481 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1482 {
1483 QStringList childIssues;
1484 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1485
1486 for ( const QString &issue : std::as_const( childIssues ) )
1487 {
1488 issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1489 }
1490 }
1491 return res;
1492}
1493
1494QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1495{
1496 return mChildAlgorithms;
1497}
1498
1499void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1500{
1501 mParameterComponents = parameterComponents;
1502}
1503
1504void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1505{
1506 mParameterComponents.insert( component.parameterName(), component );
1507}
1508
1509QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1510{
1511 if ( !mParameterComponents.contains( name ) )
1512 {
1513 QgsProcessingModelParameter &component = mParameterComponents[ name ];
1514 component.setParameterName( name );
1515 return component;
1516 }
1517 return mParameterComponents[ name ];
1518}
1519
1520QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1521{
1522 QList< QgsProcessingModelParameter > res;
1523 QSet< QString > found;
1524 for ( const QString &parameter : mParameterOrder )
1525 {
1526 if ( mParameterComponents.contains( parameter ) )
1527 {
1528 res << mParameterComponents.value( parameter );
1529 found << parameter;
1530 }
1531 }
1532
1533 // add any missing ones to end of list
1534 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1535 {
1536 if ( !found.contains( it.key() ) )
1537 {
1538 res << it.value();
1539 }
1540 }
1541 return res;
1542}
1543
1544void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1545{
1546 mParameterOrder = order;
1547}
1548
1549QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
1550{
1551 QList< QgsProcessingModelOutput > res;
1552 QSet< QString > found;
1553
1554 for ( const QString &output : mOutputOrder )
1555 {
1556 bool foundOutput = false;
1557 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1558 {
1559 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1560 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1561 {
1562 if ( output == QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) )
1563 {
1564 res << outputIt.value();
1565 foundOutput = true;
1566 found.insert( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) );
1567 }
1568 }
1569 if ( foundOutput )
1570 break;
1571 }
1572 }
1573
1574 // add any missing ones to end of list
1575 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1576 {
1577 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1578 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1579 {
1580 if ( !found.contains( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) ) )
1581 {
1582 res << outputIt.value();
1583 }
1584 }
1585 }
1586
1587 return res;
1588}
1589
1590void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
1591{
1592 mOutputOrder = order;
1593}
1594
1595QString QgsProcessingModelAlgorithm::outputGroup() const
1596{
1597 return mOutputGroup;
1598}
1599
1600void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
1601{
1602 mOutputGroup = group;
1603}
1604
1605void QgsProcessingModelAlgorithm::updateDestinationParameters()
1606{
1607 //delete existing destination parameters
1608 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1609 while ( it.hasNext() )
1610 {
1611 const QgsProcessingParameterDefinition *def = it.next();
1612 if ( def->isDestination() )
1613 {
1614 delete def;
1615 it.remove();
1616 }
1617 }
1618 // also delete outputs
1619 qDeleteAll( mOutputs );
1620 mOutputs.clear();
1621
1622 // rebuild
1623 QSet< QString > usedFriendlyNames;
1624 auto uniqueSafeName = [&usedFriendlyNames ]( const QString & name )->QString
1625 {
1626 const QString base = safeName( name, false );
1627 QString candidate = base;
1628 int i = 1;
1629 while ( usedFriendlyNames.contains( candidate ) )
1630 {
1631 i++;
1632 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
1633 }
1634 usedFriendlyNames.insert( candidate );
1635 return candidate;
1636 };
1637
1638 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1639 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1640 {
1641 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1642 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1643 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1644 {
1645 if ( !childIt->isActive() || !childIt->algorithm() )
1646 continue;
1647
1648 // child algorithm has a destination parameter set, copy it to the model
1649 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1650 if ( !source )
1651 continue;
1652
1653 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1654 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1655 // outputs.
1656 param->setFlags( param->flags() & ~ static_cast< int >( Qgis::ProcessingParameterFlag::Hidden ) );
1657 if ( outputIt->isMandatory() )
1658 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Optional ) );
1659 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1660 {
1661 QString friendlyName = uniqueSafeName( outputIt->description() );
1662 param->setName( friendlyName );
1663 }
1664 else
1665 {
1666 param->setName( outputIt->childId() + ':' + outputIt->name() );
1667 }
1668 // add some metadata so we can easily link this parameter back to the child source
1669 param->metadata().insert( QStringLiteral( "_modelChildId" ), outputIt->childId() );
1670 param->metadata().insert( QStringLiteral( "_modelChildOutputName" ), outputIt->name() );
1671 param->metadata().insert( QStringLiteral( "_modelChildProvider" ), childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1672
1673 param->setDescription( outputIt->description() );
1674 param->setDefaultValue( outputIt->defaultValue() );
1675
1676 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1677 if ( addParameter( param.release() ) && newDestParam )
1678 {
1679 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1680 {
1681 // we need to copy the constraints given by the provider which creates this output across
1682 // and replace those which have been set to match the model provider's constraints
1683 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1684 newDestParam->mOriginalProvider = provider;
1685 }
1686 }
1687 }
1688 }
1689}
1690
1691void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1692{
1693 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1694}
1695
1696QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1697{
1698 return mGroupBoxes.values();
1699}
1700
1701void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1702{
1703 mGroupBoxes.remove( uuid );
1704}
1705
1706QVariant QgsProcessingModelAlgorithm::toVariant() const
1707{
1708 QVariantMap map;
1709 map.insert( QStringLiteral( "model_name" ), mModelName );
1710 map.insert( QStringLiteral( "model_group" ), mModelGroup );
1711 map.insert( QStringLiteral( "help" ), mHelpContent );
1712 map.insert( QStringLiteral( "internal_version" ), qgsEnumValueToKey( mInternalVersion ) );
1713
1714 QVariantMap childMap;
1715 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1716 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1717 {
1718 childMap.insert( childIt.key(), childIt.value().toVariant() );
1719 }
1720 map.insert( QStringLiteral( "children" ), childMap );
1721
1722 QVariantMap paramMap;
1723 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1724 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1725 {
1726 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1727 }
1728 map.insert( QStringLiteral( "parameters" ), paramMap );
1729
1730 QVariantMap paramDefMap;
1731 for ( const QgsProcessingParameterDefinition *def : mParameters )
1732 {
1733 paramDefMap.insert( def->name(), def->toVariantMap() );
1734 }
1735 map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1736
1737 QVariantList groupBoxDefs;
1738 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1739 {
1740 groupBoxDefs.append( it.value().toVariant() );
1741 }
1742 map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1743
1744 map.insert( QStringLiteral( "modelVariables" ), mVariables );
1745
1746 map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1747
1748 map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1749 map.insert( QStringLiteral( "outputOrder" ), mOutputOrder );
1750 map.insert( QStringLiteral( "outputGroup" ), mOutputGroup );
1751
1752 return map;
1753}
1754
1755bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1756{
1757 QVariantMap map = model.toMap();
1758
1759 mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1760 mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1761 mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1762 mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1763
1764 mInternalVersion = qgsEnumKeyToValue( map.value( QStringLiteral( "internal_version" ) ).toString(), InternalVersion::Version1 );
1765
1766 mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1767 mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1768
1769 mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1770 mOutputOrder = map.value( QStringLiteral( "outputOrder" ) ).toStringList();
1771 mOutputGroup = map.value( QStringLiteral( "outputGroup" ) ).toString();
1772
1773 mChildAlgorithms.clear();
1774 QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1775 QVariantMap::const_iterator childIt = childMap.constBegin();
1776 for ( ; childIt != childMap.constEnd(); ++childIt )
1777 {
1778 QgsProcessingModelChildAlgorithm child;
1779 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1780 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1781 // with no way for users to repair them
1782 if ( !child.loadVariant( childIt.value() ) )
1783 continue;
1784
1785 mChildAlgorithms.insert( child.childId(), child );
1786 }
1787
1788 mParameterComponents.clear();
1789 QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1790 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1791 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1792 {
1793 QgsProcessingModelParameter param;
1794 if ( !param.loadVariant( paramIt.value().toMap() ) )
1795 return false;
1796
1797 mParameterComponents.insert( param.parameterName(), param );
1798 }
1799
1800 qDeleteAll( mParameters );
1801 mParameters.clear();
1802 QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1803
1804 auto addParam = [this]( const QVariant & value )
1805 {
1806 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1807 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1808 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1809 // with no way for users to repair them
1810 if ( param )
1811 {
1812 if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1813 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1814
1815 // set parameter help from help content
1816 param->setHelp( mHelpContent.value( param->name() ).toString() );
1817
1818 // add parameter
1819 addParameter( param.release() );
1820 }
1821 else
1822 {
1823 QVariantMap map = value.toMap();
1824 QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1825 QString name = map.value( QStringLiteral( "name" ) ).toString();
1826
1827 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1828 }
1829 };
1830
1831 QSet< QString > loadedParams;
1832 // first add parameters respecting mParameterOrder
1833 for ( const QString &name : std::as_const( mParameterOrder ) )
1834 {
1835 if ( paramDefMap.contains( name ) )
1836 {
1837 addParam( paramDefMap.value( name ) );
1838 loadedParams << name;
1839 }
1840 }
1841 // then load any remaining parameters
1842 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1843 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1844 {
1845 if ( !loadedParams.contains( paramDefIt.key() ) )
1846 addParam( paramDefIt.value() );
1847 }
1848
1849 mGroupBoxes.clear();
1850 const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1851 for ( const QVariant &groupBoxDef : groupBoxList )
1852 {
1853 QgsProcessingModelGroupBox groupBox;
1854 groupBox.loadVariant( groupBoxDef.toMap() );
1855 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1856 }
1857
1858 updateDestinationParameters();
1859
1860 return true;
1861}
1862
1863bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType )
1864{
1865 // This method is intended to be "permissive" rather than "restrictive".
1866 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1867 // if there's doubt then we default to returning true.
1868 return ( acceptableDataTypes.empty()
1869 || acceptableDataTypes.contains( static_cast< int >( outputType ) )
1871 || outputType == Qgis::ProcessingSourceType::Vector
1873 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1874 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1875 || ( acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && ( outputType == Qgis::ProcessingSourceType::VectorPoint ||
1878}
1879
1880void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1881{
1882 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1883 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1884 {
1885 if ( !childIt->algorithm() )
1886 childIt->reattach();
1887 }
1888}
1889
1890bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1891{
1892 QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1893 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1894 doc.appendChild( elem );
1895
1896 QFile file( path );
1897 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1898 {
1899 QTextStream stream( &file );
1900 doc.save( stream, 2 );
1901 file.close();
1902 return true;
1903 }
1904 return false;
1905}
1906
1907bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1908{
1909 QDomDocument doc;
1910
1911 QFile file( path );
1912 if ( file.open( QFile::ReadOnly ) )
1913 {
1914 if ( !doc.setContent( &file ) )
1915 return false;
1916
1917 file.close();
1918 }
1919 else
1920 {
1921 return false;
1922 }
1923
1924 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1925 return loadVariant( props );
1926}
1927
1928void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1929{
1930 mChildAlgorithms = childAlgorithms;
1931 updateDestinationParameters();
1932}
1933
1934void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1935{
1936 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1937 updateDestinationParameters();
1938}
1939
1940QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1941{
1942 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1943 algorithm.generateChildId( *this );
1944
1945 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1946 updateDestinationParameters();
1947 return algorithm.childId();
1948}
1949
1950QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1951{
1952 return mChildAlgorithms[ childId ];
1953}
1954
1955bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1956{
1957 if ( !dependentChildAlgorithms( id ).isEmpty() )
1958 return false;
1959
1960 mChildAlgorithms.remove( id );
1961 updateDestinationParameters();
1962 return true;
1963}
1964
1965void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1966{
1967 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1968 for ( const QString &child : constDependentChildAlgorithms )
1969 {
1970 childAlgorithm( child ).setActive( false );
1971 }
1972 childAlgorithm( id ).setActive( false );
1973 updateDestinationParameters();
1974}
1975
1976bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1977{
1978 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1979 for ( const QString &child : constDependsOnChildAlgorithms )
1980 {
1981 if ( !childAlgorithm( child ).isActive() )
1982 return false;
1983 }
1984 childAlgorithm( id ).setActive( true );
1985 updateDestinationParameters();
1986 return true;
1987}
1988
1989void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1990{
1991 if ( addParameter( definition ) )
1992 mParameterComponents.insert( definition->name(), component );
1993}
1994
1995void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
1996{
1997 removeParameter( definition->name() );
1998 addParameter( definition );
1999}
2000
2001void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
2002{
2003 removeParameter( name );
2004 mParameterComponents.remove( name );
2005}
2006
2007void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
2008{
2009 QgsProcessingContext context;
2010 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
2011
2012 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString & expressionString ) -> std::tuple< bool, QString >
2013 {
2014 QgsExpression expression( expressionString );
2015 expression.prepare( &expressionContext );
2016 QSet<QString> variables = expression.referencedVariables();
2017 if ( variables.contains( oldName ) )
2018 {
2019 QString newExpression = expressionString;
2020 newExpression.replace( QStringLiteral( "@%1" ).arg( oldName ), QStringLiteral( "@%2" ).arg( newName ) );
2021 return { true, newExpression };
2022 }
2023 return { false, QString() };
2024 };
2025
2026 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
2027 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
2028 {
2029 bool changed = false;
2030 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2031 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
2032 for ( ; paramIt != childParams.end(); ++paramIt )
2033 {
2034 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
2035 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
2036 {
2037 switch ( valueIt->source() )
2038 {
2040 {
2041 if ( valueIt->parameterName() == oldName )
2042 {
2043 valueIt->setParameterName( newName );
2044 changed = true;
2045 }
2046 break;
2047 }
2048
2050 {
2051 bool updatedExpression = false;
2052 QString newExpression;
2053 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
2054 if ( updatedExpression )
2055 {
2056 valueIt->setExpression( newExpression );
2057 changed = true;
2058 }
2059 break;
2060 }
2061
2063 {
2064 if ( valueIt->staticValue().userType() == qMetaTypeId<QgsProperty>() )
2065 {
2066 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
2067 if ( property.propertyType() == Qgis::PropertyType::Expression )
2068 {
2069 bool updatedExpression = false;
2070 QString newExpression;
2071 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
2072 if ( updatedExpression )
2073 {
2074 property.setExpressionString( newExpression );
2075 valueIt->setStaticValue( property );
2076 changed = true;
2077 }
2078 }
2079 }
2080 break;
2081 }
2082
2086 break;
2087 }
2088 }
2089 }
2090 if ( changed )
2091 childIt->setParameterSources( childParams );
2092 }
2093}
2094
2095bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
2096{
2097 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2098 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2099 {
2100 // check whether child requires this parameter
2101 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2102 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2103 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2104 {
2105 const auto constValue = paramIt.value();
2106 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2107 {
2109 && source.parameterName() == name )
2110 {
2111 return true;
2112 }
2113 }
2114 }
2115 }
2116 return false;
2117}
2118
2119bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
2120{
2121 const auto constMParameters = mParameters;
2122 for ( const QgsProcessingParameterDefinition *def : constMParameters )
2123 {
2124 if ( def->name() == name )
2125 continue;
2126
2127 if ( def->dependsOnOtherParameters().contains( name ) )
2128 return true;
2129 }
2130 return false;
2131}
2132
2133QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
2134{
2135 return mParameterComponents;
2136}
2137
2138void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
2139{
2140 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2141 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2142 {
2143 if ( depends.contains( childIt->childId() ) )
2144 continue;
2145
2146 // does alg have a direct dependency on this child?
2147 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
2148 bool hasDependency = false;
2149 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
2150 {
2151 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
2152 {
2153 hasDependency = true;
2154 break;
2155 }
2156 }
2157
2158 if ( hasDependency )
2159 {
2160 depends.insert( childIt->childId() );
2161 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2162 continue;
2163 }
2164
2165 // check whether child requires any outputs from the target alg
2166 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2167 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2168 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2169 {
2170 const auto constValue = paramIt.value();
2171 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2172 {
2174 && source.outputChildId() == childId )
2175 {
2176 depends.insert( childIt->childId() );
2177 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2178 break;
2179 }
2180 }
2181 }
2182 }
2183}
2184
2185QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
2186{
2187 QSet< QString > algs;
2188
2189 // temporarily insert the target child algorithm to avoid
2190 // unnecessarily recursion though it
2191 algs.insert( childId );
2192
2193 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
2194
2195 // remove temporary target alg
2196 algs.remove( childId );
2197
2198 return algs;
2199}
2200
2201
2202void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
2203{
2204 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
2205
2206 // add direct dependencies
2207 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
2208 for ( const QgsProcessingModelChildDependency &val : constDependencies )
2209 {
2210 if ( !depends.contains( val.childId ) )
2211 {
2212 depends.insert( val.childId );
2213 dependsOnChildAlgorithmsRecursive( val.childId, depends );
2214 }
2215 }
2216
2217 // check through parameter dependencies
2218 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
2219 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2220 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2221 {
2222 const auto constValue = paramIt.value();
2223 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2224 {
2225 switch ( source.source() )
2226 {
2228 if ( !depends.contains( source.outputChildId() ) )
2229 {
2230 depends.insert( source.outputChildId() );
2231 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
2232 }
2233 break;
2234
2236 {
2237 const QgsExpression exp( source.expression() );
2238 const QSet<QString> vars = exp.referencedVariables();
2239 if ( vars.empty() )
2240 break;
2241
2242 // find the source of referenced variables and check if it's another child algorithm
2243 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> availableVariables = variablesForChildAlgorithm( childId );
2244 for ( auto childVarIt = availableVariables.constBegin(); childVarIt != availableVariables.constEnd(); ++childVarIt )
2245 {
2246 // we're only looking here for variables coming from other child algorithm outputs
2247 if ( childVarIt->source.source() != Qgis::ProcessingModelChildParameterSource::ChildOutput )
2248 continue;
2249
2250 if ( !vars.contains( childVarIt.key() ) || depends.contains( childVarIt->source.outputChildId() ) )
2251 continue;
2252
2253 // this variable is required for the child's expression, so the corresponding algorithm must be run first
2254 depends.insert( childVarIt->source.outputChildId() );
2255 dependsOnChildAlgorithmsRecursive( childVarIt->source.outputChildId(), depends );
2256 }
2257 break;
2258 }
2259
2264 break;
2265 }
2266 }
2267 }
2268}
2269
2270QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2271{
2272 QSet< QString > algs;
2273
2274 // temporarily insert the target child algorithm to avoid
2275 // unnecessarily recursion though it
2276 algs.insert( childId );
2277
2278 dependsOnChildAlgorithmsRecursive( childId, algs );
2279
2280 // remove temporary target alg
2281 algs.remove( childId );
2282
2283 return algs;
2284}
2285
2286QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2287{
2288 QSet< QString > dependent;
2289 if ( !childId.isEmpty() )
2290 {
2291 dependent.unite( dependentChildAlgorithms( childId ) );
2292 dependent.insert( childId );
2293 }
2294
2295 QList<QgsProcessingModelChildDependency> res;
2296 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2297 {
2298 if ( !dependent.contains( it->childId() ) )
2299 {
2300 // check first if algorithm provides output branches
2301 bool hasBranches = false;
2302 if ( it->algorithm() )
2303 {
2304 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2305 for ( const QgsProcessingOutputDefinition *def : defs )
2306 {
2308 {
2309 hasBranches = true;
2310 QgsProcessingModelChildDependency alg;
2311 alg.childId = it->childId();
2312 alg.conditionalBranch = def->name();
2313 res << alg;
2314 }
2315 }
2316 }
2317
2318 if ( !hasBranches )
2319 {
2320 QgsProcessingModelChildDependency alg;
2321 alg.childId = it->childId();
2322 res << alg;
2323 }
2324 }
2325 }
2326 return res;
2327}
2328
2329bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2330{
2331 issues.clear();
2332 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2333 if ( childIt != mChildAlgorithms.constEnd() )
2334 {
2335 if ( !childIt->algorithm() )
2336 {
2337 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2338 return false;
2339 }
2340 bool res = true;
2341
2342 // loop through child algorithm parameters and check that they are all valid
2343 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2344 for ( const QgsProcessingParameterDefinition *def : defs )
2345 {
2346 if ( childIt->parameterSources().contains( def->name() ) )
2347 {
2348 // is the value acceptable?
2349 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2350 for ( const QgsProcessingModelChildParameterSource &source : sources )
2351 {
2352 switch ( source.source() )
2353 {
2355 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2356 {
2357 res = false;
2358 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2359 }
2360 break;
2361
2363 if ( !parameterComponents().contains( source.parameterName() ) )
2364 {
2365 res = false;
2366 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2367 }
2368 break;
2369
2371 if ( !childAlgorithms().contains( source.outputChildId() ) )
2372 {
2373 res = false;
2374 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2375 }
2376 break;
2377
2381 break;
2382 }
2383 }
2384 }
2385 else
2386 {
2387 // not specified. Is it optional?
2388
2389 // ignore destination parameters -- they shouldn't ever be mandatory
2390 if ( def->isDestination() )
2391 continue;
2392
2393 if ( !def->checkValueIsAcceptable( QVariant() ) )
2394 {
2395 res = false;
2396 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2397 }
2398 }
2399 }
2400
2401 return res;
2402 }
2403 else
2404 {
2405 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2406 return false;
2407 }
2408}
2409
2410bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2411{
2412 reattachAlgorithms();
2413 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2414 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2415 {
2416 if ( !childIt->algorithm() )
2417 {
2418 if ( errorMessage )
2419 {
2420 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2421 }
2422 return false;
2423 }
2424 }
2425 return true;
2426}
2427
2428QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2429{
2430 if ( mSourceFile.isEmpty() )
2431 return QString(); // temporary model - can't run as python command
2432
2433 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2434}
2435
2436QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2437{
2438 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2439 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2440 return res;
2441}
2442
2443QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2444{
2445 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2446 alg->loadVariant( toVariant() );
2447 alg->setProvider( provider() );
2448 alg->setSourceFilePath( sourceFilePath() );
2449 return alg;
2450}
2451
2452QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2453{
2454 QString n = name.toLower().trimmed();
2455 const thread_local QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
2456 n.replace( rx, QString() );
2457 const thread_local QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
2458 n.replace( rx2, QString() );
2459 if ( !capitalize )
2460 n = n.replace( ' ', '_' );
2462}
2463
2464QVariantMap QgsProcessingModelAlgorithm::variables() const
2465{
2466 return mVariables;
2467}
2468
2469void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2470{
2471 mVariables = variables;
2472}
2473
2474QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2475{
2476 return mDesignerParameterValues;
2477}
2478
ProcessingSourceType
Processing data source types.
Definition qgis.h:3434
@ 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:3511
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:6602
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6583
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:6483
QMap< QString, QString > QgsStringMap
Definition qgis.h:6906
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.