Skip to content

Download the global heatmap as a PNG or SVG - #193

Merged
TianYuan-Liu merged 2 commits into
masterfrom
feat/download-heatmap
Oct 13, 2026
Merged

TianYuan-Liu merged 2 commits into
masterfrom
feat/download-heatmap

Conversation

@TianYuan-Liu

Copy link
Copy Markdown
Member

Why

A user who downloads the painted diagram for their reports asked how to download the heatmap as well. The only way was a screenshot, which keeps whatever fits on the screen: not a heatmap a few hundred rows tall, and never all the omics at once. The Global heatmap panel's Download button had been sitting commented out over a download() that threw "Not implemented".

What

  • Download in the Global heatmap header (labelled, like the diagram's) opens a two-item menu:
    • PNG image (high resolution): 3x, scaled down only past what a browser canvas can hold. A heatmap too tall even then is refused with a message pointing to the SVG.
    • SVG (vector, editable): no size limit.
  • The figure is rebuilt in the browser as one standalone SVG (PA_Step3Views.js, shared heatmap helpers):
    • paCollectHeatmapFigure reads each chart's own SVG verbatim and re-writes the surrounding HTML (omic headings, colour ramps, zero ticks, separators, row labels, relevance and per-cell stars) as SVG.
    • paHeatmapFigureSVG serialises that description (pure, unit-tested under node).
    • paRasteriseSVG draws it on a canvas for the PNG.
    • Highcharts' own exporter could not do this: the figure is one chart per omic, and its labels and stars are useHTML.
  • Output details
    • Omics are laid out side by side, so rows line up under Force order.
    • Always drawn on white, in the dark theme too; cell gaps are repainted white, light ink is darkened, and the grid stub is dropped.
    • Row labels (symbol + KEGG id / matched identifier) and condition names are written out in full where the panel truncates them.
    • Title (pathway + job), a key for exactly the stars used, and a "Created with PaintOmics AI" footer.
  • The button's tooltip is closed when the menu opens. It is interactive and opened exactly over the PNG item, taking its click (found in Chrome).
  • Docs: the Heatmaps page said there was no download. It, the diagram page, the FAQ download table and the three-group tutorial now describe the button. CHANGELOG updated.

Verified in Chrome (local server, this branch)

  • Five-omic STATegra job (Morphine addiction), light and dark themes, with and without Force order: SVG parses, and the PNG is 5190x4776.
  • Three-group tutorial data (TrtA/TrtB/Ctrl, per-contrast relevance), PPAR signaling: a star on each significant cell for each contrast, and full TrtA_vs_Ctrl-style column names.
  • Both files land in ~/Downloads as paintomics_heatmap_<pathway>_<jobID>.png/.svg.
  • Nothing drawn yet shows a warning, not an error. An oversized PNG shows the "too large, take the SVG" message.
  • Alignment guides: 0 off-rail, all rows on baseline.

Known and unchanged: with Force order, a secondary omic carrying several rows per gene (miRNA) is squeezed into the reference omic's row height and its labels overlap. That happens on screen already, and the export reproduces it faithfully.

Tests

  • New test_heatmap_figure_export.py (21 tests): static guards on the button, menu, tooltip and teardown; node-run checks that the SVG is well-formed, escaped, translated into its margin, embeds the chart verbatim, uses no web font, carries real gradients, sizes for the title and key, has safe file names and capped PNG scales.
  • Mutation-checked: dropping the text escaping, the translate, the tooltip close, or re-commenting the button each turns it red.
  • test_heatmap_axis_and_labels, test_heatmap_colour_and_column_space and test_versioned_assets_are_bumped pass; ESLint, ruff, the vulture gate (two decorated test classes added to the baseline, as for the other heatmap suites) and mkdocs build --strict are clean.

🤖 Generated with Claude Code

TianYuan-Liu and others added 2 commits October 13, 2026 12:16
A user who downloads the painted diagram for reports asked how to download
the heatmap too: the only way was a screenshot, which keeps what fits on the
screen, not a heatmap a few hundred rows tall or all its omics at once. The
panel's Download button had sat commented out for years over a download()
that threw "Not implemented".

The button is now live, labelled like the diagram's, and offers a
high-resolution PNG (3x, scaled down only past what a canvas can hold) or an
editable SVG. Highcharts' exporter could not do this - one chart per omic,
headings and colour scales drawn in HTML around them, row labels and stars
drawn in HTML over them - so the figure is rebuilt in the browser as one
standalone SVG: each chart's own SVG verbatim, the HTML around it re-written
as SVG text, stars and gradients, omics laid side by side, on white in either
theme. Row labels and condition names are written out in full where the
panel truncates them, and a key explains the stars the figure uses.

The button's tooltip is closed when the menu opens: it is interactive and
opened exactly over the PNG item, taking its click.

Verified in Chrome on a five-omic job (light and dark, with and without Force
order) and on the three-group tutorial data with per-contrast stars; both
files download, and an oversized PNG is refused with a pointer to the SVG.
Docs: the Heatmaps page said there was no download; it, the diagram page,
the FAQ table and the three-group tutorial now describe it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l characters

An independent review of the export found four defects, each reproduced and
fixed:

- The row-under-the-pointer outline was exported. Nothing hides it when the
  pointer leaves, so hovering a row on the way to Download framed that row
  in every omic of the figure. The live outline is tagged before the clone
  and dropped from it.
- A tall heatmap came out below screen size without a word: the 16,000 px
  side cap pushed a ~600-row figure to 0.88x and a very tall one to 0.26x
  (9px labels at 2px), still saved as a success. The side cap is now
  Chrome's real ~32,000 px, and a figure that does not fit at 1x is refused
  before the spinner with a pointer to the SVG.
- With Force order an omic's charts stack in one column, and the full
  condition names of an upper chart ran under the next chart's white
  background. Only a chart with nothing stacked under it gets full names.
- A C0 control character in a name (a vertical tab, a DOS end-of-file) is
  legal in a TSV and illegal in XML 1.0, and made the SVG unparseable; they
  are stripped from everything the export writes. A throw while drawing the
  PNG now reaches the caller, instead of leaving its spinner up, and that
  failure no longer claims the heatmap is too large.

Verified in Chrome on the three-group job with Force order: no outline in
the file while it stays on the page, short names on the stacked chart and
full ones on the bottom charts, the refusal message before any spinner.
28 tests, each new one mutation-checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 13, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@TianYuan-Liu

Copy link
Copy Markdown
Member Author

Review record

Automated review. Claude review (gateway) completed on 4798341: 30 turns, 255 tool calls and 10 agents over the full diff, no issues found.

  • It raised two candidates and dropped both as false positives:
    • The ?v= bump: these view modules load through $.ajax with cache:false.
    • The Big-O mandate: it is aimed at backend pipelines.
  • The subscription Claude review job is red for a reason unrelated to this diff. The token is refused with oauth_org_not_allowed ("Your organization has disabled Claude subscription access for Claude Code"), so every PR will fail that job until access is re-enabled.
  • The first two gateway runs on 11cc7bc hung to the 25-minute limit with no output, although the gateway answered normally when probed. The third run, above, completed.

Independent review (a separate agent over 11cc7bc). Four defects, all fixed in 4798341, each with a test that was mutation-checked, and each verified in Chrome:

# Finding Fix
1 The row-under-the-pointer outline was exported. Nothing hides it when the pointer leaves, so hovering a row before Download framed that row in every omic. The live outline is tagged and dropped from the clone. It stays on the page.
2 A tall heatmap was silently scaled below 1x (9px labels down to 2px) and still saved as a success. The side cap is now Chrome's ~32k px. A figure that does not fit at 1x is refused before the spinner, with a pointer to the SVG.
3 Under Force order, full condition names on a stacked chart ran under the next chart's white background. Only a chart with nothing stacked under it gets full names. The others keep the screen's.
4 A C0 control character in a name made the SVG unparseable, and a throw while rasterising left the spinner up. XML-illegal characters are stripped from everything written. Any rasterise failure reaches the caller, with an accurate message.

Gate is green on 4798341. Merging with the owner's go-ahead.

@TianYuan-Liu
TianYuan-Liu merged commit 7b66845 into master Oct 13, 2026
8 of 9 checks passed
@TianYuan-Liu
TianYuan-Liu deleted the feat/download-heatmap branch October 13, 2026 05:57
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