diff --git a/python/core/processing/qgsprocessingalgorithm.sip b/python/core/processing/qgsprocessingalgorithm.sip index e1e238b6e44..547bd53ef95 100644 --- a/python/core/processing/qgsprocessingalgorithm.sip +++ b/python/core/processing/qgsprocessingalgorithm.sip @@ -368,6 +368,12 @@ class QgsProcessingAlgorithm :rtype: QgsRasterLayer %End + QString parameterAsRasterOutputLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; +%Docstring + Evaluates the parameter with matching ``name`` to a raster output layer destination. + :rtype: str +%End + QgsVectorLayer *parameterAsVectorLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; %Docstring Evaluates the parameter with matching ``name`` to a vector layer. diff --git a/python/core/processing/qgsprocessingoutputs.sip b/python/core/processing/qgsprocessingoutputs.sip index 2b4c3eb30fc..a958b128501 100644 --- a/python/core/processing/qgsprocessingoutputs.sip +++ b/python/core/processing/qgsprocessingoutputs.sip @@ -30,6 +30,8 @@ class QgsProcessingOutputDefinition %ConvertToSubClassCode if ( sipCpp->type() == "outputVector" ) sipType = sipType_QgsProcessingOutputVectorLayer; + else if ( sipCpp->type() == "outputRaster" ) + sipType = sipType_QgsProcessingOutputRasterLayer; %End public: @@ -118,6 +120,26 @@ class QgsProcessingOutputVectorLayer : QgsProcessingOutputDefinition }; +class QgsProcessingOutputRasterLayer : QgsProcessingOutputDefinition +{ +%Docstring + A raster layer output for processing algorithms. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsprocessingoutputs.h" +%End + public: + + QgsProcessingOutputRasterLayer( const QString &name, const QString &description = QString() ); +%Docstring + Constructor for QgsProcessingOutputRasterLayer. +%End + + virtual QString type() const; +}; + diff --git a/python/core/processing/qgsprocessingparameters.sip b/python/core/processing/qgsprocessingparameters.sip index 701b4094750..0be889096a1 100644 --- a/python/core/processing/qgsprocessingparameters.sip +++ b/python/core/processing/qgsprocessingparameters.sip @@ -171,6 +171,8 @@ class QgsProcessingParameterDefinition sipType = sipType_QgsProcessingParameterFeatureSource; else if ( sipCpp->type() == "sink" ) sipType = sipType_QgsProcessingParameterFeatureSink; + else if ( sipCpp->type() == "rasterOut" ) + sipType = sipType_QgsProcessingParameterRasterOutput; %End public: @@ -419,6 +421,12 @@ class QgsProcessingParameters :rtype: QgsRasterLayer %End + static QString parameterAsRasterOutputLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); +%Docstring + Evaluates the parameter with matching ``definition`` to a raster output layer destination. + :rtype: str +%End + static QgsVectorLayer *parameterAsVectorLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); %Docstring Evaluates the parameter with matching ``definition`` to a vector layer. @@ -1221,6 +1229,31 @@ class QgsProcessingParameterFeatureSink : QgsProcessingParameterDefinition }; +class QgsProcessingParameterRasterOutput : QgsProcessingParameterDefinition +{ +%Docstring + A raster layer output parameter. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsprocessingparameters.h" +%End + public: + + QgsProcessingParameterRasterOutput( const QString &name, const QString &description = QString(), + const QVariant &defaultValue = QVariant(), + bool optional = false ); +%Docstring + Constructor for QgsProcessingParameterRasterOutput. +%End + + virtual QString type() const; + virtual bool isDestination() const; + virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; + +}; + /************************************************************************ diff --git a/python/plugins/processing/gui/AlgorithmDialog.py b/python/plugins/processing/gui/AlgorithmDialog.py index a40a6e5043c..ba5994a647e 100644 --- a/python/plugins/processing/gui/AlgorithmDialog.py +++ b/python/plugins/processing/gui/AlgorithmDialog.py @@ -34,9 +34,11 @@ from qgis.core import (QgsProject, QgsProcessingUtils, QgsMessageLog, QgsProcessingParameterDefinition, + QgsProcessingOutputRasterLayer, QgsProcessingOutputVectorLayer, QgsProcessingOutputLayerDefinition, QgsProcessingParameterFeatureSink, + QgsProcessingParameterRasterOutput, QgsProcessingAlgorithm) from qgis.gui import QgsMessageBar from qgis.utils import iface @@ -56,8 +58,6 @@ from processing.core.parameters import ParameterExtent from processing.core.parameters import ParameterMultipleInput from processing.core.GeoAlgorithm import executeAlgorithm -from processing.core.outputs import OutputRaster -from processing.core.outputs import OutputVector from processing.core.outputs import OutputTable from processing.tools import dataobjects @@ -113,7 +113,7 @@ class AlgorithmDialog(AlgorithmDialogBase): else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ - isinstance(param, (OutputRaster, QgsProcessingParameterFeatureSink, OutputTable)): + isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() diff --git a/python/plugins/processing/gui/ParameterGuiUtils.py b/python/plugins/processing/gui/ParameterGuiUtils.py index f09e5113b2b..fb461e48d17 100644 --- a/python/plugins/processing/gui/ParameterGuiUtils.py +++ b/python/plugins/processing/gui/ParameterGuiUtils.py @@ -56,10 +56,10 @@ def getFileFilter(param): for i in range(len(exts)): exts[i] = tr('{0} files (*.{1})', 'QgsProcessingParameterMultipleLayers').format(exts[i].upper(), exts[i].lower()) return ';;'.join(exts) - elif param.type() == 'raster': + elif param.type() in ('raster', 'rasterOut'): exts = dataobjects.getSupportedOutputRasterLayerExtensions() for i in range(len(exts)): - exts[i] = tr('{0} files (*.{1})', 'ParameterRaster').format(exts[i].upper(), exts[i].lower()) + exts[i] = tr('{0} files (*.{1})', 'QgsProcessingParameterRasterOutput').format(exts[i].upper(), exts[i].lower()) return ';;'.join(exts) elif param.type() == 'table': exts = ['csv', 'dbf'] diff --git a/python/plugins/processing/gui/ParametersPanel.py b/python/plugins/processing/gui/ParametersPanel.py index af0b7f1433f..e104b1eff9e 100644 --- a/python/plugins/processing/gui/ParametersPanel.py +++ b/python/plugins/processing/gui/ParametersPanel.py @@ -36,6 +36,8 @@ from qgis.core import (QgsProcessingParameterDefinition, QgsProcessingParameterPoint, QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer, + QgsProcessingOutputRasterLayer, + QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink) from qgis.PyQt import uic from qgis.PyQt.QtCore import QCoreApplication @@ -46,9 +48,7 @@ from qgis.PyQt.QtGui import QIcon from processing.gui.DestinationSelectionPanel import DestinationSelectionPanel from processing.gui.wrappers import WidgetWrapperFactory from processing.core.parameters import ParameterVector, ParameterExtent, ParameterPoint -from processing.core.outputs import OutputRaster from processing.core.outputs import OutputTable -from processing.core.outputs import OutputVector pluginPath = os.path.split(os.path.dirname(__file__))[0] WIDGET, BASE = uic.loadUiType( @@ -157,7 +157,7 @@ class ParametersPanel(BASE, WIDGET): widget = DestinationSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) - if isinstance(output, (OutputRaster, QgsProcessingParameterFeatureSink, OutputTable)): + if isinstance(output, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)): check = QCheckBox() check.setText(self.tr('Open output file after running algorithm')) check.setChecked(True) diff --git a/src/core/processing/qgsprocessingalgorithm.cpp b/src/core/processing/qgsprocessingalgorithm.cpp index e438a980d66..35c7904b658 100644 --- a/src/core/processing/qgsprocessingalgorithm.cpp +++ b/src/core/processing/qgsprocessingalgorithm.cpp @@ -247,6 +247,11 @@ QgsRasterLayer *QgsProcessingAlgorithm::parameterAsRasterLayer( const QVariantMa return QgsProcessingParameters::parameterAsRasterLayer( parameterDefinition( name ), parameters, context ); } +QString QgsProcessingAlgorithm::parameterAsRasterOutputLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const +{ + return QgsProcessingParameters::parameterAsRasterOutputLayer( parameterDefinition( name ), parameters, context ); +} + QgsVectorLayer *QgsProcessingAlgorithm::parameterAsVectorLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const { return QgsProcessingParameters::parameterAsVectorLayer( parameterDefinition( name ), parameters, context ); diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index da1f2d26b6b..f2a76bf1bf5 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -354,6 +354,11 @@ class CORE_EXPORT QgsProcessingAlgorithm */ QgsRasterLayer *parameterAsRasterLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; + /** + * Evaluates the parameter with matching \a name to a raster output layer destination. + */ + QString parameterAsRasterOutputLayer( const QVariantMap ¶meters, const QString &name, QgsProcessingContext &context ) const; + /** * Evaluates the parameter with matching \a name to a vector layer. * diff --git a/src/core/processing/qgsprocessingoutputs.cpp b/src/core/processing/qgsprocessingoutputs.cpp index 2972d181a2c..ba0db92e168 100644 --- a/src/core/processing/qgsprocessingoutputs.cpp +++ b/src/core/processing/qgsprocessingoutputs.cpp @@ -38,3 +38,7 @@ void QgsProcessingOutputVectorLayer::setDataType( QgsProcessingParameterDefiniti { mDataType = type; } + +QgsProcessingOutputRasterLayer::QgsProcessingOutputRasterLayer( const QString &name, const QString &description ) + : QgsProcessingOutputDefinition( name, description ) +{} diff --git a/src/core/processing/qgsprocessingoutputs.h b/src/core/processing/qgsprocessingoutputs.h index d30e5b153a1..030f2349f57 100644 --- a/src/core/processing/qgsprocessingoutputs.h +++ b/src/core/processing/qgsprocessingoutputs.h @@ -45,6 +45,8 @@ class CORE_EXPORT QgsProcessingOutputDefinition SIP_CONVERT_TO_SUBCLASS_CODE if ( sipCpp->type() == "outputVector" ) sipType = sipType_QgsProcessingOutputVectorLayer; + else if ( sipCpp->type() == "outputRaster" ) + sipType = sipType_QgsProcessingOutputRasterLayer; SIP_END #endif @@ -137,6 +139,24 @@ class CORE_EXPORT QgsProcessingOutputVectorLayer : public QgsProcessingOutputDef QgsProcessingParameterDefinition::LayerType mDataType = QgsProcessingParameterDefinition::TypeVectorAny; }; +/** + * \class QgsProcessingOutputRasterLayer + * \ingroup core + * A raster layer output for processing algorithms. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsProcessingOutputRasterLayer : public QgsProcessingOutputDefinition +{ + public: + + /** + * Constructor for QgsProcessingOutputRasterLayer. + */ + QgsProcessingOutputRasterLayer( const QString &name, const QString &description = QString() ); + + QString type() const override { return QStringLiteral( "outputRaster" ); } +}; + #endif // QGSPROCESSINGOUTPUTS_H diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 50704fa0469..453135276d6 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -338,6 +338,46 @@ QgsRasterLayer *QgsProcessingParameters::parameterAsRasterLayer( const QgsProces return qobject_cast< QgsRasterLayer *>( parameterAsLayer( definition, parameters, context ) ); } +QString QgsProcessingParameters::parameterAsRasterOutputLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ) +{ + QVariant val; + if ( definition ) + { + val = parameters.value( definition->name() ); + } + + QgsProject *destinationProject = nullptr; + QVariantMap createOptions; + if ( val.canConvert() ) + { + // input is a QgsProcessingOutputLayerDefinition - get extra properties from it + QgsProcessingOutputLayerDefinition fromVar = qvariant_cast( val ); + destinationProject = fromVar.destinationProject; + createOptions = fromVar.createOptions; + val = fromVar.sink; + } + + QString dest; + if ( val.canConvert() ) + { + dest = val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() ); + } + else if ( !val.isValid() || val.toString().isEmpty() ) + { + // fall back to default + dest = definition->defaultValue().toString(); + } + else + { + dest = val.toString(); + } + + if ( destinationProject ) + context.addLayerToLoadOnCompletion( dest, QgsProcessingContext::LayerDetails( definition ? definition->description() : QString(), destinationProject ) ); + + return dest; +} + QgsVectorLayer *QgsProcessingParameters::parameterAsVectorLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ) { return qobject_cast< QgsVectorLayer *>( parameterAsLayer( definition, parameters, context ) ); @@ -681,6 +721,11 @@ bool QgsProcessingParameterMapLayer::checkValueIsAcceptable( const QVariant &inp return true; } + if ( qobject_cast< QgsMapLayer * >( qvariant_cast( input ) ) ) + { + return true; + } + if ( input.type() != QVariant::String || input.toString().isEmpty() ) return mFlags & FlagOptional; @@ -713,11 +758,6 @@ bool QgsProcessingParameterExtent::checkValueIsAcceptable( const QVariant &input return true; } - if ( qobject_cast< QgsMapLayer * >( qvariant_cast( input ) ) ) - { - return true; - } - if ( input.type() != QVariant::String || input.toString().isEmpty() ) return mFlags & FlagOptional; @@ -1442,3 +1482,34 @@ void QgsProcessingParameterFeatureSink::setDataType( QgsProcessingParameterDefin { mDataType = type; } + +QgsProcessingParameterRasterOutput::QgsProcessingParameterRasterOutput( const QString &name, const QString &description, const QVariant &defaultValue, bool optional ) + : QgsProcessingParameterDefinition( name, description, defaultValue, optional ) +{} + +bool QgsProcessingParameterRasterOutput::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const +{ + QVariant var = input; + if ( !var.isValid() ) + return mFlags & FlagOptional; + + if ( var.canConvert() ) + { + QgsProcessingOutputLayerDefinition fromVar = qvariant_cast( var ); + var = fromVar.sink; + } + + if ( var.canConvert() ) + { + return true; + } + + if ( var.type() != QVariant::String ) + return false; + + if ( var.toString().isEmpty() ) + return mFlags & FlagOptional; + + return true; +} + diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 3c49225dccb..2332604abff 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -206,6 +206,8 @@ class CORE_EXPORT QgsProcessingParameterDefinition sipType = sipType_QgsProcessingParameterFeatureSource; else if ( sipCpp->type() == "sink" ) sipType = sipType_QgsProcessingParameterFeatureSink; + else if ( sipCpp->type() == "rasterOut" ) + sipType = sipType_QgsProcessingParameterRasterOutput; SIP_END #endif @@ -445,6 +447,11 @@ class CORE_EXPORT QgsProcessingParameters */ static QgsRasterLayer *parameterAsRasterLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); + /** + * Evaluates the parameter with matching \a definition to a raster output layer destination. + */ + static QString parameterAsRasterOutputLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); + /** * Evaluates the parameter with matching \a definition to a vector layer. * @@ -1219,6 +1226,28 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingParame QgsProcessingParameterDefinition::LayerType mDataType = QgsProcessingParameterDefinition::TypeVectorAny; }; +/** + * \class QgsProcessingParameterRasterOutput + * \ingroup core + * A raster layer output parameter. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsProcessingParameterRasterOutput : public QgsProcessingParameterDefinition +{ + public: + + /** + * Constructor for QgsProcessingParameterRasterOutput. + */ + QgsProcessingParameterRasterOutput( const QString &name, const QString &description = QString(), + const QVariant &defaultValue = QVariant(), + bool optional = false ); + + QString type() const override { return QStringLiteral( "rasterOut" ); } + bool isDestination() const override { return true; } + bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; +}; + #endif // QGSPROCESSINGPARAMETERS_H diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index c5537ee5674..6bfb91b6641 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -216,6 +216,7 @@ class TestQgsProcessing: public QObject void parameterField(); void parameterFeatureSource(); void parameterFeatureSink(); + void parameterRasterOut(); void checkParamValues(); void combineLayerExtent(); void processingFeatureSource(); @@ -2357,6 +2358,48 @@ void TestQgsProcessing::parameterFeatureSink() } +void TestQgsProcessing::parameterRasterOut() +{ + // setup a context + QgsProject p; + p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) ); + QgsProcessingContext context; + context.setProject( &p ); + + // not optional! + std::unique_ptr< QgsProcessingParameterRasterOutput > def( new QgsProcessingParameterRasterOutput( "non_optional", QString(), QString(), false ) ); + QVERIFY( !def->checkValueIsAcceptable( false ) ); + QVERIFY( !def->checkValueIsAcceptable( true ) ); + QVERIFY( !def->checkValueIsAcceptable( 5 ) ); + QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) ); + QVERIFY( !def->checkValueIsAcceptable( "" ) ); + QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); + QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) ); + + // should be OK with or without context - it's an output layer! + QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) ); + QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif", &context ) ); + + QVariantMap params; + params.insert( "non_optional", "test.tif" ); + QCOMPARE( QgsProcessingParameters::parameterAsRasterOutputLayer( def.get(), params, context ), QStringLiteral( "test.tif" ) ); + + // optional + def.reset( new QgsProcessingParameterRasterOutput( "optional", QString(), QString( "default.tif" ), true ) ); + QVERIFY( !def->checkValueIsAcceptable( false ) ); + QVERIFY( !def->checkValueIsAcceptable( true ) ); + QVERIFY( !def->checkValueIsAcceptable( 5 ) ); + QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) ); + QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) ); + QVERIFY( def->checkValueIsAcceptable( "" ) ); + QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); + QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) ); + + params.insert( "optional", QVariant() ); + QCOMPARE( QgsProcessingParameters::parameterAsRasterOutputLayer( def.get(), params, context ), QStringLiteral( "default.tif" ) ); + +} + void TestQgsProcessing::checkParamValues() { DummyAlgorithm a( "asd" );