Ideam_Logo

Estaciones hidrometeorológicas


Introducción

En este cuadernillo (Notebook) aprenderemos:

  1. Introduccion a la red de monitoreo del IDEAM

  2. Cátalogo de estaciones de IDEAM

  3. Consulta de datos usando la plataforma datosabiertos.gov.co

  4. Consulta de datos de temperatura y precipitación

  5. Otros datos disponibles

Prerequisitos

Conceptos

Importancia

Notas

Introducción a Pandas

Necesario

Lectura de datos tabulares

Introducción a Datetime

Necesario

Entender estampas de tiempo

Introducción a Cartopy

Necesario

Entender estampas de tiempo

Introducción a folium

Ú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)
../../_images/e6f64b2c331a67a45105af8b64207cb4acb3918cfc49585db0dd4faaef1e65b3.png

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>
../../_images/c1ba67ee42f8e777e399aa351f4a819e4ed661afca95c6881e9eeeab15eee080.png

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_
Make this Notebook Trusted to load map: File -> Trust Notebook