bluegreencity-ki

WFS Service for Tree Data Sharing

This document describes the Web Feature Service (WFS) that shares the current tree dataset — including the daily-updated Soil Water Balance Model (SWBM) values and the AutoML drought risk scores — with external partners (d.b.g. Database Company GmbH, Landeshauptstadt Hannover).

The service is served through the QGIS Web Client (QWC) stack, which proxies WFS requests to QGIS Server with role-based permission filtering.

Overview

The WFS exposes a single layer, wfs_trees_current, backed by the PostGIS view bgcki.wfs_trees_current.

Endpoint

The WFS is reached through the QWC API gateway. The partner-facing URL is the short service path /ila/bgcki (a transparent nginx alias in qwc-docker/api-gateway/nginx.conf that proxies to the QWC OGC service):

https://<qwc-host>:<qwc-port>/ila/bgcki?SERVICE=WFS&VERSION=1.1.0&REQUEST=...
Parameter Value
Host <qwc-host> (project network / Tailscale)
Port <qwc-port> (QWC API gateway)
Path /ila/bgcki (partner alias)
Service WFS
Version 1.1.0
Layer / TypeName wfs_trees_current

The legacy QWC service path /ows/scan/bgcki_trees (<service_name> = scan/bgcki_trees) still works and is used internally; new partners should receive the /ila/bgcki link. The alias is dataset-agnostic on purpose — future layers (non-tree data) will be served under the same service path and distinguished by TYPENAME.

Note: The exact host and port are internal infrastructure values and are intentionally not published here. They are provided to partners out of band.

Example Requests

GetCapabilities

GET /ila/bgcki?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities

Returns the service metadata, including the title “BlueGreenCity-KI Bäume” and the layer list.

GetFeature (limited)

GET /ila/bgcki?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=wfs_trees_current&MAXFEATURES=10

Returns up to 10 features as GML 3.1.1.

GetFeature (spatial filter)

GET /ila/bgcki?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=wfs_trees_current&FILTER=<url-encoded CQL>

CQL filters are supported, e.g. to select a bounding box or attribute subset.

Data Sources

The view bgcki.wfs_trees_current joins three sources:

  1. bgcki.analysis_tree_risk_scores_operational_current — the current operational tree risk scores (all inventory and AutoML columns). Since Issue #253 (2026-10) the AutoML columns include drought_risk_ml_calibrated — the Platt-calibrated damage probability; the risk-class thresholds apply to that column, while drought_risk_ml keeps the raw model output.
  2. bgcki.swbm_daily_state — the latest SWBM state per tree (model version *:irrigation-lower) plus the 7-day forecast window (state_date = CURRENT_DATE + 1 … + 7), joined via a single LATERAL subquery on tree_id = objectid. The window is evaluated at query time, so it advances automatically with the daily 04:00 run.

    swbm_daily_state holds multiple model_version variants per tree (unknown-age / unknown-SVF scenarios) and stale variants from earlier runs. The join therefore keeps only the current variants (the ones the latest run wrote) and computes the conservative envelope over them — the same convention as the irrigation planning run: demand/stress = max, fractions/volumes = min (driest), trigger date = min (earliest), avoidable-by-rain = AND. Trees with several current variants are labelled conservative-scenario-envelope in swbm_model_version.

  3. bgcki.operational_tree_product — the daily operational product (04:00 refresh, same tree set, 1:1 on objectid). Exposed via the view: irrigation_action_priority — the 0–1 irrigation prioritization score combining ml_damage_score_t1, site_vulnerability_rulebased, the SWBM forecast stress and the irrigation demand in liters (see bluegreencity_ki/risk_products.py, calculate_action_priority; applied per tree in bluegreencity_ki/operational_products.py). This is the score used to rank trees for irrigation action.

The SWBM columns are prefixed with swbm_ to avoid name collisions, e.g.:

Column Description
swbm_state_date date of the latest SWBM state
swbm_soil_water_fraction_total total soil water fraction (0–1)
swbm_soil_water_fraction_layer_1 … _layer_3 per-layer soil water fractions
swbm_soil_water_stress_today soil water stress for the current day
swbm_soil_water_stress_forecast_max_7d max forecast soil water stress (7 d)
swbm_irrigation_demand_liters irrigation demand in liters
swbm_irrigation_trigger_date date the irrigation trigger is reached
swbm_irrigation_avoidable_by_rain whether irrigation is avoidable by rain
swbm_data_quality data quality flag
swbm_soil_water_m3_total … _layer_3 soil water volume in m³
swbm_surface_wet_fraction surface wet fraction
swbm_meteorology_source meteorology source
swbm_model_version SWBM model version
swbm_irrigation_demand_day_1 … _day_7 forecast irrigation demand in liters for CURRENT_DATE + N (day 1 = tomorrow)
swbm_fraction_layer_1_day_1 … _day_7 forecast layer-1 soil water fraction (0–1.2) for CURRENT_DATE + N

Forecast columns are NULL when the forecast row for that day is missing (e.g. trees the daily resume run has not backfilled yet). The per-day soil water stress is intentionally not exposed here — the aggregated swbm_soil_water_stress_forecast_max_7d column covers the warning case, and day-by-day stress values remain available in swbm_daily_state.

The migrations that define the view are sql/migrations/2026-09-14_create_wfs_trees_view.sql, sql/migrations/2026-09-16_fix_wfs_trees_view_forecast_rows.sql sql/migrations/2026-09-21_add_wfs_forecast_day_columns.sql and sql/migrations/2026-10-09_add_wfs_calibrated_column.sql (adds drought_risk_ml_calibrated, issue #253) and sql/migrations/2026-10-09_add_wfs_priority_column.sql (current definition, adds irrigation_action_priority via the operational_tree_product join).

Outputs

Output formats

QGIS Server supports GML/XML and GeoJSON for WFS GetFeature. Requests with OUTPUTFORMAT=gpkg, csv, or shp are ignored and GML is returned instead.

This is a known QGIS Server limitation. GML 3.1.1 can be imported directly by:

For partners who need a flat file (GPKG/CSV/Shapefile), the recommended workflow is to fetch the GML and convert it locally, e.g.:

import geopandas as gpd

gdf = gpd.read_file(
    "https://<qwc-host>:<qwc-port>/ila/bgcki",
    layer="wfs_trees_current",
)
gdf.to_file("trees.gpkg", driver="GPKG")

Permissions

The WFS service is restricted by default in QWC. Access is granted to the public role via the QWC ConfigDB:

Once the service is permitted, all layers and attributes of the service are allowed by default. The generated permissions.json reflects this.

To change permissions, use the QWC Admin GUI or update the ConfigDB directly, then regenerate the configs.

Maintenance

Troubleshooting

Symptom Cause Fix
Service unknown or not permitted WFS service not permitted for the requesting role Grant wfs_service permission in ConfigDB, regenerate configs, restart OGC service
Layer(s) not valid in QGIS Server logs QGIS project layer is invalid (e.g. broken DB connection) Check the PostGIS connection in the .qgs project
QGIS Desktop: “Layerausdehnung ist nicht richtig” bei Layer-Reload Achsen-Order-Mismatch: Bei Projekt-CRS EPSG:4326 sendet der Desktop-Client (QGIS 4.x) den WFS-BBOX in der offiziellen 4326-Achsenreihenfolge (lat/lon), der Server interpretiert “EPSG:4326” als lon/lat (GeoServer-Legacy-Konvention, im Server-Code hartkodiert) → Box liegt an falscher Stelle → 0 Features. Der 5. BBOX-Parameter (CRS) greift nur zusätzlich zu SRSNAME. Derselbe lat/lon-BBOX funktioniert im WMS 1.3.0 desselben Servers. Nicht gefixt bis QGIS Server 4.3-dev (Code identisch, am 2026-10-09 geprüft). Reproduziert am 2026-10-09 mit minimalem Testprojekt (3-Punkt-Shapefile, ohne Postgres/QWC) — keine Projekt-Konfiguration schuld Option 1 (Client): ignoreAxisOrientation=1 an die URL des WFS-Layers hängen (Quellverwaltungen → WFS → Layer) → Client sendet lon/lat → funktioniert (Server-Seite verifiziert: SRSNAME=EPSG:4326 + lon/lat-BBOX liefert Features). Option 2 (Projekt): Projekt-CRS auf EPSG:25832 setzen (Projekt-Standard) — Client fragt dann in 25832 an. Übergangsweise: einmal zum Layer zoomen. Bug-Report bei qgis/QGIS ist in Vorbereitung
GML returned instead of GPKG/CSV QGIS Server does not support those WFS output formats Convert GML locally (see Output formats)
Full-city download fails: “Operation abgebrochen” / 500 Complete GetFeature (89k features, ~122 MB GML) exceeds the memory of the QWC ogc-service attribute-whitelist filter (ElementTree DOM parse). BBOX-restricted and RESULTTYPE=hits requests work fine. Zoom to the area of interest before loading (layer is restricted to the view extent), or use the direct PostGIS connection for full-dataset work. Server side: QGIS Server generates the full GML in ~50 s without issue
Config generation fails with “critical error” Pre-existing broken QGIS projects produce ERROR logs Set "ignore_errors": true in the tenant config config block