Repository: bokeh/bokeh

Issue:
[BUG] HoverTool indices empty when using GlyphRenderer from a GraphRenderer
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 1.34
Python 3.7.4
Windows 10 / Linux
Firefox Dev / Chrome Dev

#### Description of expected behavior and the observed behavior

There seems to be some kind of problem with point detection in ```HoverTool``` depending on the source of the renderer it is linked up to.

With a renderer created by one of the high level ```bokeh.plotting``` methods like ```circle```, it works as expected. If I instead set the renderer to a ```GlyphRenderer``` from a manually constructed ```GraphRenderer```, JS callbacks will be triggered but the ```cb_data.index['1d'].indices``` array is never populated. Oddly enough, it *does* work if I add a second ```HoverTool``` to the same plot. 

The example below hopefully explains this more clearly. The hover callback on the first plot works fine, on the second one it never displays anything because the indices array is always empty. 

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import random

import bokeh
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CustomJS, HoverTool, GraphRenderer, StaticLayoutProvider, Circle
from bokeh.plotting import figure
from bokeh.io import show, output_file

POINTS = 25
testdata = {}
testdata['index'] = [i for i in range(POINTS)]
testdata['x'] = [random.random() for i in range(POINTS)]
testdata['y'] = [random.random() for i in range(POINTS)]
layout = {i: (testdata['x'][i], testdata['y'][i]) for i in range(POINTS)}

# just print out something to the JS console when hovering over points
callback_code = """
                    if (cb_data.index['1d'].indices.length > 0)
                        console.log('[%o], ndices = %o', src, cb_data.index['1d'].indices);
                """

# plot 1: create markers using .circle 
# => things work as expected
p_1 = figure(x_range=(-0.25, 1.25), y_range=(-0.25, 1.25), name='p_1', title='Hover working')
p_1.circle('x', 'y', source=ColumnDataSource(data=testdata), size=15, fill_color='#ff0000', line_color='#000000')

# plot 2: duplicate the plot using a manually constructed GraphRenderer instance
# => callback never triggered as cb_data.index structure never populated
p_2 = figure(x_range=(-0.25, 1.25), y_range=(-0.25, 1.25), name='p_2', title='Hover not working')
gr = GraphRenderer()
gr.node_renderer.data_source.data = ColumnDataSource(data=testdata).data
gr.node_renderer.glyph = Circle(size=15, fill_color='#ff0000', line_color='#000000')
gr.layout_provider = StaticLayoutProvider(graph_layout=layout)
p_2.renderers.append(gr)

# add a HoverTool instance to each plot, explicitly telling it to use the <GraphRenderer>.node_renderer for p_2
p_1.add_tools(HoverTool(tooltips=None, callback=CustomJS(args={'src': p_1.name}, code=callback_code)))
p_2.add_tools(HoverTool(tooltips=None, callback=CustomJS(args={'src': p_2.name}, code=callback_code), renderers=[gr.node_renderer]))

# uncommenting this line obviously adds a duplicate tool, but somehow makes the 
# callback on the original tool work properly??
# p_2.add_tools(HoverTool(tooltips=None))

output_file('hover.html')
show(row(p_1, p_2))
```



Discussion:
@andrewramsay We will have to look in to this when we can, but please not that the `['1d']` syntax has been deprecated for going on two years, and it will be removed entirely in the next few months. It should not be used at all. 

https://docs.bokeh.org/en/latest/docs/releases.html#release-0-12-15-migration
This may also be intended behavior. The Hover tool automatically applies itself to all standard glyph renderers if no explicit configuration is given. But GraphRenders are a compound of two separate edge and node renderers. It's not clear that automatically applying to *both* is a useful or desirable thing to do in most situations, in which case having to be explicit as you have done above, is simply the correct process. 
@bryevdv About ```['1d']```, fair enough, but if that syntax has been deprecated for so long then the [CustomJS for Hover example](https://bokeh.pydata.org/en/1.3.4/docs/user_guide/interaction/callbacks.html#customjs-for-hover) from the current version of the documentation really needs updating - that's what I was basing my code on! 

About the renderers, I wasn't suggesting that it was a bug/problem to need to supply them explicitly. I'd already come across #8347 after running into the same thing as described there and I understand the reasoning. This issue was only about what I thought was unexpected behaviour from the HoverTool in this scenario. It's not a major bug of course, just thought I'd report in case it saved anyone else from spending time trying to figure it out. 
@andrewramsay it definitely does need updating, I thought we had updated everywhere but we clearly missed that one. Would you be interested in making a PR to update it? The file in question is here:

https://github.com/bokeh/bokeh/blob/master/sphinx/source/docs/user_guide/examples/interaction_callbacks_for_hover.py

I'm inclined in this instance to simply make the docs more clear that by default hover only automatically adds "regular" glyphs and that it is necessary to be explicit with GraphRenderers. I do recall now that having other the node and edge renderers added automatically led to bad results and frustration because users almost always wanted one, but not the other. 
Sure, I guess I can do that in a day or two. Is there anything else about that example that you'd want to see updated at the same time or is it just the indices part? 
@andrewramsay sorry I missed this, just the indices part is fine, I think 

Resolving patch:
diff --git a/sphinx/source/docs/user_guide/examples/interaction_callbacks_for_hover.py b/sphinx/source/docs/user_guide/examples/interaction_callbacks_for_hover.py
--- a/sphinx/source/docs/user_guide/examples/interaction_callbacks_for_hover.py
+++ b/sphinx/source/docs/user_guide/examples/interaction_callbacks_for_hover.py
@@ -23,21 +23,20 @@
 
 # Add a hover tool, that sets the link data for a hovered circle
 code = """
-var links = %s;
-var data = {'x0': [], 'y0': [], 'x1': [], 'y1': []};
-var cdata = circle.data;
-var indices = cb_data.index['1d'].indices;
+const links = %s
+const data = {'x0': [], 'y0': [], 'x1': [], 'y1': []}
+const indices = cb_data.index.indices
 for (var i = 0; i < indices.length; i++) {
-    var ind0 = indices[i]
-    for (var j = 0; j < links[ind0].length; j++) {
-        var ind1 = links[ind0][j];
-        data['x0'].push(cdata.x[ind0]);
-        data['y0'].push(cdata.y[ind0]);
-        data['x1'].push(cdata.x[ind1]);
-        data['y1'].push(cdata.y[ind1]);
+    const start = indices[i]
+    for (var j = 0; j < links[start].length; j++) {
+        const end = links[start][j]
+        data['x0'].push(circle.data.x[start])
+        data['y0'].push(circle.data.y[start])
+        data['x1'].push(circle.data.x[end])
+        data['y1'].push(circle.data.y[end])
     }
 }
-segment.data = data;
+segment.data = data
 """ % links
 
 callback = CustomJS(args={'circle': cr.data_source, 'segment': sr.data_source}, code=code)


Test patch: