Update Line Chart on Click event on hvplot map in a panel app

I have a world map hvplot image, and when I hover it I can observe the geographical coordinates being displayed. I would like to connect a click event on this map to a line chart below that can display the historical available data for this geographical coordinate. But I do not know how to connect such a click event from the hvplot chart. I was not able to find an example doing this in the documentation, and therefore I was wondering if anyone had a solution or a pointer where to look. Thank you!

Below is what I have so far:

import panel as pn
import os
import glob
import xarray as xr
import datetime
import pandas as pd
import hvplot.xarray

ds = xr.open_mfdataset(file_glob, engine="h5netcdf")

date_options = pd.to_datetime(ds.time.data)
date_dict = {d.strftime("%Y-%m-%d"): d.timestamp() for d in date_options}
min_date = ds.time.min().dt
max_date = ds.time.max().dt
t_slider = pn.widgets.DiscreteSlider(
    name="Time Selection",
    value = pd.Timestamp(datetime.date(min_date.year.item(), min_date.month.item(), min_date.day.item())).timestamp(),
    options=date_dict
)

# create a world map
def get_world_map(unix_time):
    """Plot world map from dataset."""
    return ds.sel(time=datetime.datetime.fromtimestamp(unix_time), method="nearest").sla.hvplot.image().opts(bgcolor="gray", cmap="rainbow", height=650,width=1300,data_aspect=1)


# define the layout of the app
world_map_row = pn.Row(
    pn.Column(title, t_slider),
    get_world_map(t_slider.value)
)

# watch for updates from slider to trigger world map update
def update_world_map_from_t_slider(event):
    """Update the world map from slider."""
    world_map_row[1].object = get_world_map(t_slider.value)

# lineplot for timeseries for coordinates
# def update_timeseries_from_click(event):

def get_timeseries():
    """Retrieve timeseries from dataset based on geographical coordinates."""
    return ds.sel(latitude=ds.latitude.median(), longitude=ds.longitude.median(), method="nearest").sla.hvplot.line()

timeseries_row = pn.Row(
    get_timeseries()
)

t_slider.param.watch(update_world_map_from_t_slider, "value")

world_map_row.servable(title="App")
timeseries_row.servable()

Hi @nilsleh

Welcome to the community.

To work with events like click events you would drop down into HoloViews. You would be looking to depend on streams of events. You can find the available streams
here. It is probably the Tap stream you are looking for. It can be found here.

Let us know if it works for you. I hope you will figure it out or someone from the community will help provide a working example for you.

Hi Marc,

Thank you for your reply. After some additional digging I found the following: Simple Panel example of map/time series interaction for data cube? which in essence describes what I am trying to achieve.

However, I have one additional issue that I am struggling with. In the above example, the Tap stream returns a x and y location, however my xarray has only an “index” dimension, where each item is associated with a timeseries of values and also has lat,lon coordinates. Therefore, to select the corresponding timeseries that I would like to display given the result of the tap stream, it would be easiest to index the xarray dataset with an index of points. However, hv.streams.Tap can only return x and y. I found Selection1D_tap but if I click on the map nothing happens, and it just returns the initial value.

So currently what I have tried is, so I have dropped the time slider and just try to plot a static image from which I can make selections through a stream.

ds = load_data()

# create a data array that holds index
loc_df = pd.DataFrame({"lat": ds.N_PROF.lat.data, "lon": ds.N_PROF.lon.data})

image = loc_df.hvplot.points(x="lon", y="lat", geo=True, color="red", alpha=0.4, tiles="ESRI").opts(height=650, width=1300)

# stream = hv.streams.Tap(source=image, index=0)
stream = hv.streams.Selection1D(source=image, index=[0])

def get_ds_timeseries(index):
    print(index)
    return index

# define layout of app
row = pn.Row(
    # pn.Column(image, pn.bind(get_ds_timeseries, x=stream.param.x, y=stream.param.y))
    pn.Column(image, pn.bind(get_ds_timeseries, index=stream.param.index))
)

row.servable(title="App")

Do you happen to have a suggestion or hint again?