Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ImageStack hover using CustomJS tools #6015

Closed
droumis opened this issue Dec 7, 2023 · 2 comments
Closed

Implement ImageStack hover using CustomJS tools #6015

droumis opened this issue Dec 7, 2023 · 2 comments
Assignees
Labels
TRIAGE Needs triaging

Comments

@droumis
Copy link
Member

droumis commented Dec 7, 2023

This is a WIP issue to track the investigation of whether HoverTool + CustomJSHover is sufficient to perform the necessary lookups and format all the information in the categorical aggregates and any additional 2D layers that are sent to the frontend by the plot.

@hoxbro, please edit this issue to fill in the details and findings

@droumis droumis added the TRIAGE Needs triaging label Dec 7, 2023
@hoxbro
Copy link
Member

hoxbro commented Dec 13, 2023

I have here a rough implementation of a CustomJSHover tool which takes the layers from ImageStack and converts them to different rows.

Code HTML
import holoviews as hv
import numpy as np
import panel as pn
from bokeh.models import CustomJSHover, HoverTool

hv.extension("bokeh")
pn.extension()

x = np.arange(0, 3)
y = np.arange(5, 8)
a = np.array([[np.nan, np.nan, 1], [np.nan] * 3, [np.nan] * 3])
b = np.array([[np.nan] * 3, [1, 1, np.nan], [np.nan] * 3])
c = np.array([[np.nan] * 3, [np.nan] * 3, [1, 1, 1]])

img_stack = hv.ImageStack((x, y, a, b, c), kdims=["x", "y"], vdims=["a", "b", "c"])


formatter = CustomJSHover(
    code="""
    const i = special_vars["image_index"]["i"]
    const j = special_vars["image_index"]["j"]
    const offset = value.shape[0] * value.shape[1] * format
    const index = (j + i * value.shape[1] + offset)
    if (isNaN(value[index]) ) {
        return "-"
    } else {
        return value[index].toString()
    }
    """,
)

html_tooltip = """
<div style="display:table;border-spacing:2px">
    <table>
        <tr style="display;table-row">
            <td style="display:table-cell", class="bk-tooltip-row-label">x: </td>
            <td class="bk-tooltip-row-value">$x</td>
        </tr>
        <tr style="display;table-row">
            <td style="display:table-cell" class="bk-tooltip-row-label">y: </td>
            <td class="bk-tooltip-row-value">$y</td>
        </tr>
        <tr style="display;table-row">
            <td style="display:table-cell" class="bk-tooltip-row-label">a: </td>
            <td class="bk-tooltip-row-value">@image{0}</td>
        </tr>
        <tr style="display;table-row">
            <td style="display:table-cell" class="bk-tooltip-row-label">b: </td>
            <td class="bk-tooltip-row-value">@image{1}</td>
        </tr>
        <tr style="display;table-row">
            <td style="display:table-cell" class="bk-tooltip-row-label">c: </td>
            <td class="bk-tooltip-row-value">@image{2}</td>
        </tr>
    </table>
</div>
"""

hover = HoverTool(tooltips=html_tooltip, formatters={"@image": formatter})

img_stack.opts(tools=[hover])

image

I would like to be able to use Bokeh's Template to at least be able to create the tooltips part of the HoverTool. This will make it possible to construct more advanced cases without diving into HTML. I don't have any problem with using CustomJSHover.

Code Template
import holoviews as hv
import numpy as np
import panel as pn
from bokeh.models import CustomJSHover, HoverTool
from bokeh.models.dom import Div, Styles, Template, ValueRef
hv.extension("bokeh")
pn.extension()

x = np.arange(0, 3)
y = np.arange(5, 8)
a = np.array([[np.nan, np.nan, 1], [np.nan] * 3, [np.nan] * 3])
b = np.array([[np.nan] * 3, [1, 1, np.nan], [np.nan] * 3])
c = np.array([[np.nan] * 3, [np.nan] * 3, [1, 1, 1]])

img_stack = hv.ImageStack((x, y, a, b, c), kdims=["x", "y"], vdims=["a", "b", "c"])


formatter = CustomJSHover(
    code="""
    const i = special_vars["image_index"]["i"]
    const j = special_vars["image_index"]["j"]
    const offset = value.shape[0] * value.shape[1] * format
    const index = (j + i * value.shape[1] + offset)
    if (isNaN(value[index]) ) {
        return "-"
    } else {
        return value[index].toString()
    }
    """,
)


def tooltips():
    style = Styles(
        display="grid",
        grid_template_columns="auto auto",
        column_gap="10px",
    )
    grid = Div(style=style)
    grid.children = [
        "a",
        ValueRef(field="image{0}"),
        "b",
        ValueRef(field="image{1}"),
        "c",
        ValueRef(field="image{2}"),
    ]
    return Template(children=[grid])


hover = HoverTool(
    tooltips=tooltips(),
    formatters={"image": formatter},
)

img_stack.opts(tools=[hover])

pn.panel(img_stack).servable()

image

@droumis droumis changed the title WIP: Implement ImageStack hover using CustomJS tools Implement ImageStack hover using CustomJS tools Jan 8, 2024
@droumis
Copy link
Member Author

droumis commented Apr 22, 2024

bokeh/bokeh#13650 seems to be almost done, but it also isn't blocking. So I'll close this as solved since HoverTool + CustomJSHover appears sufficient to perform the necessary lookups and format the information in the categorical aggregates, albeit by diving into the HTML.

@droumis droumis closed this as completed Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TRIAGE Needs triaging
Projects
None yet
Development

No branches or pull requests

2 participants