Clima României pe hartă¶
În Clima localității mele am decupat din cubul meteo EOCube.Ro un singur oraș și am lucrat în memorie. Aici punem întrebările pentru toată țara deodată: unde s-a încălzit cel mai mult, unde a crescut numărul zilelor tropicale, cum arată „normala” 1991–2020 pe hartă, ce județe s-au schimbat cel mai mult.
Diferența este de volum: o hartă medie pe 30 de ani înseamnă citirea a ~20 GB de date pentru fiecare variabilă. Pe un laptop durează minute; cu un cluster Dask pornit din platformă durează secunde, iar codul rămâne același. Pentru cluster este nevoie de un cont EOCube.Ro — caietul este gândit pentru notebooks.eocube.ro, unde clusterul este la un apel distanță. Datele în sine rămân publice și se citesc anonim, ca și în caietul anterior.
Modificați aici¶
Perioadele comparate: referința istorică, perioada recentă și normala climatică folosită pentru hărțile de bază. Anul curent nu intră în nicio perioadă, fiind incomplet.
ISTORIC = ("1961", "1990") # perioada de referință
RECENT = ("2011", "2025") # perioada recentă, ani compleți
NORMALA = ("1991", "2020") # normala climatică standard
NUMAR_WORKERI = 8 # noduri de lucru în cluster
import warnings
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import rioxarray # noqa: F401 (activează accesorul .rio)
import s3fs
import xarray as xr
from eocube.integrations.notebooks import setup_personal_cluster
warnings.filterwarnings("ignore")
plt.rcParams.update({"figure.dpi": 110, "axes.spines.top": False, "axes.spines.right": False})
Pornim clusterul¶
Un singur apel: clusterul se creează dacă nu există, altfel este refolosit, și este scalat la numărul de workeri cerut. Legătura „Dashboard” din rezultat deschide tabloul de bord Dask, unde se vede în timp real cum se împart calculele.
cluster, client = setup_personal_cluster(NUMAR_WORKERI)
client
Deschidem cubul și conturul județelor¶
Cubul se deschide exact ca în caietul anterior. Pentru contururi citim, tot de la
geo-spatial.org, stratul WFS cu cele 42 de județe (inclusiv Municipiul București),
direct cu geopandas.
fs = s3fs.S3FileSystem(anon=True, endpoint_url="https://storage.svc.uvt-01.eocube.ro")
cub = xr.open_zarr(fs.get_mapper("meteo/cube/open-v1")).rio.write_crs("EPSG:4326")
ultima_zi = pd.Timestamp(cub.last_precipitation_time.values)
print("Date disponibile: 1961-01-01 →", ultima_zi.date())
judete = gpd.read_file(
"https://services.geo-spatial.org/geoserver/ows?service=WFS&version=1.1.0&request=GetFeature"
"&typeName=geospatial:ro_judete_poligon&outputFormat=json&srsName=EPSG:4326"
)
print(len(judete), "județe")
Date disponibile: 1961-01-01 → 2026-09-28
42 județe
O funcție mică de desenat hărți, ca să nu repetăm aceleași cinci linii de
matplotlib de fiecare dată: imaginea, conturul județelor și bara de culori.
def harta(date, titlu, ax=None, **stil):
ax = ax or plt.subplots(figsize=(11, 6))[1]
im = date.plot.imshow(ax=ax, add_colorbar=False, **stil)
judete.boundary.plot(ax=ax, color="0.25", linewidth=0.5)
ax.set(title=titlu, xlabel="", ylabel="", aspect=1.45)
plt.colorbar(im, ax=ax, shrink=0.75, pad=0.02)
return ax
Normala climatică 1991–2020¶
Două hărți de bază: precipitațiile medii anuale și temperatura maximă medie din
iulie. Selecția este leneșă — nimic nu se citește până la .compute(), care
trimite lucrul în cluster și aduce înapoi doar harta finală (o jumătate de milion
de valori).
normala = cub.sel(time=slice(*NORMALA))
precip_anual = normala["precipitation"].resample(time="YS").sum().mean("time").compute()
tmax_iulie = normala["max_temp"].sel(time=normala["time"].dt.month == 7).mean("time").compute()
fig, axes = plt.subplots(1, 2, figsize=(16, 5))
harta(precip_anual, "precipitații medii anuale, 1991–2020 (mm)",
ax=axes[0], cmap="Blues", vmin=300, vmax=1200)
harta(tmax_iulie, "temperatura maximă medie în iulie, 1991–2020 (°C)",
ax=axes[1], cmap="Reds", vmin=14, vmax=34)
plt.tight_layout(); plt.show()
Cât s-a încălzit fiecare loc¶
Temperatura medie (media dintre maxima și minima zilnică) din perioada recentă minus cea din perioada istorică. Nicăieri în țară diferența nu este sub +1 °C, așa că scara începe de la 1. Harta este la ~1 km, deci se văd diferențele dintre câmpie, dealuri și munte.
def temperatura_medie(perioada):
p = cub.sel(time=slice(*perioada))
return ((p["max_temp"] + p["min_temp"]) / 2).mean("time")
incalzire = (temperatura_medie(RECENT) - temperatura_medie(ISTORIC)).compute()
harta(incalzire, f"încălzirea: {RECENT[0]}–{RECENT[1]} față de {ISTORIC[0]}–{ISTORIC[1]} (°C)",
cmap="Reds", vmin=1, vmax=2.5)
plt.show()
print(f"Media pe țară: {float(incalzire.mean()):+.2f} °C")
Zile tropicale în plus¶
Numărul mediu de zile pe an cu maxima ≥ 30 °C în perioada recentă, minus același număr în perioada istorică. La munte nu se schimbă nimic (nu se ajunge la 30 °C nici acum); în câmpia de sud sunt cu peste o lună de zile tropicale în plus.
def zile_tropicale_pe_an(perioada):
p = cub["max_temp"].sel(time=slice(*perioada))
return (p >= 30).resample(time="YS").sum().mean("time")
zile_in_plus = (zile_tropicale_pe_an(RECENT) - zile_tropicale_pe_an(ISTORIC)).compute()
titlu = f"zile tropicale în plus pe an: {RECENT[0]}–{RECENT[1]} față de {ISTORIC[0]}–{ISTORIC[1]}"
harta(zile_in_plus, titlu,
cmap="Reds", vmin=0, vmax=40)
plt.show()
Clasamentul județelor¶
Aceleași două hărți, agregate pe județe: pentru fiecare poligon decupăm harta
(rio.clip) și luăm media. Sunt operații pe tablouri deja în memorie, deci
durează câteva secunde pentru toate cele 42 de județe.
def media_pe_judete(harta_calculata):
return {j["name"]: float(harta_calculata.rio.clip([j.geometry]).mean())
for _, j in judete.iterrows()}
pe_judet = pd.DataFrame({
"încălzire (°C)": media_pe_judete(incalzire),
"zile tropicale în plus / an": media_pe_judete(zile_in_plus),
}).sort_values("încălzire (°C)")
fig, axes = plt.subplots(1, 2, figsize=(14, 10), sharey=True)
for ax, col, culoare in zip(axes, pe_judet.columns, ["#c0392b", "#c98a1b"]):
ax.barh(pe_judet.index, pe_judet[col], color=culoare, alpha=0.8)
ax.set(title=col); ax.grid(axis="x", alpha=0.3)
plt.tight_layout(); plt.show()
pe_judet.round(2).tail(5)
Export pentru QGIS și Excel¶
Orice hartă calculată se scrie ca GeoTIFF cu rio.to_raster (georeferențiat, se
deschide direct în QGIS), iar tabelul județelor ca CSV.
incalzire.rio.to_raster(f"incalzire_{RECENT[0]}-{RECENT[1]}_vs_{ISTORIC[0]}-{ISTORIC[1]}.tif")
pe_judet.round(3).to_csv("clima_pe_judete.csv")
Închidem clusterul¶
Workerii ocupă resurse cât timp există; când ați terminat, îi eliberați.
client.close()
cluster.close()
Ce urmează¶
- Schimbați perioadele din prima celulă, sau indicatorul (de exemplu nopți
tropicale,
min_temp >= 20, sau zile cu peste 20 mm de precipitații) — restul caietului rămâne neschimbat. - Pentru o singură localitate nu este nevoie de cluster: vedeți Clima localității mele.
- Sursa datelor: Administrația Națională de Meteorologie (© MeteoRomania), set publicat pe data.gov.ro și re-gridat de Universitatea de Vest din Timișoara. Contururi administrative: geo-spatial.org. Citați ANM în orice lucrare bazată pe aceste date.