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()