|
25 | 25 |
|
26 | 26 | __revision__ = '$Format:%H$'
|
27 | 27 |
|
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): |
45 | 46 |
|
46 | 47 | INPUT_LAYER = 'INPUT_LAYER'
|
47 | 48 | FIELDS_MAPPING = 'FIELDS_MAPPING'
|
48 | 49 | OUTPUT_LAYER = 'OUTPUT_LAYER'
|
49 | 50 |
|
50 |
| - def __init__(self): |
51 |
| - GeoAlgorithm.__init__(self) |
52 |
| - self.mapping = None |
53 |
| - |
54 | 51 | def group(self):
|
55 | 52 | return self.tr('Vector table tools')
|
56 | 53 |
|
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): |
64 | 55 |
|
65 |
| - class ParameterFieldsMapping(Parameter): |
| 56 | + class ParameterFieldsMapping(QgsProcessingParameterDefinition): |
66 | 57 |
|
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 |
70 | 61 |
|
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' |
75 | 64 |
|
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): |
81 | 67 | 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): |
92 | 76 | return False
|
93 |
| - return False |
| 77 | + return True |
94 | 78 |
|
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) |
101 | 98 |
|
102 | 99 | def name(self):
|
103 | 100 | return 'refactorfields'
|
104 | 101 |
|
105 | 102 | def displayName(self):
|
106 | 103 | return self.tr('Refactor fields')
|
107 | 104 |
|
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') |
112 | 107 |
|
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 = [] |
116 | 117 |
|
117 | 118 | da = QgsDistanceArea()
|
118 |
| - da.setSourceCrs(layer.crs()) |
| 119 | + da.setSourceCrs(source.sourceCrs()) |
119 | 120 | da.setEllipsoid(context.project().ellipsoid())
|
120 | 121 |
|
121 |
| - exp_context = layer.createExpressionContext() |
122 |
| - |
123 | 122 | 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))) |
129 | 128 | expression = QgsExpression(field_def['expression'])
|
130 | 129 | expression.setGeomCalculator(da)
|
131 | 130 | expression.setDistanceUnits(context.project().distanceUnits())
|
132 | 131 | expression.setAreaUnits(context.project().areaUnits())
|
133 |
| - expression.prepare(exp_context) |
134 | 132 | if expression.hasParserError():
|
135 |
| - raise GeoAlgorithmExecutionException( |
| 133 | + raise QgsProcessingException( |
136 | 134 | self.tr(u'Parser error in expression "{}": {}')
|
137 | 135 | .format(str(expression.expression()),
|
138 | 136 | 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 |
0 commit comments