Loading...
Loading...
Use when writing QGIS expressions for filtering, labeling, symbology, or field calculations. Prevents expression syntax errors and context misconfiguration. Covers QgsExpression parsing, evaluation contexts, field calculator, data-defined properties, and custom functions. Keywords: QgsExpression, expression, field calculator, label expression, data-defined, @qgsfunction, filter, evaluate, calculate field, formula, conditional label, dynamic value.
npx skill4agent add openaec-foundation/qgis-claude-skill-package qgis-syntax-expressions| Step | Class | Purpose |
|---|---|---|
| 1. Parse | | Validate syntax, build AST |
| 2. Context | | Provide variables, fields, scopes |
| 3. Scopes | | Add global/project/layer/feature scopes |
| 4. Evaluate | | Execute and return result |
| 5. Validate | | Check for runtime errors |
| Element | Syntax | Example |
|---|---|---|
| Field reference | | |
| String literal | | |
| Number literal | | |
| NULL check | | |
| Comparison | | |
| Logical | | |
| Arithmetic | | |
| Pattern match | | |
| Concatenation | | |
| Conditional | | |
| Geometry vars | | |
| Current geometry | | |
| Category | Functions |
|---|---|
| Math | |
| String | |
| Conversion | |
| Date/Time | |
| Geometry | |
| Aggregates | |
| Conditionals | |
| Arrays | |
| Map/Record | |
| Color | |
exp.hasParserError()QgsExpressionexp.hasEvalError()exp.evaluate()QgsExpressionContext"name"'name'context.setFeature(feature)print()@qgsfunctionQgsMessageLogprint()referenced_columns@qgsfunction[QgsFeatureRequest.ALL_ATTRIBUTES][]usesgeometry=True@qgsfunctionfeature.geometry()Need to use an expression?
|
+-- Filter features? --> QgsFeatureRequest.setFilterExpression()
|
+-- Calculate field values? --> Field Calculator pattern (edit session + evaluate per feature)
|
+-- Drive labeling? --> QgsPalLayerSettings.fieldName + isExpression = True
|
+-- Drive symbology? --> QgsProperty.fromExpression() on symbol/renderer properties
|
+-- Evaluate standalone? --> QgsExpression.evaluate(context)
|
+-- Need custom logic? --> @qgsfunction decorator + QgsExpression.registerFunction()from qgis.core import (
QgsExpression, QgsExpressionContext, QgsExpressionContextUtils
)
exp = QgsExpression('"population" * 1.05')
if exp.hasParserError():
raise ValueError(f"Parse error: {exp.parserErrorString()}")
context = QgsExpressionContext()
context.appendScopes(
QgsExpressionContextUtils.globalProjectLayerScopes(layer)
)
for feature in layer.getFeatures():
context.setFeature(feature)
value = exp.evaluate(context)
if exp.hasEvalError():
raise ValueError(f"Eval error: {exp.evalErrorString()}")
print(f"Feature {feature.id()}: {value}")from qgis.core import QgsFeatureRequest
# Method A: expression string directly
request = QgsFeatureRequest().setFilterExpression('"population" >= 50000')
features = list(layer.getFeatures(request))
# Method B: QgsExpression object
exp = QgsExpression('"name" ILIKE \'%lake%\'')
request = QgsFeatureRequest(exp)
features = list(layer.getFeatures(request))from qgis.core import (
edit, QgsExpression, QgsExpressionContext,
QgsExpressionContextUtils
)
exp = QgsExpression('"population" * 2')
context = QgsExpressionContext()
context.appendScopes(
QgsExpressionContextUtils.globalProjectLayerScopes(layer)
)
with edit(layer):
for f in layer.getFeatures():
context.setFeature(f)
f['doubled_pop'] = exp.evaluate(context)
layer.updateFeature(f)from qgis.core import QgsPalLayerSettings, QgsVectorLayerSimpleLabeling
settings = QgsPalLayerSettings()
settings.fieldName = '"name" || \' (\' || "population" || \')\''
settings.isExpression = True
settings.enabled = True
text_format = settings.format()
text_format.setSize(12)
settings.setFormat(text_format)
labeling = QgsVectorLayerSimpleLabeling(settings)
layer.setLabeling(labeling)
layer.setLabelsEnabled(True)
layer.triggerRepaint()from qgis.core import QgsProperty
symbol = layer.renderer().symbol()
# Size driven by expression
symbol.setDataDefinedSize(
QgsProperty.fromExpression('"population" / 1000')
)
# Color driven by expression
symbol.setDataDefinedColor(
QgsProperty.fromExpression(
"CASE WHEN \"type\" = 'city' THEN '#ff0000' ELSE '#0000ff' END"
)
)
layer.triggerRepaint()from qgis.core import qgsfunction, QgsExpression
@qgsfunction(args='auto', group='Custom', referenced_columns=[])
def population_density(population, area_km2, feature, parent):
"""
Calculate population density per square kilometer.
<p>Usage: population_density("pop_field", "area_field")</p>
"""
if area_km2 and area_km2 > 0:
return population / area_km2
return None
# Register (call once, e.g., in plugin initGui)
QgsExpression.registerFunction(population_density)
# Now usable: population_density("population", "area")
# Unregister (call in plugin unload)
QgsExpression.unregisterFunction('population_density')Global scope → @qgis_version, @user_full_name, custom global vars
Project scope → @project_title, @project_crs, custom project vars
Layer scope → @layer_name, @layer_id, layer fields
Feature scope → Feature attributes, $geometry, $id, $currentfeaturefrom qgis.core import QgsExpressionContextUtils
# Set global variable (persists across sessions)
QgsExpressionContextUtils.setGlobalVariable('my_threshold', 42)
# Set project variable (saved with project)
QgsExpressionContextUtils.setProjectVariable(
QgsProject.instance(), 'analysis_year', 2024
)
# Set layer variable (saved with layer in project)
QgsExpressionContextUtils.setLayerVariable(layer, 'source_date', '2024-01-15')
# Access in expressions: @my_threshold, @analysis_year, @source_date# In expression strings:
# aggregate('layer_name', 'sum', "population")
# aggregate('layer_name', 'mean', "area", filter:="type" = 'residential')
# Common shorthand (current layer):
# sum("population")
# count("id", filter:="active" = 1)
# mean("temperature", group_by:="region")filter