Skip to content

Hover tooltips for Patch and Areas#11992

Merged
mattpap merged 7 commits intobokeh:branch-3.0from
xaptronic:9182-tooltip-patch-area
Mar 4, 2022
Merged

Hover tooltips for Patch and Areas#11992
mattpap merged 7 commits intobokeh:branch-3.0from
xaptronic:9182-tooltip-patch-area

Conversation

@xaptronic
Copy link
Copy Markdown
Contributor

This PR adds tooltip support for Patch and Area glyphs. Hit testing for both glyphs was already added, and so this PR is largely updating the handling of the hit test result to determine what to show in the tooltip.

Considerations

Based on initial discussions and follow up via Slack there are possibly some different ideas about how this could be implemented and what use cases would be addressed.

My use case relates to viewing stacked time series data, specifically using a VArea. One example is viewing web traffic to different marketing channels overtime and being able to stack the data for each channel so you can visually see the changes in proportion of time. Being able to then hover and see extra contextual information - value of the slice, channel name, % total, total value etc makes for a great view.

In order to accomplish this, the PR changes the hit test of HArea and VArea such that not only does it hit test if the mouse is anywhere inside the entire area, it will hit test when the mouse is within the bounds of any segment of said polygon - segment being a set of coordinates, (x1, y1), (x1, y2), (x2, y1), (x2, y2) and then reporting the index of the hit. That way the user can show data values from the CDS in the tool tip.

After some discussion, it was agreed that we would like to solve for Patch as well as areas. Patch is different from Area in that it's data points are truly arbitrary and I think it would be ambiguous to define where to index into a CDS in that scenario. For those reasons, the hover tool changes for Patch simply show "special vars" ($name, $x,etc) or simply fixed text.

Example Code

VArea

from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource

ds = ColumnDataSource(data=dict(
    x=[1,2,3,4,5],
    y=[1,3,2,4,3],
    y2=[3,5,4,6,5],
    desc=['A','b','C','d','E'],
))

p = figure(width=400, height=400, tooltips=[('index', '@desc')], title="Data xTreme")

p.varea('x','y','y2',source=ds)

curdoc().add_root(p)

HArea


from bokeh.io import curdoc, show
from bokeh.models import ColumnDataSource, Grid, HArea,\
    HoverTool, LinearAxis, Plot

N = 30
y = np.linspace(-2, 3, N)
x1 = np.zeros(N)
x2 = 10 - y**2

source = ColumnDataSource(dict(x1=x1, x2=x2, y=y))

plot = Plot(
    title=None, width=300, height=300,
    min_border=0, toolbar_location=None)

glyph = HArea(x1="x1", x2="x2", y="y", fill_color="#f46d43")
renderer = plot.add_glyph(source, glyph)

renderer.name = 'varea'
plot.add_tools(HoverTool(
    renderers=[renderer],
    tooltips=[
        ('index', '$index'),
        ('name', '$name'),
        ('x', '$x'),
        ('y', '$y'),
        ('x1', '@x1'),
        ('x2', '@x2'),
    ]
))

xaxis = LinearAxis()
plot.add_layout(xaxis, 'below')

yaxis = LinearAxis()
plot.add_layout(yaxis, 'left')

plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

curdoc().add_root(plot)

Patch

import numpy as np

from bokeh.io import curdoc, show
from bokeh.models import ColumnDataSource, Grid, LinearAxis, Patch, Plot, HoverTool

N = 30
x1 = np.linspace(-2, 2, N)
x2 = x1[::-1]
y1 = x1**2
y2 = x2**2 + (x2+2.2)
x = np.hstack((x1, x2))
y = np.hstack((y1, y2))

source = ColumnDataSource(dict(x=x, y=y))
plot = Plot(
    title=None, width=300, height=300,
    min_border=0, toolbar_location=None)

glyph = Patch(x="x", y="y", fill_color="#a6cee3", name='patch!')
renderer = plot.add_glyph(source, glyph)
renderer.name = "testing name"
plot.add_tools(HoverTool(
    renderers=[renderer],
    tooltips=[
        ('x', '$x'),
        ('y', '$y'),
        ('name', '$name'),
        ('custom', 'you are hovering the patch')
    ]
))


xaxis = LinearAxis()
plot.add_layout(xaxis, 'below')

yaxis = LinearAxis()
plot.add_layout(yaxis, 'left')

plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

curdoc().add_root(plot)

Appreciate some feedback on the approach as well as some pointers on how this is coded - cc @bryevdv @mattpap

Cheers!

result.view = this
for (let i = 0, end = L-1; i < end; i++) {
const sx = to_screen([this.sx1[i], this.sx1[i+1], this.sx2[i+1], this.sx2[i]])
const sy = to_screen([this.sy[i], this.sy[i+1], this.sy[i+1], this.sy[i]])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xaptronic the "s" in sx etc denotes "screen", these values should already be in screen coordinates. Are you seeing something different?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was making use of code in another area - I'll take a second look at this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - yep, that was not needed - originally I thought that the ScreenArray was required in hittest.point_in_poly - thanks for pointing that out.

@xaptronic xaptronic force-pushed the 9182-tooltip-patch-area branch from f934be6 to 19fc241 Compare February 12, 2022 19:40
@xaptronic
Copy link
Copy Markdown
Contributor Author

I think the more interesting piece of this is what goes in hover_tool.ts:_update; would it be preferable to try and merge this somehow with the LineView handling, or perhaps another approach could be to collapse line_indices back into indices and maybe have this call out to separate methods to handle each type of glyph view object?

@mattpap mattpap added this to the 3.0 milestone Feb 17, 2022
@bryevdv
Copy link
Copy Markdown
Member

bryevdv commented Feb 28, 2022

collapse line_indices back into indices and maybe have this call out to separate methods to handle each type of glyph view object?

We cant' do that, they are recorded differently because they are not the same. A (poly)line has one more points than segments (at least one more, but possibly even more than that if nans are present) so the indices necessarily mean different things. It's also possible to hit a line's segment without hitting any of its points, and vice versa, as well as every combination in between. indices and line_indices are distinct so that all the possibilities can be differentiated.

_update; would it be preferable to try and merge this somehow with the LineView handling.

All the hovering code in general is due for a re-factor and cleanup, but I have not had an opportunity to give it any thought personally. If you have concrete ideas than a GH discussion is a good place to start!

In any case I think the widget test failure is spurious so I have restarted the tests.

@mattpap mattpap force-pushed the 9182-tooltip-patch-area branch from eed24dd to 3f54143 Compare March 4, 2022 13:26
@mattpap mattpap merged commit 3f4a617 into bokeh:branch-3.0 Mar 4, 2022
mattpap added a commit that referenced this pull request Mar 31, 2022
Fixes a bug introduced in PR #11992.
bryevdv pushed a commit that referenced this pull request May 16, 2022
* Initial functioning WIP

* WIP Patch and HArea

* check for potentially null value

* Remove commented out code

* remove unnecessary to_screen call

* break after successful polygon hit test

* Add a comment regarding selection.is_empty()

Co-authored-by: Mateusz Paprocki <mattpap@gmail.com>
@bryevdv bryevdv mentioned this pull request May 16, 2022
@bryevdv bryevdv modified the milestones: 3.0, 2.4.3 May 16, 2022
bryevdv added a commit that referenced this pull request May 16, 2022
* Update CONTRIBUTING.MD (#11957)

* track modified files in release build more carefully (#11972)

* Remove katex example (#11894)

* Remove katex example

* Remove test based on katex example

* Revert "Remove test based on katex example"

This reverts commit f0df5ed.

* Cleaner white space/indentation in templates (#11866)

* Enable block trimming, add initial test

* Add test for script start/stops

* Trim comment blocks in templates

* Remove hard-coded white space in templates

* Minimize manual trimming/indenting

* Clean up indentation, standardize CSS/JS

* Simplify new template test

* Expand new test to cover multiple resource modes

* Reduce whitespace around title and scripts

* isort, lint test_templates

* Combine JS/CSS resource defs in test_templates

* Reduce indent of plot script

* Consistent indentation in template itself

* Update template in docs

* 11726 Updated links to visjs.org Graph3d examples (#11728)

* Add sphinx_copybutton (#11993)

* Add sphinx_copybutton

* Update CI environments

* Move sphinx-copybutton to pip dependencies

* Fix typo in tools.py (#11935)

perfrom -> perform

* Fixed a few typos and changed wordings (#11698)

* Fix issue probably-meant-fstring found at https://codereview.doctor (#12097)

* add functools.wraps to _needs_document_lock_wrapper (#11976)

dask.distributed intermittently logs:
`RuntimeWarning: coroutine '_needs_document_lock.<locals>._needs_document_lock_wrapper' was never awaited`
with this wrapper it will be possible to see which method wasn't
awaited

* Md files checking and updating (#11903)

* Update readme

Resolving issue [#11868] (#11868)

* Update README.md

* Update README.md

* Update README.md

* Update commits with comments from #11903

* Update according to #1193 comments

* Update readme to comply code quality test

* update readme to comply code quality issues

* Elaborate on git commit/push process in docs (#11758)

* Add more git instructions

* Fix typo in models contributor guide

* Update sphinx/source/docs/dev_guide/pull_requests.rst

Co-authored-by: Timo Cornelius Metzger <39711796+tcmetzger@users.noreply.github.com>

Co-authored-by: Timo Cornelius Metzger <39711796+tcmetzger@users.noreply.github.com>

* Hover tooltips for Patch and Areas (#11992)

* Initial functioning WIP

* WIP Patch and HArea

* check for potentially null value

* Remove commented out code

* remove unnecessary to_screen call

* break after successful polygon hit test

* Add a comment regarding selection.is_empty()

Co-authored-by: Mateusz Paprocki <mattpap@gmail.com>

* Remove references to gen.coroutine from docs and update without_document_lock decorator (#12010)

* Update without_document_lock to decorate async functions and asyncio.coroutines as well

* Update docs to use async functions instead of tornado.gen.coroutines

* Add fact that async funcs are accepted in docstring

* Add test for async func decorated by without_document_lock

* Remove trailing whitespace

* simplify CI jobs with composite action (#11833)

* simplfy CI jobs with composite action

* lower verbosity on unpack

* Removed unnecessary calls to _recompute_all_models() to speed up adding roots (#11739)

* Removed unnecessary calls to _recompute_all_models() to speed up adding roots

* Corrected last commit so that general calls to add_root() still call recompute_all_models(), and that is only worked around in special situations.

* Wrapping calls to add_root() within push_all_models_freeze()

* Made last commit more elegant by getting rid of loop

Co-authored-by: Efrem Braun <efrem.braun@gmail.comm>

Co-authored-by: Timo Cornelius Metzger <39711796+tcmetzger@users.noreply.github.com>
Co-authored-by: g-parki <61096711+g-parki@users.noreply.github.com>
Co-authored-by: Bill <35750915+roadswitcher@users.noreply.github.com>
Co-authored-by: Ikko Ashimine <eltociear@gmail.com>
Co-authored-by: Harold <eldrickmargarejo@gmail.com>
Co-authored-by: code-review-doctor <72647856+code-review-doctor@users.noreply.github.com>
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: girolamo <58935223+girolamodaschio@users.noreply.github.com>
Co-authored-by: Alex Pilon <alex.pilon@gmail.com>
Co-authored-by: Mateusz Paprocki <mattpap@gmail.com>
Co-authored-by: Ben Russell <bprussell80@protonmail.com>
Co-authored-by: Efrem Braun <efrem.braun@gmail.com>
Co-authored-by: Efrem Braun <efrem.braun@gmail.comm>
@bryevdv
Copy link
Copy Markdown
Member

bryevdv commented May 29, 2022

Hi @xaptronic I have some quick follow-up questions here. I am trying to rationalize the $rx, $ry and $data_x and $data_y special vars in another PR. What is seems they were previously in other cases was: the screen/data coords of any "snap to data" position for the tooltip if that is requested, or just mouse screen/data coords otherwise.

The data_x1 etc here seems to muddy that a bit. Since this is still unreleased (and undocumented) I would like to change or remove them. So two questions:

  • Is having those datapsec data series for the glyph actually a specific need in your original ask? If so what's the general use-case, maybe there is a better name?
  • Alternatively, maybe we can just return $glyph for all results. Then e.g. a custom formatter could pull of whatever fields it wants. I feel like that might be cleaner.

Please let me know your thoughts.

@bryevdv
Copy link
Copy Markdown
Member

bryevdv commented May 30, 2022

HI @xaptronic specifically, in the PR just linked above, there are no more data_ special vars. I.e. these are gone:

        const data_x = glyph._x
        const data_y1 = glyph._y1
        const data_y2 = glyph._y2

I can't tell from the discussion whether these were tied to a specific use-case, or just added because. However, the var $glyph is now available so a custom formatter could access anything on the glyph, e.g vars.glyph._x. But perhaps we should consider making public accessors of those attributes if those are the ones you want.

@github-actions
Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add hover tooltip support for Patch, HArea, and VArea

3 participants