I’m trying to implement a python callback function for a RangeSlider. The Slider Value should tell which Index a IndexFilter should get for display.
For example: If rangeslider.value is (3, 25) my plots should only contain/view data with the Index from 3 to 25.
from bokeh.io import output_file, show from bokeh.models import ColumnDataSource, GMapOptions, CustomJS, CDSView, IndexFilter from bokeh.plotting import gmap, ColumnDataSource, figure from bokeh.layouts import column, row from bokeh.models.widgets import RangeSlider import numpy as np def slider_callback(attr, old, new): p.view = CDSView(source=source, filters=[IndexFilter(np.arange(new.value[0], new.value[1]))]) v.view = CDSView(source=source, filters=[IndexFilter(np.arange(new.value[0], new.value[1]))]) # data set lon = [[48.7886, 48.7887, 48.7888, 48.7889, 48.789], [48.7876, 48.7877, 48.78878, 48.7879, 48.787], [48.7866, 48.7867, 48.7868, 48.7869, 48.786], [48.7856, 48.7857, 48.7858, 48.7859, 48.785], [48.7846, 48.7847, 48.7848, 48.7849, 48.784]] lat = [[8.92, 8.921, 8.922, 8.923, 8.924], [8.91, 8.911, 8.912, 8.913, 8.914], [8.90, 8.901, 8.902, 8.903, 8.904], [8.89, 8.891, 8.892, 8.893, 8.894], [8.88, 8.881, 8.882, 8.883, 8.884]] time = [0, 1, 2, 3, 4, 5] velocity = [23, 24, 25, 24, 20] lenght_dataset = len(lon) # define source and map source = ColumnDataSource(data = {'x': lon, 'y': lat, 't': time, 'v': velocity}) view = CDSView(source=source, filters=[IndexFilter(np.arange(0, lenght_dataset))]) map_options = GMapOptions(lat=48.7886, lng=8.92, map_type="satellite", zoom=13) p = gmap("MY_API_KEY", map_options, title="Trajectory Map") v = figure(plot_width=400, plot_height=400, title="Velocity") # plot lines on map p.multi_line('y', 'x', view=view, source=source, line_width=1) v.line('t', 'v', view=view, source=source, line_width=3) # slider to limit plotted data range_slider = RangeSlider(title="Data Range Slider: ", start=0, end=lenght_dataset, value=(0, lenght_dataset), step=1) range_slider.on_change('value', slider_callback) # Layout to plot and output layout = row(column(p, range_slider), column(v) ) output_file("diag_plot_bike_data.html") show(layout)
Advertisement
Answer
Some notes:
time
is longer than the rest of the columns – you will receive a warning about it. In my code below, I just removed its last elementview
with filters in general should not be used for continuous glyphs like lines (v.line
in particular –multi_line
is fine). You will receive a warning about it. But if the indices inIndexFilter
are always continuous, then you should be fine. Either way, you can use the segment glyph to avoid the warning- In your callback, you’re trying to set view on the figures – views only exist on glyph renderers
- In general, you don’t want to recreate views, you want to recreate as few Bokeh models as possible. Ideally, you would have to just change the
indices
field of the filter. But there’s some missing wiring in Bokeh, so you will have to set thefilters
field of the view, as below new
argument of Python callbacks receives the new value for the attribute passed as the first parameter to the correspondingon_change
call. In this case, it will be a tuple, so instead ofnew.value[0]
you should usenew[0]
- Since you’ve decided to use Python callbacks, you can no longer use
show
and have a static HTML file – you will have to usecurdoc().add_root
andbokeh serve
. The UI needs that Python code to run somewhere in runtime - When changing the slider values, you will notice that the separate segments of
multi_line
will be joined together – it’s a bug and I just created https://github.com/bokeh/bokeh/issues/10589 for it
Here’s a working example:
from bokeh.io import curdoc from bokeh.layouts import column, row from bokeh.models import GMapOptions, CDSView, IndexFilter from bokeh.models.widgets import RangeSlider from bokeh.plotting import gmap, ColumnDataSource, figure lon = [[48.7886, 48.7887, 48.7888, 48.7889, 48.789], [48.7876, 48.7877, 48.78878, 48.7879, 48.787], [48.7866, 48.7867, 48.7868, 48.7869, 48.786], [48.7856, 48.7857, 48.7858, 48.7859, 48.785], [48.7846, 48.7847, 48.7848, 48.7849, 48.784]] lat = [[8.92, 8.921, 8.922, 8.923, 8.924], [8.91, 8.911, 8.912, 8.913, 8.914], [8.90, 8.901, 8.902, 8.903, 8.904], [8.89, 8.891, 8.892, 8.893, 8.894], [8.88, 8.881, 8.882, 8.883, 8.884]] time = [0, 1, 2, 3, 4] velocity = [23, 24, 25, 24, 20] lenght_dataset = len(lon) # define source and map source = ColumnDataSource(data={'x': lon, 'y': lat, 't': time, 'v': velocity}) view = CDSView(source=source, filters=[IndexFilter(list(range(lenght_dataset)))]) map_options = GMapOptions(lat=48.7886, lng=8.92, map_type="satellite", zoom=13) p = gmap("API_KEY", map_options, title="Trajectory Map") v = figure(plot_width=400, plot_height=400, title="Velocity") p.multi_line('y', 'x', view=view, source=source, line_width=1) v.line('t', 'v', view=view, source=source, line_width=3) range_slider = RangeSlider(title="Data Range Slider: ", start=0, end=lenght_dataset, value=(0, lenght_dataset), step=1) def slider_callback(attr, old, new): view.filters = [IndexFilter(list(range(*new)))] range_slider.on_change('value', slider_callback) layout = row(column(p, range_slider), column(v)) curdoc().add_root(layout)