Skip to content

Overlay does not have separate zoom controls by axis #6654

@BnMcG

Description

@BnMcG

ALL software version info

(this library, plus any other relevant software, e.g. bokeh, python, notebook, OS, browser, etc should be added within the dropdown below.)

Software Version Info
"panel>=1.7.2",
"holoviews>=1.21.0",
"bokeh>=3.7.3",
"pandas",
"numpy<2"

Browser: Firefox 139
OS: MacOS 14.4
Python 3.12

Description of expected behavior and the observed behavior

When plotting an overlay, the separate zoom controls (for X axis and Y axis) are not added to the toolbar, only the combined "Wheel Zoom" tool is added. The wheel zoom does still correctly zoom the X and Y axes separately if you hover over the axis while zooming.

The expected behaviour is for the X and Y zoom tools to be added separately to the toolbar if specified independently or together in tools. This is the behaviour seen when plotting an individual Curve or Points plot (for example).

I tried different variations to attempt to work around this, but they all suffer the problem detailed above:

  • Only specify xwheel_zoom, ywheel_zoom on a single plot in the overlay
  • Specify xwheel_zoom and ywheel_zoom on separate plots in the overlay (1 per plot)
  • Specify xwheel_zoom and ywheel_zoom on the overlay itself, not its child plots
  • Create WheelZoomTool manually rather than using string aliases

I can see that specifying other tools (eg: hover) are correctly propagated to the overlay.

Complete, minimal, self-contained example code that reproduces the issue

#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "panel>=1.7.2",
#     "holoviews>=1.21.0",
#     "bokeh>=3.7.3",
#     "pandas",
#     "numpy<2"
# ]
# ///

import numpy as np
import pandas as pd
import holoviews as hv
import panel as pn

# Enable Panel/HoloViews
pn.extension()
hv.extension("bokeh")

# Generate random data
np.random.seed(42)
n_points = 100

# Data for curve plot
time_range = pd.date_range('2024-01-01', periods=n_points, freq='1min')
curve_data = pd.DataFrame({
    'time': time_range,
    'value': np.cumsum(np.random.randn(n_points)) + 100
})

# Data for points plot
points_data = pd.DataFrame({
    'time': np.random.choice(time_range, size=20),
    'value': np.random.uniform(95, 105, 20),
    'category': np.random.choice(['A', 'B', 'C'], 20)
})

# Data with different dimension names for decoupled plots
curve_data_separate = curve_data.rename(columns={'time': 'curve_time', 'value': 'curve_value'})
points_data_separate = points_data.rename(columns={'time': 'points_time', 'value': 'points_value'})

def create_plots():
    # Create curve plot for overlay
    curve = hv.Curve(
        curve_data,
        kdims=['time'],
        vdims=['value']
    ).opts(
        color='blue',
        line_width=2,
        tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
        title="Combined Curve + Points"
    )

    # Create points plot for overlay (using same dimensions for proper overlay)
    points = hv.Points(
        points_data,
        kdims=['time', 'value'],
        vdims=['category']
    ).opts(
        color='category',
        cmap={'A': 'red', 'B': 'green', 'C': 'orange'},
        size=10,
        tools=['xwheel_zoom', 'ywheel_zoom', 'hover']
    )

    # Create overlay with both plots
    overlay = hv.Overlay([curve, points])

    return overlay

def create_separate_curve():
    # Create standalone curve plot with different dimension names
    curve = hv.Curve(
        curve_data_separate,
        kdims=['curve_time'],
        vdims=['curve_value']
    ).opts(
        color='blue',
        line_width=2,
        tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
        title="Standalone Curve Plot"
    )
    return curve

def create_separate_points():
    # Create standalone points plot with different dimension names
    points = hv.Points(
        points_data_separate,
        kdims=['points_time', 'points_value'],
        vdims=['category']
    ).opts(
        color='category',
        cmap={'A': 'red', 'B': 'green', 'C': 'orange'},
        size=10,
        tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
        title="Standalone Points Plot"
    )
    return points

# Create the plots
overlay_pane = create_plots()
curve_pane = create_separate_curve()
points_pane = create_separate_points()

# Create Panel dashboard
dashboard = pn.Column(
    pn.pane.Markdown("## Minimal Reproduction: Curve + Points with Zoom Controls"),
    pn.pane.Markdown("### Combined Overlay (no separate zoom controls)"),
    pn.panel(overlay_pane, sizing_mode="stretch_both"),
    pn.pane.Markdown("### Separate Curve Plot (has separate zoom controls)"),
    pn.panel(curve_pane, sizing_mode="stretch_both"),
    pn.pane.Markdown("### Separate Points Plot (has separate zoom controls)"),
    pn.panel(points_pane, sizing_mode="stretch_both"),
    sizing_mode="stretch_both"
)

if __name__ == "__main__" or __name__.startswith("bokeh"):
    pn.serve(
        dashboard,
        title="Repro - Curve and Points",
        websocket_origin="*",
        show=True,
        port=5007
    )

Stack traceback and/or browser JavaScript console output

N/A

Screenshots or screencasts of the bug in action

Individual curve and points plots have both zoom tools independently on the toolbar (on the right hand side)
Image

An overlay of a curve and a points plot only has the combined wheel zoom tool on the right hand side:

Image
  • I may be interested in making a pull request to address this

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions