QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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() == QMetaType::type( "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 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
304 {
305 if ( childIt->isActive() )
306 {
307 if ( childIt->algorithm() )
308 toExecute.insert( childIt->childId() );
309 else
310 broken.insert( childIt->childId() );
311 }
312 }
313
314 if ( !broken.empty() )
315 throw QgsProcessingException( QCoreApplication::translate( "QgsProcessingModelAlgorithm", "Cannot run model, the following algorithms are not available on this system: %1" ).arg( qgsSetJoin( broken, QLatin1String( ", " ) ) ) );
316
317 QElapsedTimer totalTime;
318 totalTime.start();
319
320 QgsProcessingMultiStepFeedback modelFeedback( toExecute.count(), feedback );
321 QgsExpressionContext baseContext = createExpressionContext( parameters, context );
322
323 QVariantMap childResults;
324 QVariantMap childInputs;
325
326 QVariantMap finalResults;
327 QSet< QString > executed;
328 bool executedAlg = true;
329 while ( executedAlg && executed.count() < toExecute.count() )
330 {
331 executedAlg = false;
332 for ( const QString &childId : std::as_const( toExecute ) )
333 {
334 if ( feedback && feedback->isCanceled() )
335 break;
336
337 if ( executed.contains( childId ) )
338 continue;
339
340 bool canExecute = true;
341 const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
342 for ( const QString &dependency : dependencies )
343 {
344 if ( !executed.contains( dependency ) )
345 {
346 canExecute = false;
347 break;
348 }
349 }
350
351 if ( !canExecute )
352 continue;
353
354 executedAlg = true;
355
356 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
357 std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
358
359 bool skipGenericLogging = true;
360 switch ( context.logLevel() )
361 {
363 // at default log level we skip all the generic logs about prepare steps, step inputs and outputs
364 skipGenericLogging = true;
365 break;
367 // at verbose log level we include all the generic logs about prepare steps, step inputs and outputs
368 // UNLESS the algorithm specifically tells to skip these (eg raise warning steps and other special cases)
369 skipGenericLogging = childAlg->flags() & Qgis::ProcessingAlgorithmFlag::SkipGenericModelLogging;
370 break;
372 // at model debug log level we'll show all the generic logs for step preparation, inputs and outputs
373 // for every child algorithm
374 skipGenericLogging = false;
375 break;
376 }
377
378 if ( feedback && !skipGenericLogging )
379 feedback->pushDebugInfo( QObject::tr( "Prepare algorithm: %1" ).arg( childId ) );
380
381 QgsExpressionContext expContext = baseContext;
382 expContext << QgsExpressionContextUtils::processingAlgorithmScope( child.algorithm(), parameters, context )
383 << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
384 context.setExpressionContext( expContext );
385
386 QString error;
387 QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error, &context );
388 if ( !error.isEmpty() )
389 throw QgsProcessingException( error );
390
391 if ( feedback && !skipGenericLogging )
392 feedback->setProgressText( QObject::tr( "Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
393
394 childInputs.insert( childId, QgsProcessingUtils::removePointerValuesFromMap( childParams ) );
395 QStringList params;
396 for ( auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
397 {
398 params << QStringLiteral( "%1: %2" ).arg( childParamIt.key(),
399 child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
400 }
401
402 if ( feedback && !skipGenericLogging )
403 {
404 feedback->pushInfo( QObject::tr( "Input Parameters:" ) );
405 feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( params.join( QLatin1String( ", " ) ) ) );
406 }
407
408 QElapsedTimer childTime;
409 childTime.start();
410
411 bool ok = false;
412
413 QThread *modelThread = QThread::currentThread();
414
415 auto prepareOnMainThread = [modelThread, &ok, &childAlg, &childParams, &context, &modelFeedback]
416 {
417 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->prepare() must be run on the main thread" );
418 ok = childAlg->prepare( childParams, context, &modelFeedback );
419 context.pushToThread( modelThread );
420 };
421
422 // Make sure we only run prepare steps on the main thread!
423 if ( modelThread == qApp->thread() )
424 ok = childAlg->prepare( childParams, context, &modelFeedback );
425 else
426 {
427 context.pushToThread( qApp->thread() );
428 QMetaObject::invokeMethod( qApp, prepareOnMainThread, Qt::BlockingQueuedConnection );
429 }
430
431 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
432
433 if ( !ok )
434 {
435 const QString error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
436 throw QgsProcessingException( error );
437 }
438
439 QVariantMap results;
440 try
441 {
442 if ( ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::NoThreading ) && ( QThread::currentThread() != qApp->thread() ) )
443 {
444 // child algorithm run step must be called on main thread
445 auto runOnMainThread = [modelThread, &context, &modelFeedback, &results, &childAlg, &childParams]
446 {
447 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->runPrepared() must be run on the main thread" );
448 results = childAlg->runPrepared( childParams, context, &modelFeedback );
449 context.pushToThread( modelThread );
450 };
451
452 if ( feedback && !skipGenericLogging && modelThread != qApp->thread() )
453 feedback->pushWarning( QObject::tr( "Algorithm “%1” cannot be run in a background thread, switching to main thread for this step" ).arg( childAlg->displayName() ) );
454
455 context.pushToThread( qApp->thread() );
456 QMetaObject::invokeMethod( qApp, runOnMainThread, Qt::BlockingQueuedConnection );
457 }
458 else
459 {
460 // safe to run on model thread
461 results = childAlg->runPrepared( childParams, context, &modelFeedback );
462 }
463 }
464 catch ( QgsProcessingException &e )
465 {
466 const QString error = ( childAlg->flags() & Qgis::ProcessingAlgorithmFlag::CustomException ) ? e.what() : QObject::tr( "Error encountered while running %1: %2" ).arg( child.description(), e.what() );
467 throw QgsProcessingException( error );
468 }
469
470 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
471
472 QVariantMap ppRes;
473 auto postProcessOnMainThread = [modelThread, &ppRes, &childAlg, &context, &modelFeedback]
474 {
475 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->postProcess() must be run on the main thread" );
476 ppRes = childAlg->postProcess( context, &modelFeedback );
477 context.pushToThread( modelThread );
478 };
479
480 // Make sure we only run postProcess steps on the main thread!
481 if ( modelThread == qApp->thread() )
482 ppRes = childAlg->postProcess( context, &modelFeedback );
483 else
484 {
485 context.pushToThread( qApp->thread() );
486 QMetaObject::invokeMethod( qApp, postProcessOnMainThread, Qt::BlockingQueuedConnection );
487 }
488
489 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
490
491 if ( !ppRes.isEmpty() )
492 results = ppRes;
493
494 if ( feedback && !skipGenericLogging )
495 {
496 const QVariantMap displayOutputs = QgsProcessingUtils::removePointerValuesFromMap( results );
497 QStringList formattedOutputs;
498 for ( auto displayOutputIt = displayOutputs.constBegin(); displayOutputIt != displayOutputs.constEnd(); ++displayOutputIt )
499 {
500 formattedOutputs << QStringLiteral( "%1: %2" ).arg( displayOutputIt.key(),
501 QgsProcessingUtils::variantToPythonLiteral( displayOutputIt.value() ) );;
502 }
503 feedback->pushInfo( QObject::tr( "Results:" ) );
504 feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( formattedOutputs.join( QLatin1String( ", " ) ) ) );
505 }
506
507 childResults.insert( childId, results );
508
509 // look through child alg's outputs to determine whether any of these should be copied
510 // to the final model outputs
511 const QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
512 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
513 {
514 const int outputSortKey = mOutputOrder.indexOf( QStringLiteral( "%1:%2" ).arg( childId, outputIt->childOutputName() ) );
515 switch ( mInternalVersion )
516 {
517 case QgsProcessingModelAlgorithm::InternalVersion::Version1:
518 finalResults.insert( childId + ':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
519 break;
520 case QgsProcessingModelAlgorithm::InternalVersion::Version2:
521 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
522 {
523 finalResults.insert( modelParam->name(), results.value( outputIt->childOutputName() ) );
524 }
525 break;
526 }
527
528 if ( !results.value( outputIt->childOutputName() ).toString().isEmpty() )
529 {
530 QgsProcessingContext::LayerDetails &details = context.layerToLoadOnCompletionDetails( results.value( outputIt->childOutputName() ).toString() );
531 details.groupName = mOutputGroup;
532 if ( outputSortKey > 0 )
533 details.layerSortKey = outputSortKey;
534 }
535 }
536
537 executed.insert( childId );
538
539 std::function< void( const QString &, const QString & )> pruneAlgorithmBranchRecursive;
540 pruneAlgorithmBranchRecursive = [&]( const QString & id, const QString &branch = QString() )
541 {
542 const QSet<QString> toPrune = dependentChildAlgorithms( id, branch );
543 for ( const QString &targetId : toPrune )
544 {
545 if ( executed.contains( targetId ) )
546 continue;
547
548 executed.insert( targetId );
549 pruneAlgorithmBranchRecursive( targetId, branch );
550 }
551 };
552
553 // prune remaining algorithms if they are dependent on a branch from this child which didn't eventuate
554 const QgsProcessingOutputDefinitions outputDefs = childAlg->outputDefinitions();
555 for ( const QgsProcessingOutputDefinition *outputDef : outputDefs )
556 {
557 if ( outputDef->type() == QgsProcessingOutputConditionalBranch::typeName() && !results.value( outputDef->name() ).toBool() )
558 {
559 pruneAlgorithmBranchRecursive( childId, outputDef->name() );
560 }
561 }
562
564 {
565 // check if any dependent algorithms should be canceled based on the outputs of this algorithm run
566 // first find all direct dependencies of this algorithm by looking through all remaining child algorithms
567 for ( const QString &candidateId : std::as_const( toExecute ) )
568 {
569 if ( executed.contains( candidateId ) )
570 continue;
571
572 // a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
573 // algorithm's outputs
574 const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
575 const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
576 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
577 bool pruned = false;
578 for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
579 {
580 for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
581 {
582 if ( source.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
583 {
584 // ok, this one is dependent on the current alg. Did we get a value for it?
585 if ( !results.contains( source.outputName() ) )
586 {
587 // oh no, nothing returned for this parameter. Gotta trim the branch back!
588 pruned = true;
589 // skip the dependent alg..
590 executed.insert( candidateId );
591 //... and everything which depends on it
592 pruneAlgorithmBranchRecursive( candidateId, QString() );
593 break;
594 }
595 }
596 }
597 if ( pruned )
598 break;
599 }
600 }
601 }
602
603 childAlg.reset( nullptr );
604 modelFeedback.setCurrentStep( executed.count() );
605 if ( feedback && !skipGenericLogging )
606 feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
607 }
608
609 if ( feedback && feedback->isCanceled() )
610 break;
611 }
612 if ( feedback )
613 feedback->pushDebugInfo( QObject::tr( "Model processed OK. Executed %n algorithm(s) total in %1 s.", nullptr, executed.count() ).arg( totalTime.elapsed() / 1000.0 ) );
614
615 mResults = finalResults;
616 mResults.insert( QStringLiteral( "CHILD_RESULTS" ), childResults );
617 mResults.insert( QStringLiteral( "CHILD_INPUTS" ), childInputs );
618 return mResults;
619}
620
621QString QgsProcessingModelAlgorithm::sourceFilePath() const
622{
623 return mSourceFile;
624}
625
626void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
627{
628 mSourceFile = sourceFile;
629}
630
631bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath() const
632{
633 if ( mSourceFile.isEmpty() )
634 return false;
635
636 const QFileInfo fi( mSourceFile );
637 return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
638}
639
640QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const int indentSize ) const
641{
642 QStringList fileDocString;
643 fileDocString << QStringLiteral( "\"\"\"" );
644 fileDocString << QStringLiteral( "Model exported as python." );
645 fileDocString << QStringLiteral( "Name : %1" ).arg( displayName() );
646 fileDocString << QStringLiteral( "Group : %1" ).arg( group() );
647 fileDocString << QStringLiteral( "With QGIS : %1" ).arg( Qgis::versionInt() );
648 fileDocString << QStringLiteral( "\"\"\"" );
649 fileDocString << QString();
650
651 QStringList lines;
652 QString indent = QString( ' ' ).repeated( indentSize );
653 QString currentIndent;
654
655 QMap< QString, QString> friendlyChildNames;
656 QMap< QString, QString> friendlyOutputNames;
657 auto uniqueSafeName = []( const QString & name, bool capitalize, const QMap< QString, QString > &friendlyNames )->QString
658 {
659 const QString base = safeName( name, capitalize );
660 QString candidate = base;
661 int i = 1;
662 while ( friendlyNames.contains( candidate ) )
663 {
664 i++;
665 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
666 }
667 return candidate;
668 };
669
670 const QString algorithmClassName = safeName( name(), true );
671
672 QSet< QString > toExecute;
673 for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
674 {
675 if ( childIt->isActive() && childIt->algorithm() )
676 {
677 toExecute.insert( childIt->childId() );
678 friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
679 }
680 }
681 const int totalSteps = toExecute.count();
682
683 QStringList importLines; // not a set - we need regular ordering
684 switch ( outputType )
685 {
687 {
688 // add specific parameter type imports
689 const auto params = parameterDefinitions();
690 importLines.reserve( params.count() + 3 );
691 importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
692 importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
693 importLines << QStringLiteral( "from qgis.core import QgsProcessingMultiStepFeedback" );
694
695 bool hasAdvancedParams = false;
696 for ( const QgsProcessingParameterDefinition *def : params )
697 {
698 if ( def->flags() & Qgis::ProcessingParameterFlag::Advanced )
699 hasAdvancedParams = true;
700
701 const QString importString = QgsApplication::processingRegistry()->parameterType( def->type() )->pythonImportString();
702 if ( !importString.isEmpty() && !importLines.contains( importString ) )
703 importLines << importString;
704 }
705
706 if ( hasAdvancedParams )
707 importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
708
709 lines << QStringLiteral( "import processing" );
710 lines << QString() << QString();
711
712 lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
713 lines << QString();
714
715 // initAlgorithm, parameter definitions
716 lines << indent + QStringLiteral( "def initAlgorithm(self, config=None):" );
717 if ( params.empty() )
718 {
719 lines << indent + indent + QStringLiteral( "pass" );
720 }
721 else
722 {
723 lines.reserve( lines.size() + params.size() );
724 for ( const QgsProcessingParameterDefinition *def : params )
725 {
726 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
727
728 if ( defClone->isDestination() )
729 {
730 const QString uniqueChildName = defClone->metadata().value( QStringLiteral( "_modelChildId" ) ).toString() + ':' + defClone->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
731 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
732 friendlyOutputNames.insert( uniqueChildName, friendlyName );
733 defClone->setName( friendlyName );
734 }
735 else
736 {
737 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
738 {
739 const QStringList parts = mParameterComponents.value( defClone->name() ).comment()->description().split( QStringLiteral( "\n" ) );
740 for ( const QString &part : parts )
741 {
742 lines << indent + indent + QStringLiteral( "# %1" ).arg( part );
743 }
744 }
745 }
746
747 if ( defClone->flags() & Qgis::ProcessingParameterFlag::Advanced )
748 {
749 lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
750 lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
751 lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
752 }
753 else
754 {
755 lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
756 }
757 }
758 }
759
760 lines << QString();
761 lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, model_feedback):" );
762 currentIndent = indent + indent;
763
764 lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
765 lines << currentIndent + QStringLiteral( "# overall progress through the model" );
766 lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
767 break;
768 }
769#if 0
770 case Script:
771 {
772 QgsStringMap params;
773 QgsProcessingContext context;
774 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
775 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
776 {
777 QString name = paramIt.value().parameterName();
778 if ( parameterDefinition( name ) )
779 {
780 // TODO - generic value to string method
781 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
782 }
783 }
784
785 if ( !params.isEmpty() )
786 {
787 lines << QStringLiteral( "parameters = {" );
788 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
789 {
790 lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
791 }
792 lines << QStringLiteral( "}" )
793 << QString();
794 }
795
796 lines << QStringLiteral( "context = QgsProcessingContext()" )
797 << QStringLiteral( "context.setProject(QgsProject.instance())" )
798 << QStringLiteral( "feedback = QgsProcessingFeedback()" )
799 << QString();
800
801 break;
802 }
803#endif
804
805 }
806
807 lines << currentIndent + QStringLiteral( "results = {}" );
808 lines << currentIndent + QStringLiteral( "outputs = {}" );
809 lines << QString();
810
811 QSet< QString > executed;
812 bool executedAlg = true;
813 int currentStep = 0;
814 while ( executedAlg && executed.count() < toExecute.count() )
815 {
816 executedAlg = false;
817 const auto constToExecute = toExecute;
818 for ( const QString &childId : constToExecute )
819 {
820 if ( executed.contains( childId ) )
821 continue;
822
823 bool canExecute = true;
824 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
825 for ( const QString &dependency : constDependsOnChildAlgorithms )
826 {
827 if ( !executed.contains( dependency ) )
828 {
829 canExecute = false;
830 break;
831 }
832 }
833
834 if ( !canExecute )
835 continue;
836
837 executedAlg = true;
838
839 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
840
841 // fill in temporary outputs
842 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
843 QgsStringMap childParams;
844 for ( const QgsProcessingParameterDefinition *def : childDefs )
845 {
846 if ( def->isDestination() )
847 {
848 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
849
850 // is destination linked to one of the final outputs from this model?
851 bool isFinalOutput = false;
852 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
853 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
854 for ( ; outputIt != outputs.constEnd(); ++outputIt )
855 {
856 if ( outputIt->childOutputName() == destParam->name() )
857 {
858 QString paramName = child.childId() + ':' + outputIt.key();
859 paramName = friendlyOutputNames.value( paramName, paramName );
860 childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
861 isFinalOutput = true;
862 break;
863 }
864 }
865
866 if ( !isFinalOutput )
867 {
868 // output is temporary
869
870 // check whether it's optional, and if so - is it required?
871 bool required = true;
873 {
874 required = childOutputIsRequired( child.childId(), destParam->name() );
875 }
876
877 // not optional, or required elsewhere in model
878 if ( required )
879 {
880 childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
881 }
882 }
883 }
884 }
885
886 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
887 currentStep++;
888 if ( currentStep < totalSteps )
889 {
890 lines << QString();
891 lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
892 lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
893 lines << currentIndent + indent + QStringLiteral( "return {}" );
894 lines << QString();
895 }
896 executed.insert( childId );
897 }
898 }
899
900 switch ( outputType )
901 {
903 lines << currentIndent + QStringLiteral( "return results" );
904 lines << QString();
905
906 // name, displayName
907 lines << indent + QStringLiteral( "def name(self):" );
908 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
909 lines << QString();
910 lines << indent + QStringLiteral( "def displayName(self):" );
911 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
912 lines << QString();
913
914 // group, groupId
915 lines << indent + QStringLiteral( "def group(self):" );
916 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
917 lines << QString();
918 lines << indent + QStringLiteral( "def groupId(self):" );
919 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
920 lines << QString();
921
922 // help
923 if ( !shortHelpString().isEmpty() )
924 {
925 lines << indent + QStringLiteral( "def shortHelpString(self):" );
926 lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
927 lines << QString();
928 }
929 if ( !helpUrl().isEmpty() )
930 {
931 lines << indent + QStringLiteral( "def helpUrl(self):" );
932 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
933 lines << QString();
934 }
935
936 // createInstance
937 lines << indent + QStringLiteral( "def createInstance(self):" );
938 lines << indent + indent + QStringLiteral( "return %1()" ).arg( algorithmClassName );
939
940 // additional import lines
941 static QMap< QString, QString > sAdditionalImports
942 {
943 { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
944 { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
945 { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
946 { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
947 { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
948 { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
949 { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
950 { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
951 { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
952 { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
953 { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
954 { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
955 { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
956 { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
957 { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
958 { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
959 { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
960 { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
961 { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
962 { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
963 };
964
965 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
966 {
967 if ( importLines.contains( it.value() ) )
968 {
969 // already got this import
970 continue;
971 }
972
973 bool found = false;
974 for ( const QString &line : std::as_const( lines ) )
975 {
976 if ( line.contains( it.key() ) )
977 {
978 found = true;
979 break;
980 }
981 }
982 if ( found )
983 {
984 importLines << it.value();
985 }
986 }
987
988 lines = fileDocString + importLines + lines;
989 break;
990 }
991
992 lines << QString();
993
994 return lines;
995}
996
997QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext *context, const QVariantMap &modelParameters, const QVariantMap &results ) const
998{
999 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
1000
1001 auto safeName = []( const QString & name )->QString
1002 {
1003 QString s = name;
1004 const thread_local QRegularExpression safeNameRe( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) );
1005 return s.replace( safeNameRe, QStringLiteral( "_" ) );
1006 };
1007
1008 // "static"/single value sources
1009 QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
1035 QStringList() << QgsProcessingOutputNumber::typeName()
1039
1040 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1041 {
1042 QString name;
1043 QVariant value;
1044 QString description;
1045 switch ( source.source() )
1046 {
1048 {
1049 name = source.parameterName();
1050 value = modelParameters.value( source.parameterName() );
1051 description = parameterDefinition( source.parameterName() )->description();
1052 break;
1053 }
1055 {
1056 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1057 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1058 source.outputChildId() : child.description(), source.outputName() );
1059 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1060 {
1061 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1062 child.description() );
1063 }
1064 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1065 break;
1066 }
1067
1072 continue;
1073 }
1074 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1075 }
1076
1077 // layer sources
1078 sources = availableSourcesForChild( childId, QStringList()
1084
1085 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1086 {
1087 QString name;
1088 QVariant value;
1089 QString description;
1090
1091 switch ( source.source() )
1092 {
1094 {
1095 name = source.parameterName();
1096 value = modelParameters.value( source.parameterName() );
1097 description = parameterDefinition( source.parameterName() )->description();
1098 break;
1099 }
1101 {
1102 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1103 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1104 source.outputChildId() : child.description(), source.outputName() );
1105 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1106 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1107 {
1108 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1109 child.description() );
1110 }
1111 break;
1112 }
1113
1118 continue;
1119
1120 }
1121
1122 if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
1123 {
1124 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1125 value = fromVar.sink;
1126 if ( value.userType() == QMetaType::type( "QgsProperty" ) && context )
1127 {
1128 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1129 }
1130 }
1131 QgsMapLayer *layer = nullptr;
1132 if ( context )
1133 {
1134 layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1135 if ( !layer )
1136 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), *context );
1137 }
1138
1139 variables.insert( safeName( name ), VariableDefinition( layer ? QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ) : QVariant(), source, description ) );
1140 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1141 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1142 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1143 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1144 }
1145
1146 sources = availableSourcesForChild( childId, QStringList()
1148 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1149 {
1150 QString name;
1151 QVariant value;
1152 QString description;
1153
1154 switch ( source.source() )
1155 {
1157 {
1158 name = source.parameterName();
1159 value = modelParameters.value( source.parameterName() );
1160 description = parameterDefinition( source.parameterName() )->description();
1161 break;
1162 }
1164 {
1165 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1166 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1167 source.outputChildId() : child.description(), source.outputName() );
1168 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1169 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1170 {
1171 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1172 child.description() );
1173 }
1174 break;
1175 }
1176
1181 continue;
1182
1183 }
1184
1185 QgsFeatureSource *featureSource = nullptr;
1186 if ( value.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) )
1187 {
1188 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1189 value = fromVar.source;
1190 }
1191 else if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
1192 {
1193 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1194 value = fromVar.sink;
1195 if ( context && value.userType() == QMetaType::type( "QgsProperty" ) )
1196 {
1197 value = value.value< QgsProperty >().valueAsString( context->expressionContext() );
1198 }
1199 }
1200 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1201 {
1202 featureSource = layer;
1203 }
1204 if ( context && !featureSource )
1205 {
1206 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), *context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1207 featureSource = vl;
1208 }
1209
1210 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1211 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1212 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1213 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1214 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1215 }
1216
1217 return variables;
1218}
1219
1220QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1221{
1222 std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QStringLiteral( "algorithm_inputs" ) ) );
1223 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, &context, modelParameters, results );
1224 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1225 for ( ; varIt != variables.constEnd(); ++varIt )
1226 {
1227 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1228 }
1229 return scope.release();
1230}
1231
1232QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1233{
1234 QgsProcessingModelChildParameterSources sources;
1235
1236 // first look through model parameters
1237 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1238 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1239 {
1240 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1241 if ( !def )
1242 continue;
1243
1244 if ( parameterTypes.contains( def->type() ) )
1245 {
1246 if ( !dataTypes.isEmpty() )
1247 {
1249 {
1250 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1251 if ( !( dataTypes.contains( static_cast< int >( fieldDef->dataType() ) ) || fieldDef->dataType() == Qgis::ProcessingFieldParameterDataType::Any ) )
1252 {
1253 continue;
1254 }
1255 }
1257 {
1258 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1259 if ( !sourceDef )
1260 continue;
1261
1262 bool ok = sourceDef->dataTypes().isEmpty();
1263 const auto constDataTypes = sourceDef->dataTypes();
1264 for ( int type : constDataTypes )
1265 {
1266 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 ) )
1267 {
1268 ok = true;
1269 break;
1270 }
1271 }
1272 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 ) ) )
1273 ok = true;
1274
1275 if ( !ok )
1276 continue;
1277 }
1278 }
1279 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1280 }
1281 }
1282
1283 QSet< QString > dependents;
1284 if ( !childId.isEmpty() )
1285 {
1286 dependents = dependentChildAlgorithms( childId );
1287 dependents << childId;
1288 }
1289
1290 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1291 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1292 {
1293 if ( dependents.contains( childIt->childId() ) )
1294 continue;
1295
1296 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1297 if ( !alg )
1298 continue;
1299
1300 const auto constOutputDefinitions = alg->outputDefinitions();
1301 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1302 {
1303 if ( outputTypes.contains( out->type() ) )
1304 {
1305 if ( !dataTypes.isEmpty() )
1306 {
1307 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1308 {
1309 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1310
1311 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1312 {
1313 //unacceptable output
1314 continue;
1315 }
1316 }
1317 }
1318 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1319 }
1320 }
1321 }
1322
1323 return sources;
1324}
1325
1326QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1327{
1328 return mHelpContent;
1329}
1330
1331void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1332{
1333 mHelpContent = helpContent;
1334}
1335
1336void QgsProcessingModelAlgorithm::setName( const QString &name )
1337{
1338 mModelName = name;
1339}
1340
1341void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1342{
1343 mModelGroup = group;
1344}
1345
1346bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1347{
1348 issues.clear();
1349 bool res = true;
1350
1351 if ( mChildAlgorithms.empty() )
1352 {
1353 res = false;
1354 issues << QObject::tr( "Model does not contain any algorithms" );
1355 }
1356
1357 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1358 {
1359 QStringList childIssues;
1360 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1361
1362 for ( const QString &issue : std::as_const( childIssues ) )
1363 {
1364 issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1365 }
1366 }
1367 return res;
1368}
1369
1370QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1371{
1372 return mChildAlgorithms;
1373}
1374
1375void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1376{
1377 mParameterComponents = parameterComponents;
1378}
1379
1380void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1381{
1382 mParameterComponents.insert( component.parameterName(), component );
1383}
1384
1385QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1386{
1387 if ( !mParameterComponents.contains( name ) )
1388 {
1389 QgsProcessingModelParameter &component = mParameterComponents[ name ];
1390 component.setParameterName( name );
1391 return component;
1392 }
1393 return mParameterComponents[ name ];
1394}
1395
1396QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1397{
1398 QList< QgsProcessingModelParameter > res;
1399 QSet< QString > found;
1400 for ( const QString &parameter : mParameterOrder )
1401 {
1402 if ( mParameterComponents.contains( parameter ) )
1403 {
1404 res << mParameterComponents.value( parameter );
1405 found << parameter;
1406 }
1407 }
1408
1409 // add any missing ones to end of list
1410 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1411 {
1412 if ( !found.contains( it.key() ) )
1413 {
1414 res << it.value();
1415 }
1416 }
1417 return res;
1418}
1419
1420void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1421{
1422 mParameterOrder = order;
1423}
1424
1425QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
1426{
1427 QList< QgsProcessingModelOutput > res;
1428 QSet< QString > found;
1429
1430 for ( const QString &output : mOutputOrder )
1431 {
1432 bool foundOutput = false;
1433 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1434 {
1435 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1436 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1437 {
1438 if ( output == QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) )
1439 {
1440 res << outputIt.value();
1441 foundOutput = true;
1442 found.insert( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) );
1443 }
1444 }
1445 if ( foundOutput )
1446 break;
1447 }
1448 }
1449
1450 // add any missing ones to end of list
1451 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1452 {
1453 const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
1454 for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
1455 {
1456 if ( !found.contains( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) ) )
1457 {
1458 res << outputIt.value();
1459 }
1460 }
1461 }
1462
1463 return res;
1464}
1465
1466void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
1467{
1468 mOutputOrder = order;
1469}
1470
1471QString QgsProcessingModelAlgorithm::outputGroup() const
1472{
1473 return mOutputGroup;
1474}
1475
1476void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
1477{
1478 mOutputGroup = group;
1479}
1480
1481void QgsProcessingModelAlgorithm::updateDestinationParameters()
1482{
1483 //delete existing destination parameters
1484 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1485 while ( it.hasNext() )
1486 {
1487 const QgsProcessingParameterDefinition *def = it.next();
1488 if ( def->isDestination() )
1489 {
1490 delete def;
1491 it.remove();
1492 }
1493 }
1494 // also delete outputs
1495 qDeleteAll( mOutputs );
1496 mOutputs.clear();
1497
1498 // rebuild
1499 QSet< QString > usedFriendlyNames;
1500 auto uniqueSafeName = [&usedFriendlyNames ]( const QString & name )->QString
1501 {
1502 const QString base = safeName( name, false );
1503 QString candidate = base;
1504 int i = 1;
1505 while ( usedFriendlyNames.contains( candidate ) )
1506 {
1507 i++;
1508 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
1509 }
1510 usedFriendlyNames.insert( candidate );
1511 return candidate;
1512 };
1513
1514 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1515 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1516 {
1517 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1518 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1519 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1520 {
1521 if ( !childIt->isActive() || !childIt->algorithm() )
1522 continue;
1523
1524 // child algorithm has a destination parameter set, copy it to the model
1525 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1526 if ( !source )
1527 continue;
1528
1529 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1530 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1531 // outputs.
1532 param->setFlags( param->flags() & ~ static_cast< int >( Qgis::ProcessingParameterFlag::Hidden ) );
1533 if ( outputIt->isMandatory() )
1534 param->setFlags( param->flags() & ~static_cast< int >( Qgis::ProcessingParameterFlag::Optional ) );
1535 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1536 {
1537 QString friendlyName = uniqueSafeName( outputIt->description() );
1538 param->setName( friendlyName );
1539 }
1540 else
1541 {
1542 param->setName( outputIt->childId() + ':' + outputIt->name() );
1543 }
1544 // add some metadata so we can easily link this parameter back to the child source
1545 param->metadata().insert( QStringLiteral( "_modelChildId" ), outputIt->childId() );
1546 param->metadata().insert( QStringLiteral( "_modelChildOutputName" ), outputIt->name() );
1547 param->metadata().insert( QStringLiteral( "_modelChildProvider" ), childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1548
1549 param->setDescription( outputIt->description() );
1550 param->setDefaultValue( outputIt->defaultValue() );
1551
1552 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1553 if ( addParameter( param.release() ) && newDestParam )
1554 {
1555 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1556 {
1557 // we need to copy the constraints given by the provider which creates this output across
1558 // and replace those which have been set to match the model provider's constraints
1559 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1560 newDestParam->mOriginalProvider = provider;
1561 }
1562 }
1563 }
1564 }
1565}
1566
1567void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1568{
1569 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1570}
1571
1572QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1573{
1574 return mGroupBoxes.values();
1575}
1576
1577void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1578{
1579 mGroupBoxes.remove( uuid );
1580}
1581
1582QVariant QgsProcessingModelAlgorithm::toVariant() const
1583{
1584 QVariantMap map;
1585 map.insert( QStringLiteral( "model_name" ), mModelName );
1586 map.insert( QStringLiteral( "model_group" ), mModelGroup );
1587 map.insert( QStringLiteral( "help" ), mHelpContent );
1588 map.insert( QStringLiteral( "internal_version" ), qgsEnumValueToKey( mInternalVersion ) );
1589
1590 QVariantMap childMap;
1591 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1592 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1593 {
1594 childMap.insert( childIt.key(), childIt.value().toVariant() );
1595 }
1596 map.insert( QStringLiteral( "children" ), childMap );
1597
1598 QVariantMap paramMap;
1599 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1600 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1601 {
1602 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1603 }
1604 map.insert( QStringLiteral( "parameters" ), paramMap );
1605
1606 QVariantMap paramDefMap;
1607 for ( const QgsProcessingParameterDefinition *def : mParameters )
1608 {
1609 paramDefMap.insert( def->name(), def->toVariantMap() );
1610 }
1611 map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1612
1613 QVariantList groupBoxDefs;
1614 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1615 {
1616 groupBoxDefs.append( it.value().toVariant() );
1617 }
1618 map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1619
1620 map.insert( QStringLiteral( "modelVariables" ), mVariables );
1621
1622 map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1623
1624 map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1625 map.insert( QStringLiteral( "outputOrder" ), mOutputOrder );
1626 map.insert( QStringLiteral( "outputGroup" ), mOutputGroup );
1627
1628 return map;
1629}
1630
1631bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1632{
1633 QVariantMap map = model.toMap();
1634
1635 mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1636 mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1637 mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1638 mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1639
1640 mInternalVersion = qgsEnumKeyToValue( map.value( QStringLiteral( "internal_version" ) ).toString(), InternalVersion::Version1 );
1641
1642 mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1643 mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1644
1645 mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1646 mOutputOrder = map.value( QStringLiteral( "outputOrder" ) ).toStringList();
1647 mOutputGroup = map.value( QStringLiteral( "outputGroup" ) ).toString();
1648
1649 mChildAlgorithms.clear();
1650 QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1651 QVariantMap::const_iterator childIt = childMap.constBegin();
1652 for ( ; childIt != childMap.constEnd(); ++childIt )
1653 {
1654 QgsProcessingModelChildAlgorithm child;
1655 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1656 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1657 // with no way for users to repair them
1658 if ( !child.loadVariant( childIt.value() ) )
1659 continue;
1660
1661 mChildAlgorithms.insert( child.childId(), child );
1662 }
1663
1664 mParameterComponents.clear();
1665 QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1666 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1667 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1668 {
1669 QgsProcessingModelParameter param;
1670 if ( !param.loadVariant( paramIt.value().toMap() ) )
1671 return false;
1672
1673 mParameterComponents.insert( param.parameterName(), param );
1674 }
1675
1676 qDeleteAll( mParameters );
1677 mParameters.clear();
1678 QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1679
1680 auto addParam = [this]( const QVariant & value )
1681 {
1682 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1683 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1684 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1685 // with no way for users to repair them
1686 if ( param )
1687 {
1688 if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1689 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1690
1691 // set parameter help from help content
1692 param->setHelp( mHelpContent.value( param->name() ).toString() );
1693
1694 // add parameter
1695 addParameter( param.release() );
1696 }
1697 else
1698 {
1699 QVariantMap map = value.toMap();
1700 QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1701 QString name = map.value( QStringLiteral( "name" ) ).toString();
1702
1703 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1704 }
1705 };
1706
1707 QSet< QString > loadedParams;
1708 // first add parameters respecting mParameterOrder
1709 for ( const QString &name : std::as_const( mParameterOrder ) )
1710 {
1711 if ( paramDefMap.contains( name ) )
1712 {
1713 addParam( paramDefMap.value( name ) );
1714 loadedParams << name;
1715 }
1716 }
1717 // then load any remaining parameters
1718 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1719 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1720 {
1721 if ( !loadedParams.contains( paramDefIt.key() ) )
1722 addParam( paramDefIt.value() );
1723 }
1724
1725 mGroupBoxes.clear();
1726 const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1727 for ( const QVariant &groupBoxDef : groupBoxList )
1728 {
1729 QgsProcessingModelGroupBox groupBox;
1730 groupBox.loadVariant( groupBoxDef.toMap() );
1731 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1732 }
1733
1734 updateDestinationParameters();
1735
1736 return true;
1737}
1738
1739bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType )
1740{
1741 // This method is intended to be "permissive" rather than "restrictive".
1742 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1743 // if there's doubt then we default to returning true.
1744 return ( acceptableDataTypes.empty()
1745 || acceptableDataTypes.contains( static_cast< int >( outputType ) )
1747 || outputType == Qgis::ProcessingSourceType::Vector
1749 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) )
1750 || acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) )
1751 || ( acceptableDataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) && ( outputType == Qgis::ProcessingSourceType::VectorPoint ||
1754}
1755
1756void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1757{
1758 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1759 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1760 {
1761 if ( !childIt->algorithm() )
1762 childIt->reattach();
1763 }
1764}
1765
1766bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1767{
1768 QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1769 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1770 doc.appendChild( elem );
1771
1772 QFile file( path );
1773 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1774 {
1775 QTextStream stream( &file );
1776 doc.save( stream, 2 );
1777 file.close();
1778 return true;
1779 }
1780 return false;
1781}
1782
1783bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1784{
1785 QDomDocument doc;
1786
1787 QFile file( path );
1788 if ( file.open( QFile::ReadOnly ) )
1789 {
1790 if ( !doc.setContent( &file ) )
1791 return false;
1792
1793 file.close();
1794 }
1795 else
1796 {
1797 return false;
1798 }
1799
1800 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1801 return loadVariant( props );
1802}
1803
1804void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1805{
1806 mChildAlgorithms = childAlgorithms;
1807 updateDestinationParameters();
1808}
1809
1810void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1811{
1812 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1813 updateDestinationParameters();
1814}
1815
1816QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1817{
1818 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1819 algorithm.generateChildId( *this );
1820
1821 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1822 updateDestinationParameters();
1823 return algorithm.childId();
1824}
1825
1826QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1827{
1828 return mChildAlgorithms[ childId ];
1829}
1830
1831bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1832{
1833 if ( !dependentChildAlgorithms( id ).isEmpty() )
1834 return false;
1835
1836 mChildAlgorithms.remove( id );
1837 updateDestinationParameters();
1838 return true;
1839}
1840
1841void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1842{
1843 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1844 for ( const QString &child : constDependentChildAlgorithms )
1845 {
1846 childAlgorithm( child ).setActive( false );
1847 }
1848 childAlgorithm( id ).setActive( false );
1849 updateDestinationParameters();
1850}
1851
1852bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1853{
1854 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1855 for ( const QString &child : constDependsOnChildAlgorithms )
1856 {
1857 if ( !childAlgorithm( child ).isActive() )
1858 return false;
1859 }
1860 childAlgorithm( id ).setActive( true );
1861 updateDestinationParameters();
1862 return true;
1863}
1864
1865void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1866{
1867 if ( addParameter( definition ) )
1868 mParameterComponents.insert( definition->name(), component );
1869}
1870
1871void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
1872{
1873 removeParameter( definition->name() );
1874 addParameter( definition );
1875}
1876
1877void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
1878{
1879 removeParameter( name );
1880 mParameterComponents.remove( name );
1881}
1882
1883void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
1884{
1885 QgsProcessingContext context;
1886 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
1887
1888 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString & expressionString ) -> std::tuple< bool, QString >
1889 {
1890 QgsExpression expression( expressionString );
1891 expression.prepare( &expressionContext );
1892 QSet<QString> variables = expression.referencedVariables();
1893 if ( variables.contains( oldName ) )
1894 {
1895 QString newExpression = expressionString;
1896 newExpression.replace( QStringLiteral( "@%1" ).arg( oldName ), QStringLiteral( "@%2" ).arg( newName ) );
1897 return { true, newExpression };
1898 }
1899 return { false, QString() };
1900 };
1901
1902 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
1903 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
1904 {
1905 bool changed = false;
1906 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1907 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
1908 for ( ; paramIt != childParams.end(); ++paramIt )
1909 {
1910 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
1911 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
1912 {
1913 switch ( valueIt->source() )
1914 {
1916 {
1917 if ( valueIt->parameterName() == oldName )
1918 {
1919 valueIt->setParameterName( newName );
1920 changed = true;
1921 }
1922 break;
1923 }
1924
1926 {
1927 bool updatedExpression = false;
1928 QString newExpression;
1929 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
1930 if ( updatedExpression )
1931 {
1932 valueIt->setExpression( newExpression );
1933 changed = true;
1934 }
1935 break;
1936 }
1937
1939 {
1940 if ( valueIt->staticValue().userType() == QMetaType::type( "QgsProperty" ) )
1941 {
1942 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
1943 if ( property.propertyType() == Qgis::PropertyType::Expression )
1944 {
1945 bool updatedExpression = false;
1946 QString newExpression;
1947 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
1948 if ( updatedExpression )
1949 {
1950 property.setExpressionString( newExpression );
1951 valueIt->setStaticValue( property );
1952 changed = true;
1953 }
1954 }
1955 }
1956 break;
1957 }
1958
1962 break;
1963 }
1964 }
1965 }
1966 if ( changed )
1967 childIt->setParameterSources( childParams );
1968 }
1969}
1970
1971bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
1972{
1973 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1974 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1975 {
1976 // check whether child requires this parameter
1977 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1978 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1979 for ( ; paramIt != childParams.constEnd(); ++paramIt )
1980 {
1981 const auto constValue = paramIt.value();
1982 for ( const QgsProcessingModelChildParameterSource &source : constValue )
1983 {
1985 && source.parameterName() == name )
1986 {
1987 return true;
1988 }
1989 }
1990 }
1991 }
1992 return false;
1993}
1994
1995bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
1996{
1997 const auto constMParameters = mParameters;
1998 for ( const QgsProcessingParameterDefinition *def : constMParameters )
1999 {
2000 if ( def->name() == name )
2001 continue;
2002
2003 if ( def->dependsOnOtherParameters().contains( name ) )
2004 return true;
2005 }
2006 return false;
2007}
2008
2009QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
2010{
2011 return mParameterComponents;
2012}
2013
2014void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
2015{
2016 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2017 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2018 {
2019 if ( depends.contains( childIt->childId() ) )
2020 continue;
2021
2022 // does alg have a direct dependency on this child?
2023 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
2024 bool hasDependency = false;
2025 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
2026 {
2027 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
2028 {
2029 hasDependency = true;
2030 break;
2031 }
2032 }
2033
2034 if ( hasDependency )
2035 {
2036 depends.insert( childIt->childId() );
2037 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2038 continue;
2039 }
2040
2041 // check whether child requires any outputs from the target alg
2042 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
2043 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2044 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2045 {
2046 const auto constValue = paramIt.value();
2047 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2048 {
2050 && source.outputChildId() == childId )
2051 {
2052 depends.insert( childIt->childId() );
2053 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
2054 break;
2055 }
2056 }
2057 }
2058 }
2059}
2060
2061QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
2062{
2063 QSet< QString > algs;
2064
2065 // temporarily insert the target child algorithm to avoid
2066 // unnecessarily recursion though it
2067 algs.insert( childId );
2068
2069 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
2070
2071 // remove temporary target alg
2072 algs.remove( childId );
2073
2074 return algs;
2075}
2076
2077
2078void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
2079{
2080 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
2081
2082 // add direct dependencies
2083 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
2084 for ( const QgsProcessingModelChildDependency &val : constDependencies )
2085 {
2086 if ( !depends.contains( val.childId ) )
2087 {
2088 depends.insert( val.childId );
2089 dependsOnChildAlgorithmsRecursive( val.childId, depends );
2090 }
2091 }
2092
2093 // check through parameter dependencies
2094 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
2095 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
2096 for ( ; paramIt != childParams.constEnd(); ++paramIt )
2097 {
2098 const auto constValue = paramIt.value();
2099 for ( const QgsProcessingModelChildParameterSource &source : constValue )
2100 {
2101 switch ( source.source() )
2102 {
2104 if ( !depends.contains( source.outputChildId() ) )
2105 {
2106 depends.insert( source.outputChildId() );
2107 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
2108 }
2109 break;
2110
2112 {
2113 const QgsExpression exp( source.expression() );
2114 const QSet<QString> vars = exp.referencedVariables();
2115 if ( vars.empty() )
2116 break;
2117
2118 // find the source of referenced variables and check if it's another child algorithm
2119 const QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> availableVariables = variablesForChildAlgorithm( childId );
2120 for ( auto childVarIt = availableVariables.constBegin(); childVarIt != availableVariables.constEnd(); ++childVarIt )
2121 {
2122 // we're only looking here for variables coming from other child algorithm outputs
2123 if ( childVarIt->source.source() != Qgis::ProcessingModelChildParameterSource::ChildOutput )
2124 continue;
2125
2126 if ( !vars.contains( childVarIt.key() ) || depends.contains( childVarIt->source.outputChildId() ) )
2127 continue;
2128
2129 // this variable is required for the child's expression, so the corresponding algorithm must be run first
2130 depends.insert( childVarIt->source.outputChildId() );
2131 dependsOnChildAlgorithmsRecursive( childVarIt->source.outputChildId(), depends );
2132 }
2133 break;
2134 }
2135
2140 break;
2141 }
2142 }
2143 }
2144}
2145
2146QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2147{
2148 QSet< QString > algs;
2149
2150 // temporarily insert the target child algorithm to avoid
2151 // unnecessarily recursion though it
2152 algs.insert( childId );
2153
2154 dependsOnChildAlgorithmsRecursive( childId, algs );
2155
2156 // remove temporary target alg
2157 algs.remove( childId );
2158
2159 return algs;
2160}
2161
2162QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2163{
2164 QSet< QString > dependent;
2165 if ( !childId.isEmpty() )
2166 {
2167 dependent.unite( dependentChildAlgorithms( childId ) );
2168 dependent.insert( childId );
2169 }
2170
2171 QList<QgsProcessingModelChildDependency> res;
2172 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2173 {
2174 if ( !dependent.contains( it->childId() ) )
2175 {
2176 // check first if algorithm provides output branches
2177 bool hasBranches = false;
2178 if ( it->algorithm() )
2179 {
2180 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2181 for ( const QgsProcessingOutputDefinition *def : defs )
2182 {
2184 {
2185 hasBranches = true;
2186 QgsProcessingModelChildDependency alg;
2187 alg.childId = it->childId();
2188 alg.conditionalBranch = def->name();
2189 res << alg;
2190 }
2191 }
2192 }
2193
2194 if ( !hasBranches )
2195 {
2196 QgsProcessingModelChildDependency alg;
2197 alg.childId = it->childId();
2198 res << alg;
2199 }
2200 }
2201 }
2202 return res;
2203}
2204
2205bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2206{
2207 issues.clear();
2208 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2209 if ( childIt != mChildAlgorithms.constEnd() )
2210 {
2211 if ( !childIt->algorithm() )
2212 {
2213 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2214 return false;
2215 }
2216 bool res = true;
2217
2218 // loop through child algorithm parameters and check that they are all valid
2219 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2220 for ( const QgsProcessingParameterDefinition *def : defs )
2221 {
2222 if ( childIt->parameterSources().contains( def->name() ) )
2223 {
2224 // is the value acceptable?
2225 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2226 for ( const QgsProcessingModelChildParameterSource &source : sources )
2227 {
2228 switch ( source.source() )
2229 {
2231 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2232 {
2233 res = false;
2234 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2235 }
2236 break;
2237
2239 if ( !parameterComponents().contains( source.parameterName() ) )
2240 {
2241 res = false;
2242 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2243 }
2244 break;
2245
2247 if ( !childAlgorithms().contains( source.outputChildId() ) )
2248 {
2249 res = false;
2250 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2251 }
2252 break;
2253
2257 break;
2258 }
2259 }
2260 }
2261 else
2262 {
2263 // not specified. Is it optional?
2264
2265 // ignore destination parameters -- they shouldn't ever be mandatory
2266 if ( def->isDestination() )
2267 continue;
2268
2269 if ( !def->checkValueIsAcceptable( QVariant() ) )
2270 {
2271 res = false;
2272 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2273 }
2274 }
2275 }
2276
2277 return res;
2278 }
2279 else
2280 {
2281 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2282 return false;
2283 }
2284}
2285
2286bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2287{
2288 reattachAlgorithms();
2289 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2290 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2291 {
2292 if ( !childIt->algorithm() )
2293 {
2294 if ( errorMessage )
2295 {
2296 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2297 }
2298 return false;
2299 }
2300 }
2301 return true;
2302}
2303
2304QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2305{
2306 if ( mSourceFile.isEmpty() )
2307 return QString(); // temporary model - can't run as python command
2308
2309 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2310}
2311
2312QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2313{
2314 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2315 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2316 return res;
2317}
2318
2319QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2320{
2321 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2322 alg->loadVariant( toVariant() );
2323 alg->setProvider( provider() );
2324 alg->setSourceFilePath( sourceFilePath() );
2325 return alg;
2326}
2327
2328QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2329{
2330 QString n = name.toLower().trimmed();
2331 const thread_local QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
2332 n.replace( rx, QString() );
2333 const thread_local QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
2334 n.replace( rx2, QString() );
2335 if ( !capitalize )
2336 n = n.replace( ' ', '_' );
2338}
2339
2340QVariantMap QgsProcessingModelAlgorithm::variables() const
2341{
2342 return mVariables;
2343}
2344
2345void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2346{
2347 mVariables = variables;
2348}
2349
2350QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2351{
2352 return mDesignerParameterValues;
2353}
2354
ProcessingSourceType
Processing data source types.
Definition: qgis.h:2858
@ 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.
@ 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 (si...
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
Definition: qgsexception.h:49
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:75
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.
Qgis::ProcessingLogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
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.
Definition: qgsexception.h:83
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 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.
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.
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 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.
Definition: qgsprocessing.h:48
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
A store for object properties.
Definition: qgsproperty.h:228
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).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
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:5417
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:5398
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:5298
QMap< QString, QString > QgsStringMap
Definition: qgis.h:5737
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2349
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Single variable definition for use within a QgsExpressionContextScope.