QGIS API Documentation 4.1.0-Master (ca2ac17535b)
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 childAlgorithmFeedback.resetFeatureSinkCounts();
440
441 if ( feedback && !skipGenericLogging )
442 {
443 feedback->pushDebugInfo( QObject::tr( "Prepare algorithm: %1" ).arg( childId ) );
444 }
445 if ( modelFeedback )
446 {
447 modelFeedback->reportPreparingChild( childId );
448 }
449
450 QgsExpressionContext expContext = baseContext;
451 expContext << QgsExpressionContextUtils::processingAlgorithmScope( child.algorithm(), parameters, context ) << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
452 context.setExpressionContext( expContext );
453
454 QString error;
455 QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error, &context );
456 if ( !error.isEmpty() )
457 {
458 if ( modelFeedback )
459 {
460 modelFeedback->reportChildPreparationFailure( childId, error );
461 }
462 throw QgsProcessingException( error );
463 }
464
465 if ( feedback && !skipGenericLogging )
466 feedback->setProgressText( QObject::tr( "Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
467
469
470 const QVariantMap thisChildParams = QgsProcessingUtils::removePointerValuesFromMap( childParams );
471 childInputs.insert( childId, thisChildParams );
472 childResult.setInputs( thisChildParams );
473
474 QStringList params;
475 for ( auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
476 {
477 params << u"%1: %2"_s.arg( childParamIt.key(), child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
478 }
479
480 if ( feedback && !skipGenericLogging )
481 {
482 feedback->pushInfo( QObject::tr( "Input Parameters:" ) );
483 feedback->pushCommandInfo( u"{ %1 }"_s.arg( params.join( ", "_L1 ) ) );
484 }
485
486 QElapsedTimer childTime;
487 childTime.start();
488
489 QVariantMap outerScopeChildInputs = childInputs;
490 QVariantMap outerScopePrevChildResults = childResults;
491 QSet< QString > outerScopeExecuted = executed;
492 QMap< QString, QgsProcessingModelChildAlgorithmResult > outerScopeContextChildResult = contextChildResults;
493 if ( dynamic_cast< QgsProcessingModelAlgorithm * >( childAlg.get() ) )
494 {
495 // don't allow results from outer models to "leak" into child models
496 childInputs.clear();
497 childResults.clear();
498 executed.clear();
499 contextChildResults.clear();
500 }
501
502 bool ok = false;
503
504 QThread *modelThread = QThread::currentThread();
505
506 auto prepareOnMainThread = [modelThread, &ok, &childAlg, &childParams, &context, &childAlgorithmFeedback] {
507 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->prepare() must be run on the main thread" );
508 ok = childAlg->prepare( childParams, context, &childAlgorithmFeedback );
509 context.pushToThread( modelThread );
510 };
511
512 // Make sure we only run prepare steps on the main thread!
513 if ( modelThread == qApp->thread() )
514 ok = childAlg->prepare( childParams, context, &childAlgorithmFeedback );
515 else
516 {
517 context.pushToThread( qApp->thread() );
518// silence false positive leak warning
519#ifndef __clang_analyzer__
520 QMetaObject::invokeMethod( qApp, prepareOnMainThread, Qt::BlockingQueuedConnection );
521#endif
522 }
523
524 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
525
526 if ( !ok )
527 {
528 const QString error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
529 if ( modelFeedback )
530 {
531 modelFeedback->reportChildPreparationFailure( childId, error );
532 }
533 throw QgsProcessingException( error );
534 }
535
536 if ( modelFeedback )
537 {
538 modelFeedback->reportChildStarted( childId, childParams );
539 }
540
541 QVariantMap results;
542
543 bool runResult = false;
544 try
545 {
546 QgsScopedConnection childProgressConnection;
547 QgsScopedConnection childSourceLoadedConnection;
548 QgsScopedConnection childSinkCountChangedConnection;
549 if ( modelFeedback )
550 {
551 // these are scoped connections -- we only want them to exist for the duration that we're actually running THIS
552 // particular child algorithm
553 childProgressConnection = QObject::connect( &childAlgorithmFeedback, &QgsFeedback::progressChanged, &childAlgorithmFeedback, [&modelFeedback, &childId]( double progress ) {
554 modelFeedback->reportChildProgress( childId, progress );
555 } );
556 childSinkCountChangedConnection
557 = QObject::connect( &childAlgorithmFeedback, &QgsProcessingFeedback::sinkFeatureCountChanged, &childAlgorithmFeedback, [&modelFeedback, &childId]( const QString &sinkId, long long featureCount ) {
558 modelFeedback->reportChildSinkFeatureCountChanged( childId, sinkId, featureCount );
559 } );
560 // note -- this is INTENTIONALLY connected to feedback, not childAlgorithmFeedback!
561 childSourceLoadedConnection = QObject::connect( feedback, &QgsProcessingFeedback::sourceLoaded, feedback, [&modelFeedback, &childId]( const QString &parameterName, long long featureCount ) {
562 modelFeedback->reportChildSourceLoaded( childId, parameterName, featureCount );
563 } );
564 }
565
566 if ( ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::NoThreading ) && ( QThread::currentThread() != qApp->thread() ) )
567 {
568 // child algorithm run step must be called on main thread
569 auto runOnMainThread = [modelThread, &context, &childAlgorithmFeedback, &results, &childAlg, &childParams] {
570 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->runPrepared() must be run on the main thread" );
571 results = childAlg->runPrepared( childParams, context, &childAlgorithmFeedback );
572 context.pushToThread( modelThread );
573 };
574
575 if ( feedback && !skipGenericLogging && modelThread != qApp->thread() )
576 feedback->pushWarning( QObject::tr( "Algorithm “%1” cannot be run in a background thread, switching to main thread for this step" ).arg( childAlg->displayName() ) );
577
578 context.pushToThread( qApp->thread() );
579// silence false positive leak warning
580#ifndef __clang_analyzer__
581 QMetaObject::invokeMethod( qApp, runOnMainThread, Qt::BlockingQueuedConnection );
582#endif
583 }
584 else
585 {
586 // safe to run on model thread
587 results = childAlg->runPrepared( childParams, context, &childAlgorithmFeedback );
588 }
589 runResult = true;
591 }
592 catch ( QgsProcessingException &e )
593 {
594 error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? e.what() : QObject::tr( "Error encountered while running %1: %2" ).arg( child.description(), e.what() );
596 }
597
598 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
599
600 QVariantMap ppRes;
601 auto postProcessOnMainThread = [modelThread, &ppRes, &childAlg, &context, &childAlgorithmFeedback, runResult] {
602 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->postProcess() must be run on the main thread" );
603 ppRes = childAlg->postProcess( context, &childAlgorithmFeedback, runResult );
604 context.pushToThread( modelThread );
605 };
606
607 // Make sure we only run postProcess steps on the main thread!
608 if ( modelThread == qApp->thread() )
609 ppRes = childAlg->postProcess( context, &childAlgorithmFeedback, runResult );
610 else
611 {
612 context.pushToThread( qApp->thread() );
613// silence false positive leak warning
614#ifndef __clang_analyzer__
615 QMetaObject::invokeMethod( qApp, postProcessOnMainThread, Qt::BlockingQueuedConnection );
616#endif
617 }
618
619 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
620
621 if ( !ppRes.isEmpty() )
622 results = ppRes;
623
624 if ( dynamic_cast< QgsProcessingModelAlgorithm * >( childAlg.get() ) )
625 {
626 childInputs = outerScopeChildInputs;
627 childResults = outerScopePrevChildResults;
628 executed = outerScopeExecuted;
629 contextChildResults = outerScopeContextChildResult;
630 }
631
632 childResults.insert( childId, results );
633 childResult.setOutputs( results );
634
635 if ( modelFeedback )
636 {
638 modelFeedback->reportChildExecutionFailure( childId, error );
640 {
641 modelFeedback->reportChildProgress( childId, 100 );
642 modelFeedback->reportChildExecutionSuccess( childId, results );
643 }
644 }
645
646 if ( runResult )
647 {
648 if ( feedback && !skipGenericLogging )
649 {
650 const QVariantMap displayOutputs = QgsProcessingUtils::removePointerValuesFromMap( results );
651 QStringList formattedOutputs;
652 for ( auto displayOutputIt = displayOutputs.constBegin(); displayOutputIt != displayOutputs.constEnd(); ++displayOutputIt )
653 {
654 formattedOutputs << u"%1: %2"_s.arg( displayOutputIt.key(), QgsProcessingUtils::variantToPythonLiteral( displayOutputIt.value() ) );
655 ;
656 }
657 feedback->pushInfo( QObject::tr( "Results:" ) );
658 feedback->pushCommandInfo( u"{ %1 }"_s.arg( formattedOutputs.join( ", "_L1 ) ) );
659 }
660
661 // look through child alg's outputs to determine whether any of these should be copied
662 // to the final model outputs
663 const QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
664 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
665 {
666 const int outputSortKey = mOutputOrder.indexOf( u"%1:%2"_s.arg( childId, outputIt->childOutputName() ) );
667 switch ( mInternalVersion )
668 {
669 case QgsProcessingModelAlgorithm::InternalVersion::Version1:
670 finalResults.insert( childId + ':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
671 break;
672 case QgsProcessingModelAlgorithm::InternalVersion::Version2:
673 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
674 {
675 finalResults.insert( modelParam->name(), results.value( outputIt->childOutputName() ) );
676 }
677 break;
678 }
679
680 const QString outputLayer = results.value( outputIt->childOutputName() ).toString();
681 if ( !outputLayer.isEmpty() && context.willLoadLayerOnCompletion( outputLayer ) )
682 {
683 QgsProcessingContext::LayerDetails &details = context.layerToLoadOnCompletionDetails( outputLayer );
684 details.groupName = mOutputGroup;
685 if ( outputSortKey > 0 )
686 details.layerSortKey = outputSortKey;
687 }
688 }
689
690 executed.insert( childId );
691
692 std::function< void( const QString &, const QString & )> pruneAlgorithmBranchRecursive;
693 pruneAlgorithmBranchRecursive = [&]( const QString &id, const QString &branch = QString() ) {
694 const QSet<QString> toPrune = dependentChildAlgorithms( id, branch );
695 for ( const QString &targetId : toPrune )
696 {
697 if ( executed.contains( targetId ) )
698 continue;
699
700 executed.insert( targetId );
701 if ( modelFeedback )
702 {
703 modelFeedback->reportChildPruned( targetId );
704 }
705 pruneAlgorithmBranchRecursive( targetId, branch );
706 }
707 };
708
709 // prune remaining algorithms if they are dependent on a branch from this child which didn't eventuate
710 const QgsProcessingOutputDefinitions outputDefs = childAlg->outputDefinitions();
711 for ( const QgsProcessingOutputDefinition *outputDef : outputDefs )
712 {
713 if ( outputDef->type() == QgsProcessingOutputConditionalBranch::typeName() && !results.value( outputDef->name() ).toBool() )
714 {
715 pruneAlgorithmBranchRecursive( childId, outputDef->name() );
716 }
717 }
718
720 {
721 // check if any dependent algorithms should be canceled based on the outputs of this algorithm run
722 // first find all direct dependencies of this algorithm by looking through all remaining child algorithms
723 for ( const QString &candidateId : std::as_const( toExecute ) )
724 {
725 if ( executed.contains( candidateId ) )
726 continue;
727
728 // a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
729 // algorithm's outputs
730 const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[candidateId];
731 const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
732 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
733 bool pruned = false;
734 for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
735 {
736 for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
737 {
738 if ( source.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
739 {
740 // ok, this one is dependent on the current alg. Did we get a value for it?
741 if ( !results.contains( source.outputName() ) )
742 {
743 // oh no, nothing returned for this parameter. Gotta trim the branch back!
744 pruned = true;
745 // skip the dependent alg..
746 executed.insert( candidateId );
747 if ( modelFeedback )
748 {
749 modelFeedback->reportChildPruned( candidateId );
750 }
751 //... and everything which depends on it
752 pruneAlgorithmBranchRecursive( candidateId, QString() );
753 break;
754 }
755 }
756 }
757 if ( pruned )
758 break;
759 }
760 }
761 }
762
763 childAlg.reset( nullptr );
764 countExecuted++;
765 childAlgorithmFeedback.setCurrentStep( countExecuted );
766 if ( feedback && !skipGenericLogging )
767 {
768 feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
769 }
770 }
771
772 // trim out just the portion of the overall log which relates to this child
773 const QString thisAlgorithmHtmlLog = feedback ? feedback->htmlLog().mid( previousHtmlLogLength ) : QString();
774 previousHtmlLogLength = feedback ? feedback->htmlLog().length() : 0;
775
776 if ( !runResult )
777 {
778 const QString formattedException = u"<span style=\"color:red\">%1</span><br/>"_s.arg( error.toHtmlEscaped() ).replace( '\n', "<br>"_L1 );
779 const QString formattedRunTime
780 = 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 );
781
782 childResult.setHtmlLog( thisAlgorithmHtmlLog + formattedException + formattedRunTime );
783 context.modelResult().childResults().insert( childId, childResult );
784
785 if ( modelFeedback )
786 {
787 modelFeedback->reportChildResult( childId, childResult );
788 }
789
790 throw QgsProcessingException( error );
791 }
792 else
793 {
794 childResult.setHtmlLog( thisAlgorithmHtmlLog );
795 context.modelResult().childResults().insert( childId, childResult );
796
797 if ( modelFeedback )
798 {
799 modelFeedback->reportChildResult( childId, childResult );
800 }
801 }
802 }
803
804 if ( feedback && feedback->isCanceled() )
805 break;
806 }
807 if ( feedback )
808 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 ) );
809
810 mResults = finalResults;
811 mResults.insert( u"CHILD_RESULTS"_s, childResults );
812 mResults.insert( u"CHILD_INPUTS"_s, childInputs );
813 return mResults;
814}
815
816QString QgsProcessingModelAlgorithm::sourceFilePath() const
817{
818 return mSourceFile;
819}
820
821void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
822{
823 mSourceFile = sourceFile;
824}
825
826bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath() const
827{
828 if ( mSourceFile.isEmpty() )
829 return false;
830
831 const QFileInfo fi( mSourceFile );
832 return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
833}
834
835QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const int indentSize ) const
836{
837 QStringList fileDocString;
838 fileDocString << u"\"\"\""_s;
839 fileDocString << u"Model exported as python."_s;
840 fileDocString << u"Name : %1"_s.arg( displayName() );
841 fileDocString << u"Group : %1"_s.arg( group() );
842 fileDocString << u"With QGIS : %1"_s.arg( Qgis::versionInt() );
843 fileDocString << u"\"\"\""_s;
844 fileDocString << QString();
845
846 QStringList lines;
847 QString indent = QString( ' ' ).repeated( indentSize );
848 QString currentIndent;
849
850 QMap< QString, QString> friendlyChildNames;
851 QMap< QString, QString> friendlyOutputNames;
852 auto uniqueSafeName = []( const QString &name, bool capitalize, const QMap< QString, QString > &friendlyNames ) -> QString {
853 const QString base = safeName( name, capitalize );
854 QString candidate = base;
855 int i = 1;
856 // iterate over friendlyNames value to find candidate
857 while ( std::find( friendlyNames.cbegin(), friendlyNames.cend(), candidate ) != friendlyNames.cend() )
858 {
859 i++;
860 candidate = u"%1_%2"_s.arg( base ).arg( i );
861 }
862 return candidate;
863 };
864
865 const QString algorithmClassName = safeName( name(), true );
866
867 QSet< QString > toExecute;
868 for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
869 {
870 if ( childIt->isActive() && childIt->algorithm() )
871 {
872 toExecute.insert( childIt->childId() );
873 friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
874 }
875 }
876 const int totalSteps = toExecute.count();
877
878 QStringList importLines; // not a set - we need regular ordering
879 switch ( outputType )
880 {
882 {
883 // add specific parameter type imports
884 const auto params = parameterDefinitions();
885 importLines.reserve( params.count() + 6 );
886 importLines << u"from typing import Any, Optional"_s;
887 importLines << QString();
888 importLines << u"from qgis.core import QgsProcessing"_s;
889 importLines << u"from qgis.core import QgsProcessingAlgorithm"_s;
890 importLines << u"from qgis.core import QgsProcessingContext"_s;
891 importLines << u"from qgis.core import QgsProcessingFeedback, QgsProcessingMultiStepFeedback"_s;
892
893 bool hasAdvancedParams = false;
894 for ( const QgsProcessingParameterDefinition *def : params )
895 {
896 if ( def->flags() & Qgis::ProcessingParameterFlag::Advanced )
897 hasAdvancedParams = true;
898
899 if ( const QgsProcessingParameterType *type = QgsApplication::processingRegistry()->parameterType( def->type() ) )
900 {
901 const QString importString = type->pythonImportString();
902 if ( !importString.isEmpty() && !importLines.contains( importString ) )
903 importLines << importString;
904 }
905 }
906
907 if ( hasAdvancedParams )
908 importLines << u"from qgis.core import QgsProcessingParameterDefinition"_s;
909
910 lines << u"from qgis import processing"_s;
911 lines << QString() << QString();
912
913 lines << u"class %1(QgsProcessingAlgorithm):"_s.arg( algorithmClassName );
914 lines << QString();
915
916 // initAlgorithm, parameter definitions
917 lines << indent + u"def initAlgorithm(self, config: Optional[dict[str, Any]] = None):"_s;
918 if ( params.empty() )
919 {
920 lines << indent + indent + u"pass"_s;
921 }
922 else
923 {
924 lines.reserve( lines.size() + params.size() );
925 for ( const QgsProcessingParameterDefinition *def : params )
926 {
927 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
928
929 if ( defClone->isDestination() )
930 {
931 const QString uniqueChildName = defClone->metadata().value( u"_modelChildId"_s ).toString() + ':' + defClone->metadata().value( u"_modelChildOutputName"_s ).toString();
932 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
933 friendlyOutputNames.insert( uniqueChildName, friendlyName );
934 defClone->setName( friendlyName );
935 }
936 else
937 {
938 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
939 {
940 const QStringList parts = mParameterComponents.value( defClone->name() ).comment()->description().split( u"\n"_s );
941 for ( const QString &part : parts )
942 {
943 lines << indent + indent + u"# %1"_s.arg( part );
944 }
945 }
946 }
947
948 if ( defClone->flags() & Qgis::ProcessingParameterFlag::Advanced )
949 {
950 lines << indent + indent + u"param = %1"_s.arg( defClone->asPythonString() );
951 lines << indent + indent + u"param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)"_s;
952 lines << indent + indent + u"self.addParameter(param)"_s;
953 }
954 else
955 {
956 lines << indent + indent + u"self.addParameter(%1)"_s.arg( defClone->asPythonString() );
957 }
958 }
959 }
960
961 lines << QString();
962 lines << indent + u"def processAlgorithm(self, parameters: dict[str, Any], context: QgsProcessingContext, model_feedback: QgsProcessingFeedback) -> dict[str, Any]:"_s;
963 currentIndent = indent + indent;
964
965 lines << currentIndent + u"# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the"_s;
966 lines << currentIndent + u"# overall progress through the model"_s;
967 lines << currentIndent + u"feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)"_s.arg( totalSteps );
968 break;
969 }
970#if 0
971 case Script:
972 {
973 QgsStringMap params;
974 QgsProcessingContext context;
975 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
976 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
977 {
978 QString name = paramIt.value().parameterName();
979 if ( parameterDefinition( name ) )
980 {
981 // TODO - generic value to string method
982 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
983 }
984 }
985
986 if ( !params.isEmpty() )
987 {
988 lines << u"parameters = {"_s;
989 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
990 {
991 lines << u" '%1':%2,"_s.arg( it.key(), it.value() );
992 }
993 lines << u"}"_s
994 << QString();
995 }
996
997 lines << u"context = QgsProcessingContext()"_s
998 << u"context.setProject(QgsProject.instance())"_s
999 << u"feedback = QgsProcessingFeedback()"_s
1000 << QString();
1001
1002 break;
1003 }
1004#endif
1005 }
1006
1007 lines << currentIndent + u"results = {}"_s;
1008 lines << currentIndent + u"outputs = {}"_s;
1009 lines << QString();
1010
1011 QSet< QString > executed;
1012 bool executedAlg = true;
1013 int currentStep = 0;
1014 while ( executedAlg && executed.count() < toExecute.count() )
1015 {
1016 executedAlg = false;
1017 const auto constToExecute = toExecute;
1018 for ( const QString &childId : constToExecute )
1019 {
1020 if ( executed.contains( childId ) )
1021 continue;
1022
1023 bool canExecute = true;
1024 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
1025 for ( const QString &dependency : constDependsOnChildAlgorithms )
1026 {
1027 if ( !executed.contains( dependency ) )
1028 {
1029 canExecute = false;
1030 break;
1031 }
1032 }
1033
1034 if ( !canExecute )
1035 continue;
1036
1037 executedAlg = true;
1038
1039 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[childId];
1040
1041 // fill in temporary outputs
1042 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
1043 QgsStringMap childParams;
1044 for ( const QgsProcessingParameterDefinition *def : childDefs )
1045 {
1046 if ( def->isDestination() )
1047 {
1048 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
1049
1050 // is destination linked to one of the final outputs from this model?
1051 bool isFinalOutput = false;
1052 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
1053 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1054 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1055 {
1056 if ( outputIt->childOutputName() == destParam->name() )
1057 {
1058 QString paramName = child.childId() + ':' + outputIt.key();
1059 paramName = friendlyOutputNames.value( paramName, paramName );
1060 childParams.insert( destParam->name(), u"parameters['%1']"_s.arg( paramName ) );
1061 isFinalOutput = true;
1062 break;
1063 }
1064 }
1065
1066 if ( !isFinalOutput )
1067 {
1068 // output is temporary
1069
1070 // check whether it's optional, and if so - is it required?
1071 bool required = true;
1073 {
1074 required = childOutputIsRequired( child.childId(), destParam->name() );
1075 }
1076
1077 // not optional, or required elsewhere in model
1078 if ( required )
1079 {
1080 childParams.insert( destParam->name(), u"QgsProcessing.TEMPORARY_OUTPUT"_s );
1081 }
1082 }
1083 }
1084 }
1085
1086 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
1087 currentStep++;
1088 if ( currentStep < totalSteps )
1089 {
1090 lines << QString();
1091 lines << currentIndent + u"feedback.setCurrentStep(%1)"_s.arg( currentStep );
1092 lines << currentIndent + u"if feedback.isCanceled():"_s;
1093 lines << currentIndent + indent + u"return {}"_s;
1094 lines << QString();
1095 }
1096 executed.insert( childId );
1097 }
1098 }
1099
1100 switch ( outputType )
1101 {
1103 lines << currentIndent + u"return results"_s;
1104 lines << QString();
1105
1106 // name, displayName
1107 lines << indent + u"def name(self) -> str:"_s;
1108 lines << indent + indent + u"return '%1'"_s.arg( mModelName );
1109 lines << QString();
1110 lines << indent + u"def displayName(self) -> str:"_s;
1111 lines << indent + indent + u"return '%1'"_s.arg( mModelName );
1112 lines << QString();
1113
1114 // group, groupId
1115 lines << indent + u"def group(self) -> str:"_s;
1116 lines << indent + indent + u"return '%1'"_s.arg( mModelGroup );
1117 lines << QString();
1118 lines << indent + u"def groupId(self) -> str:"_s;
1119 lines << indent + indent + u"return '%1'"_s.arg( mModelGroupId );
1120 lines << QString();
1121
1122 // help
1123 if ( !shortHelpString().isEmpty() )
1124 {
1125 lines << indent + u"def shortHelpString(self) -> str:"_s;
1126 lines << indent + indent + u"return \"\"\"%1\"\"\""_s.arg( shortHelpString() );
1127 lines << QString();
1128 }
1129 if ( !helpUrl().isEmpty() )
1130 {
1131 lines << indent + u"def helpUrl(self) -> str:"_s;
1132 lines << indent + indent + u"return '%1'"_s.arg( helpUrl() );
1133 lines << QString();
1134 }
1135
1136 // createInstance
1137 lines << indent + u"def createInstance(self):"_s;
1138 lines << indent + indent + u"return self.__class__()"_s;
1139
1140 // additional import lines
1141 static QMap< QString, QString > sAdditionalImports {
1142 { u"QgsCoordinateReferenceSystem"_s, u"from qgis.core import QgsCoordinateReferenceSystem"_s },
1143 { u"QgsExpression"_s, u"from qgis.core import QgsExpression"_s },
1144 { u"QgsRectangle"_s, u"from qgis.core import QgsRectangle"_s },
1145 { u"QgsReferencedRectangle"_s, u"from qgis.core import QgsReferencedRectangle"_s },
1146 { u"QgsPoint"_s, u"from qgis.core import QgsPoint"_s },
1147 { u"QgsReferencedPoint"_s, u"from qgis.core import QgsReferencedPoint"_s },
1148 { u"QgsProperty"_s, u"from qgis.core import QgsProperty"_s },
1149 { u"QgsRasterLayer"_s, u"from qgis.core import QgsRasterLayer"_s },
1150 { u"QgsMeshLayer"_s, u"from qgis.core import QgsMeshLayer"_s },
1151 { u"QgsVectorLayer"_s, u"from qgis.core import QgsVectorLayer"_s },
1152 { u"QgsMapLayer"_s, u"from qgis.core import QgsMapLayer"_s },
1153 { u"QgsProcessingFeatureSourceDefinition"_s, u"from qgis.core import QgsProcessingFeatureSourceDefinition"_s },
1154 { u"QgsPointXY"_s, u"from qgis.core import QgsPointXY"_s },
1155 { u"QgsReferencedPointXY"_s, u"from qgis.core import QgsReferencedPointXY"_s },
1156 { u"QgsGeometry"_s, u"from qgis.core import QgsGeometry"_s },
1157 { u"QgsProcessingOutputLayerDefinition"_s, u"from qgis.core import QgsProcessingOutputLayerDefinition"_s },
1158 { u"QColor"_s, u"from qgis.PyQt.QtGui import QColor"_s },
1159 { u"QDateTime"_s, u"from qgis.PyQt.QtCore import QDateTime"_s },
1160 { u"QDate"_s, u"from qgis.PyQt.QtCore import QDate"_s },
1161 { u"QTime"_s, u"from qgis.PyQt.QtCore import QTime"_s },
1162 };
1163
1164 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
1165 {
1166 if ( importLines.contains( it.value() ) )
1167 {
1168 // already got this import
1169 continue;
1170 }
1171
1172 bool found = false;
1173 for ( const QString &line : std::as_const( lines ) )
1174 {
1175 if ( line.contains( it.key() ) )
1176 {
1177 found = true;
1178 break;
1179 }
1180 }
1181 if ( found )
1182 {
1183 importLines << it.value();
1184 }
1185 }
1186
1187 lines = fileDocString + importLines + lines;
1188 break;
1189 }
1190
1191 lines << QString();
1192
1193 return lines;
1194}
1195
1196QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm(
1197 const QString &childId, QgsProcessingContext *context, const QVariantMap &modelParameters, const QVariantMap &results
1198) const
1199{
1200 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
1201
1202 auto safeName = []( const QString &name ) -> QString {
1203 QString s = name;
1204 const thread_local QRegularExpression safeNameRe( u"[\\s'\"\\(\\):\\.]"_s );
1205 return s.replace( safeNameRe, u"_"_s );
1206 };
1207
1208 // "static"/single value sources
1209 QgsProcessingModelChildParameterSources sources = availableSourcesForChild(
1210 childId,
1211 QStringList()
1241 );
1242
1243 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1244 {
1245 QString name;
1246 QVariant value;
1247 QString description;
1248 switch ( source.source() )
1249 {
1251 {
1252 name = source.parameterName();
1253 value = modelParameters.value( source.parameterName() );
1254 description = parameterDefinition( source.parameterName() )->description();
1255 break;
1256 }
1258 {
1259 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1260 name = u"%1_%2"_s.arg( child.description().isEmpty() ? source.outputChildId() : child.description(), source.outputName() );
1261 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1262 {
1263 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(), child.description() );
1264 }
1265 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1266 break;
1267 }
1268
1273 continue;
1274 }
1275 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1276 }
1277
1278 // layer sources
1279 sources = availableSourcesForChild(
1280 childId,
1283 );
1284
1285 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1286 {
1287 QString name;
1288 QVariant value;
1289 QString description;
1290
1291 switch ( source.source() )
1292 {
1294 {
1295 name = source.parameterName();
1296 value = modelParameters.value( source.parameterName() );
1297 description = parameterDefinition( source.parameterName() )->description();
1298 break;
1299 }
1301 {
1302 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1303 name = u"%1_%2"_s.arg( child.description().isEmpty() ? source.outputChildId() : child.description(), source.outputName() );
1304 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1305 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1306 {
1307 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(), child.description() );
1308 }
1309 break;
1310 }
1311
1316 continue;
1317 }
1318
1319 if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1320 {
1321 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1322 value = fromVar.sink;
1323 if ( value.userType() == qMetaTypeId<QgsProperty>() && context )
1324 {
1325 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1326 }
1327 }
1328 QgsMapLayer *layer = nullptr;
1329 if ( context )
1330 {
1331 layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1332 if ( !layer )
1333 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), *context );
1334 }
1335
1336 variables.insert( safeName( name ), VariableDefinition( layer ? QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ) : QVariant(), source, description ) );
1337 variables.insert( safeName( u"%1_minx"_s.arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1338 variables.insert( safeName( u"%1_miny"_s.arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1339 variables.insert( safeName( u"%1_maxx"_s.arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1340 variables.insert( safeName( u"%1_maxy"_s.arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1341 }
1342
1343 sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterFeatureSource::typeName() );
1344 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1345 {
1346 QString name;
1347 QVariant value;
1348 QString description;
1349
1350 switch ( source.source() )
1351 {
1353 {
1354 name = source.parameterName();
1355 value = modelParameters.value( source.parameterName() );
1356 description = parameterDefinition( source.parameterName() )->description();
1357 break;
1358 }
1360 {
1361 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1362 name = u"%1_%2"_s.arg( child.description().isEmpty() ? source.outputChildId() : child.description(), source.outputName() );
1363 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1364 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1365 {
1366 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(), child.description() );
1367 }
1368 break;
1369 }
1370
1375 continue;
1376 }
1377
1378 QgsFeatureSource *featureSource = nullptr;
1379 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
1380 {
1381 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1382 value = fromVar.source;
1383 }
1384 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1385 {
1386 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1387 value = fromVar.sink;
1388 if ( context && value.userType() == qMetaTypeId<QgsProperty>() )
1389 {
1390 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1391 }
1392 }
1393 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1394 {
1395 featureSource = layer;
1396 }
1397 if ( context && !featureSource )
1398 {
1399 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), *context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1400 featureSource = vl;
1401 }
1402
1403 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1404 variables.insert( safeName( u"%1_minx"_s.arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1405 variables.insert( safeName( u"%1_miny"_s.arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1406 variables.insert( safeName( u"%1_maxx"_s.arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1407 variables.insert( safeName( u"%1_maxy"_s.arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1408 }
1409
1410 return variables;
1411}
1412
1413QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm(
1414 const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results
1415) const
1416{
1417 auto scope = std::make_unique<QgsExpressionContextScope>( u"algorithm_inputs"_s );
1418 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, &context, modelParameters, results );
1419 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1420 for ( ; varIt != variables.constEnd(); ++varIt )
1421 {
1422 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1423 }
1424 return scope.release();
1425}
1426
1427QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QgsProcessingParameterDefinition *param ) const
1428{
1430 if ( !paramType )
1431 return QgsProcessingModelChildParameterSources();
1432 return availableSourcesForChild( childId, paramType->acceptedParameterTypes(), paramType->acceptedOutputTypes(), paramType->acceptedDataTypes( param ) );
1433}
1434
1435QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild(
1436 const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes
1437) const
1438{
1439 QgsProcessingModelChildParameterSources sources;
1440
1441 // first look through model parameters
1442 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1443 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1444 {
1445 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1446 if ( !def )
1447 continue;
1448
1449 if ( parameterTypes.contains( def->type() ) )
1450 {
1451 if ( !dataTypes.isEmpty() )
1452 {
1454 {
1455 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1456 if ( !( dataTypes.contains( static_cast< int >( fieldDef->dataType() ) ) || fieldDef->dataType() == Qgis::ProcessingFieldParameterDataType::Any ) )
1457 {
1458 continue;
1459 }
1460 }
1462 {
1463 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1464 if ( !sourceDef )
1465 continue;
1466
1467 bool ok = sourceDef->dataTypes().isEmpty();
1468 const auto constDataTypes = sourceDef->dataTypes();
1469 for ( int type : constDataTypes )
1470 {
1471 if ( dataTypes.contains( type )
1472 || type == static_cast< int >( Qgis::ProcessingSourceType::MapLayer )
1473 || type == static_cast< int >( Qgis::ProcessingSourceType::Vector )
1474 || type == static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) )
1475 {
1476 ok = true;
1477 break;
1478 }
1479 }
1480 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1481 || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1482 || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) )
1483 ok = true;
1484
1485 if ( !ok )
1486 continue;
1487 }
1488 }
1489 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1490 }
1491 }
1492
1493 QSet< QString > dependents;
1494 if ( !childId.isEmpty() )
1495 {
1496 dependents = dependentChildAlgorithms( childId );
1497 dependents << childId;
1498 }
1499
1500 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1501 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1502 {
1503 if ( dependents.contains( childIt->childId() ) )
1504 continue;
1505
1506 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1507 if ( !alg )
1508 continue;
1509
1510 const auto constOutputDefinitions = alg->outputDefinitions();
1511 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1512 {
1513 if ( outputTypes.contains( out->type() ) )
1514 {
1515 if ( !dataTypes.isEmpty() )
1516 {
1517 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1518 {
1519 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1520
1521 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1522 {
1523 //unacceptable output
1524 continue;
1525 }
1526 }
1527 }
1528 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1529 }
1530 }
1531 }
1532
1533 return sources;
1534}
1535
1536QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1537{
1538 return mHelpContent;
1539}
1540
1541void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1542{
1543 mHelpContent = helpContent;
1544}
1545
1546void QgsProcessingModelAlgorithm::setName( const QString &name )
1547{
1548 mModelName = name;
1549}
1550
1551void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1552{
1553 mModelGroup = group;
1554}
1555
1556bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1557{
1558 issues.clear();
1559 bool res = true;
1560
1561 if ( mChildAlgorithms.empty() )
1562 {
1563 res = false;
1564 issues << QObject::tr( "Model does not contain any algorithms" );
1565 }
1566
1567 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1568 {
1569 QStringList childIssues;
1570 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1571
1572 for ( const QString &issue : std::as_const( childIssues ) )
1573 {
1574 issues << u"<b>%1</b>: %2"_s.arg( it->description(), issue );
1575 }
1576 }
1577 return res;
1578}
1579
1580QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1581{
1582 return mChildAlgorithms;
1583}
1584
1585void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1586{
1587 mParameterComponents = parameterComponents;
1588}
1589
1590void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1591{
1592 mParameterComponents.insert( component.parameterName(), component );
1593}
1594
1595QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1596{
1597 if ( !mParameterComponents.contains( name ) )
1598 {
1599 QgsProcessingModelParameter &component = mParameterComponents[name];
1600 component.setParameterName( name );
1601 return component;
1602 }
1603 return mParameterComponents[name];
1604}
1605
1606QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1607{
1608 QList< QgsProcessingModelParameter > res;
1609 QSet< QString > found;
1610 for ( const QString &parameter : mParameterOrder )
1611 {
1612 if ( mParameterComponents.contains( parameter ) )
1613 {
1614 res << mParameterComponents.value( parameter );
1615 found << parameter;
1616 }
1617 }
1618
1619 // add any missing ones to end of list
1620 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1621 {
1622 if ( !found.contains( it.key() ) )
1623 {
1624 res << it.value();
1625 }
1626 }
1627 return res;
1628}
1629
1630void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1631{
1632 mParameterOrder = order;
1633}
1634
1635QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
1636{
1637 QList< QgsProcessingModelOutput > res;
1638 QSet< QString > found;
1639
1640 for ( const QString &output : mOutputOrder )
1641 {
1642 bool foundOutput = false;
1643 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1644 {
1645 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1646 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1647 {
1648 if ( output == u"%1:%2"_s.arg( outputIt->childId(), outputIt->childOutputName() ) )
1649 {
1650 res << outputIt.value();
1651 foundOutput = true;
1652 found.insert( u"%1:%2"_s.arg( outputIt->childId(), outputIt->childOutputName() ) );
1653 }
1654 }
1655 if ( foundOutput )
1656 break;
1657 }
1658 }
1659
1660 // add any missing ones to end of list
1661 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1662 {
1663 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1664 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1665 {
1666 if ( !found.contains( u"%1:%2"_s.arg( outputIt->childId(), outputIt->childOutputName() ) ) )
1667 {
1668 res << outputIt.value();
1669 }
1670 }
1671 }
1672
1673 return res;
1674}
1675
1676void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
1677{
1678 mOutputOrder = order;
1679}
1680
1681QString QgsProcessingModelAlgorithm::outputGroup() const
1682{
1683 return mOutputGroup;
1684}
1685
1686void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
1687{
1688 mOutputGroup = group;
1689}
1690
1691void QgsProcessingModelAlgorithm::updateDestinationParameters()
1692{
1693 //delete existing destination parameters
1694 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1695 while ( it.hasNext() )
1696 {
1697 const QgsProcessingParameterDefinition *def = it.next();
1698 if ( def->isDestination() )
1699 {
1700 delete def;
1701 it.remove();
1702 }
1703 }
1704 // also delete outputs
1705 qDeleteAll( mOutputs );
1706 mOutputs.clear();
1707
1708 // rebuild
1709 QSet< QString > usedFriendlyNames;
1710 auto uniqueSafeName = [&usedFriendlyNames]( const QString &name ) -> QString {
1711 const QString base = safeName( name, false );
1712 QString candidate = base;
1713 int i = 1;
1714 while ( usedFriendlyNames.contains( candidate ) )
1715 {
1716 i++;
1717 candidate = u"%1_%2"_s.arg( base ).arg( i );
1718 }
1719 usedFriendlyNames.insert( candidate );
1720 return candidate;
1721 };
1722
1723 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1724 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1725 {
1726 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1727 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1728 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1729 {
1730 if ( !childIt->isActive() || !childIt->algorithm() )
1731 continue;
1732
1733 // child algorithm has a destination parameter set, copy it to the model
1734 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1735 if ( !source )
1736 continue;
1737
1738 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1739 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1740 // outputs.
1741 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Hidden ) );
1742 if ( outputIt->isMandatory() )
1743 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Optional ) );
1744 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1745 {
1746 QString friendlyName = uniqueSafeName( outputIt->description() );
1747 param->setName( friendlyName );
1748 }
1749 else
1750 {
1751 param->setName( outputIt->childId() + ':' + outputIt->name() );
1752 }
1753 // add some metadata so we can easily link this parameter back to the child source
1754 param->metadata().insert( u"_modelChildId"_s, outputIt->childId() );
1755 param->metadata().insert( u"_modelChildOutputName"_s, outputIt->name() );
1756 param->metadata().insert( u"_modelChildProvider"_s, childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1757
1758 param->setDescription( outputIt->description() );
1759 param->setDefaultValue( outputIt->defaultValue() );
1760
1761 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1762 if ( addParameter( param.release() ) && newDestParam )
1763 {
1764 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1765 {
1766 // we need to copy the constraints given by the provider which creates this output across
1767 // and replace those which have been set to match the model provider's constraints
1768 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1769 newDestParam->mOriginalProvider = provider;
1770 }
1771 }
1772 }
1773 }
1774}
1775
1776void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1777{
1778 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1779}
1780
1781QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1782{
1783 return mGroupBoxes.values();
1784}
1785
1786void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1787{
1788 mGroupBoxes.remove( uuid );
1789}
1790
1791QVariant QgsProcessingModelAlgorithm::toVariant() const
1792{
1793 QVariantMap map;
1794 map.insert( u"model_name"_s, mModelName );
1795 map.insert( u"model_group"_s, mModelGroup );
1796 map.insert( u"help"_s, mHelpContent );
1797 map.insert( u"internal_version"_s, qgsEnumValueToKey( mInternalVersion ) );
1798
1799 QVariantMap childMap;
1800 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1801 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1802 {
1803 childMap.insert( childIt.key(), childIt.value().toVariant() );
1804 }
1805 map.insert( u"children"_s, childMap );
1806
1807 QVariantMap paramMap;
1808 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1809 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1810 {
1811 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1812 }
1813 map.insert( u"parameters"_s, paramMap );
1814
1815 QVariantMap paramDefMap;
1816 for ( const QgsProcessingParameterDefinition *def : mParameters )
1817 {
1818 paramDefMap.insert( def->name(), def->toVariantMap() );
1819 }
1820 map.insert( u"parameterDefinitions"_s, paramDefMap );
1821
1822 QVariantList groupBoxDefs;
1823 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1824 {
1825 groupBoxDefs.append( it.value().toVariant() );
1826 }
1827 map.insert( u"groupBoxes"_s, groupBoxDefs );
1828
1829 map.insert( u"modelVariables"_s, mVariables );
1830
1831 map.insert( u"designerParameterValues"_s, mDesignerParameterValues );
1832
1833 map.insert( u"parameterOrder"_s, mParameterOrder );
1834 map.insert( u"outputOrder"_s, mOutputOrder );
1835 map.insert( u"outputGroup"_s, mOutputGroup );
1836
1837 return map;
1838}
1839
1840bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1841{
1842 QVariantMap map = model.toMap();
1843
1844 mModelName = map.value( u"model_name"_s ).toString();
1845 mModelGroup = map.value( u"model_group"_s ).toString();
1846 mModelGroupId = map.value( u"model_group"_s ).toString();
1847 mHelpContent = map.value( u"help"_s ).toMap();
1848
1849 mInternalVersion = qgsEnumKeyToValue( map.value( u"internal_version"_s ).toString(), InternalVersion::Version1 );
1850
1851 mVariables = map.value( u"modelVariables"_s ).toMap();
1852 mDesignerParameterValues = map.value( u"designerParameterValues"_s ).toMap();
1853
1854 mParameterOrder = map.value( u"parameterOrder"_s ).toStringList();
1855 mOutputOrder = map.value( u"outputOrder"_s ).toStringList();
1856 mOutputGroup = map.value( u"outputGroup"_s ).toString();
1857
1858 mChildAlgorithms.clear();
1859 QVariantMap childMap = map.value( u"children"_s ).toMap();
1860 QVariantMap::const_iterator childIt = childMap.constBegin();
1861 for ( ; childIt != childMap.constEnd(); ++childIt )
1862 {
1863 QgsProcessingModelChildAlgorithm child;
1864 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1865 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1866 // with no way for users to repair them
1867 if ( !child.loadVariant( childIt.value() ) )
1868 continue;
1869
1870 mChildAlgorithms.insert( child.childId(), child );
1871 }
1872
1873 mParameterComponents.clear();
1874 QVariantMap paramMap = map.value( u"parameters"_s ).toMap();
1875 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1876 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1877 {
1878 QgsProcessingModelParameter param;
1879 if ( !param.loadVariant( paramIt.value().toMap() ) )
1880 return false;
1881
1882 mParameterComponents.insert( param.parameterName(), param );
1883 }
1884
1885 qDeleteAll( mParameters );
1886 mParameters.clear();
1887 QVariantMap paramDefMap = map.value( u"parameterDefinitions"_s ).toMap();
1888
1889 auto addParam = [this]( const QVariant &value ) {
1890 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1891 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1892 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1893 // with no way for users to repair them
1894 if ( param )
1895 {
1896 if ( param->name() == "VERBOSE_LOG"_L1 )
1897 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1898
1899 // set parameter help from help content
1900 param->setHelp( mHelpContent.value( param->name() ).toString() );
1901
1902 // add parameter
1903 addParameter( param.release() );
1904 }
1905 else
1906 {
1907 QVariantMap map = value.toMap();
1908 QString type = map.value( u"parameter_type"_s ).toString();
1909 QString name = map.value( u"name"_s ).toString();
1910
1911 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1912 }
1913 };
1914
1915 QSet< QString > loadedParams;
1916 // first add parameters respecting mParameterOrder
1917 for ( const QString &name : std::as_const( mParameterOrder ) )
1918 {
1919 if ( paramDefMap.contains( name ) )
1920 {
1921 addParam( paramDefMap.value( name ) );
1922 loadedParams << name;
1923 }
1924 }
1925 // then load any remaining parameters
1926 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1927 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1928 {
1929 if ( !loadedParams.contains( paramDefIt.key() ) )
1930 addParam( paramDefIt.value() );
1931 }
1932
1933 mGroupBoxes.clear();
1934 const QVariantList groupBoxList = map.value( u"groupBoxes"_s ).toList();
1935 for ( const QVariant &groupBoxDef : groupBoxList )
1936 {
1937 QgsProcessingModelGroupBox groupBox;
1938 groupBox.loadVariant( groupBoxDef.toMap() );
1939 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1940 }
1941
1942 updateDestinationParameters();
1943
1944 return true;
1945}
1946
1947bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType )
1948{
1949 // This method is intended to be "permissive" rather than "restrictive".
1950 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1951 // if there's doubt then we default to returning true.
1952 return (
1953 acceptableDataTypes.empty()
1954 || acceptableDataTypes.contains( static_cast< int >( outputType ) )
1956 || outputType == Qgis::ProcessingSourceType::Vector
1958 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1959 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1960 || ( acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && ( outputType == Qgis::ProcessingSourceType::VectorPoint || outputType == Qgis::ProcessingSourceType::VectorLine || outputType == Qgis::ProcessingSourceType::VectorPolygon ) )
1961 );
1962}
1963
1964void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1965{
1966 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1967 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1968 {
1969 if ( !childIt->algorithm() )
1970 childIt->reattach();
1971 }
1972}
1973
1974bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1975{
1976 QDomDocument doc = QDomDocument( u"model"_s );
1977 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1978 doc.appendChild( elem );
1979
1980 QFile file( path );
1981 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1982 {
1983 QTextStream stream( &file );
1984 doc.save( stream, 2 );
1985 file.close();
1986 return true;
1987 }
1988 return false;
1989}
1990
1991bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1992{
1993 QDomDocument doc;
1994
1995 QFile file( path );
1996 if ( file.open( QFile::ReadOnly ) )
1997 {
1998 if ( !doc.setContent( &file ) )
1999 return false;
2000
2001 file.close();
2002 }
2003 else
2004 {
2005 return false;
2006 }
2007
2008 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
2009 return loadVariant( props );
2010}
2011
2012void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
2013{
2014 mChildAlgorithms = childAlgorithms;
2015 updateDestinationParameters();
2016}
2017
2018void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
2019{
2020 mChildAlgorithms.insert( algorithm.childId(), algorithm );
2021 updateDestinationParameters();
2022}
2023
2024QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
2025{
2026 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
2027 algorithm.generateChildId( *this );
2028
2029 mChildAlgorithms.insert( algorithm.childId(), algorithm );
2030 updateDestinationParameters();
2031 return algorithm.childId();
2032}
2033
2034QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
2035{
2036 return mChildAlgorithms[childId];
2037}
2038
2039bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
2040{
2041 if ( !dependentChildAlgorithms( id ).isEmpty() )
2042 return false;
2043
2044 mChildAlgorithms.remove( id );
2045 updateDestinationParameters();
2046 return true;
2047}
2048
2049void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
2050{
2051 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
2052 for ( const QString &child : constDependentChildAlgorithms )
2053 {
2054 childAlgorithm( child ).setActive( false );
2055 }
2056 childAlgorithm( id ).setActive( false );
2057 updateDestinationParameters();
2058}
2059
2060bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
2061{
2062 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
2063 for ( const QString &child : constDependsOnChildAlgorithms )
2064 {
2065 if ( !childAlgorithm( child ).isActive() )
2066 return false;
2067 }
2068 childAlgorithm( id ).setActive( true );
2069 updateDestinationParameters();
2070 return true;
2071}
2072
2073void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
2074{
2075 if ( addParameter( definition ) )
2076 mParameterComponents.insert( definition->name(), component );
2077}
2078
2079void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
2080{
2081 removeParameter( definition->name() );
2082 addParameter( definition );
2083}
2084
2085void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
2086{
2087 removeParameter( name );
2088 mParameterComponents.remove( name );
2089}
2090
2091void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
2092{
2093 QgsProcessingContext context;
2094 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
2095
2096 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString &expressionString ) -> std::tuple< bool, QString > {
2097 QgsExpression expression( expressionString );
2098 expression.prepare( &expressionContext );
2099 QSet<QString> variables = expression.referencedVariables();
2100 if ( variables.contains( oldName ) )
2101 {
2102 QString newExpression = expressionString;
2103 newExpression.replace( u"@%1"_s.arg( oldName ), u"@%2"_s.arg( newName ) );
2104 return { true, newExpression };
2105 }
2106 return { false, QString() };
2107 };
2108
2109 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
2110 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
2111 {
2112 bool changed = false;
2113 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2114 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
2115 for ( ; paramIt != childParams.end(); ++paramIt )
2116 {
2117 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
2118 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
2119 {
2120 switch ( valueIt->source() )
2121 {
2123 {
2124 if ( valueIt->parameterName() == oldName )
2125 {
2126 valueIt->setParameterName( newName );
2127 changed = true;
2128 }
2129 break;
2130 }
2131
2133 {
2134 bool updatedExpression = false;
2135 QString newExpression;
2136 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
2137 if ( updatedExpression )
2138 {
2139 valueIt->setExpression( newExpression );
2140 changed = true;
2141 }
2142 break;
2143 }
2144
2146 {
2147 if ( valueIt->staticValue().userType() == qMetaTypeId<QgsProperty>() )
2148 {
2149 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
2150 if ( property.propertyType() == Qgis::PropertyType::Expression )
2151 {
2152 bool updatedExpression = false;
2153 QString newExpression;
2154 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
2155 if ( updatedExpression )
2156 {
2157 property.setExpressionString( newExpression );
2158 valueIt->setStaticValue( property );
2159 changed = true;
2160 }
2161 }
2162 }
2163 break;
2164 }
2165
2169 break;
2170 }
2171 }
2172 }
2173 if ( changed )
2174 childIt->setParameterSources( childParams );
2175 }
2176}
2177
2178bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
2179{
2180 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2181 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2182 {
2183 // check whether child requires this parameter
2184 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2185 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2186 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2187 {
2188 const auto constValue = paramIt.value();
2189 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2190 {
2191 if ( source.source() == Qgis::ProcessingModelChildParameterSource::ModelParameter && source.parameterName() == name )
2192 {
2193 return true;
2194 }
2195 }
2196 }
2197 }
2198 return false;
2199}
2200
2201bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
2202{
2203 const auto constMParameters = mParameters;
2204 for ( const QgsProcessingParameterDefinition *def : constMParameters )
2205 {
2206 if ( def->name() == name )
2207 continue;
2208
2209 if ( def->dependsOnOtherParameters().contains( name ) )
2210 return true;
2211 }
2212 return false;
2213}
2214
2215QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
2216{
2217 return mParameterComponents;
2218}
2219
2220void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
2221{
2222 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2223 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2224 {
2225 if ( depends.contains( childIt->childId() ) )
2226 continue;
2227
2228 // does alg have a direct dependency on this child?
2229 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
2230 bool hasDependency = false;
2231 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
2232 {
2233 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
2234 {
2235 hasDependency = true;
2236 break;
2237 }
2238 }
2239
2240 if ( hasDependency )
2241 {
2242 depends.insert( childIt->childId() );
2243 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2244 continue;
2245 }
2246
2247 // check whether child requires any outputs from the target alg
2248 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2249 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2250 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2251 {
2252 const auto constValue = paramIt.value();
2253 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2254 {
2255 if ( source.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
2256 {
2257 depends.insert( childIt->childId() );
2258 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2259 break;
2260 }
2261 }
2262 }
2263 }
2264}
2265
2266QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
2267{
2268 QSet< QString > algs;
2269
2270 // temporarily insert the target child algorithm to avoid
2271 // unnecessarily recursion though it
2272 algs.insert( childId );
2273
2274 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
2275
2276 // remove temporary target alg
2277 algs.remove( childId );
2278
2279 return algs;
2280}
2281
2282
2283void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
2284{
2285 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
2286
2287 // add direct dependencies
2288 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
2289 for ( const QgsProcessingModelChildDependency &val : constDependencies )
2290 {
2291 if ( !depends.contains( val.childId ) )
2292 {
2293 depends.insert( val.childId );
2294 dependsOnChildAlgorithmsRecursive( val.childId, depends );
2295 }
2296 }
2297
2298 // check through parameter dependencies
2299 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
2300 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2301 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2302 {
2303 const auto constValue = paramIt.value();
2304 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2305 {
2306 switch ( source.source() )
2307 {
2309 if ( !depends.contains( source.outputChildId() ) )
2310 {
2311 depends.insert( source.outputChildId() );
2312 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
2313 }
2314 break;
2315
2317 {
2318 const QgsExpression exp( source.expression() );
2319 const QSet<QString> vars = exp.referencedVariables();
2320 if ( vars.empty() )
2321 break;
2322
2323 // find the source of referenced variables and check if it's another child algorithm
2324 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> availableVariables = variablesForChildAlgorithm( childId );
2325 for ( auto childVarIt = availableVariables.constBegin(); childVarIt != availableVariables.constEnd(); ++childVarIt )
2326 {
2327 // we're only looking here for variables coming from other child algorithm outputs
2328 if ( childVarIt->source.source() != Qgis::ProcessingModelChildParameterSource::ChildOutput )
2329 continue;
2330
2331 if ( !vars.contains( childVarIt.key() ) || depends.contains( childVarIt->source.outputChildId() ) )
2332 continue;
2333
2334 // this variable is required for the child's expression, so the corresponding algorithm must be run first
2335 depends.insert( childVarIt->source.outputChildId() );
2336 dependsOnChildAlgorithmsRecursive( childVarIt->source.outputChildId(), depends );
2337 }
2338 break;
2339 }
2340
2345 break;
2346 }
2347 }
2348 }
2349}
2350
2351QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2352{
2353 QSet< QString > algs;
2354
2355 // temporarily insert the target child algorithm to avoid
2356 // unnecessarily recursion though it
2357 algs.insert( childId );
2358
2359 dependsOnChildAlgorithmsRecursive( childId, algs );
2360
2361 // remove temporary target alg
2362 algs.remove( childId );
2363
2364 return algs;
2365}
2366
2367QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2368{
2369 QSet< QString > dependent;
2370 if ( !childId.isEmpty() )
2371 {
2372 dependent.unite( dependentChildAlgorithms( childId ) );
2373 dependent.insert( childId );
2374 }
2375
2376 QList<QgsProcessingModelChildDependency> res;
2377 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2378 {
2379 if ( !dependent.contains( it->childId() ) )
2380 {
2381 // check first if algorithm provides output branches
2382 bool hasBranches = false;
2383 if ( it->algorithm() )
2384 {
2385 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2386 for ( const QgsProcessingOutputDefinition *def : defs )
2387 {
2389 {
2390 hasBranches = true;
2391 QgsProcessingModelChildDependency alg;
2392 alg.childId = it->childId();
2393 alg.conditionalBranch = def->name();
2394 res << alg;
2395 }
2396 }
2397 }
2398
2399 if ( !hasBranches )
2400 {
2401 QgsProcessingModelChildDependency alg;
2402 alg.childId = it->childId();
2403 res << alg;
2404 }
2405 }
2406 }
2407 return res;
2408}
2409
2410bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2411{
2412 issues.clear();
2413 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2414 if ( childIt != mChildAlgorithms.constEnd() )
2415 {
2416 if ( !childIt->algorithm() )
2417 {
2418 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2419 return false;
2420 }
2421 bool res = true;
2422
2423 // loop through child algorithm parameters and check that they are all valid
2424 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2425 for ( const QgsProcessingParameterDefinition *def : defs )
2426 {
2427 if ( childIt->parameterSources().contains( def->name() ) )
2428 {
2429 // is the value acceptable?
2430 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2431 for ( const QgsProcessingModelChildParameterSource &source : sources )
2432 {
2433 switch ( source.source() )
2434 {
2436 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2437 {
2438 res = false;
2439 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2440 }
2441 break;
2442
2444 if ( !parameterComponents().contains( source.parameterName() ) )
2445 {
2446 res = false;
2447 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2448 }
2449 break;
2450
2452 if ( !childAlgorithms().contains( source.outputChildId() ) )
2453 {
2454 res = false;
2455 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2456 }
2457 break;
2458
2462 break;
2463 }
2464 }
2465 }
2466 else
2467 {
2468 // not specified. Is it optional?
2469
2470 // ignore destination parameters -- they shouldn't ever be mandatory
2471 if ( def->isDestination() )
2472 continue;
2473
2474 if ( !def->checkValueIsAcceptable( QVariant() ) )
2475 {
2476 res = false;
2477 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2478 }
2479 }
2480 }
2481
2482 return res;
2483 }
2484 else
2485 {
2486 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2487 return false;
2488 }
2489}
2490
2491bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2492{
2493 reattachAlgorithms();
2494 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2495 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2496 {
2497 if ( !childIt->algorithm() )
2498 {
2499 if ( errorMessage )
2500 {
2501 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2502 }
2503 return false;
2504 }
2505 }
2506 return true;
2507}
2508
2509QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2510{
2511 if ( mSourceFile.isEmpty() )
2512 return QString(); // temporary model - can't run as python command
2513
2514 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2515}
2516
2517QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2518{
2519 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2520 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2521 return res;
2522}
2523
2524QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2525{
2526 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2527 alg->loadVariant( toVariant() );
2528 alg->setProvider( provider() );
2529 alg->setSourceFilePath( sourceFilePath() );
2530 return alg;
2531}
2532
2533QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2534{
2535 QString n = name.toLower().trimmed();
2536 const thread_local QRegularExpression rx( u"[^\\sa-z_A-Z0-9]"_s );
2537 n.replace( rx, QString() );
2538 const thread_local QRegularExpression rx2( u"^\\d*"_s ); // name can't start in a digit
2539 n.replace( rx2, QString() );
2540 if ( !capitalize )
2541 n = n.replace( ' ', '_' );
2543}
2544
2545QVariantMap QgsProcessingModelAlgorithm::variables() const
2546{
2547 return mVariables;
2548}
2549
2550void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2551{
2552 mVariables = variables;
2553}
2554
2555QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2556{
2557 return mDesignerParameterValues;
2558}
2559
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.
void sinkFeatureCountChanged(const QString &output, long long featureCount)
Emitted when the count of features pushed to a sink has changed.
virtual QString htmlLog() const
Returns the HTML formatted contents of the log, which contains all messages pushed to the feedback ob...
void sourceLoaded(const QString &parameterName, long long featureCount)
Emitted when a feature source was retrieved for the specified algorithm input parameter.
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 reportChildSinkFeatureCountChanged(const QString &childId, const QString &childOutput, long long featureCount)
Reports that the count of features pushed to a child algorithm's sink has changed.
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.
void reportChildSourceLoaded(const QString &childId, const QString &parameterName, long long featureCount)
Reports that a feature source was retrieved for the specified child algorithm input parameter.
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:7437
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7418
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:7323
QMap< QString, QString > QgsStringMap
Definition qgis.h:7751
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.