How to write a JS Callback in Bokeh to obtain the identifier of a polygon (patch)

I am trying to generate an interactive plot using Bokeh. I have the following incomplete code

# data (data that we are going to interact in the plot)
src = ColumnDataSource(data = cbg[['cbg_id', 'color', 'alpha', 'selected']] )

# view 
view = CDSView(source = src, filters=[BooleanFilter(src.data['selected'])])

# callback 
callback = CustomJS( args = dict(source = src, view = view), code =
                    """
                    ...

                    // trigger update of data source
                    source.change.emit();

                      """)

# figure
p = figure(plot_height=500,
           plot_width=450,
           y_range = Range1d(min_northing, max_northing, bounds='auto'), 
           x_range = Range1d(min_easting, max_easting, bounds='auto'), 
           x_axis_type = 'mercator',
           y_axis_type = 'mercator',
           tools = 'pan, reset, save',
           match_aspect= True
)

# tools
hover = HoverTool(tooltips = [ ('cbg','@{cbg_id}')], mode='mouse')
box_zoom = BoxZoomTool(match_aspect= True)
wheel_zoom = WheelZoomTool(zoom_on_axis= False)
taptool = TapTool(callback = callback)
p.add_tools(hover, box_zoom, wheel_zoom, taptool)

# map
tile_provider = get_provider('OSM')
p.add_tile(tile_provider)

# polygons
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
cbgroups = p.patches('xs','ys', source = geo_src,
          fill_color = 'color', # 'blue'
          fill_alpha = 'alpha', # 0.2
          line_color = 'black',
          line_width = 0.25)

show(p)

cbg is a pandas dataframe where:

  • cbg_id is a numeral
  • color is text e.g. gray
  • alpha is value 0-1.
  • selected is boolean

I am trying to write the appropriate javascript callback that will allow me to store in a variable the cbg_id value of a patch(= polygon) after clicking on it with the mouse. Everything seems to work fine but I just stuck as to how to achieve this.

from bokeh.io import show
from bokeh.models import ColumnDataSource, CustomJS, TapTool
from bokeh.plotting import figure

ds = ColumnDataSource(data=dict(xs=[[0, 1, 1, 0], [1, 2, 2, 1]],
                                ys=[[0, 0, 1, 1], [1, 1, 2, 2]]))

callback = CustomJS(args=dict(), code="""\
const selected_rows_indices = cb_data.source.selected.indices;
console.log(selected_rows_indices);
""")

tt = TapTool(callback=callback)
p = figure(tools=[tt])
p.patches('xs', 'ys', source=ds)

show(p)

Thank you Eugene!

I had used ‘source.selected.indices;’ instead of ‘cb_data.source.selected.indices;’’

Building on your example. If ds looked something like,

ds = ColumnDataSource(data=dict(xs=[[0, 1, 1, 0], [1, 2, 2, 1]],
ys=[[0, 0, 1, 1], [1, 1, 2, 2]],
select=[false, false]))

So, each patch would have associated an attribute called ‘select’. Once a patch was selected with the taptool, the ‘select’ attribute would be updated from false’ to ‘true’. Finally, How would I access the patch index in Python (i.e. outside the javascript code)?
thanks again

You would have to run a Bokeh server application for that. When you use output_file and show, you are generating static, standalone HTML+JS output. The Python code process runs the code, generates the HTML+JS, and then exits completely out of the picture. The purpose of the Bokeh server is to generate output that can maintain a persistent connection to a real, running Python process (so that e.g. selections or other events can trigger Python callbacks to run).