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

Add support for zooming sub-plots #13345

Merged
merged 12 commits into from
Sep 28, 2023
Merged

Conversation

mattpap
Copy link
Contributor

@mattpap mattpap commented Aug 22, 2023

This PR does two things:

  1. It liberates range management internals from using named range/scale mappings, to make it possible for range management logic to work with sub-coordinates at all.
  2. Makes various zoom tools and zooming logic aware of sub-coordinates and to scale accordingly.

Example. Notice that zoom in/out works on x frame range, however not y frame range, but y sub-coordinate ranges:

Screencast_00003.mp4
Code
import numpy as np

from bokeh.core.properties import field
from bokeh.io import show
from bokeh.models import (ColumnDataSource, DataRange1d, FactorRange,
                          HoverTool, Range1d, WheelZoomTool, ZoomInTool, ZoomOutTool)
from bokeh.plotting import figure
from bokeh.palettes import Category10

np.random.seed(0)

n_channels = 10
n_seconds = 15
fs = 512
max_ch_disp = 5  # max channels to initially display
max_t_disp = 3 # max time in seconds to initially display

total_samples = n_seconds * fs
time = np.linspace(0, n_seconds, total_samples)
data = np.random.randn(n_channels, total_samples).cumsum(axis=1)
channels = [f'EEG {i}' for i in range(n_channels)]

hover = HoverTool(tooltips=[
    ("Channel", "$name"),
    ("Time", "$x s"),
    ("Amplitude", "$y µV"),
])

x_range = Range1d(start=time.min(), end=time.max())
#x_range = DataRange1d()
y_range = FactorRange(factors=channels)

p = figure(x_range=x_range, y_range=y_range, lod_threshold=None)

source = ColumnDataSource(data=dict(time=time))
renderers = []

for i, channel in enumerate(channels):
    xy = p.subplot(
        x_source=p.x_range,
        y_source=Range1d(start=data[i].min(), end=data[i].max()),
        #y_source=DataRange1d(),
        x_target=p.x_range,
        y_target=Range1d(start=i, end=i + 1),
    )

    source.data[channel] = data[i]
    line = xy.line(field("time"), field(channel), color=Category10[10][i], source=source, name=channel)
    renderers.append(line)

wheel_zoom = WheelZoomTool()#renderers=renderers)
zoom_in = ZoomInTool(renderers=renderers)
zoom_out = ZoomOutTool(renderers=renderers)

p.add_tools(wheel_zoom, zoom_in, zoom_out, hover)
p.toolbar.active_scroll = wheel_zoom

show(p)

TODO:

  • tests

fixes #13339

@bryevdv
Copy link
Member

bryevdv commented Aug 22, 2023

Really cool! Are x_range and x_target already included in any release, if so are they designated experimental? I think "source" is too generic and already overloaded, so that we should strongly consider alternative names for these properties to make searching for documentation more unambigious. E.g. maybe

  • x_local/ x_remote
  • x_range / x_embed_range

@codecov
Copy link

codecov bot commented Aug 22, 2023

Codecov Report

Merging #13345 (5e7a667) into branch-3.3 (7cd6b36) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@             Coverage Diff             @@
##           branch-3.3   #13345   +/-   ##
===========================================
  Coverage       92.45%   92.45%           
===========================================
  Files             317      317           
  Lines           20268    20275    +7     
===========================================
+ Hits            18738    18745    +7     
  Misses           1530     1530           

@droumis
Copy link
Member

droumis commented Aug 23, 2023

Looking great!

@mattpap mattpap added the grant: CZI R5 Funded by CZI Round 5 grant label Aug 29, 2023
@mattpap mattpap force-pushed the mattpap/13339_subcoordinates_zoom branch 2 times, most recently from 79339bb to 6802a8a Compare September 18, 2023 19:03
@mattpap
Copy link
Contributor Author

mattpap commented Sep 18, 2023

This is tentatively ready for review. With this PR ZoomInTool, ZoomOutTool and WheelZoomTool were made aware of sub-coordinates. The default behavior is as before though more ranges will be taken into consideration. When working with true sub-coordinates, i.e. with CompositeScale in either or both dimensions, then the aforementioned tools provide new level property, which allows to configure which actual ranges will be zoomed. 0 is the default and acts on top-level (frame) ranges. Using positive values allows to descend composite scales and select inner sub-coordinate ranges.

@mattpap
Copy link
Contributor Author

mattpap commented Sep 18, 2023

Are x_range and x_target already included in any release, if so are they designated experimental? I think "source" is too generic and already overloaded, so that we should strongly consider alternative names for these properties to make searching for documentation more unambigious.

This should be discussed/performed in PR #13346. I'm generally not opposed to renaming. The current naming convention is taken from scales where we have source_range and target_range, thus {x,y}_source and {x,y}_target for composite scales.

@droumis
Copy link
Member

droumis commented Sep 18, 2023

Hi @mattpap , could you provide code for a working example?

I'm getting Javascript Error: unknown property WheelZoomTool.renderers when trying to plot

@mattpap
Copy link
Contributor Author

mattpap commented Sep 18, 2023

could you provide code for a working example?

@droumis, see the example (examples/interaction/tools/subcoordinates_zoom.py) added in this PR.

I'm getting Javascript Error: unknown property WheelZoomTool.renderers when trying to plot

This is a mismatch between bokeh and bokehjs.

Copy link
Member

@ianthomas23 ianthomas23 left a comment

Choose a reason for hiding this comment

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

Looks good to me. It would be good to get @droumis' approval that this provides all the required functionality.

@droumis
Copy link
Member

droumis commented Sep 19, 2023

apart from my comment above, all looks good to go


target_scale = Required(Instance(Scale), help="""
The target scale.
""")
Copy link
Member

@bryevdv bryevdv Sep 21, 2023

Choose a reason for hiding this comment

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

These help strings don't add anything, really. What would be helpful for both of these is a brief concrete example to anchor the concepts, e.g. if we have a plot and a subplot, which one of those is the source and which one is the target?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will continue documenting sub-coordinates' related stuff in PR #13346.

@mattpap mattpap force-pushed the mattpap/13339_subcoordinates_zoom branch from d13d567 to 5e7a667 Compare September 27, 2023 23:37
@mattpap mattpap merged commit ed34340 into branch-3.3 Sep 28, 2023
27 checks passed
@mattpap mattpap deleted the mattpap/13339_subcoordinates_zoom branch September 28, 2023 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
grant: CZI R5 Funded by CZI Round 5 grant status: accepted
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Allow zoom tools to scale subplots/subcoordinates
4 participants