QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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
32#include <QFile>
33#include <QTextStream>
34#include <QRegularExpression>
36
37QgsProcessingModelAlgorithm::QgsProcessingModelAlgorithm( const QString &name, const QString &group, const QString &groupId )
38 : mModelName( name.isEmpty() ? QObject::tr( "model" ) : name )
39 , mModelGroup( group )
40 , mModelGroupId( groupId )
41{}
42
43void QgsProcessingModelAlgorithm::initAlgorithm( const QVariantMap & )
44{
45}
46
47QString QgsProcessingModelAlgorithm::name() const
48{
49 return mModelName;
50}
51
52QString QgsProcessingModelAlgorithm::displayName() const
53{
54 return mModelName;
55}
56
57QString QgsProcessingModelAlgorithm::group() const
58{
59 return mModelGroup;
60}
61
62QString QgsProcessingModelAlgorithm::groupId() const
63{
64 return mModelGroupId;
65}
66
67QIcon QgsProcessingModelAlgorithm::icon() const
68{
69 return QgsApplication::getThemeIcon( QStringLiteral( "/processingModel.svg" ) );
70}
71
72QString QgsProcessingModelAlgorithm::svgIconPath() const
73{
74 return QgsApplication::iconPath( QStringLiteral( "processingModel.svg" ) );
75}
76
77QString QgsProcessingModelAlgorithm::shortHelpString() const
78{
79 if ( mHelpContent.empty() )
80 return QString();
81
82 return QgsProcessingUtils::formatHelpMapAsHtml( mHelpContent, this );
83}
84
85QString QgsProcessingModelAlgorithm::shortDescription() const
86{
87 return mHelpContent.value( QStringLiteral( "SHORT_DESCRIPTION" ) ).toString();
88}
89
90QString QgsProcessingModelAlgorithm::helpUrl() const
91{
92 return mHelpContent.value( QStringLiteral( "HELP_URL" ) ).toString();
93}
94
95QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const QgsProcessingModelChildAlgorithm &child, const QVariantMap &modelParameters, const QVariantMap &results, const QgsExpressionContext &expressionContext, QString &error ) const
96{
97 error.clear();
98 auto evaluateSources = [ =, &error ]( const QgsProcessingParameterDefinition * def )->QVariant
99 {
100 const QgsProcessingModelChildParameterSources paramSources = child.parameterSources().value( def->name() );
101
102 QString expressionText;
103 QVariantList paramParts;
104 for ( const QgsProcessingModelChildParameterSource &source : paramSources )
105 {
106 switch ( source.source() )
107 {
108 case QgsProcessingModelChildParameterSource::StaticValue:
109 paramParts << source.staticValue();
110 break;
111
112 case QgsProcessingModelChildParameterSource::ModelParameter:
113 paramParts << modelParameters.value( source.parameterName() );
114 break;
115
116 case QgsProcessingModelChildParameterSource::ChildOutput:
117 {
118 QVariantMap linkedChildResults = results.value( source.outputChildId() ).toMap();
119 paramParts << linkedChildResults.value( source.outputName() );
120 break;
121 }
122
123 case QgsProcessingModelChildParameterSource::Expression:
124 {
125 QgsExpression exp( source.expression() );
126 paramParts << exp.evaluate( &expressionContext );
127 if ( exp.hasEvalError() )
128 {
129 error = QObject::tr( "Could not evaluate expression for parameter %1 for %2: %3" ).arg( def->name(), child.description(), exp.evalErrorString() );
130 }
131 break;
132 }
133 case QgsProcessingModelChildParameterSource::ExpressionText:
134 {
135 expressionText = QgsExpression::replaceExpressionText( source.expressionText(), &expressionContext );
136 break;
137 }
138
139 case QgsProcessingModelChildParameterSource::ModelOutput:
140 break;
141 }
142 }
143
144 if ( ! expressionText.isEmpty() )
145 {
146 return expressionText;
147 }
148 else if ( paramParts.count() == 1 )
149 return paramParts.at( 0 );
150 else
151 return paramParts;
152 };
153
154
155 QVariantMap childParams;
156 const QList< const QgsProcessingParameterDefinition * > childParameterDefinitions = child.algorithm()->parameterDefinitions();
157 for ( const QgsProcessingParameterDefinition *def : childParameterDefinitions )
158 {
159 if ( !def->isDestination() )
160 {
161 if ( !child.parameterSources().contains( def->name() ) )
162 continue; // use default value
163
164 const QVariant value = evaluateSources( def );
165 childParams.insert( def->name(), value );
166 }
167 else
168 {
169 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
170
171 // is destination linked to one of the final outputs from this model?
172 bool isFinalOutput = false;
173 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
174 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
175 for ( ; outputIt != outputs.constEnd(); ++outputIt )
176 {
177 if ( outputIt->childOutputName() == destParam->name() )
178 {
179 QString paramName = child.childId() + ':' + outputIt.key();
180 bool foundParam = false;
181 QVariant value;
182
183 // if parameter was specified using child_id:child_name directly, take that
184 if ( modelParameters.contains( paramName ) )
185 {
186 value = modelParameters.value( paramName );
187 foundParam = true;
188 }
189
190 // ...otherwise we need to find the corresponding model parameter which matches this output
191 if ( !foundParam )
192 {
193 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
194 {
195 if ( modelParameters.contains( modelParam->name() ) )
196 {
197 value = modelParameters.value( modelParam->name() );
198 foundParam = true;
199 }
200 }
201 }
202
203 if ( foundParam )
204 {
205 if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
206 {
207 // make sure layer output name is correctly set
208 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
209 fromVar.destinationName = outputIt.key();
210 value = QVariant::fromValue( fromVar );
211 }
212
213 childParams.insert( destParam->name(), value );
214 }
215 isFinalOutput = true;
216 break;
217 }
218 }
219
220 bool hasExplicitDefinition = false;
221 if ( !isFinalOutput && child.parameterSources().contains( def->name() ) )
222 {
223 // explicitly defined source for output
224 const QVariant value = evaluateSources( def );
225 if ( value.isValid() )
226 {
227 childParams.insert( def->name(), value );
228 hasExplicitDefinition = true;
229 }
230 }
231
232 if ( !isFinalOutput && !hasExplicitDefinition )
233 {
234 // output is temporary
235
236 // check whether it's optional, and if so - is it required?
237 bool required = true;
239 {
240 required = childOutputIsRequired( child.childId(), destParam->name() );
241 }
242
243 // not optional, or required elsewhere in model
244 if ( required )
245 childParams.insert( destParam->name(), destParam->generateTemporaryDestination() );
246 }
247 }
248 }
249 return childParams;
250}
251
252const QgsProcessingParameterDefinition *QgsProcessingModelAlgorithm::modelParameterFromChildIdAndOutputName( const QString &childId, const QString &childOutputName ) const
253{
254 for ( const QgsProcessingParameterDefinition *definition : mParameters )
255 {
256 if ( !definition->isDestination() )
257 continue;
258
259 const QString modelChildId = definition->metadata().value( QStringLiteral( "_modelChildId" ) ).toString();
260 const QString modelOutputName = definition->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
261
262 if ( modelChildId == childId && modelOutputName == childOutputName )
263 return definition;
264 }
265 return nullptr;
266}
267
268bool QgsProcessingModelAlgorithm::childOutputIsRequired( const QString &childId, const QString &outputName ) const
269{
270 // look through all child algs
271 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
272 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
273 {
274 if ( childIt->childId() == childId || !childIt->isActive() )
275 continue;
276
277 // look through all sources for child
278 QMap<QString, QgsProcessingModelChildParameterSources> candidateChildParams = childIt->parameterSources();
279 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator childParamIt = candidateChildParams.constBegin();
280 for ( ; childParamIt != candidateChildParams.constEnd(); ++childParamIt )
281 {
282 const auto constValue = childParamIt.value();
283 for ( const QgsProcessingModelChildParameterSource &source : constValue )
284 {
285 if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
286 && source.outputChildId() == childId
287 && source.outputName() == outputName )
288 {
289 return true;
290 }
291 }
292 }
293 }
294 return false;
295}
296
297QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
298{
299 QSet< QString > toExecute;
300 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
301 QSet< QString > broken;
302 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
303 {
304 if ( childIt->isActive() )
305 {
306 if ( childIt->algorithm() )
307 toExecute.insert( childIt->childId() );
308 else
309 broken.insert( childIt->childId() );
310 }
311 }
312
313 if ( !broken.empty() )
314 throw QgsProcessingException( QCoreApplication::translate( "QgsProcessingModelAlgorithm", "Cannot run model, the following algorithms are not available on this system: %1" ).arg( qgsSetJoin( broken, QLatin1String( ", " ) ) ) );
315
316 QElapsedTimer totalTime;
317 totalTime.start();
318
319 QgsProcessingMultiStepFeedback modelFeedback( toExecute.count(), feedback );
320 QgsExpressionContext baseContext = createExpressionContext( parameters, context );
321
322 QVariantMap childResults;
323 QVariantMap childInputs;
324
325 const bool verboseLog = context.logLevel() == QgsProcessingContext::Verbose;
326
327 QVariantMap finalResults;
328 QSet< QString > executed;
329 bool executedAlg = true;
330 while ( executedAlg && executed.count() < toExecute.count() )
331 {
332 executedAlg = false;
333 for ( const QString &childId : std::as_const( toExecute ) )
334 {
335 if ( feedback && feedback->isCanceled() )
336 break;
337
338 if ( executed.contains( childId ) )
339 continue;
340
341 bool canExecute = true;
342 const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
343 for ( const QString &dependency : dependencies )
344 {
345 if ( !executed.contains( dependency ) )
346 {
347 canExecute = false;
348 break;
349 }
350 }
351
352 if ( !canExecute )
353 continue;
354
355 executedAlg = true;
356
357 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
358 std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
359
360 const bool skipGenericLogging = !verboseLog || childAlg->flags() & QgsProcessingAlgorithm::FlagSkipGenericModelLogging;
361 if ( feedback && !skipGenericLogging )
362 feedback->pushDebugInfo( QObject::tr( "Prepare algorithm: %1" ).arg( childId ) );
363
364 QgsExpressionContext expContext = baseContext;
365 expContext << QgsExpressionContextUtils::processingAlgorithmScope( child.algorithm(), parameters, context )
366 << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
367 context.setExpressionContext( expContext );
368
369 QString error;
370 QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error );
371 if ( !error.isEmpty() )
372 throw QgsProcessingException( error );
373
374 if ( feedback && !skipGenericLogging )
375 feedback->setProgressText( QObject::tr( "Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
376
377 childInputs.insert( childId, QgsProcessingUtils::removePointerValuesFromMap( childParams ) );
378 QStringList params;
379 for ( auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
380 {
381 params << QStringLiteral( "%1: %2" ).arg( childParamIt.key(),
382 child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
383 }
384
385 if ( feedback && !skipGenericLogging )
386 {
387 feedback->pushInfo( QObject::tr( "Input Parameters:" ) );
388 feedback->pushCommandInfo( QStringLiteral( "{ %1 }" ).arg( params.join( QLatin1String( ", " ) ) ) );
389 }
390
391 QElapsedTimer childTime;
392 childTime.start();
393
394 bool ok = false;
395
396 QThread *modelThread = QThread::currentThread();
397
398 auto prepareOnMainThread = [modelThread, &ok, &childAlg, &childParams, &context, &modelFeedback]
399 {
400 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->prepare() must be run on the main thread" );
401 ok = childAlg->prepare( childParams, context, &modelFeedback );
402 context.pushToThread( modelThread );
403 };
404
405 // Make sure we only run prepare steps on the main thread!
406 if ( modelThread == qApp->thread() )
407 ok = childAlg->prepare( childParams, context, &modelFeedback );
408 else
409 {
410 context.pushToThread( qApp->thread() );
411 QMetaObject::invokeMethod( qApp, prepareOnMainThread, Qt::BlockingQueuedConnection );
412 }
413
414 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
415
416 if ( !ok )
417 {
418 const QString error = ( childAlg->flags() & QgsProcessingAlgorithm::FlagCustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
419 throw QgsProcessingException( error );
420 }
421
422 QVariantMap results;
423 try
424 {
425 if ( childAlg->flags() & QgsProcessingAlgorithm::FlagNoThreading )
426 {
427 // child algorithm run step must be called on main thread
428 auto runOnMainThread = [modelThread, &context, &modelFeedback, &results, &childAlg, &childParams]
429 {
430 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->runPrepared() must be run on the main thread" );
431 results = childAlg->runPrepared( childParams, context, &modelFeedback );
432 context.pushToThread( modelThread );
433 };
434
435 if ( feedback && !skipGenericLogging && modelThread != qApp->thread() )
436 feedback->pushWarning( QObject::tr( "Algorithm “%1” cannot be run in a background thread, switching to main thread for this step" ).arg( childAlg->displayName() ) );
437
438 context.pushToThread( qApp->thread() );
439 QMetaObject::invokeMethod( qApp, runOnMainThread, Qt::BlockingQueuedConnection );
440 }
441 else
442 {
443 // safe to run on model thread
444 results = childAlg->runPrepared( childParams, context, &modelFeedback );
445 }
446 }
447 catch ( QgsProcessingException & )
448 {
449 const QString error = ( childAlg->flags() & QgsProcessingAlgorithm::FlagCustomException ) ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
450 throw QgsProcessingException( error );
451 }
452
453 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
454
455 QVariantMap ppRes;
456 auto postProcessOnMainThread = [modelThread, &ppRes, &childAlg, &context, &modelFeedback]
457 {
458 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "childAlg->postProcess() must be run on the main thread" );
459 ppRes = childAlg->postProcess( context, &modelFeedback );
460 context.pushToThread( modelThread );
461 };
462
463 // Make sure we only run postProcess steps on the main thread!
464 if ( modelThread == qApp->thread() )
465 ppRes = childAlg->postProcess( context, &modelFeedback );
466 else
467 {
468 context.pushToThread( qApp->thread() );
469 QMetaObject::invokeMethod( qApp, postProcessOnMainThread, Qt::BlockingQueuedConnection );
470 }
471
472 Q_ASSERT_X( QThread::currentThread() == context.thread(), "QgsProcessingModelAlgorithm::processAlgorithm", "context was not transferred back to model thread" );
473
474 if ( !ppRes.isEmpty() )
475 results = ppRes;
476
477 childResults.insert( childId, results );
478
479 // look through child alg's outputs to determine whether any of these should be copied
480 // to the final model outputs
481 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
482 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
483 for ( ; outputIt != outputs.constEnd(); ++outputIt )
484 {
485 switch ( mInternalVersion )
486 {
487 case QgsProcessingModelAlgorithm::InternalVersion::Version1:
488 finalResults.insert( childId + ':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
489 break;
490 case QgsProcessingModelAlgorithm::InternalVersion::Version2:
491 if ( const QgsProcessingParameterDefinition *modelParam = modelParameterFromChildIdAndOutputName( child.childId(), outputIt.key() ) )
492 {
493 finalResults.insert( modelParam->name(), results.value( outputIt->childOutputName() ) );
494 }
495 break;
496 }
497 }
498
499 executed.insert( childId );
500
501 std::function< void( const QString &, const QString & )> pruneAlgorithmBranchRecursive;
502 pruneAlgorithmBranchRecursive = [&]( const QString & id, const QString &branch = QString() )
503 {
504 const QSet<QString> toPrune = dependentChildAlgorithms( id, branch );
505 for ( const QString &targetId : toPrune )
506 {
507 if ( executed.contains( targetId ) )
508 continue;
509
510 executed.insert( targetId );
511 pruneAlgorithmBranchRecursive( targetId, branch );
512 }
513 };
514
515 // prune remaining algorithms if they are dependent on a branch from this child which didn't eventuate
516 const QgsProcessingOutputDefinitions outputDefs = childAlg->outputDefinitions();
517 for ( const QgsProcessingOutputDefinition *outputDef : outputDefs )
518 {
519 if ( outputDef->type() == QgsProcessingOutputConditionalBranch::typeName() && !results.value( outputDef->name() ).toBool() )
520 {
521 pruneAlgorithmBranchRecursive( childId, outputDef->name() );
522 }
523 }
524
526 {
527 // check if any dependent algorithms should be canceled based on the outputs of this algorithm run
528 // first find all direct dependencies of this algorithm by looking through all remaining child algorithms
529 for ( const QString &candidateId : std::as_const( toExecute ) )
530 {
531 if ( executed.contains( candidateId ) )
532 continue;
533
534 // a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
535 // algorithm's outputs
536 const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
537 const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
538 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
539 bool pruned = false;
540 for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
541 {
542 for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
543 {
544 if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
545 {
546 // ok, this one is dependent on the current alg. Did we get a value for it?
547 if ( !results.contains( source.outputName() ) )
548 {
549 // oh no, nothing returned for this parameter. Gotta trim the branch back!
550 pruned = true;
551 // skip the dependent alg..
552 executed.insert( candidateId );
553 //... and everything which depends on it
554 pruneAlgorithmBranchRecursive( candidateId, QString() );
555 break;
556 }
557 }
558 }
559 if ( pruned )
560 break;
561 }
562 }
563 }
564
565 childAlg.reset( nullptr );
566 modelFeedback.setCurrentStep( executed.count() );
567 if ( feedback && !skipGenericLogging )
568 feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
569 }
570
571 if ( feedback && feedback->isCanceled() )
572 break;
573 }
574 if ( feedback )
575 feedback->pushDebugInfo( QObject::tr( "Model processed OK. Executed %n algorithm(s) total in %1 s.", nullptr, executed.count() ).arg( totalTime.elapsed() / 1000.0 ) );
576
577 mResults = finalResults;
578 mResults.insert( QStringLiteral( "CHILD_RESULTS" ), childResults );
579 mResults.insert( QStringLiteral( "CHILD_INPUTS" ), childInputs );
580 return mResults;
581}
582
583QString QgsProcessingModelAlgorithm::sourceFilePath() const
584{
585 return mSourceFile;
586}
587
588void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
589{
590 mSourceFile = sourceFile;
591}
592
593bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath() const
594{
595 if ( mSourceFile.isEmpty() )
596 return false;
597
598 const QFileInfo fi( mSourceFile );
599 return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
600}
601
602QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const int indentSize ) const
603{
604 QStringList fileDocString;
605 fileDocString << QStringLiteral( "\"\"\"" );
606 fileDocString << QStringLiteral( "Model exported as python." );
607 fileDocString << QStringLiteral( "Name : %1" ).arg( displayName() );
608 fileDocString << QStringLiteral( "Group : %1" ).arg( group() );
609 fileDocString << QStringLiteral( "With QGIS : %1" ).arg( Qgis::versionInt() );
610 fileDocString << QStringLiteral( "\"\"\"" );
611 fileDocString << QString();
612
613 QStringList lines;
614 QString indent = QString( ' ' ).repeated( indentSize );
615 QString currentIndent;
616
617 QMap< QString, QString> friendlyChildNames;
618 QMap< QString, QString> friendlyOutputNames;
619 auto uniqueSafeName = []( const QString & name, bool capitalize, const QMap< QString, QString > &friendlyNames )->QString
620 {
621 const QString base = safeName( name, capitalize );
622 QString candidate = base;
623 int i = 1;
624 while ( friendlyNames.contains( candidate ) )
625 {
626 i++;
627 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
628 }
629 return candidate;
630 };
631
632 const QString algorithmClassName = safeName( name(), true );
633
634 QSet< QString > toExecute;
635 for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
636 {
637 if ( childIt->isActive() && childIt->algorithm() )
638 {
639 toExecute.insert( childIt->childId() );
640 friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
641 }
642 }
643 const int totalSteps = toExecute.count();
644
645 QStringList importLines; // not a set - we need regular ordering
646 switch ( outputType )
647 {
649 {
650 // add specific parameter type imports
651 const auto params = parameterDefinitions();
652 importLines.reserve( params.count() + 3 );
653 importLines << QStringLiteral( "from qgis.core import QgsProcessing" );
654 importLines << QStringLiteral( "from qgis.core import QgsProcessingAlgorithm" );
655 importLines << QStringLiteral( "from qgis.core import QgsProcessingMultiStepFeedback" );
656
657 bool hasAdvancedParams = false;
658 for ( const QgsProcessingParameterDefinition *def : params )
659 {
661 hasAdvancedParams = true;
662
663 const QString importString = QgsApplication::processingRegistry()->parameterType( def->type() )->pythonImportString();
664 if ( !importString.isEmpty() && !importLines.contains( importString ) )
665 importLines << importString;
666 }
667
668 if ( hasAdvancedParams )
669 importLines << QStringLiteral( "from qgis.core import QgsProcessingParameterDefinition" );
670
671 lines << QStringLiteral( "import processing" );
672 lines << QString() << QString();
673
674 lines << QStringLiteral( "class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
675 lines << QString();
676
677 // initAlgorithm, parameter definitions
678 lines << indent + QStringLiteral( "def initAlgorithm(self, config=None):" );
679 if ( params.empty() )
680 {
681 lines << indent + indent + QStringLiteral( "pass" );
682 }
683 else
684 {
685 lines.reserve( lines.size() + params.size() );
686 for ( const QgsProcessingParameterDefinition *def : params )
687 {
688 std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
689
690 if ( defClone->isDestination() )
691 {
692 const QString uniqueChildName = defClone->metadata().value( QStringLiteral( "_modelChildId" ) ).toString() + ':' + defClone->metadata().value( QStringLiteral( "_modelChildOutputName" ) ).toString();
693 const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), true, friendlyOutputNames ) : defClone->name();
694 friendlyOutputNames.insert( uniqueChildName, friendlyName );
695 defClone->setName( friendlyName );
696 }
697 else
698 {
699 if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
700 lines << indent + indent + QStringLiteral( "# %1" ).arg( mParameterComponents.value( defClone->name() ).comment()->description() );
701 }
702
703 if ( defClone->flags() & QgsProcessingParameterDefinition::FlagAdvanced )
704 {
705 lines << indent + indent + QStringLiteral( "param = %1" ).arg( defClone->asPythonString() );
706 lines << indent + indent + QStringLiteral( "param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
707 lines << indent + indent + QStringLiteral( "self.addParameter(param)" );
708 }
709 else
710 {
711 lines << indent + indent + QStringLiteral( "self.addParameter(%1)" ).arg( defClone->asPythonString() );
712 }
713 }
714 }
715
716 lines << QString();
717 lines << indent + QStringLiteral( "def processAlgorithm(self, parameters, context, model_feedback):" );
718 currentIndent = indent + indent;
719
720 lines << currentIndent + QStringLiteral( "# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
721 lines << currentIndent + QStringLiteral( "# overall progress through the model" );
722 lines << currentIndent + QStringLiteral( "feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
723 break;
724 }
725#if 0
726 case Script:
727 {
728 QgsStringMap params;
729 QgsProcessingContext context;
730 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
731 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
732 {
733 QString name = paramIt.value().parameterName();
734 if ( parameterDefinition( name ) )
735 {
736 // TODO - generic value to string method
737 params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
738 }
739 }
740
741 if ( !params.isEmpty() )
742 {
743 lines << QStringLiteral( "parameters = {" );
744 for ( auto it = params.constBegin(); it != params.constEnd(); ++it )
745 {
746 lines << QStringLiteral( " '%1':%2," ).arg( it.key(), it.value() );
747 }
748 lines << QStringLiteral( "}" )
749 << QString();
750 }
751
752 lines << QStringLiteral( "context = QgsProcessingContext()" )
753 << QStringLiteral( "context.setProject(QgsProject.instance())" )
754 << QStringLiteral( "feedback = QgsProcessingFeedback()" )
755 << QString();
756
757 break;
758 }
759#endif
760
761 }
762
763 lines << currentIndent + QStringLiteral( "results = {}" );
764 lines << currentIndent + QStringLiteral( "outputs = {}" );
765 lines << QString();
766
767 QSet< QString > executed;
768 bool executedAlg = true;
769 int currentStep = 0;
770 while ( executedAlg && executed.count() < toExecute.count() )
771 {
772 executedAlg = false;
773 const auto constToExecute = toExecute;
774 for ( const QString &childId : constToExecute )
775 {
776 if ( executed.contains( childId ) )
777 continue;
778
779 bool canExecute = true;
780 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
781 for ( const QString &dependency : constDependsOnChildAlgorithms )
782 {
783 if ( !executed.contains( dependency ) )
784 {
785 canExecute = false;
786 break;
787 }
788 }
789
790 if ( !canExecute )
791 continue;
792
793 executedAlg = true;
794
795 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
796
797 // fill in temporary outputs
798 const QgsProcessingParameterDefinitions childDefs = child.algorithm()->parameterDefinitions();
799 QgsStringMap childParams;
800 for ( const QgsProcessingParameterDefinition *def : childDefs )
801 {
802 if ( def->isDestination() )
803 {
804 const QgsProcessingDestinationParameter *destParam = static_cast< const QgsProcessingDestinationParameter * >( def );
805
806 // is destination linked to one of the final outputs from this model?
807 bool isFinalOutput = false;
808 QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
809 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
810 for ( ; outputIt != outputs.constEnd(); ++outputIt )
811 {
812 if ( outputIt->childOutputName() == destParam->name() )
813 {
814 QString paramName = child.childId() + ':' + outputIt.key();
815 paramName = friendlyOutputNames.value( paramName, paramName );
816 childParams.insert( destParam->name(), QStringLiteral( "parameters['%1']" ).arg( paramName ) );
817 isFinalOutput = true;
818 break;
819 }
820 }
821
822 if ( !isFinalOutput )
823 {
824 // output is temporary
825
826 // check whether it's optional, and if so - is it required?
827 bool required = true;
829 {
830 required = childOutputIsRequired( child.childId(), destParam->name() );
831 }
832
833 // not optional, or required elsewhere in model
834 if ( required )
835 {
836
837 childParams.insert( destParam->name(), QStringLiteral( "QgsProcessing.TEMPORARY_OUTPUT" ) );
838 }
839 }
840 }
841 }
842
843 lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
844 currentStep++;
845 if ( currentStep < totalSteps )
846 {
847 lines << QString();
848 lines << currentIndent + QStringLiteral( "feedback.setCurrentStep(%1)" ).arg( currentStep );
849 lines << currentIndent + QStringLiteral( "if feedback.isCanceled():" );
850 lines << currentIndent + indent + QStringLiteral( "return {}" );
851 lines << QString();
852 }
853 executed.insert( childId );
854 }
855 }
856
857 switch ( outputType )
858 {
860 lines << currentIndent + QStringLiteral( "return results" );
861 lines << QString();
862
863 // name, displayName
864 lines << indent + QStringLiteral( "def name(self):" );
865 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
866 lines << QString();
867 lines << indent + QStringLiteral( "def displayName(self):" );
868 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelName );
869 lines << QString();
870
871 // group, groupId
872 lines << indent + QStringLiteral( "def group(self):" );
873 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroup );
874 lines << QString();
875 lines << indent + QStringLiteral( "def groupId(self):" );
876 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( mModelGroupId );
877 lines << QString();
878
879 // help
880 if ( !shortHelpString().isEmpty() )
881 {
882 lines << indent + QStringLiteral( "def shortHelpString(self):" );
883 lines << indent + indent + QStringLiteral( "return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
884 lines << QString();
885 }
886 if ( !helpUrl().isEmpty() )
887 {
888 lines << indent + QStringLiteral( "def helpUrl(self):" );
889 lines << indent + indent + QStringLiteral( "return '%1'" ).arg( helpUrl() );
890 lines << QString();
891 }
892
893 // createInstance
894 lines << indent + QStringLiteral( "def createInstance(self):" );
895 lines << indent + indent + QStringLiteral( "return %1()" ).arg( algorithmClassName );
896
897 // additional import lines
898 static QMap< QString, QString > sAdditionalImports
899 {
900 { QStringLiteral( "QgsCoordinateReferenceSystem" ), QStringLiteral( "from qgis.core import QgsCoordinateReferenceSystem" ) },
901 { QStringLiteral( "QgsExpression" ), QStringLiteral( "from qgis.core import QgsExpression" ) },
902 { QStringLiteral( "QgsRectangle" ), QStringLiteral( "from qgis.core import QgsRectangle" ) },
903 { QStringLiteral( "QgsReferencedRectangle" ), QStringLiteral( "from qgis.core import QgsReferencedRectangle" ) },
904 { QStringLiteral( "QgsPoint" ), QStringLiteral( "from qgis.core import QgsPoint" ) },
905 { QStringLiteral( "QgsReferencedPoint" ), QStringLiteral( "from qgis.core import QgsReferencedPoint" ) },
906 { QStringLiteral( "QgsProperty" ), QStringLiteral( "from qgis.core import QgsProperty" ) },
907 { QStringLiteral( "QgsRasterLayer" ), QStringLiteral( "from qgis.core import QgsRasterLayer" ) },
908 { QStringLiteral( "QgsMeshLayer" ), QStringLiteral( "from qgis.core import QgsMeshLayer" ) },
909 { QStringLiteral( "QgsVectorLayer" ), QStringLiteral( "from qgis.core import QgsVectorLayer" ) },
910 { QStringLiteral( "QgsMapLayer" ), QStringLiteral( "from qgis.core import QgsMapLayer" ) },
911 { QStringLiteral( "QgsProcessingFeatureSourceDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
912 { QStringLiteral( "QgsPointXY" ), QStringLiteral( "from qgis.core import QgsPointXY" ) },
913 { QStringLiteral( "QgsReferencedPointXY" ), QStringLiteral( "from qgis.core import QgsReferencedPointXY" ) },
914 { QStringLiteral( "QgsGeometry" ), QStringLiteral( "from qgis.core import QgsGeometry" ) },
915 { QStringLiteral( "QgsProcessingOutputLayerDefinition" ), QStringLiteral( "from qgis.core import QgsProcessingOutputLayerDefinition" ) },
916 { QStringLiteral( "QColor" ), QStringLiteral( "from qgis.PyQt.QtGui import QColor" ) },
917 { QStringLiteral( "QDateTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QDateTime" ) },
918 { QStringLiteral( "QDate" ), QStringLiteral( "from qgis.PyQt.QtCore import QDate" ) },
919 { QStringLiteral( "QTime" ), QStringLiteral( "from qgis.PyQt.QtCore import QTime" ) },
920 };
921
922 for ( auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
923 {
924 if ( importLines.contains( it.value() ) )
925 {
926 // already got this import
927 continue;
928 }
929
930 bool found = false;
931 for ( const QString &line : std::as_const( lines ) )
932 {
933 if ( line.contains( it.key() ) )
934 {
935 found = true;
936 break;
937 }
938 }
939 if ( found )
940 {
941 importLines << it.value();
942 }
943 }
944
945 lines = fileDocString + importLines + lines;
946 break;
947 }
948
949 lines << QString();
950
951 return lines;
952}
953
954QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
955{
956 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
957
958 auto safeName = []( const QString & name )->QString
959 {
960 QString s = name;
961 return s.replace( QRegularExpression( QStringLiteral( "[\\s'\"\\(\\):\\.]" ) ), QStringLiteral( "_" ) );
962 };
963
964 // "static"/single value sources
965 QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
991 QStringList() << QgsProcessingOutputNumber::typeName()
994
995 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
996 {
997 QString name;
998 QVariant value;
999 QString description;
1000 switch ( source.source() )
1001 {
1002 case QgsProcessingModelChildParameterSource::ModelParameter:
1003 {
1004 name = source.parameterName();
1005 value = modelParameters.value( source.parameterName() );
1006 description = parameterDefinition( source.parameterName() )->description();
1007 break;
1008 }
1009 case QgsProcessingModelChildParameterSource::ChildOutput:
1010 {
1011 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1012 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1013 source.outputChildId() : child.description(), source.outputName() );
1014 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1015 {
1016 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1017 child.description() );
1018 }
1019 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1020 break;
1021 }
1022
1023 case QgsProcessingModelChildParameterSource::Expression:
1024 case QgsProcessingModelChildParameterSource::ExpressionText:
1025 case QgsProcessingModelChildParameterSource::StaticValue:
1026 case QgsProcessingModelChildParameterSource::ModelOutput:
1027 continue;
1028 }
1029 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1030 }
1031
1032 // layer sources
1033 sources = availableSourcesForChild( childId, QStringList()
1039
1040 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1041 {
1042 QString name;
1043 QVariant value;
1044 QString description;
1045
1046 switch ( source.source() )
1047 {
1048 case QgsProcessingModelChildParameterSource::ModelParameter:
1049 {
1050 name = source.parameterName();
1051 value = modelParameters.value( source.parameterName() );
1052 description = parameterDefinition( source.parameterName() )->description();
1053 break;
1054 }
1055 case QgsProcessingModelChildParameterSource::ChildOutput:
1056 {
1057 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1058 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1059 source.outputChildId() : child.description(), source.outputName() );
1060 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1061 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1062 {
1063 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1064 child.description() );
1065 }
1066 break;
1067 }
1068
1069 case QgsProcessingModelChildParameterSource::Expression:
1070 case QgsProcessingModelChildParameterSource::ExpressionText:
1071 case QgsProcessingModelChildParameterSource::StaticValue:
1072 case QgsProcessingModelChildParameterSource::ModelOutput:
1073 continue;
1074
1075 }
1076
1077 if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
1078 {
1079 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1080 value = fromVar.sink;
1081 if ( value.userType() == QMetaType::type( "QgsProperty" ) )
1082 {
1083 value = value.value< QgsProperty >().valueAsString( context.expressionContext() );
1084 }
1085 }
1086 QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
1087 if ( !layer )
1088 layer = QgsProcessingUtils::mapLayerFromString( value.toString(), context );
1089
1090 variables.insert( safeName( name ), VariableDefinition( QVariant::fromValue( QgsWeakMapLayerPointer( layer ) ), source, description ) );
1091 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1092 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1093 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1094 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1095 }
1096
1097 sources = availableSourcesForChild( childId, QStringList()
1099 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
1100 {
1101 QString name;
1102 QVariant value;
1103 QString description;
1104
1105 switch ( source.source() )
1106 {
1107 case QgsProcessingModelChildParameterSource::ModelParameter:
1108 {
1109 name = source.parameterName();
1110 value = modelParameters.value( source.parameterName() );
1111 description = parameterDefinition( source.parameterName() )->description();
1112 break;
1113 }
1114 case QgsProcessingModelChildParameterSource::ChildOutput:
1115 {
1116 const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
1117 name = QStringLiteral( "%1_%2" ).arg( child.description().isEmpty() ?
1118 source.outputChildId() : child.description(), source.outputName() );
1119 value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
1120 if ( const QgsProcessingAlgorithm *alg = child.algorithm() )
1121 {
1122 description = QObject::tr( "Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
1123 child.description() );
1124 }
1125 break;
1126 }
1127
1128 case QgsProcessingModelChildParameterSource::Expression:
1129 case QgsProcessingModelChildParameterSource::ExpressionText:
1130 case QgsProcessingModelChildParameterSource::StaticValue:
1131 case QgsProcessingModelChildParameterSource::ModelOutput:
1132 continue;
1133
1134 }
1135
1136 QgsFeatureSource *featureSource = nullptr;
1137 if ( value.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) )
1138 {
1139 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
1140 value = fromVar.source;
1141 }
1142 else if ( value.userType() == QMetaType::type( "QgsProcessingOutputLayerDefinition" ) )
1143 {
1144 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
1145 value = fromVar.sink;
1146 if ( value.userType() == QMetaType::type( "QgsProperty" ) )
1147 {
1148 value = value.value< QgsProperty >().valueAsString( context.expressionContext() );
1149 }
1150 }
1151 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
1152 {
1153 featureSource = layer;
1154 }
1155 if ( !featureSource )
1156 {
1157 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( value.toString(), context, true, QgsProcessingUtils::LayerHint::Vector ) ) )
1158 featureSource = vl;
1159 }
1160
1161 variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
1162 variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source, QObject::tr( "Minimum X of %1" ).arg( description ) ) );
1163 variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source, QObject::tr( "Minimum Y of %1" ).arg( description ) ) );
1164 variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source, QObject::tr( "Maximum X of %1" ).arg( description ) ) );
1165 variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source, QObject::tr( "Maximum Y of %1" ).arg( description ) ) );
1166 }
1167
1168 return variables;
1169}
1170
1171QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
1172{
1173 std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QStringLiteral( "algorithm_inputs" ) ) );
1174 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, context, modelParameters, results );
1175 QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
1176 for ( ; varIt != variables.constEnd(); ++varIt )
1177 {
1178 scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true, false, varIt->description ) );
1179 }
1180 return scope.release();
1181}
1182
1183QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( const QString &childId, const QStringList &parameterTypes, const QStringList &outputTypes, const QList<int> &dataTypes ) const
1184{
1185 QgsProcessingModelChildParameterSources sources;
1186
1187 // first look through model parameters
1188 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1189 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1190 {
1191 const QgsProcessingParameterDefinition *def = parameterDefinition( paramIt->parameterName() );
1192 if ( !def )
1193 continue;
1194
1195 if ( parameterTypes.contains( def->type() ) )
1196 {
1197 if ( !dataTypes.isEmpty() )
1198 {
1200 {
1201 const QgsProcessingParameterField *fieldDef = static_cast< const QgsProcessingParameterField * >( def );
1202 if ( !( dataTypes.contains( fieldDef->dataType() ) || fieldDef->dataType() == QgsProcessingParameterField::Any ) )
1203 {
1204 continue;
1205 }
1206 }
1208 {
1209 const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def );
1210 if ( !sourceDef )
1211 continue;
1212
1213 bool ok = sourceDef->dataTypes().isEmpty();
1214 const auto constDataTypes = sourceDef->dataTypes();
1215 for ( int type : constDataTypes )
1216 {
1217 if ( dataTypes.contains( type ) || type == QgsProcessing::TypeMapLayer || type == QgsProcessing::TypeVector || type == QgsProcessing::TypeVectorAnyGeometry )
1218 {
1219 ok = true;
1220 break;
1221 }
1222 }
1223 if ( dataTypes.contains( QgsProcessing::TypeMapLayer ) || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) )
1224 ok = true;
1225
1226 if ( !ok )
1227 continue;
1228 }
1229 }
1230 sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
1231 }
1232 }
1233
1234 QSet< QString > dependents;
1235 if ( !childId.isEmpty() )
1236 {
1237 dependents = dependentChildAlgorithms( childId );
1238 dependents << childId;
1239 }
1240
1241 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1242 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1243 {
1244 if ( dependents.contains( childIt->childId() ) )
1245 continue;
1246
1247 const QgsProcessingAlgorithm *alg = childIt->algorithm();
1248 if ( !alg )
1249 continue;
1250
1251 const auto constOutputDefinitions = alg->outputDefinitions();
1252 for ( const QgsProcessingOutputDefinition *out : constOutputDefinitions )
1253 {
1254 if ( outputTypes.contains( out->type() ) )
1255 {
1256 if ( !dataTypes.isEmpty() )
1257 {
1258 if ( out->type() == QgsProcessingOutputVectorLayer::typeName() )
1259 {
1260 const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out );
1261
1262 if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->dataType() ) )
1263 {
1264 //unacceptable output
1265 continue;
1266 }
1267 }
1268 }
1269 sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
1270 }
1271 }
1272 }
1273
1274 return sources;
1275}
1276
1277QVariantMap QgsProcessingModelAlgorithm::helpContent() const
1278{
1279 return mHelpContent;
1280}
1281
1282void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
1283{
1284 mHelpContent = helpContent;
1285}
1286
1287void QgsProcessingModelAlgorithm::setName( const QString &name )
1288{
1289 mModelName = name;
1290}
1291
1292void QgsProcessingModelAlgorithm::setGroup( const QString &group )
1293{
1294 mModelGroup = group;
1295}
1296
1297bool QgsProcessingModelAlgorithm::validate( QStringList &issues ) const
1298{
1299 issues.clear();
1300 bool res = true;
1301
1302 if ( mChildAlgorithms.empty() )
1303 {
1304 res = false;
1305 issues << QObject::tr( "Model does not contain any algorithms" );
1306 }
1307
1308 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
1309 {
1310 QStringList childIssues;
1311 res = validateChildAlgorithm( it->childId(), childIssues ) && res;
1312
1313 for ( const QString &issue : std::as_const( childIssues ) )
1314 {
1315 issues << QStringLiteral( "<b>%1</b>: %2" ).arg( it->description(), issue );
1316 }
1317 }
1318 return res;
1319}
1320
1321QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
1322{
1323 return mChildAlgorithms;
1324}
1325
1326void QgsProcessingModelAlgorithm::setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents )
1327{
1328 mParameterComponents = parameterComponents;
1329}
1330
1331void QgsProcessingModelAlgorithm::setParameterComponent( const QgsProcessingModelParameter &component )
1332{
1333 mParameterComponents.insert( component.parameterName(), component );
1334}
1335
1336QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( const QString &name )
1337{
1338 if ( !mParameterComponents.contains( name ) )
1339 {
1340 QgsProcessingModelParameter &component = mParameterComponents[ name ];
1341 component.setParameterName( name );
1342 return component;
1343 }
1344 return mParameterComponents[ name ];
1345}
1346
1347QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters() const
1348{
1349 QList< QgsProcessingModelParameter > res;
1350 QSet< QString > found;
1351 for ( const QString &parameter : mParameterOrder )
1352 {
1353 if ( mParameterComponents.contains( parameter ) )
1354 {
1355 res << mParameterComponents.value( parameter );
1356 found << parameter;
1357 }
1358 }
1359
1360 // add any missing ones to end of list
1361 for ( auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
1362 {
1363 if ( !found.contains( it.key() ) )
1364 {
1365 res << it.value();
1366 }
1367 }
1368 return res;
1369}
1370
1371void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
1372{
1373 mParameterOrder = order;
1374}
1375
1376void QgsProcessingModelAlgorithm::updateDestinationParameters()
1377{
1378 //delete existing destination parameters
1379 QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
1380 while ( it.hasNext() )
1381 {
1382 const QgsProcessingParameterDefinition *def = it.next();
1383 if ( def->isDestination() )
1384 {
1385 delete def;
1386 it.remove();
1387 }
1388 }
1389 // also delete outputs
1390 qDeleteAll( mOutputs );
1391 mOutputs.clear();
1392
1393 // rebuild
1394 QSet< QString > usedFriendlyNames;
1395 auto uniqueSafeName = [&usedFriendlyNames ]( const QString & name )->QString
1396 {
1397 const QString base = safeName( name, false );
1398 QString candidate = base;
1399 int i = 1;
1400 while ( usedFriendlyNames.contains( candidate ) )
1401 {
1402 i++;
1403 candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
1404 }
1405 usedFriendlyNames.insert( candidate );
1406 return candidate;
1407 };
1408
1409 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1410 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1411 {
1412 QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
1413 QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
1414 for ( ; outputIt != outputs.constEnd(); ++outputIt )
1415 {
1416 if ( !childIt->isActive() || !childIt->algorithm() )
1417 continue;
1418
1419 // child algorithm has a destination parameter set, copy it to the model
1420 const QgsProcessingParameterDefinition *source = childIt->algorithm()->parameterDefinition( outputIt->childOutputName() );
1421 if ( !source )
1422 continue;
1423
1424 std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() );
1425 // Even if an output was hidden in a child algorithm, we want to show it here for the final
1426 // outputs.
1427 param->setFlags( param->flags() & ~QgsProcessingParameterDefinition::FlagHidden );
1428 if ( outputIt->isMandatory() )
1429 param->setFlags( param->flags() & ~QgsProcessingParameterDefinition::FlagOptional );
1430 if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
1431 {
1432 QString friendlyName = uniqueSafeName( outputIt->description() );
1433 param->setName( friendlyName );
1434 }
1435 else
1436 {
1437 param->setName( outputIt->childId() + ':' + outputIt->name() );
1438 }
1439 // add some metadata so we can easily link this parameter back to the child source
1440 param->metadata().insert( QStringLiteral( "_modelChildId" ), outputIt->childId() );
1441 param->metadata().insert( QStringLiteral( "_modelChildOutputName" ), outputIt->name() );
1442 param->metadata().insert( QStringLiteral( "_modelChildProvider" ), childIt->algorithm()->provider() ? childIt->algorithm()->provider()->id() : QString() );
1443
1444 param->setDescription( outputIt->description() );
1445 param->setDefaultValue( outputIt->defaultValue() );
1446
1447 QgsProcessingDestinationParameter *newDestParam = dynamic_cast< QgsProcessingDestinationParameter * >( param.get() );
1448 if ( addParameter( param.release() ) && newDestParam )
1449 {
1450 if ( QgsProcessingProvider *provider = childIt->algorithm()->provider() )
1451 {
1452 // we need to copy the constraints given by the provider which creates this output across
1453 // and replace those which have been set to match the model provider's constraints
1454 newDestParam->setSupportsNonFileBasedOutput( provider->supportsNonFileBasedOutput() );
1455 newDestParam->mOriginalProvider = provider;
1456 }
1457 }
1458 }
1459 }
1460}
1461
1462void QgsProcessingModelAlgorithm::addGroupBox( const QgsProcessingModelGroupBox &groupBox )
1463{
1464 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1465}
1466
1467QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes() const
1468{
1469 return mGroupBoxes.values();
1470}
1471
1472void QgsProcessingModelAlgorithm::removeGroupBox( const QString &uuid )
1473{
1474 mGroupBoxes.remove( uuid );
1475}
1476
1477QVariant QgsProcessingModelAlgorithm::toVariant() const
1478{
1479 QVariantMap map;
1480 map.insert( QStringLiteral( "model_name" ), mModelName );
1481 map.insert( QStringLiteral( "model_group" ), mModelGroup );
1482 map.insert( QStringLiteral( "help" ), mHelpContent );
1483 map.insert( QStringLiteral( "internal_version" ), qgsEnumValueToKey( mInternalVersion ) );
1484
1485 QVariantMap childMap;
1486 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1487 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1488 {
1489 childMap.insert( childIt.key(), childIt.value().toVariant() );
1490 }
1491 map.insert( QStringLiteral( "children" ), childMap );
1492
1493 QVariantMap paramMap;
1494 QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
1495 for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
1496 {
1497 paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
1498 }
1499 map.insert( QStringLiteral( "parameters" ), paramMap );
1500
1501 QVariantMap paramDefMap;
1502 for ( const QgsProcessingParameterDefinition *def : mParameters )
1503 {
1504 paramDefMap.insert( def->name(), def->toVariantMap() );
1505 }
1506 map.insert( QStringLiteral( "parameterDefinitions" ), paramDefMap );
1507
1508 QVariantList groupBoxDefs;
1509 for ( auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
1510 {
1511 groupBoxDefs.append( it.value().toVariant() );
1512 }
1513 map.insert( QStringLiteral( "groupBoxes" ), groupBoxDefs );
1514
1515 map.insert( QStringLiteral( "modelVariables" ), mVariables );
1516
1517 map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
1518
1519 map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
1520
1521 return map;
1522}
1523
1524bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
1525{
1526 QVariantMap map = model.toMap();
1527
1528 mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
1529 mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
1530 mModelGroupId = map.value( QStringLiteral( "model_group" ) ).toString();
1531 mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
1532
1533 mInternalVersion = qgsEnumKeyToValue( map.value( QStringLiteral( "internal_version" ) ).toString(), InternalVersion::Version1 );
1534
1535 mVariables = map.value( QStringLiteral( "modelVariables" ) ).toMap();
1536 mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
1537
1538 mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
1539
1540 mChildAlgorithms.clear();
1541 QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
1542 QVariantMap::const_iterator childIt = childMap.constBegin();
1543 for ( ; childIt != childMap.constEnd(); ++childIt )
1544 {
1545 QgsProcessingModelChildAlgorithm child;
1546 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1547 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1548 // with no way for users to repair them
1549 if ( !child.loadVariant( childIt.value() ) )
1550 continue;
1551
1552 mChildAlgorithms.insert( child.childId(), child );
1553 }
1554
1555 mParameterComponents.clear();
1556 QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
1557 QVariantMap::const_iterator paramIt = paramMap.constBegin();
1558 for ( ; paramIt != paramMap.constEnd(); ++paramIt )
1559 {
1560 QgsProcessingModelParameter param;
1561 if ( !param.loadVariant( paramIt.value().toMap() ) )
1562 return false;
1563
1564 mParameterComponents.insert( param.parameterName(), param );
1565 }
1566
1567 qDeleteAll( mParameters );
1568 mParameters.clear();
1569 QVariantMap paramDefMap = map.value( QStringLiteral( "parameterDefinitions" ) ).toMap();
1570
1571 auto addParam = [ = ]( const QVariant & value )
1572 {
1573 std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( value.toMap() ) );
1574 // we be lenient here - even if we couldn't load a parameter, don't interrupt the model loading
1575 // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters)
1576 // with no way for users to repair them
1577 if ( param )
1578 {
1579 if ( param->name() == QLatin1String( "VERBOSE_LOG" ) )
1580 return; // internal parameter -- some versions of QGIS incorrectly stored this in the model definition file
1581
1582 // set parameter help from help content
1583 param->setHelp( mHelpContent.value( param->name() ).toString() );
1584
1585 // add parameter
1586 addParameter( param.release() );
1587 }
1588 else
1589 {
1590 QVariantMap map = value.toMap();
1591 QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
1592 QString name = map.value( QStringLiteral( "name" ) ).toString();
1593
1594 QgsMessageLog::logMessage( QCoreApplication::translate( "Processing", "Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( "Processing", "Processing" ) );
1595 }
1596 };
1597
1598 QSet< QString > loadedParams;
1599 // first add parameters respecting mParameterOrder
1600 for ( const QString &name : std::as_const( mParameterOrder ) )
1601 {
1602 if ( paramDefMap.contains( name ) )
1603 {
1604 addParam( paramDefMap.value( name ) );
1605 loadedParams << name;
1606 }
1607 }
1608 // then load any remaining parameters
1609 QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
1610 for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
1611 {
1612 if ( !loadedParams.contains( paramDefIt.key() ) )
1613 addParam( paramDefIt.value() );
1614 }
1615
1616 mGroupBoxes.clear();
1617 const QVariantList groupBoxList = map.value( QStringLiteral( "groupBoxes" ) ).toList();
1618 for ( const QVariant &groupBoxDef : groupBoxList )
1619 {
1620 QgsProcessingModelGroupBox groupBox;
1621 groupBox.loadVariant( groupBoxDef.toMap() );
1622 mGroupBoxes.insert( groupBox.uuid(), groupBox );
1623 }
1624
1625 updateDestinationParameters();
1626
1627 return true;
1628}
1629
1630bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, QgsProcessing::SourceType outputType )
1631{
1632 // This method is intended to be "permissive" rather than "restrictive".
1633 // I.e. we only reject outputs which we know can NEVER be acceptable, but
1634 // if there's doubt then we default to returning true.
1635 return ( acceptableDataTypes.empty()
1636 || acceptableDataTypes.contains( outputType )
1637 || outputType == QgsProcessing::TypeMapLayer
1638 || outputType == QgsProcessing::TypeVector
1640 || acceptableDataTypes.contains( QgsProcessing::TypeVector )
1641 || acceptableDataTypes.contains( QgsProcessing::TypeMapLayer )
1642 || ( acceptableDataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) && ( outputType == QgsProcessing::TypeVectorPoint ||
1643 outputType == QgsProcessing::TypeVectorLine ||
1644 outputType == QgsProcessing::TypeVectorPolygon ) ) );
1645}
1646
1647void QgsProcessingModelAlgorithm::reattachAlgorithms() const
1648{
1649 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1650 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1651 {
1652 if ( !childIt->algorithm() )
1653 childIt->reattach();
1654 }
1655}
1656
1657bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
1658{
1659 QDomDocument doc = QDomDocument( QStringLiteral( "model" ) );
1660 QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
1661 doc.appendChild( elem );
1662
1663 QFile file( path );
1664 if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
1665 {
1666 QTextStream stream( &file );
1667 doc.save( stream, 2 );
1668 file.close();
1669 return true;
1670 }
1671 return false;
1672}
1673
1674bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
1675{
1676 QDomDocument doc;
1677
1678 QFile file( path );
1679 if ( file.open( QFile::ReadOnly ) )
1680 {
1681 if ( !doc.setContent( &file ) )
1682 return false;
1683
1684 file.close();
1685 }
1686 else
1687 {
1688 return false;
1689 }
1690
1691 QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
1692 return loadVariant( props );
1693}
1694
1695void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
1696{
1697 mChildAlgorithms = childAlgorithms;
1698 updateDestinationParameters();
1699}
1700
1701void QgsProcessingModelAlgorithm::setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm )
1702{
1703 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1704 updateDestinationParameters();
1705}
1706
1707QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm )
1708{
1709 if ( algorithm.childId().isEmpty() || mChildAlgorithms.contains( algorithm.childId() ) )
1710 algorithm.generateChildId( *this );
1711
1712 mChildAlgorithms.insert( algorithm.childId(), algorithm );
1713 updateDestinationParameters();
1714 return algorithm.childId();
1715}
1716
1717QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( const QString &childId )
1718{
1719 return mChildAlgorithms[ childId ];
1720}
1721
1722bool QgsProcessingModelAlgorithm::removeChildAlgorithm( const QString &id )
1723{
1724 if ( !dependentChildAlgorithms( id ).isEmpty() )
1725 return false;
1726
1727 mChildAlgorithms.remove( id );
1728 updateDestinationParameters();
1729 return true;
1730}
1731
1732void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( const QString &id )
1733{
1734 const auto constDependentChildAlgorithms = dependentChildAlgorithms( id );
1735 for ( const QString &child : constDependentChildAlgorithms )
1736 {
1737 childAlgorithm( child ).setActive( false );
1738 }
1739 childAlgorithm( id ).setActive( false );
1740 updateDestinationParameters();
1741}
1742
1743bool QgsProcessingModelAlgorithm::activateChildAlgorithm( const QString &id )
1744{
1745 const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( id );
1746 for ( const QString &child : constDependsOnChildAlgorithms )
1747 {
1748 if ( !childAlgorithm( child ).isActive() )
1749 return false;
1750 }
1751 childAlgorithm( id ).setActive( true );
1752 updateDestinationParameters();
1753 return true;
1754}
1755
1756void QgsProcessingModelAlgorithm::addModelParameter( QgsProcessingParameterDefinition *definition, const QgsProcessingModelParameter &component )
1757{
1758 if ( addParameter( definition ) )
1759 mParameterComponents.insert( definition->name(), component );
1760}
1761
1762void QgsProcessingModelAlgorithm::updateModelParameter( QgsProcessingParameterDefinition *definition )
1763{
1764 removeParameter( definition->name() );
1765 addParameter( definition );
1766}
1767
1768void QgsProcessingModelAlgorithm::removeModelParameter( const QString &name )
1769{
1770 removeParameter( name );
1771 mParameterComponents.remove( name );
1772}
1773
1774void QgsProcessingModelAlgorithm::changeParameterName( const QString &oldName, const QString &newName )
1775{
1776 QgsProcessingContext context;
1777 QgsExpressionContext expressionContext = createExpressionContext( QVariantMap(), context );
1778
1779 auto replaceExpressionVariable = [oldName, newName, &expressionContext]( const QString & expressionString ) -> std::tuple< bool, QString >
1780 {
1781 QgsExpression expression( expressionString );
1782 expression.prepare( &expressionContext );
1783 QSet<QString> variables = expression.referencedVariables();
1784 if ( variables.contains( oldName ) )
1785 {
1786 QString newExpression = expressionString;
1787 newExpression.replace( QStringLiteral( "@%1" ).arg( oldName ), QStringLiteral( "@%2" ).arg( newName ) );
1788 return { true, newExpression };
1789 }
1790 return { false, QString() };
1791 };
1792
1793 QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
1794 for ( ; childIt != mChildAlgorithms.end(); ++childIt )
1795 {
1796 bool changed = false;
1797 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1798 QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
1799 for ( ; paramIt != childParams.end(); ++paramIt )
1800 {
1801 QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
1802 for ( auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
1803 {
1804 switch ( valueIt->source() )
1805 {
1806 case QgsProcessingModelChildParameterSource::ModelParameter:
1807 {
1808 if ( valueIt->parameterName() == oldName )
1809 {
1810 valueIt->setParameterName( newName );
1811 changed = true;
1812 }
1813 break;
1814 }
1815
1816 case QgsProcessingModelChildParameterSource::Expression:
1817 {
1818 bool updatedExpression = false;
1819 QString newExpression;
1820 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
1821 if ( updatedExpression )
1822 {
1823 valueIt->setExpression( newExpression );
1824 changed = true;
1825 }
1826 break;
1827 }
1828
1829 case QgsProcessingModelChildParameterSource::StaticValue:
1830 {
1831 if ( valueIt->staticValue().userType() == QMetaType::type( "QgsProperty" ) )
1832 {
1833 QgsProperty property = valueIt->staticValue().value< QgsProperty >();
1834 if ( property.propertyType() == QgsProperty::ExpressionBasedProperty )
1835 {
1836 bool updatedExpression = false;
1837 QString newExpression;
1838 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
1839 if ( updatedExpression )
1840 {
1841 property.setExpressionString( newExpression );
1842 valueIt->setStaticValue( property );
1843 changed = true;
1844 }
1845 }
1846 }
1847 break;
1848 }
1849
1850 case QgsProcessingModelChildParameterSource::ChildOutput:
1851 case QgsProcessingModelChildParameterSource::ExpressionText:
1852 case QgsProcessingModelChildParameterSource::ModelOutput:
1853 break;
1854 }
1855 }
1856 }
1857 if ( changed )
1858 childIt->setParameterSources( childParams );
1859 }
1860}
1861
1862bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( const QString &name ) const
1863{
1864 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1865 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1866 {
1867 // check whether child requires this parameter
1868 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1869 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1870 for ( ; paramIt != childParams.constEnd(); ++paramIt )
1871 {
1872 const auto constValue = paramIt.value();
1873 for ( const QgsProcessingModelChildParameterSource &source : constValue )
1874 {
1875 if ( source.source() == QgsProcessingModelChildParameterSource::ModelParameter
1876 && source.parameterName() == name )
1877 {
1878 return true;
1879 }
1880 }
1881 }
1882 }
1883 return false;
1884}
1885
1886bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( const QString &name ) const
1887{
1888 const auto constMParameters = mParameters;
1889 for ( const QgsProcessingParameterDefinition *def : constMParameters )
1890 {
1891 if ( def->name() == name )
1892 continue;
1893
1894 if ( def->dependsOnOtherParameters().contains( name ) )
1895 return true;
1896 }
1897 return false;
1898}
1899
1900QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents() const
1901{
1902 return mParameterComponents;
1903}
1904
1905void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const
1906{
1907 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
1908 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
1909 {
1910 if ( depends.contains( childIt->childId() ) )
1911 continue;
1912
1913 // does alg have a direct dependency on this child?
1914 const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
1915 bool hasDependency = false;
1916 for ( const QgsProcessingModelChildDependency &dep : constDependencies )
1917 {
1918 if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
1919 {
1920 hasDependency = true;
1921 break;
1922 }
1923 }
1924
1925 if ( hasDependency )
1926 {
1927 depends.insert( childIt->childId() );
1928 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
1929 continue;
1930 }
1931
1932 // check whether child requires any outputs from the target alg
1933 QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
1934 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1935 for ( ; paramIt != childParams.constEnd(); ++paramIt )
1936 {
1937 const auto constValue = paramIt.value();
1938 for ( const QgsProcessingModelChildParameterSource &source : constValue )
1939 {
1940 if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
1941 && source.outputChildId() == childId )
1942 {
1943 depends.insert( childIt->childId() );
1944 dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
1945 break;
1946 }
1947 }
1948 }
1949 }
1950}
1951
1952QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch ) const
1953{
1954 QSet< QString > algs;
1955
1956 // temporarily insert the target child algorithm to avoid
1957 // unnecessarily recursion though it
1958 algs.insert( childId );
1959
1960 dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
1961
1962 // remove temporary target alg
1963 algs.remove( childId );
1964
1965 return algs;
1966}
1967
1968
1969void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( const QString &childId, QSet< QString > &depends ) const
1970{
1971 const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
1972
1973 // add direct dependencies
1974 const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
1975 for ( const QgsProcessingModelChildDependency &val : constDependencies )
1976 {
1977 if ( !depends.contains( val.childId ) )
1978 {
1979 depends.insert( val.childId );
1980 dependsOnChildAlgorithmsRecursive( val.childId, depends );
1981 }
1982 }
1983
1984 // check through parameter dependencies
1985 QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
1986 QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
1987 for ( ; paramIt != childParams.constEnd(); ++paramIt )
1988 {
1989 const auto constValue = paramIt.value();
1990 for ( const QgsProcessingModelChildParameterSource &source : constValue )
1991 {
1992 if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && !depends.contains( source.outputChildId() ) )
1993 {
1994 depends.insert( source.outputChildId() );
1995 dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
1996 }
1997 }
1998 }
1999}
2000
2001QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( const QString &childId ) const
2002{
2003 QSet< QString > algs;
2004
2005 // temporarily insert the target child algorithm to avoid
2006 // unnecessarily recursion though it
2007 algs.insert( childId );
2008
2009 dependsOnChildAlgorithmsRecursive( childId, algs );
2010
2011 // remove temporary target alg
2012 algs.remove( childId );
2013
2014 return algs;
2015}
2016
2017QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( const QString &childId ) const
2018{
2019 QSet< QString > dependent;
2020 if ( !childId.isEmpty() )
2021 {
2022 dependent.unite( dependentChildAlgorithms( childId ) );
2023 dependent.insert( childId );
2024 }
2025
2026 QList<QgsProcessingModelChildDependency> res;
2027 for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
2028 {
2029 if ( !dependent.contains( it->childId() ) )
2030 {
2031 // check first if algorithm provides output branches
2032 bool hasBranches = false;
2033 if ( it->algorithm() )
2034 {
2035 const QgsProcessingOutputDefinitions defs = it->algorithm()->outputDefinitions();
2036 for ( const QgsProcessingOutputDefinition *def : defs )
2037 {
2039 {
2040 hasBranches = true;
2041 QgsProcessingModelChildDependency alg;
2042 alg.childId = it->childId();
2043 alg.conditionalBranch = def->name();
2044 res << alg;
2045 }
2046 }
2047 }
2048
2049 if ( !hasBranches )
2050 {
2051 QgsProcessingModelChildDependency alg;
2052 alg.childId = it->childId();
2053 res << alg;
2054 }
2055 }
2056 }
2057 return res;
2058}
2059
2060bool QgsProcessingModelAlgorithm::validateChildAlgorithm( const QString &childId, QStringList &issues ) const
2061{
2062 issues.clear();
2063 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
2064 if ( childIt != mChildAlgorithms.constEnd() )
2065 {
2066 if ( !childIt->algorithm() )
2067 {
2068 issues << QObject::tr( "Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2069 return false;
2070 }
2071 bool res = true;
2072
2073 // loop through child algorithm parameters and check that they are all valid
2074 const QgsProcessingParameterDefinitions defs = childIt->algorithm()->parameterDefinitions();
2075 for ( const QgsProcessingParameterDefinition *def : defs )
2076 {
2077 if ( childIt->parameterSources().contains( def->name() ) )
2078 {
2079 // is the value acceptable?
2080 const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->name() );
2081 for ( const QgsProcessingModelChildParameterSource &source : sources )
2082 {
2083 switch ( source.source() )
2084 {
2085 case QgsProcessingModelChildParameterSource::StaticValue:
2086 if ( !def->checkValueIsAcceptable( source.staticValue() ) )
2087 {
2088 res = false;
2089 issues << QObject::tr( "Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->name() );
2090 }
2091 break;
2092
2093 case QgsProcessingModelChildParameterSource::ModelParameter:
2094 if ( !parameterComponents().contains( source.parameterName() ) )
2095 {
2096 res = false;
2097 issues << QObject::tr( "Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->name() );
2098 }
2099 break;
2100
2101 case QgsProcessingModelChildParameterSource::ChildOutput:
2102 if ( !childAlgorithms().contains( source.outputChildId() ) )
2103 {
2104 res = false;
2105 issues << QObject::tr( "Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->name() );
2106 }
2107 break;
2108
2109 case QgsProcessingModelChildParameterSource::Expression:
2110 case QgsProcessingModelChildParameterSource::ExpressionText:
2111 case QgsProcessingModelChildParameterSource::ModelOutput:
2112 break;
2113 }
2114 }
2115 }
2116 else
2117 {
2118 // not specified. Is it optional?
2119
2120 // ignore destination parameters -- they shouldn't ever be mandatory
2121 if ( def->isDestination() )
2122 continue;
2123
2125 {
2126 res = false;
2127 issues << QObject::tr( "Parameter <i>%1</i> is mandatory" ).arg( def->name() );
2128 }
2129 }
2130 }
2131
2132 return res;
2133 }
2134 else
2135 {
2136 issues << QObject::tr( "Invalid child ID: <i>%1</i>" ).arg( childId );
2137 return false;
2138 }
2139}
2140
2141bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
2142{
2143 reattachAlgorithms();
2144 QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
2145 for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
2146 {
2147 if ( !childIt->algorithm() )
2148 {
2149 if ( errorMessage )
2150 {
2151 *errorMessage = QObject::tr( "The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
2152 }
2153 return false;
2154 }
2155 }
2156 return true;
2157}
2158
2159QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
2160{
2161 if ( mSourceFile.isEmpty() )
2162 return QString(); // temporary model - can't run as python command
2163
2164 return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
2165}
2166
2167QgsExpressionContext QgsProcessingModelAlgorithm::createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source ) const
2168{
2169 QgsExpressionContext res = QgsProcessingAlgorithm::createExpressionContext( parameters, context, source );
2170 res << QgsExpressionContextUtils::processingModelAlgorithmScope( this, parameters, context );
2171 return res;
2172}
2173
2174QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const
2175{
2176 QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm();
2177 alg->loadVariant( toVariant() );
2178 alg->setProvider( provider() );
2179 alg->setSourceFilePath( sourceFilePath() );
2180 return alg;
2181}
2182
2183QString QgsProcessingModelAlgorithm::safeName( const QString &name, bool capitalize )
2184{
2185 QString n = name.toLower().trimmed();
2186 const thread_local QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
2187 n.replace( rx, QString() );
2188 const thread_local QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
2189 n.replace( rx2, QString() );
2190 if ( !capitalize )
2191 n = n.replace( ' ', '_' );
2193}
2194
2195QVariantMap QgsProcessingModelAlgorithm::variables() const
2196{
2197 return mVariables;
2198}
2199
2200void QgsProcessingModelAlgorithm::setVariables( const QVariantMap &variables )
2201{
2202 mVariables = variables;
2203}
2204
2205QVariantMap QgsProcessingModelAlgorithm::designerParameterValues() const
2206{
2207 return mDesignerParameterValues;
2208}
2209
@ 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:282
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.
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 SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Base class for all map layer types.
Definition: qgsmaplayer.h:73
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.
@ FlagNoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
@ FlagCustomException
Algorithm raises custom exception notices, don't use the standard ones.
@ FlagSkipGenericModelLogging
When running as part of a model, the generic algorithm setup and results logging should be skipped.
@ FlagPruneModelBranchesBasedOnAlgorithmResults
Algorithm results will cause remaining model branches to be pruned based on the results of running th...
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.
Contains information about the context in which a processing algorithm is executed.
@ Verbose
Verbose logging.
QgsExpressionContext & expressionContext()
Returns the expression context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
LogLevel 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
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.
A vector layer output for processing algorithms.
static QString typeName()
Returns the type name for the output class.
QgsProcessing::SourceType dataType() const
Returns the layer type for the output layer.
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.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
@ FlagHidden
Parameter is hidden and should not be shown to users.
virtual QgsProcessingParameterDefinition * clone() const =0
Creates a clone of the parameter definition.
virtual QString type() const =0
Unique parameter type name.
Flags flags() const
Returns any flags associated with the parameter.
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.
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.
static QString typeName()
Returns the type name for the parameter class.
DataType 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.
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 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)
Interprets a string as a map layer within the supplied context.
PythonOutputType
Available Python output types.
Definition: qgsprocessing.h:64
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
Definition: qgsprocessing.h:65
SourceType
Data source types enum.
Definition: qgsprocessing.h:47
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:51
@ TypeMapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
Definition: qgsprocessing.h:48
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:52
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:55
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:50
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:49
A store for object properties.
Definition: qgsproperty.h:230
@ ExpressionBasedProperty
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:239
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 yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
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:3732
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:3713
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:3613
QMap< QString, QString > QgsStringMap
Definition: qgis.h:4054
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2206
QList< const QgsProcessingOutputDefinition * > QgsProcessingOutputDefinitions
List of processing parameters.
QList< const QgsProcessingParameterDefinition * > QgsProcessingParameterDefinitions
List of processing parameters.
Single variable definition for use within a QgsExpressionContextScope.