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