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