0

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)
Pm740
  • 339
  • 2
  • 12

1 Answers1

1

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 element
  • view 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 in IndexFilter 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 the filters 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 corresponding on_change call. In this case, it will be a tuple, so instead of new.value[0] you should use new[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 use curdoc().add_root and bokeh 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)
Eugene Pakhomov
  • 9,309
  • 3
  • 27
  • 53
  • Thank you very much! Now I understand what I implemented wrong. This bug is very annoying... do you know how long it usually takes until somthing like this gets fixed? Also my gmap won't work anymore, because there is no JS involved whatsoever. But I'll issue a new question regarding this problem. – Pm740 Oct 21 '20 at 09:00
  • Nevermind about the gmap issue. I simply forgot to enter my google api key... :D – Pm740 Oct 21 '20 at 09:09
  • 1
    "do you know how long it usually takes until somthing like this gets fixed" - the times vary greatly, I cannot really say. – Eugene Pakhomov Oct 21 '20 at 09:22
  • Ok thank you! This bug is a dealbreaker for the work I try to do. Since u are very adept with Bokeh: do you know any workaround? – Pm740 Oct 21 '20 at 09:40
  • 1
    There are some, but they're cumbersome. But actually the issue has already been fixed by https://github.com/bokeh/bokeh/pull/10590. You can either install Bokeh from GitHub by following https://docs.bokeh.org/en/latest/docs/dev_guide/setup.html#devguide-setup or wait up to a month for the next development version to be released. – Eugene Pakhomov Oct 21 '20 at 14:20