3

I have a PyQGIS script being run from within QGIS that updates attribute values in a GeoPackage, based on a mapping. It currently iterates through fields and features one by one, updating the values using QgsVectorLayer.changeAttributeValue.

This seems inefficient, particularly if there are many features. I'm looking to update it to use QgsVectorDataProvider.changeAttributeValues: https://qgis.github.io/pyqgis/3.28/core/QgsVectorDataProvider.html#qgis.core.QgsVectorDataProvider.changeAttributeValues

The changeAttributeValues method takes as a parameter "attr_map (object) – a map containing changed attributes". Unfortunately, the documentation doesn't set out the format of the map, and a Google search hasn't yielded any useful information.

The aim is to update the code to create the attribute map, and then run one operation using changeAttributeValues to update all changes at once. I am specifically looking for the format of the attribute map, though code is of course welcome.

The dummy existing code is as follows:

outfile = "c:/temp/output.gpkg"
oldFieldNames = ["oldField1", "oldField2", "oldField3"]
newFieldNames = ["newField1", "newField2", "newField3"]
mapping = [
 {0: "orange", 1: "apple", 2: "pineapple"},
 {0: "red", 1: "yellow", 2: "green", 3: "blue"},
 {0: "dog", 1: "cat", 2: "goat", 3: "cow", 4: "fish"},
]
vl = QgsVectorLayer(outfile, 'temp', 'ogr') 
pr = vl.dataProvider()
for i in range(len(oldFieldNames)):
 newFieldIndex = pr.fieldNameIndex(newFieldNames[i])
 with edit(vl):
 for feature in vl.getFeatures():
 vl.changeAttributeValue(feature.id(), newFieldIndex, mapping[i][feature[oldFieldNames[i]]])

If the initial layer attributes are as follows:

fid oldField1 oldField2 oldField3 newField1 newField2 newField3
1 0 3 4
2 1 2 3

then the final result after mapping should be:

fid oldField1 oldField2 oldField3 newField1 newField2 newField3
1 0 3 4 orange blue fish
2 1 2 3 apple green cow

As an aside, I have found quite regularly with the QGIS Python API documentation, that there is not enough detail to know the exact format of parameters required for some methods.

Taras
35.7k5 gold badges77 silver badges151 bronze badges
asked Apr 24, 2023 at 11:00
3
  • 2
    Not much help but you can look through tests in github.com/qgis/QGIS/search?q=changeAttributeValues to see how methods are used there. Commented Apr 24, 2023 at 11:14
  • 1
    The C++ documentation gives sometimes more hints : api.qgis.org/api/… & api.qgis.org/api/… Commented Apr 24, 2023 at 11:20
  • 4
    I'm no expert but when I have used it example, it is a dictionary of dictionaries: {feature_id: {field_index:new value}, ... }. Commented Apr 24, 2023 at 11:58

1 Answer 1

3

As @BERA says, the attribute map is a dictionary of dictionaries, with the first key as the feature ID, and the second key as the field index.

e.g.

attrMap = {
 featureId1: {fieldIndex1: value1, fieldIndex2: value2, fieldIndex3: value3}, 
 featureId2: {fieldIndex1: value4, fieldIndex2: value5}, 
 ...}

Updated code using changeAttributeValues (there may be more efficient ways of iterating):

outfile = "c:/temp/output.gpkg"
oldFieldNames = ["oldField1", "oldField2", "oldField3"]
newFieldNames = ["newField1", "newField2", "newField3"]
mapping = [
 {0: "orange", 1: "apple", 2: "pineapple"},
 {0: "red", 1: "yellow", 2: "green", 3: "blue"},
 {0: "dog", 1: "cat", 2: "goat", 3: "cow", 4: "fish"},
]
vl = QgsVectorLayer(outfile, 'temp', 'ogr') 
pr = vl.dataProvider()
attrMap = {}
newFieldIndices = []
for i in range(len(newFieldNames)):
 newFieldIndices.append(pr.fieldNameIndex(newFieldNames[i]))
for feature in vl.getFeatures():
 featureAttrMap = {}
 for i in range(len(oldFieldNames)):
 featureAttrMap[newFieldIndices[i]] = mapping[i][feature[oldFieldNames[i]]]
 attrMap[feature.id()] = featureAttrMap
# print(attrMap)
pr.changeAttributeValues(attrMap)

The attrMap data for the example above is:

attrMap = {
 1: {4: 'orange', 5: 'blue', 6: 'fish'}, 
 2: {4: 'apple', 5: 'green', 6: 'cow'}
}
Taras
35.7k5 gold badges77 silver badges151 bronze badges
answered Apr 25, 2023 at 6:43

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.