ncv documentation
Documentation for ncv 0.5.21.
ncv is a terminal-native, read-only scientific data viewer for NetCDF-4,
GRIB2, and Kerchunk/VirtualiZarr reference manifests. It is designed for SSH
sessions on HPC systems and ships as a single Rust binary with no native
NetCDF/HDF5 runtime dependency.
The focus is fast inspection, not editing: slice a variable, inspect exact values, compare time/depth frames, and export a publication-ready view — all without leaving the terminal.
How these docs are organized
This site follows the Diátaxis framework — four quadrants for four different reader needs:
| Quadrant | Answers | Start here |
|---|---|---|
| Tutorials | “Teach me the basics” | Quickstart |
| How-To Guides | “Help me do a specific task” | Inspect a variable |
| Reference | “Tell me exactly what X does” | Keyboard · CLI · Environment |
| Explanation | “Help me understand why” | Data fidelity |
At a glance
- Open:
ncv data.nc,ncv forecast*.grib2, orncv s3://bucket/key - Navigate:
↑/↓variables,</>time,[/]depth,{/}files - Style:
cpalette,vreverse,a/llimits,fmask,sscale,zscope - Analyze:
Spaceplayback, click to pin,Enter/pfor plots - Share:
eexports PNG + SVG + JSON - Help:
?in-app help,Ctrl-P/:command palette,qquits cleanly
Get the tool
Download from GitHub Releases
or build with cargo install --path . --locked. See the
Quickstart for both paths.
Source, issues, and releases live at https://github.com/bbakernoaa/ncview-rs.
Tutorials
Learning-oriented walkthroughs that teach ncv by doing. Start here if you
have never run the viewer.
- Quickstart: your first view in five minutes — install
ncv, open a local or remote dataset, and make your first moves.
Quickstart: your first view in five minutes
This tutorial takes you from an empty terminal to an interactive view of a
scientific variable, including a remote public-bucket dataset. By the end you
will know how to install ncv, open a file, confirm you are looking at real
values, and find your way around the dashboard.
What you need: a terminal on Linux (x86_64), macOS (arm64), or Windows (x86_64), and any NetCDF-4 (HDF5) or GRIB2 file — or the public example below.
1. Install the viewer
Option A — download a release (recommended)
Grab the platform archive from the
GitHub Releases page,
unpack it, and put ncv on your PATH:
tar -xzf ncv-linux-x86_64.tar.gz
install -m 755 ncv/ncv ~/.local/bin/ncv
ncv --version
# ncv 0.5.21
On older HPC distributions with an aging glibc, use the static build
ncv-linux-x86_64-musl.tar.gz instead. Every release ships a SHA256SUMS
file — verify before installing on shared systems.
Option B — build from source
With Rust 1.90 or newer:
git clone https://github.com/bbakernoaa/ncview-rs.git
cd ncview-rs
cargo install --path . --locked
ncv --version
There is no native NetCDF/HDF5 runtime dependency to install.
2. Open a dataset
Point ncv at one or more files (shell globs are expanded for you):
ncv path/to/data.nc
ncv forecast*.grib2
You should land on the dashboard: a variable list on the left, the rendered field in the middle, a colorbar on the right, and a timeline/level band along the bottom. The status line shows the displayed slice’s min/max/mean and a finite count — your first fidelity check that real source values are in play.
3. Do the three things you will do most
- Change variable —
↑/↓moves through the variable list. - Step through time —
</>moves to the previous/next time slice;Spaceplays the timeline (-/+set playback speed). - Get help —
?opens the keyboard/mouse reference;Ctrl-P(or:) opens a searchable command palette. Pressqto quit — the terminal is always restored exactly as it was.
To start with a numeric region, give all four x/y bounds on the command line:
ncv --min-x -130 --max-x -60 --min-y 20 --max-y 55 path/to/data.nc
Inside the viewer, use Ctrl-P and choose Set view bounds to enter the
same four values. Both paths work with geographic coordinates and with
zero-based x/y indices when the dataset has no coordinate values. Box zoom
continues to work, and changing variables returns to that variable’s global
view. See the CLI reference for longitude conventions
and validation details.
4. Try a remote public bucket (optional)
ncv reads explicit object locations directly, with credential-free access
for public buckets:
ncv s3://noaa-gefs-pds/chem/2026/09/30/12/GEFS.chem.t12z.a2d_0p25.f000.grib2
If your environment has no cloud credentials, public buckets are read
anonymously without stalling. Force the mode with NCVIEW_ANONYMOUS_ACCESS=1;
any other value, including =0, leaves the choice to the automatic decision.
See
Access remote data for the details.
5. Know what you are looking at
- Values are never modified: missing, fill, NaN, and infinity cells are masked by the loader and reported honestly in the statistics.
- If the map looks like colored blocks rather than an image, you are on the portable cell fallback — one truecolor block per terminal cell, just less pretty than an image protocol. See Run over SSH.
eexports the current slice as PNG + SVG + JSON for slides and notebooks.
Next steps
- Inspect a variable in detail
- Style the map: palettes, limits, masks
- Full keyboard reference
- Command-line reference
- Why the values can be trusted
How-To Guides
Task-oriented recipes: each guide solves one concrete problem.
- Inspect a variable — read exact values, pin points, plot time series, handle curvilinear grids
- Style the map — palettes, limits, masking, scaling, zoom/pan, coastline backdrop
- Navigate time, depth, and files — playback, level bar, multi-file switching, session restore
- Access remote data — S3/GCS/Azure locations, public buckets without credentials
- Run over SSH — image protocols, cell fallback, tmux quirks
- Generate a GRIB2 manifest — Kerchunk /
VirtualiZarr reference JSON from a
.idxsidecar - Compare and export — difference mode, PNG/SVG/JSON exports for slides
- Derive variables with formulas —
VERDI-style formula editor across multiple files,
--formulaand--batch
Inspect a variable
Goal: read exact values, understand coordinates, and inspect a specific point of a dataset slice.
Open and select files
ncv data.nc # single file
ncv run*.nc # shell glob (expanded by ncv itself too)
ncv a.nc b.nc c.nc # explicit list; { and } switch files
Source files are opened read-only and never modified.
Move through variables and slices
| Do this | Press |
|---|---|
| Previous / next variable | ↑ / ↓ (or click the sidebar list) |
| Previous / next time slice | ← / →, or < / > |
| Previous / next depth slice | [ / ] (or the level bar) |
| Search all plottable variables | / |
| Run any action by name | Ctrl-P or : |
Read the status bar
The status line reports, for the displayed slice:
- the active variable name (subgroup variables appear qualified, e.g.
physics/temperature), - min / max / mean and a finite count — non-finite values are counted but never averaged into the statistics,
- the active time label and depth label when the axes exist.
Hover the mouse anywhere on the map to see that cell’s latitude, longitude, and source value. On files without recognizable coordinate variables the readout falls back to source indices — it says so explicitly rather than inventing coordinates.
Pin a point and inspect its history
- Click a map cell — it gets a
◆marker. - Press
Enterto open its time-series plot, orpfor the plot chooser:ttime series,dscatter,hhistogram,kCDF,uvertical profile. - Hover other points and press
mto add them to the same plot for multi-trace comparison.
Curvilinear and projected grids
Files with 2-D latitude/longitude fields render in projected mode. Press g
to switch between logical and projected grid. In projected mode the pixel →
coordinate lookup is nearest-coordinate; the status bar discloses the
approximation and keeps the original source indices visible. See
Data fidelity for what this does and does
not guarantee.
Related
- Style the map — palettes, limits, masks
- Keyboard reference
- Supported formats
Style the map
Goal: get a publication-worthy view — choose a colormap, set data limits, mask values, pick a scale mode, and add geographic context.
Choose and tune a colormap
| Task | Do this |
|---|---|
| Cycle built-in palettes | c or the sidebar palette button |
| Reverse the palette | v |
Add custom .ncmap files | put them in the working directory, or set NCVIEW_COLORMAPS=/path/to/maps (NCVIEW_LIB_DIR and NCVIEWBASE are also searched, including share/ncview/colormaps) |
Built-ins: Viridis, Plasma, Turbo, Inferno, Magma, Cividis, Cool, Warm,
Cubehelix, Spectral. Malformed .ncmap files are ignored at startup, so a bad
file can never break the viewer.
Set data limits
| Task | Do this |
|---|---|
| Automatic limits from the displayed slice | a |
| Type exact min/max | l — type into a field, Tab switches fields, Enter applies, Esc cancels |
| Mask everything outside a range | f |
| Linear ↔ logarithmic color scale | s |
| Scale scope: current-slice vs unzoomed-global | z or the sidebar scale-scope button |
| Reset zoom | r |
Manual limits always take precedence over either automatic mode. _FillValue
and missing values stay masked automatically regardless of your settings.
Zoom and pan
- Drag across the map to zoom to a rectangle.
- Drag a zoomed map to pan, or use
Shift+arrows. Shift-drag while already zoomed zooms again inside the current view.rreturns to the full map.
Zooming changes which values the current-slice automatic limits see; switch
the scope to global with z if you want colors to stay stable while zooming.
Geographic backdrop
Press b to layer a D3-inspired ocean, graticule, and filled Natural Earth
land backdrop beneath the raster. Valid data composites on top; fill/missing
cells reveal the geography. It is a presentation aid only — it never
changes data values or replaces a dataset-provided land/sea mask.
Select coastline detail with NCVIEW_LAND_DETAIL=auto|110m|50m|10m
(110m is the fast default once enabled; the overlay starts disabled so first
render stays light).
Export the result
Press e to write the current variable/time/depth slice as PNG + SVG + JSON.
See Compare and export.
Related
Navigate time, depth, and files
Goal: move through the fourth dimension efficiently — animate time, seek vertical levels, and switch between open datasets.
Step and play the time axis
| Task | Do this |
|---|---|
| Previous / next time slice | ← / → or < / > |
| Play / pause the timeline | Space, or click the Play/Pause button |
| Playback speed | - / + or the labeled speed controls on the timeline |
| Seek to a date | click or drag the timeline gauge |
| Switch active file | { / } (the header shows “opened file N/M”) |
When several files form one collection, the timeline spans all of them and file switching keeps you on the corresponding time slot of the new file.
Seek vertical levels
For variables with a depth/level axis, a full-width Level bar sits above
the time bar showing index/N plus the CF level label (falling back to the
dimension name when the file has none):
- Click or drag the level bar to seek.
- Scroll over it to step one level.
[/]step levels from anywhere.Tabfocuses the sidebar Level list;↑/↓move the cursor,Enterapplies it,Tab/Escleave focus.
Two-dimensional variables hide the level bar entirely — no dead controls.
Zoom to a region
Drag across the map to zoom to a rectangle. To enter numeric bounds, open the
command palette with Ctrl-P or :, search for Set view bounds, and enter
minimum and maximum x/y values. Tab moves between fields; Enter applies;
Esc cancels. The existing box zoom and pan controls remain available.
For geographic axes, longitude accepts signed degrees or 0..360, and
latitude must be within -90..90. The viewer maps coordinate endpoints to the
smallest source-index range containing the samples. Other axes use their
one-dimensional coordinate values when available, or inclusive zero-based
indices when they are dimension-only. Invalid or non-overlapping bounds leave
the accepted view unchanged. Curvilinear grids use a row/column envelope and
disclose that approximation in the status line.
Selecting a different variable returns to that variable’s global view, since its coordinate system and domain may differ. The zoom for a dataset is still remembered between launches; explicit CLI bounds override the restored zoom. See the CLI reference for startup examples.
Session restore
ncv remembers your last view (variable, slice, limits) per dataset and
restores it on the next launch. To start clean:
ncv --no-restore data.nc
The session directory can be relocated with NCVIEW_SESSION_DIR (useful on
shared HPC head nodes). See Environment reference.
Related
Access remote data
Goal: open NetCDF-4 or GRIB2 objects stored in S3, GCS, or Azure Blob without downloading them first.
Accepted location forms
ncv s3://bucket/key
ncv gs://bucket/key
ncv az://container/key
ncv abfs://container@account.dfs.core.windows.net/key # and abfss://
The terminal starts immediately; remote metadata and range reads happen on a loader thread, so the UI never blocks on the network.
Credentials
Ambient provider configuration is used when present (AWS_*, GOOGLE_*,
AZURE_*). Secrets are never accepted in arguments and never persisted.
Public buckets without credentials
On a laptop or any host with no cloud credentials, ncv detects that the
instance metadata service is unreachable and reads public buckets (for example
s3://noaa-gefs-pds/...) with anonymous, credential-free access instead
of stalling on metadata-token retries.
| Want | Do this |
|---|---|
| Force anonymous mode | NCVIEW_ANONYMOUS_ACCESS=1 |
| Keep the automatic decision | Unset it, or set any non-truthy value such as 0 |
See Remote access explanation for why the metadata probe matters.
GRIB2 over the network
Remote GRIB2 objects use bounded HEAD/range requests, plus an optional
colocated .idx sidecar when available. A large unindexed GRIB2 object is
refused rather than silently downloaded in full — add the .idx sidecar (or
generate a reference manifest, see Generate a GRIB2 manifest).
Remote NetCDF-4
Objects up to 64 MiB use a bounded fallback byte reader. Larger objects use a bounded metadata window and source-backed HDF5 range reads for chunk indexes, compressed chunks, masks, coordinates, and selected rows — for these larger objects, the whole object is never materialized. Unsupported HDF5 layouts fail with an actionable diagnostic rather than a partial view.
Related
Run over SSH
Goal: get the best map rendering on a remote HPC or cloud session, and know the portable fallback when graphics protocols are unavailable.
It just works (usually)
Connect with SSH and run ncv as usual. ncv detects the terminal’s image
capability after entering the session and uses Kitty, Sixel, or iTerm2 graphics
when available. The header always reports the active renderer:
Kitty truecolor · Sixel truecolor · iTerm2 truecolor · cell fallback
No X11 forwarding, native NetCDF/HDF5 runtime, or Chafa installation is needed.
The cell fallback is always available
If your terminal (or a multiplexer) supports no image protocol, ncv renders
the map with its own cell rasterizer: one truecolor background block per
terminal cell. It is fully functional — hover, click, zoom, and export all
work. Capability enhancement never becomes capability lockout.
Force a protocol when probing is blocked
tmux and some multiplexers swallow the capability query. Tell ncv what to
use:
NCVIEW_IMAGE_PROTOCOL=kitty ncv data.nc
NCVIEW_IMAGE_PROTOCOL=sixel ncv data.nc
NCVIEW_IMAGE_PROTOCOL=iterm2 ncv data.nc
NCVIEW_IMAGE_PROTOCOL=cells ncv data.nc # conservative, works everywhere
A conservative SSH launch that always renders:
NCVIEW_IMAGE_PROTOCOL=cells ncv data.nc
Open directly to a bounded region
You can set the initial extent in an SSH command without interacting with the
terminal first. Pass all four bounds together; geographic datasets accept
signed longitude or 0..360 longitude and latitude from -90..90:
ncv --min-x 0 --max-x 60 --min-y 20 --max-y 65 forecast.nc
The command also works for dimension-only data, where bounds are inclusive, zero-based x/y cell indices. In the interactive viewer, Set view bounds in the command palette provides the same entry fields alongside the existing box zoom. Invalid or non-overlapping bounds keep the current view intact. See the CLI reference for coordinate and error behavior.
Terminal-specific protocols
Some terminals or terminal versions have protocol-specific limitations. Set
NCVIEW_IMAGE_PROTOCOL to the protocol you want to use; ncv honors an
explicit setting as given. Use NCVIEW_IMAGE_PROTOCOL=cells for the portable
fallback.
Keep scientific cells crisp
By default the map is nearest-neighbor upsampled so zoomed scientific cells stay sharp. To allow interpolation:
NCVIEW_SCIENTIFIC_RENDERING=0 NCVIEW_IMAGE_FILTER=lanczos3 ncv data.nc
(Then i cycles filters interactively. See
Terminal protocols explanation.)
Related
Generate a GRIB2 reference manifest
Goal: turn a GRIB2 file and its NOAA-style .idx sidecar into a deterministic
Kerchunk-compatible reference JSON that tools like VirtualiZarr (or ncv
itself) can open as a virtual dataset.
The command
ncv manifest \
--format kerchunk \
--input forecast.grib2 \
--idx forecast.grib2.idx \
--output forecast.refs.json \
--strict
| Flag | Meaning |
|---|---|
--format | kerchunk or virtualizarr (the VirtualiZarr Kerchunk-parser profile) |
--input | GRIB2 source object |
--idx | matching .idx sidecar |
--output | destination manifest JSON |
--source-uri | URI to embed in byte-range references instead of the local path (use this when the manifest will point at a remote object) |
--strict | treat warnings and mismatches as errors |
What the manifest preserves
The manifest keeps raw GRIB2 table codes and the full Section 4 product
payload, so fields that share a display short name (for example aerosol
AOTK) remain independently selectable by species, particle-size interval,
wavelength, and product-template context. Opening the manifest later shows each
species as its own variable.
Use it
ncv forecast.refs.json # open the virtual dataset directly — no download
ncv s3://bucket/forecast.refs.json # or pair it with the remote source object
Reference manifests describe byte ranges — they never copy or modify the source GRIB2 data.
Table inventory
The pinned NOAA/NCEP GRIB2 table inventory and its update policy live in
tools/grib2_tables/README.md in the repository.
Related
Compare and export
Goal: see the difference between two datasets, and export a slice for a presentation or notebook.
Difference mode
Compare two files — or two sets of files — side by side:
ncv --diff control.nc experiment.nc
ncv --diff first="run1*.nc" second="run2*.nc"
actionable error otherwise.
For multiple files on each side, use the explicit first= (or 1=) and
second= (or 2=) prefixes to keep the two sets clear. Without prefixes, the
first file is side one and all remaining files are side two. Diff mode needs at
least one file on each side and reports an actionable error otherwise.
Export the current view
Press e to write the current variable/time/depth slice as three artifacts:
| File | Use it for |
|---|---|
| PNG | direct insertion into slides/reports — map, colorbar, and tick marks on a 16:9 canvas |
| SVG | editable text and full metadata; scales to any size |
| JSON sidecar | machine-readable CF/COARDS names, units, limits, and slice coordinates |
Tune the export
| Want | Set |
|---|---|
| Output directory | NCVIEW_EXPORT_DIR=/path |
| Opaque white canvas | NCVIEW_EXPORT_BACKGROUND=white (default is transparent) |
| Light text for dark slides | NCVIEW_EXPORT_TEXT=light |
| Custom SVG font | NCVIEW_EXPORT_FONT="Iosevka Nerd Font, sans-serif" |
PNG exports always use the embedded font so they stay deterministic on machines without your chosen font installed.
Related
Derive variables with formulas
Goal: compute new fields from the variables you already have — differences
between runs, unit conversions, wind speed, time-mean maps — and view or export
them like any other variable. The feature follows the
VERDI formula editor: every dataset you
open gets a number, and NAME[n] refers to variable NAME in dataset n.
Open several datasets at once
List every file on the command line (globs work, quoted or not). The order sets the dataset numbers used in formulas:
ncv base.nc sensitivity.nc # [1] = base.nc, [2] = sensitivity.nc
ncv "run_*.nc" # numbered in sorted order
ncv s3://bucket/a.nc local/b.nc # remote and local sources mix freely
The formula editor lists the numbered datasets at the top, so you never have to remember the order.
Use the formula editor
- Press
=(or open the command palette with:and choose Open formula editor). - Type an expression, for example
O3[1] - O3[2]. - Press
Enter. The result is added to the variable list and displayed immediately; time, depth, zoom, plots, and export (e) all work on it.
| Key | In the formula editor |
|---|---|
| any character | type into the formula |
Backspace | delete the last character |
Enter | add the formula (or replace the one with the same name) and plot it |
↑ / ↓ | recall a formula defined earlier into the input line |
Delete | remove the recalled formula |
Esc | close the editor (the draft is kept) |
Give a result a short name with name = expression; otherwise the expression
text itself is the variable name:
dO3 = O3[1] - O3[2]
wspd = sqrt(U^2 + V^2)
T_C = T - 273.15
Formula language
| Category | Syntax | Notes |
|---|---|---|
| Arithmetic | + - * / | usual precedence; parentheses group |
| Exponent | ^ or ** | right-associative: 2^3^2 = 2^9; -x^2 = -(x^2) |
| Trigonometry | sin(x) cos(x) tan(x) | radians |
| Logarithm / exponential | log(x) (natural; alias ln), log10(x), exp(x) | |
| Other | sqrt(x), abs(x) | |
| Time aggregation | mean(x) sum(x) min(x) max(x) | per grid cell, over every time step |
| Layer aggregation | layer_mean(x) layer_sum(x) layer_min(x) layer_max(x) | per grid cell, over every vertical level |
| Variables | NAME | the dataset currently on screen |
| Dataset variables | NAME[n] | dataset n (1-based) |
| Quoted names | "air-temp" or 'air temp' | names with characters other than letters, digits, _, . |
| Numbers | 2, 0.5, 1.5e-3 |
Aggregations can be combined with everything else, e.g. the anomaly from the
time mean is T - mean(T), and the column-mean difference between two runs is
layer_mean(O3[1]) - layer_mean(O3[2]).
How results are shaped
- The result takes the grid and dimensions of the first variable in the formula; all other grids must have the same 2-D shape.
- A time aggregation removes the time axis when nothing outside it varies in
time (
mean(T)is a single map;T - mean(T)still has every time step). Layer aggregations do the same for the vertical axis. - A variable without a time (or vertical) axis is reused for every step, so a
static field such as
areacan multiply a time-varying one. - Variables whose time lengths differ (other than 1) are rejected rather than silently misaligned.
Missing values and domain errors
- A cell that is missing, fill, or out of the valid range in any input is missing in the result.
- Aggregations skip missing samples; a cell with no valid sample is missing.
- Values produced by the arithmetic itself, like
logof a negative number or division by zero, are kept as NaN / ±Inf and shown with the usual non-finite diagnostics, so domain errors are never hidden.
Which dataset owns a formula
- A formula that uses any unqualified
NAMEis evaluated for each dataset: in a time collection,T * 2doubles every file. - A formula in which every reference has
[n]is attached only to the first referenced dataset, so it appears once in the timeline. Selecting it switches to that dataset automatically.
Pass formulas on the command line
--formula (repeatable) preloads formulas and selects the first one. The
VERDI spelling -formula is accepted too:
ncv --formula "dO3 = O3[1] - O3[2]" base.nc sensitivity.nc
ncv -formula "mean(PM25)" -formula "max(PM25)" cmaq_*.nc
Generate images without the terminal UI (batch mode)
Add --batch to evaluate every --formula and write the same PNG, SVG, and
JSON files as the e key, then exit — useful in scripts, cron jobs, and HPC
batch queues:
ncv --batch --export-dir plots \
--formula "dO3 = O3[1] - O3[2]" \
--formula "avg = mean(O3[1])" \
--time 12 --level 0 \
base.nc sensitivity.nc
| Flag | Default | Purpose |
|---|---|---|
--batch | off | export instead of opening the terminal UI |
--export-dir <DIR> | NCVIEW_EXPORT_DIR or . | output directory |
--time <INDEX> | 0 | zero-based time step to export |
--level <INDEX> | 0 | zero-based vertical level to export |
Each written PNG path is printed on its own line. File names follow
<dataset>_<formula>_t<time>_z<level>.{png,svg,json}; the export environment
variables in Compare and export apply.
A parse error, an unknown variable, or an out-of-range index stops the run with
exit status 2 and a message naming the formula.
Related
- Compare and export —
--diffmode and export options - Navigate time, depth, and files
- Command-line reference
- Keyboard reference
Reference
Information-oriented lookup: complete, accurate, and nothing else.
- Keyboard & mouse reference — every control in one table
- Command-line reference — flags, positional arguments, the
manifestsubcommand - Environment reference — every
NCVIEW_*variable with values and defaults - Supported formats and environments — what opens, what is rejected on purpose, platforms, terminals
- Library API reference — the Rust crate’s public surface, generated from rustdoc
Keyboard & mouse reference
Every interactive control, in one table. This mirrors the in-app help (?).
Keyboard
| Key | Action |
|---|---|
q / Esc | quit (Esc closes a dialog first) |
↑ / ↓ | previous / next variable |
← / → | previous / next time slice |
< / > | previous / next time slice |
Space | play / pause the timeline |
- / + | decrease / increase playback speed |
{ / } | previous / next file |
[ / ] | previous / next depth slice |
, / . | select previous / next extra dimension |
; / ' | previous / next index in selected dimension |
Tab | focus the sidebar level list (Tab/Esc leave it); in dialogs/plot chooser it switches field/axis |
Shift+arrows | pan the zoomed map |
c | open colormap chooser with a focused preview |
v | reverse the active colormap; in the chooser, reverse the focused preview |
i | cycle interpolation (set NCVIEW_SCIENTIFIC_RENDERING=0 to unlock) |
e | export current slice (PNG + SVG + JSON) |
= | formula editor — derive variables such as O3[1]-O3[2] or mean(T) |
a | automatic limits |
l | edit min/max limits |
f | mask data outside a range |
s | toggle linear/log color scale |
z | toggle current/global color scale |
r | reset zoom to the full view |
R | reset variable settings and view |
g | logical / projected grid |
b | toggle filled land/ocean map backdrop |
Enter | open pinned-point plot menu |
p | open plot chooser |
t / d / h | plot chooser: time series / scatter / histogram |
k / u | plot chooser: CDF / vertical profile |
m | add/remove the hovered point from the plot selection |
Ctrl-P / : | command palette — search actions, Enter to run |
/ | browse and search all plottable variables |
? | open/close this help |
Limits dialog
Type numbers directly, Tab switches between min and max fields, Enter
applies, Esc cancels.
Set view bounds dialog
Open the command palette with Ctrl-P / :, search for Set view bounds,
then enter minimum and maximum x and y values. Tab moves between the four
fields, Enter applies the bounds, and Esc cancels. Geographic axes accept
signed longitude or 0..360 longitude and latitude from -90..90. Other axes
use their coordinate values when available, or inclusive zero-based indices.
Unphysical or non-overlapping bounds are reported without replacing the
current view. For curvilinear grids, the status line notes that the region is
represented by a row/column envelope. Map dragging continues to provide box
zoom.
Mouse
| Gesture | Action |
|---|---|
| Move over map | hover row/col/value readout in the status bar |
| Click map | pin a point (◆), then Enter or p for plot choices |
Hover + m | accumulate multiple points for multi-trace plots |
| Click | sidebar buttons, variables, timeline play/pause, speed controls |
| Click / drag the level bar | seek the vertical level |
| Wheel over the sidebar | scroll the level or variable list |
| Drag map | zoom to a rectangle; drag a zoomed map to pan |
Shift+drag | zoom again while already zoomed |
Right-click or ? | close this help |
Related
Command-line reference
ncv [OPTIONS] [DATASET]... [COMMAND]
Options
| Flag | Values | Purpose |
|---|---|---|
--diff | — | Enable difference mode between two files or two sets of files |
--first <PATTERN> | path/glob | First file or glob pattern for diff mode |
--second <PATTERN> | path/glob | Second file or glob pattern for diff mode |
--formula <EXPR> | expression | Add a derived variable (repeatable); NAME[n] reads dataset n. -formula is accepted as a VERDI-style alias |
--batch | — | Evaluate every --formula and export PNG/SVG/JSON without opening the terminal UI |
--export-dir <DIR> | path | Output directory for --batch (default NCVIEW_EXPORT_DIR or .) |
--time <INDEX> | integer | Zero-based time step exported by --batch (default 0) |
--level <INDEX> | integer | Zero-based vertical level exported by --batch (default 0) |
--no-restore | — | Do not restore previous session state for the dataset(s) |
--grid <PATH> | path | MPAS mesh/coordinate file supplying latCell/lonCell or latVertex/lonVertex when the dataset has none |
--min-x <X> | number | Minimum x coordinate or zero-based x index for the initial view |
--max-x <X> | number | Maximum x coordinate or zero-based x index for the initial view |
--min-y <Y> | number | Minimum y coordinate or zero-based y index for the initial view |
--max-y <Y> | number | Maximum y coordinate or zero-based y index for the initial view |
-h, --help | — | Print help |
-V, --version | — | Print version |
Positional arguments
[DATASET]... — one or more NetCDF-4 or GRIB2 datasets to inspect. Shell
globs are supported (expanded by ncv itself, so quoting patterns works on
any shell). With --diff, inputs are split into two sets: use first=/1=
and second=/2= prefixes, or --first/--second, or simply list the first
set followed by the second.
Subcommand: ncv manifest
Create a Kerchunk-compatible GRIB2 reference manifest from a .idx sidecar.
| Flag | Values | Purpose |
|---|---|---|
--format | kerchunk | virtualizarr | Output profile; virtualizarr emits a VirtualiZarr-consumable Kerchunk profile |
--input | path/URI | GRIB2 source object |
--idx | path | Matching NOAA-style .idx sidecar |
--output | path | Manifest destination JSON |
--source-uri | URI | URI to place in byte-range references instead of the local source path |
--strict | — | Treat warnings and mismatches as errors |
ncv manifest --format kerchunk --input forecast.grib2 --idx forecast.grib2.idx \
--output forecast.refs.json --strict
Examples
ncv data.nc # open one file
ncv run*.grib2 # open a collection
ncv --diff control.nc experiment.nc # two-file difference mode
ncv --diff first="a*.nc" second="b*.nc" # set-vs-set difference mode
ncv --no-restore data.nc # ignore saved session state
ncv s3://noaa-gefs-pds/...f000.grib2 # remote public bucket
ncv --min-x -130 --max-x -60 --min-y 20 --max-y 55 data.nc
ncv --min-x 0 --max-x 100 --min-y 20 --max-y 80 image-only.nc
ncv --formula "d = O3[1]-O3[2]" a.nc b.nc # cross-file formula in the UI
ncv --batch --export-dir out --formula "mean(O3)" a.nc # headless export
Supply all four bounds together. Geographic axes use coordinate values; longitude
input accepts either signed degrees (-180..180) or 0..360, and latitude
must be between -90 and 90. The viewer maps the inclusive endpoints to the
smallest source-index rectangle containing those coordinate samples. Other axes
use their one-dimensional coordinate values when available, and inclusive,
zero-based cell indices when no coordinate values are available. Reversed,
non-finite, out-of-domain, or non-overlapping bounds produce an error.
Explicit CLI bounds take precedence over a zoom restored from the saved session.
Without CLI bounds, the usual restored view is preserved. Use r or the
command palette’s Reset zoom action to return to the selected variable’s
global view. For curvilinear grids the requested geographic rectangle is
approximated by its smallest row/column envelope; the status line reports this.
See Derive variables with formulas
for the formula language.
Related
Environment reference
Runtime configuration is done with environment variables. Set them before
launching ncv.
| Variable | Values | Default | Purpose |
|---|---|---|---|
NCVIEW_THREADS | integer | min(CPUs, 8) | Parallel rasterization threads (RAYON_NUM_THREADS is also honored). The conservative default avoids hogging HPC head-node resources. |
NCVIEW_IMAGE_PROTOCOL | kitty, sixel, iterm2, cells | auto-detected | Override graphics capability detection. Auto-detection identifies iTerm2 from environment hints; Kitty and Sixel are selected only through this override or a positive capability match. An explicit setting is honored as given. |
NCVIEW_SCIENTIFIC_RENDERING | 1 / 0 | 1 (locked) | Keep nearest-neighbor scientific rendering, or allow interpolation (0). |
NCVIEW_IMAGE_FILTER | nearest, lanczos3, catmull-rom, triangle, gaussian | nearest | Image filter when scientific rendering is unlocked. |
NCVIEW_CELL_PIXEL_SIZE | WxH pixels, e.g. 10x20 | from the terminal | Terminal cell size used to size graphics images and map mouse clicks. Set it when the terminal does not report usable pixel dimensions (common over SSH); known 640x480 placeholders and reports under 4x8 pixels per cell are ignored, and 10x20 is assumed. |
NCVIEW_ANONYMOUS_ACCESS | truthy (1, true, yes, on, y) | auto | Force credential-free access for public S3/GCS/Azure objects. Any other value, including 0, leaves the automatic decision in charge rather than forcing signed access. Auto mode uses ambient credentials if present, otherwise a cached metadata-endpoint probe, and selects anonymous access when neither yields credentials. |
NCVIEW_LAND_DETAIL | 110m, 50m, 10m, auto | 110m | Coastline resolution after enabling the b backdrop; auto selects by zoom level. |
NCVIEW_UG_INTERMEDIATE_SPACING_DEG | degrees, 0 < x <= 90 | 0.25 | Cell size of the regular latitude/longitude grid that unstructured-grid fields (currently MPAS) are regridded onto. Smaller values sharpen the map but cost more per read. |
NCVIEW_COLORMAPS | directory | — | Extra directory of .ncmap files for the palette catalog. |
NCVIEW_LIB_DIR | directory | — | Also searched for colormaps, including share/ncview/colormaps. |
NCVIEWBASE | directory | — | Also searched for colormaps, including share/ncview/colormaps. |
NCVIEW_EXPORT_DIR | directory | current directory | Where e writes exports. |
NCVIEW_EXPORT_BACKGROUND | transparent, white | transparent | Export canvas background. |
NCVIEW_EXPORT_TEXT | dark, light | dark | Export text/border color (light for dark slides). |
NCVIEW_EXPORT_FONT | font list | Fira Code, monospace | SVG export font selection (PNG always uses the embedded font). |
NCVIEW_SESSION_DIR | directory | platform default | Where per-dataset session state is stored. |
Notes
- Secrets are never read from arguments; cloud credentials come from ambient
provider configuration (
AWS_*,GOOGLE_*,AZURE_*). NCVIEW_IMAGE_FILTERandi-cycling only take effect whenNCVIEW_SCIENTIFIC_RENDERING=0; the default preserves exact scientific cells.- Malformed
.ncmapfiles discovered via any colormap path are ignored at startup rather than causing an error.
Related
Supported formats and environments
Inputs ncv opens
| Input | Forms | Notes |
|---|---|---|
| NetCDF-4 (HDF5) | .nc, .nc4, .hdf5-magic NetCDF-4 | Local and remote; subgroups appear as qualified names (physics/temperature) |
| GRIB2 | .grib, .grib2, .grb, .grb2, GRIB-magic detection | Local and remote; remote objects use .idx sidecars or bounded range reads |
| Kerchunk / VirtualiZarr reference manifests | .json | Virtual datasets describing byte ranges; never copies or modifies source data |
| Remote object locations | s3://, gs://, az://, abfs[s]:// | See Access remote data |
Inputs ncv rejects (on purpose)
| Input | Behavior |
|---|---|
| NetCDF-3 | Rejected before terminal entry with an actionable diagnostic |
| Arbitrary HDF5 (non-NetCDF-4) | Rejected with an actionable diagnostic |
| Large unindexed remote GRIB2 | Refused rather than silently downloaded in full |
Rejection happens before the TUI starts, so your terminal is never left in a modified state.
Platforms
Release archives are published for:
| OS | Architecture |
|---|---|
| Linux | x86_64 (glibc) and x86_64 musl (static, for older distributions) |
| macOS | arm64 |
| Windows | x86_64 |
Each release ships a SHA256SUMS file. The binary is pure Rust with no native
NetCDF/HDF5 runtime dependency.
Terminals and SSH
- Image protocols: Kitty, Sixel, iTerm2 (auto-detected after terminal entry;
overridable with
NCVIEW_IMAGE_PROTOCOL). - Portable fallback:
ncv’s own cell rasterizer — one truecolor background block per terminal cell — always functional, including over OpenSSH and tmux. - Explicit
NCVIEW_IMAGE_PROTOCOLsettings are honored as given on all terminals; the portable cell renderer remains available withNCVIEW_IMAGE_PROTOCOL=cells. - No X11 forwarding required.
Data safety
All datasets are opened read-only. Source files are never modified. Missing, fill, NaN, and infinity values are preserved and masked honestly — see Data fidelity.
Related
Library API reference
ncview-rs exposes its internals as a Rust library (ncview-rs) alongside the
ncv binary. The full, always-current API documentation is generated from
the code’s own rustdoc comments and published with this site under the
api/ section.
- Crate:
ncview-rs(MIT OR Apache-2.0) - Minimum Rust: 1.90 (edition 2024)
- Public surface:
#![deny(unsafe_code)]— the whole crate is safe Rust
Module map
| Module | Responsibility |
|---|---|
data | Read-only dataset and slice abstractions: NetCDF-4/HDF5, GRIB2 (local and remote), coordinates, diff, formula-derived variables (data::formula::FormulaSource), reference manifests, virtual datasets |
storage | Provider-neutral remote object access: locations (s3://, gs://, az://, abfs[s]://), range cache, typed operations, session state. All network work lives behind this module and its background runtime |
render | Color normalization and terminal raster rendering: palettes, land mask, map background, image-protocol selection, viewport-bounded rasterization |
ui | Ratatui view composition: dashboard, canvas, colorbar, sidebar, level bar, timeline, charts, help, popups, layout |
events | Terminal event and session handling: keyboard input, mouse, terminal enter/restore lifecycle |
analysis | Scientific analysis and coordinate-mapping primitives: mapping, projection, time series, equation-editor formula parsing and evaluation (analysis::formula) |
app | Application state: view model, generation counters, variable/palette/limits state, decoded working-set bounds |
export | File exporters for the current scientific view (PNG, SVG, JSON sidecar) |
error | NcvError — actionable errors carrying file/variable/dimension/capability context |
Reading the generated docs
The api/ section of this site is rebuilt automatically whenever the code
changes, so signatures, doc comments, and trait implementations always match
the source at that commit. It includes:
- every public type, function, and method with its rustdoc,
- the
NcvErrorvariants and their messages, - module-level overviews (the table above comes from them).
For behavior contracts, the narrative pages are the entry point:
- Data fidelity — what values guarantee
- Terminal protocols — rendering selection
- Remote access — byte-range I/O design
Explanation
Understanding-oriented discussion: how ncv works and why it behaves the way
it does.
- Why the values can be trusted — source-value fidelity, missing/fill/NaN handling, disclosed approximations
- How
ncvchooses a terminal renderer — capability detection, the cell fallback, protocol quirks over SSH and tmux - How remote access works — byte-range reads, the metadata probe, anonymous public-bucket access
Why the values can be trusted
ncv is a scientific viewer first and a visualization tool second. Its core
correctness rule is source-value fidelity: every displayed value,
coordinate, limit, axis, and index is traceable to the source dataset, and
presentation never silently alters data.
Missing, fill, NaN, and infinity
- NetCDF
_FillValueand missing-value attributes are applied by the data loader; masked cells are never painted as if they were data. - Non-finite floating-point values (
NaN,+Inf,-Inf) are preserved — they are never coerced to zero, dropped from counting, or averaged into statistics. The status bar’s min/max/mean cover finite values only, and the finite count tells you how much of the slice was real data. - The
fdata mask keeps only values between chosen bounds, but automatic fill/missing masking stays active regardless — masking is additive, never a way to make invalid values look valid.
Coordinates and indices
- Coordinate variables are read from dataset metadata, including 2-D curvilinear latitude/longitude fields.
- When a file has no usable coordinate variable, the hover readout falls back to source indices and says so, rather than inventing plausible coordinates. Grouped (subgroup) coordinate fields currently use the same honest fallback while retaining the variable’s data and metadata.
- Pinned-point time series and plots use the original source indices, so a click always identifies the exact source cell.
Where approximation is allowed — and how it is disclosed
Rendering and projection MAY approximate spatial presentation. The places they do are always labeled:
- Curvilinear lat/lon: 2-D coordinate planes are read and used to report
each cell’s latitude/longitude, but pixels map to source cells by index, so
a cell’s value and readout are always the exact source cell. Nearest-lat/lon
projection helpers (
ProjectionIndex,projected_lookup) exist in the library but are not yet wired into the display; theggrid-mode toggle records a preference that no renderer consumes yet. - Viewport-bounded aggregation: when a slice is larger than the terminal
canvas, source cells are reduced into display bins before color mapping.
This bounds memory to the visible area — it changes how densely values are
drawn, not their values. Nearest-neighbor is the default so scientific
cells stay exact at any zoom; interpolation (
NCVIEW_SCIENTIFIC_RENDERING=0plus a filter) is opt-in and clearly a display choice.
The coastline backdrop (b) is likewise presentation-only: it never changes
data values and never replaces a dataset-provided land/sea mask.
Limits and scaling honesty
- Automatic limits follow the displayed slice (current-scope) or the full
unzoomed range (global-scope) — the mode is visible, switchable (
z), and both are computed from actual data. - Manual limits always win over automatic modes.
- Log scaling is only applied when meaningful; the scale mode is reported in the UI.
Read-only by construction
Datasets are opened read-only. Reference manifests (Kerchunk/VirtualiZarr) describe byte ranges of the original files; opening one never copies or mutates the source. Exports write new files and leave sources untouched.
Related
How ncv chooses a terminal renderer
The map is a true-color image, but terminals speak different graphics
languages. ncv selects the best one automatically — and guarantees a usable
view when none exists.
Capability detection, carefully
After entering the terminal session, the viewer settles on one of four renderers
— Kitty, Sixel, iTerm2, or the cell fallback. Capability detection is
deliberately conservative: querying capabilities over SSH or through
multiplexers can hang for seconds while waiting for escape-sequence responses,
so ncv avoids the blocking query path. Instead it derives the choice from the
observed window size and environment hints. Those hints can only identify
iTerm2 automatically; Kitty and Sixel are used when you request them explicitly
with NCVIEW_IMAGE_PROTOCOL, and the user can override anything it gets wrong.
The header always reports the active renderer (Kitty truecolor, Sixel truecolor, iTerm2 truecolor, cell fallback, or cells (graphics unavailable) after a protocol encoding failure), so you never have to guess
what is actually being used.
The fallback is a first-class mode
If no image protocol is available — plain OpenSSH, tmux with passthrough
disabled, an older terminal — ncv renders the map with its own cell
rasterizer: one truecolor background block per terminal cell. Everything still
works: hover readouts, clicking, zoom, export. The project rule is capability
enhancement must never become capability lockout: the fanciest protocol is
never required for any workflow.
Explicit protocol selection
NCVIEW_IMAGE_PROTOCOL is honored as given, even when the terminal may not
support that protocol correctly. To force the portable renderer, set
NCVIEW_IMAGE_PROTOCOL=cells.
- iTerm2 uses its own protocol rather than Kitty/Sixel.
- tmux and other multiplexers may swallow capability queries; set
NCVIEW_IMAGE_PROTOCOLexplicitly to bypass capability detection for the protocol choice.
Scaling choices
By default the image is nearest-neighbor upsampled so a zoomed scientific cell
remains one crisp cell — the pixels you see correspond to source values.
Interpolation filters (Lanczos, Catmull-Rom, triangle, Gaussian) are available
only after unlocking scientific rendering (NCVIEW_SCIENTIFIC_RENDERING=0),
making it explicit when a display is smoothing data rather than showing it.
Related
How remote access works without downloading files
ncv opens S3, GCS, and Azure Blob objects by byte-range reading — it
fetches only the metadata and data slices you actually look at.
Range reads, not downloads
A NetCDF-4/HDF5 file stores its structure in headers and its data in chunks
scattered through the file. ncv reads:
- a bounded metadata window (growing only to 64 MiB) to find variable definitions, chunk indexes, and coordinates, then
- targeted range requests for the compressed chunks inside your view.
Objects larger than 64 MiB are never materialized in full. Objects up to 64 MiB use a simpler bounded fallback reader that loads the whole (small) object into memory in one range read. Unsupported HDF5 layouts and non-unit-stride contiguous hyperslabs fail explicitly with an actionable diagnostic instead of showing a partial or wrong view — the same fidelity rule as local files.
GRIB2 over the network follows the same principle with HEAD/range requests,
optionally guided by a colocated NOAA-style .idx sidecar that maps records
to byte ranges. A large unindexed GRIB2 object is refused rather than silently
downloaded in full. Reference manifests (ncv manifest ...) are the portable
alternative: they record the byte ranges as JSON so any Kerchunk-compatible
tool gets the same map.
Why public buckets used to hang (and the metadata probe)
Cloud SDKs normally look for credentials in ambient configuration, then ask
the instance metadata service (a link-local endpoint, e.g.
169.254.169.254) for a role token. On a laptop that endpoint does not
exist — and without care, every object request retries the token lookup for
minutes, making public buckets appear broken.
ncv now probes the metadata endpoint once per process (a cached TCP
connect with an 800 ms budget for each candidate endpoint; custom endpoints set
through AWS_EC2_METADATA_SERVICE_ENDPOINT, GCE_METADATA_HOST or
GCE_METADATA_IP, MSI_ENDPOINT, or IDENTITY_ENDPOINT are probed too). When
there are no ambient credentials and no reachable metadata service, it uses
anonymous, credential-free access — the correct mode for public research
buckets such as s3://noaa-gefs-pds. Provider-internal retries are bounded
(three retries, ten seconds total) and each ncv range request adds its own
bound of three attempts with a 30-second per-request timeout and 100 ms to 2 s
exponential backoff, so a genuinely misconfigured private bucket fails fast with
a clear error.
Force the behavior with NCVIEW_ANONYMOUS_ACCESS=1 to always use anonymous
access. Any other value, including 0, leaves the automatic decision (ambient
credentials, then the metadata probe) in charge — it does not force signed
access. The probe and open happen on a background loader thread, so the
terminal UI starts before the network does any work.
Credentials and safety
- Credentials come only from ambient provider configuration (
AWS_*,GOOGLE_*,AZURE_*); secrets are never accepted in command-line arguments and never persisted. - Remote sources are read-only, exactly like local ones.
Related
sequenceDiagram
autonumber
participant UI as Terminal UI
participant Loader as Background loader
participant Store as Object store
participant Cloud as S3 or GCS or Azure
UI->>Loader: open s3 bucket key
Loader->>Store: build provider store
Store->>Store: decide anonymous or signed
Loader->>Store: HEAD object
Store->>Cloud: bounded HEAD request
Cloud-->>Store: object size and etag
Loader->>Store: read magic bytes range
Store->>Cloud: range read
Cloud-->>Store: first bytes
Loader->>Store: read metadata window
Store->>Cloud: bounded range reads
Cloud-->>Store: header bytes plus chunk index
UI->>Loader: request slice for current view
Loader->>Store: read visible chunk ranges
Store->>Cloud: bounded range reads
Cloud-->>Store: compressed chunks
Loader-->>UI: decoded slice with source values intact