Skip to content
Permalink
Browse files

[processing] evaluate parameters before executing algorithm

This allows a better use of expressions
  • Loading branch information
volaya committed Oct 5, 2016
1 parent 01f3808 commit e353d226a8cc0904bfbd6b5809f88bbd9de45798
@@ -199,15 +199,15 @@ def execute(self, progress=SilentProgress(), model=None):
self.setOutputCRS()
self.resolveTemporaryOutputs()
self.resolveDataObjects()
self.resolveMinCoveringExtent()
self.evaluateParameterValues()
self.checkOutputFileExtensions()
self.runPreExecutionScript(progress)
self.processAlgorithm(progress)
progress.setPercentage(100)
self.convertUnsupportedFormats(progress)
self.runPostExecutionScript(progress)
except GeoAlgorithmExecutionException as gaee:
lines = [self.tr('Uncaught error while executing algorithm')]
lines = [self.tr('Error while executing algorithm')]
lines.append(traceback.format_exc())
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, gaee.msg)
raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
@@ -340,11 +340,9 @@ def checkOutputFileExtensions(self):
if not os.path.isabs(out.value):
continue
if isinstance(out, OutputRaster):
exts = \
dataobjects.getSupportedOutputRasterLayerExtensions()
exts = dataobjects.getSupportedOutputRasterLayerExtensions()
elif isinstance(out, OutputVector):
exts = \
dataobjects.getSupportedOutputVectorLayerExtensions()
exts = dataobjects.getSupportedOutputVectorLayerExtensions()
elif isinstance(out, OutputTable):
exts = dataobjects.getSupportedOutputTableExtensions()
elif isinstance(out, OutputHTML):
@@ -360,60 +358,12 @@ def checkOutputFileExtensions(self):
out.value = out.value + '.' + exts[0]


def canUseAutoExtent(self):
for param in self.parameters:
if isinstance(param, (ParameterRaster, ParameterVector)):
return True
if isinstance(param, ParameterMultipleInput):
return True
return False

def resolveMinCoveringExtent(self):
for param in self.parameters:
if isinstance(param, ParameterExtent):
if param.value is None:
param.value = self.getMinCoveringExtent()

def getMinCoveringExtent(self):
first = True
found = False
def evaluateParameterValues(self):
for param in self.parameters:
if param.value:
if isinstance(param, (ParameterRaster, ParameterVector)):
if isinstance(param.value, (QgsRasterLayer,
QgsVectorLayer)):
layer = param.value
else:
layer = dataobjects.getObject(param.value)
if layer:
found = True
self.addToRegion(layer, first)
first = False
elif isinstance(param, ParameterMultipleInput):
layers = param.value.split(';')
for layername in layers:
layer = dataobjects.getObject(layername)
if layer:
found = True
self.addToRegion(layer, first)
first = False
if found:
return '{},{},{},{}'.format(
self.xmin, self.xmax, self.ymin, self.ymax)
else:
return None

def addToRegion(self, layer, first):
if first:
self.xmin = layer.extent().xMinimum()
self.xmax = layer.extent().xMaximum()
self.ymin = layer.extent().yMinimum()
self.ymax = layer.extent().yMaximum()
else:
self.xmin = min(self.xmin, layer.extent().xMinimum())
self.xmax = max(self.xmax, layer.extent().xMaximum())
self.ymin = min(self.ymin, layer.extent().yMinimum())
self.ymax = max(self.ymax, layer.extent().yMaximum())
try:
param.evaluate(self)
except ValueError, e:
raise GeoAlgorithmExecutionException(str(e))

def resolveTemporaryOutputs(self):
"""Sets temporary outputs (output.value = None) with a
@@ -33,11 +33,12 @@
from inspect import isclass
from copy import deepcopy


from qgis.utils import iface
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinateReferenceSystem
from qgis.core import (QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinateReferenceSystem,
QgsExpressionContext, QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope)

from processing.tools.vector import resolveFieldIndex, features
from processing.tools.system import isWindows
from processing.tools import dataobjects

def parseBool(s):
@@ -58,7 +59,47 @@ def _splitParameterOptions(line):
def _createDescriptiveName(s):
return s.replace('_', ' ')

class Parameter(object):
def _expressionContext():
context = QgsExpressionContext()
context.appendScope(QgsExpressionContextUtils.globalScope())
context.appendScope(QgsExpressionContextUtils.projectScope())
processingScope = QgsExpressionContextScope()
layers = dataobjects.getAllLayers()
for layer in layers:
name = layer.name()
processingScope.setVariable('%s_minx' % name, layer.extent().xMinimum())
processingScope.setVariable('%s_miny' % name, layer.extent().yMinimum())
processingScope.setVariable('%s_maxx' % name, layer.extent().xMaximum())
processingScope.setVariable('%s_maxy' % name, layer.extent().yMaximum())
if isinstance(layer, QgsRasterLayer):
cellsize = (layer.extent().xMaximum()
- layer.extent().xMinimum()) / layer.width()
processingScope.setVariable('%s_cellsize' % name, cellsize)

layers = dataobjects.getRasterLayers()
for layer in layers:
for i in range(layer.bandCount()):
stats = layer.dataProvider().bandStatistics(i + 1)
processingScope.setVariable('%s_band%i_avg' % (name, i + 1), stats.mean)
processingScope.setVariable('%s_band%i_stddev' % (name, i + 1), stats.stdDev)
processingScope.setVariable('%s_band%i_min' % (name, i + 1), stats.minimumValue)
processingScope.setVariable('%s_band%i_max' % (name, i + 1), stats.maximumValue)

extent = iface.mapCanvas().extent()
processingScope.setVariable('canvasextent_minx', extent.xMinimum())
processingScope.setVariable('canvasextent_miny', extent.yMinimum())
processingScope.setVariable('canvasextent_maxx', extent.xMaximum())
processingScope.setVariable('canvasextent_maxy', extent.yMaximum())

extent = iface.mapCanvas().fullExtent()
processingScope.setVariable('fullextent_minx', extent.xMinimum())
processingScope.setVariable('fullextent_miny', extent.yMinimum())
processingScope.setVariable('fullextent_maxx', extent.xMaximum())
processingScope.setVariable('fullextent_maxy', extent.yMaximum())
context.appendScope(processingScope)
return context

class Parameter:

"""
Base class for all parameters that a geoalgorithm might
@@ -148,6 +189,9 @@ def wrapper(self, dialog, row=0, col=0):
wrapper = wrapper(self, dialog, row, col)
# or a wrapper instance
return wrapper

def evaluate(self, alg):
pass

class ParameterBoolean(Parameter):

@@ -330,6 +374,51 @@ def fromScriptCode(self, line):
default = definition.strip()[len('extent') + 1:] or None
return ParameterExtent(name, descName, default, isOptional)

def evaluate(self, alg):
if self.optional and not bool(self.value):
self.value = self.getMinCoveringExtent()

def getMinCoveringExtent(self, alg):
first = True
found = False
for param in alg.parameters:
if param.value:
if isinstance(param, (ParameterRaster, ParameterVector)):
if isinstance(param.value, (QgsRasterLayer,
QgsVectorLayer)):
layer = param.value
else:
layer = dataobjects.getObject(param.value)
if layer:
found = True
self.addToRegion(layer, first)
first = False
elif isinstance(param, ParameterMultipleInput):
layers = param.value.split(';')
for layername in layers:
layer = dataobjects.getObject(layername)
if layer:
found = True
self.addToRegion(layer, first)
first = False
if found:
return '{},{},{},{}'.format(
self.xmin, self.xmax, self.ymin, self.ymax)
else:
return None

def addToRegion(self, layer, first):
if first:
self.xmin = layer.extent().xMinimum()
self.xmax = layer.extent().xMaximum()
self.ymin = layer.extent().yMinimum()
self.ymax = layer.extent().yMaximum()
else:
self.xmin = min(self.xmin, layer.extent().xMinimum())
self.xmax = max(self.xmax, layer.extent().xMaximum())
self.ymin = min(self.ymin, layer.extent().yMinimum())
self.ymax = max(self.ymax, layer.extent().yMaximum())


class ParameterPoint(Parameter):

@@ -715,21 +804,30 @@ def setValue(self, n):
self.value = None
return True

try:
if float(n) - int(float(n)) == 0:
value = int(float(n))
else:
value = float(n)
if self.min is not None:
if value < self.min:
return False
if self.max is not None:
if value > self.max:
return False
self.value = value
return True
except:
return False
if isinstance(n, basestring):
try:
v = self._evaluate(n)
float(v)
self.value = n
return True
except:
return False
else:
try:
if float(n) - int(float(n)) == 0:
value = int(float(n))
else:
value = float(n)
if self.min is not None:
if value < self.min:
return False
if self.max is not None:
if value > self.max:
return False
self.value = value
return True
except:
return False

def getAsScriptCode(self):
param_type = ''
@@ -745,6 +843,31 @@ def fromScriptCode(self, line):
if definition.lower().strip().startswith('number'):
default = definition.strip()[len('number') + 1:] or None
return ParameterNumber(name, descName, default=default, optional=isOptional)

def _evaluate(self, v):
exp = QgsExpression(v)
if exp.hasParserError():
raise ValueError(self.tr("Error in parameter expression: ") + exp.parserErrorString())
result = exp.evaluate(_expressionContext())
if exp.hasEvalError():
raise ValueError("Error evaluating parameter expression: " + exp.evalErrorString())
return result

def evaluate(self, alg):
if isinstance(self.value, basestring):
self.value = self._evaluate(self.value)

def expressionContext(self):
return _expressionContext()

def getValueAsCommandLineParameter(self):
if self.value is None:
return str(None)
if isinstance(self.value, basestring):
return '"%s"' % self.value
return str(self.value)



class ParameterRange(Parameter):

@@ -919,6 +1042,13 @@ def fromScriptCode(self, line):
return ParameterSelection(name, descName, options, optional=isOptional)


class ParameterEvaluationException(Exception):

def __init__(self, param, msg):
Exception.__init__(msg)
self.param = param


class ParameterString(Parameter):

default_metadata = {
@@ -976,6 +1106,18 @@ def fromScriptCode(self, line):
return ParameterString(name, descName, default, multiline=True, optional=isOptional)
else:
return ParameterString(name, descName, multiline=True, optional=isOptional)

def evaluate(self, alg):
exp = QgsExpression(self.value)
if exp.hasParserError():
raise ValueError(self.tr("Error in parameter expression: ") + exp.parserErrorString())
result = exp.evaluate(_expressionContext())
if exp.hasEvalError():
raise ValueError("Error evaluating parameter expression: " + exp.evalErrorString())
self.value = result

def expressionContext(self):
return _expressionContext()

class ParameterTable(ParameterDataObject):

@@ -31,7 +31,7 @@
from qgis.PyQt.QtWidgets import QMessageBox, QApplication, QPushButton, QWidget, QVBoxLayout
from qgis.PyQt.QtGui import QCursor, QColor, QPalette

from qgis.core import QgsMapLayerRegistry, QgsExpressionContext, QgsExpressionContextUtils, QgsExpression
from qgis.core import QgsMapLayerRegistry

from processing.core.ProcessingLog import ProcessingLog
from processing.core.ProcessingConfig import ProcessingConfig
@@ -62,8 +62,6 @@
from processing.core.outputs import OutputVector
from processing.core.outputs import OutputTable

from processing.tools import dataobjects


class AlgorithmDialog(AlgorithmDialogBase):

@@ -114,18 +112,6 @@ def setParamValues(self):

return True

def evaluateExpression(self, text):
context = QgsExpressionContext()
context.appendScope(QgsExpressionContextUtils.globalScope())
context.appendScope(QgsExpressionContextUtils.projectScope())
exp = QgsExpression(text)
if exp.hasParserError():
raise Exception(exp.parserErrorString())
result = exp.evaluate(context)
if exp.hasEvalError():
raise ValueError(exp.evalErrorString())
return result

def setParamValue(self, param, wrapper, alg=None):
return param.setValue(wrapper.value())

0 comments on commit e353d22

Please sign in to comment.
You can’t perform that action at this time.