Holoviews to HoloViews

Interacting between HoloViews objects.

Pros:

  • Dynamically generate values

  • All in Python

Cons:

  • Interactivity may not be retained when saved

  • May require backend server

References:

Tap for update plot

Server required.

[7]:
import xarray as xr
import holoviews as hv

hv.extension("bokeh")

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(0, 3))
coord_stream = hv.streams.Tap(x=-88 + 360, y=40)
time_stream = hv.streams.Tap(x=ds["time"].values[0])

def create_geomap(x, y):
    ds_sel = ds.sel(time=x, method="nearest")
    return hv.Image(ds_sel, ["lon", "lat"], ["air"]).opts(
        "Image", title=str(x)[:16], tools=["tap", "hover"])

def create_timeseries(x, y):
    ds_sel = ds.sel(lon=x, lat=y, method="nearest")
    return hv.Curve(ds_sel, ["time"], ["air"]).opts(
        "Curve", title=f"{x:.1f}, {y:.1f}", framewise=True, tools=["tap", "hover"])

def create_point(x, y):
    return hv.Points((x, y)).opts("Points", color="red", marker="x", size=25)

def create_vline(x, y):
    return hv.VLine(x).opts("VLine", color="red")

# create base plots
geomap = hv.DynamicMap(create_geomap, streams=[time_stream])
timeseries = hv.DynamicMap(create_timeseries, streams=[coord_stream])

# link source
coord_stream.source = geomap
time_stream.source = timeseries

# create annotations
coord_point = hv.DynamicMap(create_point, streams=[coord_stream])
time_vline = hv.DynamicMap(create_vline, streams=[time_stream])

# overlay + layout
layout = geomap * coord_point + timeseries * time_vline
[3]:
import panel as pn
pn.extension()
pn.pane.GIF("tap_for_update_plot.gif")
[3]:

Pointer Y for cross section (Hovmueller)

[4]:
import xarray as xr
import holoviews as hv

hv.extension("bokeh")

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(0, 5))

def create_xsection(y):
    ds_sel = ds.sel(lat=y, method="nearest")
    return hv.Image(ds_sel, ["lon", "time"], ["air"])

def create_hline(y):
    return hv.HLine(y).opts("HLine", color="red")

geomap = hv.Image(ds.isel(time=0), ["lon", "lat"], ["air"]).opts(
    "Image", tools=["hover"])

lat_stream = hv.streams.PointerY(y=40, source=geomap)
xsection = hv.DynamicMap(create_xsection, streams=[lat_stream])

# create annotations
lat_hline = hv.DynamicMap(create_hline, streams=[lat_stream])

# overlay + layout
layout = geomap * lat_hline + xsection
[5]:
import panel as pn
pn.extension()
pn.pane.GIF("pointer_y_for_cross_section.gif")
[5]:

Pointer X for violin distribution

[6]:
import xarray as xr
import holoviews as hv

hv.extension("bokeh")

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(0, 10))
ds = ds.coarsen(lat=3, lon=3, boundary="trim").mean()

def create_violin(x):
    ds_sel = ds.sel(lon=x, method="nearest").to_dataframe()
    return hv.Violin(ds_sel, ["lat"], ["air"]).opts(invert_axes=True)

def create_vline(x):
    return hv.VLine(x).opts("VLine", color="red")

geomap = hv.Image(ds.isel(time=0), ["lon", "lat"], ["air"]).opts(
    "Image", tools=["hover"])

lon_stream = hv.streams.PointerX(x=40, source=geomap)
violin = hv.DynamicMap(create_violin, streams=[lon_stream])

# create annotations
lon_vline = hv.DynamicMap(create_vline, streams=[lon_stream])

# overlay + layout
layout = geomap * lon_vline + violin
[7]:
import panel as pn
pn.extension()
pn.pane.GIF("pointer_x_for_violin_distribution.gif")
[7]:

Bounds x for subset x-axis limits

Works with overlays.

[8]:
import holoviews as hv

hv.extension("bokeh")

x = [0, 1, 2]
y = [3, 4, 5]

source = hv.Curve((x, y)).opts(
    "Curve", width=500, height=125, labelled=["y"], default_tools=["xbox_select"]
)

stream = hv.streams.BoundsX(source=source, boundsx=(0, 2))
target = (hv.Curve((x, y)) * hv.Scatter((x, y)))

opts = dict(
    axiswise=True,
    framewise=True,
    width=500,
    height=250,
    default_tools=["xwheel_zoom"],
    active_tools=["xwheel_zoom"]
)
target = target.opts({"Curve": opts, "Scatter": opts, "Overlay": opts})
target = target.apply.opts(xlim=stream.param.boundsx)

layout = (source + target).opts(merge_tools=False).cols(1)
[9]:
import panel as pn
pn.extension()
pn.pane.GIF("bounds_x_for_subset_x_axis_limits.gif")
[9]: