Skip to content

Commit

Permalink
add modeler support and deprecate Python implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Sep 25, 2023
1 parent 8d52b54 commit 3c1f510
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 14 deletions.
4 changes: 4 additions & 0 deletions python/plugins/processing/algs/qgis/RasterCalculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.algs.gdal.GdalUtils import GdalUtils
from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterCrs,
Expand Down Expand Up @@ -88,6 +89,9 @@ def clone(self):
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'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Down
166 changes: 163 additions & 3 deletions src/analysis/processing/qgsalgorithmrastercalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsRasterCalculatorAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagHideFromModeler;
}

QString QgsRasterCalculatorAlgorithm::name() const
{
return QStringLiteral( "rastercalc" );
Expand Down Expand Up @@ -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() );
Expand All @@ -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 ) )
{
Expand Down Expand Up @@ -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

30 changes: 29 additions & 1 deletion src/analysis/processing/qgsalgorithmrastercalculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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

0 comments on commit 3c1f510

Please sign in to comment.