Why does updating data of a pipe not trigger an update of the corresponding range?

In Bokeh any data update on a ColumnDataSource does trigger an update of the range. Using Pipe and datashading no update occurs:

# dummy timeseries data
n_samples = 10000
data_ts = np.empty((n_samples,2))
data_ts[:,0] = np.arange(0,n_samples,1)
data_ts[:,1] = np.random.rand(n_samples)
data_ts.astype(np.float16)
time_s = pd.DataFrame(data_ts, columns=["t","x"])

pipe = Pipe(data=[])
#ts_curve = hv.Curve((time_s['t'], time_s['x']), kdims=['Time'], vdims=['Value'])
ts_dmap = hv.DynamicMap(hv.Curve, streams=[pipe]).opts(framewise=True) 
ts = datashade(ts_dmap, cnorm='linear', interpolation = "default", aggregator=ds.count()).opts(framewise=True)

pipe.send(time_s.iloc[:1000,:])


def update_data():
    pipe.send(time_s) # range is NOT updated here
    #manual update works:
    #ts.streams[1].x_range = (0.0, 10000)

Hi @maxxor

Welcome to the community. Can you update your example to be self contained? So it can be copied and pasted to run. Right now the example is missing the module import and the function update_data is not being called.

It seems like I can no longer edit the example code, so here is a full example which can be run via bokeh serve --show example_file_name.py

import numpy as np
import panel as pn
import datashader as ds
import datashader.transfer_functions as tf
from collections import OrderedDict as odict, OrderedDict
import holoviews as hv
import holoviews.plotting.bokeh
from holoviews.operation.datashader import datashade
from bokeh.layouts import row
from bokeh.models import Button, ColumnDataSource, CDSView, DataTable, IndexFilter, TableColumn
from bokeh.plotting import curdoc, figure
from bokeh.layouts import layout
from holoviews import opts
from holoviews.operation.datashader import datashade, shade, dynspread, spread, rasterize
from holoviews.streams import Pipe

hv.extension('bokeh')
renderer = hv.renderer('bokeh').instance(mode='server')
renderer.webgl = True
renderer.theme = None

doc = curdoc()

# dummy timeseries data
n_samples = 50000
data_ts = np.empty((n_samples,2))
data_ts[:,0] = np.arange(0,n_samples,1)
data_ts[:,1] = np.random.rand(n_samples)
data_ts.astype(np.float16)
time_s = pd.DataFrame(data_ts, columns=["t","x"])

pipe = Pipe(data=[])

ts_dmap = hv.DynamicMap(hv.Curve, streams=[pipe]).opts(framewise=True) #.redim.range(t=(0,500))
ts = datashade(ts_dmap, cnorm='linear', interpolation = "default", aggregator=ds.count()).opts(framewise=True) #streams=[pipe],

pipe.send(time_s.iloc[:1000,:])


def update_data():
    #manual range update on RangeXY streams works
    #ts.streams[1].x_range = (time_s['t'].min(), time_s['t'].max())
    #ts.streams[1].y_range = (time_s['x'].min(), time_s['x'].max())
    pipe.send(time_s)



hvplot = renderer.get_plot_state(ts, doc, width=500, height=500)
button = Button(label='Update', width=60)
button.on_click(update_data)
layout = layout([[button, hvplot]], sizing_mode='fixed') #, hvplot.state
doc.add_root(layout)

I have reduced the example to the following (basically removed the Bokeh part of your example) and running it with panel serve and I see the problem you are describing. I have a suspicion it could be related to holoviews #5158.

import datashader as ds
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
from holoviews.operation.datashader import datashade
from holoviews.streams import Pipe

hv.extension("bokeh")

# dummy timeseries data
df = pd.DataFrame({"t": np.arange(500), "x": np.random.rand(500)})

pipe = Pipe(data=[])
ts_dmap = hv.DynamicMap(hv.Curve, streams=[pipe]).opts(framewise=True)
ts = datashade(
    ts_dmap, cnorm="linear", interpolation="default", aggregator=ds.count()
).opts(framewise=True)
pipe.send(df.iloc[:10, :])


def update_data(event):
    # manual range update on RangeXY streams works
    # ts.streams[1].x_range = (df['t'].min(), df['t'].max())
    # ts.streams[1].y_range = (df['x'].min(), df['x'].max())
    pipe.send(df)


button = pn.widgets.Button(name="Update")
button.on_click(update_data)
pn.Column(button, ts).servable()