add modeler support and deprecate Python implementation

This commit is contained in:
Alexander Bruy 2023-09-15 13:44:42 +03:00
parent 8d52b54f33
commit 3c1f510115
9 changed files with 394 additions and 14 deletions

View File

@ -24,6 +24,7 @@ import math
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.algs.gdal.GdalUtils import GdalUtils
from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterCrs,
@ -88,6 +89,9 @@ class RasterCalculator(QgisAlgorithm):
self.addParameter(QgsProcessingParameterCrs(self.CRS, 'Output CRS', optional=True))
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output')))
def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagDeprecated | QgsProcessingAlgorithm.FlagNotAvailableInStandaloneTool
def name(self):
return 'rastercalculator'

View File

@ -1190,7 +1190,7 @@ tests:
- 'Maximum value: 15:29:22'
- 'NULL \(missing\) values: 1'
- algorithm: qgis:rastercalculator
- algorithm: native:rastercalc
name: Raster Calculator with cellsize
params:
LAYERS:
@ -1199,13 +1199,14 @@ tests:
type: raster
type: multi
CELLSIZE: 0.001
EXPRESSION: dem@1
EXPRESSION: '"dem.tif@1"'
results:
OUTPUT:
hash: 525577c05dd999239d9c6f95fd5e70d96355da3a0ea71bfcf021e729
hash: 6ced822cc490c7a3d9346b6c8cd4b282eb4e2a9fdd6e7371f6174117
type: rasterhash
- algorithm: qgis:rastercalculator
- algorithm: native:rastercalc
name: Raster Calculator
params:
LAYERS:
@ -1214,7 +1215,7 @@ tests:
type: raster
type: multi
CELLSIZE: 0.0
EXPRESSION: dem@1 * 2
EXPRESSION: '"dem.tif@1" * 2'
results:
OUTPUT:
hash: 98daf025230ec9d031f7502c6a80a3b04dd060808d6b7bcb4328e87c

View File

@ -21,6 +21,11 @@
///@cond PRIVATE
QgsProcessingAlgorithm::Flags QgsRasterCalculatorAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagHideFromModeler;
}
QString QgsRasterCalculatorAlgorithm::name() const
{
return QStringLiteral( "rastercalc" );
@ -59,8 +64,8 @@ QgsRasterCalculatorAlgorithm *QgsRasterCalculatorAlgorithm::createInstance() con
void QgsRasterCalculatorAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT" ), QObject::tr( "Input layers" ), QgsProcessing::SourceType::TypeRaster ) );
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "INPUT" ), false, Qgis::ExpressionType::RasterCalculator ) );
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::SourceType::TypeRaster ) );
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "LAYERS" ), false, Qgis::ExpressionType::RasterCalculator ) );
std::unique_ptr<QgsProcessingParameterExtent> extentParam = std::make_unique<QgsProcessingParameterExtent>( QStringLiteral( "EXTENT" ), QObject::tr( "Output extent" ), QVariant(), true );
extentParam->setHelp( QObject::tr( "Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
addParameter( extentParam.release() );
@ -75,7 +80,7 @@ void QgsRasterCalculatorAlgorithm::initAlgorithm( const QVariantMap & )
bool QgsRasterCalculatorAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
for ( const QgsMapLayer *layer : std::as_const( layers ) )
{
@ -198,5 +203,160 @@ QVariantMap QgsRasterCalculatorAlgorithm::processAlgorithm( const QVariantMap &p
return outputs;
}
QgsProcessingAlgorithm::Flags QgsRasterCalculatorModelerAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagHideFromToolbox;
}
QString QgsRasterCalculatorModelerAlgorithm::name() const
{
return QStringLiteral( "modelerrastercalc" );
}
QString QgsRasterCalculatorModelerAlgorithm::displayName() const
{
return QObject::tr( "Raster calculator" );
}
QStringList QgsRasterCalculatorModelerAlgorithm::tags() const
{
return QObject::tr( "raster,calculator" ).split( ',' );
}
QString QgsRasterCalculatorModelerAlgorithm::group() const
{
return QObject::tr( "Raster analysis" );
}
QString QgsRasterCalculatorModelerAlgorithm::groupId() const
{
return QStringLiteral( "rasteranalysis" );
}
QgsRasterCalculatorModelerAlgorithm *QgsRasterCalculatorModelerAlgorithm::createInstance() const
{
return new QgsRasterCalculatorModelerAlgorithm();
}
QVariantMap QgsRasterCalculatorModelerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
for ( QgsMapLayer *layer : std::as_const( mLayers ) )
{
layer->moveToThread( QThread::currentThread() );
}
QgsCoordinateReferenceSystem crs;
if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
{
crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
}
else
{
crs = mLayers.at( 0 )->crs();
}
QgsRectangle bbox;
if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
{
bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
}
else
{
bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
}
double minCellSize = 1e9;
QVector< QgsRasterCalculatorEntry > entries;
int n = 0;
for ( QgsMapLayer *layer : mLayers )
{
QgsRasterLayer *rLayer = static_cast<QgsRasterLayer *>( layer );
if ( !rLayer )
{
continue;
}
n++;
const int nBands = rLayer->dataProvider()->bandCount();
for ( int i = 0; i < nBands; ++i )
{
QgsRasterCalculatorEntry entry;
entry.ref = QStringLiteral( "%1@%2" ).arg( indexToName( n ) ).arg( i + 1 );
entry.raster = rLayer;
entry.bandNumber = i + 1;
entries << entry;
}
QgsRectangle ext = rLayer->extent();
if ( rLayer->crs() != crs )
{
QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
ext = ct.transformBoundingBox( ext );
}
double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
if ( cellSize < minCellSize )
{
minCellSize = cellSize;
}
}
double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
if ( cellSize == 0 )
{
cellSize = minCellSize;
}
const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
const QFileInfo fi( outputFile );
const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
QgsRasterCalculator::Result result = calc.processCalculation( feedback );
qDeleteAll( mLayers );
mLayers.clear();
switch ( result )
{
case QgsRasterCalculator::CreateOutputError:
throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
case QgsRasterCalculator::InputLayerError:
throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
case QgsRasterCalculator::ParserError:
throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
case QgsRasterCalculator::MemoryError:
throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
case QgsRasterCalculator::BandError:
throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
case QgsRasterCalculator::CalculationError:
throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
default:
break;
}
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
return outputs;
}
QString QgsRasterCalculatorModelerAlgorithm::indexToName( int index ) const
{
QString name;
int div = index;
int mod = 0;
while ( div > 0 )
{
mod = ( div - 1 ) % 26;
name = static_cast<char>( 65 + mod ) + name;
div = ( int )( ( div - mod ) / 26 );
}
return name;
}
///@endcond

View File

@ -38,6 +38,7 @@ class QgsRasterCalculatorAlgorithm : public QgsProcessingAlgorithm
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmRasterCalculator.svg" ) ); }
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmRasterCalculator.svg" ) ); }
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
@ -53,10 +54,37 @@ class QgsRasterCalculatorAlgorithm : public QgsProcessingAlgorithm
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
QList< QgsMapLayer * > mLayers;
};
class QgsRasterCalculatorModelerAlgorithm : public QgsRasterCalculatorAlgorithm
{
public:
QgsRasterCalculatorModelerAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QgsRasterCalculatorModelerAlgorithm *createInstance() const override SIP_FACTORY;
protected:
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
/**
* Generates Excel-like names from the number
* A, B, C, , Y, Z, AA, AB, AC, , AZ, BA, BB, BC
*/
QString indexToName( int index ) const;
};
///@endcond PRIVATE
#endif // QGSALGORITHMRASTERCALCULATOR_H

View File

@ -22,7 +22,7 @@
QgsProcessingAlgorithm::Flags QgsVirtualRasterCalculatorAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagNoThreading;
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagNoThreading | FlagHideFromModeler;
}
QString QgsVirtualRasterCalculatorAlgorithm::name() const
@ -62,8 +62,8 @@ QgsVirtualRasterCalculatorAlgorithm *QgsVirtualRasterCalculatorAlgorithm::create
void QgsVirtualRasterCalculatorAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT" ), QObject::tr( "Input layers" ), QgsProcessing::SourceType::TypeRaster ) );
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "INPUT" ), false, Qgis::ExpressionType::RasterCalculator ) );
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::SourceType::TypeRaster ) );
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "LAYERS" ), false, Qgis::ExpressionType::RasterCalculator ) );
std::unique_ptr<QgsProcessingParameterExtent> extentParam = std::make_unique<QgsProcessingParameterExtent>( QStringLiteral( "EXTENT" ), QObject::tr( "Output extent" ), QVariant(), true );
extentParam->setHelp( QObject::tr( "Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
addParameter( extentParam.release() );
@ -81,7 +81,7 @@ QVariantMap QgsVirtualRasterCalculatorAlgorithm::processAlgorithm( const QVarian
{
Q_UNUSED( feedback );
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
if ( layers.isEmpty() )
{
throw QgsProcessingException( QObject::tr( "No input layers selected" ) );
@ -180,4 +180,159 @@ QVariantMap QgsVirtualRasterCalculatorAlgorithm::processAlgorithm( const QVarian
return outputs;
}
QgsProcessingAlgorithm::Flags QgsVirtualRasterCalculatorModelerAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagNoThreading | FlagHideFromToolbox;
}
QString QgsVirtualRasterCalculatorModelerAlgorithm::name() const
{
return QStringLiteral( "modelervirtualrastercalc" );
}
QString QgsVirtualRasterCalculatorModelerAlgorithm::displayName() const
{
return QObject::tr( "Raster calculator (virtual)" );
}
QStringList QgsVirtualRasterCalculatorModelerAlgorithm::tags() const
{
return QObject::tr( "raster,calculator,virtual" ).split( ',' );
}
QString QgsVirtualRasterCalculatorModelerAlgorithm::group() const
{
return QObject::tr( "Raster analysis" );
}
QString QgsVirtualRasterCalculatorModelerAlgorithm::groupId() const
{
return QStringLiteral( "rasteranalysis" );
}
QgsVirtualRasterCalculatorModelerAlgorithm *QgsVirtualRasterCalculatorModelerAlgorithm::createInstance() const
{
return new QgsVirtualRasterCalculatorModelerAlgorithm();
}
QVariantMap QgsVirtualRasterCalculatorModelerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
Q_UNUSED( feedback );
const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
if ( layers.isEmpty() )
{
throw QgsProcessingException( QObject::tr( "No input layers selected" ) );
}
QgsCoordinateReferenceSystem crs;
if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
{
crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
}
else
{
crs = layers.at( 0 )->crs();
}
QgsRectangle bbox;
if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
{
bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
}
else
{
bbox = QgsProcessingUtils::combineLayerExtents( layers, crs, context );
}
double minCellSize = 1e9;
QgsRasterDataProvider::VirtualRasterParameters rasterParameters;
int n = 0;
for ( const QgsMapLayer *layer : layers )
{
const QgsRasterLayer *rLayer = static_cast<const QgsRasterLayer *>( layer );
if ( !rLayer )
{
continue;
}
n++;
QgsRasterDataProvider::VirtualRasterInputLayers rasterLayer;
rasterLayer.name = indexToName( n );
rasterLayer.provider = rLayer->dataProvider()->name();
rasterLayer.uri = rLayer->source();
rasterParameters.rInputLayers.append( rasterLayer );
QgsRectangle ext = rLayer->extent();
if ( rLayer->crs() != crs )
{
QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
ext = ct.transformBoundingBox( ext );
}
double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
if ( cellSize < minCellSize )
{
minCellSize = cellSize;
}
}
double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
if ( cellSize == 0 )
{
cellSize = minCellSize;
}
const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
QString layerName = parameterAsString( parameters, QStringLiteral( "LAYER_NAME" ), context );
if ( layerName.isEmpty() )
{
layerName = expression;
}
double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
rasterParameters.crs = crs;
rasterParameters.extent = bbox;
rasterParameters.width = width;
rasterParameters.height = height;
rasterParameters.formula = expression;
std::unique_ptr< QgsRasterLayer > layer;
layer = std::make_unique< QgsRasterLayer >( QgsRasterDataProvider::encodeVirtualRasterProviderUri( rasterParameters ),
layerName, QStringLiteral( "virtualraster" ) );
if ( !layer->isValid() )
{
feedback->reportError( QObject::tr( "Failed to create virtual raster layer" ) );
}
else
{
}
const QString layerId = layer->id();
const QgsProcessingContext::LayerDetails details( layer->name(), context.project(), QStringLiteral( "OUTPUT" ), QgsProcessingUtils::LayerHint::Raster );
context.addLayerToLoadOnCompletion( layerId, details );
context.temporaryLayerStore()->addMapLayer( layer.release() );
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), layerId );
return outputs;
}
QString QgsVirtualRasterCalculatorModelerAlgorithm::indexToName( int index ) const
{
QString name;
int div = index;
int mod = 0;
while ( div > 0 )
{
mod = ( div - 1 ) % 26;
name = static_cast<char>( 65 + mod ) + name;
div = ( int )( ( div - mod ) / 26 );
}
return name;
}
///@endcond

View File

@ -52,10 +52,36 @@ class QgsVirtualRasterCalculatorAlgorithm : public QgsProcessingAlgorithm
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
QList< QgsMapLayer * > mLayers;
};
class QgsVirtualRasterCalculatorModelerAlgorithm : public QgsVirtualRasterCalculatorAlgorithm
{
public:
QgsVirtualRasterCalculatorModelerAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QgsVirtualRasterCalculatorModelerAlgorithm *createInstance() const override SIP_FACTORY;
protected:
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
/**
* Generates Excel-like names from the number
* A, B, C, , Y, Z, AA, AB, AC, , AZ, BA, BB, BC
*/
QString indexToName( int index ) const;
};
///@endcond PRIVATE
#endif // QGSALGORITHMVIRTUALRASTERCALCULATOR_H

View File

@ -443,6 +443,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsRandomPoissonRasterAlgorithm() );
addAlgorithm( new QgsRandomUniformRasterAlgorithm() );
addAlgorithm( new QgsRasterCalculatorAlgorithm() );
addAlgorithm( new QgsRasterCalculatorModelerAlgorithm() );
addAlgorithm( new QgsRasterDtmSlopeBasedFilterAlgorithm() );
addAlgorithm( new QgsRasterFrequencyByEqualOperatorAlgorithm() );
addAlgorithm( new QgsRasterFrequencyByGreaterThanOperatorAlgorithm() );
@ -525,6 +526,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsUnionAlgorithm() );
addAlgorithm( new QgsVariableWidthBufferByMAlgorithm() );
addAlgorithm( new QgsVirtualRasterCalculatorAlgorithm() );
addAlgorithm( new QgsVirtualRasterCalculatorModelerAlgorithm() );
addAlgorithm( new QgsWedgeBuffersAlgorithm() );
addAlgorithm( new QgsWriteVectorTilesXyzAlgorithm() );
addAlgorithm( new QgsWriteVectorTilesMbtilesAlgorithm() );

View File

@ -176,7 +176,7 @@ QString QgsProcessingRasterCalculatorExpressionDialog::quoteBandEntry( const QSt
void QgsProcessingRasterCalculatorExpressionDialog::mLayersList_itemDoubleClicked( QListWidgetItem *item )
{
mExpressionTextEdit->insertPlainText( quoteBandEntry( item->text() ) );
mExpressionTextEdit->insertPlainText( quoteBandEntry( QStringLiteral( "%1@1" ).arg( item->text() ) ) );
}
void QgsProcessingRasterCalculatorExpressionDialog::mBtnPlus_clicked()

View File

@ -2303,6 +2303,10 @@ QWidget *QgsProcessingExpressionWidgetWrapper::createWidget()
{
mRasterCalculatorExpLineEdit = new QgsProcessingRasterCalculatorExpressionLineEdit();
mRasterCalculatorExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
if ( type() == QgsProcessingGui::Modeler )
{
mRasterCalculatorExpLineEdit->setLayers( QVariantList() << "A" << "B" << "C" << "D" << "E" << "F" << "G" );
}
connect( mRasterCalculatorExpLineEdit, &QgsProcessingRasterCalculatorExpressionLineEdit::expressionChanged, this, [ = ]( const QString & )
{
emit widgetValueHasChanged( this );