function examples
A subscription's function runs in a sandbox when a matching
dynamical.org data product event fires. You write only the body of
handler(event, ds) — the imports and signature are fixed:
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
def handler(event, ds):
...your body...
event is the milestone payload (event_id, product_id,
init_time, …); ds is that product's dataset, already opened as an
xarray.Dataset. Your return value is placed on the result key of the
webhook body; return None to skip the delivery. To send an image, base64-encode
it and return the string.
event["init_time"] is an ISO-8601 string with a trailing Z. Strip it and
select with method="nearest" —
ds.sel(init_time=event["init_time"].replace("Z", ""), method="nearest"): the
Z makes the label tz-aware so it won't compare against the dataset's tz-naive index,
and nearest covers the test run's synthetic init before any real event has fired
(e.g. the current hour, when the product only runs 00/06/12/18Z). The examples below are written
for noaa-gfs-forecast, whose temperature_2m is in °C
and whose longitudes run −180..180; both vary by dataset.
1. Maximum 2 m temperature
Reduce the run to a single number and return it as a small JSON object.
init = event["init_time"].replace("Z", "")
t2m = ds["temperature_2m"].sel(init_time=init, method="nearest").max().item()
return {"max_temperature_2m_c": round(t2m, 2)}
2. Notify only on a threshold skip
Return None to drop the delivery — the function doubles as a filter.
init = event["init_time"].replace("Z", "")
t2m = ds["temperature_2m"].sel(init_time=init, method="nearest").max().item()
if t2m < 40: # < 40°C: nothing notable, skip
return None
return {"max_temperature_2m_c": round(t2m, 2)}
3. Regional summary with xarray
Subset to a bounding box and return a few aggregates.
init = event["init_time"].replace("Z", "")
da = ds["temperature_2m"].sel(init_time=init, method="nearest")
conus = da.sel(latitude=slice(50, 24), longitude=slice(-125, -65))
return {
"conus_mean_c": round(float(conus.mean()), 2),
"conus_max_c": round(float(conus.max()), 2),
}
4. Map plot → base64 PNG matplotlib
Plot a field with matplotlib, render to PNG in memory, and return the base64 string on
result. (Import base64 and io in your body — only
numpy/pandas/xarray/matplotlib are pre-imported.)
import base64, io
init = event["init_time"].replace("Z", "")
da = ds["temperature_2m"].sel(init_time=init, method="nearest").isel(lead_time=0)
fig, ax = plt.subplots(figsize=(8, 4))
da.plot(ax=ax)
ax.set_title(f"{event['product_id']} t2m @ {event['init_time']}")
buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=100, bbox_inches="tight")
plt.close(fig)
return {"plot_png_b64": base64.b64encode(buf.getvalue()).decode()}
5. Forecast time series at a point → base64 PNG matplotlib
Pull the lead-time series at one location and plot it against
valid_time (a clean date axis — plotting the raw lead_time renders
as nanoseconds). Set the title explicitly, or xarray's auto-title lists every scalar
coordinate (e.g. ingested_forecast_length=NaT).
import base64, io
init = event["init_time"].replace("Z", "")
series = ds["temperature_2m"].sel(
init_time=init, latitude=40.0, longitude=-105.0, method="nearest"
)
fig, ax = plt.subplots(figsize=(8, 3))
series.plot(ax=ax, x="valid_time")
ax.set_title(f"{event['product_id']} 2 m temperature @ 40N, 105W")
ax.set_ylabel("2 m temperature (°C)")
buf = io.BytesIO()
fig.savefig(buf, format="png", bbox_inches="tight")
plt.close(fig)
return {"point_forecast_png_b64": base64.b64encode(buf.getvalue()).decode()}
Variable names, coordinates, and units vary by dataset — browse a product at dynamical.org/catalog. Use the editor's test run to preview the exact body before saving.