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