1

I am creating 12 mem_layers through a for loop and then using the zonal statistics tool for each one, to calculate a sum value. This sum value has many decimal places and I would like to reduce the amount of decimal places for each value, so that it contains two decimal places. This is the code I am using the create and apply zonal stats to the mem_layers:

# Creating the distance buffers
layer = iface.activeLayer()
feats = [ feat for feat in layer.getFeatures() ]
epsg = layer.crs().postgisSrid()
uri = "Polygon?crs=epsg:" + str(epsg) + "&field=id:integer&field=x:real&    field=y:real&field=point_id:integer""&index=yes"

colors = ["#FCFF00", "#FFF700", "#FFE000", "#FFCC00", "#FFB900", "#FF9E00", "#FF9200", "#FF7B00", "#FF6C00", "#FF4500", "#FF0000", "#FF0000"]

#######
# this list will save all mem layers
mem_layers = []
for color_idx, (intensity, dist) in enumerate(DistInt.items()):

    if dist <= 0:
        continue

    mem_layer = QgsVectorLayer(uri,
                           f'MMI {intensity} Buffer',
                           'memory')
    prov = mem_layer.dataProvider()

#Using zonal statistics to work out affected population sum column  
    prefix = rlayer.name()
    params = {'INPUT_RASTER': rlayer.name(), 'RASTER_BAND': 1, 'INPUT_VECTOR': f'MMI {intensity} Buffer', 'COLUMN_PREFIX': prefix+'_', 'STATS': 1}
    processing.run("qgis:zonalstatistics", params)

1 Answer 1

2

You can add another column in the attribute table with the value you want.

This code should work, you need to change the name of the field to copy, the name of the output field you desire :

mem_layer = QgsProject.instance().mapLayersByName("test")[0]
targetField = "gm_area"
new_field = "two_float"

mem_layer.startEditing()
mem_layer.addAttribute(QgsField(new_field, QVariant.Double, "double", 10,2))
mem_layer.commitChanges()
mem_layer.triggerRepaint()

mem_layer.startEditing()
for feature in mem_layer.getFeatures():
    attrs = feature.attributes()
    value = attrs[mem_layer.fields().indexFromName(targetField)]
    value = '%.2f' % value
    mem_layer.changeAttributeValue(feature.id(), mem_layer.fields().indexFromName(new_field), value)
mem_layer.commitChanges()
mem_layer.triggerRepaint()

In my example I have a layer test with a field "gm_area" with 15 decimals. enter image description here

I want to create a new field "two_float" with only 2 decimals. This is the result of my code :

enter image description here

EDIT AFTER COMMENT OF THE AUTHOR :

If you want to modify your column and not create another one, unless you use the refactorfields algorithm, the new value of your column are going to look like this :

enter image description here

To obtain this result you can use this code :

mem_layer = QgsProject.instance().mapLayersByName("test")[0]
targetField = "gm_area"

mem_layer.startEditing()
for feature in mem_layer.getFeatures():
    attrs = feature.attributes()
    oldvalue = attrs[mem_layer.fields().indexFromName(targetField)]
    newvalue = '%.2f' % oldvalue
    mem_layer.changeAttributeValue(feature.id(), mem_layer.fields().indexFromName(targetField), newvalue, oldvalue)
mem_layer.commitChanges()
mem_layer.triggerRepaint()

Else you're gonna need to create another layer with the fieldcalculator or the refactorfields functions. As you suggested you can use the round() function in the field calculator :

mem_layer = QgsProject.instance().mapLayersByName("test")[0]
targetField = "gm_area"
output_path = path_to_new_layer
    
expression = 'round({}, 2)'.format(targetField)
processing.run("native:fieldcalculator", {'INPUT':mem_layer,'FIELD_NAME':targetField,'FIELD_TYPE':0,'FIELD_LENGTH':0,'FIELD_PRECISION':0,'FORMULA':expression,'OUTPUT':output_path})
new_layer = iface.addVectorLayer (output_path, '', 'ogr')

And if you use the refactorfields function the code looks like this :

mem_layer = QgsProject.instance().mapLayersByName("test")[0]
targetField = "gm_area"
output_path = path_to_new_layer

list_champ = []
for field in mem_layer.fields():
    champ = {}
    champ['expression']=str("\"")+field.name()+str("\"")
    champ['length']=field.length()
    champ['name']=field.name()
    if str(field.name()) == str(targetField ):
        champ['precision']=2
    else :
        champ['precision']=field.precision()
    champ['type']=field.type()
    list_champ.append(champ)


processing.run("native:refactorfields", {'INPUT':mem_layer,'FIELDS_MAPPING':list_champ,'OUTPUT':output_path})
new_layer = iface.addVectorLayer (output_path, '', 'ogr')
3
  • Is there a way I can change the value without adding in another column? Possibly using the round() function? Thanks
    – LiamHems
    Dec 14, 2020 at 12:59
  • 1
    @LiamHems I edited my answer to gave you some ways of modifyng a field without creating another column but some ways require to create another layer.
    – JULESG
    Dec 14, 2020 at 14:25
  • Thank you very much!!
    – LiamHems
    Dec 15, 2020 at 10:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.