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