Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Partial port of raster calculator to new API
TODO: modeler handling
  • Loading branch information
nyalldawson committed Aug 20, 2017
1 parent 338ee36 commit 4d242c5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 76 deletions.
5 changes: 2 additions & 3 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -124,6 +124,7 @@
from .RandomSelection import RandomSelection
from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
from .Rasterize import RasterizeAlgorithm
from .RasterCalculator import RasterCalculator
from .RasterLayerStatistics import RasterLayerStatistics
from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed
from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable
Expand Down Expand Up @@ -172,7 +173,6 @@
# from .SpatialJoin import SpatialJoin
# from .GeometryConvert import GeometryConvert
# from .SelectByAttributeSum import SelectByAttributeSum
# from .RasterCalculator import RasterCalculator

pluginPath = os.path.normpath(os.path.join(
os.path.split(os.path.dirname(__file__))[0], os.pardir))
Expand All @@ -192,8 +192,6 @@ def getAlgs(self):
# SpatialJoin(),
# GeometryConvert(),
# SelectByAttributeSum()
# RasterCalculator(),
#
# ]
algs = [AddTableField(),
Aggregate(),
Expand Down Expand Up @@ -278,6 +276,7 @@ def getAlgs(self):
RandomPointsPolygons(),
RandomSelection(),
RandomSelectionWithinSubsets(),
RasterCalculator(),
RasterizeAlgorithm(),
RasterLayerStatistics(),
RectanglesOvalsDiamondsFixed(),
Expand Down
99 changes: 55 additions & 44 deletions python/plugins/processing/algs/qgis/RasterCalculator.py
Expand Up @@ -16,7 +16,7 @@
* *
***************************************************************************
"""
from processing.modeler.ModelerAlgorithm import ValueFromInput, ValueFromOutput

import os

__author__ = 'Victor Olaya'
Expand All @@ -29,17 +29,18 @@

import math
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterMultipleInput, ParameterExtent, ParameterString, ParameterRaster, ParameterNumber
from processing.core.outputs import OutputRaster
from processing.tools import dataobjects
from processing.algs.gdal.GdalUtils import GdalUtils
from qgis.core import (QgsApplication,
QgsRectangle,
from qgis.core import (QgsProcessing,
QgsProcessingException,
QgsProcessingUtils,
QgsProject)
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterNumber,
QgsProcessingParameterExtent,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterRasterLayer,
QgsProcessingOutputRasterLayer,
QgsProcessingParameterString)
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.algs.qgis.ui.RasterCalculatorWidgets import LayersListWidgetWrapper, ExpressionWidgetWrapper


class RasterCalculator(QgisAlgorithm):
Expand All @@ -57,41 +58,54 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterMultipleInput(self.LAYERS,
self.tr('Input layers'),
datatype=dataobjects.TYPE_RASTER,
optional=True,
metadata={'widget_wrapper': LayersListWidgetWrapper}))
layer_param = QgsProcessingParameterMultipleLayers(self.LAYERS,
self.tr('Input layers'),
layerType=QgsProcessing.TypeRaster,
optional=True)
layer_param.setMetadata({'widget_wrapper': 'processing.algs.qgis.ui.RasterCalculatorWidgets.LayersListWidgetWrapper'})
self.addParameter(layer_param)

class ParameterRasterCalculatorExpression(QgsProcessingParameterString):

def __init__(self, name='', description='', multiLine=False):
super().__init__(name, description, multiLine=multiLine)
self.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.RasterCalculatorWidgets.ExpressionWidgetWrapper'
})

class ParameterRasterCalculatorExpression(ParameterString):
def type(self):
return 'raster_calc_expression'

def clone(self):
return ParameterRasterCalculatorExpression(self.name(), self.description(), self.multiLine())

def evaluateForModeler(self, value, model):
for i in list(model.inputs.values()):
param = i.param
if isinstance(param, ParameterRaster):
if isinstance(param, QgsProcessingParameterRasterLayer):
new = "{}@".format(os.path.basename(param.value))
old = "{}@".format(param.name())
value = value.replace(old, new)

for alg in list(model.algs.values()):
for out in alg.algorithm.outputs:
if isinstance(out, OutputRaster):
if isinstance(out, QgsProcessingOutputRasterLayer):
if out.value:
new = "{}@".format(os.path.basename(out.value))
old = "{}:{}@".format(alg.modeler_name, out.name)
value = value.replace(old, new)
return value

self.addParameter(ParameterRasterCalculatorExpression(self.EXPRESSION, self.tr('Expression'),
multiline=True,
metadata={'widget_wrapper': ExpressionWidgetWrapper}))
self.addParameter(ParameterNumber(self.CELLSIZE,
self.tr('Cell size (use 0 or empty to set it automatically)'),
minValue=0.0, default=0.0, optional=True))
self.addParameter(ParameterExtent(self.EXTENT,
self.tr('Output extent'),
optional=True))
self.addOutput(OutputRaster(self.OUTPUT, self.tr('Output')))
multiLine=True))
self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE,
self.tr('Cell size (use 0 or empty to set it automatically)'),
type=QgsProcessingParameterNumber.Double,
minValue=0.0, defaultValue=0.0, optional=True))
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
self.tr('Output extent'),
optional=True))
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output')))

def name(self):
return 'rastercalculator'
Expand All @@ -100,11 +114,10 @@ def displayName(self):
return self.tr('Raster calculator')

def processAlgorithm(self, parameters, context, feedback):
expression = self.getParameterValue(self.EXPRESSION)
layersValue = self.getParameterValue(self.LAYERS)
expression = self.parameterAsString(parameters, self.EXPRESSION, context)
layers = self.parameterAsLayerList(parameters, self.LAYERS, context)
layersDict = {}
if layersValue:
layers = [QgsProcessingUtils.mapLayerFromString(f, context) for f in layersValue.split(";")]
if layers:
layersDict = {os.path.basename(lyr.source().split(".")[0]): lyr for lyr in layers}

for lyr in QgsProcessingUtils.compatibleRasterLayers(context.project()):
Expand All @@ -121,26 +134,22 @@ def processAlgorithm(self, parameters, context, feedback):
entry.bandNumber = n + 1
entries.append(entry)

output = self.getOutputValue(self.OUTPUT)
extentValue = self.getParameterValue(self.EXTENT)
if not extentValue:
extentValue = QgsProcessingUtils.combineLayerExtents(layersValue)
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
if bbox.isNull():
bbox = QgsProcessingUtils.combineLayerExtents(layers)

if extentValue:
extent = extentValue.split(',')
bbox = QgsRectangle(float(extent[0]), float(extent[2]),
float(extent[1]), float(extent[3]))
else:
if bbox.isNull():
if layersDict:
bbox = list(layersDict.values())[0].extent()
for lyr in layersDict.values():
bbox.combineExtentWith(lyr.extent())
else:
raise GeoAlgorithmExecutionException(self.tr("No layers selected"))
raise QgsProcessingException(self.tr("No layers selected"))

def _cellsize(layer):
return (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width()
cellsize = self.getParameterValue(self.CELLSIZE) or min([_cellsize(lyr) for lyr in layersDict.values()])
cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context) or min([_cellsize(lyr) for lyr in layersDict.values()])
width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize)
height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize)
driverName = GdalUtils.getFormatShortNameFromFilename(output)
Expand All @@ -154,14 +163,16 @@ def _cellsize(layer):

res = calc.processCalculation()
if res == QgsRasterCalculator.ParserError:
raise GeoAlgorithmExecutionException(self.tr("Error parsing formula"))
raise QgsProcessingException(self.tr("Error parsing formula"))

return {self.OUTPUT: output}

def processBeforeAddingToModeler(self, algorithm, model):
values = []
expression = algorithm.params[self.EXPRESSION]
for i in list(model.inputs.values()):
param = i.param
if isinstance(param, ParameterRaster) and "{}@".format(param.name) in expression:
if isinstance(param, QgsProcessingParameterRasterLayer) and "{}@".format(param.name) in expression:
values.append(ValueFromInput(param.name()))

if algorithm.name:
Expand All @@ -171,7 +182,7 @@ def processBeforeAddingToModeler(self, algorithm, model):
for alg in list(model.algs.values()):
if alg.modeler_name not in dependent:
for out in alg.algorithm.outputs:
if (isinstance(out, OutputRaster) and
if (isinstance(out, QgsProcessingOutputRasterLayer) and
"{}:{}@".format(alg.modeler_name, out.name) in expression):
values.append(ValueFromOutput(alg.modeler_name, out.name))

Expand Down
58 changes: 29 additions & 29 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -2210,35 +2210,35 @@ tests:
- 'Maximum value: 15:29:22'
- 'NULL \(missing\) values: 1'

# - algorithm: qgis:rastercalculator
# name: Raster Calculator with cellsize
# params:
# LAYERS:
# params:
# - name: dem.tif
# type: raster
# type: multi
# CELLSIZE: 0.001
# EXPRESSION: dem@1
# results:
# OUTPUT:
# hash: ef97a22ee16e0e28bbdc0341449777b1527e37febc3c4339b2c057c9
# type: rasterhash
#
# - algorithm: qgis:rastercalculator
# name: Raster Calculator
# params:
# LAYERS:
# params:
# - name: dem.tif
# type: raster
# type: multi
# CELLSIZE: 0.0
# EXPRESSION: dem@1 * 2
# results:
# OUTPUT:
# hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939
# type: rasterhash
- algorithm: qgis:rastercalculator
name: Raster Calculator with cellsize
params:
LAYERS:
params:
- name: dem.tif
type: raster
type: multi
CELLSIZE: 0.001
EXPRESSION: dem@1
results:
OUTPUT:
hash: ef97a22ee16e0e28bbdc0341449777b1527e37febc3c4339b2c057c9
type: rasterhash

- algorithm: qgis:rastercalculator
name: Raster Calculator
params:
LAYERS:
params:
- name: dem.tif
type: raster
type: multi
CELLSIZE: 0.0
EXPRESSION: dem@1 * 2
results:
OUTPUT:
hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939
type: rasterhash

- algorithm: qgis:orientedminimumboundingbox
name: Oriented minimum bounding box polys
Expand Down

0 comments on commit 4d242c5

Please sign in to comment.