[FEAT] Report figures: deterministic square/matched-colorbar layout, colour registries, units, and an opt-in figure-debug view - #75
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, ...)inplotters/primitives.pylays out annrows × ncolsgrid of exact-square panels deterministically in inches (the approach proven in the cruise-map work), with either one shared colorbar axes or, viaper_panel_colorbar=True, an(nrows, ncols)array of colorbar axes each height-matched to its panel.top_pad_in/bottom_pad_inreserve 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. AGRID_PANEL_ROW_INtoken sets one shared row height for gridded-section panels.Colour and unit registries
config/parameters.pygainsLINE_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), andvunit(var)returning the registrylabel_units. Every axis label and colorbar unit across the report plotters now routes throughvunit/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=1prints a small.debugline 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 asreports/_figdebug.py(a thin wrapper around the vendored encoder that records per-figure metadata), afigdbgJinja global, and a shareddbgmacro 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_colorbartick/label drift; and consolidation of the duplicated_instrument_panels.Testing
pytest— 764 passed, 9 skipped.ruff checkandruff format --checkclean. The golden-file net was re-baselined; its only non-figure diffs are the added.debugCSS, 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_panelsis internal — so no caller needs to change.