Skip to content

Commit f2a62ae

Browse files
committed
Clear panel axis decorations for left/top/bottom colorbars (#687)
The divider only knows about the axes box, so left/top/bottom colorbars landed on the panel's own y-ticks, title, and x-ticks. Measure each side's decoration clearance (tight bbox vs axes box, in inches) and pad the first colorbar on that side past it, plus the requested pad. Right-side colorbars are unaffected. Colorbar size is now absolute (fraction x axis extent) for consistent stacking.
1 parent 89892e0 commit f2a62ae

1 file changed

Lines changed: 33 additions & 9 deletions

File tree

‎src/spatialdata_plot/pl/basic.py‎

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,14 @@ def show(
15211521
outline_legend_title=outline_legend_title,
15221522
)
15231523

1524-
def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_counts: dict[str, int]) -> None:
1524+
def _draw_colorbar(
1525+
spec: ColorbarSpec,
1526+
fig: Figure,
1527+
divider: AxesDivider,
1528+
side_counts: dict[str, int],
1529+
clearance: dict[str, float],
1530+
axes_size_in: tuple[float, float],
1531+
) -> None:
15251532
norm = spec.mappable.norm
15261533
if isinstance(norm, LogNorm):
15271534
vmin, vmax = norm.vmin, norm.vmax
@@ -1555,16 +1562,17 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
15551562

15561563
# Append the colorbar axes through the panel's shared divider. This steals space from the
15571564
# panel (so the colorbar matches the equal-aspect plot's extent) and keeps it inside the
1558-
# panel's grid cell, never overflowing into a neighbouring panel. Repeated appends on the
1559-
# same divider stack additional colorbars on that side; the first sits next to the plot
1560-
# with the requested (relative) pad, while each subsequent one uses an absolute pad wide
1561-
# enough to clear the previous colorbar's tick labels instead of overlapping them.
1565+
# panel's grid cell, never overflowing into a neighbouring panel. The first colorbar on a
1566+
# side is padded past the panel's own decorations (ticks/labels/title) plus the requested
1567+
# pad; each subsequent one uses an absolute pad wide enough to clear the previous
1568+
# colorbar's tick labels instead of overlapping them.
15621569
n_on_side = side_counts.get(location, 0)
1563-
pad_arg: str | Fixed = Fixed(CBAR_STACK_PAD_INCHES) if n_on_side else f"{max(pad, 0.0) * 100}%"
1570+
ref_in = axes_size_in[0] if location in {"left", "right"} else axes_size_in[1]
1571+
pad_in = CBAR_STACK_PAD_INCHES if n_on_side else clearance.get(location, 0.0) + max(pad, 0.0) * ref_in
15641572
cax = divider.append_axes(
15651573
location,
1566-
size=f"{fraction * 100}%",
1567-
pad=pad_arg,
1574+
size=Fixed(max(fraction, 0.0) * ref_in),
1575+
pad=Fixed(pad_in),
15681576
axes_class=Axes,
15691577
)
15701578
side_counts[location] = n_on_side + 1
@@ -1779,6 +1787,9 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
17791787

17801788
if pending_colorbars and fig_params.fig is not None:
17811789
fig = fig_params.fig
1790+
fig.canvas.draw()
1791+
renderer = fig.canvas.get_renderer()
1792+
dpi = fig.get_dpi()
17821793
for axis, requests in pending_colorbars:
17831794
unique_specs: list[ColorbarSpec] = []
17841795
seen_mappables: set[int] = set()
@@ -1788,11 +1799,24 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
17881799
continue
17891800
seen_mappables.add(mappable_id)
17901801
unique_specs.append(spec)
1802+
# Clearance (inches) that the panel's own ticks, tick labels and title extend beyond
1803+
# the axes box on each side. The first colorbar on a side is padded past this so it
1804+
# clears those decorations instead of overlapping them (the divider only knows about
1805+
# the axes box, not its decorations).
1806+
box = axis.get_window_extent(renderer)
1807+
tight = axis.get_tightbbox(renderer)
1808+
clearance = {
1809+
"left": max(0.0, (box.x0 - tight.x0) / dpi),
1810+
"right": max(0.0, (tight.x1 - box.x1) / dpi),
1811+
"bottom": max(0.0, (box.y0 - tight.y0) / dpi),
1812+
"top": max(0.0, (tight.y1 - box.y1) / dpi),
1813+
}
1814+
axes_size_in = (box.width / dpi, box.height / dpi)
17911815
# One divider per panel so multiple colorbars on the same panel stack via the divider.
17921816
divider = make_axes_locatable(axis)
17931817
side_counts: dict[str, int] = {}
17941818
for spec in unique_specs:
1795-
_draw_colorbar(spec, fig, divider, side_counts)
1819+
_draw_colorbar(spec, fig, divider, side_counts, clearance, axes_size_in)
17961820

17971821
if fig_params.fig is not None and save is not None:
17981822
save_fig(fig_params.fig, path=save)

0 commit comments

Comments
 (0)