Estaciones hidrometeorológicas
Introducción
En este cuadernillo (Notebook) aprenderemos:
Introduccion a la red de monitoreo del IDEAM
Cátalogo de estaciones de IDEAM
Consulta de datos usando la plataforma datosabiertos.gov.co
Consulta de datos de temperatura y precipitación
Otros datos disponibles
Prerequisitos
Conceptos |
Importancia |
Notas |
---|---|---|
Necesario |
Lectura de datos tabulares |
|
Necesario |
Entender estampas de tiempo |
|
Necesario |
Entender estampas de tiempo |
|
Útil |
Mapas interactivos |
Tiempo de aprendizaje: 30 minutos
1. Catálogo nacional de estaciones de IDEAM
Según el catálogo de estaciones hidrometeorológicas del IDEAM, el país cuenta con alrededor de 4.400 estaciones de diferentes categorías. En el siguiente cuadro se resume el estado de las estaciones por categoría de acuerdo a la PQR No. 20229050190832 (Enero de 2023)
Categoría |
Activa |
Mantenimiento |
Suspendidas |
---|---|---|---|
Limnográfica |
287 |
109 |
106 |
Climátologica principal |
215 |
60 |
92 |
Mareográfica |
4 |
2 |
2 |
Pluviográfica |
104 |
0 |
87 |
Limnométrica |
323 |
11 |
557 |
Climática Ordinaria |
211 |
31 |
253 |
Agrometeorológica |
51 |
4 |
57 |
Radio Sonda |
6 |
2 |
2 |
Pluviométrica |
1109 |
9 |
603 |
Meteorológica Especial |
40 |
4 |
68 |
Sinóptica Principal |
27 |
3 |
4 |
Sinóptica Secundaria |
2 |
0 |
5 |
Total |
2381 |
235 |
1866 |
Librerías
A continuación vamos a importar las librerías que utilizaremos en este cuadernillo.
from datetime import datetime, timedelta
import cartopy.crs as ccrs
import cartopy.feature as feature
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.dates import DateFormatter, HourLocator
from pandas import to_datetime
2. Acceso al catálogo en bart.ideam.gov.co
El catálogo nacional de estaciones del IDEAM actualizado se encuentra disponible en el servidor Bart. Podemos leer el catálogo usando pandas.read_excel
como se muestra a continuación:
df_cat = pd.read_excel("http://bart.ideam.gov.co/cneideam/CNE_IDEAM.xls")
# df_cat.head()
df_cat.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4493 entries, 0 to 4492
Data columns (total 21 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 OBJECTID 4493 non-null int64
1 CODIGO 4493 non-null int64
2 nombre 4493 non-null object
3 CATEGORIA 4493 non-null object
4 TECNOLOGIA 4493 non-null object
5 ESTADO 4493 non-null object
6 FECHA_INSTALACION 4492 non-null datetime64[ns]
7 altitud 4493 non-null int64
8 latitud 4493 non-null float64
9 longitud 4493 non-null float64
10 DEPARTAMENTO 4493 non-null object
11 MUNICIPIO 4493 non-null object
12 AREA_OPERATIVA 4493 non-null object
13 AREA_HIDROGRAFICA 4493 non-null object
14 ZONA_HIDROGRAFICA 4493 non-null object
15 observacion 1332 non-null object
16 CORRIENTE 4493 non-null object
17 FECHA_SUSPENSION 1844 non-null datetime64[ns]
18 SUBZONA_HIDROGRAFICA 4493 non-null object
19 ENTIDAD 4493 non-null object
20 subred 1143 non-null object
dtypes: datetime64[ns](2), float64(2), int64(3), object(14)
memory usage: 737.3+ KB
2.1 Mapa de estaciones
Podemos usar cartopy
para hacer un mapa y visualizar las estaciones de monitoreo en el país
fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()}, dpi=150)
ax.coastlines()
gl = ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree())
ax.scatter(df_cat["longitud"], df_cat["latitud"], transform=ccrs.PlateCarree(), s=0.5)
ax.add_feature(feature.LAND)
ax.add_feature(feature.OCEAN)
ax.add_feature(feature.COASTLINE, linewidth=0.5)
ax.add_feature(feature.BORDERS, linewidth=0.5)
<cartopy.mpl.feature_artist.FeatureArtist at 0x7fb347bc6f50>
/usr/share/miniconda3/envs/atmoscol2023/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/usr/share/miniconda3/envs/atmoscol2023/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_ocean.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/usr/share/miniconda3/envs/atmoscol2023/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_physical/ne_50m_coastline.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/usr/share/miniconda3/envs/atmoscol2023/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/50m_cultural/ne_50m_admin_0_boundary_lines_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
podemos agrupar la data por área operativa, tipo de estación, tecnología, y otras variables
# df_grp = df_cat.groupby('AREA_OPERATIVA')
# df_grp = df_cat.groupby('TECNOLOGIA')
df_grp = df_cat.groupby("ESTADO")
fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()}, dpi=150)
for _, group in df_grp:
ax.scatter(
group["longitud"],
group["latitud"],
transform=ccrs.PlateCarree(),
s=0.5,
label=_,
)
ax.coastlines()
gl = ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree())
ax.add_feature(feature.LAND)
ax.add_feature(feature.OCEAN)
ax.add_feature(feature.COASTLINE, linewidth=0.5)
ax.add_feature(feature.BORDERS, linewidth=0.5)
ax.legend(fontsize=5)
<matplotlib.legend.Legend at 0x7fb347bb14d0>
Podemos validar el número total de estaciones activas, en matenimiento y suspendidas de acuerdo con la información contenida en el catálogo
for grp in df_grp.groups.keys():
print(f"{grp}: {len(df_grp.get_group(grp))}")
Activa: 2198
En Mantenimiento: 429
Suspendida: 1866
2.2 Mapa de estaciones interactivo
También podemos hacer mapas interactivos usando folium
import folium
from folium import plugins
from folium.plugins import MarkerCluster
min_lon, max_lon, min_lat, max_lat = -90, -72, -1, 14
map_ = folium.Map(
location=[8, -76],
zoom_start=6,
min_lat=min_lat,
max_lat=max_lat,
min_lon=min_lon,
max_lon=max_lon,
zoom_control=False,
control_scale=True,
scrollWheelZoom=True,
width=1000,
height=600,
)
marker_cluster = MarkerCluster(name="Estaciones").add_to(map_)
folium.TileLayer("cartodbpositron").add_to(map_)
folium.TileLayer("openstreetmap").add_to(map_)
folium.TileLayer("cartodbdark_matter").add_to(map_)
folium.LayerControl().add_to(map_)
minimap = plugins.MiniMap()
_ = map_.add_child(minimap)
Ahora agregamos las estaciones usando la siguiente función:
def plot_station(row):
"""input: series that contains a numeric named latitude and a numeric named longitude
this function creates a CircleMarker and adds it to your this_map"""
html = row.to_frame("_").to_html(
classes="table table-striped table-hover table-condensed table-responsive"
)
popup = folium.Popup(html, max_width=2650)
folium.Marker(location=[row.latitud, row.longitud], popup=popup).add_to(
marker_cluster
)
df_cat.apply(plot_station, axis=1)
map_