Navigation Menu

Skip to content

Commit

Permalink
[processing] Add explicit output type for multiple layers
Browse files Browse the repository at this point in the history
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.
38 changes: 38 additions & 0 deletions python/core/processing/qgsprocessingoutputs.sip.in
Expand Up @@ -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() )
Expand Down Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion python/plugins/processing/core/Processing.py
Expand Up @@ -42,7 +42,8 @@
QgsProcessingParameterDefinition,
QgsProcessingOutputVectorLayer,
QgsProcessingOutputRasterLayer,
QgsProcessingOutputMapLayer)
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers)

import processing
from processing.core.ProcessingConfig import ProcessingConfig
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion python/plugins/processing/core/outputs.py
Expand Up @@ -42,7 +42,8 @@
QgsProcessingOutputHtml,
QgsProcessingOutputNumber,
QgsProcessingOutputString,
QgsProcessingOutputFolder)
QgsProcessingOutputFolder,
QgsProcessingOutputMultipleLayers)


def getOutputFromString(s):
Expand All @@ -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':
Expand Down
23 changes: 16 additions & 7 deletions python/plugins/processing/gui/wrappers.py
Expand Up @@ -66,6 +66,7 @@
QgsProcessingOutputRasterLayer,
QgsProcessingOutputVectorLayer,
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers,
QgsProcessingOutputFile,
QgsProcessingOutputString,
QgsProcessingOutputNumber,
Expand Down Expand Up @@ -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))
Expand Down
11 changes: 8 additions & 3 deletions src/core/processing/qgsprocessingoutputs.cpp
Expand Up @@ -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();
}
32 changes: 32 additions & 0 deletions src/core/processing/qgsprocessingoutputs.h
Expand Up @@ -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() )
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 4bcc9df

Please sign in to comment.