Exports para Observable y D3
Cuaderno suplementario del CRC002. Describe y valida los cinco archivos planos (JSON y GeoJSON) que article.ipynb escribe en observable/data/ y que alimentan la galería interactiva del proyecto Riesgos.
Prerrequisito: ejecutar
article.ipynb, cuya última celda de código genera los archivos. La carpetaobservable/data/no se versiona porque se regenera en cada ejecución.
Cambio de esquema respecto de la versión 1. Los archivos conservan sus nombres, pero cambia su contenido: ahora vienen de datos reales (modelos NEX-GDDP-CMIP6, Landsat) y no del ensamble sintético. hazard_timeseries.json usa la columna modelo en vez de member, y los índices son TX90p, HWF, HWD, HWML y TX35.
import json
from pathlib import Path
import pandas as pd
import matplotlib.pyplot as plt
OBS = Path('../observable/data')
if not OBS.exists():
OBS = Path('observable/data')
archivos = sorted(OBS.glob('*'))
pd.DataFrame({'archivo': [a.name for a in archivos], 'tamaño [KB]': [round(a.stat().st_size / 1024) for a in archivos]})1. Fase 1: series de amenaza climática¶
hazard_timeseries.json tiene una fila por modelo, escenario y año. hazard_ensemble_stats.json resume la mediana, el mínimo y el máximo entre modelos.
ts = pd.read_json(OBS / 'hazard_timeseries.json')
est = pd.read_json(OBS / 'hazard_ensemble_stats.json')
print('Columnas de la serie:', list(ts.columns))
print('Filas:', len(ts), '| modelos:', sorted(ts.modelo.unique()), '| escenarios:', sorted(ts.escenario.unique()))
fig, ax = plt.subplots(figsize=(9, 4))
for e, g in est.groupby('escenario'):
ax.plot(g.anio, g.TX90p_median.rolling(10, center=True, min_periods=5).mean(), label=e)
ax.set_xlabel('año'); ax.set_ylabel('% de días con Tmáx sobre el P90'); ax.legend(); plt.tight_layout(); plt.show()Columnas de la serie: ['anio', 'modelo', 'escenario', 'TX90p', 'TN90p', 'HWF', 'HWD', 'HWML', 'HWF_tx', 'HWD_tx', 'HWML_tx', 'TX35', 'escenario_etiqueta']
Filas: 1440 | modelos: ['ACCESS-CM2', 'CMCC-ESM2', 'MPI-ESM1-2-HR', 'MRI-ESM2-0', 'NorESM2-MM'] | escenarios: ['ssp245', 'ssp370', 'ssp585']

2. Fase 1: celdas del cambio espacial¶
hazard_delta_hwf.geojson guarda el porcentaje de días con Tmáx sobre el P90 en 2061–2080 para cada celda de 0,25° que no es mar. Conserva el nombre de la versión 1 por compatibilidad.
g = json.load(open(OBS / 'hazard_delta_hwf.geojson', encoding='utf-8'))
pd.DataFrame([f['properties'] for f in g['features']]).groupby('escenario').tx90p_2061_2080.describe().round(0)3. Fase 2: isla de calor urbana¶
uhi_lst_raster.geojson contiene la LST media y P90 en bloques de unos 360 m, junto con la fracción construida; uhi_zonal_summary.json resume nueve zonas operativas.
lst = json.load(open(OBS / 'uhi_lst_raster.geojson', encoding='utf-8'))
df = pd.DataFrame([f['properties'] for f in lst['features']])
print(len(df), 'bloques;', 'LST P90: mediana', round(df.lst_p90.median(), 1), '°C')
pd.read_json(OBS / 'uhi_zonal_summary.json')1932 bloques; LST P90: mediana 46.6 °C