2015-01-22 14:52:52 +01:00
# -*- coding: utf-8 -*-
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
FieldsMapper . py
- - - - - - - - - - - - - - - - - - - - -
Date : October 2014
Copyright : ( C ) 2014 by Arnaud Morvan
Email : arnaud dot morvan at camptocamp dot com
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
__author__ = ' Arnaud Morvan '
__date__ = ' October 2014 '
__copyright__ = ' (C) 2014, Arnaud Morvan '
2017-07-21 19:46:27 +02:00
from qgis . core import (
QgsDistanceArea ,
QgsExpression ,
QgsField ,
QgsFields ,
2017-10-13 08:28:34 +10:00
QgsProcessing ,
2017-07-21 19:46:27 +02:00
QgsProcessingException ,
2018-02-26 08:51:29 +10:00
QgsProcessingParameterDefinition ,
2018-03-03 11:16:35 -05:00
QgsProcessingParameterType ,
2018-02-26 08:51:29 +10:00
NULL )
2017-07-21 19:46:27 +02:00
2019-03-09 22:45:51 -04:00
from qgis . PyQt . QtCore import QCoreApplication
2018-03-03 11:16:35 -05:00
2017-07-21 19:46:27 +02:00
from processing . algs . qgis . QgisAlgorithm import QgisFeatureBasedAlgorithm
class FieldsMapper ( QgisFeatureBasedAlgorithm ) :
2015-01-22 14:52:52 +01:00
INPUT_LAYER = ' INPUT_LAYER '
FIELDS_MAPPING = ' FIELDS_MAPPING '
OUTPUT_LAYER = ' OUTPUT_LAYER '
2017-03-29 12:04:09 +10:00
def group ( self ) :
2017-08-22 23:36:42 +10:00
return self . tr ( ' Vector table ' )
2017-03-29 12:04:09 +10:00
2017-12-14 14:03:56 +02:00
def groupId ( self ) :
return ' vectortable '
2018-02-28 09:58:33 +07:00
def tags ( self ) :
return self . tr ( ' attributes,table ' ) . split ( ' , ' )
2017-07-21 19:46:27 +02:00
def initParameters ( self , config = None ) :
2018-03-01 08:07:07 -05:00
fields_mapping = FieldsMapper . ParameterFieldsMapping ( self . FIELDS_MAPPING ,
description = self . tr ( ' Fields mapping ' ) )
2017-07-21 19:46:27 +02:00
fields_mapping . setMetadata ( {
' widget_wrapper ' : ' processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper '
} )
self . addParameter ( fields_mapping )
2015-01-22 14:52:52 +01:00
2017-05-15 13:40:38 +10:00
def name ( self ) :
return ' refactorfields '
def displayName ( self ) :
return self . tr ( ' Refactor fields ' )
2017-07-21 19:46:27 +02:00
def outputName ( self ) :
return self . tr ( ' Refactored ' )
2015-01-22 14:52:52 +01:00
2017-10-13 08:28:34 +10:00
def inputLayerTypes ( self ) :
return [ QgsProcessing . TypeVector ]
2017-07-21 19:46:27 +02:00
def parameterAsFieldsMapping ( self , parameters , name , context ) :
return parameters [ name ]
2018-09-13 17:01:26 +02:00
def supportInPlaceEdit ( self , layer ) :
return False
2017-07-21 19:46:27 +02:00
def prepareAlgorithm ( self , parameters , context , feedback ) :
source = self . parameterAsSource ( parameters , ' INPUT ' , context )
2018-04-27 12:31:56 +10:00
if source is None :
2018-05-30 08:19:19 +10:00
raise QgsProcessingException ( self . invalidSourceError ( parameters , ' INPUT ' ) )
2018-04-27 12:31:56 +10:00
2017-07-21 19:46:27 +02:00
mapping = self . parameterAsFieldsMapping ( parameters , self . FIELDS_MAPPING , context )
self . fields = QgsFields ( )
self . expressions = [ ]
2016-02-28 12:45:43 +11:00
da = QgsDistanceArea ( )
2017-12-20 15:08:14 +10:00
da . setSourceCrs ( source . sourceCrs ( ) , context . transformContext ( ) )
2017-07-27 10:49:52 +10:00
da . setEllipsoid ( context . project ( ) . ellipsoid ( ) )
2016-02-28 12:45:43 +11:00
2017-11-23 11:22:33 +01:00
# create an expression context using thread safe processing context
self . expr_context = self . createExpressionContext ( parameters , context , source )
2015-01-22 14:52:52 +01:00
for field_def in mapping :
2017-07-21 19:46:27 +02:00
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 ) ) )
2018-02-26 08:51:29 +10:00
if field_def [ ' expression ' ] :
expression = QgsExpression ( field_def [ ' expression ' ] )
expression . setGeomCalculator ( da )
expression . setDistanceUnits ( context . project ( ) . distanceUnits ( ) )
expression . setAreaUnits ( context . project ( ) . areaUnits ( ) )
if expression . hasParserError ( ) :
feedback . reportError (
self . tr ( u ' Parser error in expression " {} " : {} ' )
. format ( expression . expression ( ) ,
expression . parserErrorString ( ) ) )
return False
self . expressions . append ( expression )
else :
self . expressions . append ( None )
2017-07-21 19:46:27 +02:00
return True
def outputFields ( self , inputFields ) :
return self . fields
def processAlgorithm ( self , parameters , context , feeback ) :
for expression in self . expressions :
2018-02-26 08:51:29 +10:00
if expression is not None :
expression . prepare ( self . expr_context )
2017-07-21 19:46:27 +02:00
self . _row_number = 0
return super ( ) . processAlgorithm ( parameters , context , feeback )
2017-11-27 07:55:01 +10:00
def processFeature ( self , feature , context , feedback ) :
2017-07-21 19:46:27 +02:00
attributes = [ ]
for expression in self . expressions :
2018-02-26 08:51:29 +10:00
if expression is not None :
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 ( expression . expression ( ) ,
2018-03-27 16:14:50 +10:00
expression . evalErrorString ( ) ) )
2018-02-26 08:51:29 +10:00
attributes . append ( value )
else :
attributes . append ( NULL )
2017-07-21 19:46:27 +02:00
feature . setAttributes ( attributes )
self . _row_number + = 1
2018-02-21 07:02:01 +10:00
return [ feature ]
2018-03-01 08:07:07 -05:00
2018-03-03 11:16:35 -05:00
class ParameterFieldsMappingType ( QgsProcessingParameterType ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
def create ( self , name ) :
return FieldsMapper . ParameterFieldsMapping ( name )
def metadata ( self ) :
return { ' widget_wrapper ' : ' processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper ' }
def name ( self ) :
return QCoreApplication . translate ( ' Processing ' , ' Fields Mapper ' )
def id ( self ) :
2018-03-03 12:44:07 -05:00
return ' fields_mapping '
2018-03-03 11:16:35 -05:00
2019-01-25 13:36:30 +10:00
def pythonImportString ( self ) :
return ' from processing.algs.qgis.FieldsMapper import FieldsMapper '
def className ( self ) :
return ' FieldsMapper.ParameterFieldsMapping '
2018-03-03 11:16:35 -05:00
def description ( self ) :
return QCoreApplication . translate ( ' Processing ' , ' A mapping of field names to field type definitions and expressions. Used for the refactor fields algorithm. ' )
2018-03-01 08:07:07 -05:00
class ParameterFieldsMapping ( QgsProcessingParameterDefinition ) :
def __init__ ( self , name , description = ' ' , parentLayerParameterName = ' INPUT ' ) :
super ( ) . __init__ ( name , description )
self . _parentLayerParameter = parentLayerParameterName
def clone ( self ) :
copy = FieldsMapper . ParameterFieldsMapping ( self . name ( ) , self . description ( ) , self . _parentLayerParameter )
return copy
def type ( self ) :
return self . typeName ( )
@staticmethod
def typeName ( ) :
return ' fields_mapping '
def checkValueIsAcceptable ( self , value , context = None ) :
if not isinstance ( value , list ) :
return False
for field_def in value :
if not isinstance ( field_def , dict ) :
return False
if ' name ' not in field_def . keys ( ) :
return False
if ' type ' not in field_def . keys ( ) :
return False
if ' expression ' not in field_def . keys ( ) :
return False
return True
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