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