QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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 = [ =, &error ]( 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() + 3 );
775 importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
776 importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
777 importLines << QStringLiteral( "from qgis.core import QgsProcessingMultiStepFeedback" );
778
779 bool hasAdvancedParams = false;
780 for ( const QgsProcessingParameterDefinition *def : params )
781 {
782 if ( def->flags() & Qgis::ProcessingParameterFlag::Advanced )
783 hasAdvancedParams = true;
784
785 const QString importString = QgsApplication::processingRegistry()->parameterType( def->type() )->pythonImportString();
786 if ( !importString.isEmpty() && !importLines.contains( importString ) )
787 importLines << importString;
788 }
789
790 if ( hasAdvancedParams )
791 importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
792
793 lines << QStringLiteral( "import processing" );
794 lines << QString() << QString();
795
796 lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
797 lines << QString();
798
799 // initAlgorithm, parameter definitions
800 lines << indent + QStringLiteral( "def initAlgorithm(self, config=None):" );
801 if ( params.empty() )
802 {
803 lines << indent + indent + QStringLiteral( "pass" );
804 }
805 else
806 {
807 lines.reserve( lines.size() + params.size() );
808 for ( const QgsProcessingParameterDefinition *def : params )
809 {
810 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
811
812 if ( defClone->isDestination() )
813 {
814 const QString uniqueChildName = defClone->metadata().value( QStringLiteral( "_modelChildId" ) ).toString() + ':' + defClone->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
815 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
816 friendlyOutputNames.insert( uniqueChildName, friendlyName );
817 defClone->setName( friendlyName );
818 }
819 else
820 {
821 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
822 {
823 const QStringList parts = mParameterComponents.value( defClone->name() ).comment()->description().split( QStringLiteral( "\n" ) );
824 for ( const QString &part : parts )
825 {
826 lines << indent + indent + QStringLiteral( "# %1" ).arg( part );
827 }
828 }
829 }
830
831 if ( defClone->flags() & Qgis::ProcessingParameterFlag::Advanced )
832 {
833 lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
834 lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
835 lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
836 }
837 else
838 {
839 lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
840 }
841 }
842 }
843
844 lines << QString();
845 lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, model_feedback):" );
846 currentIndent = indent + indent;
847
848 lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
849 lines << currentIndent + QStringLiteral( "# overall progress through the model" );
850 lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
851 break;
852 }
853#if 0
854 case Script:
855 {
856 QgsStringMap params;
857 QgsProcessingContext context;
858 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
859 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
860 {
861 QString name = paramIt.value().parameterName();
862 if ( parameterDefinition( name ) )
863 {
864 // TODO - generic value to string method
865 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
866 }
867 }
868
869 if ( !params.isEmpty() )
870 {
871 lines << QStringLiteral( "parameters = {" );
872 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
873 {
874 lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
875 }
876 lines << QStringLiteral( "}" )
877 << QString();
878 }
879
880 lines << QStringLiteral( "context = QgsProcessingContext()" )
881 << QStringLiteral( "context.setProject(QgsProject.instance())" )
882 << QStringLiteral( "feedback = QgsProcessingFeedback()" )
883 << QString();
884
885 break;
886 }
887#endif
888
889 }
890
891 lines << currentIndent + QStringLiteral( "results = {}" );
892 lines << currentIndent + QStringLiteral( "outputs = {}" );
893 lines << QString();
894
895 QSet< QString > executed;
896 bool executedAlg = true;
897 int currentStep = 0;
898 while ( executedAlg && executed.count() < toExecute.count() )
899 {
900 executedAlg = false;
901 const auto constToExecute = toExecute;
902 for ( const QString &childId : constToExecute )
903 {
904 if ( executed.contains( childId ) )
905 continue;
906
907 bool canExecute = true;
908 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
909 for ( const QString &dependency : constDependsOnChildAlgorithms )
910 {
911 if ( !executed.contains( dependency ) )
912 {
913 canExecute = false;
914 break;
915 }
916 }
917
918 if ( !canExecute )
919 continue;
920
921 executedAlg = true;
922
923 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
924
925 // fill in temporary outputs
926 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
927 QgsStringMap childParams;
928 for ( const QgsProcessingParameterDefinition *def : childDefs )
929 {
930 if ( def->isDestination() )
931 {
932 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
933
934 // is destination linked to one of the final outputs from this model?
935 bool isFinalOutput = false;
936 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
937 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
938 for ( ; outputIt != outputs.constEnd(); ++outputIt )
939 {
940 if ( outputIt->childOutputName() == destParam->name() )
941 {
942 QString paramName = child.childId() + ':' + outputIt.key();
943 paramName = friendlyOutputNames.value( paramName, paramName );
944 childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
945 isFinalOutput = true;
946 break;
947 }
948 }
949
950 if ( !isFinalOutput )
951 {
952 // output is temporary
953
954 // check whether it's optional, and if so - is it required?
955 bool required = true;
957 {
958 required = childOutputIsRequired( child.childId(), destParam->name() );
959 }
960
961 // not optional, or required elsewhere in model
962 if ( required )
963 {
964 childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
965 }
966 }
967 }
968 }
969
970 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
971 currentStep++;
972 if ( currentStep < totalSteps )
973 {
974 lines << QString();
975 lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
976 lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
977 lines << currentIndent + indent + QStringLiteral( "return {}" );
978 lines << QString();
979 }
980 executed.insert( childId );
981 }
982 }
983
984 switch ( outputType )
985 {
987 lines << currentIndent + QStringLiteral( "return results" );
988 lines << QString();
989
990 // name, displayName
991 lines << indent + QStringLiteral( "def name(self):" );
992 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
993 lines << QString();
994 lines << indent + QStringLiteral( "def displayName(self):" );
995 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
996 lines << QString();
997
998 // group, groupId
999 lines << indent + QStringLiteral( "def group(self):" );
1000 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
1001 lines << QString();
1002 lines << indent + QStringLiteral( "def groupId(self):" );
1003 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
1004 lines << QString();
1005
1006 // help
1007 if ( !shortHelpString().isEmpty() )
1008 {
1009 lines << indent + QStringLiteral( "def shortHelpString(self):" );
1010 lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
1011 lines << QString();
1012 }
1013 if ( !helpUrl().isEmpty() )
1014 {
1015 lines << indent + QStringLiteral( "def helpUrl(self):" );
1016 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
1017 lines << QString();
1018 }
1019
1020 // createInstance
1021 lines << indent + QStringLiteral( "def createInstance(self):" );
1022 lines << indent + indent + QStringLiteral( "return %1()" ).arg( algorithmClassName );
1023
1024 // additional import lines
1025 static QMap< QString, QString > sAdditionalImports
1026 {
1027 { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
1028 { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
1029 { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
1030 { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
1031 { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
1032 { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
1033 { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
1034 { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
1035 { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
1036 { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
1037 { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
1038 { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
1039 { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
1040 { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
1041 { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
1042 { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
1043 { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
1044 { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
1045 { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
1046 { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
1047 };
1048
1049 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
1050 {
1051 if ( importLines.contains( it.value() ) )
1052 {
1053 // already got this import
1054 continue;
1055 }
1056
1057 bool found = false;
1058 for ( const QString &line : std::as_const( lines ) )
1059 {
1060 if ( line.contains( it.key() ) )
1061 {
1062 found = true;
1063 break;
1064 }
1065 }
1066 if ( found )
1067 {
1068 importLines << it.value();
1069 }
1070 }
1071
1072 lines = fileDocString + importLines + lines;
1073 break;
1074 }
1075
1076 lines << QString();
1077
1078 return lines;
1079}
1080
1081QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext *context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1082{
1083 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
1084
1085 auto safeName = []( const QString & name )->QString
1086 {
1087 QString s = name;
1088 const thread_local QRegularExpression safeNameRe( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) );
1089 return s.replace( safeNameRe, QStringLiteral( "_" ) );
1090 };
1091
1092 // "static"/single value sources
1093 QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
1121 QStringList() << QgsProcessingOutputNumber::typeName()
1125
1126 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1127 {
1128 QString name;
1129 QVariant value;
1130 QString description;
1131 switch ( source.source() )
1132 {
1134 {
1135 name = source.parameterName();
1136 value = modelParameters.value( source.parameterName() );
1137 description = parameterDefinition( source.parameterName() )->description();
1138 break;
1139 }
1141 {
1142 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1143 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1144 source.outputChildId() : child.description(), source.outputName() );
1145 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1146 {
1147 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1148 child.description() );
1149 }
1150 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1151 break;
1152 }
1153
1158 continue;
1159 }
1160 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1161 }
1162
1163 // layer sources
1164 sources = availableSourcesForChild( childId, QStringList()
1170
1171 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1172 {
1173 QString name;
1174 QVariant value;
1175 QString description;
1176
1177 switch ( source.source() )
1178 {
1180 {
1181 name = source.parameterName();
1182 value = modelParameters.value( source.parameterName() );
1183 description = parameterDefinition( source.parameterName() )->description();
1184 break;
1185 }
1187 {
1188 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1189 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1190 source.outputChildId() : child.description(), source.outputName() );
1191 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1192 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1193 {
1194 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1195 child.description() );
1196 }
1197 break;
1198 }
1199
1204 continue;
1205
1206 }
1207
1208 if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1209 {
1210 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1211 value = fromVar.sink;
1212 if ( value.userType() == qMetaTypeId<QgsProperty>() && context )
1213 {
1214 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1215 }
1216 }
1217 QgsMapLayer *layer = nullptr;
1218 if ( context )
1219 {
1220 layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1221 if ( !layer )
1222 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), *context );
1223 }
1224
1225 variables.insert( safeName( name ), VariableDefinition( layer ? QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ) : QVariant(), source, description ) );
1226 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1227 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1228 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1229 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1230 }
1231
1232 sources = availableSourcesForChild( childId, QStringList()
1234 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1235 {
1236 QString name;
1237 QVariant value;
1238 QString description;
1239
1240 switch ( source.source() )
1241 {
1243 {
1244 name = source.parameterName();
1245 value = modelParameters.value( source.parameterName() );
1246 description = parameterDefinition( source.parameterName() )->description();
1247 break;
1248 }
1250 {
1251 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1252 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1253 source.outputChildId() : child.description(), source.outputName() );
1254 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1255 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1256 {
1257 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1258 child.description() );
1259 }
1260 break;
1261 }
1262
1267 continue;
1268
1269 }
1270
1271 QgsFeatureSource *featureSource = nullptr;
1272 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
1273 {
1274 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1275 value = fromVar.source;
1276 }
1277 else if ( value.userType() == qMetaTypeId<QgsProcessingOutputLayerDefinition>() )
1278 {
1279 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1280 value = fromVar.sink;
1281 if ( context && value.userType() == qMetaTypeId<QgsProperty>() )
1282 {
1283 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1284 }
1285 }
1286 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1287 {
1288 featureSource = layer;
1289 }
1290 if ( context && !featureSource )
1291 {
1292 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), *context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1293 featureSource = vl;
1294 }
1295
1296 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1297 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1298 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1299 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1300 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1301 }
1302
1303 return variables;
1304}
1305
1306QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1307{
1308 std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QStringLiteral( "algorithm_inputs" ) ) );
1309 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, &context, modelParameters, results );
1310 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1311 for ( ; varIt != variables.constEnd(); ++varIt )
1312 {
1313 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1314 }
1315 return scope.release();
1316}
1317
1318QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1319{
1320 QgsProcessingModelChildParameterSources sources;
1321
1322 // first look through model parameters
1323 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1324 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1325 {
1326 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1327 if ( !def )
1328 continue;
1329
1330 if ( parameterTypes.contains( def->type() ) )
1331 {
1332 if ( !dataTypes.isEmpty() )
1333 {
1335 {
1336 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1337 if ( !( dataTypes.contains( static_cast< int >( fieldDef->dataType() ) ) || fieldDef->dataType() == Qgis::ProcessingFieldParameterDataType::Any ) )
1338 {
1339 continue;
1340 }
1341 }
1343 {
1344 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1345 if ( !sourceDef )
1346 continue;
1347
1348 bool ok = sourceDef->dataTypes().isEmpty();
1349 const auto constDataTypes = sourceDef->dataTypes();
1350 for ( int type : constDataTypes )
1351 {
1352 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 ) )
1353 {
1354 ok = true;
1355 break;
1356 }
1357 }
1358 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 ) ) )
1359 ok = true;
1360
1361 if ( !ok )
1362 continue;
1363 }
1364 }
1365 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1366 }
1367 }
1368
1369 QSet< QString > dependents;
1370 if ( !childId.isEmpty() )
1371 {
1372 dependents = dependentChildAlgorithms( childId );
1373 dependents << childId;
1374 }
1375
1376 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1377 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1378 {
1379 if ( dependents.contains( childIt->childId() ) )
1380 continue;
1381
1382 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1383 if ( !alg )
1384 continue;
1385
1386 const auto constOutputDefinitions = alg->outputDefinitions();
1387 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1388 {
1389 if ( outputTypes.contains( out->type() ) )
1390 {
1391 if ( !dataTypes.isEmpty() )
1392 {
1393 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1394 {
1395 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1396
1397 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1398 {
1399 //unacceptable output
1400 continue;
1401 }
1402 }
1403 }
1404 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1405 }
1406 }
1407 }
1408
1409 return sources;
1410}
1411
1412QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1413{
1414 return mHelpContent;
1415}
1416
1417void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1418{
1419 mHelpContent = helpContent;
1420}
1421
1422void QgsProcessingModelAlgorithm::setName( const QString &name )
1423{
1424 mModelName = name;
1425}
1426
1427void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1428{
1429 mModelGroup = group;
1430}
1431
1432bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1433{
1434 issues.clear();
1435 bool res = true;
1436
1437 if ( mChildAlgorithms.empty() )
1438 {
1439 res = false;
1440 issues << QObject::tr( "Model does not contain any algorithms" );
1441 }
1442
1443 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1444 {
1445 QStringList childIssues;
1446 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1447
1448 for ( const QString &issue : std::as_const( childIssues ) )
1449 {
1450 issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1451 }
1452 }
1453 return res;
1454}
1455
1456QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1457{
1458 return mChildAlgorithms;
1459}
1460
1461void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1462{
1463 mParameterComponents = parameterComponents;
1464}
1465
1466void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1467{
1468 mParameterComponents.insert( component.parameterName(), component );
1469}
1470
1471QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1472{
1473 if ( !mParameterComponents.contains( name ) )
1474 {
1475 QgsProcessingModelParameter &component = mParameterComponents[ name ];
1476 component.setParameterName( name );
1477 return component;
1478 }
1479 return mParameterComponents[ name ];
1480}
1481
1482QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1483{
1484 QList< QgsProcessingModelParameter > res;
1485 QSet< QString > found;
1486 for ( const QString &parameter : mParameterOrder )
1487 {
1488 if ( mParameterComponents.contains( parameter ) )
1489 {
1490 res << mParameterComponents.value( parameter );
1491 found << parameter;
1492 }
1493 }
1494
1495 // add any missing ones to end of list
1496 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1497 {
1498 if ( !found.contains( it.key() ) )
1499 {
1500 res << it.value();
1501 }
1502 }
1503 return res;
1504}
1505
1506void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1507{
1508 mParameterOrder = order;
1509}
1510
1511QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
1512{
1513 QList< QgsProcessingModelOutput > res;
1514 QSet< QString > found;
1515
1516 for ( const QString &output : mOutputOrder )
1517 {
1518 bool foundOutput = false;
1519 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1520 {
1521 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1522 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1523 {
1524 if ( output == QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) )
1525 {
1526 res << outputIt.value();
1527 foundOutput = true;
1528 found.insert( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) );
1529 }
1530 }
1531 if ( foundOutput )
1532 break;
1533 }
1534 }
1535
1536 // add any missing ones to end of list
1537 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1538 {
1539 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1540 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1541 {
1542 if ( !found.contains( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) ) )
1543 {
1544 res << outputIt.value();
1545 }
1546 }
1547 }
1548
1549 return res;
1550}
1551
1552void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
1553{
1554 mOutputOrder = order;
1555}
1556
1557QString QgsProcessingModelAlgorithm::outputGroup() const
1558{
1559 return mOutputGroup;
1560}
1561
1562void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
1563{
1564 mOutputGroup = group;
1565}
1566
1567void QgsProcessingModelAlgorithm::updateDestinationParameters()
1568{
1569 //delete existing destination parameters
1570 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1571 while ( it.hasNext() )
1572 {
1573 const QgsProcessingParameterDefinition *def = it.next();
1574 if ( def->isDestination() )
1575 {
1576 delete def;
1577 it.remove();
1578 }
1579 }
1580 // also delete outputs
1581 qDeleteAll( mOutputs );
1582 mOutputs.clear();
1583
1584 // rebuild
1585 QSet< QString > usedFriendlyNames;
1586 auto uniqueSafeName = [&usedFriendlyNames ]( const QString & name )->QString
1587 {
1588 const QString base = safeName( name, false );
1589 QString candidate = base;
1590 int i = 1;
1591 while ( usedFriendlyNames.contains( candidate ) )
1592 {
1593 i++;
1594 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
1595 }
1596 usedFriendlyNames.insert( candidate );
1597 return candidate;
1598 };
1599
1600 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1601 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1602 {
1603 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1604 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1605 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1606 {
1607 if ( !childIt->isActive() || !childIt->algorithm() )
1608 continue;
1609
1610 // child algorithm has a destination parameter set, copy it to the model
1611 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1612 if ( !source )
1613 continue;
1614
1615 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1616 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1617 // outputs.
1618 param->setFlags( param->flags() & ~ static_cast< int >( Qgis::ProcessingParameterFlag::Hidden ) );
1619 if ( outputIt->isMandatory() )
1620 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Optional ) );
1621 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1622 {
1623 QString friendlyName = uniqueSafeName( outputIt->description() );
1624 param->setName( friendlyName );
1625 }
1626 else
1627 {
1628 param->setName( outputIt->childId() + ':' + outputIt->name() );
1629 }
1630 // add some metadata so we can easily link this parameter back to the child source
1631 param->metadata().insert( QStringLiteral( "_modelChildId" ), outputIt->childId() );
1632 param->metadata().insert( QStringLiteral( "_modelChildOutputName" ), outputIt->name() );
1633 param->metadata().insert( QStringLiteral( "_modelChildProvider" ), childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1634
1635 param->setDescription( outputIt->description() );
1636 param->setDefaultValue( outputIt->defaultValue() );
1637
1638 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1639 if ( addParameter( param.release() ) && newDestParam )
1640 {
1641 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1642 {
1643 // we need to copy the constraints given by the provider which creates this output across
1644 // and replace those which have been set to match the model provider's constraints
1645 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1646 newDestParam->mOriginalProvider = provider;
1647 }
1648 }
1649 }
1650 }
1651}
1652
1653void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1654{
1655 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1656}
1657
1658QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1659{
1660 return mGroupBoxes.values();
1661}
1662
1663void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1664{
1665 mGroupBoxes.remove( uuid );
1666}
1667
1668QVariant QgsProcessingModelAlgorithm::toVariant() const
1669{
1670 QVariantMap map;
1671 map.insert( QStringLiteral( "model_name" ), mModelName );
1672 map.insert( QStringLiteral( "model_group" ), mModelGroup );
1673 map.insert( QStringLiteral( "help" ), mHelpContent );
1674 map.insert( QStringLiteral( "internal_version" ), qgsEnumValueToKey( mInternalVersion ) );
1675
1676 QVariantMap childMap;
1677 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1678 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1679 {
1680 childMap.insert( childIt.key(), childIt.value().toVariant() );
1681 }
1682 map.insert( QStringLiteral( "children" ), childMap );
1683
1684 QVariantMap paramMap;
1685 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1686 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1687 {
1688 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1689 }
1690 map.insert( QStringLiteral( "parameters" ), paramMap );
1691
1692 QVariantMap paramDefMap;
1693 for ( const QgsProcessingParameterDefinition *def : mParameters )
1694 {
1695 paramDefMap.insert( def->name(), def->toVariantMap() );
1696 }
1697 map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1698
1699 QVariantList groupBoxDefs;
1700 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1701 {
1702 groupBoxDefs.append( it.value().toVariant() );
1703 }
1704 map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1705
1706 map.insert( QStringLiteral( "modelVariables" ), mVariables );
1707
1708 map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1709
1710 map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1711 map.insert( QStringLiteral( "outputOrder" ), mOutputOrder );
1712 map.insert( QStringLiteral( "outputGroup" ), mOutputGroup );
1713
1714 return map;
1715}
1716
1717bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1718{
1719 QVariantMap map = model.toMap();
1720
1721 mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1722 mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1723 mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1724 mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1725
1726 mInternalVersion = qgsEnumKeyToValue( map.value( QStringLiteral( "internal_version" ) ).toString(), InternalVersion::Version1 );
1727
1728 mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1729 mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1730
1731 mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1732 mOutputOrder = map.value( QStringLiteral( "outputOrder" ) ).toStringList();
1733 mOutputGroup = map.value( QStringLiteral( "outputGroup" ) ).toString();
1734
1735 mChildAlgorithms.clear();
1736 QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1737 QVariantMap::const_iterator childIt = childMap.constBegin();
1738 for ( ; childIt != childMap.constEnd(); ++childIt )
1739 {
1740 QgsProcessingModelChildAlgorithm child;
1741 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1742 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1743 // with no way for users to repair them
1744 if ( !child.loadVariant( childIt.value() ) )
1745 continue;
1746
1747 mChildAlgorithms.insert( child.childId(), child );
1748 }
1749
1750 mParameterComponents.clear();
1751 QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1752 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1753 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1754 {
1755 QgsProcessingModelParameter param;
1756 if ( !param.loadVariant( paramIt.value().toMap() ) )
1757 return false;
1758
1759 mParameterComponents.insert( param.parameterName(), param );
1760 }
1761
1762 qDeleteAll( mParameters );
1763 mParameters.clear();
1764 QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1765
1766 auto addParam = [this]( const QVariant & value )
1767 {
1768 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1769 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1770 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1771 // with no way for users to repair them
1772 if ( param )
1773 {
1774 if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1775 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1776
1777 // set parameter help from help content
1778 param->setHelp( mHelpContent.value( param->name() ).toString() );
1779
1780 // add parameter
1781 addParameter( param.release() );
1782 }
1783 else
1784 {
1785 QVariantMap map = value.toMap();
1786 QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1787 QString name = map.value( QStringLiteral( "name" ) ).toString();
1788
1789 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1790 }
1791 };
1792
1793 QSet< QString > loadedParams;
1794 // first add parameters respecting mParameterOrder
1795 for ( const QString &name : std::as_const( mParameterOrder ) )
1796 {
1797 if ( paramDefMap.contains( name ) )
1798 {
1799 addParam( paramDefMap.value( name ) );
1800 loadedParams << name;
1801 }
1802 }
1803 // then load any remaining parameters
1804 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1805 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1806 {
1807 if ( !loadedParams.contains( paramDefIt.key() ) )
1808 addParam( paramDefIt.value() );
1809 }
1810
1811 mGroupBoxes.clear();
1812 const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1813 for ( const QVariant &groupBoxDef : groupBoxList )
1814 {
1815 QgsProcessingModelGroupBox groupBox;
1816 groupBox.loadVariant( groupBoxDef.toMap() );
1817 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1818 }
1819
1820 updateDestinationParameters();
1821
1822 return true;
1823}
1824
1825bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType )
1826{
1827 // This method is intended to be "permissive" rather than "restrictive".
1828 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1829 // if there's doubt then we default to returning true.
1830 return ( acceptableDataTypes.empty()
1831 || acceptableDataTypes.contains( static_cast< int >( outputType ) )
1833 || outputType == Qgis::ProcessingSourceType::Vector
1835 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1836 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1837 || ( acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && ( outputType == Qgis::ProcessingSourceType::VectorPoint ||
1840}
1841
1842void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1843{
1844 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1845 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1846 {
1847 if ( !childIt->algorithm() )
1848 childIt->reattach();
1849 }
1850}
1851
1852bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1853{
1854 QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1855 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1856 doc.appendChild( elem );
1857
1858 QFile file( path );
1859 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1860 {
1861 QTextStream stream( &file );
1862 doc.save( stream, 2 );
1863 file.close();
1864 return true;
1865 }
1866 return false;
1867}
1868
1869bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1870{
1871 QDomDocument doc;
1872
1873 QFile file( path );
1874 if ( file.open( QFile::ReadOnly ) )
1875 {
1876 if ( !doc.setContent( &file ) )
1877 return false;
1878
1879 file.close();
1880 }
1881 else
1882 {
1883 return false;
1884 }
1885
1886 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1887 return loadVariant( props );
1888}
1889
1890void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1891{
1892 mChildAlgorithms = childAlgorithms;
1893 updateDestinationParameters();
1894}
1895
1896void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1897{
1898 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1899 updateDestinationParameters();
1900}
1901
1902QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1903{
1904 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1905 algorithm.generateChildId( *this );
1906
1907 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1908 updateDestinationParameters();
1909 return algorithm.childId();
1910}
1911
1912QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1913{
1914 return mChildAlgorithms[ childId ];
1915}
1916
1917bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1918{
1919 if ( !dependentChildAlgorithms( id ).isEmpty() )
1920 return false;
1921
1922 mChildAlgorithms.remove( id );
1923 updateDestinationParameters();
1924 return true;
1925}
1926
1927void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1928{
1929 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1930 for ( const QString &child : constDependentChildAlgorithms )
1931 {
1932 childAlgorithm( child ).setActive( false );
1933 }
1934 childAlgorithm( id ).setActive( false );
1935 updateDestinationParameters();
1936}
1937
1938bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1939{
1940 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1941 for ( const QString &child : constDependsOnChildAlgorithms )
1942 {
1943 if ( !childAlgorithm( child ).isActive() )
1944 return false;
1945 }
1946 childAlgorithm( id ).setActive( true );
1947 updateDestinationParameters();
1948 return true;
1949}
1950
1951void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1952{
1953 if ( addParameter( definition ) )
1954 mParameterComponents.insert( definition->name(), component );
1955}
1956
1957void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
1958{
1959 removeParameter( definition->name() );
1960 addParameter( definition );
1961}
1962
1963void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
1964{
1965 removeParameter( name );
1966 mParameterComponents.remove( name );
1967}
1968
1969void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
1970{
1971 QgsProcessingContext context;
1972 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
1973
1974 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString & expressionString ) -> std::tuple< bool, QString >
1975 {
1976 QgsExpression expression( expressionString );
1977 expression.prepare( &expressionContext );
1978 QSet<QString> variables = expression.referencedVariables();
1979 if ( variables.contains( oldName ) )
1980 {
1981 QString newExpression = expressionString;
1982 newExpression.replace( QStringLiteral( "@%1" ).arg( oldName ), QStringLiteral( "@%2" ).arg( newName ) );
1983 return { true, newExpression };
1984 }
1985 return { false, QString() };
1986 };
1987
1988 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
1989 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
1990 {
1991 bool changed = false;
1992 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1993 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
1994 for ( ; paramIt != childParams.end(); ++paramIt )
1995 {
1996 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
1997 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
1998 {
1999 switch ( valueIt->source() )
2000 {
2002 {
2003 if ( valueIt->parameterName() == oldName )
2004 {
2005 valueIt->setParameterName( newName );
2006 changed = true;
2007 }
2008 break;
2009 }
2010
2012 {
2013 bool updatedExpression = false;
2014 QString newExpression;
2015 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
2016 if ( updatedExpression )
2017 {
2018 valueIt->setExpression( newExpression );
2019 changed = true;
2020 }
2021 break;
2022 }
2023
2025 {
2026 if ( valueIt->staticValue().userType() == qMetaTypeId<QgsProperty>() )
2027 {
2028 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
2029 if ( property.propertyType() == Qgis::PropertyType::Expression )
2030 {
2031 bool updatedExpression = false;
2032 QString newExpression;
2033 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
2034 if ( updatedExpression )
2035 {
2036 property.setExpressionString( newExpression );
2037 valueIt->setStaticValue( property );
2038 changed = true;
2039 }
2040 }
2041 }
2042 break;
2043 }
2044
2048 break;
2049 }
2050 }
2051 }
2052 if ( changed )
2053 childIt->setParameterSources( childParams );
2054 }
2055}
2056
2057bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
2058{
2059 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2060 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2061 {
2062 // check whether child requires this parameter
2063 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2064 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2065 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2066 {
2067 const auto constValue = paramIt.value();
2068 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2069 {
2071 && source.parameterName() == name )
2072 {
2073 return true;
2074 }
2075 }
2076 }
2077 }
2078 return false;
2079}
2080
2081bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
2082{
2083 const auto constMParameters = mParameters;
2084 for ( const QgsProcessingParameterDefinition *def : constMParameters )
2085 {
2086 if ( def->name() == name )
2087 continue;
2088
2089 if ( def->dependsOnOtherParameters().contains( name ) )
2090 return true;
2091 }
2092 return false;
2093}
2094
2095QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
2096{
2097 return mParameterComponents;
2098}
2099
2100void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
2101{
2102 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2103 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2104 {
2105 if ( depends.contains( childIt->childId() ) )
2106 continue;
2107
2108 // does alg have a direct dependency on this child?
2109 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
2110 bool hasDependency = false;
2111 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
2112 {
2113 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
2114 {
2115 hasDependency = true;
2116 break;
2117 }
2118 }
2119
2120 if ( hasDependency )
2121 {
2122 depends.insert( childIt->childId() );
2123 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2124 continue;
2125 }
2126
2127 // check whether child requires any outputs from the target alg
2128 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2129 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2130 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2131 {
2132 const auto constValue = paramIt.value();
2133 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2134 {
2136 && source.outputChildId() == childId )
2137 {
2138 depends.insert( childIt->childId() );
2139 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2140 break;
2141 }
2142 }
2143 }
2144 }
2145}
2146
2147QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
2148{
2149 QSet< QString > algs;
2150
2151 // temporarily insert the target child algorithm to avoid
2152 // unnecessarily recursion though it
2153 algs.insert( childId );
2154
2155 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
2156
2157 // remove temporary target alg
2158 algs.remove( childId );
2159
2160 return algs;
2161}
2162
2163
2164void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
2165{
2166 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
2167
2168 // add direct dependencies
2169 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
2170 for ( const QgsProcessingModelChildDependency &val : constDependencies )
2171 {
2172 if ( !depends.contains( val.childId ) )
2173 {
2174 depends.insert( val.childId );
2175 dependsOnChildAlgorithmsRecursive( val.childId, depends );
2176 }
2177 }
2178
2179 // check through parameter dependencies
2180 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
2181 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2182 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2183 {
2184 const auto constValue = paramIt.value();
2185 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2186 {
2187 switch ( source.source() )
2188 {
2190 if ( !depends.contains( source.outputChildId() ) )
2191 {
2192 depends.insert( source.outputChildId() );
2193 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
2194 }
2195 break;
2196
2198 {
2199 const QgsExpression exp( source.expression() );
2200 const QSet<QString> vars = exp.referencedVariables();
2201 if ( vars.empty() )
2202 break;
2203
2204 // find the source of referenced variables and check if it's another child algorithm
2205 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> availableVariables = variablesForChildAlgorithm( childId );
2206 for ( auto childVarIt = availableVariables.constBegin(); childVarIt != availableVariables.constEnd(); ++childVarIt )
2207 {
2208 // we're only looking here for variables coming from other child algorithm outputs
2209 if ( childVarIt->source.source() != Qgis::ProcessingModelChildParameterSource::ChildOutput )
2210 continue;
2211
2212 if ( !vars.contains( childVarIt.key() ) || depends.contains( childVarIt->source.outputChildId() ) )
2213 continue;
2214
2215 // this variable is required for the child's expression, so the corresponding algorithm must be run first
2216 depends.insert( childVarIt->source.outputChildId() );
2217 dependsOnChildAlgorithmsRecursive( childVarIt->source.outputChildId(), depends );
2218 }
2219 break;
2220 }
2221
2226 break;
2227 }
2228 }
2229 }
2230}
2231
2232QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2233{
2234 QSet< QString > algs;
2235
2236 // temporarily insert the target child algorithm to avoid
2237 // unnecessarily recursion though it
2238 algs.insert( childId );
2239
2240 dependsOnChildAlgorithmsRecursive( childId, algs );
2241
2242 // remove temporary target alg
2243 algs.remove( childId );
2244
2245 return algs;
2246}
2247
2248QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2249{
2250 QSet< QString > dependent;
2251 if ( !childId.isEmpty() )
2252 {
2253 dependent.unite( dependentChildAlgorithms( childId ) );
2254 dependent.insert( childId );
2255 }
2256
2257 QList<QgsProcessingModelChildDependency> res;
2258 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2259 {
2260 if ( !dependent.contains( it->childId() ) )
2261 {
2262 // check first if algorithm provides output branches
2263 bool hasBranches = false;
2264 if ( it->algorithm() )
2265 {
2266 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2267 for ( const QgsProcessingOutputDefinition *def : defs )
2268 {
2270 {
2271 hasBranches = true;
2272 QgsProcessingModelChildDependency alg;
2273 alg.childId = it->childId();
2274 alg.conditionalBranch = def->name();
2275 res << alg;
2276 }
2277 }
2278 }
2279
2280 if ( !hasBranches )
2281 {
2282 QgsProcessingModelChildDependency alg;
2283 alg.childId = it->childId();
2284 res << alg;
2285 }
2286 }
2287 }
2288 return res;
2289}
2290
2291bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2292{
2293 issues.clear();
2294 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2295 if ( childIt != mChildAlgorithms.constEnd() )
2296 {
2297 if ( !childIt->algorithm() )
2298 {
2299 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2300 return false;
2301 }
2302 bool res = true;
2303
2304 // loop through child algorithm parameters and check that they are all valid
2305 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2306 for ( const QgsProcessingParameterDefinition *def : defs )
2307 {
2308 if ( childIt->parameterSources().contains( def->name() ) )
2309 {
2310 // is the value acceptable?
2311 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2312 for ( const QgsProcessingModelChildParameterSource &source : sources )
2313 {
2314 switch ( source.source() )
2315 {
2317 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2318 {
2319 res = false;
2320 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2321 }
2322 break;
2323
2325 if ( !parameterComponents().contains( source.parameterName() ) )
2326 {
2327 res = false;
2328 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2329 }
2330 break;
2331
2333 if ( !childAlgorithms().contains( source.outputChildId() ) )
2334 {
2335 res = false;
2336 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2337 }
2338 break;
2339
2343 break;
2344 }
2345 }
2346 }
2347 else
2348 {
2349 // not specified. Is it optional?
2350
2351 // ignore destination parameters -- they shouldn't ever be mandatory
2352 if ( def->isDestination() )
2353 continue;
2354
2355 if ( !def->checkValueIsAcceptable( QVariant() ) )
2356 {
2357 res = false;
2358 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2359 }
2360 }
2361 }
2362
2363 return res;
2364 }
2365 else
2366 {
2367 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2368 return false;
2369 }
2370}
2371
2372bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2373{
2374 reattachAlgorithms();
2375 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2376 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2377 {
2378 if ( !childIt->algorithm() )
2379 {
2380 if ( errorMessage )
2381 {
2382 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2383 }
2384 return false;
2385 }
2386 }
2387 return true;
2388}
2389
2390QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2391{
2392 if ( mSourceFile.isEmpty() )
2393 return QString(); // temporary model - can't run as python command
2394
2395 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2396}
2397
2398QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2399{
2400 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2401 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2402 return res;
2403}
2404
2405QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2406{
2407 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2408 alg->loadVariant( toVariant() );
2409 alg->setProvider( provider() );
2410 alg->setSourceFilePath( sourceFilePath() );
2411 return alg;
2412}
2413
2414QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2415{
2416 QString n = name.toLower().trimmed();
2417 const thread_local QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
2418 n.replace( rx, QString() );
2419 const thread_local QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
2420 n.replace( rx2, QString() );
2421 if ( !capitalize )
2422 n = n.replace( ' ', '_' );
2424}
2425
2426QVariantMap QgsProcessingModelAlgorithm::variables() const
2427{
2428 return mVariables;
2429}
2430
2431void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2432{
2433 mVariables = variables;
2434}
2435
2436QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2437{
2438 return mDesignerParameterValues;
2439}
2440
ProcessingSourceType
Processing data source types.
Definition qgis.h:3270
@ 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:3347
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() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
Represents a vector layer which manages a vector based data sets.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6168
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6149
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:6049
QMap< QString, QString > QgsStringMap
Definition qgis.h:6496
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.