I want to make an interactive graph. In the first graph, using the "tap" widget, the values "x" shown by the "hover" widget are selected, the values d(:,x) are plotted on the second graph. When you click the "tap" widget again to another place in Graph 1, graph 2 is updated. But I do not know how to write a callback to the "tap" widget. An example of what I am looking for is this
the presented code creates two graphs, but does not update the second graph:
import numpy as np
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.layouts import row
import os
from numpy.lib.function_base import select
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, TapTool
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
len_d=len(d)
plot1 = figure(tooltips=[("x", "$x{0}"), ("y", "$y"), ("value", "@image")],
plot_height=350, plot_width=400,title="Graph 1 d",tools = "tap")
plot2=figure(plot_height=350, plot_width=400, title="Graph 2 d(tap selection(x))",
tools="pan,box_select,crosshair,box_zoom,reset,save,wheel_zoom,hover")
#f = d[:,select]
lines = plot2.line(x = 'x', y = 'y', source = ColumnDataSource({'x': d[0] , 'y': -np.arange(len_d)}))
lines.visible = False
plot1.x_range.range_padding = plot1.y_range.range_padding = 0
data = dict(
image=[d], pattern=['smooth ramp'],
x=[0], y=[5], dw=[20], dh=[10]
)
code = '''if (cb_data.source.selected.indices.length > 0){
lines.visible = true;
var selected_index = cb_data.source.selected.indices[0];
lines.data_source.data['x'] = d[selected_index]
lines.data_source.change.emit();
}'''
cds = ColumnDataSource(data=data)
plot1.image(source=cds, x=0, y=-len(d), dw=len(d[0]), dh=len(d),palette='Spectral11', level="image")
plot1.grid.grid_line_width = 0.5
plot1.select(TapTool).callback = CustomJS(args = {'lines': lines, 'd': d}, code = code)
plots = row(plot1, plot2)
show(plots)
I really appreciate any help you can provide
Here is a solution which selects data from the image.
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_notebook, show
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CustomJS
output_notebook()
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
len_d=len(d)
data = dict(
image=[d], pattern=['smooth ramp'],
x=[0], y=[5], dw=[20], dh=[10]
)
# plot 1
cds_p1 = ColumnDataSource(data=data)
plot1 = figure(
plot_height=350,
plot_width=400,
title="Graph 1 d",
x_range =(0,500),
y_range =(-500, 0),
tooltips=[("x", "$x{0}")]
)
plot1.grid.grid_line_width = 0.5
plot1.image(
source=cds_p1,
x=0,
y=-len_d,
dw=len_d,
dh=len_d,
palette='Spectral11',
level="image"
)
# plot 2
y_index = -np.arange(len_d)
cds_p2 = ColumnDataSource(
{'x': d[0] , 'y':-np.arange(len_d) }
)
plot2=figure(
plot_height=350,
plot_width=400,
title="Graph 2 d(tap selection(x))",
x_range =(-1,1),
y_range =(-500, 0),
tools="")
plot2.line(x = 'x', y = 'y', source=cds_p2)
# important JS part
callback = CustomJS(
args=dict(cds_original=cds_p1, cds_p2=cds_p2),
code="""
// console.log('Tap event occurred at x-position: ' + cb_obj.x)
const data = cds_original.data
const next = cds_p2.data
var xpos = Math.round(cb_obj.x)
console.log((xpos-1)*500)
console.log(data['image'])
var x = []
for(var i=xpos; i<500*500; i+=500){
console.log(data['image'][0][i])
x.push(data['image'][0][i])
}
next['x'] = x
console.log(next)
cds_p2.change.emit()
""")
plot1.js_on_event('tap', callback)
plots = row(plot1, plot2)
show(plots)
I think this is not the final solution but maybe this brings you one step closer.