config/config.yamlThis document is the authoritative reference for every key in config/config.yaml.
It is structured to mirror the file’s top-level sections so you can read them in
parallel.
config/config.yaml is the single configuration file that controls the entire
BlueGreenCity-KI pipeline. It is loaded by bluegreencity_ki.core.config.load_config
and passed as a plain Python dict to every module.
config/config.yaml
│
├── project – CRS, run_date, backend, study areas
├── analytics – Grafana read-only PostGIS analytics cache
├── inputs – data sources (trees, rasters, roads, soil, …)
├── criteria – list of risk criteria with weights and params
├── land_use – land-use code → risk-score mapping
├── output – GeoPackage / PostGIS output settings
├── integration – optional API integrations
├── sustainability – energy/CO₂ tracking
├── savings – optional water/fuel/green-volume estimates
├── validation – comparison against drought-damage ground truth
├── plausibility – automated sanity checks
├── report – artefact toggles (histograms, PNGs)
├── data_management – PostGIS import and versioning metadata
├── climate – DWD/Open-Meteo import settings
├── risk_products – site vulnerability + irrigation action priority
├── irrigation_planning – deterministic irrigation draft planner
├── irrigation_review – local Taipy review UI settings
└── ml – AutoGluon training and inference settings
The file ships with Hannover-specific defaults. All paths are relative to the project root unless stated otherwise or an absolute path is provided.
projectGlobal pipeline settings.
| Key | Type | Default | Description |
|---|---|---|---|
crs |
string | "EPSG:25832" |
Coordinate reference system used throughout the pipeline (ETRS89 / UTM zone 32N for Hannover). |
run_date |
string | "auto" |
Analysis reference date. "auto" uses today’s date; pass an ISO date string ("2025-06-01") to fix the date for reproducibility. |
feature_reference_year |
int | null | null |
Explicit reference year for year-aware criteria (age, groundwater, NDVI). When set, overrides the year derived from run_date. Use this to produce historical or panel snapshots (e.g. 2020). When run_date is "auto" and this key is set, run_date is automatically set to {feature_reference_year}-01-01. |
backend |
string | "osgeo" |
Spatial processing backend. Only "osgeo" (GeoPandas + GDAL) is supported in production. |
scenario |
string | "baseline" |
Scenario label stored in output metadata. Supports {feature_reference_year}. Change this when running alternative parameter scenarios. |
neutral_score |
float | 0.5 |
Score assigned when a criterion cannot compute a value (missing data). Range: 0–1. |
raster_base_dir |
string | (none) | Base directory prepended to relative raster paths in inputs. Absolute raster paths always override this. If omitted, relative paths are resolved from the project root. |
raster_sampling_cache |
mapping | see below | Optional persistent cache for raster sampling results across runs. |
File output paths support {scenario}, {feature_reference_year}, {run_date}
(YYYY-MM-DD), {run_date_underscore} (YYYY_MM_DD), {study_area} and
{active_area}. For example,
./data/output/{run_date_underscore}_{scenario}/trees_at_risk.gpkg
creates a readable run folder such as
2026_07_28_ml_operational_panel_2018_2023_predict_2024_validate_2025.
project.raster_sampling_cachePersistent cache settings for expensive raster sampling (point/zonal).
The cache stores tree-level sampled values on disk and reuses them in later runs
when the cache key matches.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enables persistent disk cache for raster sampling. |
cache_dir |
string | "./data/cache/raster_sampling" |
Directory where cache files are stored. |
refresh |
bool | false |
If true, bypasses existing cache entries for the current run and rewrites them. |
version |
int | string | 1 |
Manual cache namespace/version. Increase this to invalidate all existing entries after algorithm changes. |
project.study_areasDefines spatial regions for the analysis.
project:
study_areas:
active: "hannover_gesamt" # Key of the currently active study area
definitions:
hannover_gesamt: { … }
hannover_ml_training_2010: { … }
study_areas.activeSet this to the key of whichever study area definition should be used for the current run.
study_areas.definitions.<area_key>Each study area definition can contain:
| Key | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Human-readable display name. |
description |
string | no | Free-text description stored in run metadata. |
bbox_source |
string | yes | "auto" (derive bounding box from tree data) or "manual" (use the bbox key below). |
bbox |
list of 4 floats | when bbox_source: manual |
[minlon, minlat, maxlon, maxlat] in EPSG:4326. |
trees |
mapping | yes | Tree data source definition (see below). |
trees sub-keys (CSV inventory)trees:
type: "csv_inventory"
path: "./data/input/trees/Baum-2010-2025-v6_historisch_prod.csv"
delimiter: ";"
encoding: "utf-8"
timestamp_column: "Zeitpunkt"
timestamp_filter: "latest" # or "2025-01-01" to pin a snapshot
keep_invalid_geometry_rows: true
exclude_from_analysis_statuses:
- "removed_tree"
- "tree_group_b"
- "planned_tree_f"
- "planned_not_planted"
- "erroneous_record"
geometry:
x_column: "Rechtswert"
y_column: "Hochwert"
source_crs: "EPSG:25832"
# Optional fallback (issue #216): rows without Rechtswert/Hochwert but with
# valid WGS84 lat/lon get a point reprojected from EPSG:4326.
lat_column: "Y (lat)"
lon_column: "X (long)"
column_mapping:
"Baum ID": "objectid"
"ID": "inventory_record_id"
"Baumnummer": "baumnummer"
"Weitere Hauptdaten Baumnummer (Typ)": "baumnummer_typ"
"Gefällt am": "gefaellt_am"
"Hauptdaten Pflanzjahr": "pflanzjahr"
# … more column renames
parse_leading_integer:
- "vitalitaet" # "2 noch befriedigend…" → 2
id_field: "objectid"
species_field: "ga_lang"
| Key | Type | Description |
|---|---|---|
type |
string | "csv_inventory" for flat CSV files; "postgis" to read from PostgreSQL. |
path |
string | Path to the CSV file (relative to project root). |
timestamp_column |
string | Column that holds the snapshot date. Used with timestamp_filter. |
timestamp_filter |
string | "latest" selects the most recent snapshot; an ISO date selects a specific one. |
timestamp_filter_mode |
string | Controls per-group filtering when timestamp_filter is a date. "latest" (default) picks the single most recent row across all groups; "latest_on_or_before_per_group" picks the latest row per tree (identified by timestamp_group_column) on or before the filter date — useful for building historical panel snapshots where each tree should have its own most-recent-available record. |
timestamp_group_column |
string | Required when timestamp_filter_mode: "latest_on_or_before_per_group". Column that identifies individual trees across multiple snapshots (e.g. "ID"). |
keep_invalid_geometry_rows |
bool | Keep rows with invalid coordinates as raw records with empty geometry. They remain available via ctx.vector["trees_raw"]. |
exclude_from_analysis_statuses |
list | Inventory statuses that are excluded from criteria evaluation after loading. |
geometry.x_column / geometry.y_column |
string | CSV columns holding X (easting) and Y (northing) coordinates. |
geometry.source_crs |
string | CRS of the coordinate columns. Reprojected to project.crs automatically. |
geometry.lat_column / geometry.lon_column |
string | (optional, issue #216) WGS84 latitude/longitude columns used as a fallback for rows whose projected coordinates are missing or zero. The point is reprojected from EPSG:4326 to source_crs. Projected coordinates always take priority. |
column_mapping |
map | Renames long CSV headers to short pipeline-compatible names. Original columns are kept in the in-memory pipeline frame; on export (PostGIS/GPKG) redundant source columns — including legacy dot-separated variants from earlier CSV versions (e.g. Hauptdaten.Pflanzjahr) — are dropped when their mapped target is present. |
parse_leading_integer |
list | Columns where a leading integer is extracted from text before use (e.g. vitality stage). |
row_filters |
map | (optional) Filter rows by column value before loading. Useful to restrict to a single Objektart. |
id_field |
string | Column used as the tree identifier throughout the pipeline. |
species_field |
string | Column containing the species name (used by tolerance criteria). |
The loader also derives a domain-level status model from the CSV, including
inventory_status, exclude_from_analysis, and flags such as
is_planned_not_planted, is_removed_tree, is_tree_group_b,
is_planned_tree_f, is_erroneous_record, and has_valid_coordinates.
trees:
<<: *hannover_tree_inventory
timestamp_filter: "2010-12-31"
id_field: "objectid"
PostGIS tree inputs are no longer used. Historical study areas reuse the configured inventory CSV and select a fixed snapshot date.
analyticsRead-only PostGIS cache tables consumed by the Grafana dashboards. The active study area’s consolidated CSV remains the single source of truth for every rebuild.
analytics:
ml_dashboard:
schema: "bgcki"
feature_source_table: "analysis_tree_risk_scores_ml_operational_panel"
categorical_numeric_features: ["land_use_value"]
files: # annual-validation artifact layout
feature_importance: "feature_importances/feature_importance_future_drought_damage_severe_t1.csv"
leaderboard: "leaderboards/leaderboard_future_drought_damage_severe_t1.csv"
shap_values: "shap/risk_classes_shap_values.csv"
feature_values: "shap/risk_classes_feature_values.csv"
operational_files: # operational artifact layout (high-risk SHAP scope)
feature_importance: "model/drought_risk_t1/explanation_feature_importance_training.csv"
shap_values: "shap/high_risk_shap_values.csv"
feature_values: "shap/high_risk_feature_values.csv"
tables:
feature_importance: "ml_feature_importance"
leaderboard: "ml_model_leaderboard"
shap_values: "ml_shap_value"
correlation: "ml_feature_correlation"
internal_validation: "ml_internal_validation"
spatial_diagnostics: "ml_spatial_diagnostics"
tree_inventory:
schema: "bgcki"
table: "tree_inventory_observation"
chunk_size: 50000
current:
observed_at: "2026-09-11"
source_columns: {…} # v6 current-extract column mapping
regular_care_areas: ["67.31.1", …, "67.31.7"]
excluded_tree_number_types: ["freier Standort", …]
source_columns: {…} # historical CSV column mapping
tree_year_analysis:
schema: "bgcki"
table: "tree_year_analysis"
feature_source_table: "analysis_tree_risk_scores_ml_operational_panel"
inventory_source_table: "tree_inventory_observation"
| Key | Description |
|---|---|
ml_dashboard |
ML dashboard cache: feature source table, categorical_numeric_features, artifact file paths (files.* for the annual-validation layout, operational_files.* for the operational layout) and target tables.*. artifact_run_dir is auto-resolved from the newest annual-validation run; set it explicitly only to pin Grafana to a historical run. |
tree_inventory |
Tree-inventory observation sync (table tree_inventory_observation, chunk_size: 50000). current.* maps the v6 current-extract columns (imported once per update as a single observation dated observed_at); source_columns maps the historical CSV columns; regular_care_areas and excluded_tree_number_types filter the sync. |
tree_year_analysis |
Yearly per-tree analysis table (tree_year_analysis) built from the ML feature source table and the inventory observation table. |
inputsExternal data sources loaded before criteria run.
inputs.treesLegacy shortcut for the tree source fields referenced from criteria modules. The actual data source is defined in the active study area.
inputs:
trees:
id_field: "objectid"
species_field: "ga_lang"
inputs.enrichmentSpatial enrichment datasets are joined onto the tree inventory before any criteria are computed. All criteria automatically receive enriched attributes.
inputs.enrichment.obskhJoins OBSkH crown data to the tree inventory via nearest-neighbour spatial join.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Master switch for this enrichment layer. |
path |
string | — | Path to the OBSkH point shapefile (relative to raster_base_dir or absolute). |
join_tolerance_m |
float | 3.0 |
Maximum join distance (metres). |
crown_diameter_field |
string | "krone" |
Column in the shapefile holding crown diameter (m). |
crown_diameter_max_m |
float | 35.0 |
Plausibility cap: values above this are set to NaN. |
inputs.enrichment.citreeLoads the maintained Citree species-criteria catalog and joins it onto the
inventory by species. Currently enabled (enabled: true); the catalog
CSV lives under the repository (maintained via
scripts/extract_citree_species_catalog.py), not under raster_base_dir.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Master switch. When enabled, all citree_* columns are available to criteria (e.g. drought_tolerance, heat_tolerance). |
path |
string | "./data/derived/species/citree_species_criteria.csv" |
Path to the species catalog CSV (relative to project root). |
species_field |
string | "ga_lang" |
Tree column matched against the catalog species names. |
columns |
map | see config | Catalog column → enriched citree_* column (14 columns, e.g. Trockenheitstoleranz → citree_trockenheitstoleranz). |
inputs.roadsOSM road network used by the street_type and salt criteria.
| Key | Type | Default | Description |
|---|---|---|---|
source |
string | "osm" |
"osm" fetches via Overpass API and caches to PostGIS; "local" reads from the PostGIS table directly. |
postgis_table |
string | "context_osm_highways" |
PostGIS table where OSM roads are cached. |
postgis_schema |
string | "bgcki" |
PostGIS schema. |
allowed |
list | see config | OSM highway types to include. Order matters for street_type risk scoring (first = highest risk). |
buffer_m |
float | 200.0 |
Bounding-box buffer (metres) added when fetching roads to ensure roads near the edge are fully captured. |
overpass_endpoint |
string | "https://overpass-api.de/…" |
Overpass API URL. |
timeout_s |
int | 180 |
Overpass request timeout in seconds. |
refresh_days |
int | 365 |
Cached OSM roads are re-fetched after this many days. |
inputs.alkis_roadsALKIS NAS WFS integration (LGLN Niedersachsen open data). Provides precise
parcel-based street names for the salt criterion.
| Key | Type | Description |
|---|---|---|
wfs_url |
string | LGLN WFS NAS endpoint URL. |
wfs_version |
string | WFS protocol version (e.g. "2.0.0"). |
flurstueck_layer |
string | WFS layer name for parcels. |
lagebezeichnung_layer |
string | WFS layer name for parcel addresses. |
street_names_csv_url |
string | URL of the NDS street-name ZIP (CSV inside). |
gemeinde_schluessel |
string | 8-digit Gemeindeschlüssel identifying the municipality. |
cache_dir |
string | Local directory for the ALKIS GPKG cache. |
cache_path |
string | Full path to the cached GPKG file. |
cache_layer |
string | Layer name inside the cache GPKG. |
refresh_days |
int | Re-fetch from WFS after this many days. |
bbox_wgs84 |
list of 4 floats | [minlon, minlat, maxlon, maxlat] bounding box of the municipality in WGS84. |
wfs_timeout |
int | HTTP timeout per WFS request (seconds). |
wfs_page_size |
int | Features per WFS page (pagination). |
landnutzung.gpkg_url |
string | STAC download URL for ALKIS Landnutzung GPKG. |
landnutzung.layer |
string | Layer name for road surfaces inside the GPKG. |
landnutzung.funktion_filter |
list of ints | ALKIS Funktion codes to keep (carriageway surfaces only). |
inputs.salt / inputs.salt_joinConfiguration for the aha Winterdienst salt-exposure dataset.
inputs:
salt:
winterdienst_csv: "./data/derived/salt/winterdienst_strassenverzeichnis_2026.csv"
winterdienst_name_field: "strassenname"
winterdienst_class_field: "winterdienst"
priority_field: "winterdienst"
salt_join:
max_distance: 50.0 # metres
| Key | Type | Description |
|---|---|---|
salt.winterdienst_csv |
string | Path to the parsed Winterdienst street list CSV. |
salt.winterdienst_name_field |
string | Column with street name in the CSV. |
salt.winterdienst_class_field |
string | Column with priority class (W0, W1, W2). |
salt.priority_field |
string | Column forwarded to the criterion as the priority attribute. |
salt_join.max_distance |
float | Maximum tree-to-road distance (metres) for the spatial join. |
inputs.osm_joinOptional cache for the tree–OSM-highway spatial join result.
| Key | Type | Description |
|---|---|---|
cache_path |
string | Path to the joined GeoPackage cache file. |
max_distance |
float | Maximum search radius (metres) for nearest-road lookup. |
All raster entries follow a common structure:
<raster_key>:
type: "file" # or "tif"
path: "…" # relative to raster_base_dir, or absolute
description: "…" # optional, metadata only
| Config key | Description |
|---|---|
landuse_raster |
Land-use classification raster (Hannover Klimaanalyse 2022). |
airtemp_raster_14h |
Daytime (14 h) air temperature raster (°C, 10 m resolution). |
airtemp_raster_04h |
Night-time (04 h) air temperature raster (°C). |
ndvi_raster |
NDVI raster from 2024 CIR aerial imagery. |
ndvi_raster_2020 |
NDVI raster from 2020 for temporal comparison. |
cold_air_kpr_raster |
Cold air production rate (KPR, m³/m²/h). |
cold_air_kvs_raster |
Cold air volume flow (KVS, m³/m/s). |
sky_view_factor_raster |
Sky View Factor (0–1, DGM + LOD2 buildings). |
sunlit_hours_spring_raster |
Sunlit hours at spring equinox (March 20). |
sunlit_hours_summer_raster |
Sunlit hours at summer solstice (June 21). |
sunlit_hours_autumn_raster |
Sunlit hours at autumn equinox (September 22). |
sunlit_hours_winter_raster |
Sunlit hours at winter solstice (December 21). |
albedo_raster |
Surface albedo from UrbanGreenEye Sentinel-2 2024 (type: "file"; stores reflectance ×1000 — the albedo criterion scales it back with scale_factor: 0.001). |
uhi_siedlungsraum_raster |
UHI night-temperature deviation (K) over settlement areas, GEONET Klimaanalyse 2022 (type: "file", 10 m). |
uhi_verkehr_raster |
UHI night-temperature deviation (K) over traffic areas, GEONET Klimaanalyse 2022 (type: "file", 10 m). |
dwsi_rasters |
UrbanGreenEye DWSI vitality rasters 2018–2025 (type: "file" per entry); a yearly_sources list with raster keys dwsi_2018 … dwsi_2025, used by the dwsi criterion. |
inputs.groundwaterGroundwater monitoring data from LHH.
| Key | Type | Description |
|---|---|---|
type |
string | "directory" – scans the folder for matching CSV files. |
path |
string | Path to the directory containing Flurabstand CSVs (relative to raster_base_dir). |
file_pattern |
string | Glob pattern to select CSVs inside the directory. |
grimm_strele_path |
string | Path to the Grimm-Strele 20-year trend shapefile. |
well_id_field |
string | Column with monitoring well ID. |
x_field / y_field |
string | Coordinate columns (Rechtswert / Hochwert, EPSG:25832). |
flurabstand_field |
string | Column with depth-to-groundwater value (m). |
year_field |
string | Column with the measurement year. |
inputs.nfk_wfsWFS source for NFK (usable field capacity) from the LBEG BK50 soil map.
| Key | Type | Description |
|---|---|---|
url |
string | LBEG WFS endpoint. |
typename |
string | WFS layer name. |
version |
string | WFS protocol version. |
srs_name |
string | CRS of the WFS service. |
bbox_wgs84 |
list | [minlon, minlat, maxlon, maxlat] in WGS84. |
nfk_value_field |
string | Attribute with numeric NFKWE value (mm). |
nfk_class_field |
string | Attribute with NFKWE class (1–5). |
cache_path |
string | Path to the local GPKG cache. |
refresh_days |
int | Refresh interval in days. |
inputs.gk25_wfsWFS source for the GK25 geologic map (LBEG NIBIS cardomap, Detailkartierung, issue #166). The server ignores BBOX filters, so the loader performs a full GML download and clips client-side; cached.
| Key | Type | Description |
|---|---|---|
url / version |
string | LBEG cardomap WFS endpoint (NodeId=201) and protocol version ("2.0.0"). |
typename |
string | WFS layer ("cls:L160"). |
srs_name |
string | Requested CRS ("EPSG:25832"). |
schichten_field / klartext_field |
string | Attribute columns with the layer-sequence symbol string ("SCHICHTEN") and its text description ("KLARTEXT"). |
bbox_wgs84 |
list | [minlon, minlat, maxlon, maxlat] client-side clip box. |
cache_path |
string | Local GPKG cache ("./data/derived/geology/gk25_hannover.gpkg"). |
refresh_days |
int | Refresh interval (live: 365). |
inputs.overture_transportationOverture Maps transportation network filtered to subway tunnel segments
(rail_classes: ["subway"], tunnel_only: true, issue #139); static
infrastructure, therefore cached. Used by the subway_tunnel criterion.
| Key | Type | Description |
|---|---|---|
bbox_wgs84 |
list | Query bounding box in WGS84. |
rail_classes |
list | Overture rail classes to fetch (live: ["subway"]). |
tunnel_only |
bool | Keep only is_tunnel segments. |
cache_path |
string | Local GPKG cache ("./data/derived/transport/subway_tunnels.gpkg"). |
refresh_days |
int | Refresh interval (live: 365). |
inputs.osm_constructionOSM construction sites (highway=construction, issue #139), fetched fresh
from the Overpass API on every run — deliberately no cache, since
construction sites are temporary. Used by the construction_sites
criterion.
| Key | Type | Description |
|---|---|---|
bbox_wgs84 |
list | Query bounding box in WGS84. |
inputs.planting_sitesLHH “Baumumfeld” planting-site polygons (issue #194). Partial coverage and
read fresh on every run — never cached. Used by land_use (surface-type
score override) and planting_structure (trees in the same polygon).
| Key | Type | Description |
|---|---|---|
type |
string | "file" — local GPKG (path relative to raster_base_dir). |
path |
string | "Baumumfeld/Baumumfeld_zusammengefasst.gpkg". |
layer |
string | GPKG layer name ("Baumumfeld_zusammengefasst"). |
surface_type_field |
string | Polygon column holding the surface type ("kategorie"). |
inputs.seasonal_weatherPer-tree × per-year weather features from DWD HYRAS + FAO-ET0
(issue #213); used by the seasonal_weather criterion.
| Key | Type | Description |
|---|---|---|
type |
string | "csv". |
path |
string | "data/derived/ml/tree_seasonal_weather.csv" (relative to project root). |
criteriaThe criteria key is a list of criterion entries. Each entry activates one
criterion and assigns it a weight in the final risk score aggregation.
criteria:
- name: "age"
weight: 0.5
params: # optional, criterion-specific
…
normalizer_params: # optional
q_low: 0.05
q_high: 0.95
clip: true
| Sub-key | Type | Description |
|---|---|---|
name |
string | Criterion identifier (must match a registered criterion in criteria_catalog). |
weight |
float | Relative weight in the weighted-average aggregation. All weights are normalized automatically; absolute values only matter relative to one another. |
params |
map | Criterion-specific parameters (see individual sections below). |
normalizer_params |
map | Parameters for the quantile normalizer (q_low, q_high, clip). Used by continuous criteria. |
Order of criteria in the list does not affect the final score. Remove a criterion entry to exclude it from the run.
saltEvaluates road-salt exposure risk using aha Winterdienst priority classes.
- name: "salt"
weight: 0.5
params:
mapping:
"W1": 1.0 # highest priority (most salt) → highest risk
"W2": 0.5
"W0": 0.0 # not on Winterdienst route → no salt risk
| Param | Type | Description |
|---|---|---|
mapping |
map | Maps Winterdienst class to risk score (0–1). |
street_typeScores drought risk based on the OSM highway classification of the nearest road.
- name: "street_type"
weight: 0.5
No params are required. The risk ordering is derived from the order of
inputs.roads.allowed: the first entry in that list is highest risk, the last
is lowest. Trees with no nearby road receive score 0.0.
ageEstimates tree age and maps it to a risk score using quantile normalization.
- name: "age"
weight: 0.5
normalizer_params:
q_low: 0.05
q_high: 0.95
clip: true
Age is determined in two stages:
pflanzjahr is available: age = feature_reference_year − pflanzjahr (uses feature_reference_year from project if set, otherwise falls back to run_date.year).pflanzjahr is missing: piecewise estimate from stem circumference
(st_umfang) using Stadt Hannover classification tables.| Normalizer param | Type | Default | Description |
|---|---|---|---|
q_low |
float | 0.05 |
Lower quantile for normalization (clipped to 0.0). |
q_high |
float | 0.95 |
Upper quantile for normalization (clipped to 1.0). |
clip |
bool | true |
Clip scores outside [0, 1] to the boundary values. |
Output extras: age_value (years), age_source ("pflanzjahr" | "circumference" | "missing"), age_reference_year (the year used for the age calculation — helpful when running historical panel snapshots).
vitalityMaps the tree vitality stage (0–5) to a risk score.
- name: "vitality"
weight: 0.5
params:
exclude_stages: [4, 5] # dead / degenerating → score 0.0
stage0_score: 0.5 # score for missing / stage-0 trees
| Param | Type | Default | Description |
|---|---|---|---|
exclude_stages |
list of int | [4, 5] |
Stages mapped to score 0.0 (watering exclusion semantics). |
stage0_score |
float | 0.5 |
Score assigned when vitality is 0 or missing. |
Stages 1–3 are linearly scaled: score = stage / 3.0.
planting_structureGroups trees by proximity and scores isolated trees as higher risk.
- name: "planting_structure"
weight: 0.5
params:
radius_m: 25.0
mode: breaks
neighbor_breaks: [0, 4, 9]
neighbor_scores: [0.8, 0.5, 0.3, 0.1]
| Param | Type | Default | Description |
|---|---|---|---|
radius_m |
float | 25.0 |
Neighbourhood radius (metres) for counting nearby trees. |
mode |
string | "breaks" |
"breaks" (class-based) or "linear" (risk decreases continuously with neighbor count). |
neighbor_breaks |
list of int | [0, 4, 9] |
Class thresholds (for mode: breaks). Defines 4 classes: 0, 1–4, 5–9, ≥10. |
neighbor_scores |
list of float | [0.8, 0.5, 0.3, 0.1] |
Risk scores per class (must have one more entry than neighbor_breaks). |
neighbor_min |
int | auto | Lower bound for linear scaling (for mode: linear). |
neighbor_max |
int | auto | Upper bound for linear scaling (for mode: linear). |
air_temperature_dayScores daytime drought risk from the 14 h air temperature raster.
- name: "air_temperature_day"
weight: 0.5
params:
threshold_method: "tree_percentile"
use_zonal_statistics: true
low_percentile: 5
high_percentile: 95
temp_day_low: 20.0 # for static method
temp_day_high: 30.0 # for static method
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "tree_percentile" (recommended), "spatial_percentile", or "static". |
use_zonal_statistics |
bool | When true, uses mean temperature over the crown buffer (requires OBSkH crown diameter); falls back to point sampling. |
low_percentile / high_percentile |
int | Percentile thresholds for adaptive methods. |
temp_day_low / temp_day_high |
float | Fixed temperature thresholds (°C) for static method. |
air_temperature_nightScores nighttime heat stress from the 04 h air temperature raster.
- name: "air_temperature_night"
weight: 0.5
params:
threshold_method: "tree_percentile"
use_zonal_statistics: true
low_percentile: 5
high_percentile: 95
temp_night_low: 15.0 # for static method
temp_night_high: 22.0 # for static method
Parameters are identical in structure to air_temperature_day.
uhi_intensityScores Urban Heat Island intensity as ΔT = T₁₄ₕ − T₀₄ₕ. A small ΔT means poor nocturnal cooling (strong UHI) → higher risk. The score is inverted: score = 1 − normalized(ΔT).
- name: "uhi_intensity"
weight: 0.5
params:
threshold_method: "tree_percentile"
use_zonal_statistics: true
low_percentile: 5
high_percentile: 95
uhi_low: 5.0 # for static method (K)
uhi_high: 10.0 # for static method (K)
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "tree_percentile" or "static". |
uhi_low / uhi_high |
float | Fixed ΔT thresholds in Kelvin for the static method. |
land_useSamples the land-use raster at tree locations and looks up the risk score from
the land_use mapping section.
- name: "land_use"
weight: 0.5
params:
nodata_value: 28 # code to treat as NoData
ndviScores vegetation health from NDVI (Normalized Difference Vegetation Index). High NDVI = healthy vegetation = lower drought risk.
- name: "ndvi"
weight: 0.5
params:
threshold_method: "sample_percentile"
low_percentile: 5
high_percentile: 95
ndvi_low: 0.2 # for static method
ndvi_high: 0.6 # for static method
mode: "multi_temporal"
raster_key_2: "ndvi_2020"
change_weight: 0.0
change_threshold: 0.1
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "sample_percentile" (from tree values), "spatial_percentile" (full raster, high RAM), or "static". |
mode |
string | "single_date" or "multi_temporal" (compares two NDVI rasters). |
raster_key_2 |
string | Config key for the second (older) NDVI raster when mode: multi_temporal. |
change_weight |
float | Weight of NDVI change in the final score (0 = change stored but not used). |
change_threshold |
float | NDVI change normalization range. |
yearly_sources |
list | (optional) Year-range-based raster selection for panel snapshots. When present, the raster used is determined by matching feature_reference_year (or run_date.year) against the defined ranges instead of using a single fixed raster key. See below for the entry structure. |
ndvi.yearly_sources entriesEach entry defines a year range and the raster to use for it:
params:
mode: "single_date"
yearly_sources:
- start_year: 2020
end_year: 2023
raster_key: "ndvi_2020"
source_year: 2020
output_column: "ndvi_value_2020" # written to extras
- start_year: 2024
end_year: 2026
raster_key: "ndvi"
source_year: 2024
output_column: "ndvi_value_2024"
| Sub-key | Type | Description |
|---|---|---|
start_year |
int | First year (inclusive) for which this raster applies. |
end_year |
int | Last year (inclusive) for which this raster applies. |
raster_key |
string | Key in inputs pointing to the raster to sample from. |
source_year |
int | Stored in output as ndvi_source_year for provenance tracking. |
output_column |
string | Year-specific extras column written alongside the canonical ndvi_value_current. |
When yearly_sources is configured, the criterion produces these dynamic output columns
instead of the static ndvi_value:
| Column | Description |
|---|---|
ndvi_value_current |
NDVI value from the matched raster (used for scoring and as the ML feature). |
ndvi_source_year |
Calendar year of the raster used (for provenance). |
ndvi_age_years |
feature_reference_year − ndvi_source_year (measures data freshness). |
ndvi_available |
True/False – whether a valid NDVI value was found for this tree. |
ndvi_value_<year> |
Year-specific column (e.g. ndvi_value_2024) for backwards-compat exports. |
albedoScores surface albedo sampled from the UrbanGreenEye Sentinel-2 raster
(inputs.albedo_raster): low albedo = darker, heat-absorbing surroundings →
higher drought stress risk.
- name: "albedo"
weight: 0.015353
params:
threshold_method: "sample_percentile"
scale_factor: 0.001 # raster stores integer reflectance × 1000
valid_min: 0.0
valid_max: 1.2
low_percentile: 5
high_percentile: 95
albedo_low: 0.10 # for static method
albedo_high: 0.22
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "sample_percentile" (live), "spatial_percentile", or "static". |
scale_factor |
float | Converts the stored integer reflectance to unitless albedo. |
valid_min / valid_max |
float | Valid physical range after scaling. |
albedo_low / albedo_high |
float | Fixed albedo thresholds for the static method. |
dwsiScores the UrbanGreenEye DWSI tree-vitality index sampled from yearly summer
rasters (inputs.dwsi_rasters): negative values = vitality loss → higher
drought risk; 5000 marks cloud-masked pixels.
- name: "dwsi"
weight: 0.050970
params:
threshold_method: "sample_percentile"
low_percentile: 5
high_percentile: 95
dwsi_low: -20.0 # for static method (percentage scale −100…+100)
dwsi_high: 20.0
valid_min: -100.0
valid_max: 100.0
cloud_mask_value: 5000
yearly_sources: # one entry per year: dwsi_2018 … dwsi_2025
- {start_year: 2018, end_year: 2018, raster_key: "dwsi_2018", source_year: 2018}
| Param | Type | Description |
|---|---|---|
dwsi_low / dwsi_high |
float | Static thresholds on the percentage scale (−100…+100). |
cloud_mask_value |
int | Raster value marking cloud-masked pixels. |
yearly_sources |
list | Year → dwsi_<year> raster selection (same structure as ndvi.yearly_sources). |
seasonal_weatherJoins per-tree × per-year weather features (DWD HYRAS + FAO-ET0, issue #213)
from inputs.seasonal_weather. Pure ML context features — it does not
contribute to the rule-based score (weight 0.0).
- name: "seasonal_weather"
weight: 0.0
params:
path: "data/derived/ml/tree_seasonal_weather.csv"
drought_toleranceMaps drought-tolerance ratings from the enriched Citree tree fields to risk scores.
- name: "drought_tolerance"
weight: 0.5
params:
source_column: "citree_trockenheitstoleranz"
mapping:
sehr tolerant: 0.1
trockentolerant: 0.3
mittel: 0.5
empfindlich: 0.8
vertraegt keine trockenheit: 0.8
unbekannt: 0.5
"": 0.5
| Param | Type | Description |
|---|---|---|
source_column |
string | Enriched tree column containing the Citree drought tolerance label. |
mapping |
map | Text rating → risk score. Required – no built-in fallback. |
pit_sizeMaps below-ground root space quality to risk scores using the inventory field
Wurzel Wurzelraum.
- name: "pit_size"
weight: 0.5
params:
mapping:
"gesamter wurzelbereich versiegelt": 0.9
"baumscheibe kleiner als 2 x 2 m": 0.8
…
"gesamter wurzelbereich unversiegelt": 0.1
"nan": 0.5
"": 0.5
| Param | Description |
|---|---|
mapping |
German inventory text → risk score (0–1). Required. |
tree_discMaps above-ground tree disc cover type to risk scores using the inventory field
Wurzel Baumscheibenbedeckung.
- name: "tree_disc"
weight: 0.5
params:
mapping:
"bewachsen": 0.1
"undurchlässig (asphalt, etc.)": 0.9
…
"nan": 0.5
"": 0.5
Structure identical to pit_size.
heat_toleranceMaps heat-tolerance ratings from the enriched Citree tree fields to risk scores.
- name: "heat_tolerance"
weight: 0.5
params:
source_column: "citree_hitzevertraeglichkeit"
mapping:
gut: 0.1
mittel: 0.4
schwach: 0.7
unbekannt: 0.5
"": 0.5
Structure identical to drought_tolerance.
cold_airCombines cold air production rate (KPR) and volume flow (KVS) from the FITNAH climate model into a single microclimate cooling score. High cold air values → better cooling → lower drought risk (inverted score).
- name: "cold_air"
weight: 0.5
params:
threshold_method: "sample_percentile"
low_percentile: 5
high_percentile: 95
kpr_low: 5.0 # for static method
kpr_high: 30.0
kvs_low: 1.0
kvs_high: 10.0
kpr_weight: 0.5 # must sum to 1.0
kvs_weight: 0.5
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "sample_percentile", "spatial_percentile", or "static". |
kpr_low / kpr_high |
float | KPR thresholds (m³/m²/h) for static method. |
kvs_low / kvs_high |
float | KVS thresholds (m³/m/s) for static method. |
kpr_weight / kvs_weight |
float | Weights for the combined score (must sum to 1.0). |
field_capacityScores water availability from NFK (usable field capacity, mm) from the LBEG BK50 soil map, with special handling for new plantings and inner-city trees.
- name: "field_capacity"
weight: 0.5
params:
new_planting_year_threshold: 2015
new_planting_year_score: 0.3
new_planting_nfkwe_value: 170
missing_score: 0.9
breakpoints:
0: 1.0
50: 0.9
90: 0.7
120: 0.5
160: 0.3
250: 0.1
| Param | Type | Description |
|---|---|---|
new_planting_year_threshold |
int | Trees planted after this year are treated as new plantings in engineered substrate. |
new_planting_year_score |
float | Fixed risk score for new plantings. |
new_planting_nfkwe_value |
int | Assumed NFKWE (mm) for new plantings (Hannover Typ 2 substrate). |
missing_score |
float | Risk score when no BK50 polygon covers the tree location (typically inner city). |
breakpoints |
map | NFKWE (mm) → risk score. Values between breakpoints are linearly interpolated. Required. |
gk25Classifies the uppermost layer of the GK25 geologic map
(inputs.gk25_wfs, issue #166) into a material class and maps it to a risk
score. Weight 0.0: contributes the gk25_material_class extra / ML
feature only, not to the weighted total, until the weights are recalibrated.
- name: "gk25"
weight: 0.0
params:
material_scores:
anthropogenic: 0.8
water: 0.1
rock: 0.6
peat: 0.2
clay: 0.4
silt_loam: 0.3
mixed_silt_sand: 0.5
gravel: 0.8
sand: 0.7
| Param | Description |
|---|---|
material_scores |
Material class → risk score (drought stress via water storage / drainage). |
subway_tunnelScores proximity to underground Stadtbahn tunnel segments
(inputs.overture_transportation, issue #139): tunnels limit soil volume
and can lower the local groundwater table. Weight 0.0: contributes the
subway_tunnel_dist_m extra / ML feature only, pending recalibration.
- name: "subway_tunnel"
weight: 0.0
params:
distance_breaks: [15, 30] # metres to nearest tunnel segment
distance_scores: [0.8, 0.5, 0.1] # ≤15 m → 0.8, ≤30 m → 0.5, else 0.1
construction_sitesScores proximity to active road construction sites
(inputs.osm_construction, fetched fresh every run, issue #139).
Weight 0.0: contributes the construction_dist_m extra / ML feature only,
pending recalibration.
- name: "construction_sites"
weight: 0.0
params:
distance_breaks: [25] # metres to nearest construction way
distance_scores: [0.7, 0.1] # ≤25 m → 0.7, else 0.1
groundwaterScores drought risk from depth to groundwater (Flurabstand) using Ordinary
Kriging interpolation (live default) from ~200 LHH monitoring wells; IDW is
available as fallback via interpolation_method. An optional Grimm-Strele
20-year trend adjustment is configured but currently inactive
(trend_enabled: false in the live config).
- name: "groundwater"
weight: 0.5
params:
scoring_source: "median" # "latest", "median", or "max"
interpolation_method: "kriging" # "kriging" (Ordinary Kriging) or "idw"
variogram_model: "exponential" # kriging only: exponential|spherical|gaussian|linear|power
idw_power: 2 # idw only
max_distance_m: 5000
min_neighbors: 3
breakpoints:
0.0: 0.1
1.0: 0.2
2.0: 0.4
4.0: 0.6
6.0: 0.8
10.0: 0.95
trend_enabled: false
trend_adjustment:
"stark steigend": -0.1
"steigend": -0.05
"gleich bleibend": 0.0
"fallend": 0.05
"stark fallend": 0.1
| Param | Type | Description |
|---|---|---|
scoring_source |
string | Which temporal aggregation drives the risk score: "latest", "median", or "max". |
interpolation_method |
string | "kriging" (Ordinary Kriging, live default) or "idw" (fallback). |
variogram_model |
string | Variogram model for kriging (live: "exponential"; also spherical, gaussian, linear, power). |
idw_power |
int | IDW weighting exponent (higher = more local influence); used only when interpolation_method: "idw". |
max_distance_m |
float | Trees beyond this distance from any well receive the neutral score. |
min_neighbors |
int | Minimum number of wells for a valid interpolation. |
breakpoints |
map | Depth (m) → risk score. Linearly interpolated. Required. |
trend_enabled |
bool | Whether to apply the Grimm-Strele trend adjustment. |
trend_adjustment |
map | Trend label → additive score modifier. |
When project.feature_reference_year is set, the groundwater criterion filters well
data to measurements up to and including that year before aggregating. This
ensures that panel snapshots for e.g. 2020 only use well readings ≤ 2020 and do not
look ahead. The output extras include feature_reference_year and
years_used_for_snapshot for provenance.
sky_view_factorScores tree exposure from the Sky View Factor (SVF), which measures the fraction of sky visible from a point (0 = fully enclosed, 1 = open sky).
- name: "sky_view_factor"
weight: 0.5
params:
threshold_method: "tree_percentile"
interpretation: "shading_protection"
low_percentile: 10
high_percentile: 90
svf_low: 0.3 # for static method
svf_high: 0.7
| Param | Type | Description |
|---|---|---|
threshold_method |
string | "tree_percentile", "spatial_percentile", or "static". |
interpretation |
string | "shading_protection" (lower SVF = more shade = less risk) or "exposure" (lower SVF = restricted light = more risk). |
svf_low / svf_high |
float | Fixed SVF thresholds for static method. |
sunlit_hoursScores exposure risk from mean daily sunlit hours over selected seasons.
- name: "sunlit_hours"
weight: 0.5
params:
threshold_method: "tree_percentile"
seasons: ["spring", "summer", "autumn"]
low_percentile: 10
high_percentile: 90
hours_low: 4.0 # for static method
hours_high: 10.0
| Param | Type | Description |
|---|---|---|
seasons |
list | Subset of ["spring", "summer", "autumn", "winter"] to include in the mean. |
hours_low / hours_high |
float | Fixed sunlit-hour thresholds for static method. |
land_use mappingTop-level section (not nested under criteria) that defines how land-use
raster codes are translated to risk scores and labels.
Currently dead config. The criterion reads
config["land_use"]["mapping"](bluegreencity_ki/criteria/land_use.py), but in the live config theland_use:key contains nomapping— the block below sits at the top level, next toland_use:. The loader therefore silently falls back to its built-in default, which is identical to the top-levelmapping:block. Editing that block changes nothing until it is moved underland_use:.
land_use:
mapping:
28: {risk_score: 0.5, label: "Tagebau (NODATA)", description: "NoData value"}
20: {risk_score: 0.9, label: "Gebäude", description: "Buildings"}
9: {risk_score: 0.1, label: "Freiland/Rasen", description: "Open land/grass"}
…
Each entry has:
| Sub-key | Type | Description |
|---|---|---|
risk_score |
float | Risk score (0–1) assigned to trees on this land-use type. |
label |
string | Short human-readable label (used in output and reports). |
description |
string | Longer description (metadata only). |
Risk score conventions used in the Hannover dataset:
| Score | Interpretation |
|---|---|
| 0.1 | Low risk (water, open grassland) |
| 0.3 | Medium-low (tree over grass) |
| 0.5 | Neutral / NoData |
| 0.7 | Medium-high (tree over non-natural grass) |
| 0.9 | High risk (buildings, sealed surfaces, rail) |
Codes not present in the mapping receive neutral_score and a warning is logged.
outputControls where pipeline results are written.
output:
format: "gpkg"
path: "./data/output/trees_at_risk.gpkg"
layer_points: "trees_at_risk_points"
aggregated:
enabled: true
output_dir: "./data/output/aggregated"
include_csv: true
include_geojson: true
include_json: true
output_crs: "EPSG:4326"
postgis:
enabled: true
schema: "bgcki"
table_prefix: "analysis_tree_risk_scores"
mode: "replace"
add_metadata: true
h3:
enabled: true
resolutions: [10, 11]
a5:
enabled: true
resolutions: [15, 16]
admin_areas:
enabled: true
postgis:
enabled: true
schema: "bgcki"
table: "analysis_tree_risk_scores"
mode: "replace" # or "append"
add_metadata: true
species_counts:
enabled: true
# path: "./data/output/trees_species_counts.csv" # optional override
| Key | Type | Default | Description |
|---|---|---|---|
format |
string | "gpkg" |
Output file format ("gpkg" = GeoPackage). |
path |
string | — | Output file path. |
layer_points |
string | — | Layer name inside the GeoPackage. |
aggregated.enabled |
bool | false unless configured |
Enables additional mean-aggregated polygon outputs for grid and administrative geographies. |
aggregated.output_dir |
string | ./data/output/aggregated |
Directory where aggregated GeoPackages and CSVs are written. |
aggregated.include_csv |
bool | true |
Whether to write a CSV version next to each aggregated GeoPackage. |
aggregated.include_geojson |
bool | false unless configured |
Whether to write a GeoJSON version next to each aggregated GeoPackage. |
aggregated.include_json |
bool | false unless configured |
Whether to also write a .json GeoJSON serialization next to each aggregated GeoPackage. |
aggregated.output_crs |
string | "EPSG:4326" |
Target CRS for aggregated polygon outputs. WGS84 is recommended for Superset map charts. |
aggregated.postgis.enabled |
bool | false unless configured |
Whether to additionally write aggregated layers to PostGIS. |
aggregated.postgis.schema |
string | postgis.schema or "bgcki" |
Target PostGIS schema for aggregated layers. |
aggregated.postgis.table_prefix |
string | postgis.table or "analysis_tree_risk_scores" |
Prefix used to derive aggregated PostGIS table names such as analysis_tree_risk_scores_h3_hex_mean_res8. |
aggregated.postgis.mode |
string | postgis.mode or "replace" |
Write mode for aggregated PostGIS tables. |
aggregated.postgis.add_metadata |
bool | postgis.add_metadata or true |
Whether to add run_date and created_at to aggregated PostGIS tables. |
aggregated.h3.enabled |
bool | true |
Enables H3-based mean aggregation over point outputs. |
aggregated.h3.resolutions |
list[int] | — | H3 resolutions to export, e.g. [10, 11]. Each resolution is written to its own file. |
aggregated.h3.filename_prefix |
string | "h3_hex_mean" |
Suffix used in the generated H3 output filenames. |
aggregated.h3.layer_name_prefix |
string | "h3_hex_mean" |
Prefix used for GeoPackage layer names of H3 outputs. |
aggregated.a5.enabled |
bool | false unless configured |
Enables experimental A5 pentagonal grid aggregation using the Python module a5. |
aggregated.a5.resolutions |
list[int] | — | A5 resolutions to export, e.g. [15, 16]. Each resolution is written to its own file. |
aggregated.a5.filename_prefix |
string | "a5_pentagon_mean" |
Suffix used in the generated A5 output filenames. |
aggregated.a5.layer_name_prefix |
string | "a5_pentagon_mean" |
Prefix used for GeoPackage layer names of A5 outputs. |
aggregated.admin_areas.enabled |
bool | true |
Enables mean aggregation against configured administrative polygon layers. |
aggregated.admin_areas.spatial_predicate |
string | "within" |
Spatial join predicate used to assign trees to polygons. |
aggregated.admin_areas.<area>.path |
string | area-specific | Path to the polygon layer for stadtteile, stadtbezirke, or pflegebezirke. |
aggregated.admin_areas.<area>.name_column |
string | auto-detected | Polygon name column used in the aggregate output. |
aggregated.admin_areas.<area>.id_column |
string | auto-detected | Stable polygon identifier used for joining aggregated values back to geometry. |
aggregated.admin_areas.<area>.filename_suffix |
string | area-specific | Suffix appended to the generated GeoPackage and CSV filenames. |
aggregated.admin_areas.<area>.layer_name |
string | area-specific | GeoPackage layer name for the aggregated polygon output. |
postgis.enabled |
bool | true |
Whether to also write to PostGIS. |
postgis.schema |
string | "bgcki" |
Target PostGIS schema. |
postgis.table |
string | — | Target PostGIS table. |
postgis.mode |
string | "replace" |
"replace" truncates the table before writing; "append" adds rows with run_date for historical tracking. |
postgis.add_metadata |
bool | true |
Whether to add run_date and scenario columns. |
species_counts.enabled |
bool | true |
Whether to export a species-count CSV alongside the main output. |
species_counts.path |
string | auto | Override the CSV path; if omitted, written next to the main output file. |
integrationOptional API integrations (currently not active in production).
integration:
greenspace_api:
enabled: false
sustainabilityTracks estimated energy consumption and CO₂ emissions per pipeline run and appends them to a run log CSV.
sustainability:
enabled: true
run_log: "./logs/run_log.csv"
avg_power_watts: 45.0
carbon_intensity_g_per_kwh: 420
include_gpu: false
active_profile: "shared_workstation"
host_profiles:
shared_workstation:
avg_power_watts: 350.0
workstation_laptop:
avg_power_watts: 85.0
| Key | Type | Description |
|---|---|---|
enabled |
bool | Master switch. When false, no tracking is done. |
run_log |
string | Path to the CSV run log. |
avg_power_watts |
float | Default power estimate when no matching host profile is found. |
carbon_intensity_g_per_kwh |
float | Carbon intensity factor for the electricity mix (g CO₂/kWh). |
include_gpu |
bool | Whether to add GPU power draw to the estimate. |
active_profile |
string | Key of the host profile to use (overrides avg_power_watts). |
host_profiles.<name>.avg_power_watts |
float | Machine-specific power estimate (W). |
savingsOptional estimation of water and fuel savings achievable by risk-based watering optimization. Disabled by default.
savings:
enabled: false
water:
liters_per_tree_per_event: 150
baseline_events_per_season: 6
optimized_events_per_season: 4
fuel:
fuel_liters_per_km: 0.12
baseline_km_per_season: 1000
optimized_km_per_season: 800
green_volume:
model: simple_growth
annual_growth_kg_co2_per_tree: 5.0
survival_gain_percent: 10.0
validationRuns an optional validation step that compares pipeline risk scores against drought-damage ground truth labels from the tree inventory.
validation:
enabled: true
mode: "pipeline_monitoring" # pipeline_monitoring | expert_validation | external_2025_2026
gisid_column: "objectid"
min_validation_matches: 50
print_report: true
primary_score_column: "drought_risk_ml"
threshold: 0.5
top_k_fractions: [0.05, 0.10, 0.20]
baseline:
enabled: true
score_column: "drought_risk_rulebased"
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Master switch. Can also be disabled at runtime with --skip-validation. |
mode |
string | "pipeline_monitoring" |
Validation mode: "pipeline_monitoring" (live), "expert_validation", or "external_2025_2026". |
gisid_column |
string | "objectid" |
Tree ID column in the pipeline output (must match the ID column in the drought CSV). |
min_validation_matches |
int | 50 |
Minimum number of non-null labels required to run; below this, validation is skipped with status "skipped". |
print_report |
bool | true |
Print the validation report to the console. |
primary_score_column |
string | "drought_risk_ml" |
Primary score column compared against the labels. |
threshold |
float | 0.5 |
Threshold for the binary classification against the labels. |
top_k_fractions |
list | [0.05, 0.10, 0.20] |
Top-k fractions for the ranking analysis (top 5 %, 10 %, 20 %). |
baseline.enabled / baseline.score_column |
bool / string | true / "drought_risk_rulebased" |
Optional baseline comparison (live: the rule-based score as technical baseline). |
plausibilityAutomated sanity checks run after criteria computation. Covers numeric spot checks, monotonicity, spatial distribution, ML-based anomaly detection, and optional external list comparison.
plausibility:
enabled: true
spot_check_samples: 5
spot_check_tolerance: 1.0e-6
spot_check_criteria:
- salt
- street_type
- age
…
visualizations:
enabled: true
monotonicity:
tolerance: 0.02
direction_overrides: {}
spatial:
group_column: "stadtteil"
warn_std_threshold: 0.20
stadtteile_path: "…/SKH20_Stadtteile.shp"
stadtbezirke_path: "…/SKH20_Stadtbezirke.shp"
stadtgrenze_path: "…/SKH20_Stadtgrenze.shp"
ml:
max_rows: 5000
correlation_threshold: 0.85
correlation_enabled: true
pca_enabled: true
cluster_enabled: true
explainability_enabled: true
species_cluster_count: 6
species_min_samples: 5
ampelliste:
path:
species_column_index: 0
rating_column_index: 1
high_discrepancy_threshold: 0.40
| Key | Type | Description |
|---|---|---|
enabled |
bool | Master switch for all plausibility checks. |
spot_check_samples |
int | Number of trees to re-compute for numeric spot checks. |
spot_check_tolerance |
float | Acceptable floating-point deviation in spot checks. |
spot_check_criteria |
list | Criteria included in the spot check (lightweight criteria only recommended). |
visualizations.enabled |
bool | Whether to write PNG/CSV artefacts for plausibility results. |
monotonicity.tolerance |
float | Allowable monotonicity violation (fraction of tree pairs). |
monotonicity.direction_overrides |
map | Override the expected monotonicity direction per criterion. |
spatial.group_column |
string | Column used to group trees for spatial distribution checks. |
spatial.warn_std_threshold |
float | Standard deviation threshold above which a spatial warning is raised. |
spatial.stadtteile_path |
string | Path to district boundary shapefile (for visualizations). |
ml.correlation_threshold |
float | Pearson correlation above which two criteria are flagged as redundant. |
ml.pca_enabled / ml.cluster_enabled |
bool | Toggle PCA and clustering analysis (disabled by default for performance). |
ml.explainability_enabled |
bool | Toggle SHAP-based explainability. |
ampelliste.path |
string | Optional external species rating list for comparison. |
ampelliste.high_discrepancy_threshold |
float | Score difference above which a discrepancy is reported. |
See validation/plausibility.md for a detailed description of each check type.
reportControls which artefact files are written alongside the main output.
report:
save_hist_png: true # Score distribution histograms per criterion
save_validation_png: true # Validation result plots
data_managementMetadata and versioning configuration for PostGIS data imports. The pipeline itself reads rasters from files; this section controls how import scripts register datasets in the data catalog.
data_management:
raster_imports:
ndvi_2024:
input_key: "ndvi_raster"
schema: "bgcki"
table: "context_raster_ndvi"
description: "NDVI from CIR aerial imagery 2024"
version: "2024"
source: "Aerial CIR imagery, Stadt Hannover"
update_frequency: "irregular"
notes: "…"
vector_imports:
tree_inventory:
input_key: "trees"
schema: "bgcki"
table_template: "trees_inventory_{version}"
description: "City street tree inventory"
version: "2025_10"
source: "Landeshauptstadt Hannover"
update_frequency: "irregular"
archive_old_versions: true
versioning:
strategy: "archive" # "archive" or "replace"
table_naming: "{table}_{version}"
metadata_table:
enabled: true
schema: "public"
table: "data_catalog"
archive:
auto_archive_on_new_version: true
archive_schema: "archive"
move_to_archive_schema: false
Each raster_imports or vector_imports entry uses these common keys:
| Key | Type | Description |
|---|---|---|
input_key |
string | Key in inputs that resolves the source path. |
schema |
string | Target PostGIS schema. |
table / table_template |
string | Table name. Template supports {version} placeholder. |
description |
string | Human-readable dataset description. |
version |
string | Version tag (e.g. "2024", "2025_10"). |
source |
string | Data provider. |
url |
string | Source URL (optional). |
update_frequency |
string | Qualitative update cadence ("annual", "irregular", …). |
notes |
string | Additional metadata, resolution, caveats. |
climateConfiguration for importing climate data from DWD CDC and the Open-Meteo API.
climate:
sources:
dwd_primary: true
openmeteo_fallback: true
dwd:
cache_dir: "data/cache/climate"
preferred_dataset: "hostrada" # "hyras" (daily 5km) or "hostrada" (hourly 1km)
hyras_variables:
- precipitation
- temperature_mean
- temperature_max
- temperature_min
- humidity
hostrada_variables:
- temperature
- humidity
- precipitation
- radiation
- wind_speed
years: [2020, 2021, 2022, 2023, 2024]
update_frequency: "annual"
openmeteo:
variables:
- temperature_2m
- precipitation
- relative_humidity_2m
- et0_fao_evapotranspiration
resolution: "hourly"
start_date: "2022-01-01"
end_date: "2024-12-31"
update_frequency: "weekly"
water_balance:
enabled: false
mode: "local_observation"
model_version: "swbm-v0.4"
method: "fao56"
et0_source: "openmeteo"
forecast_days: 7
soil_data_source: "nfk_polygons"
execution:
tree_features_path: "data/derived/water_balance/tree_features.gpkg"
dwd_cache_dir: "data/cache/water_balance"
write_batch_size: 5000
write_postgis: true
schedule_time: "04:00"
parquet:
daily_state_path: "data/derived/water_balance/daily_state.parquet"
irrigation_event_path: "data/derived/water_balance/irrigation_events.parquet"
postgis:
schema: "bgcki"
daily_state_table: "swbm_daily_state"
irrigation_event_table: "irrigation_event"
irrigation_event_import:
enabled: false
source: "local_csv"
path: null
timezone: "Europe/Berlin"
write_postgis: true
read_csv: {delimiter: ",", encoding: "utf-8"}
columns:
tree_id: null
irrigated_at: null
source_event_id: null
observed_amount_liters: null
irrigation:
enabled: false
trigger_soil_water_fraction_layer_1: 0.4
target_field_capacity_fraction: 0.8
typical_amount_liters: 150.0
unknown_amount_strategy: "modeled_deficit"
species_specific: true
database:
schema: "bgcki"
dwd_table_prefix: "climate_dwd"
openmeteo_table_prefix: "climate_openmeteo"
create_time_index: true
create_spatial_index: true
quality:
temperature_range: [-30, 45]
precipitation_range: [0, 200]
humidity_range: [0, 100]
wind_speed_range: [0, 150]
allow_missing: true
max_missing_percent: 10
water_balance sub-keys not shown in the sample above| Key | Live value (abridged) | Description |
|---|---|---|
meteorology |
source_quality: {dwd: 1.0, parquet: 1.0, openmeteo: 0.7}, default_quality: 0.5, elevation_m: 55.0; openmeteo_fallback.enabled: true; dwd_preprocessing (HYRAS 6-1, ET0 1.1) |
Meteorology source-quality weights plus DWD/Open-Meteo fetch settings. |
forcing |
sunlit-hours fallbacks 8/14/10/4 h (spring/summer/autumn/winter), stem diameter 30.0 cm, unknown-SVF scenarios 0.3/0.6/0.9 |
Fallback values (each with a quality weight) used when forcing data is missing. |
vegetation |
defaults: LAI allometry [1.50, 0.20], branch area index 0.35, transpiration fraction [0.80, 0.10], phenology [91, 145, 182, 305]; plus per-genus overrides |
Vegetation parameters (defaults + genus-specific). |
soil |
field capacity 0.26, wilting point 0.07, drainage 0.35, interception 1.0 mm/LAI, runoff 0.1/0.15, infiltration 0.55, pit buffer 0.30 m |
Soil water-balance parameters. |
capillary_rise |
enabled: true; source kriged_groundwater (kriging, exponential variogram, max_distance_m: 5000, min_neighbors: 3); lookup 5.0 → 0.1 mm/day |
Groundwater-driven capillary rise into the deepest root-zone layer. |
profile_imputation |
unknown age → young 5 / mature 20 / old 40 years; crown: per-species means (enabled: true, 1–35 m) |
Imputation for missing tree-age and crown inputs. |
hda_validation |
gate budgets: median demand diff ≤ 1.0 %, p95 ≤ 10.0 %; reports to docs/validation |
Output-vs-output validation against the h_da standalone historical exports. |
See data_maintenance/Climate_Data_Import.md for the climate import workflow and data_maintenance/Soil_Water_Balance_Integration.md for the current SWBM integration boundary.
risk_productsComposite products derived from criterion scores, used by the Lagebild and the irrigation planner.
risk_products:
site_vulnerability:
enabled: true
groups:
water_soil:
weight: 0.2
components:
groundwater_risk: 0.45
field_capacity_risk: 0.45
gk25_risk: 0.1
root_site:
weight: 0.2
components:
pit_size_risk: 0.3
tree_disc_risk: 0.15
land_use_risk: 0.3
planting_structure_risk: 0.25
exposure:
weight: 0.2
components:
sky_view_factor_risk: 0.2
sunlit_hours_risk: 0.15
air_temperature_day_risk: 0.15
air_temperature_night_risk: 0.1
uhi_intensity_risk: 0.15
cold_air_risk: 0.15
albedo_risk: 0.1
species_resilience:
weight: 0.2
components:
drought_tolerance_risk: 0.5
heat_tolerance_risk: 0.5
chronic_load:
weight: 0.2
components:
salt_risk: 0.25
street_type_risk: 0.35
subway_tunnel_risk: 0.4
action_priority:
weights:
swbm: 0.5
ml_damage: 0.3
site_vulnerability: 0.2
| Key | Description |
|---|---|
site_vulnerability |
Five equally weighted groups (water_soil, root_site, exposure, species_resilience, chronic_load) combined into the site_vulnerability_rulebased score. gk25_risk is deliberately down-weighted to 0.1 (2026-08-30, Plan C.3). |
action_priority.weights |
Staged-cascade weights for the irrigation action priority: stage 1 is a hard gate (irrigation_demand_liters > 0); stages 2–4 rank the trees that need water by swbm: 0.5, ml_damage: 0.3, site_vulnerability: 0.2. |
irrigation_planningConfiguration for the deterministic draft irrigation planner. It is
enabled in the live config: the CLI refuses to run while it is false,
and the daily cycle only calls the planner when it is true (live since the
Phase 3 smoke run on 2026-09-24, issue #227). inputs.risk_path and
inputs.daily_state_path are PostGIS schema.table pointers
(bgcki.operational_tree_product / bgcki.swbm_daily_state); file paths
(parquet/gpkg/csv) keep working for local runs.
irrigation_planning:
enabled: true
swbm_model_version: null
inputs:
daily_state_path: "bgcki.swbm_daily_state"
risk_path: "bgcki.operational_tree_product"
irrigation_event_path: "data/derived/water_balance/irrigation_events.parquet"
weather_forecast_path: null
outputs:
parquet_path: "data/derived/irrigation_planning/plan_decisions.parquet"
write_postgis: true
postgis: {schema: "bgcki", table: "irrigation_plan_decision"}
columns:
state_tree_id: "tree_id"
risk_tree_id: "objectid"
event_tree_id: "tree_id"
state_date: "state_date"
model_version: "model_version"
demand_liters: "irrigation_demand_liters"
stress_today: "soil_water_stress_today"
forecast_stress: "soil_water_stress_forecast_max_7d"
trigger_date: "irrigation_trigger_date"
avoidable_by_rain: "irrigation_avoidable_by_rain"
data_quality: "data_quality"
rulebased_risk: "drought_risk_rulebased"
ml_risk: "drought_risk_ml"
site_vulnerability: "site_vulnerability_rulebased"
risk_date: "run_date"
irrigated_at: "irrigated_at"
strategic_value: null
heat_vulnerability: null
service_minutes: null
candidate_preparation:
rulebased_risk_weight: 0.0
ml_risk_weight: 1.0
default_strategic_value: 0.5
default_heat_vulnerability: 0.5
default_service_minutes: 8
state_scenario_strategy: "conservative_max"
minimum_risk_join_fraction: 0.95
policy:
version: "irrigation-policy-v0.1"
horizon_days: 7
risk_threshold: 0.7
urgent_stress_threshold: 0.8
min_irrigation_interval_days: 3
solver_time_limit_seconds: 30.0
priority_levels: {critical: 0.8, high: 0.6, medium: 0.4}
priority_weights:
risk: 0.3
current_stress: 0.0
forecast_stress: 0.5
strategic_value: 0.0
site_vulnerability: 0.2
data_quality: 0.0
heatwave:
threshold_c: 30.0
minimum_consecutive_days: 3
priority_boost: 0.1
resources:
daily_water_liters: 108000.0
team_count: 9
team_hours_per_day: 8.0
shift: "night"
shift_hours: {night: 8.0, day: 7.0}
vehicle_count: 9
max_jobs_per_vehicle: 60
The columns: sub-map names the source column in each input (daily state,
risk product, irrigation events) for every planner field; null entries are
optional fields with defaults.
All policy values are versioned initial assumptions. Change policy.version
whenever behavior changes. See
data_maintenance/Irrigation_Planning.md
for inputs, execution, output semantics, and current boundaries.
irrigation_reviewLocal-only Phase 3 Taipy review and execution settings:
irrigation_review:
enabled: false
draft_limit: 1000
postgis_schema: "bgcki"
gui:
host: "127.0.0.1"
port: 5000
inputs:
state_path: "data/derived/water_balance/dwd_fullcity_v4_2025-07-15.parquet"
risk_path: "data/output/<YYYY_MM_DD>_ml_operational_train_<START>_<END>_predict_<YEAR>/hannover_gesamt/trees_at_risk_hannover_gesamt.gpkg"
weather_path: null
inputs.risk_path is maintained automatically along two paths, both
going through the single writer in
bluegreencity_ki.core.risk_pointer (issue #231, Options B + C):
scripts/run_ml_refresh_overnight.ps1 invokes
scripts/update_operational_risk_pointer.py and points the pointer at the
newest data/output/<YYYY_MM_DD>_ml_operational_train_* GPKG.Daily at 04:00: scripts/run_scheduled_water_balance.ps1 runs the same
script in --check mode before the Lagebild refresh resolves the config
paths. A stale pointer (e.g. after a manual operational run via
run_ml_path.py operational-current, such as a tree-cadastre switch) is
logged and self-healed, so the Lagebild of the same day already reflects
the new run — no manual pointer update needed.
PostGIS table pointers are out of scope. A pointer value of the form
schema.table (e.g. irrigation_planning.inputs.risk_path →
bgcki.operational_tree_product, issue #227 Phase 2) is a canonical
PostGIS source, not a GPKG file pointer. Its freshness is managed by the
daily operational product refresh, and the check skips it. Without this
rule the check would treat the table name as a stale GPKG pointer and
self-heal it back to the newest training-run GPKG — silently overriding
the Phase 2 design (incident 26.09.2026).
Check exit codes: 0 = up to date, healed, or skipped (risk_path: null
or a PostGIS schema.table pointer);
1 = pointer unusable (missing file and no candidate run) — the daily
refresh aborts, leaving the Lagebild at its previous state. The self-heal
writes to config/config.yaml but never commits; pick the change up with
the next regular commit (the log line says so).
The UI remains disabled and bound to localhost by default. See data_maintenance/Irrigation_Review_Taipy.md for installation, migration, and audit semantics.
mlConfiguration for AutoGluon-based machine learning drought risk prediction.
For the stable reference benchmark and the documented ablation runs, see risk_assessment_ml/reports/stable_ablation_report_2026-04-03.md.
ml:
enabled: true
dataset_mode: "operational_panel" # "operational_panel" | "single_snapshot"
training_data: "data/derived/ml/training_dataset_v6_operational_panel_2018_2024.csv"
drought_csv: "data/input/trees/Baum-2010-2025-v6_historisch_prod.csv"
labeling:
strategy: "operational_panel_drought_damage" # or "future_drought_damage" | "historical_drought_damage"
snapshot_year: 2025
label_horizon_years: 1
target_label: "future_drought_damage_severe_t1"
operational_panel:
start_year: 2018
end_year: 2024
feature_query_mode: "snapshot_year"
feature_year_column: "snapshot_year"
training:
model_path: "data/models/autogluon"
features_table: "bgcki.analysis_tree_risk_scores_ml_operational_panel"
presets: "best_quality"
time_limit: 3600
eval_metric: "roc_auc"
num_bag_folds: 5
num_bag_sets: 1
num_stack_levels: 1
feature_columns:
- "ga_lang"
- "age_value"
- "vitality_value"
…
excluded_columns:
- "objectid"
- "snapshot_year"
- "label_year"
…
labels:
- "future_drought_damage_severe_t1"
inference:
label: "future_drought_damage_severe_t1"
feature_aliases: {} # optional: map missing features to known synonyms
thresholds:
critical: 0.85
high: 0.30
medium: 0.10
low: 0.00
output:
leaderboards: "data/models/autogluon/leaderboards"
feature_importances: "data/models/autogluon/feature_importances"
shap_explanations: "data/models/autogluon/shap"
predictions: "data/output/{run_date_underscore}_{scenario}/ml_predictions.csv"
postgis: {enabled: true, schema: "bgcki", table_prefix: "{scenario}", mode: "replace"}
| Key | Type | Description |
|---|---|---|
enabled |
bool | Whether to run ML scoring in the pipeline. Requires a trained model. |
dataset_mode |
string | "operational_panel" – multi-year panel approach (one row per tree×year, recommended); "single_snapshot" – legacy single snapshot. |
training_data |
string | Path to the training dataset CSV. |
drought_csv |
string | Raw inventory CSV used to extract drought labels. |
labeling.strategy |
string | Label extraction strategy: "operational_panel_drought_damage" (panel, one snapshot per year), "future_drought_damage" (predicts next-year damage from reference year), "historical_drought_damage" (legacy window approach). |
labeling.snapshot_year |
int | Reference year for "future_drought_damage" strategy: labels are extracted strictly after this year. |
labeling.label_horizon_years |
int | How many years ahead the label looks. Default 1 means next-year damage. |
labeling.target_label |
string | Column name of the target label (e.g. "future_drought_damage_severe_t1"). Must match a column produced by the label extractor. |
operational_panel.start_year |
int | First year (inclusive) of the multi-year panel. |
operational_panel.end_year |
int | Last year (inclusive) of the multi-year panel. Also drives the training window: train_end = end_year - 1, prediction_year = end_year. |
operational_panel.feature_query_mode |
string | How features are fetched per snapshot: "snapshot_year" (reads feature_year_column from the features table). |
operational_panel.feature_year_column |
string | Column in the features table that holds the snapshot year for the panel query. |
operational_panel.snapshot_years |
list | (optional) Explicit list of snapshot years. When set, it overrides start_year/end_year for the panel build only. The training window still derives from end_year, so you can extend the feature panel (e.g. add 2025 for the Baumkataster) without changing what the model trains on. |
annual_validation.output_dir |
string | Base directory for the annual validation model ("data/models/autogluon/annual_validation"); each (re-)training creates a timestamped <YYYY_MM>/train_…__predict_…__validate_… run directory. Also export_gpkg: true and drift_watch.metric_delta: 0.05 (warns when ROC-AUC drops / Brier increases vs. the previous month’s run). |
operational.output_dir |
string | Base directory for the operational (refit-final) model ("data/models/autogluon/operational"). |
shap |
map | SHAP explanation settings (live: scope: "high-risk", high_risk_max_rows: 25, background_sample_size: 50, batch_size: 50, waterfall_limit: 10); the risk-classes mode stratifies risk_classes_max_rows_per_class: 50 rows per class. |
grouped_cv |
map | Grouped cross-validation (live: n_splits: 5, group_columns: ["objectid", "h3_res8"], threshold: 0.5, random_state: 42) plus spatial diagnostics (Zone-AUC + Moran’s I around the city center, zone_radius_m: 5000, n_permutations: 999). |
rolling_validation |
map | Rolling-origin validation (live: min_train_years: 4, group_column: "objectid", threshold: 0.5, per-species via species_column: "ga_lang"). |
training.presets |
string | AutoGluon quality preset: "best_quality", "high_quality", "medium_quality", "good_quality". |
training.time_limit |
int | AutoGluon time budget per label (seconds). |
training.eval_metric |
string | Evaluation metric (e.g. "roc_auc"). |
training.hyperparameters |
map | Per-family AutoGluon hyperparameter sets (GBM/CAT/XGB/RF/XT/NN_TORCH). Tree ensembles run with num_gpus: 0; NN_TORCH with num_gpus: 1 and min_time_limit: 300 so it can compete within the budget. |
training.num_gpus |
int | AutoGluon GPU count (live: 1). |
training.features_table |
string | schema.table in PostGIS from which features are read for each panel snapshot during training dataset construction. This table must be materialized first, e.g. via scripts/ml/build_operational_panel_snapshots.py. |
training.feature_columns |
list | Allowlist of features used in training. The operative list has 54 features (the correlation-cleaned “Modell Hannover” base plus the 2026-10 spatial / SWBM / A-B-bundle additions); it is defined in config/config.yaml; see risk_assessment_ml/automl_features.md for the feature documentation of the current (October 2026) model, and the archive for superseded snapshots (e.g. the 17-feature April 2026 generation). |
training.excluded_columns |
list | Technical exclusion list (IDs, label columns, panel metadata that must not leak into features). |
training.spatial_features |
map | Spatial features (Bundle 2026-10, enabled: true): H3 cell categories (cell_resolutions: [7, 8]), raw UTM coordinates, and a res-8 ring aggregation (self-excluded mean) over m_svf_value / m_air_temp_14h_value. The spatial_block 2×3 grid is a grouping key only, not a model feature. |
training.swbm_features |
map | SWBM soil-water features (enabled: true), strictly from the snapshot year: table bgcki.swbm_daily_state, scenario irrigation-lower, dry season 05-01…10-31, reference dates jul15/aug15, stress_days_threshold: 0.5. |
training.spatial_cv |
map | Spatial groups for model selection (group_column: "spatial_block", max_groups: 10); live enabled: false (2026-10-07 ablation: spatial groups in training hurt external holdout AUC). |
training.class_weight_positive |
float | Sample-weight multiplier for positive rows (live: 1.0 = off). |
labels |
list | Target label column(s) to train. |
inference.label |
string | Which trained model is used for pipeline scoring. |
inference.feature_aliases |
map | (optional) Maps missing feature names to known synonyms. Useful when a feature was renamed between training and inference (e.g. {"ndvi_value_2024": "ndvi_value_current"}). |
local_explainability |
map | Per-tree local explanations (live: enabled: true, scope: "all", top_k: 3, batch_size: 500); the extreme_groups / none scopes exist for faster debug runs. |
calibration |
map | Platt calibration of the raw AutoGluon probabilities (live: enabled: true, method: platt); fitted on the external holdout per validation run and applied at inference → drought_risk_ml_calibrated. |
thresholds |
map | Probability → risk class mapping. First matching threshold wins (descending). Current operative values: critical: 0.85, high: 0.30, medium: 0.10, low: 0.00 — they apply to the calibrated score drought_risk_ml_calibrated (Issue #253, 2026-10-08). The ML run stamps the class with its active thresholds, but the daily operational refresh re-derives the class from the score + this config, so a change takes effect at the next refresh. |
swbm_irrigation_map |
map | Color bands for the “Bewässerungsbetrieb” Grafana map: SWBM demand bands ≤150 / ≤400 / ≤1000 l (blue shades) and planned-amount bands ≤20 / ≤50 / ≤150 l, plus high_risk_no_demand_color: "#d7263d" for high-risk trees without demand. |
output.leaderboards / output.feature_importances |
string | Artefact directories (live: under data/models/autogluon/). |
output.shap_explanations |
string | SHAP artefact directory (live: "data/models/autogluon/shap"). |
output.predictions |
string | Prediction CSV pattern (live: "data/output/{run_date_underscore}_{scenario}/ml_predictions.csv"). |
output.postgis |
map | PostGIS export of the ML results (live: enabled: true, schema bgcki, table_prefix: "{scenario}", mode: "replace"). |
config/config.yaml is the single source of truth. The per-run configs in
data/derived/ml/run_configs/ are generated artifacts, not hand-maintained
files. They are produced by scripts/run_ml_path.py from the base config via
bluegreencity_ki/ml/run_profiles.py and are git-ignored (data/derived/).
| Generated file | Profile | Scenario | Writes to |
|---|---|---|---|
validation.yaml |
validation |
ml_validation_train_<a>_<b>_predict_<p>_validate_<v> |
analysis_tree_risk_scores_validation_<p> |
operational_current.yaml |
operational-current |
ml_operational_train_<a>_<b>_predict_<p> |
analysis_tree_risk_scores_operational_current |
operational_current_provisional.yaml |
operational-current --provisional |
…_provisional_existing_model |
analysis_tree_risk_scores_operational_current_provisional |
Regenerate (and optionally run) a profile:
# Write the run config only (no pipeline run)
conda run -n bgcki_wesemann python scripts/run_ml_path.py validation --write-config-only
conda run -n bgcki_wesemann python scripts/run_ml_path.py operational-current --write-config-only
# Write + run (add --shap risk-classes, --train, --refit-final, --grouped-cv as needed)
conda run -n bgcki_wesemann python scripts/run_ml_path.py operational-current --shap risk-classes
Do not edit the generated files directly – change config/config.yaml (or
pass CLI flags) and regenerate. See how_to_run.md for the full
run workflow.
The same ml configuration block is also used by the grouped cross-validation runner scripts/ml/evaluate_grouped_cv.py. That workflow writes panel-aware validation artefacts to the validation run directory (<run_dir>/grouped_cv, derived from the annual-validation run layout) and is documented in risk_assessment_ml/reports/operational_panel_grouped_cv_report_2026-09-16.md.
See risk_assessment_ml/ml_risk_scoring_guide.md for methodology details and risk_assessment_ml/automl_features.md for the current operative feature set.
project.study_areas.definitions.bbox_source: "manual" and provide a bbox in WGS84, or use
bbox_source: "auto" to derive the bounding box from the tree data.trees to the correct data source.project.study_areas.active to the new key.project:
study_areas:
active: "my_district"
definitions:
my_district:
name: "My District"
bbox_source: "manual"
bbox: [9.72, 52.36, 9.80, 52.41]
trees:
type: "postgis"
schema: "bgcki"
table: "trees_inventory"
id_field: "objectid"
Change the weight value next to the criterion name. Weights are normalized
automatically so only relative values matter.
criteria:
- name: "vitality"
weight: 2.0 # twice as influential as a weight-1 criterion
- name: "age"
weight: 0.5
Most raster-based criteria support three threshold methods. Use
tree_percentile (the default) for production runs:
params:
threshold_method: "tree_percentile" # recommended
low_percentile: 5
high_percentile: 95
Switch to static for reproducible, version-independent scoring:
params:
threshold_method: "static"
temp_day_low: 20.0
temp_day_high: 30.0
Remove the criterion’s entry from the criteria list, or set its weight to
0. Removing it is cleaner and avoids unnecessary computation.