Search code examples
pythonholoviewsdatashader

Using Holoview, datashader plot dynamicmap but freeze


Basic needs: plot a map, and update accoding to variable. Challenges:

  • have to use rasterize, aggregation by datashade (big data)
  • holoviews .DynamicMap seems like not support rasterize.
import holoviews as hv, datashader as ds, geoviews as gv, geoviews.tile_sources as gvts
from holoviews.operation.datashader import datashade, rasterize, dynspread
import panel as pn

hv.extension('bokeh')
opts = dict(width=700, height=500, tools=['hover'], 
            colorbar=True,symmetric=True,clim=(-5,5),
            cmap='Spectral')
tiles = gvts.OSM.options(alpha=0.6)

def load_dh(var=None):
    points = gv.Points(df, kdims=['longitude','latitude'])
    return rasterize(points, x_sampling=0.01, y_sampling=0.01, aggregator=ds.mean(var)).options(**opts)* tiles

def on_var_select(event):
    var = event.obj.value
    col[-1] = load_dh(var=var)

var_select = pn.widgets.Select(name='var:', options=['dh_after_dtm10', '2', '3'])

var_select.param.watch(on_var_select, parameter_names=['value'])

col = pn.Column(var_select, load_dh(var_select.value))
col

The plot did show, but not update with changing variables:

images


I also try DynamicMap, which is not compataible with rasterize](Panel/Hvplot interaction when variable is changing):

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

Solution

  • The original code isn't runnable (try to show a fully reproducible example!), but when I made it runnable and removed GeoViews (not in my current envt), it seems to work:

    import holoviews as hv, datashader as ds, panel as pn, pandas as pd
    from holoviews.operation.datashader import datashade, rasterize, dynspread
    
    df = pd.DataFrame(dict(longitude=[70,80,90,100], latitude=[10,30,20,5],
                           c1=[1,0,9,7], c2=[5,2,0,1], c3=[3,1,3,8]))
    
    hv.extension('bokeh')
    opts = dict(width=700, height=500, tools=['hover'], 
                colorbar=True,symmetric=True,clim=(-5,5),
                cmap='Spectral')
    
    def load_dh(var=None):
        points = hv.Points(df, kdims=['longitude','latitude'])
        return dynspread(rasterize(points, x_sampling=0.01, y_sampling=0.01, aggregator=ds.mean(var))).options(**opts)
    
    def on_var_select(event):
        var = event.obj.value
        col[-1] = load_dh(var=var)
    
    var_select = pn.widgets.Select(name='var:', options=['c1', 'c2', 'c3'])
    
    var_select.param.watch(on_var_select, parameter_names=['value'])
    
    col = pn.Column(var_select, load_dh(var_select.value))
    col
    

    Screenshot1

    That said, it seems like an overly complex solution, when pn.bind can be used very straightforwardly for this:

    import holoviews as hv, datashader as ds, panel as pn, pandas as pd
    from holoviews.operation.datashader import datashade, rasterize, dynspread
    
    df = pd.DataFrame(dict(longitude=[70,80,90,100], latitude=[10,30,20,5],
                           c1=[1,0,9,7], c2=[5,2,0,1], c3=[3,1,3,8]))
    
    hv.extension('bokeh')
    opts = dict(width=700, height=500, tools=['hover'], 
                colorbar=True,symmetric=True,clim=(-5,5),
                cmap='Spectral')
    
    def load_dh(var=None):
        points = hv.Points(df, kdims=['longitude','latitude'])
        return dynspread(rasterize(points, x_sampling=0.01, y_sampling=0.01, aggregator=ds.mean(var))).options(**opts)
    
    var_select = pn.widgets.Select(name='var:', options=['c1', 'c2', 'c3'])
    
    col = pn.Column(var_select, pn.bind(load_dh, var=var_select))
    col
    

    Screenshot2

    Also, it's not so much that DynamicMap doesn't support rasterize as that the result of rasterize is already a DynamicMap, and only a single level of DynamicMap-ping is supported. It's always possible to make a single-level DynamicMap that does what you want, but because HoloViews operations (unlike DynamicMaps) do accept a DynamicMap as an argument, all you usually need to do is to call rasterize on your DynamicMap. So if you want to make a DynamicMap, just do so, then rasterize it.