Using pandas Dataframe as hover tooltips data source for an hv.Image

Using github copilot I managed to come to this example code

import pandas as pd
import numpy as np
import holoviews as hv
from bokeh.models import HoverTool, CustomJS

x_vals = np.linspace(-10, 10, 100)
y_vals = np.linspace(-10, 10, 100)
X, Y = np.meshgrid(x_vals, y_vals)
Z = np.sin(np.sqrt(X**2 + Y**2))

gradient = np.gradient(Z)
category = np.where(Z > 0, 'Positive', 'Negative')

data = {'x':x_vals, 'y':y_vals, 'z':Z.T, 
        'gradient_x':gradient[0].T,
        'gradient_y':gradient[1].T,
        'category':category.T}

image = hv.Image((x_vals, y_vals, Z), vdims=['z'])

def add_hover_callback(plot, element):
    bokeh_plot = plot.state
    data_source = bokeh_plot.renderers[0].data_source

    callback = CustomJS(args=dict(source=data_source, data=data), code="""
        const x = cb_data.geometry.x;
        const y = cb_data.geometry.y;

        const xs = data.x;
        const ys = data.y;
        const zs = data.z;
        const gradients_x = data.gradient_x;
        const gradients_y = data.gradient_y;
        const categories = data.category;

        let closestXIndex = -1;
        let closestYIndex = -1;
        let minDistanceX = Infinity;
        let minDistanceY = Infinity;
                        
        for (let i = 0; i < xs.length; i++) {
            const dx = Math.abs(xs[i] - x);
            if (dx < minDistanceX) {
                minDistanceX = dx;
                closestXIndex = i;  
            }
        }
                        
        for (let j = 0; j < ys.length; j++) {
            const dy = Math.abs(ys[i] - y);
            if (dy < minDistanceY) {
                minDistanceY = dy;
                closestYIndex = j;  
            }
        }
                        
        if (closestXIndex !== -1 && closestYIndex !== -1) {
            source.data['x'] = [xs[closestXIndex]];
            source.data['y'] = [ys[closestYIndex]];
            source.data['image'] = [zs[closestXIndex][closestYIndex]]*100.0;
            source.data['gradient_x'] = [gradients_x[closestXIndex][closestYIndex]];
            source.data['gradient_y'] = [gradients_y[closestXIndex][closestYIndex]];
            source.data['category'] = [categories[closestXIndex][closestYIndex]];
            source.change.emit();
        }
        
    """)
    hover = HoverTool(
        tooltips=[
            ("X", "$x"),
            ("Y", "$y"),
            ("Z", "@image"),
            ("Gradient X", "@gradient_x"),
            ("Gradient Y", "@gradient_y"),
            ("Category", "@category")
        ],
        callback=callback,
        mode='mouse'
    )
    bokeh_plot.add_tools(hover)

image.opts(hooks=[add_hover_callback], tools=['hover'])

hv.output(image)

The problem is that the callback appear to not be working, since only the ā€œ@imageā€ tag appears to be working with the original values and not with the modified ones (scaled by 100) and also the other categories only appears as ???.

Any help would be appreciated

If I’m not mistaken, hover tooltips are supported using pd.DataFrame:
https://hvplot.holoviz.org/reference/tabular/heatmap.html

Does that help?