Command-line rendering (s100)
EncDotNet.S100.Cli is a cross-platform .NET console tool, invoked as s100,
that renders any supported S-100 dataset — or a composite of several — to a
PNG, JPEG, or WebP image, and
validates datasets and exchange sets against the normative rule packs. It
drives the same portrayal and validation pipelines as the Avalonia viewer, but
rasterises through the Mapsui-free Skia headless renderers so it can run
without a UI — making it suitable for batch scripts (e.g. generating sea-ice or
surface-current previews) and CI conformance checks.
It offers four subcommands:
| Command | Purpose |
|---|---|
s100 render <dataset> <output>s100 render --layer … <output>s100 render <exchange-set> <output> |
Render one dataset — or composite several with --layer, or a whole exchange set / directory — to an image (PNG, JPEG, or WebP). |
s100 validate <dataset-or-exchange-set> |
Validate against the spec's normative rule pack (plus exchange-set signature/checksum integrity), with compiler-style output and suppression. |
s100 info <dataset> |
Show the detected spec, edition, headless-render capability, and (for time-series datasets) the available time steps. |
s100 list-specs |
List the supported product specifications and which support headless rendering. |
Why it matters
Use s100 when you need repeatable, headless rendering and validation in local
automation or CI pipelines.
Quick win
s100 render dataset.h5 out.png
Expected result: one rendered image (out.png) and zero UI dependencies.
Install (standalone download)
Each GitHub Release
attaches a self-contained, per-platform archive of s100. It bundles the
.NET runtime and SkiaSharp's native libraries, so no .NET installation is
required — just download, extract, and run.
| Platform | Asset | Run |
|---|---|---|
| Windows x64 | s100-<version>-win-x64.zip |
s100.exe render dataset.h5 out.png |
| Windows arm64 | s100-<version>-win-arm64.zip |
s100.exe render dataset.h5 out.png |
| macOS (Apple silicon) | s100-<version>-osx-arm64.tar.gz |
./s100 render dataset.h5 out.png |
| Linux x64 | s100-<version>-linux-x64.tar.gz |
./s100 render dataset.h5 out.png |
| Linux arm64 | s100-<version>-linux-arm64.tar.gz |
./s100 render dataset.h5 out.png |
# macOS / Linux
tar -xzf s100-<version>-<rid>.tar.gz
./s100 list-specs
The macOS archive is code-signed and notarized, so Gatekeeper verifies it
online on first launch. If a copied binary is still quarantined, clear the
attribute with xattr -d com.apple.quarantine ./s100. The Windows s100.exe
is Authenticode-signed via Azure Trusted Signing.
The same s100 executable also ships inside the SoundCharts application
bundle (under cli/); see the project README for that layout.
Quick start
# Render a dataset to a 1024x768 PNG (auto-detects the spec)
dotnet run --project tools/EncDotNet.S100.Cli -- render dataset.h5 out.png
# Inspect a time-series dataset and list its time steps
dotnet run --project tools/EncDotNet.S100.Cli -- info currents.h5
# Validate a dataset or exchange set against its normative rule pack
dotnet run --project tools/EncDotNet.S100.Cli -- validate exchange-set/
# Render the 7th time step at night-palette, larger canvas
dotnet run --project tools/EncDotNet.S100.Cli -- \
render currents.h5 currents.png --time-step 6 --palette night -w 2048 -h 1536
# Composite several products into one chart (repeated --layer + output)
dotnet run --project tools/EncDotNet.S100.Cli -- \
render --layer enc.000 --layer bathy.h5 --layer warnings.gml chart.png
# Composite an entire exchange set / directory (auto-detected)
dotnet run --project tools/EncDotNet.S100.Cli -- \
render exchange-set/ chart.png
How it works
The CLI detects a dataset's product specification from the file, runs that
spec's portrayal pipeline — the same pipeline the Avalonia viewer uses, wired to
the feature and portrayal catalogues bundled in the tool — and encodes the
result to the requested image format (PNG by default; JPEG or WebP via
--format or the output extension). Vector specs (S-101, S-401, and the GML products) and coverage specs
(S-102/104/111) each rasterise through their own headless Skia renderer; for
S-111 the current arrows are overlaid on the coverage. No UI or map projection
stack is involved, so it runs anywhere .NET does.
Render options
| Option | Default | Description |
|---|---|---|
-w, --width / -h, --height |
1024 × 768 |
Output image size in pixels. |
--palette |
day |
Colour palette: day, dusk, or night. |
--symbol-scale / --text-scale |
1.0 |
Symbol and text scale factors. |
--time-step <index> |
0 |
Zero-based time step for time-series datasets (S-104 / S-111). |
--background <hex> |
opaque white | Background colour, #RRGGBB or #AARRGGBB. |
--format <fmt> |
inferred from extension (else png) |
Output image format: png, jpeg (jpg), or webp. |
--quality <1-100> |
90 |
Encoder quality for lossy formats (jpeg, webp). Ignored for png. |
--bbox <minLon,minLat,maxLon,maxLat> |
auto-fit | Explicit WGS-84 viewport for a single dataset or composite image. Mutually exclusive with --center/--scale. Gridded coverages sample only the intersecting region. |
--center <lon,lat> + --scale <denominator> |
auto-fit | Explicit viewport by centre and scale denominator. Gridded coverages sample only the intersecting region. |
--no-text |
off | Suppress text/label drawing instructions (shorthand for --hide text). |
--hide <list> |
none | Suppress drawing-instruction categories — any of text, points, lines, areas — useful for clean fills on label-dense products such as S-411 sea-ice. |
--basemap <mode> |
none |
Draw a basemap beneath the chart data: none (default) or offline. offline composites the bundled Natural Earth 1:10m land layer (public domain, parchment tone 238,232,220) under all chart layers, projected with the chart's own viewport. Applies to both forms. Online tile basemaps are not available headlessly. |
--display-mode <mode> |
ice-concentration |
S-411 only. Select the sea-ice portrayal display mode: ice-concentration (total concentration, default), ice-sod (stage of development) or ice-navigational (provisional preview derived from total concentration — not a POLARIS/RIO navigational-risk computation). A single dataset carries the full WMO egg code, so the same data renders in any mode. Supplying the option for a non-S-411 dataset is an error; s100 info <dataset> lists the modes a dataset supports. |
Compositing multiple datasets
Pass a dataset per --layer (repeatable) to stack several products into one
image via the renderer-neutral S-98 interoperability engine. The output path
is the trailing positional argument or -o|--output:
s100 render --layer enc.000 --layer bathy.h5 --layer warnings.gml chart.png
s100 render --layer enc.000 --layer bathy.h5 -o chart.png --bbox -1.5,50.0,-1.0,50.5
s100 render --layer enc.000 --layer bathy.h5 chart.png --center -1.25,50.25 --scale 50000
The same viewport options apply to a single gridded S-102, S-104, or S-111 dataset:
s100 render bathy.h5 window.png --bbox -1.5,50.0,-1.0,50.5
Coverage viewport coordinates are WGS-84. For projected grids, the sampler transforms the requested window into the dataset's native CRS before selecting cells, then reprojects the sampled subset for output. A viewport that extends beyond the dataset paints only the intersecting coverage; the remaining image uses the selected background or basemap. Omitting viewport options preserves the existing dataset auto-fit.
The --palette, --symbol-scale, --text-scale, --time-step,
--background, --width/--height, --format/--quality,
--hide/--no-text, and --basemap options apply as in the single-dataset
form; suppression (--hide/--no-text) is global — it applies to every
layer, and --basemap offline draws the shared land layer beneath all layers.
| Composite-only option | Default | Description |
|---|---|---|
--layer <path> |
none | Add a dataset as a layer (repeatable). Any --layer selects the composite form. |
-o, --output <path> |
positional | Output image path (alternative to the positional <output>). |
--bbox <minLon,minLat,maxLon,maxLat> |
union auto-fit | Explicit shared viewport as a WGS-84 bounding box. Mutually exclusive with --center/--scale. |
--center <lon,lat> + --scale <denominator> |
union auto-fit | Explicit shared viewport by centre + scale denominator (e.g. --center -1.25,50.25 --scale 50000). |
Two behaviours differ from the single-dataset form:
- Ordering. The S-98 authority orders layers by display plane, so the
--layerorder is only a within-plane tiebreak — hand-ordering layers generally has no visible effect (an S-102 surface is already placed above an S-101 chart by its plane). - S-101 updates. The composite form does not apply S-101
sequential/sibling updates;
--no-updatesapplies to the single-dataset form only. Render an S-101 cell singly if you need its updates folded in.
When no --bbox / --center+--scale is supplied, the compositor auto-fits
the union extent of all layers to the requested --width × --height. The
auto-fit is antimeridian-aware: datasets whose geometry straddles the ±180°
seam (e.g. an S-411 Alaska product spanning ~175°E → ~225°E) are framed on their
true, narrow extent instead of collapsing to a near-global viewport (issue #413).
This applies to both single-dataset renders and --layer / exchange-set
composites.
Compositing a whole exchange set / directory
Rather than listing every --layer by hand, point render at an exchange
set and composite everything discoverable in it (issue #407). The source may
be a directory containing a top-level CATALOG.XML, a CATALOG.XML file, or a
.zip archive whose root holds one — passed positionally (auto-detected) or via
--exchange-set / --from:
s100 render exchange-set/ chart.png # positional directory
s100 render exchange-set/CATALOG.XML chart.png # positional catalogue
s100 render --exchange-set set.zip -o chart.png # explicit, ZIP
s100 render --from set/ chart.png --only S101,S102 # restrict to some specs
The datasets are discovered with the same exchange-set reader the viewer and
validate use, then composited through the identical S-98 engine — so every
option above (--bbox/--center/--scale, palette, --hide/--no-text,
etc.) applies unchanged. --exchange-set/--from is mutually exclusive with
--layer.
| Exchange-set option | Default | Description |
|---|---|---|
--exchange-set, --from <path> |
positional | The exchange set to composite (directory / CATALOG.XML / .zip). A directory / CATALOG.XML / .zip passed positionally is auto-detected. |
--only <specs> |
all | Restrict compositing to a comma-separated list of product specifications (e.g. --only S101,S128; hyphenation and case are ignored). |
Discovery notes:
- No S-101 updates. Like the
--layerform, the exchange-set form applies no S-101 sequential/sibling updates: only base and single cells are composited; update files (and orphan updates with no in-set base) are skipped. - Partial sets. Datasets whose product specification is unsupported, whose
file is missing, or that declare data protection (encryption — this CLI has no
decryption keys) are skipped with a warning on stderr rather than failing
the whole render. If nothing renderable remains,
renderexits non-zero. - ZIP archives are extracted to a uniquely-named temporary directory (cleaned up after rendering, even on failure); a large exchange set therefore needs transient temporary disk space of roughly its uncompressed size.
Capabilities
- Headless rendering for S-101; S-102; S-104 and S-111 gridded coverages; and the GML products S-122/124/125/127/128/129/131/201/411/421.
- Output formats: PNG, JPEG, and WebP. The format is inferred from the
output file extension (or set explicitly with
--format);--qualitycontrols the encoder quality for the lossy formats. - Text and category suppression via
--no-text/--hidefor cleaner previews of label-dense products. - Offline land basemap via
--basemap offline— composites the bundled Natural Earth 1:10m land layer beneath the chart data (headless; no online tiles). - Fixed-station coverage (S-104 / S-111 data coding format 3 / 8) is not rendered headlessly; the CLI returns a descriptive error.
- S-57 is not supported by the headless path.
See the project README under tools/EncDotNet.S100.Cli/README.md for the full
option reference and exit codes.
Troubleshooting
Warning
If rendering fails on S-104/S-111 fixed-station encodings, that's expected for
headless mode; inspect with s100 info first.
Tip
If output layering looks surprising in composite renders, remember S-98 interoperability rules determine cross-product ordering.