Skip to content

Instantly share code, notes, and snippets.

@sfkeller
Created May 14, 2025 15:39
Show Gist options
  • Select an option

  • Save sfkeller/7dca8ab5fa8c6f754ca384218dcb0aa8 to your computer and use it in GitHub Desktop.

Select an option

Save sfkeller/7dca8ab5fa8c6f754ca384218dcb0aa8 to your computer and use it in GitHub Desktop.
This QGS Python API script enables features in a vector layer to be filtered dynamically according to the layer's current visible extent on the map canvas.
"""
QGIS Dynamic Filtering Script
Usage:
0. Ensure your vector dataset contains a ranking field, then load it into QGIS.
1. Adapt 'LAYER_NAME' and 'SQL_FILTER_TEMPLATE' in the 'Constants' section below.
2. Open the Python console in QGIS, then open and execute this script.
3. To disable filtering, copy and paste the following into the Python console:
disable_dynamic_filter()
This script enables features in a vector layer to be filtered dynamically
according to the layer's current visible extent on the map canvas. Only the e.g.
50 highest-ranked visible features are displayed (depending on a customizable
filter query).
It is assumed that there is a vector dataset containing a ranking field (e.g.
'qrank'). Here's an example (no guarantee how long this storage will last):
https://drive.switch.ch/index.php/s/RsYy9oBxWfad1am
"""
# Constants
LAYER_NAME = "castles_etc_ranked_osm_2025"
SQL_FILTER_TEMPLATE = """
SELECT * FROM "{layer_name}"
WHERE "X" BETWEEN {x_min} AND {x_max}
AND "Y" BETWEEN {y_min} AND {y_max}
ORDER BY qrank DESC LIMIT 50
"""
def update_filter_on_extent_change():
"""Update the layer filter whenever the map canvas extent changes."""
# Get the current map canvas
canvas = iface.mapCanvas()
# Get the current extent
extent = canvas.extent()
try:
# Get the layer by name
layer = QgsProject.instance().mapLayersByName(LAYER_NAME)[0]
# Transform coordinates if needed
layer_crs = layer.crs()
canvas_crs = canvas.mapSettings().destinationCrs()
if layer_crs != canvas_crs:
transform = QgsCoordinateTransform(canvas_crs, layer_crs, QgsProject.instance())
extent = transform.transformBoundingBox(extent)
# Create the SQL filter with the current extent coordinates
sql_filter = SQL_FILTER_TEMPLATE.format(
layer_name=LAYER_NAME,
x_min=extent.xMinimum(),
x_max=extent.xMaximum(),
y_min=extent.yMinimum(),
y_max=extent.yMaximum()
)
# Apply the filter to the layer
layer.setSubsetString(sql_filter)
# Refresh the layer
layer.triggerRepaint()
except IndexError:
print(f"Error: Layer '{LAYER_NAME}' not found")
except Exception as e:
print(f"Error updating filter: {e}")
def disable_dynamic_filter():
"""Disable the dynamic filter and restore full layer visibility.
To disable the filter, copy and paste this function call:
disable_dynamic_filter()
"""
try:
# Get the layer by name
layer = QgsProject.instance().mapLayersByName(LAYER_NAME)[0]
# Remove filter query
layer.setSubsetString("")
# Disconnect the extent changed signal
iface.mapCanvas().extentsChanged.disconnect(update_filter_function)
print(f"Dynamic filter disabled for layer '{LAYER_NAME}'")
# Refresh the layer
layer.triggerRepaint()
except IndexError:
print(f"Error: Layer '{LAYER_NAME}' not found")
except NameError:
print("Error: update_filter_function not defined")
except Exception as e:
print(f"Error removing dynamic filter: {e}")
# Store a reference to our function
global update_filter_function
update_filter_function = update_filter_on_extent_change
# Connect the function to the extentChanged signal
iface.mapCanvas().extentsChanged.connect(update_filter_function)
# Run it once to set the initial filter
update_filter_function()
print("Dynamic filtering enabled. To disable, run 'disable_dynamic_filter()' in the Python console.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment