2012-10-04 19:33:47 +02:00
# -*- coding: utf-8 -*-
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
FieldPyculator . py
- - - - - - - - - - - - - - - - - - - - -
Date : August 2012
2013-10-01 20:52:22 +03:00
Copyright : ( C ) 2012 by Victor Olaya
2012-10-04 19:33:47 +02:00
Email : volayaf at gmail 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
2016-09-21 18:24:26 +02:00
from builtins import str
2012-10-04 19:33:47 +02:00
2013-09-14 15:52:28 +02:00
__author__ = ' Victor Olaya & NextGIS '
2012-10-04 19:33:47 +02:00
__date__ = ' August 2012 '
2013-09-14 15:52:28 +02:00
__copyright__ = ' (C) 2012, Victor Olaya & NextGIS '
2013-10-01 20:52:22 +03:00
2012-10-04 19:33:47 +02:00
# This will get replaced with a git SHA1 when you do a git archive
2013-10-01 20:52:22 +03:00
2012-10-04 19:33:47 +02:00
__revision__ = ' $Format: % H$ '
2013-04-11 11:13:45 +04:00
import sys
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
2016-04-22 10:38:48 +02:00
from qgis . PyQt . QtCore import QVariant
2017-08-19 23:31:31 +10:00
from qgis . core import ( QgsProcessingException ,
2017-03-29 10:42:42 +10:00
QgsField ,
2017-06-23 14:34:38 +10:00
QgsFeatureSink ,
2017-08-19 23:31:31 +10:00
QgsProcessingParameterFeatureSource ,
QgsProcessingParameterString ,
QgsProcessingParameterEnum ,
QgsProcessingParameterNumber ,
QgsProcessingParameterFeatureSink )
2017-06-06 13:41:42 +10:00
from processing . algs . qgis . QgisAlgorithm import QgisAlgorithm
2012-09-15 18:25:25 +03:00
2017-05-19 11:27:16 +10:00
class FieldsPyculator ( QgisAlgorithm ) :
2012-09-15 18:25:25 +03:00
2017-08-19 23:31:31 +10:00
INPUT = ' INPUT '
2013-10-01 20:52:22 +03:00
FIELD_NAME = ' FIELD_NAME '
FIELD_TYPE = ' FIELD_TYPE '
FIELD_LENGTH = ' FIELD_LENGTH '
FIELD_PRECISION = ' FIELD_PRECISION '
GLOBAL = ' GLOBAL '
FORMULA = ' FORMULA '
2017-08-19 23:31:31 +10:00
OUTPUT = ' OUTPUT '
2013-10-01 20:52:22 +03:00
RESULT_VAR_NAME = ' value '
2013-04-11 11:13:45 +04:00
TYPES = [ QVariant . Int , QVariant . Double , QVariant . String ]
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-05-15 13:40:38 +10:00
def __init__ ( self ) :
super ( ) . __init__ ( )
2017-07-10 16:31:14 +10:00
def initAlgorithm ( self , config = None ) :
2015-08-31 16:59:11 +02:00
self . type_names = [ self . tr ( ' Integer ' ) ,
self . tr ( ' Float ' ) ,
self . tr ( ' String ' ) ]
2017-08-19 23:31:31 +10:00
self . addParameter ( QgsProcessingParameterFeatureSource ( self . INPUT , self . tr ( ' Input layer ' ) ) )
self . addParameter ( QgsProcessingParameterString ( self . FIELD_NAME ,
self . tr ( ' Result field name ' ) , defaultValue = ' NewField ' ) )
self . addParameter ( QgsProcessingParameterEnum ( self . FIELD_TYPE ,
self . tr ( ' Field type ' ) , options = self . type_names ) )
self . addParameter ( QgsProcessingParameterNumber ( self . FIELD_LENGTH ,
self . tr ( ' Field length ' ) , minValue = 1 , maxValue = 255 , defaultValue = 10 ) )
self . addParameter ( QgsProcessingParameterNumber ( self . FIELD_PRECISION ,
self . tr ( ' Field precision ' ) , minValue = 0 , maxValue = 15 , defaultValue = 3 ) )
self . addParameter ( QgsProcessingParameterString ( self . GLOBAL ,
self . tr ( ' Global expression ' ) , multiLine = True , optional = True ) )
self . addParameter ( QgsProcessingParameterString ( self . FORMULA ,
self . tr ( ' Formula ' ) , defaultValue = ' value = ' , multiLine = True ) )
self . addParameter ( QgsProcessingParameterFeatureSink ( self . OUTPUT ,
self . tr ( ' Calculated ' ) ) )
2012-09-15 18:25:25 +03:00
2017-05-15 13:40:38 +10:00
def name ( self ) :
return ' advancedpythonfieldcalculator '
def displayName ( self ) :
return self . tr ( ' Advanced Python field calculator ' )
2017-05-15 16:19:46 +10:00
def processAlgorithm ( self , parameters , context , feedback ) :
2017-08-19 23:31:31 +10:00
source = self . parameterAsSource ( parameters , self . INPUT , context )
field_name = self . parameterAsString ( parameters , self . FIELD_NAME , context )
field_type = self . TYPES [ self . parameterAsEnum ( parameters , self . FIELD_TYPE , context ) ]
width = self . parameterAsInt ( parameters , self . FIELD_LENGTH , context )
precision = self . parameterAsInt ( parameters , self . FIELD_PRECISION , context )
code = self . parameterAsString ( parameters , self . FORMULA , context )
globalExpression = self . parameterAsString ( parameters , self . GLOBAL , context )
fields = source . fields ( )
fields . append ( QgsField ( field_name , self . TYPES [ field_type ] , ' ' ,
width , precision ) )
2012-09-15 18:25:25 +03:00
new_ns = { }
2017-08-19 23:31:31 +10:00
( sink , dest_id ) = self . parameterAsSink ( parameters , self . OUTPUT , context ,
fields , source . wkbType ( ) , source . sourceCrs ( ) )
2013-10-01 20:52:22 +03:00
# Run global code
if globalExpression . strip ( ) != ' ' :
2012-09-15 18:25:25 +03:00
try :
bytecode = compile ( globalExpression , ' <string> ' , ' exec ' )
2015-08-22 14:29:41 +02:00
exec ( bytecode , new_ns )
2012-09-15 18:25:25 +03:00
except :
2017-08-19 23:31:31 +10:00
raise QgsProcessingException (
2017-03-04 16:23:36 +01:00
self . tr ( " FieldPyculator code execute error.Global code block can ' t be executed! \n {0} \n {1} " ) . format ( str ( sys . exc_info ( ) [ 0 ] . __name__ ) , str ( sys . exc_info ( ) [ 1 ] ) ) )
2012-09-15 18:25:25 +03:00
2013-10-01 20:52:22 +03:00
# Replace all fields tags
2017-08-19 23:31:31 +10:00
fields = source . fields ( )
2013-02-04 00:14:39 +01:00
num = 0
for field in fields :
2016-09-21 18:24:26 +02:00
field_name = str ( field . name ( ) )
replval = ' __attr[ ' + str ( num ) + ' ] '
2013-10-01 20:52:22 +03:00
code = code . replace ( ' < ' + field_name + ' > ' , replval )
2013-02-04 00:14:39 +01:00
num + = 1
2012-09-15 18:25:25 +03:00
2013-10-01 20:52:22 +03:00
# Replace all special vars
2013-04-11 11:13:45 +04:00
code = code . replace ( ' $id ' , ' __id ' )
code = code . replace ( ' $geom ' , ' __geom ' )
2013-10-01 20:52:22 +03:00
need_id = code . find ( ' __id ' ) != - 1
need_geom = code . find ( ' __geom ' ) != - 1
need_attrs = code . find ( ' __attr ' ) != - 1
2012-09-15 18:25:25 +03:00
2013-10-01 20:52:22 +03:00
# Compile
2012-09-15 18:25:25 +03:00
try :
bytecode = compile ( code , ' <string> ' , ' exec ' )
except :
2017-08-19 23:31:31 +10:00
raise QgsProcessingException (
2017-03-04 16:23:36 +01:00
self . tr ( " FieldPyculator code execute error. Field code block can ' t be executed! \n {0} \n {1} " ) . format ( str ( sys . exc_info ( ) [ 0 ] . __name__ ) , str ( sys . exc_info ( ) [ 1 ] ) ) )
2012-09-15 18:25:25 +03:00
2013-10-01 20:52:22 +03:00
# Run
2017-08-19 23:31:31 +10:00
features = source . getFeatures ( )
total = 100.0 / source . featureCount ( ) if source . featureCount ( ) else 0
2016-02-17 09:36:59 +02:00
for current , feat in enumerate ( features ) :
2017-08-19 23:31:31 +10:00
if feedback . isCanceled ( ) :
break
2017-01-06 20:04:00 +10:00
feedback . setProgress ( int ( current * total ) )
2013-04-11 11:13:45 +04:00
attrs = feat . attributes ( )
2012-12-24 00:03:30 +01:00
feat_id = feat . id ( )
2013-01-12 23:36:00 +01:00
2013-10-01 20:52:22 +03:00
# Add needed vars
2012-12-24 00:03:30 +01:00
if need_id :
new_ns [ ' __id ' ] = feat_id
2013-01-12 23:36:00 +01:00
2012-12-24 00:03:30 +01:00
if need_geom :
geom = feat . geometry ( )
new_ns [ ' __geom ' ] = geom
2013-01-12 23:36:00 +01:00
2012-09-15 18:25:25 +03:00
if need_attrs :
2013-06-03 21:25:22 +02:00
pyattrs = [ a for a in attrs ]
2013-02-04 00:14:39 +01:00
new_ns [ ' __attr ' ] = pyattrs
2013-01-12 23:36:00 +01:00
2013-10-01 20:52:22 +03:00
# Clear old result
if self . RESULT_VAR_NAME in new_ns :
2012-12-24 00:03:30 +01:00
del new_ns [ self . RESULT_VAR_NAME ]
2013-01-12 23:36:00 +01:00
2013-10-01 20:52:22 +03:00
# Exec
2015-08-22 14:29:41 +02:00
exec ( bytecode , new_ns )
2013-01-12 23:36:00 +01:00
2013-10-01 20:52:22 +03:00
# Check result
if self . RESULT_VAR_NAME not in new_ns :
2017-08-19 23:31:31 +10:00
raise QgsProcessingException (
2015-01-15 20:41:15 +02:00
self . tr ( " FieldPyculator code execute error \n "
2017-03-04 16:23:36 +01:00
" Field code block does not return ' {0} ' variable! "
" Please declare this variable in your code! " ) . format ( self . RESULT_VAR_NAME ) )
2013-01-12 23:36:00 +01:00
2013-10-01 20:52:22 +03:00
# Write feature
2013-06-03 21:25:22 +02:00
attrs . append ( new_ns [ self . RESULT_VAR_NAME ] )
2017-08-19 23:31:31 +10:00
feat . setAttributes ( attrs )
sink . addFeature ( feat , QgsFeatureSink . FastInsert )
2013-01-12 23:36:00 +01:00
2017-08-19 23:31:31 +10:00
return { self . OUTPUT : dest_id }
2012-09-15 18:25:25 +03:00
2017-05-16 15:21:41 +10:00
def checkParameterValues ( self , parameters , context ) :
2013-10-01 20:52:22 +03:00
# TODO check that formula is correct and fields exist
2017-05-16 15:21:41 +10:00
return super ( FieldsPyculator , self ) . checkParameterValues ( parameters , context )