name: plot description: Render a 2D heatmap or 1D time series PNG from any gridded or station weather-skills envelope Zarr. Use when you need to visualize a single dataset as a map or as a time/step profile. license: MIT compatibility: Requires Python 3.12 and uv. metadata: version: "0.1.13" catalog-group: visualization
plot
Source-agnostic single-dataset visualization. Two styles:
heatmap— CartoPyPlateCarreemap with country/coastline boundaries. If the input has astep(ortime) dimension, panels are laid out one per step (up to 4 columns; rows added as needed) with a shared color scale and a horizontal colorbar spanning all panels at the bottom. Ensemble members (numberdim) are averaged before plotting. Use--indexto override the default reduction for any other extra dim.timeseries— 1D profile. Averages across all non-time dims.
When to use
- Producing a quick-look forecast map panel for any gridded envelope.
- Producing a time/step profile for a gridded or station envelope.
For two-dataset comparisons, use the plot-compare skill.
Usage
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py --input <in.zarr> --output <out.png> \
[--variable NAME] [--style heatmap|timeseries] \
[--colormap NAME] [--title TEXT] [--index DIM=POS,...] \
[--extent LON_MIN,LON_MAX,LAT_MIN,LAT_MAX] \
[--cities JSON_OR_PATH] [--fontsize N] [--bbox N/W/S/E] \
[--mask-geojson PATH]
Arguments
--input,-i— Zarr input.--output,-o— PNG output path.--variable,-v— variable name. Defaults to the first data variable.--style—heatmap(default) ortimeseries.--colormap— either a matplotlib colormap name (defaultviridis) or a comma-separated list of colors to interpolate between (e.g.white,wheat,green). Named matplotlib colormaps cannot contain commas, so the presence of a comma unambiguously selects the custom-list form.--title— optional plot title.--index— dim selections likestep=3,number=0. A dim may take several comma-separated positions, e.g.step=0,1,2, which keeps the dim with just those positions. Negative positions are accepted and count from the end, Python-style (step=-1is the last step). Repeating a dim is an error, as are positions that address the same element — including negative aliases (step=0,-3on a 3-step axis). List selections are only supported on the panel (step/time) dimension; other dims take a single position. Applied before panel layout: e.g.--index step=2reduces to a single-panel map at step 2, while--index step=0,1,2panels exactly those three steps; otherwise all steps are paneled. Panels follow the order given in the spec (step=2,0renders position 2 first). Heatmap-only — with--style timeseriesthe spec is syntax-checked, then ignored with a stderr warning.--extent— heatmap map extent aslon_min,lon_max,lat_min,lat_max. Defaults to the data's cell-center min/max expanded by half the mean grid spacing on each side, so the view matches whatpcolormeshactually draws (it treats coords as cell centers and extends ±½ spacing).--cities— heatmap city overlay. Inline JSON like'{"Windhoek": [-22.55, 17.08]}'or a path to such a JSON file. Off by default.--fontsize— base font size for titles/colorbar label (default 16).--bbox— optionalN/W/S/Edecimal degrees. Slices the gridded input to the bbox usingda.sel(...)and sets the heatmap extent to that bbox. This is a rectangular slice (admin boundaries are drawn as decoration bycfeature.BORDERS/cfeature.COASTLINE, not used as a mask). To restrict to a country, get its bbox from theresolve-regionskill. Longitudes in[0, 360]are auto-wrapped to[-180, 180]before slicing so global grids still intersect negative-lon bboxes.--extent(if passed) wins over the bbox-derived extent. Heatmap-only —--style timeseriesignores--bboxwith a stderr warning. Default unset → no slice.--mask-geojson— optional path to a GeoJSON boundary polygon (e.g. the--geojsonoutput of theresolve-regionskill). Gridded cells whose centers fall outside the polygon are set to NaN before plotting, so the heatmap shows the country shape rather than its bounding rectangle. All features in the file are unioned. Combine with--bboxto crop to the rectangle first, then mask to the polygon within it. Heatmap-only —--style timeseriesignores it with a stderr warning. Default unset → no mask.
Output
A PNG at --output. The colorbar label resolves from variable attrs:
long_name → GRIB_name → bare variable name → "value", suffixed
with [units] when the units attr is present.
Provenance
Every PNG carries two tEXt chunk keys written via matplotlib's
savefig(metadata=...):
weather_skills_history— a JSON-encoded array of{skill, version, args, input}entries with the same schema used for the zarrweather_skills_historyattribute. Each entry records one pipeline step. The last entry is thisplotinvocation; preceding entries are the upstream chain inherited from the input zarr'sweather_skills_history(empty array if the input had none — a stderr warning is emitted in that case and the array contains only theplotentry).Software— set toforecasting-skillsso generic image tools likeexiftoolsurface the producer prominently.
Read-back:
python3 -c "from PIL import Image; import json; print(json.loads(Image.open('out.png').info['weather_skills_history']))"
Or:
exiftool out.png
Examples
Multi-step forecast panel:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py -i /tmp/ecmwf_namibia.zarr -o /tmp/ecmwf.png \
--variable tp --style heatmap --colormap magma --title "S2S precip"
Multi-step forecast panel with a custom precipitation palette:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py -i /tmp/ecmwf_namibia.zarr -o /tmp/ecmwf.png \
--variable tp --style heatmap \
--colormap white,wheat,lightgreen,green,lightblue,blue,yellow,orange,red,purple
Single-step map with cities and an explicit extent:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py -i /tmp/ecmwf_namibia.zarr -o /tmp/ecmwf_step0.png \
--variable tp --index step=0 \
--extent 11,29,-30,-15 \
--cities '{"Windhoek": [-22.55, 17.08]}'
Country-shaped map masked to a boundary polygon:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py -i /tmp/chirps_kenya.zarr -o /tmp/kenya.png \
--variable precip --bbox 5.0/33.9/-4.7/41.9 --mask-geojson /tmp/kenya.geojson
Time series:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/plot.py -i /tmp/ecmwf_namibia.zarr -o /tmp/ts.png \
--variable tp --style timeseries