In [1]:
import geopandas as gp
import pandas as pd
import searvey
import hvplot.pandas
In [20]:
from shapely.geometry import Point
def get_stofs2d_meta():
    stofs2d = pd.read_csv(
        "https://polar.ncep.noaa.gov/stofs/data/stofs_2d_glo_elev_stat_v2_1_0",
        names=["coords", "name"],
        sep="!",
        header=None,
        skiprows=1
    )
    stofs2d = stofs2d.assign(
        lon=stofs2d.coords.str.split("\t", n=1).str[0].astype(float),
        lat=stofs2d.coords.str.strip().str.rsplit("\t", n=1).str[1].astype(float),
        stofs2d_name=stofs2d.name.str.strip(),
    ).drop(columns=["coords", "name"])
    stofs2d["geometry"] = [Point(xy) for xy in zip(stofs2d.lon, stofs2d.lat)]

    return stofs2d


def get_ioc_meta() -> gp.GeoDataFrame:
    meta_web = searvey.get_ioc_stations().drop(columns=["lon", "lat"])
    meta_api = (
        pd.read_json(
            "http://www.ioc-sealevelmonitoring.org/service.php?query=stationlist&showall=all"
        )
        .drop_duplicates()
        .drop(columns=["lon", "lat"])
        .rename(columns={"Code": "ioc_code", "Lon": "lon", "Lat": "lat"})
    )
    merged = pd.merge(
        meta_web,
        meta_api[["ioc_code", "lon", "lat"]].drop_duplicates(),
        on=["ioc_code"],
    )
    return merged


def merge_ioc_and_stofs(ioc: pd.DataFrame, stofs2d: pd.DataFrame) -> pd.DataFrame:
    stations = pd.concat((ioc, stofs2d), ignore_index=True)
    stations = stations.assign(unique_id=stations.ioc_code.combine_first(stations.stofs2d_name))
    return stations
In [21]:
ioc = get_ioc_meta()
stofs2d = get_stofs2d_meta()
m = merge_ioc_and_stofs(ioc=ioc, stofs2d=stofs2d)
m.to_csv("assets/ioc_stofs.csv")

you can grab the csv file at: https://github.com/tomsail/static/assets/ioc_stofs.csv

In [31]:
(ioc.hvplot.points(
    x='lon', y='lat', 
    geo=True, 
    s=50,
    tiles = True, 
    cmap = "coolwarm", 
    hover_cols=["unique_id"], 
)*stofs2d.hvplot.points(
    x='lon', y='lat', 
    geo=True, 
    tiles = True, 
    cmap = "coolwarm", 
    hover_cols=["unique_id"], 
)).opts(
    width = 1800, 
    height=1000,
    title="False = STOFS list, True = IOC database", 
)
Out[31]: