11. Выражения, фильтрация и вычисление значений

Подсказка

The code snippets on this page need the following imports if you’re outside the pyqgis console:

 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 has some support for parsing of SQL-like expressions. Only a small subset of SQL syntax is supported. The expressions can be evaluated either as boolean predicates (returning True or False) or as functions (returning a scalar value). See Expressions in the User Manual for a complete list of available functions.

Поддерживается три основных типа данных:

  • число — как целые, так и десятичные, например, 123, 3.14

  • строка — должна заключаться в одинарные кавычки: 'hello world'

  • ссылка на столбец — при вычислении ссылка заменяется на значение поля. Имена полей не экранируются.

Доступны следующие операции:

  • арифметические операторы: +, -, *, /, ^

  • скобки: для изменения приоритета операций: (1 + 1) * 3

  • унарный плюс и минус: -12, +5

  • математические функции: sqrt, sin, cos, tan, asin, acos, atan

  • conversion functions: to_int, to_real, to_string, to_date

  • геометрические функции: $area, $length

  • geometry handling functions: $x, $y, $geometry, num_geometries, centroid

And the following predicates are supported:

  • сравнение: =, !=, >, >=, <, <=

  • соответствие образцу: LIKE (using % and _), ~ (регулярные выражения)

  • огические операторы: AND, OR, NOT

  • проверка на NULL: IS NULL, IS NOT NULL

Примеры предикатов:

  • 1 + 2 = 3

  • sin(angle) > 0

  • 'Hello' LIKE 'He%'

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

Примеры скалярных выражений:

  • 2 ^ 10

  • sqrt(val)

  • $length + 1

11.1. Разбор выражений

The following example shows how to check if a given expression can be parsed correctly:

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. Вычисление выражений

Expressions can be used in different contexts, for example to filter features or to compute new field values. In any case, the expression has to be evaluated. That means that its value is computed by performing the specified computational steps, which can range from simple arithmetic to aggregate expressions.

11.2.1. Простые выражения

This basic expression evaluates a simple arithmetic operation:

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

Expression can also be used for comparison, evaluating to 1 (True) or 0 (False)

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

11.2.2. Выражения с объектами

To evaluate an expression against a feature, a QgsExpressionContext object has to be created and passed to the evaluate function in order to allow the expression to access the feature’s field values.

The following example shows how to create a feature with a field called «Column» and how to add this feature to the expression context.

 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

The following is a more complete example of how to use expressions in the context of a vector layer, in order to compute new field values:

 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. Filtering a layer with expressions

Следующие примеры могут использоваться для фильтрации слоя и возвращения объектов, удовлетворяющих условию.

 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. Handling expression errors

Expression-related errors can occur during expression parsing or evaluation:

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