11. Expressões, filtragem e cálculo dos valores

Dica

Os trechos de código desta página precisam das seguintes importações se você estiver fora do console do 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)

O QGIS tem suporte para analisar expressões do tipo SQL. Apenas um pequeno subconjunto da sintaxe SQL é suportada. As expressões podem ser avaliadas como predicados boleanos (retornando True ou False) ou como funções (retornando um valor escalar). Veja Expressões no Manual do Usuário para uma lista completa das funções disponíveis.

Três tipos básicos são suportados:

  • número — ambos os números inteiros e números decimais, por exemplo, 123, 3.14

  • texto — eles devem estar entre aspas simples: 'hello world'

  • coluna de referência — ao avaliar, a referência é substituída com o valor real do campo. Os nomes não alteram.

As seguintes operações estão disponíveis:

  • operadores aritméticos: +, -, *, /, ^

  • parênteses: para fazer cumprir a precedência do operador: (1 + 1) * 3

  • Sinal mais e menos: -12, +5

  • funções matemáticas: sqrt, sen, cos, tan, asen, acos, atan

  • Funções de conversão: to_int, to_real, to_string, ``to_date`

  • funções geométricas: $area, $length

  • funções de manipulação de geometria: $x, $y, $geometry, num_geometries, centroid

E os seguintes predicados são suportados:

  • comparação: =, !=, >, >=, <, <=

  • correspondência padrão: LIKE (usando % e _), ~ (expressões regulares)

  • predicados lógicos: AND, OR, NOT

  • verificação de valor nulo: IS NULL, IS NOT NULL

Exemplos de predicados:

  • 1 + 2 = 3

  • sin(ângulo) > 0

  • 'Hello' LIKE 'He%'

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

Exemplos de expressões escalares:

  • 2 ^ 10

  • sqrt(val)

  • $length + 1

11.1. Analisando Expressões

O exemplo a seguir mostra como verificar se uma determinada expressão pode ser analisada corretamente:

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 of file')

11.2. Avaliando expressões

Expressões podem ser usadas em diferentes contextos, por exemplo, para filtrar recursos ou calcular novos valores de campo. De qualquer forma, a expressão deve ser avaliada. Isso significa que seu valor é calculado executando as etapas computacionais especificadas, que podem variar de aritmética simples a expressões agregadas.

11.2.1. Expressões Básicas

Esta expressão básica avalia uma simples operação aritmética:

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

A expressão também pode ser usada para comparação, avaliando com 1 (True) ou 0 (False)

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

11.2.2. Expressões com feições

Para avaliar uma expressão em relação a uma feição, um objeto QgsExpressionContext deve ser criado e passado à função de avaliação para permitir que a expressão acesse os valores de campo da feição.

O exemplo a seguir mostra como criar uma feição com um campo chamado “Column” e como adicionar essa feição ao contexto da expressão.

 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

A seguir, é apresentado um exemplo mais completo de como usar expressões no contexto de uma camada vetorial, para calcular novos valores de 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. Filtrando uma camada com expressões

O exemplo seguinte pode ser usado para filtrar uma camada e devolver qualquer feição que corresponda a um predicado.

 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. Manipulando erros de expressão

Erros relacionados à expressão podem ocorrer durante a análise ou avaliação de expressão:

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