Skip to content

Commit

Permalink
Ensure plot ranges for all renderers are combined in auto-ranging (#6173
Browse files Browse the repository at this point in the history
)

Co-authored-by: Simon Høxbro Hansen <simon.hansen@me.com>
  • Loading branch information
philippjfr and hoxbro authored Apr 15, 2024
1 parent f100137 commit 2d6f46f
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 35 deletions.
55 changes: 20 additions & 35 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,6 @@ def _setup_autorange(self):
else:
p0, p1 = self.padding, self.padding

# Clean this up in bokeh 3.0 using View.find_one API
callback = CustomJS(code=f"""
const cb = function() {{
Expand All @@ -1349,30 +1348,10 @@ def _setup_autorange(self):
return invert ? [upper, lower] : [lower, upper]
}}
const ref = plot.id
const find = (view) => {{
let iterable = view.child_views === undefined ? [] : view.child_views
for (const sv of iterable) {{
if (sv.model.id == ref)
return sv
const obj = find(sv)
if (obj !== null)
return obj
}}
return null
}}
let plot_view = null;
for (const root of plot.document.roots()) {{
const root_view = window.Bokeh.index[root.id]
if (root_view === undefined)
return
plot_view = find(root_view)
if (plot_view != null)
break
}}
if (plot_view == null)
let plot_view = Bokeh.index.find_one(plot)
if (plot_view == null) {{
return
}}
let range_limits = {{}}
for (const dr of plot.data_renderers) {{
Expand All @@ -1393,20 +1372,23 @@ def _setup_autorange(self):
}}
}}
if (y_range_name) {{
if (y_range_name in range_limits) {{
const [vmin_old, vmax_old] = range_limits[y_range_name]
range_limits[y_range_name] = [Math.min(vmin, vmin_old), Math.max(vmax, vmax_old)]
}} else {{
range_limits[y_range_name] = [vmin, vmax]
}}
}}
let range_tags_extras = plot.{dim}_range.tags[1]
if (range_tags_extras['autorange']) {{
let lowerlim = range_tags_extras['y-lowerlim'] ?? null
let upperlim = range_tags_extras['y-upperlim'] ?? null
let [start, end] = get_padded_range('default', lowerlim, upperlim, range_tags_extras['invert_yaxis'])
if ((start != end) && window.Number.isFinite(start) && window.Number.isFinite(end)) {{
plot.{dim}_range.setv({{start, end}})
}}
}}
let range_tags_extras = plot.{dim}_range.tags[1]
if (range_tags_extras['autorange']) {{
let lowerlim = range_tags_extras['y-lowerlim'] ?? null
let upperlim = range_tags_extras['y-upperlim'] ?? null
let [start, end] = get_padded_range('default', lowerlim, upperlim, range_tags_extras['invert_yaxis'])
if ((start != end) && window.Number.isFinite(start) && window.Number.isFinite(end)) {{
plot.{dim}_range.setv({{start, end}})
}}
}}
for (let key in plot.extra_{dim}_ranges) {{
const extra_range = plot.extra_{dim}_ranges[key]
Expand Down Expand Up @@ -2665,7 +2647,7 @@ class OverlayPlot(GenericOverlayPlot, LegendPlot):
'min_height', 'max_height', 'min_width', 'min_height',
'margin', 'aspect', 'data_aspect', 'frame_width',
'frame_height', 'responsive', 'fontscale', 'subcoordinate_y',
'subcoordinate_scale']
'subcoordinate_scale', 'autorange']

def __init__(self, overlay, **kwargs):
self._multi_y_propagation = self.lookup_options(overlay, 'plot').options.get('multi_y', False)
Expand Down Expand Up @@ -2986,6 +2968,9 @@ def initialize_plot(self, ranges=None, plot=None, plots=None):
if self.top_level:
self.init_links()

if self.autorange:
self._setup_autorange()

self._execute_hooks(element)

return self.handles['plot']
Expand Down
86 changes: 86 additions & 0 deletions holoviews/tests/ui/bokeh/test_autorange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import numpy as np
import pytest

from holoviews.element import Curve
from holoviews.plotting.bokeh.renderer import BokehRenderer

from .. import expect, wait_until

pytestmark = pytest.mark.ui


@pytest.mark.usefixtures("bokeh_backend")
def test_autorange_single(serve_hv):
curve = Curve(np.arange(1000)).opts(autorange='y', active_tools=['box_zoom'])

plot = BokehRenderer.get_plot(curve)

page = serve_hv(plot)

hv_plot = page.locator('.bk-events')

expect(hv_plot).to_have_count(1)

bbox = hv_plot.bounding_box()
hv_plot.click()

page.mouse.move(bbox['x']+100, bbox['y']+100)
page.mouse.down()
page.mouse.move(bbox['x']+150, bbox['y']+150, steps=5)
page.mouse.up()

y_range = plot.handles['y_range']
wait_until(lambda: y_range.start == 163.2 and y_range.end == 448.8, page)


@pytest.mark.usefixtures("bokeh_backend")
def test_autorange_single_in_overlay(serve_hv):
c1 = Curve(np.arange(1000))
c2 = Curve(-np.arange(1000)).opts(autorange='y')

overlay = (c1*c2).opts(active_tools=['box_zoom'])

plot = BokehRenderer.get_plot(overlay)

page = serve_hv(plot)

hv_plot = page.locator('.bk-events')

expect(hv_plot).to_have_count(1)

bbox = hv_plot.bounding_box()
hv_plot.click()

page.mouse.move(bbox['x']+100, bbox['y']+100)
page.mouse.down()
page.mouse.move(bbox['x']+150, bbox['y']+150, steps=5)
page.mouse.up()

y_range = plot.handles['y_range']
wait_until(lambda: y_range.start == -486 and y_range.end == 486, page)

@pytest.mark.usefixtures("bokeh_backend")
def test_autorange_overlay(serve_hv):
c1 = Curve(np.arange(1000))
c2 = Curve(-np.arange(1000))

overlay = (c1*c2).opts(active_tools=['box_zoom'], autorange='y')

plot = BokehRenderer.get_plot(overlay)

page = serve_hv(plot)

hv_plot = page.locator('.bk-events')

expect(hv_plot).to_have_count(1)

bbox = hv_plot.bounding_box()
hv_plot.click()

page.mouse.move(bbox['x']+100, bbox['y']+100)
page.mouse.down()
page.mouse.move(bbox['x']+150, bbox['y']+150, steps=5)
page.mouse.up()

y_range = plot.handles['y_range']
wait_until(lambda: y_range.start == -486 and y_range.end == 486, page)

0 comments on commit 2d6f46f

Please sign in to comment.