Skip to content

Commit 2fc5c01

Browse files
committed
[processing] port QGIS native field calculator
1 parent f1551df commit 2fc5c01

7 files changed

+737
-41
lines changed

python/plugins/processing/algs/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ FILE(GLOB PY_FILES *.py)
22

33
ADD_SUBDIRECTORY(mmqgisx)
44
ADD_SUBDIRECTORY(ftools)
5+
ADD_SUBDIRECTORY(ui)
56

67
PLUGIN_INSTALL(processing ./algs ${PY_FILES})

python/plugins/processing/algs/FieldsCalculator.py

+84-41
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,33 @@
2727

2828
from PyQt4.QtCore import *
2929
from qgis.core import *
30+
from processing import interface
3031
from processing.core.GeoAlgorithm import GeoAlgorithm
32+
from processing.core.GeoAlgorithmExecutionException import \
33+
GeoAlgorithmExecutionException
3134
from processing.parameters.ParameterVector import ParameterVector
3235
from processing.parameters.ParameterString import ParameterString
3336
from processing.parameters.ParameterNumber import ParameterNumber
37+
from processing.parameters.ParameterBoolean import ParameterBoolean
3438
from processing.parameters.ParameterSelection import ParameterSelection
3539
from processing.outputs.OutputVector import OutputVector
3640
from processing.tools import dataobjects, vector
3741

42+
from processing.algs.ui.FieldsCalculatorDialog import FieldsCalculatorDialog
3843

3944
class FieldsCalculator(GeoAlgorithm):
4045

4146
INPUT_LAYER = 'INPUT_LAYER'
47+
NEW_FIELD = 'NEW_FIELD'
4248
FIELD_NAME = 'FIELD_NAME'
4349
FIELD_TYPE = 'FIELD_TYPE'
4450
FIELD_LENGTH = 'FIELD_LENGTH'
4551
FIELD_PRECISION = 'FIELD_PRECISION'
4652
FORMULA = 'FORMULA'
4753
OUTPUT_LAYER = 'OUTPUT_LAYER'
4854

49-
TYPE_NAMES = ['Float', 'Integer', 'String']
50-
TYPES = [QVariant.Double, QVariant.Int, QVariant.String]
55+
TYPE_NAMES = ['Float', 'Integer', 'String', 'Date']
56+
TYPES = [QVariant.Double, QVariant.Int, QVariant.String, QVariant.Date]
5157

5258
def defineCharacteristics(self):
5359
self.name = 'Field calculator'
@@ -61,58 +67,95 @@ def defineCharacteristics(self):
6167
self.addParameter(ParameterNumber(self.FIELD_LENGTH, 'Field length',
6268
1, 255, 10))
6369
self.addParameter(ParameterNumber(self.FIELD_PRECISION,
64-
'Field precision', 0, 10, 5))
70+
'Field precision', 0, 15, 3))
71+
self.addParameter(ParameterBoolean(self.NEW_FIELD,
72+
'Create new field', True))
6573
self.addParameter(ParameterString(self.FORMULA, 'Formula'))
6674
self.addOutput(OutputVector(self.OUTPUT_LAYER, 'Output layer'))
6775

6876
def processAlgorithm(self, progress):
77+
layer = dataobjects.getObjectFromUri(
78+
self.getParameterValue(self.INPUT_LAYER))
6979
fieldName = self.getParameterValue(self.FIELD_NAME)
70-
fieldType = self.getParameterValue(self.FIELD_TYPE)
71-
fieldLength = self.getParameterValue(self.FIELD_LENGTH)
72-
fieldPrecision = self.getParameterValue(self.FIELD_PRECISION)
80+
fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)]
81+
width = self.getParameterValue(self.FIELD_LENGTH)
82+
precision = self.getParameterValue(self.FIELD_PRECISION)
83+
newField = self.getParameterValue(self.NEW_FIELD)
7384
formula = self.getParameterValue(self.FORMULA)
85+
7486
output = self.getOutputFromName(self.OUTPUT_LAYER)
7587

76-
layer = dataobjects.getObjectFromUri(
77-
self.getParameterValue(self.INPUT_LAYER))
7888
provider = layer.dataProvider()
79-
fields = provider.fields()
80-
fields.append(QgsField(fieldName, self.TYPES[fieldType], '',
81-
fieldLength, fieldPrecision))
89+
fields = layer.pendingFields()
90+
if newField:
91+
fields.append(QgsField(fieldName, fieldType, '', width, precision))
92+
8293
writer = output.getVectorWriter(fields, provider.geometryType(),
83-
layer.crs())
94+
layer.crs())
95+
96+
exp = QgsExpression(formula)
97+
98+
da = QgsDistanceArea()
99+
da.setSourceCrs(layer.crs().srsid())
100+
canvas = interface.iface.mapCanvas()
101+
da.setEllipsoidalMode(canvas.mapRenderer().hasCrsTransformEnabled())
102+
da.setEllipsoid(QgsProject.instance().readEntry('Measure',
103+
'/Ellipsoid',
104+
GEO_NONE)[0])
105+
exp.setGeomCalculator(da)
106+
107+
if not exp.prepare(layer.pendingFields()):
108+
raise GeoAlgorithmExecutionException(
109+
'Evaluation error: ' + exp.evalErrorString())
84110

85-
outFeat = QgsFeature()
86-
inGeom = QgsGeometry()
87-
nFeat = provider.featureCount()
88-
nElement = 0
111+
outFeature = QgsFeature()
112+
outFeature.initAttributes(len(fields))
113+
outFeature.setFields(fields)
114+
115+
error = ''
116+
calculationSuccess = True
117+
118+
current = 0
89119
features = vector.features(layer)
120+
total = 100.0 / len(features)
121+
122+
rownum = 1
123+
for f in features:
124+
exp.setCurrentRowNumber(rownum)
125+
value = exp.evaluate(f)
126+
if exp.hasEvalError():
127+
calculationSuccess = False
128+
error = exp.evalErrorString()
129+
break
130+
else:
131+
outFeature.setGeometry(f.geometry())
132+
for fld in f.fields():
133+
outFeature[fld.name()] = f[fld.name()]
134+
outFeature[fieldName] = value
135+
writer.addFeature(outFeature)
90136

91-
fieldnames = [field.name() for field in provider.fields()]
92-
fieldnames.sort(key=len, reverse=False)
93-
fieldidx = [fieldnames.index(field.name()) for field in
94-
provider.fields()]
95-
print fieldidx
96-
for inFeat in features:
97-
progress.setPercentage(int(100 * nElement / nFeat))
98-
attrs = inFeat.attributes()
99-
expression = formula
100-
for idx in fieldidx:
101-
expression = expression.replace(unicode(fields[idx].name()),
102-
unicode(attrs[idx]))
103-
try:
104-
result = eval(expression)
105-
except Exception:
106-
result = None
107-
nElement += 1
108-
inGeom = inFeat.geometry()
109-
outFeat.setGeometry(inGeom)
110-
attrs = inFeat.attributes()
111-
attrs.append(result)
112-
outFeat.setAttributes(attrs)
113-
writer.addFeature(outFeat)
137+
current += 1
138+
progress.setPercentage(int(current * total))
114139
del writer
115140

141+
if not calculationSuccess:
142+
raise GeoAlgorithmExecutionException(
143+
'An error occured while evaluating the calculation '
144+
'string:\n' + error)
145+
146+
116147
def checkParameterValuesBeforeExecuting(self):
117-
# TODO check that formula is correct and fields exist
118-
pass
148+
newField = self.getParameterValue(self.NEW_FIELD)
149+
fieldName = self.getParameterValue(self.FIELD_NAME)
150+
if newField and len(fieldName) == 0:
151+
raise GeoAlgorithmExecutionException('Field name is not set. '
152+
'Please enter a field name')
153+
154+
outputName = self.getOutputValue(self.OUTPUT_LAYER)
155+
if outputName == '':
156+
raise GeoAlgorithmExecutionException('Output is not set. '
157+
'Please specify valid filename')
158+
159+
160+
def getCustomParametersDialog(self):
161+
return FieldsCalculatorDialog(self)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FILE(GLOB PY_FILES *.py)
2+
3+
FILE(GLOB UI_FILES *.ui)
4+
PYQT4_WRAP_UI(PYUI_FILES ${UI_FILES})
5+
6+
PLUGIN_INSTALL(processing ./algs/ui ${PY_FILES} ${PYUI_FILES})

0 commit comments

Comments
 (0)