11. Espressioni, Filtraggio e Calcolo di Valori

Suggerimento

I frammenti di codice di questa pagina necessitano delle seguenti importazioni se sei al di fuori della console pyqgis:

 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 ha un qualche supporto per l’analisi di espressioni di tipo SQL. È supportato solo un piccolo sottoinsieme della sintassi SQL. Le espressioni possono essere valutate come predicati booleani (restituendo True o False) o come funzioni (restituendo un valore scalare). Per un elenco completo delle funzioni disponibili, vedere Espressioni nel Manuale utente.

Sono supportati tre tipi base:

  • numero – sia numeri interi che decimali, e.g. 123, 3.14

  • stringa – devono essere racchiuse tra apici singoli: 'hello world'

  • riferimento a colonna – durante la valutazione, il riferimento é sostituito con il valore del campo. I nomi non sono racchiusi tra apici.

Sono disponibili le seguenti operazioni:

  • operatori aritmetici: +, -, *, /, ^

  • parentesi: per forzare la precedenza tra operatori: (1 + 1) * 3

  • somma e sottrazione unari: -12, +5

  • funzioni matematiche: sqrt, sin, cos, tan, asin, acos, atan

  • funzioni di conversione: to_int, to_real, to_string, to_date

  • funzioni sulla geometria: $area, $length

  • funzioni di manipolazione della geometria: $x, $y, $geometry, num_geometries, centroid

Sono supportati i seguenti predicati:

  • comparazione: =, !=, >, >=, <, <=

  • pattern matching: LIKE (usando % e _), ~ (espressioni regolari)

  • predicati logici: AND, OR, NOT

  • controllo di valori NULL: IS NULL, IS NOT NULL

Esempi di predicati:

  • 1 + 2 = 3

  • sin(angolo) > 0

  • 'Hello' LIKE 'He%'

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

Esempi di espressioni scalari:

  • 2 ^ 10

  • sqrt(val)

  • $length + 1

11.1. Analisi di Espressioni

L’esempio seguente mostra come verificare se una determinata espressione può essere analizzata correttamente:

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. Valutazione di Espressioni

Le espressioni possono essere utilizzate in diversi contesti, ad esempio per filtrare gli elementi o per calcolare i valori di nuovi campi. In ogni caso, l’espressione deve essere valorizzata. Ciò significa che il suo valore viene calcolato eseguendo le fasi di calcolo specificate, che possono andare dalla semplice aritmetica alle espressioni aggregate.

11.2.1. Espressioni Base

Questa espressione di base valuta una semplice operazione aritmetica:

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

L’espressione può essere utilizzata anche per il confronto, fornendo una risposta pari a 1 (True) o a 0 (False).

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

11.2.2. Espressioni con geometrie

Per valutare un’espressione rispetto a un elemento, è necessario creare un oggetto QgsExpressionContext e passarlo alla funzione evaluate, per consentire all’espressione di accedere ai valori dei campi dell’elemento.

L’esempio seguente mostra come creare un elemento con un campo chiamato «Column» e come aggiungere questo elemento al contesto dell’espressione.

 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

Di seguito è riportato un esempio più completo di come utilizzare le espressioni nel contesto di un layer vettoriale, al fine di calcolare nuovi valori di campo:

 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. Filtrare un layer con le espressioni

L’esempio seguente puó essere usato per filtrare un layer e restituire qualsiasi geometria che soddisfi il predicato.

 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. Gestione degli errori delle espressioni

Gli errori relativi alle espressioni possono verificarsi durante l’analisi o la valutazione delle stesse:

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())