Get a regional average timeseries interactively

Drag a slider to explore data over time on a map–then, subset a region on the map to get an regional averaged timeseries!

Cross sections too: https://medium.com/stackademic/holoviews-streams-for-exploring-multidimensional-data-44b4eebc25da

import xarray as xr
import holoviews as hv
import panel as pn
from holoviews.streams import BoundsXY
hv.extension("bokeh")

def plot_image(time):
    return hv.Image(ds.sel(time=time), ["lon", "lat"]).opts(
        title=time, tools=["box_select"], active_tools=["box_select"]
    )

def plot_timeseries(bounds):
    lons = bounds[0], bounds[2]
    lats = bounds[1], bounds[3]
    ds_sel = ds.sel(lon=slice(*lons), lat=slice(*lats)).mean(("lat", "lon"))
    return hv.Curve(ds_sel, kdims="time", vdims="air").opts(
        title=f"{lons[0]:.2f} to {lons[1]:.2f}, {lats[0]:.2f} to {lats[1]:.2f}"
    )

# load data
ds = xr.tutorial.open_dataset("air_temperature").sortby("lat")

# make image depend on time
slider = pn.widgets.DiscreteSlider(
    options=ds.time.dt.strftime("%Y-%m-%d %H:%M").values.tolist(),
    name="time",
    align="center",
)
image = hv.DynamicMap(pn.bind(plot_image, time=slider.param.value))

# make timeseries depend on bounds
bounds_stream = BoundsXY(
    bounds=(ds["lon"].min(), ds["lat"].min(), ds["lon"].max(), ds["lat"].max()),
    source=image,
)
timeseries = hv.DynamicMap(plot_timeseries, streams=[bounds_stream])

# draw a black box on image (optional)
image *= hv.DynamicMap(lambda bounds: hv.Bounds(bounds), streams=[bounds_stream])

# layout (overlay, layout)
pn.Column(image + timeseries, slider).show()
2 Likes