Skip to content

Commit

Permalink
Add flag to allow a child algorithm's results to prune back the remai…
Browse files Browse the repository at this point in the history
…ning model branches

When an algorithm returns this flag, and after executing it in a model it doesn't set a
certain output which remaining model algorithms depend on, then these branches are
pruned back and don't get executed for the model run.

Allows for creation of algorithms which impact the overall model flow.
  • Loading branch information
nyalldawson committed Mar 17, 2020
1 parent 813c9af commit dee6f3f
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 6 deletions.
Expand Up @@ -47,6 +47,7 @@ Abstract base class for processing algorithms.
FlagSupportsInPlaceEdits,
FlagKnownIssues,
FlagCustomException,
FlagPruneModelBranchesBasedOnAlgorithmResults,
FlagDeprecated,
};
typedef QFlags<QgsProcessingAlgorithm::Flag> Flags;
Expand Down
Expand Up @@ -2425,5 +2425,5 @@ tests:
name: expected/filter_geom_lines_poly.gml
type: vector


# See ../README.md for a description of the file format
64 changes: 59 additions & 5 deletions src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Expand Up @@ -259,8 +259,7 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
while ( executedAlg && executed.count() < toExecute.count() )
{
executedAlg = false;
const auto constToExecute = toExecute;
for ( const QString &childId : constToExecute )
for ( const QString &childId : qgis::as_const( toExecute ) )
{
if ( feedback && feedback->isCanceled() )
break;
Expand All @@ -269,8 +268,8 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
continue;

bool canExecute = true;
const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
for ( const QString &dependency : constDependsOnChildAlgorithms )
const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
for ( const QString &dependency : dependencies )
{
if ( !executed.contains( dependency ) )
{
Expand Down Expand Up @@ -321,7 +320,6 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
const QString error = childAlg->flags() & QgsProcessingAlgorithm::FlagCustomException ? QString() : QObject::tr( "Error encountered while running %1" ).arg( child.description() );
throw QgsProcessingException( error );
}
childAlg.reset( nullptr );
childResults.insert( childId, results );

// look through child alg's outputs to determine whether any of these should be copied
Expand All @@ -334,6 +332,62 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
}

executed.insert( childId );

std::function< void( const QString & )> pruneAlgorithmBranchRecursive;
pruneAlgorithmBranchRecursive = [&]( const QString & id )
{
const QSet<QString> toPrune = dependentChildAlgorithms( id );
for ( const QString &targetId : toPrune )
{
if ( executed.contains( targetId ) )
continue;

executed.insert( targetId );
pruneAlgorithmBranchRecursive( targetId );
}
};

if ( childAlg->flags() & QgsProcessingAlgorithm::FlagPruneModelBranchesBasedOnAlgorithmResults )
{
// check if any dependent algorithms should be canceled based on the outputs of this algorithm run
// first find all direct dependancies of this algorithm by looking through all remaining child algorithms
for ( const QString &candidateId : qgis::as_const( toExecute ) )
{
if ( executed.contains( candidateId ) )
continue;

// a pending algorithm was found..., check it's parameter sources to see if it links to any of the current
// algorithm's outputs
const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
bool pruned = false;
for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
{
for ( const QgsProcessingModelChildParameterSource &source : paramIt.value() )
{
if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
{
// ok, this one is dependent on the current alg. Did we get a value for it?
if ( !results.contains( source.outputName() ) )
{
// oh no, nothing returned for this parameter. Gotta trim the branch back!
pruned = true;
// skip the dependent alg..
executed.insert( candidateId );
//... and everything which depends on it
pruneAlgorithmBranchRecursive( candidateId );
break;
}
}
}
if ( pruned )
break;
}
}
}

childAlg.reset( nullptr );
modelFeedback.setCurrentStep( executed.count() );
if ( feedback )
feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%2 outputs)." ).arg( childTime.elapsed() / 1000.0 ).arg( results.count() ) );
Expand Down
1 change: 1 addition & 0 deletions src/core/processing/qgsprocessingalgorithm.h
Expand Up @@ -77,6 +77,7 @@ class CORE_EXPORT QgsProcessingAlgorithm
FlagSupportsInPlaceEdits = 1 << 8, //!< Algorithm supports in-place editing
FlagKnownIssues = 1 << 9, //!< Algorithm has known issues
FlagCustomException = 1 << 10, //!< Algorithm raises custom exception notices, don't use the standard ones
FlagPruneModelBranchesBasedOnAlgorithmResults = 1 << 11, //!< Algorithm results will cause remaining model branches to be pruned based on the results of running the algorithm
FlagDeprecated = FlagHideFromToolbox | FlagHideFromModeler, //!< Algorithm is deprecated
};
Q_DECLARE_FLAGS( Flags, Flag )
Expand Down

0 comments on commit dee6f3f

Please sign in to comment.