Search code examples
pythonholoviewspyviz

Panel/Hvplot interaction when variable is changing


I'm trying to create a dashboard with two holoviews objects: a panel pn.widgets.Select object that contains a list of xarray variables, and a hvplot object that takes the selected variable on input, like this:

def hvmesh(var=None):
    mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, 
       width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
    return mesh

Here's what an example mesh looks like for a particular variable (one that has both time and height dimensions): enter image description here

I would like to have the map update when I select a variable from the panel widget: enter image description here I tried to do this as a dynamic map, like this:

from holoviews.streams import Params
import holoviews as hv

var_stream = Params(var_select, ['value'], rename={'value': 'var'})

mesh = hv.DynamicMap(hvmesh, streams=[var_stream])

but when I try to display the map, I get:

Exception: Nesting a DynamicMap inside a DynamicMap is not supported.

It would seem a common need to select the variable for hvplot from a panel widget. What is the best way to accomplish this with pyviz?

In case it's useful, here is my full attempt Jupyter Notebook.


Solution

  • Because the groupby changes with each variable selected, a list of variables can not be passed to hvplot. So one solution is to just recreate the plot each time a new variable is selected. This works:

    import holoviews as hv
    from holoviews.streams import Params
    
    def plot(var=None, tiles=None):
        var = var or var_select.value
        tiles = tiles or map_select.value
        mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, title=var,
                                       width=600, height=400, groupby=list(ds[var].dims[:-2]), 
                                       cmap='jet')
        return mesh.opts(alpha=0.7) * tiles
    
    def on_var_select(event):
        var = event.obj.value
        col[-1] = plot(var=var)
    
    def on_map_select(event):
        tiles = event.obj.value
        col[-1] = plot(tiles=tiles)
    
    var_select.param.watch(on_var_select, parameter_names=['value']);
    map_select.param.watch(on_map_select, parameter_names=['value']);
    
    col = pn.Column(var_select, map_select, plot(var_select.value) * tiles)
    

    producing: enter image description here

    Here is the full notebook.