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