Updating Image Array in ColumnDataSource with a CustomJS callback in 2.x.x

Expected behavior

In Bokeh 1.4.0, I have a CustomJS callback function that works well but does not work in 2.x.x. In the callback, I am summing detector signals stored in a ColumnDataSource and updating the displayed image using a callback triggered by a CheckboxButtonGroup.

Observed behavior

In 2.x.x when the callback executes the source.change.emit() line I get an Uncaught RangeError: “Maximum call stack size exceeded” in the js console. It appears that the typed array for Bokeh has changed, but I don’t know how to effect the same behaviour in this version. i.e. I don’t know how to construct an appropriate array for the update.

Example code

import numpy as np
from bokeh.plotting import Figure, show, output_notebook()
from bokeh.models import ColumnDataSource, CheckboxButtonGroup, CustomJS
from bokeh.layouts import column

output_notebook()

det1 = np.zeros((1000,256))
np.fill_diagonal(det1, 5)
det2 = np.empty((256, 1000))
np.fill_diagonal(det2, 3)

data = {'x': np.linspace(270, 320, 1000), 
        'y': np.linspace(0, 2560, 256),
        'image': det1,
        'det1': det1,
        'det2': det2,
        'det3': np.ones((1000, 256)),
        'det4': np.zeros((1000, 256)) -0.5
       }


delta = max(data['x']) - min(data['x'])
bins = max(data['y']) - min(data['y'])
source = ColumnDataSource(dict(image=[data['image'].T],
                               det1=[data['det1'].T],
                               det2=[data['det2']],
                               det3=[data['det3'].T],
                               det4=[data['det4'].T],
                               x=[min(data['x'])],
                               y=[min(data['y'])],
                               delta=[delta],
                               bins=[bins]))
plot = Figure(plot_width=600, plot_height=600, tools="box_select,save,box_zoom, wheel_zoom,hover,pan,reset")

plot.image(image='image', y='y', x='x', dh='bins', dw='delta', source=source,
                palette="Spectral11")



select = CheckboxButtonGroup(name="Detector Select:", labels=['1', '2', '3', '4'], active=[0])

callback_str = """
var d1 = source.data['det1'][0];
var d2 = source.data['det2'][0];
var d3 = source.data['det3'][0];
var d4 = source.data['det4'][0];
var d = source.data['image'];
var sum = new Array();

function sumArrays(...arrays) {
  const n = arrays.reduce((max, xs) => Math.max(max, xs.length), 0);
  const result = Float64Array.from({ length: n });
  return result.map((_, i) => arrays.map(xs => xs[i] || 0).reduce((sum, x) => sum + x, 0));
}

var f = cb_obj.active;
if (f.indexOf(0) > -1) {
    sum.push(d1);
}
if (f.indexOf(1) > -1) {
    sum.push(d2);
}
if (f.indexOf(2) > -1) {
    sum.push(d3);
}
if (f.indexOf(3) > -1) {
    sum.push(d4);
}
d[0] = sumArrays(...sum);

//Everything above works fine, error occurs during update of source.data['image'].
source.change.emit();
"""

select_callback = CustomJS(args=dict(source=source), code=callback_str)

select.js_on_change('active', select_callback)
layout = column(select, plot)
show(layout)

Posted on StackOverflow