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