Skip to content

[FEAT] Report figures: deterministic square/matched-colorbar layout, colour registries, units, and an opt-in figure-debug view - #75

Merged
eleanorfrajka merged 4 commits into
mainfrom
rep/04-encoder-sizing
Aug 15, 2026
Merged

[FEAT] Report figures: deterministic square/matched-colorbar layout, colour registries, units, and an opt-in figure-debug view#75
eleanorfrajka merged 4 commits into
mainfrom
rep/04-encoder-sizing

Conversation

@eleanorfrajka

@eleanorfrajka eleanorfrajka commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

This reworks the report figure layer around a single deterministic layout primitive so that square panels and their colorbars are sized in inches by construction, rather than fought over by matplotlib's auto-layout tools. It adds two colour registries and routes every axis/colorbar unit through the variable registry's pretty units, converts the grid, T-S, hodograph, trajectory, rose, and spectrum figures onto the shared path, and introduces an opt-in per-figure debug view that surfaces figsize-vs-display-slot mismatches. It also folds in a round of code-review fixes.

The dominant motivation is the recurring "colorbar too tall / figure downscaled / fonts shrink" class of bug: figures were being built at arbitrary widths and heights, then rescaled by the browser to fit their display slot, which shrank the fonts by a per-figure-varying factor. The fix is to size each figure to its slot and place its colorbar at the panel's exact pixel height.

New primitives and helpers

square_axes_grid(fig_w, nrows, ncols, ...) in plotters/primitives.py lays out an nrows × ncols grid of exact-square panels deterministically in inches (the approach proven in the cruise-map work), with either one shared colorbar axes or, via per_panel_colorbar=True, an (nrows, ncols) array of colorbar axes each height-matched to its panel. top_pad_in / bottom_pad_in reserve room for a suptitle or rotated tick labels.

unit_colorbar(cax, mappable, unit=..., ticks=..., ticklabels=...) draws a colorbar into a pre-placed axes with the unit as a title above the bar (saves width, reads cleanly).

square_limits(x, y) frames data as an equal-extent square; grid_despine(ax) turns the grid on and hides the top/right spines together; ordered_line_colors(cmap, n) samples a colormap skipping washed-out colours by luminance; nice_colorbar_ticks(vmin, vmax, max_ticks=6) returns round tick positions decoupled from the colour discretisation; date_offset_left(ax) moves the year/month date-axis offset label to the bottom-left. A GRID_PANEL_ROW_IN token sets one shared row height for gridded-section panels.

Colour and unit registries

config/parameters.py gains LINE_CMAPS_BY_VARIABLE (per-variable colormaps for depth-ordered multi-instrument line plots, distinct from the pcolormesh field maps), VAR_COLORS (one colourblind-safe colour per variable — Okabe-Ito physics, Paul Tol biogeochemistry — for single-instrument panels), and vunit(var) returning the registry label_units. Every axis label and colorbar unit across the report plotters now routes through vunit/vlabel, so units render as the pretty forms (°C, m s⁻¹, dbar, PSU, kg m⁻³) rather than the raw CF file attributes.

Figures converted to the shared path

Hodographs, particle trajectories, current roses, all three T-S diagrams (dot plot, count heatmap, O₂ panel), and the temperature power and rotary spectra now use square_axes_grid + unit_colorbar. The T-S dot plot and heatmap share axis limits; the rotary spectrum replaces its pressure colorbar with a depth legend to free horizontal space; the temperature spectrum uses two-line panel titles. Gridded pcolormesh figures (hydro, velocity, sigma, N²) were 13-inch wide (1950 px) and are now full-width (1350 px), with units on top of the colorbar, nice ticks, and constrained layout so the right margin is tight. The wavelet figure moves its per-panel pressure label into the time-series corner (no longer overlapping the scalogram above) and uses constrained layout to crop surrounding whitespace.

Figure-debug view

Setting OCEANARRAY_REPORT_DEBUG=1 prints a small .debug line under each report figure showing the display slot the template chose alongside the render-side draw function, figsize, and PNG pixel size — so a figsize-vs-slot mismatch is visible at a glance. It is a no-op when the variable is unset (the golden output is unchanged). Implemented as reports/_figdebug.py (a thin wrapper around the vendored encoder that records per-figure metadata), a figdbg Jinja global, and a shared dbg macro imported by every template.

Code-review fixes

Malformed HTML from a debug line injected mid-<img> tag; a diverging-colormap line washing out to near-white (lowered the luminance ceiling and pick the darkest colour for a single line); an ADCP trajectory built full-width but displayed at half; a colorbar that could land off-canvas on a too-small figure; the debug wrapper losing the real draw-function name in encoder error messages; unit_colorbar tick/label drift; and consolidation of the duplicated _instrument_panels.

Testing

pytest — 764 passed, 9 skipped. ruff check and ruff format --check clean. The golden-file net was re-baselined; its only non-figure diffs are the added .debug CSS, the stack trajectory container width, and the instrument rose display slot.

Breaking changes

None. No public API is removed or renamed — new function parameters are keyword-only and _instrument_panels is internal — so no caller needs to change.

@eleanorfrajka
eleanorfrajka merged commit 809e52f into main Aug 15, 2026
5 checks passed
@eleanorfrajka
eleanorfrajka deleted the rep/04-encoder-sizing branch August 15, 2026 07:54
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.

1 participant