Skip to content

Commit

Permalink
[processing] Port refactor fields to new API
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-morvan authored and m-kuhn committed Aug 14, 2017
1 parent 2364801 commit b3a9e46
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 310 deletions.
2 changes: 2 additions & 0 deletions python/core/processing/qgsprocessingoutputs.sip
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class QgsProcessingOutputDefinition
sipType = sipType_QgsProcessingOutputString;
else if ( sipCpp->type() == QgsProcessingOutputFolder::typeName() )
sipType = sipType_QgsProcessingOutputFolder;
else
sipType = nullptr;
%End
public:

Expand Down
2 changes: 2 additions & 0 deletions python/core/processing/qgsprocessingparameters.sip
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ class QgsProcessingParameterDefinition
sipType = sipType_QgsProcessingParameterFolderDestination;
else if ( sipCpp->type() == QgsProcessingParameterBand::typeName() )
sipType = sipType_QgsProcessingParameterBand;
else
sipType = nullptr;
%End
public:

Expand Down
222 changes: 102 additions & 120 deletions python/plugins/processing/algs/qgis/FieldsMapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,159 +25,141 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsField,
QgsFields,
QgsExpression,
QgsDistanceArea,
QgsFeatureSink,
QgsProject,
QgsFeature,
QgsApplication,
QgsProcessingUtils)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterTable
from processing.core.parameters import Parameter
from processing.core.outputs import OutputVector


class FieldsMapper(QgisAlgorithm):
from qgis.core import (
QgsApplication,
QgsDistanceArea,
QgsExpression,
QgsFeature,
QgsFeatureSink,
QgsField,
QgsFields,
QgsProcessingException,
QgsProcessingParameterDefinition,
QgsProcessingUtils,
QgsProject,
)

from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm


class FieldsMapper(QgisFeatureBasedAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
FIELDS_MAPPING = 'FIELDS_MAPPING'
OUTPUT_LAYER = 'OUTPUT_LAYER'

def __init__(self):
GeoAlgorithm.__init__(self)
self.mapping = None

def group(self):
return self.tr('Vector table tools')

def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterTable(self.INPUT_LAYER,
self.tr('Input layer'),
False))
def initParameters(self, config=None):

class ParameterFieldsMapping(Parameter):
class ParameterFieldsMapping(QgsProcessingParameterDefinition):

default_metadata = {
'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'
}
def __init__(self, name, description, parentLayerParameterName='INPUT'):
super().__init__(name, description)
self._parentLayerParameter = parentLayerParameterName

def __init__(self, name='', description='', parent=None):
Parameter.__init__(self, name, description)
self.parent = parent
self.value = []
def type(self):
return 'fields_mapping'

def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"'

def setValue(self, value):
if value is None:
def checkValueIsAcceptable(self, value, context):
if not isinstance(value, list):
return False
if isinstance(value, list):
self.value = value
return True
if isinstance(value, str):
try:
self.value = eval(value)
return True
except Exception as e:
# fix_print_with_import
print(str(e)) # display error in console
for field_def in value:
if not isinstance(field_def, dict):
return False
if not field_def.get('name', False):
return False
if not field_def.get('type', False):
return False
if not field_def.get('expression', False):
return False
return False
return True

self.addParameter(ParameterFieldsMapping(self.FIELDS_MAPPING,
self.tr('Fields mapping'),
self.INPUT_LAYER))
self.addOutput(OutputVector(self.OUTPUT_LAYER,
self.tr('Refactored'),
base_input=self.INPUT_LAYER))
def valueAsPythonString(self, value, context):
return str(value)

def asScriptCode(self):
raise NotImplementedError()

@classmethod
def fromScriptCode(cls, name, description, isOptional, definition):
raise NotImplementedError()

def parentLayerParameter(self):
return self._parentLayerParameter

fields_mapping = ParameterFieldsMapping(self.FIELDS_MAPPING,
description=self.tr('Fields mapping'))
fields_mapping.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'
})
self.addParameter(fields_mapping)

def name(self):
return 'refactorfields'

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

def processAlgorithm(self, parameters, context, feedback):
layer = self.getParameterValue(self.INPUT_LAYER)
mapping = self.getParameterValue(self.FIELDS_MAPPING)
output = self.getOutputFromName(self.OUTPUT_LAYER)
def outputName(self):
return self.tr('Refactored')

layer = QgsProcessingUtils.mapLayerFromString(layer, context)
fields = QgsFields()
expressions = []
def parameterAsFieldsMapping(self, parameters, name, context):
return parameters[name]

def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, 'INPUT', context)
mapping = self.parameterAsFieldsMapping(parameters, self.FIELDS_MAPPING, context)

self.fields = QgsFields()
self.expressions = []

da = QgsDistanceArea()
da.setSourceCrs(layer.crs())
da.setSourceCrs(source.sourceCrs())
da.setEllipsoid(context.project().ellipsoid())

exp_context = layer.createExpressionContext()

for field_def in mapping:
fields.append(QgsField(field_def['name'],
field_def['type'],
field_def['length'],
field_def['precision']))

self.fields.append(QgsField(name=field_def['name'],
type=field_def['type'],
typeName="",
len=field_def.get('length', 0),
prec=field_def.get('precision', 0)))
expression = QgsExpression(field_def['expression'])
expression.setGeomCalculator(da)
expression.setDistanceUnits(context.project().distanceUnits())
expression.setAreaUnits(context.project().areaUnits())
expression.prepare(exp_context)
if expression.hasParserError():
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr(u'Parser error in expression "{}": {}')
.format(str(expression.expression()),
str(expression.parserErrorString())))
expressions.append(expression)

writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context)

# Create output vector layer with new attributes
error_exp = None
inFeat = QgsFeature()
outFeat = QgsFeature()
features = QgsProcessingUtils.getFeatures(layer, context)
count = QgsProcessingUtils.featureCount(layer, context)
if count > 0:
total = 100.0 / count
for current, inFeat in enumerate(features):
rownum = current + 1

geometry = inFeat.geometry()
outFeat.setGeometry(geometry)

attrs = []
for i in range(0, len(mapping)):
field_def = mapping[i]
expression = expressions[i]
exp_context.setFeature(inFeat)
exp_context.lastScope().setVariable("row_number", rownum)
value = expression.evaluate(exp_context)
if expression.hasEvalError():
error_exp = expression
break

attrs.append(value)
outFeat.setAttributes(attrs)

writer.addFeature(outFeat, QgsFeatureSink.FastInsert)

feedback.setProgress(int(current * total))
else:
feedback.setProgress(100)

del writer

if error_exp is not None:
raise GeoAlgorithmExecutionException(
self.tr(u'Evaluation error in expression "{}": {}')
.format(str(error_exp.expression()),
str(error_exp.parserErrorString())))
self.expressions.append(expression)
return True

def outputFields(self, inputFields):
return self.fields

def processAlgorithm(self, parameters, context, feeback):
# create an expression context using thead safe processing context
self.expr_context = self.createExpressionContext(parameters, context)
for expression in self.expressions:
expression.prepare(self.expr_context)
self._row_number = 0
return super().processAlgorithm(parameters, context, feeback)

def processFeature(self, feature, feedback):
attributes = []
for expression in self.expressions:
self.expr_context.setFeature(feature)
self.expr_context.lastScope().setVariable("row_number", self._row_number)
value = expression.evaluate(self.expr_context)
if expression.hasEvalError():
raise QgsProcessingException(
self.tr(u'Evaluation error in expression "{}": {}')
.format(str(expression.expression()),
str(expression.parserErrorString())))
attributes.append(value)
feature.setAttributes(attributes)
self._row_number += 1
return feature
3 changes: 2 additions & 1 deletion python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
# from .SetRasterStyle import SetRasterStyle
# from .SelectByAttributeSum import SelectByAttributeSum
# from .HypsometricCurves import HypsometricCurves
# from .FieldsMapper import FieldsMapper
from .FieldsMapper import FieldsMapper
# from .Datasources2Vrt import Datasources2Vrt
# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
# from .DefineProjection import DefineProjection
Expand Down Expand Up @@ -231,6 +231,7 @@ def getAlgs(self):
ExtentFromLayer(),
ExtractNodes(),
ExtractSpecificNodes(),
FieldsMapper(),
FixedDistanceBuffer(),
FixGeometry(),
GeometryByExpression(),
Expand Down
Loading

0 comments on commit b3a9e46

Please sign in to comment.