Skip to content

Commit b3a9e46

Browse files
arnaud-morvanm-kuhn
authored andcommittedAug 14, 2017
[processing] Port refactor fields to new API
1 parent 2364801 commit b3a9e46

File tree

10 files changed

+409
-310
lines changed

10 files changed

+409
-310
lines changed
 

‎python/core/processing/qgsprocessingoutputs.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class QgsProcessingOutputDefinition
4040
sipType = sipType_QgsProcessingOutputString;
4141
else if ( sipCpp->type() == QgsProcessingOutputFolder::typeName() )
4242
sipType = sipType_QgsProcessingOutputFolder;
43+
else
44+
sipType = nullptr;
4345
%End
4446
public:
4547

‎python/core/processing/qgsprocessingparameters.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ class QgsProcessingParameterDefinition
191191
sipType = sipType_QgsProcessingParameterFolderDestination;
192192
else if ( sipCpp->type() == QgsProcessingParameterBand::typeName() )
193193
sipType = sipType_QgsProcessingParameterBand;
194+
else
195+
sipType = nullptr;
194196
%End
195197
public:
196198

‎python/plugins/processing/algs/qgis/FieldsMapper.py

Lines changed: 102 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -25,159 +25,141 @@
2525

2626
__revision__ = '$Format:%H$'
2727

28-
from qgis.core import (QgsField,
29-
QgsFields,
30-
QgsExpression,
31-
QgsDistanceArea,
32-
QgsFeatureSink,
33-
QgsProject,
34-
QgsFeature,
35-
QgsApplication,
36-
QgsProcessingUtils)
37-
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
38-
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
39-
from processing.core.parameters import ParameterTable
40-
from processing.core.parameters import Parameter
41-
from processing.core.outputs import OutputVector
42-
43-
44-
class FieldsMapper(QgisAlgorithm):
28+
from qgis.core import (
29+
QgsApplication,
30+
QgsDistanceArea,
31+
QgsExpression,
32+
QgsFeature,
33+
QgsFeatureSink,
34+
QgsField,
35+
QgsFields,
36+
QgsProcessingException,
37+
QgsProcessingParameterDefinition,
38+
QgsProcessingUtils,
39+
QgsProject,
40+
)
41+
42+
from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm
43+
44+
45+
class FieldsMapper(QgisFeatureBasedAlgorithm):
4546

4647
INPUT_LAYER = 'INPUT_LAYER'
4748
FIELDS_MAPPING = 'FIELDS_MAPPING'
4849
OUTPUT_LAYER = 'OUTPUT_LAYER'
4950

50-
def __init__(self):
51-
GeoAlgorithm.__init__(self)
52-
self.mapping = None
53-
5451
def group(self):
5552
return self.tr('Vector table tools')
5653

57-
def __init__(self):
58-
super().__init__()
59-
60-
def initAlgorithm(self, config=None):
61-
self.addParameter(ParameterTable(self.INPUT_LAYER,
62-
self.tr('Input layer'),
63-
False))
54+
def initParameters(self, config=None):
6455

65-
class ParameterFieldsMapping(Parameter):
56+
class ParameterFieldsMapping(QgsProcessingParameterDefinition):
6657

67-
default_metadata = {
68-
'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'
69-
}
58+
def __init__(self, name, description, parentLayerParameterName='INPUT'):
59+
super().__init__(name, description)
60+
self._parentLayerParameter = parentLayerParameterName
7061

71-
def __init__(self, name='', description='', parent=None):
72-
Parameter.__init__(self, name, description)
73-
self.parent = parent
74-
self.value = []
62+
def type(self):
63+
return 'fields_mapping'
7564

76-
def getValueAsCommandLineParameter(self):
77-
return '"' + str(self.value) + '"'
78-
79-
def setValue(self, value):
80-
if value is None:
65+
def checkValueIsAcceptable(self, value, context):
66+
if not isinstance(value, list):
8167
return False
82-
if isinstance(value, list):
83-
self.value = value
84-
return True
85-
if isinstance(value, str):
86-
try:
87-
self.value = eval(value)
88-
return True
89-
except Exception as e:
90-
# fix_print_with_import
91-
print(str(e)) # display error in console
68+
for field_def in value:
69+
if not isinstance(field_def, dict):
70+
return False
71+
if not field_def.get('name', False):
72+
return False
73+
if not field_def.get('type', False):
74+
return False
75+
if not field_def.get('expression', False):
9276
return False
93-
return False
77+
return True
9478

95-
self.addParameter(ParameterFieldsMapping(self.FIELDS_MAPPING,
96-
self.tr('Fields mapping'),
97-
self.INPUT_LAYER))
98-
self.addOutput(OutputVector(self.OUTPUT_LAYER,
99-
self.tr('Refactored'),
100-
base_input=self.INPUT_LAYER))
79+
def valueAsPythonString(self, value, context):
80+
return str(value)
81+
82+
def asScriptCode(self):
83+
raise NotImplementedError()
84+
85+
@classmethod
86+
def fromScriptCode(cls, name, description, isOptional, definition):
87+
raise NotImplementedError()
88+
89+
def parentLayerParameter(self):
90+
return self._parentLayerParameter
91+
92+
fields_mapping = ParameterFieldsMapping(self.FIELDS_MAPPING,
93+
description=self.tr('Fields mapping'))
94+
fields_mapping.setMetadata({
95+
'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'
96+
})
97+
self.addParameter(fields_mapping)
10198

10299
def name(self):
103100
return 'refactorfields'
104101

105102
def displayName(self):
106103
return self.tr('Refactor fields')
107104

108-
def processAlgorithm(self, parameters, context, feedback):
109-
layer = self.getParameterValue(self.INPUT_LAYER)
110-
mapping = self.getParameterValue(self.FIELDS_MAPPING)
111-
output = self.getOutputFromName(self.OUTPUT_LAYER)
105+
def outputName(self):
106+
return self.tr('Refactored')
112107

113-
layer = QgsProcessingUtils.mapLayerFromString(layer, context)
114-
fields = QgsFields()
115-
expressions = []
108+
def parameterAsFieldsMapping(self, parameters, name, context):
109+
return parameters[name]
110+
111+
def prepareAlgorithm(self, parameters, context, feedback):
112+
source = self.parameterAsSource(parameters, 'INPUT', context)
113+
mapping = self.parameterAsFieldsMapping(parameters, self.FIELDS_MAPPING, context)
114+
115+
self.fields = QgsFields()
116+
self.expressions = []
116117

117118
da = QgsDistanceArea()
118-
da.setSourceCrs(layer.crs())
119+
da.setSourceCrs(source.sourceCrs())
119120
da.setEllipsoid(context.project().ellipsoid())
120121

121-
exp_context = layer.createExpressionContext()
122-
123122
for field_def in mapping:
124-
fields.append(QgsField(field_def['name'],
125-
field_def['type'],
126-
field_def['length'],
127-
field_def['precision']))
128-
123+
self.fields.append(QgsField(name=field_def['name'],
124+
type=field_def['type'],
125+
typeName="",
126+
len=field_def.get('length', 0),
127+
prec=field_def.get('precision', 0)))
129128
expression = QgsExpression(field_def['expression'])
130129
expression.setGeomCalculator(da)
131130
expression.setDistanceUnits(context.project().distanceUnits())
132131
expression.setAreaUnits(context.project().areaUnits())
133-
expression.prepare(exp_context)
134132
if expression.hasParserError():
135-
raise GeoAlgorithmExecutionException(
133+
raise QgsProcessingException(
136134
self.tr(u'Parser error in expression "{}": {}')
137135
.format(str(expression.expression()),
138136
str(expression.parserErrorString())))
139-
expressions.append(expression)
140-
141-
writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context)
142-
143-
# Create output vector layer with new attributes
144-
error_exp = None
145-
inFeat = QgsFeature()
146-
outFeat = QgsFeature()
147-
features = QgsProcessingUtils.getFeatures(layer, context)
148-
count = QgsProcessingUtils.featureCount(layer, context)
149-
if count > 0:
150-
total = 100.0 / count
151-
for current, inFeat in enumerate(features):
152-
rownum = current + 1
153-
154-
geometry = inFeat.geometry()
155-
outFeat.setGeometry(geometry)
156-
157-
attrs = []
158-
for i in range(0, len(mapping)):
159-
field_def = mapping[i]
160-
expression = expressions[i]
161-
exp_context.setFeature(inFeat)
162-
exp_context.lastScope().setVariable("row_number", rownum)
163-
value = expression.evaluate(exp_context)
164-
if expression.hasEvalError():
165-
error_exp = expression
166-
break
167-
168-
attrs.append(value)
169-
outFeat.setAttributes(attrs)
170-
171-
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
172-
173-
feedback.setProgress(int(current * total))
174-
else:
175-
feedback.setProgress(100)
176-
177-
del writer
178-
179-
if error_exp is not None:
180-
raise GeoAlgorithmExecutionException(
181-
self.tr(u'Evaluation error in expression "{}": {}')
182-
.format(str(error_exp.expression()),
183-
str(error_exp.parserErrorString())))
137+
self.expressions.append(expression)
138+
return True
139+
140+
def outputFields(self, inputFields):
141+
return self.fields
142+
143+
def processAlgorithm(self, parameters, context, feeback):
144+
# create an expression context using thead safe processing context
145+
self.expr_context = self.createExpressionContext(parameters, context)
146+
for expression in self.expressions:
147+
expression.prepare(self.expr_context)
148+
self._row_number = 0
149+
return super().processAlgorithm(parameters, context, feeback)
150+
151+
def processFeature(self, feature, feedback):
152+
attributes = []
153+
for expression in self.expressions:
154+
self.expr_context.setFeature(feature)
155+
self.expr_context.lastScope().setVariable("row_number", self._row_number)
156+
value = expression.evaluate(self.expr_context)
157+
if expression.hasEvalError():
158+
raise QgsProcessingException(
159+
self.tr(u'Evaluation error in expression "{}": {}')
160+
.format(str(expression.expression()),
161+
str(expression.parserErrorString())))
162+
attributes.append(value)
163+
feature.setAttributes(attributes)
164+
self._row_number += 1
165+
return feature

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
# from .SetRasterStyle import SetRasterStyle
161161
# from .SelectByAttributeSum import SelectByAttributeSum
162162
# from .HypsometricCurves import HypsometricCurves
163-
# from .FieldsMapper import FieldsMapper
163+
from .FieldsMapper import FieldsMapper
164164
# from .Datasources2Vrt import Datasources2Vrt
165165
# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
166166
# from .DefineProjection import DefineProjection
@@ -231,6 +231,7 @@ def getAlgs(self):
231231
ExtentFromLayer(),
232232
ExtractNodes(),
233233
ExtractSpecificNodes(),
234+
FieldsMapper(),
234235
FixedDistanceBuffer(),
235236
FixGeometry(),
236237
GeometryByExpression(),

0 commit comments

Comments
 (0)