Skip to content
Permalink
Browse files

Add flag to allow a child algorithm's results to prune back the remai…

…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 dee6f3f30dc7f2da789fa8f7fa99413b8c8e94ff
@@ -47,6 +47,7 @@ Abstract base class for processing algorithms.
FlagSupportsInPlaceEdits,
FlagKnownIssues,
FlagCustomException,
FlagPruneModelBranchesBasedOnAlgorithmResults,
FlagDeprecated,
};
typedef QFlags<QgsProcessingAlgorithm::Flag> Flags;
@@ -2425,5 +2425,5 @@ tests:
name: expected/filter_geom_lines_poly.gml
type: vector


# See ../README.md for a description of the file format
@@ -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;
@@ -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 ) )
{
@@ -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
@@ -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() ) );
@@ -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 )

0 comments on commit dee6f3f

Please sign in to comment.
You can’t perform that action at this time.