Metadata#

Demonstrate the Synoptic Metadata service with SynopticPy.

[1]:
from datetime import datetime

import polars as pl
import seaborn as sns
import matplotlib.pyplot as plt

import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = "Mona-Sans"  # or 'Hubot-Sans
plt.rcParams["font.monospace"] = "Fira Code"

import synoptic
from herbie.toolbox import EasyMap, pc, ccrs
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/pyproj/__init__.py:89: UserWarning: pyproj unable to set database path.
  _pyproj_global_context_initialize()

Synoptic’s station metadata service allows you to get just the metadata of stations you are interested in and filter based on station selection parameters.

Let’s start by getting metadata from good ‘ol trusty WBB…

[2]:
s = synoptic.Metadata(stid="wbb")
s.df()
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 1 stations.
[2]:
shape: (1, 14)
idstidnameelevationlatitudelongitudemnet_idstatetimezoneelev_demperiod_of_record_startperiod_of_record_endis_restrictedis_active
u32strstrf64f64f64u32strstrf64datetime[μs, UTC]datetime[μs, UTC]boolbool
1"WBB""U of U William Browning Buildi…4806.040.76623-111.84755153"UT""America/Denver"4727.71997-01-01 00:00:00 UTC2024-11-11 22:20:00 UTCfalsetrue

We can also get all the stations in Utah that report air temperature, but only those that reported air temp since January 1, 2024. To make things interesting (and to showcase Poalrs), lets sort by the stations with the longest period of record.

After looking at the result, I wouldn’t take too much stock in Synoptics period-of-record database. It has errors like some stations with records back to 1970 (if you tried getting data that far back Synoptic will tell you the “Query start and/or end time outside valid time range” error) and some stations have end dates after start dates.

[3]:
s = synoptic.Metadata(state="UT", vars="air_temp", obrange=datetime(2024, 1, 1))

df = (
    s.df()
    .with_columns(
        duration_of_record=pl.col("period_of_record_end")
        - pl.col("period_of_record_start")
    )
    .sort("duration_of_record", descending=True)
)
df
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 1,377 stations.
[3]:
shape: (1_377, 15)
idstidnameelevationlatitudelongitudemnet_idstatetimezoneelev_demperiod_of_record_startperiod_of_record_endis_restrictedis_activeduration_of_record
u32strstrf64f64f64u32strstrf64datetime[μs, UTC]datetime[μs, UTC]boolboolduration[μs]
58745"COOPFISU1""FISH SPRINGS NWR"4357.039.8401-113.397873"UT""America/Denver"4357.0nullnullfalsefalsenull
63187"UCL21""PelicanLake - Agrimet PELU"4820.040.17422-109.6665926"UT""America/Denver"4822.81970-01-01 00:00:00 UTC2024-11-11 22:15:00 UTCfalsetrue20038d 22h 15m
63191"UCL25""Monroe - Agrimet MNRU"5404.038.63411-112.1576626"UT""America/Denver"5278.91970-01-01 00:00:00 UTC2024-11-11 22:15:00 UTCfalsetrue20038d 22h 15m
63192"UCL26""CastleValley - Agrimet CSVU"4685.038.64793-109.3992526"UT""America/Denver"4701.41970-01-01 00:00:00 UTC2024-11-11 22:15:00 UTCfalsetrue20038d 22h 15m
2329"BSTU1""BRIMSTONE RESERVOIR"5620.038.30733-113.386192"UT""America/Denver"5629.91997-01-01 00:00:00 UTC2024-11-11 22:25:00 UTCfalsetrue10176d 22h 25m
251182"PC595""Bluff"4368.037.28918-109.55899247"UT""America/Denver"null2024-11-06 19:30:00 UTC2024-11-11 22:20:00 UTCfalsetrue5d 2h 50m
251284"PC597""Huntington SW"5816.039.31449-110.98184247"UT""America/Denver"null2024-11-08 19:40:00 UTC2024-11-11 22:20:00 UTCfalsetrue3d 2h 40m
251289"PC598""Catamount"8323.038.82905-111.62334247"UT""America/Denver"null2024-11-09 07:40:00 UTC2024-11-11 22:20:00 UTCfalsetrue2d 14h 40m
59704"COOPGUNU1""GUNLOCK PH"4110.037.2805-113.728377"UT""America/Denver"4107.62016-08-01 16:38:00 UTC2016-07-31 14:00:00 UTCfalsefalse-1d -2h -38m
48319"LEGCY""Legacy Prep Academy"4249.040.8676-111.9167812"UT""America/Denver"4252.02016-02-08 21:33:00 UTC2016-02-02 19:30:00 UTCfalsefalse-6d -2h -3m

All Stations#

It takes a few seconds, but you can get metadata for all Synoptic stations.

[4]:
%%time
s = synoptic.Metadata()
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 103,529 stations.
CPU times: user 1.71 s, sys: 2.63 s, total: 4.34 s
Wall time: 18 s

Let’s plot the location of all those stations, and color them by timezone. Just for fun, let’s also plot what available data existed in 1997. Of course, all this information is only as good as the metadata really is.

[5]:
in_2024 = synoptic.Metadata(obrange=datetime(2024, 1, 1)).df()
in_1997 = synoptic.Metadata(obrange=(datetime(1997, 1, 1), datetime(1998, 1, 1))).df()

fig, (ax1, ax2) = plt.subplots(2, 1, subplot_kw=dict(projection=pc))

for ax in (ax1, ax2):
    EasyMap(ax=ax, theme="dark", figsize=(12, 12)).OCEAN()

sns.scatterplot(
    in_2024.unique(["latitude", "longitude"]),
    ax=ax1,
    x="longitude",
    y="latitude",
    hue="timezone",
    legend=False,
    marker=".",
    s=10,
)

sns.scatterplot(
    in_1997.unique(["latitude", "longitude"]),
    ax=ax2,
    x="longitude",
    y="latitude",
    hue="timezone",
    legend=False,
    marker=".",
    s=10,
)

ax1.set_title("Synoptic Networks 2024", fontsize="x-large")
ax1.set_title(f"n={len(in_2024):,}", loc="right")

ax2.set_title("Synoptic Networks 1997", fontsize="x-large")
ax2.set_title(f"n={len(in_1997):,}", loc="right")

plt.tight_layout()
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 67,765 stations.
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 5,846 stations.
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
/home/blaylock/miniconda3/envs/synoptic2/lib/python3.12/site-packages/seaborn/_core/data.py:313: SettingWithCopyWarning: modifications to a method of a datetimelike object are not supported and are discarded. Change values on the original.
  return pd.api.interchange.from_dataframe(data)
../../_images/user_guide_services_tutorials_Metadata_9_2.png

PG&E Stations#

[6]:
df = synoptic.Metadata(network=229).df()
df
🚚💨 Speedy delivery from Synoptic metadata service.
📦 Received data from 1,582 stations.
[6]:
shape: (1_582, 14)
idstidnameelevationlatitudelongitudemnet_idstatetimezoneelev_demperiod_of_record_startperiod_of_record_endis_restrictedis_active
u32strstrf64f64f64u32strstrf64datetime[μs, UTC]datetime[μs, UTC]boolbool
65233"PG002""Highland Peak Road"2487.037.81296-121.80534229"CA""America/Los_Angeles"2490.22018-05-11 22:07:00 UTC2024-11-11 19:40:00 UTCfalsetrue
65234"PG003""Page Mill Road"2230.037.32591-122.18035229"CA""America/Los_Angeles"2217.82018-05-11 22:07:00 UTC2024-11-11 22:20:00 UTCfalsetrue
65235"PG004""Loma Prieta Road"2964.037.09883-121.85796229"CA""America/Los_Angeles"2956.02018-05-11 22:07:00 UTC2024-11-11 22:20:00 UTCfalsetrue
65236"PG005""Franz Valley Road"1060.038.56465-122.68386229"CA""America/Los_Angeles"1059.72018-05-11 22:07:00 UTC2024-11-11 22:20:00 UTCfalsetrue
65237"PG006""Hogback Road"1033.037.91166-122.57871229"CA""America/Los_Angeles"971.12018-05-11 22:24:00 UTC2024-11-11 22:20:00 UTCfalsetrue
246840"492PG""French Bridge"4113.039.24268-122.84953229"CA""America/Los_Angeles"null2024-10-14 23:40:00 UTC2024-11-11 22:20:00 UTCfalsetrue
247836"578PG""West Sacramento"8.038.50707-121.57904229"CA""America/Los_Angeles"null2024-10-21 23:50:00 UTC2024-11-11 22:20:00 UTCfalsetrue
247862"616PG""Arroyo Seco Campground"875.036.23525-121.48049229"CA""America/Los_Angeles"null2024-10-22 20:40:00 UTC2024-11-11 22:10:00 UTCfalsetrue
247869"581PG""Dos Palos"110.036.97466-120.63433229"CA""America/Los_Angeles"null2024-10-24 02:40:00 UTC2024-11-11 22:20:00 UTCfalsetrue
251183"615PG""Lower Tunitas Creek Road"556.037.37129-122.35965229"CA""America/Los_Angeles"null2024-11-06 17:40:00 UTC2024-11-11 22:10:00 UTCfalsetrue
[7]:
ax = EasyMap("10m", theme="grey").OCEAN().STATES().ax

ax.scatter(df["longitude"], df["latitude"], marker=".", ec="k", lw=0.1)
ax.set_title("PG&E Station Locations", loc="left", fontweight="bold")
ax.set_title(f"Total: {len(df):,}", loc="right")

[7]:
Text(1.0, 1.0, 'Total: 1,582')
../../_images/user_guide_services_tutorials_Metadata_12_1.png
[8]:
from matplotlib.dates import DateFormatter

plt.hist(df["period_of_record_start"].dt.round("3mo"), bins=30, ec="w", zorder=10)
plt.grid()

plt.gca().tick_params(axis="x", labelrotation=45)
date_form = DateFormatter("%Y %b")
plt.gca().xaxis.set_major_formatter(date_form)

plt.title("Number of PG&E weather stations by\nperiod of record start date")
[8]:
Text(0.5, 1.0, 'Number of PG&E weather stations by\nperiod of record start date')
../../_images/user_guide_services_tutorials_Metadata_13_1.png