Previously, we already covered how to create vector layers, how to add fields to their attribute table, and how to set the field values. This time, instead of setting the fields to some preset values, we’ll be using QGIS expressions to compute the values.
First, let’s create a vector layer with three features! The layer creation code reuses things we’ve already covered in previous examples:
from qgis.PyQt.QtCore import QVariant vl = QgsVectorLayer("Point", "Companies", "memory") pr = vl.dataProvider() pr.addAttributes([QgsField("Name", QVariant.String), QgsField("Employees", QVariant.Int), QgsField("Revenue", QVariant.Double), QgsField("Rev. per employee", QVariant.Double), QgsField("Sum", QVariant.Double), QgsField("Fun", QVariant.Double)]) vl.updateFields() my_data = [ {'x': 0, 'y': 0, 'name': 'ABC', 'emp': 10, 'rev': 100.1}, {'x': 1, 'y': 1, 'name': 'DEF', 'emp': 2, 'rev': 50.5}, {'x': 5, 'y': 5, 'name': 'GHI', 'emp': 100, 'rev': 725.9}] for rec in my_data: f = QgsFeature() pt = QgsPointXY(rec['x'], rec['y']) f.setGeometry(QgsGeometry.fromPointXY(pt)) f.setAttributes([rec['name'], rec['emp'], rec['rev']]) pr.addFeature(f) vl.updateExtents() QgsProject.instance().addMapLayer(vl)
Open the attribute table and you’ll see that the last three fields are empty.
Let’s fill them with expressions!
The expression syntax is exactly the same as in the field calculator GUI. So you can test them in the GIU first, before copying them into your PyQGIS script.
The first expression computes the revenue per employee. The second one computes the sum of all revenue values in the layer. The final third expression doesn’t really make sense but illustrates the fact that we can use a wide range of expression functions, such as area and buffer in our expressions:
expression1 = QgsExpression('Revenue/Employees') expression2 = QgsExpression('sum(Revenue)') expression3 = QgsExpression('area(buffer($geometry,Employees))')
To execute our expressions, we need to provide an appropriate QgsExpressionContext. To set it up, use:
context = QgsExpressionContext() context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(vl))
(QgsExpressionContextUtils.globalProjectLayerScopes() is a convenience function that adds the global, project, and layer scopes all at once. Alternatively, those scopes can also be added manually. In any case, it is important to always go from “most generic” to “most specific” scope, i.e. from global to project to layer.)
Now we’re ready! We can loop through the layer features and apply the expressions by calling their evaluate() function:
with edit(vl): for f in vl.getFeatures(): context.setFeature(f) f['Rev. per employee'] = expression1.evaluate(context) f['Sum'] = expression2.evaluate(context) f['Fun'] = expression3.evaluate(context) vl.updateFeature(f)
However, this is not the only way to compute field values. Alternatively, you can also use pure Python, for example:
with edit(vl): for f in vl.getFeatures(): f['Rev. per employee'] = f['Revenue'] / f['Employees'] vl.updateFeature(f)
These are the basics of using QGIS expressions as well as pure Python to compute field values in PyQGIS. You can use both of these options depending on your preferences and specific requirements of your project.
PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!
expression1 = QgsExpression(‘Revenue/Employees’)
expression2 = QgsExpression(‘sum(Revenue)’)
expression3 = QgsExpression(‘area(buffer($geometry,Employees))’)
The field’s names should not be inside double quotes?
Double quotes around field names are not necessary here.
I don’t know whether it’s a version difference or how the columns are defined, but my code did not work unless I used double quotes for the column names.
Great course otherwise! 🙂
what’s the “edit” in
with edit(vl):
for f in vl.getFeatures():
f[‘Rev. per employee’] = f[‘Revenue’] / f[‘Employees’]
vl.updateFeature(f)
?
with edit
is a shortcut that replaceslayer.beginEditCommand
andlayer.endEditCommand
as described in https://docs.qgis.org/3.4/en/docs/pyqgis_developer_cookbook/vector.html#modifying-vector-layers-with-an-editing-bufferSeems that for sizeable tables attribute editing can be very slow.
I would suggest calling from python the appropriate C++ processor. For example “Advanced Python field calculator”.
Thank you Anita! It is so cool way how one can mix expressions and Python code.
For self-written Python expression functions (https://docs.qgis.org/3.22/en/docs/user_manual/expressions/expression.html) it looks really terse and to the point, as one can get the context as magical last parameter:
from qgis.core import *
from qgis.gui import *
@qgsfunction(args=’auto’, group=’Custom’, usesgeometry=True)
def expression_via_python(feature, parent, context):
expression = QgsExpression(‘area($geometry)’)
value = expression.evaluate(context)
return value
Hello Anita, this course is excelent and very usefull, thank you very much.
I had the ambition to extend the “pure python”-code-part, so…
total_sum = vl.aggregate(QgsAggregateCalculator.Sum, “Revenue”)[0]
with edit(vl):
for f in vl.getFeatures():
f[‘Rev. per employee’] = f[‘Revenue’] / f[‘Employees’]
f[‘Sum’] = total_sum
f[‘Fun’] = f.geometry().buffer(f[‘Employees’],8).area()
vl.updateFeature(f)
I’m so proud… :-)
Hi Anita, I wonder if there is a possibility to set all values in one go using expressions without iterating over the features. Like in QGIS’ field calculator. I would appreciate if you could share any information on this.