Skip to content

Fix data-aspect ratio for responsive Bokeh/HoloViews plots#774

Open
SimonHeybrock wants to merge 5 commits intomainfrom
js-plot-aspect
Open

Fix data-aspect ratio for responsive Bokeh/HoloViews plots#774
SimonHeybrock wants to merge 5 commits intomainfrom
js-plot-aspect

Conversation

@SimonHeybrock
Copy link
Member

@SimonHeybrock SimonHeybrock commented Mar 10, 2026

Summary

  • HoloViews' aspect options (aspect="square", data_aspect, aspect="equal", aspect=N) do not produce correctly shaped data areas (frames) when plots use responsive=True inside Panel containers (upstream bug spanning Bokeh/HoloViews/Panel)
  • Add JS hooks that enforce correct frame dimensions by adjusting fig.height or fig.width via CustomJS callbacks after each Bokeh layout pass
  • Two hook variants: fixed frame ratio (for square and aspect=N — ignores data ranges) and data aspect (for equal and data_aspect=N — reads x/y ranges to compute the correct frame shape)
  • HoloViews' own data_aspect/match_aspect opts are deliberately not set — match_aspect pads ranges to fit the (wrong) frame shape, creating a circular dependency with the hook
  • The user's StretchMode setting (Fill width / Fill height) controls which axis the figure fills and which the JS adjusts, avoiding overflow in either direction

Fixes #576.

Limitations

This does not eliminate all issues - depending on Zoom level we will get plots that slightly exceed width or height (and do not have correct aspect any more). If that is the case, the user must switch from stretch-width to stretch-height mode (or vice-versa) manually. Did not see a simple automatic solution for this, as naive approaches a broken due to some cycles.

Examples

A couple of exampes with equal setting. Note how the whitespace around the plots changes with zoom level:

large zoom level small zoom level

Extreme case (note that our sizing of non-plot elements is broken at this scale, unrelated to what is fixed here):

extreme size with tiny plot area and huge fonts

Test plan

  • Verify square produces a square frame
  • Verify equal and data_aspect plots have correct frame proportions matching data coordinate ranges
  • Verify zooming adapts the frame shape to the visible range (data-aspect modes)
  • Verify Fill width vs Fill height stretch modes both work
  • Verify free type is unaffected
  • Verify no _update_layout console errors on page load
  • Works with multiple overlays
  • Save filename hook still works

🤖 Generated with Claude Code

@SimonHeybrock SimonHeybrock marked this pull request as draft March 10, 2026 10:07
@SimonHeybrock SimonHeybrock changed the title Fix data-aspect ratio for responsive plots in Panel Fix data-aspect ratio for responsive Bokeh/HoloViews plots Mar 10, 2026
@SimonHeybrock SimonHeybrock marked this pull request as ready for review March 10, 2026 12:51
@SimonHeybrock SimonHeybrock marked this pull request as draft March 10, 2026 12:53
@SimonHeybrock SimonHeybrock marked this pull request as draft March 10, 2026 12:53
@SimonHeybrock SimonHeybrock marked this pull request as ready for review March 10, 2026 13:01
SimonHeybrock and others added 4 commits March 10, 2026 13:01
HoloViews' data_aspect and aspect="equal" do not produce correctly
shaped data areas when plots use responsive=True inside Panel
containers (upstream bug in Bokeh/HoloViews/Panel).

Add a JS hook that reads x/y ranges from the Bokeh figure and adjusts
the figure height so pixels_per_x_unit / pixels_per_y_unit matches the
configured data_aspect. HoloViews' own data_aspect/match_aspect opts
are deliberately not set for these cases, as match_aspect pads ranges
to fit the (wrong) frame shape, creating a circular dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The frame-aspect JS hook now respects the user's StretchMode setting:
Fill width adjusts height (default), Fill height adjusts width. This
lets users pick the mode that fits their container shape, avoiding
overflow when the data aspect would require growing beyond the
container along the fixed axis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All non-free aspect types now use the JS hook for frame enforcement,
since HoloViews aspect opts conflict with responsive mode in Panel.

Two hook variants: fixed frame ratio (square, aspect=N) that ignores
data ranges, and data-aspect (equal, data_aspect=N) that reads x/y
ranges. Guard the fig.height/width assignment with try/catch to avoid
_update_layout errors during initial render before the view tree is
fully connected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HoloViews drops overlay-level opts (including hooks) when an Overlay
contains 3+ DynamicMaps during internal resolution. Calling .collate()
first produces a single DynamicMap whose outputs are plain Overlays, so
opts applied afterwards land on the OverlayPlot and persist correctly.

Also adds document-presence guards to the JS callbacks in
frame_aspect.py to prevent TypeError on detached figures during tab
switching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
for curve in result:
opts = hv.Store.lookup_options('bokeh', curve, 'plot').kwargs
assert opts.get('aspect') == 'square'
assert opts.get('responsive') is True
Copy link
Member

@YooSunYoung YooSunYoung Mar 10, 2026

Choose a reason for hiding this comment

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

Should we also check that the plot width/height are the same? Or is not possible/unecessary...?

Copy link
Member Author

Choose a reason for hiding this comment

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

They are not - sizing is done in JS, so I don't think the actual size ever makes it to the Python-side of the plot?

Copy link
Member

Choose a reason for hiding this comment

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

It's a shame we need to have all this extra javascript code 😞

What do the plots look like without this fix? Are they really that bad?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes:
image

It seems little JS to me?

Copy link
Member Author

@SimonHeybrock SimonHeybrock Mar 11, 2026

Choose a reason for hiding this comment

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

If Holoviews accepts my fix for the missing cbar label it gets worse (note I even zoomed out more here):
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plots do not seem to have exact square data proportions despite config

3 participants