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