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