The land_use criterion evaluates drought risk based on land use classification at tree locations. Different land use types have different impacts on tree water availability and stress levels, ranging from high-risk sealed surfaces to low-risk vegetation areas.
Where the LHH planting-site dataset (the “Baumumfeld” polygons from the Grünflächenkataster) covers a tree, its surface type overrides the raster-based value and — if mapped via planting_site_surface_type_scores — the risk score (issue #194). Trees outside its currently partial coverage keep the raster method; land_use_score_source records the score method per tree.
bluegreencity_ki/criteria/land_use.pytests/test_land_use.pyrasterio index + array access for local GeoTIFF files (note: rasterio.sample() is deliberately not used — it crashes on Windows)within) onto the planting-site polygons (inputs.planting_sites in config). Covered trees receive the polygon surface type as planting_site_surface_type; when that type appears in planting_site_surface_type_scores (case-insensitive), the mapped score replaces the raster score. Unmapped surface types keep the raster score and are logged as a warning. The dataset is read fresh on every run and never cached because it currently covers only part of the city and will be replaced by a full-coverage export.Land use code 7 → Risk Score = 0.9 (high risk)
Land use code 25 → Risk Score = 0.3 (low risk)
Unknown code → Risk Score = 0.5 (neutral)
NoData value → Risk Score = 0.5 (neutral)
The criterion uses a configurable mapping table that defines how land use codes map to risk scores and labels:
land_use:
mapping:
# Hohes Risiko (high risk - sealed surfaces, buildings)
7: {risk_score: 0.9, label: "Gleis", description: "Rail tracks"}
20: {risk_score: 0.9, label: "Gebäude", description: "Buildings"}
# Geringes Risiko (low risk - vegetation, water)
9: {risk_score: 0.1, label: "Freiland/Rasen", description: "Open land/grass"}
14: {risk_score: 0.1, label: "Wasser", description: "Water"}
# ... other codes ...
Risk Score Interpretation:
criteria:
- name: "land_use"
weight: 0.5 # example; current calibrated weight in config/config.yaml
params:
nodata_value: 28 # NoData code for the raster dataset
# Planting-site surface type → risk score (case-insensitive matching)
planting_site_surface_type_scores:
"Baumbestandsflächen": 0.1
"Wiese": 0.2
"Gebrauchsrasen": 0.3
"Wassergebundene Flächen": 0.5
"Pflasterflächen": 0.8
"Asphaltflächen": 0.9
# ... see config/config.yaml for the full 47-category mapping
The planting-site input itself is configured under inputs. Its source file
retains the LHH delivery name Baumumfeld_zusammengefasst.gpkg:
inputs:
planting_sites:
type: "file"
path: "Baumumfeld/Baumumfeld_zusammengefasst.gpkg" # relative to raster_base_dir
layer: "Baumumfeld_zusammengefasst"
surface_type_field: "kategorie" # read inside inputs.planting_sites
| Parameter | Type | Default | Description |
|---|---|---|---|
nodata_value |
int | 28 | The NoData value used in the raster (maps to neutral score) |
planting_site_surface_type_scores |
dict | — | Planting-site surface type → risk score; unmapped types keep the raster score (warning logged) |
The criterion produces the following output fields:
land_use_risk - Normalized risk score (0-1)
land_use_value - Raw land use code from raster
land_use_label - Descriptive label
planting_site_surface_type - Planting-site surface type (issue #194)
kategorie (e.g. “Gebrauchsrasen”, “Pflasterflächen”); NaN outside coverageplanting_site_area_m2 - Area of the matched planting-site polygon in m²
land_use_score_source - Scoring method per tree: "raster" or "planting_site"The default mapping is based on the Stadt Hannover Klimaanalyse 2022 dataset with 10m resolution:
| Code | Label | Description | Risk Score | Risk Level |
|---|---|---|---|---|
| 7 | Gleis | Rail tracks | 0.9 | High |
| 20 | Gebäude | Buildings | 0.9 | High |
| 22 | Ebenerdig versiegelt | Impervious surface (level) | 0.9 | High |
| 24 | Baum über Versiegelung | Tree over sealed surface | 0.9 | High |
| 26 | Baum über Rasen naturferner Boden | Tree over grass on non-natural soil | 0.7 | Medium-High |
| 23 | Rasen naturferner Boden | Grass on non-natural soil (e.g., sports field) | 0.5 | Medium |
| 25 | Baum über Rasen | Tree over grass | 0.3 | Low |
| 9 | Freiland/Rasen | Open land/grass | 0.1 | Low |
| 14 | Wasser | Water | 0.1 | Low |
| 28 | Tagebau (NODATA) | NoData value | 0.5 | Neutral |
The land use criterion complements other risk factors:
Run tests with:
conda activate bgcki_wesemann
python -m pytest tests/test_land_use.py -v
Tests cover:
After running the pipeline, validate results by:
import geopandas as gpd
import matplotlib.pyplot as plt
# Load results
gdf = gpd.read_file("./data/output/<study_area>/trees_at_risk_<area>.gpkg", layer="trees_at_risk_points")
# Plot land use codes vs risk score
plt.figure(figsize=(10, 6))
for code in sorted(gdf['land_use_value'].unique()):
subset = gdf[gdf['land_use_value'] == code]
plt.scatter([code]*len(subset), subset['land_use_risk'], alpha=0.5, label=str(code))
plt.xlabel('Land Use Code')
plt.ylabel('Risk Score')
plt.title('Land Use Codes vs. Risk Scores')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.grid(True, alpha=0.3)
plt.show()
# Land use distribution
plt.figure(figsize=(10, 6))
gdf['land_use_value'].value_counts().sort_index().plot(kind='bar')
plt.xlabel('Land Use Code')
plt.ylabel('Count')
plt.title('Distribution of Land Use Types at Tree Locations')
plt.grid(True, alpha=0.3)
plt.show()
# Summary statistics
print("Risk Score Statistics:")
print(gdf['land_use_risk'].describe())
planting_sites; the external source-file name remains unchanged.planting_site_surface_type_scores maps the source field kategorie to scores.planting_site_surface_type, planting_site_area_m2, and land_use_score_source.Author: BlueGreenCity-KI Team Last Updated: 2026-07-22