pythonbokehbokehjs

How to define Python Bokeh RangeSlider.on_change callback function to alter IndexFilter for plots?


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)

Solution

  • Some notes:

    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)