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