| Table | From | Content |
|---|---|---|
room_to_green.csv | Cadastre footprints (01), Urban Atlas 2018 (01) | per assessed building: share of the land within 50, 100 and 200 m of its footprint that lies in an Urban Atlas class that could be greened |
buildings.csv | analysis building_risk.parquet, vulnerability_by_building.csv | per building, map (land cover × climate × return period) and risk: residents, at risk or not; its residents’ vulnerability fifth |
Land that could be greened (Urban Atlas 2018 codes): discontinuous low and very low density urban fabric (11230, 11240), construction sites (13300), land without current use (13400), green urban areas (14100), sports and leisure facilities (14200), agricultural land (21000, 22000, 23000), forests and semi-natural areas (31000, 32000) and wetlands (40000). Dense urban fabric, industrial and commercial units, roads, railways, extraction sites and water are not. REACHOUT does not publish the cells its all-green scenario changes, so this is a proxy for where greening can act.
Vulnerability is the analysis’s rates version (census-section shares of children, elderly people living alone, unemployed, residents without higher education), ranked over all residential buildings; residents are split into five groups of equal population (Q1 least, Q5 most vulnerable). Whether a building is at risk does not depend on the indicator version.
import json
from pathlib import Path
import geopandas as gpd
import pandas as pd
RAW = Path("../data/raw")
sources = json.loads((RAW / "sources.json").read_text())
RESULTS_A = Path(sources["analysis"]["path"]) / "results"
CLEAN = Path("../data/clean")
CLEAN.mkdir(parents=True, exist_ok=True)
CRS = "EPSG:25830"
GREENABLE = ["11230", "11240", "13300", "13400", "14100", "14200",
"21000", "22000", "23000", "31000", "32000", "40000"]
RADII = [50, 100, 200]1. Assessed buildings and their footprints¶
group = pd.read_csv(RESULTS_A / "vulnerability_by_building.csv").set_index("ID")["vulnerability_group"]
footprints = gpd.read_file(RAW / "cadastre_buildings/A.ES.SDGC.BU.26900.building.gml")
footprints = footprints.to_crs(CRS).rename(columns={"localId": "ID"}).drop_duplicates("ID").set_index("ID")
matched = group.index.intersection(footprints.index)
print(f"{len(group):,} assessed buildings in the analysis; {len(matched):,} found in today's Cadastre "
f"({len(group) - len(matched)} missing)")
fp = footprints.loc[matched, ["geometry"]]4,540 assessed buildings in the analysis; 4,540 found in today's Cadastre (0 missing)
/home/runner/work/logrono-vulnerable-room-to-green/logrono-vulnerable-room-to-green/.pixi/envs/default/lib/python3.12/site-packages/geopandas/io/file.py:582: UserWarning: Error parsing datetimes, original strings are returned: Out of bounds nanosecond timestamp: 3026-08-10T00:00:00, at position 1. You might want to try:
- passing `format` if your strings have a consistent format;
- passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
- passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
return pyogrio.read_dataframe(path_or_bytes, bbox=bbox, **kwargs)
2. Land that could be greened around each building¶
ua = gpd.read_file(RAW / "urban_atlas_2018_logrono.geojson").to_crs(CRS)
ua["code"] = ua["code_2018"].astype(str)
print(ua.groupby(["code", "class_2018"]).size().to_string())
greenable = ua.loc[ua["code"].isin(GREENABLE)].geometry.union_all()
room = pd.DataFrame(index=fp.index)
for r in RADII:
zone = fp.geometry.buffer(r)
room[f"greenable_share_{r}m"] = zone.intersection(greenable).area / zone.area
room = room.join(group)
room.reset_index().to_csv(CLEAN / "room_to_green.csv", index=False)
print(room.groupby("vulnerability_group").median().round(3).to_string())code class_2018
11100 Continuous urban fabric (S.L. : > 80%) 372
11210 Discontinuous dense urban fabric (S.L. : 50% - 80%) 209
11220 Discontinuous medium density urban fabric (S.L. : 30% - 50%) 120
11230 Discontinuous low density urban fabric (S.L. : 10% - 30%) 110
11240 Discontinuous very low density urban fabric (S.L. : < 10%) 86
11300 Isolated structures 166
12100 Industrial, commercial, public, military and private units 459
12210 Fast transit roads and associated land 34
12220 Other roads and associated land 16
12230 Railways and associated land 12
13100 Mineral extraction and dump sites 36
13300 Construction sites 5
13400 Land without current use 104
14100 Green urban areas 133
14200 Sports and leisure facilities 51
21000 Arable land (annual crops) 592
22000 Permanent crops (vineyards, fruit trees, olive groves) 499
23000 Pastures 153
31000 Forests 46
32000 Herbaceous vegetation associations (natural grassland, moors...) 47
40000 Wetlands 1
50000 Water 25
greenable_share_50m greenable_share_100m greenable_share_200m
vulnerability_group
1 0.199 0.288 0.395
2 0.113 0.215 0.251
3 0.000 0.020 0.102
4 0.000 0.000 0.041
5 0.000 0.000 0.060
3. Residents at risk for every map¶
risk = pd.read_parquet(RESULTS_A / "building_risk.parquet", filters=[("variant", "==", "rates")])
keep = ["land_cover", "climate", "rp", "ID", "R", "PFRWB", "PFRMA"]
buildings = risk[keep].copy()
buildings["at_risk_PFRWB"] = buildings["PFRWB"] > 0
buildings["at_risk_PFRMA"] = buildings["PFRMA"] > 0
buildings = buildings.drop(columns=["PFRWB", "PFRMA"]).join(group, on="ID")
buildings.to_csv(CLEAN / "buildings.csv", index=False)
print(f"{buildings.groupby(['land_cover', 'climate', 'rp']).ngroups} maps; "
f"{buildings['ID'].nunique():,} buildings")82 maps; 4,540 buildings