11. Expressies, filteren en waarden berekenen

Hint

De codesnippers op deze pagina hebben de volgende import nodig als u buiten de console van PyQGIS bent:

 1from qgis.core import (
 2    edit,
 3    QgsExpression,
 4    QgsExpressionContext,
 5    QgsFeature,
 6    QgsFeatureRequest,
 7    QgsField,
 8    QgsFields,
 9    QgsVectorLayer,
10    QgsPointXY,
11    QgsGeometry,
12    QgsProject,
13    QgsExpressionContextUtils
14)

QGIS heeft enige ondersteuning voor het parsen van SQL-achtige expressies. Alleen een klein deel van de syntaxis voor SQL wordt ondersteund. De expressies kunnen worden geëvalueerd ófwel als Booleaanse uitdrukkingen (die True of False teruggeven) of als functies (die een scalaire waarde teruggeven). Bekijk Expressies in de Gebruikershandleiding voor een volledige lijst van beschikbare functies.

Drie basistypen worden ondersteund:

  • number — zowel gehele getallen als decimale getallen, bijv. 123, 3.14

  • string — zij moeten zijn omsloten door enkele aanhalingstekens: 'hallo wereld'

  • kolomverwijzing — tijdens evaluatie wordt de verwijzing vervangen door de actuele waarde van het veld. De namen worden niet geëscaped.

De volgende bewerkingen zijn beschikbaar:

  • rekenkundige operatoren: +, -, *, /, ^

  • haakjes: voor het forceren van de voorrang van de operator: (1 + 1) * 3

  • unaire plus en minus: -12, +5

  • wiskundige functies: sqrt, sin, cos, tan, asin, acos, atan

  • functies voor conversie: to_int, to_real, to_string, to_date

  • geometrische functies: $area, $length

  • functies voor afhandelen van geometrie: $x, $y, $geometry, num_geometries, centroid

En de volgende termen worden ondersteund:

  • vergelijking: =, !=, >, >=, <, <=

  • overeenkomst van patroon: LIKE (gebruiken van % en _), ~ (reguliere expressies)

  • logische termen: AND, OR, NOT

  • controle op waarde NULL: IS NULL, IS NOT NULL

Voorbeelden van termen:

  • 1 + 2 = 3

  • sin(hoek) > 0

  • 'Hallo' LIKE 'Ha%'

  • (x > 10 AND y > 10) OR z = 0

Voorbeelden van scalaire expressies:

  • 2 ^ 10

  • sqrt(waarde)

  • $length + 1

11.1. Parsen van expressies

Het volgende voorbeeld laat zien hoe te controleren of een bepaalde expressie juist kan worden geparsd:

1exp = QgsExpression('1 + 1 = 2')
2assert(not exp.hasParserError())
3
4exp = QgsExpression('1 + 1 = ')
5assert(exp.hasParserError())
6
7assert(exp.parserErrorString() == '\nsyntax error, unexpected $end')

11.2. Evalueren van expressies

Expressies kunnen in verschillende contexten worden gebruikt, bijvoorbeeld om objecten te filteren of om nieuwe veldwaarden te berekenen. In alle gevallen moet de expressie worden geëvalueerd. Dat betekent dat de waarde ervan wordt berekent door de gespecificeerde stappen voor de berekening uit te voeren, wat eenvoudige rekenkundige of samengestelde expressies kunnen zijn.

11.2.1. Basisexpressies

Deze basis expressie evalueert een eenvoudige rekenkundige bewerking:

exp = QgsExpression('2 * 3')
print(exp)
print(exp.evaluate())
<QgsExpression: '2 * 3'>
6

Expressie mag ook gebruikt worden voor vergelijking, evalueert tot 1 (True) of 0 (False)

exp = QgsExpression('1 + 1 = 2')
exp.evaluate()
# 1

11.2.2. Expressies met objecten

Voor het evalueren van een expressie ten opzichte van een object dient een object QgsExpressionContext te worden gemaakt en doorgegeven aan de functie evaluate om de expressie toe te staan toegang te krijgen tot de veldwaarden van het object.

Het volgende voorbeeld laat zien hoe een object te maken met een veld “Column” en hoe dit object toe te voegen aan de context van de expressie.

 1fields = QgsFields()
 2field = QgsField('Column')
 3fields.append(field)
 4feature = QgsFeature()
 5feature.setFields(fields)
 6feature.setAttribute(0, 99)
 7
 8exp = QgsExpression('"Column"')
 9context = QgsExpressionContext()
10context.setFeature(feature)
11exp.evaluate(context)
12# 99

Het volgende is een meer compleet voorbeeld van hoe expressies te gebruiken in de context van een vectorlaag, om nieuwe veldwaarden te kunnen berekenen:

 1from qgis.PyQt.QtCore import QVariant
 2
 3# create a vector layer
 4vl = QgsVectorLayer("Point", "Companies", "memory")
 5pr = vl.dataProvider()
 6pr.addAttributes([QgsField("Name", QVariant.String),
 7                  QgsField("Employees",  QVariant.Int),
 8                  QgsField("Revenue", QVariant.Double),
 9                  QgsField("Rev. per employee", QVariant.Double),
10                  QgsField("Sum", QVariant.Double),
11                  QgsField("Fun", QVariant.Double)])
12vl.updateFields()
13
14# add data to the first three fields
15my_data = [
16    {'x': 0, 'y': 0, 'name': 'ABC', 'emp': 10, 'rev': 100.1},
17    {'x': 1, 'y': 1, 'name': 'DEF', 'emp': 2, 'rev': 50.5},
18    {'x': 5, 'y': 5, 'name': 'GHI', 'emp': 100, 'rev': 725.9}]
19
20for rec in my_data:
21    f = QgsFeature()
22    pt = QgsPointXY(rec['x'], rec['y'])
23    f.setGeometry(QgsGeometry.fromPointXY(pt))
24    f.setAttributes([rec['name'], rec['emp'], rec['rev']])
25    pr.addFeature(f)
26
27vl.updateExtents()
28QgsProject.instance().addMapLayer(vl)
29
30# The first expression computes the revenue per employee.
31# The second one computes the sum of all revenue values in the layer.
32# The final third expression doesn’t really make sense but illustrates
33# the fact that we can use a wide range of expression functions, such
34# as area and buffer in our expressions:
35expression1 = QgsExpression('"Revenue"/"Employees"')
36expression2 = QgsExpression('sum("Revenue")')
37expression3 = QgsExpression('area(buffer($geometry,"Employees"))')
38
39# QgsExpressionContextUtils.globalProjectLayerScopes() is a convenience
40# function that adds the global, project, and layer scopes all at once.
41# Alternatively, those scopes can also be added manually. In any case,
42# it is important to always go from “most generic” to “most specific”
43# scope, i.e. from global to project to layer
44context = QgsExpressionContext()
45context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(vl))
46
47with edit(vl):
48    for f in vl.getFeatures():
49        context.setFeature(f)
50        f['Rev. per employee'] = expression1.evaluate(context)
51        f['Sum'] = expression2.evaluate(context)
52        f['Fun'] = expression3.evaluate(context)
53        vl.updateFeature(f)
54
55print(f['Sum'])
876.5

11.2.3. Een laag filteren met expressies

Het volgende voorbeeld kan worden gebruikt om een laag te filteren en elk object terug te geven dat overeenkomt met een term.

 1layer = QgsVectorLayer("Point?field=Test:integer",
 2                           "addfeat", "memory")
 3
 4layer.startEditing()
 5
 6for i in range(10):
 7    feature = QgsFeature()
 8    feature.setAttributes([i])
 9    assert(layer.addFeature(feature))
10layer.commitChanges()
11
12expression = 'Test >= 3'
13request = QgsFeatureRequest().setFilterExpression(expression)
14
15matches = 0
16for f in layer.getFeatures(request):
17   matches += 1
18
19print(matches)
7

11.3. Fouten in expressies afhandelen

Fouten die gerelateerd zijn aan expressies kunnen optreden tijdens het parsen of evalueren van de expressie:

1exp = QgsExpression("1 + 1 = 2")
2if exp.hasParserError():
3   raise Exception(exp.parserErrorString())
4
5value = exp.evaluate()
6if exp.hasEvalError():
7   raise ValueError(exp.evalErrorString())