Skip to content
Permalink
Browse files
[processing] Add explicit output type for multiple layers
This was a missing capability in the processing API - while algorithms
could declare multiple layer input parameters, there was no corresponding
multi-layer output. This meant that algorithms (such as Package Layers,
Vector Split) which create a set of layers which cannot be determined
in advance had no way to pass these generated layers on for further model
processing steps.

It's also useful for algorithms which operate on a specified folder,
processing all layers found there, and allowing these generated
outputs to be utilised in other model steps (e.g. packaging
all of them, merging them, etc)
  • Loading branch information
nyalldawson committed Feb 11, 2018
1 parent 779fe1a commit 4bcc9df
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 12 deletions.
@@ -33,6 +33,8 @@ as generated layers or calculated values.
sipType = sipType_QgsProcessingOutputRasterLayer;
else if ( sipCpp->type() == QgsProcessingOutputMapLayer::typeName() )
sipType = sipType_QgsProcessingOutputMapLayer;
else if ( sipCpp->type() == QgsProcessingOutputMultipleLayers::typeName() )
sipType = sipType_QgsProcessingOutputMultipleLayers;
else if ( sipCpp->type() == QgsProcessingOutputHtml::typeName() )
sipType = sipType_QgsProcessingOutputHtml;
else if ( sipCpp->type() == QgsProcessingOutputNumber::typeName() )
@@ -196,6 +198,42 @@ Returns the type name for the output class.
virtual QString type() const;


};

class QgsProcessingOutputMultipleLayers : QgsProcessingOutputDefinition
{
%Docstring
A multi-layer output for processing algorithms which create map layers, when
the number and nature of the output layers is not predefined.

.. note::

Always prefer to explicitly define QgsProcessingOutputVectorLayer,
QgsProcessingOutputRasterLayer or QgsProcessingOutputMapLayer where possible. :py:class:`QgsProcessingOutputMultipleLayers`
should only ever be used when the number of output layers is not
fixed - e.g. as a result of processing all layers in a specified
folder.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsprocessingoutputs.h"
%End
public:

QgsProcessingOutputMultipleLayers( const QString &name, const QString &description = QString() );
%Docstring
Constructor for QgsProcessingOutputMultipleLayers.
%End

static QString typeName();
%Docstring
Returns the type name for the output class.
%End
virtual QString type() const;


};

class QgsProcessingOutputHtml : QgsProcessingOutputDefinition
@@ -42,7 +42,8 @@
QgsProcessingParameterDefinition,
QgsProcessingOutputVectorLayer,
QgsProcessingOutputRasterLayer,
QgsProcessingOutputMapLayer)
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers)

import processing
from processing.core.ProcessingConfig import ProcessingConfig
@@ -163,6 +164,22 @@ def runAlgorithm(algOrName, parameters, onFinish=None, feedback=None, context=No
layer = context.takeResultLayer(result) # transfer layer ownership out of context
if layer:
results[out.name()] = layer # replace layer string ref with actual layer (+ownership)
elif isinstance(out, QgsProcessingOutputMultipleLayers):
result = results[out.name()]
if result:
layers_result = []
for l in result:
if not isinstance(result, QgsMapLayer):
layer = context.takeResultLayer(l) # transfer layer ownership out of context
if layer:
layers_result.append(layer)
else:
layers_result.append(l)
else:
layers_result.append(l)

results[out.name()] = layers_result # replace layers strings ref with actual layers (+ownership)

else:
msg = Processing.tr("There were errors executing the algorithm.")
feedback.reportError(msg)
@@ -42,7 +42,8 @@
QgsProcessingOutputHtml,
QgsProcessingOutputNumber,
QgsProcessingOutputString,
QgsProcessingOutputFolder)
QgsProcessingOutputFolder,
QgsProcessingOutputMultipleLayers)


def getOutputFromString(s):
@@ -69,6 +70,8 @@ def getOutputFromString(s):
out = QgsProcessingOutputVectorLayer(name, description)
elif token.lower().strip() == 'outputlayer':
out = QgsProcessingOutputMapLayer(name, description)
elif token.lower().strip() == 'outputmultilayers':
out = QgsProcessingOutputMultipleLayers(name, description)
# elif token.lower().strip() == 'vector point':
# out = OutputVector(datatype=[dataobjects.TYPE_VECTOR_POINT])
# elif token.lower().strip() == 'vector line':
@@ -66,6 +66,7 @@
QgsProcessingOutputRasterLayer,
QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers,
QgsProcessingOutputFile,
QgsProcessingOutputString,
QgsProcessingOutputNumber,
@@ -544,47 +545,55 @@ def _getOptions(self):
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer])
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers])
elif self.param.layerType() == QgsProcessing.TypeVector:
options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer],
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers],
[QgsProcessing.TypeVector])
elif self.param.layerType() == QgsProcessing.TypeVectorPoint:
options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer],
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers],
[QgsProcessing.TypeVectorPoint,
QgsProcessing.TypeVectorAnyGeometry])
elif self.param.layerType() == QgsProcessing.TypeVectorLine:
options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer],
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers],
[QgsProcessing.TypeVectorLine,
QgsProcessing.TypeVectorAnyGeometry])
elif self.param.layerType() == QgsProcessing.TypeVectorPolygon:
options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer],
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers],
[QgsProcessing.TypeVectorPolygon,
QgsProcessing.TypeVectorAnyGeometry])
elif self.param.layerType() == QgsProcessing.TypeRaster:
options = self.dialog.getAvailableValuesOfType(
(QgsProcessingParameterRasterLayer, QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputRasterLayer,
QgsProcessingOutputMapLayer])
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers])
elif self.param.layerType() == QgsProcessing.TypeVector:
options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer)
QgsProcessingParameterMultipleLayers),
[QgsProcessingOutputVectorLayer,
QgsProcessingOutputMultipleLayers])
else:
options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, QgsProcessingOutputFile)
options = sorted(options, key=lambda opt: self.dialog.resolveValueDescription(opt))
@@ -65,13 +65,18 @@ QgsProcessingOutputFile::QgsProcessingOutputFile( const QString &name, const QSt

QgsProcessingOutputMapLayer::QgsProcessingOutputMapLayer( const QString &name, const QString &description )
: QgsProcessingOutputDefinition( name, description )
{
{}

QString QgsProcessingOutputMapLayer::type() const
{
return typeName();
}

QgsProcessingOutputMultipleLayers::QgsProcessingOutputMultipleLayers( const QString &name, const QString &description )
: QgsProcessingOutputDefinition( name, description )
{}


QString QgsProcessingOutputMapLayer::type() const
QString QgsProcessingOutputMultipleLayers::type() const
{
return typeName();
}
@@ -49,6 +49,8 @@ class CORE_EXPORT QgsProcessingOutputDefinition
sipType = sipType_QgsProcessingOutputRasterLayer;
else if ( sipCpp->type() == QgsProcessingOutputMapLayer::typeName() )
sipType = sipType_QgsProcessingOutputMapLayer;
else if ( sipCpp->type() == QgsProcessingOutputMultipleLayers::typeName() )
sipType = sipType_QgsProcessingOutputMultipleLayers;
else if ( sipCpp->type() == QgsProcessingOutputHtml::typeName() )
sipType = sipType_QgsProcessingOutputHtml;
else if ( sipCpp->type() == QgsProcessingOutputNumber::typeName() )
@@ -209,6 +211,36 @@ class CORE_EXPORT QgsProcessingOutputRasterLayer : public QgsProcessingOutputDef

};

/**
* \class QgsProcessingOutputMultipleLayers
* \ingroup core
* A multi-layer output for processing algorithms which create map layers, when
* the number and nature of the output layers is not predefined.
*
* \note Always prefer to explicitly define QgsProcessingOutputVectorLayer,
* QgsProcessingOutputRasterLayer or QgsProcessingOutputMapLayer where possible. QgsProcessingOutputMultipleLayers
* should only ever be used when the number of output layers is not
* fixed - e.g. as a result of processing all layers in a specified
* folder.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsProcessingOutputMultipleLayers : public QgsProcessingOutputDefinition
{
public:

/**
* Constructor for QgsProcessingOutputMultipleLayers.
*/
QgsProcessingOutputMultipleLayers( const QString &name, const QString &description = QString() );

/**
* Returns the type name for the output class.
*/
static QString typeName() { return QStringLiteral( "outputMultilayer" ); }
QString type() const override;

};

/**
* \class QgsProcessingOutputHtml
* \ingroup core

0 comments on commit 4bcc9df

Please sign in to comment.