Add visualize sos CLI command and heatmap --vmin/--vmax#228
Conversation
Wire the existing PlotManager Science On a Sphere renderer to the CLI so gridded data can be rendered as SOS frames (full-globe, PlateCarree, 2:1, edge-to-edge) directly from `zyra visualize sos` and from `zyra run` YAML. - Add `zyra visualize sos` (alias `render sos`) backed by PlotManager, with single (--input/--output) and batch (--inputs/--output-dir) modes and a fixed --vmin/--vmax color range for flicker-free frame sequences. - Add --vmin/--vmax to `visualize heatmap` so frame sequences can share a fixed color scale instead of self-scaling per frame. - Add a shared `load_data_array` helper (.nc/.nc4/.npy) in cli_utils. - PlotManager.sos_plot_data now returns the saved output path. - pipeline_runner: expand list/tuple arg values to multi-token flags so nargs options (e.g. inputs, extent) work from YAML/JSON pipelines. - Register sos in both the CLI and API manifest registration paths and regenerate the wizard capabilities manifests. - Add tests (single + batch render, 2:1 edge-to-edge, consistent color scaling across frames) and a sample FV3-Chem SOS pipeline. Signed-off-by: Claude <noreply@anthropic.com>
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
There was a problem hiding this comment.
Pull request overview
Adds a new visualization entry point for Science On a Sphere (SOS) frame rendering and introduces fixed color scaling (--vmin/--vmax) to avoid per-frame autoscaling “flicker” in sequences. It also improves zyra run pipeline argument handling to support argparse nargs options from YAML/JSON.
Changes:
- Added
zyra visualize sos(andrender sosalias) wired toPlotManager, supporting single and batch frame rendering with optional fixed--vmin/--vmax. - Added
--vmin/--vmaxtovisualize heatmapCLI plumbing. - Updated pipeline runner argv building to expand list/tuple args into multi-token flags; updated wizard capability manifests and added tests/samples.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/zyra/visualization/cli_sos.py |
New SOS CLI handler + parser registration. |
src/zyra/visualization/cli_utils.py |
Adds load_data_array helper for .nc/.nc4/.npy inputs. |
src/zyra/visualization/plot_manager.py |
sos_plot_data now returns the saved output path (or None). |
src/zyra/visualization/cli_register.py |
Registers sos subcommand; adds --vmin/--vmax to heatmap parser. |
src/zyra/visualization/__init__.py |
Mirrors CLI registration changes for the alternate registration path. |
src/zyra/visualization/cli_heatmap.py |
Passes through vmin/vmax to HeatmapManager.render. |
src/zyra/pipeline_runner.py |
Expands list/tuple stage args into multi-token argv entries. |
tests/visualization/test_sos.py |
New SOS CLI + helper tests; includes Cartopy-gated render assertions. |
tests/visualization/test_cli_commands.py |
Includes visualize sos --help in subcommand help smoke test. |
tests/misc/test_pipeline_runner.py |
Adds regression test for list expansion in _build_argv_for_stage. |
src/zyra/wizard/zyra_capabilities/visualize.json |
Updates wizard capabilities for SOS + heatmap --vmin/--vmax. |
src/zyra/wizard/zyra_capabilities.json |
Updates aggregated wizard capabilities for SOS + heatmap --vmin/--vmax. |
samples/pipelines/sos_fv3chem_frames.yaml |
Adds sample pipeline rendering SOS frames and composing them into MP4. |
| elif isinstance(v, (list, tuple)): | ||
| # Expand to a multi-valued flag (argparse nargs), e.g. | ||
| # ``inputs: [a, b]`` -> ``--inputs a b`` and | ||
| # ``extent: [-180, 180, -90, 90]`` -> ``--extent -180 180 -90 90``. | ||
| argv.append(to_flag(k)) | ||
| argv.extend(str(item) for item in v) |
| outputs: list[str] = [] | ||
| for src in ns.inputs: | ||
| dest = outdir_p / f"{Path(str(src)).stem}.png" | ||
| out = _render_one(ns, src, str(dest)) | ||
| if out: | ||
| logging.info(out) | ||
| outputs.append(out) | ||
| try: | ||
| print(json.dumps({"outputs": outputs})) | ||
| except Exception: | ||
| pass | ||
| return 0 |
| out = _render_one(ns, ns.input, ns.output) | ||
| if out: | ||
| logging.info(out) | ||
| return 0 |
| frame_a = np.full((32, 64), 25.0) | ||
| frame_b = np.full((32, 64), 25.0) | ||
|
|
||
| out_a = tmp_path / "a.png" | ||
| out_b = tmp_path / "b.png" | ||
|
|
||
| for arr, out in ((frame_a, out_a), (frame_b, out_b)): | ||
| pm = PlotManager(image_extent=[-180, 180, -90, 90]) | ||
| pm.sos_plot_data( | ||
| arr, | ||
| custom_cmap="YlOrBr", | ||
| output_path=str(out), | ||
| width=256, | ||
| height=128, | ||
| vmin=0.0, | ||
| vmax=50.0, | ||
| ) | ||
|
|
||
| from PIL import Image | ||
|
|
||
| a = np.asarray(Image.open(out_a).convert("RGB")) | ||
| b = np.asarray(Image.open(out_b).convert("RGB")) | ||
| assert a.shape == b.shape | ||
| # Identical inputs + identical fixed scale -> pixel-identical output. | ||
| assert np.array_equal(a, b) |
Deploying zyra with
|
| Latest commit: |
34da338
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b0ca9474.zyra.pages.dev |
| Branch Preview URL: | https://claude-github-issue-access-w.zyra.pages.dev |
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
a7a29f5 to
53fc45b
Compare
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
…e test - cli_sos: exit non-zero when a frame fails to render (single and batch modes) instead of silently returning 0, so `zyra run` pipelines see failures. - test_sos: rewrite the fixed-scale test to use two frames with different ranges sharing a mid-value, comparing the center pixel block; add a "teeth" assertion that self-scaling differs. Add failure-exit regression tests. Signed-off-by: Eric Hackathorn <62408375+Hackshaven@users.noreply.github.com>
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
Skip empty list/tuple stage args rather than emitting a bare `--flag`, which is a hard parse error for argparse nargs='+' options. Signed-off-by: Eric Hackathorn <62408375+Hackshaven@users.noreply.github.com>
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
Signed-off-by: Eric Hackathorn <62408375+Hackshaven@users.noreply.github.com>
|
🔁 Relay created/updated upstream PR: NOAA-GSL/zyra#287 |
Summary
Wires the existing
PlotManagerScience On a Sphere (SOS) renderer to the CLI and adds fixed color scaling — closing the gap the issue identified between "frames render" and "production-quality SOS animation." The library already did full-globe / PlateCarree / 2:1 / edge-to-edge rendering viaPlotManager; it just wasn't reachable from the CLI orzyra runYAML.Addresses NOAA-GSL/zyra#284.
Changes
New command
zyra visualize sos(aliasrender sos), backed byPlotManager(src/zyra/visualization/cli_sos.py). Renders gridded data as SOS frames (full-globe, PlateCarree, 2:1, edge-to-edge). Supports single (--input/--output) and batch (--inputs/--output-dir) modes with a fixed--vmin/--vmaxcolor range for flicker-free sequences. Defaults to 4096×2048; NaN/inf masked transparent.Heatmap (the "additionally" option in the issue)
--vmin/--vmaxtovisualize heatmapso frame sequences share a fixed color scale instead of self-scaling per frame.Supporting changes
load_data_arrayhelper (.nc/.nc4/.npy) incli_utils.py, validating--varbefore the heavy xarray import.PlotManager.sos_plot_datanow returns the saved output path.pipeline_runnerexpands list/tuple arg values into multi-token flags sonargsoptions (inputs,extent) work fromzyra runYAML/JSON.sosin both the CLI (cli_register.py) and API manifest (__init__.py) registration paths; regenerated both wizard capability manifests (only the newsosentries and heatmap--vmin/--vmaxdiffer).Tests & samples
tests/visualization/test_sos.py: single + batch render, 2:1 edge-to-edge assertion, and identical color scaling across frames. Render tests are gated behindDATAVIZHUB_RUN_CARTOPY_TESTS=1(repo convention); lightweight CLI/helper tests always run.tests/misc/test_pipeline_runner.py.samples/pipelines/sos_fv3chem_frames.yaml(FV3-Chem SOS frames → MP4), dry-run verified.Notes
api/{routers,server,models,schemas}, none of which were touched. The CLI command list is a runtime discovery endpoint, not baked into the OpenAPI schema, so the hash is unaffected.test_cli_heatmap_smokeinvokespython -m zyra.visualization.cli heatmapwithout thevisualizeprefix the compat wrapper now requires. It fails on the untouched baseline and is normally skipped in CI — left as-is, out of scope.🤖 Generated with Claude Code
Generated by Claude Code