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