
Radares Meteorológicos¶
🧭 Introducción¶
Este cuaderno enseña cómo acceder, procesar y visualizar datos de radares meteorológicos del IDEAM desde AWS.
📚 Descripción general¶
En este cuaderno aprenderás a acceder y procesar datos de radar meteorológico en Colombia. Explorarás cómo leer archivos en formato RAWDSX2 utilizando la biblioteca raw2zarr, convertir los datos a formatos cloud-nativos (Zarr), y realizar visualizaciones básicas de reflectividad y otros productos radar.
Los datos provienen de la red de radares del IDEAM (Instituto de Hidrología, Meteorología y Estudios Ambientales de Colombia), que monitorea la atmósfera en tiempo real para pronóstico de tiempo severo y estimación de precipitación.
✅ Requisitos Previos¶
Conceptos | Importancia | Notas |
|---|---|---|
Necesario | Lectura de datos multidimensionales | |
Necesario | Lectura de datos de radar | |
Necesario | Lectura de datos de radar | |
Necesario | Lectura de datos de radar | |
Útil | Fundamentos básicos en radares meteorológicos | |
Útil | Entender la metadata de los datos |
⏱️ Tiempo estimado de aprendizaje: 30 minutos
✍️ Formato: Interactivo. Ejecuta y modifica el código en cada celda.
Librerías¶
Importamos las librerías necesarias para este cuaderno.
# Librerías necesarias
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import cmweather
import fsspec
import matplotlib.pyplot as plt
import pandas as pd
import xradar as xd
from cartopy import geodesic
from shapely.geometry import Point1. 🌎 Red de Radares del IDEAM en AWS¶
Colombia cuenta con una red de radares meteorológicos operados por diferentes instituciones. El IDEAM publica datos de 4 radares en banda C mediante un bucket público en Amazon Web Services (AWS).
Radar | Ubicación | Banda | Alcance |
|---|---|---|---|
Carimagua | Meta | C | ~200 km |
Guaviare | Guaviare | C | ~200 km |
Munchique | Cauca | C | ~200 km |
Barrancabermeja | Santander | C | ~200 km |
1.1 🗺️ Ubicación de radares del IDEAM¶
El siguiente mapa muestra la ubicación geográfica de los radares IDEAM disponibles en AWS:
# Cargar los datos
df_radares = pd.read_csv("../data/radar_locations.csv")
# Crear figura con proyección geográfica
fig, ax = plt.subplots(
figsize=(8, 8),
subplot_kw={"projection": ccrs.PlateCarree()},
dpi=120
)
# Configurar mapa base
ax.set_extent([-85, -65, -5, 15])
ax.add_feature(cfeature.LAND, facecolor="whitesmoke")
ax.add_feature(cfeature.COASTLINE, linewidth=0.5)
ax.add_feature(cfeature.BORDERS, linestyle=":")
ax.gridlines(draw_labels=True, linestyle="--", linewidth=0.5)
# Dibujar cada radar
for _, row in df_radares.iterrows():
lon, lat = row["lon"], row["lat"]
name = row["Name"]
color = row["color"]
# Punto del radar
ax.plot(lon, lat, "o", color=color, markersize=6)
ax.text(lon + 0.3, lat, name, fontsize=8)
# Determinar radio en km (IDIGER tiene menor alcance)
radio_km = 40 if "IDIGER" in name.upper() else 200
# Dibujar círculo geodésico
circle = geodesic.Geodesic().circle(lon=lon, lat=lat, radius=radio_km * 1000, n_samples=100)
ax.plot(*circle.T, color=color, linewidth=1, alpha=0.3)
ax.set_title("Red de radares meteorológicos de Colombia")
plt.tight_layout()/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_coastline.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_cultural/ne_50m_admin_0_boundary_lines_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)

2.2 📡 Radares meteorológicos del IDEAM disponibles en AWS¶
El Instituto de Hidrología, Meteorología y Estudios Ambientales (IDEAM) ha puesto a disposición pública los datos de sus radares meteorológicos mediante un repositorio en la nube de Amazon Web Services (AWS). Este repositorio facilita el acceso a información cruda de reflectividad y otros productos en formato propietario (SIGMET/IRIS), permitiendo su análisis mediante herramientas especializadas.
La estructura del bucket es la siguiente:
s3://s3-radaresideam/l2_data/YYYY/MM/DD/Radar_name/RRRAAMMDDTTTTTT.RAWXXXX Donde:
YYYY,MM,DDindican el año, mes y día del escaneo.Radar_namees el nombre del radar (por ejemplo: Guaviare, Munchique, Barrancabermeja, Carimagua).RRRAAMMDDTTTTTT.RAWXXXXes el nombre del archivo en formato SIGMET, compuesto por:RRR: código del radar (ej.GUApara Guaviare)AA: año en 2 dígitosMM: mesDD: díaTTTTTT: hora de adquisición (UTC)RAWXXXX: identificador interno generado por el software IRIS
IDEAM actualmente dispone de archivos para sus radares principales desde aproximadamente 2018. Los datos están organizados por fecha, por lo que es necesario navegar dentro del bucket por año, mes y día para acceder a los archivos específicos.
!aws s3 ls --no-sign-request s3://s3-radaresideam/l2_data/2021/09/19/ PRE Guaviare/
PRE Munchique/
3. 📥 Acceso a los datos de radar usando Python¶
IDEAM publica los datos de sus radares meteorológicos en un bucket público de Amazon Web Services (AWS). Gracias a herramientas como fsspec y xradar, es posible explorar estos datos, filtrarlos por fecha y estación, y analizarlos sin necesidad de descargarlos manualmente desde la web.
3.1 🔎 Exploración de archivos disponibles en el bucket¶
Primero, se establece una conexión con el bucket S3 de IDEAM utilizando fsspec. Esto permite buscar archivos según una ruta de interés que incluye fecha y nombre del radar.
import fsspec
# Crear sistema de archivos con acceso anónimo
fs = fsspec.filesystem("s3", anon=True)
# Buscar archivos de radar disponibles en una fecha específica
files = sorted(fs.glob("s3-radaresideam/l2_data/2022/08/09/Carimagua/CAR22080919*"))
files[:5] # Mostrar los primeros 5 archivos encontrados['s3-radaresideam/l2_data/2022/08/09/Carimagua/CAR220809190003.RAWDSVV',
's3-radaresideam/l2_data/2022/08/09/Carimagua/CAR220809190315.RAWDSW0',
's3-radaresideam/l2_data/2022/08/09/Carimagua/CAR220809190401.RAWDSW3',
's3-radaresideam/l2_data/2022/08/09/Carimagua/CAR220809190505.RAWDSW8',
's3-radaresideam/l2_data/2022/08/09/Carimagua/CAR220809191003.RAWDSWM']3.2 📡 Lectura de archivos mediante streaming¶
Una vez localizado un archivo de interés, podemos leerlo directamente desde AWS mediante streaming sin necesidad de descargarlo al sistema local. Utilizamos fsspec.open() para abrir el archivo en modo lectura y luego xradar lo lee directamente, retornando una estructura jerárquica del tipo DataTree que organiza los barridos (sweeps) contenidos en el archivo.
# Seleccionamos un archivo del bucket
filepath = f"s3://{files[7]}"
# Opciones de acceso a S3 (anónimo)
storage_options = {"anon": True}
# Abrimos el archivo en modo streaming y lo leemos con xradar
stream = fsspec.open(filepath, mode="rb", **storage_options).open()
radar = xd.io.open_iris_datatree(stream.read())
# Visualizamos el contenido del datatree
display(radar)📘 xradar utiliza xarray y devuelve una estructura tipo DataTree, que organiza jerárquicamente la información contenida en los archivos SIGMET/IRIS, incluyendo múltiples niveles de escaneo por elevación.
3.3 📊 Gráfico de reflectividad¶
Una vez cargado el archivo con xradar, podemos acceder a un barrido individual (por ejemplo, sweep_0) y visualizar la reflectividad horizontal (DBZH) utilizando la funcionalidad xarray.plot que se encuentra incorporada en xarray.
Este primer gráfico mostrará la reflectividad en coordenadas de azimut y rango, tal como fue registrada por el radar.
# Accedemos al primer barrido (elevación más baja)
sweep = radar["sweep_0"]
# Visualizamos la reflectividad horizontal (DBZH)
sweep["DBZH"].plot(cmap="ChaseSpectral", vmin=-10, vmax=60);
🎯 Este tipo de visualización es útil para inspeccionar rápidamente los valores de reflectividad en coordenadas polares. La reflectividad se mide en decibelios Z (dBZ) y representa la intensidad de los retornos del radar.
3.4 🌐 Georreferenciación de datos de radar¶
Los datos de radar inicialmente se encuentran en coordenadas polares: azimuth (ángulo de rotación horizontal) y range (distancia radial). Para representar estos datos en un sistema de coordenadas cartesianas (x, y, z), es necesario aplicar un proceso de georreferenciación.
La librería xradar permite realizar este proceso fácilmente con el método .xradar.georeference().
# Aplicamos georreferenciación al datatree completo
radar = radar.xradar.georeference()
# Inspeccionamos el barrido ya georreferenciado
display(radar["sweep_0"])🌐 La georreferenciación agrega nuevas coordenadas físicas x, y y z que representan la ubicación espacial real del haz de radar sobre la superficie terrestre. Esto es esencial para graficar en mapas o combinar con otras fuentes geoespaciales.
3.5 🗺️ Visualización georreferenciada¶
Una vez que los datos han sido georreferenciados, podemos generar una visualización en coordenadas cartesianas usando las nuevas variables x e y. Esto permite observar la reflectividad del radar en el espacio físico real, facilitando su integración con mapas o capas geográficas.
# Graficamos la reflectividad horizontal usando coordenadas georreferenciadas
radar["sweep_0"]["DBZH"].plot(
x="x",
y="y",
cmap="ChaseSpectral",
vmin=-10,
vmax=60
);
🗺️ Esta visualización representa los datos del radar en un plano cartesiano, donde la ubicación espacial ya no depende de azimut y rango, sino de distancias en metros respecto al radar (coordenadas x, y).
3.6 🗺️ Visualización en sistema de coordenadas geográficas¶
Para representar los datos de reflectividad en un mapa geográfico, es necesario convertir el sistema de coordenadas cartesianas del radar a un sistema proyectado. Podemos obtener esta proyección directamente desde el objeto radar utilizando xradar.georeference.get_crs() y crear una proyección compatible con cartopy.
# Obtener el sistema de referencia proyectado desde el radar
proj_crs = xd.georeference.get_crs(radar["sweep_0"].ds)
# Crear una proyección de Cartopy usando ese CRS
cart_crs = ccrs.Projection(proj_crs)🧭 Esta proyección permitirá superponer correctamente los datos del radar sobre mapas de costas, fronteras y otros elementos geográficos usando cartopy.
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
radar["sweep_0"]["DBZH"].plot(
x="x",
y="y",
cmap="ChaseSpectral",
transform=cart_crs,
cbar_kwargs=dict(pad=0.075, shrink=0.75),
vmin=-10,
vmax=60,
)
ax.coastlines()
ax.gridlines(draw_labels=True, ls="--", lw=0.5);/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/mpl/geoaxes.py:1762: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
result = super().pcolormesh(*args, **kwargs)
/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_physical/ne_10m_coastline.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)

🌐 Este gráfico muestra la reflectividad del radar proyectada sobre un sistema de coordenadas geográficas. Es útil para análisis meteorológico, estudios hidrológicos y validación con otras fuentes de datos espaciales.
4. 🌧️ Estimación de lluvia a partir de reflectividad¶
Una de las aplicaciones más comunes del radar meteorológico es la estimación de tasas de precipitación. Para ello se utiliza una relación empírica entre la reflectividad Z y la intensidad de lluvia R, conocida como relación Z–R.
Una de las formas más conocidas y utilizadas es la propuesta por Marshall & Gunn (1953):
Despejando para :
A continuación, implementamos este procedimiento paso a paso para estimar la lluvia a partir de un barrido de reflectividad horizontal (DBZH):
# Reflectividad en unidades logarítmicas (dBZ)
ref_log = radar["sweep_0"]["DBZH"]
# Conversión a unidades lineales
ref_lin = 10 ** (ref_log / 10)
# Aplicación de la ecuación de Marshall & Gunn (1953)
rr = (1 / 200) ** (1 / 1.6) * ref_lin ** (1 / 1.6)Ahora podemos visualizar el campo de lluvia generado
4.1 🗺️ Visualización del campo de lluvia¶
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
rr.plot(
x="x",
y="y",
cmap="jet",
transform=cart_crs,
cbar_kwargs=dict(
pad=0.075, shrink=0.75, label=r"$Intensidad \ de \ lluvia \ [mm hr^{-1}]$"
),
vmin=0,
vmax=50,
)
ax.set_title(r"$Estimado \ de \ lluvia$")
ax.coastlines()
ax.gridlines(draw_labels=True, ls="--", lw=0.5);/home/runner/micromamba/envs/cdh-python/lib/python3.13/site-packages/cartopy/mpl/geoaxes.py:1762: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
result = super().pcolormesh(*args, **kwargs)

🏋️ Práctica: Explora otro radar y fecha¶
Aplica lo aprendido consultando datos de un radar diferente.
Desafío:
Elige otro radar IDEAM (Guaviare, Munchique, o Barrancabermeja)
Selecciona una fecha diferente (2021-2023)
Descarga, visualiza y estima precipitación
Pistas:
Usa
fs.glob()con el patrón del radar elegidoVerifica que la fecha tenga datos disponibles
Recuerda georreferenciar antes de graficar en mapa
# Tu código aquí
# 1. Buscar archivos de otro radar
# mi_radar = "Guaviare"
# mi_fecha = "2022/03/15"
# files = sorted(fs.glob(f"s3-radaresideam/l2_data/{mi_fecha}/{mi_radar}/*"))
# 2. Descargar y leer
# ...
# 3. Visualizar y estimar lluvia
# ...💡 Solución
A continuación se presenta una posible solución completa del ejercicio:
# 1. Buscar archivos del radar Guaviare para una fecha específica
mi_radar = "Guaviare"
mi_fecha = "2022/03/15"
# Buscar archivos disponibles
files_guaviare = sorted(fs.glob(f"s3-radaresideam/l2_data/{mi_fecha}/{mi_radar}/GUA*"))
print(f"📡 Radar seleccionado: {mi_radar}")
print(f"📅 Fecha: {mi_fecha}")
print(f"📁 Archivos encontrados: {len(files_guaviare)}")
print("\nPrimeros 5 archivos:")
for f in files_guaviare[:5]:
print(f" - {f}")
# 2. Leer archivo mediante streaming
# Seleccionamos un archivo de la tarde (hora local ~14:00 UTC)
filepath_gua = f"s3://{files_guaviare[20]}" # Ajusta el índice según disponibilidad
print(f"📥 Leyendo: {filepath_gua.split('/')[-1]}")
# Abrir y leer con xradar
stream_gua = fsspec.open(filepath_gua, mode="rb", anon=True).open()
radar_gua = xd.io.open_iris_datatree(stream_gua.read())
# Mostrar información del radar
print(f"\n📍 Ubicación: {float(radar_gua.latitude.values):.2f}°N, "
f"{float(radar_gua.longitude.values):.2f}°E")
print(f"🎯 Elevación: {float(radar_gua.altitude.values):.0f} m")
print(f"📊 Barridos (sweeps): {len(radar_gua.children)}")
# 3. Georreferenciar los datos
radar_gua = radar_gua.xradar.georeference()
# Verificar que se añadieron coordenadas x, y, z
print("✅ Datos georreferenciados")
print(f" Coordenadas agregadas: {list(radar_gua['sweep_0'].coords.keys())}")
# 4. Visualizar reflectividad en mapa
# Obtener proyección del radar
proj_crs_gua = xd.georeference.get_crs(radar_gua["sweep_0"].ds)
cart_crs_gua = ccrs.Projection(proj_crs_gua)
# Crear figura
fig, ax = plt.subplots(
figsize=(9, 9),
subplot_kw={'projection': ccrs.PlateCarree()},
dpi=120
)
# Graficar reflectividad
radar_gua["sweep_0"]["DBZH"].plot(
x="x",
y="y",
cmap="ChaseSpectral",
transform=cart_crs_gua,
cbar_kwargs={
'pad': 0.075,
'shrink': 0.75,
'label': 'Reflectividad [dBZ]'
},
vmin=-10,
vmax=60,
)
# Añadir elementos geográficos
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.8)
ax.gridlines(draw_labels=True, linewidth=0.5, alpha=0.5)
# Título con información del radar
hora_scan = str(radar_gua["sweep_0"]["time"].values[0])[:19]
ax.set_title(f'Reflectividad - Radar {mi_radar}\n{hora_scan} UTC',
fontsize=13, weight='bold')
plt.tight_layout()
plt.show()
# 5. Estimar intensidad de lluvia usando la relación Z-R
# Marshall & Gunn (1953): Z = 200 * R^1.6
# Reflectividad en dBZ
ref_log_gua = radar_gua["sweep_0"]["DBZH"]
# Convertir a unidades lineales
ref_lin_gua = 10 ** (ref_log_gua / 10)
# Aplicar ecuación Z-R
rr_gua = (1 / 200) ** (1 / 1.6) * ref_lin_gua ** (1 / 1.6)
# Estadísticas
print(f"📊 Estadísticas de lluvia estimada:")
print(f" Máxima: {float(rr_gua.max().values):.1f} mm/h")
print(f" Promedio (no-cero): {float(rr_gua.where(rr_gua > 0.1).mean().values):.1f} mm/h")
print(f" Área con lluvia > 5 mm/h: "
f"{float((rr_gua > 5).sum().values) / float(rr_gua.size) * 100:.1f}%")
# 6. Visualizar campo de lluvia estimada
fig, ax = plt.subplots(
figsize=(9, 9),
subplot_kw={'projection': ccrs.PlateCarree()},
dpi=120
)
rr_gua.plot(
x="x",
y="y",
cmap="jet",
transform=cart_crs_gua,
cbar_kwargs={
'pad': 0.075,
'shrink': 0.75,
'label': 'Intensidad de lluvia [mm/h]'
},
vmin=0,
vmax=50,
)
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.8)
ax.gridlines(draw_labels=True, linewidth=0.5, alpha=0.5)
ax.set_title(f'Estimación de precipitación - Radar {mi_radar}\n{hora_scan} UTC',
fontsize=13, weight='bold')
plt.tight_layout()
plt.show()
**Notas sobre la solución:**
- **Radar Guaviare**: Ubicado en el departamento del Guaviare, monitorea la región amazónica colombiana
- **Fecha seleccionada**: Marzo suele tener actividad convectiva en la región
- **Relación Z-R**: Marshall & Gunn (1953) es una relación general; para mejores resultados se pueden usar relaciones específicas por tipo de lluvia (convectiva, estratiforme, tropical)
- **Unidades**: La reflectividad viene en dBZ (logarítmica), pero la ecuación Z-R requiere Z en unidades lineales (mm⁶/m³)
💡 **Experimenta con**:
- Otros radares: `Munchique` (costa Pacífico), `Barrancabermeja` (Magdalena Medio)
- Diferentes fechas: busca eventos extremos reportados en noticias
- Otros barridos: prueba con `sweep_1`, `sweep_2` (elevaciones mayores)
- Umbrales de lluvia: identifica zonas con precipitación > 20 mm/h✅ Resumen¶
¡Felicitaciones! Ahora puedes:
✅ Acceder a datos de radar del IDEAM desde AWS S3
✅ Leer archivos SIGMET usando xradar y fsspec
✅ Georreferenciar datos de radar a coordenadas cartesianas
✅ Visualizar reflectividad en mapas con cartopy
✅ Estimar precipitación usando relaciones Z-R empíricas
🚀 ¿Qué sigue?¶
Ahora que dominas el acceso a datos de radar, puedes explorar:
[3.5. Estimación Cuantitativa de Precipitación (QPE)] - Técnicas avanzadas de estimación de lluvia
[3.4. Perfiles Cuasi-Verticales (QVP)] - Análisis de estructura vertical de tormentas
Aplicaciones hidrológicas - Integrar lluvia estimada por radar en modelos de caudal
Proyecto sugerido:¶
Descarga datos de un evento extremo en tu región y analiza:
Evolución temporal de la tormenta
Máximos de reflectividad y ubicación
Lluvia acumulada estimada
Comparación con estaciones pluviométricas
📚 Otros recursos¶
A continuación, se listan recursos clave y bibliografía utilizada en el desarrollo de este cuadernillo:
🧰 Herramientas y documentación¶
📘 Lecturas recomendadas¶
📄 Referencias bibliográficas¶
Grover, M., Sherman, Z., Sharma, M., Ladino, A., Camron, C., & Radar Cookbook Contributors. (2023). Radar Cookbook. Zenodo. Grover et al. (2023)
Rose, B. E. J., Kent, J., Tyle, K., Clyne, J., Banihirwe, A., Camron, D., Ford, R., Morley, J., Grover, M., et al. (2023). Pythia Foundations (Version v2023.05.01). Zenodo. Rose et al. (2023)
Marshall, J. S., & Palmer, W. M. (1948). The distribution of raindrops with size. J. Atmos. Sci., 5, 165–166. Marshall & Palmer (1948)
- Grover, M., Sherman, Z., Sharma, M., Ladino, A., Camron, C., & Radar Cookbook Contributors. (2023). Radar Cookbook. Zenodo. 10.5281/ZENODO.8075855
- Rose, B., Kent, J., Tyle, K., Clyne, Banihirwe, A., Camron, D., Ford, R., Morley, J., Grover, M., Eroglu, O., Paul, K., May, R., Lkailynncar, Irving, D., Uieda, L., Ojaybee, Blain, P., & Moon, Z. (2023). ProjectPythia/pythia-foundations: v2023.05.01. Zenodo. 10.5281/ZENODO.7884572
- Marshall, J. S., & Palmer, W. M. K. (1948). THE DISTRIBUTION OF RAINDROPS WITH SIZE. Journal of Meteorology, 5(4), 165–166. https://doi.org/10.1175/1520-0469(1948)005<0165:tdorws>2.0.co;2