Choose a path
Every Nonius chart draws the same thing — a series of points in time order — but the points can reach it four ways. Which one fits depends on two questions: where are the points now, and do they stop? (A word new to you — a tile, a summary — is in the glossary.)
| Your points | Path | What the chart holds |
|---|---|---|
| Already in the page, made or fetched by your own code | Push them | All of them |
| A finished history, small enough to send whole | A file | All of them, fetched once |
| A history too long to send, or one many people read | Tiles from a server | Only what the view on screen needs |
| Arriving now, as they happen | A live feed | The newest, as they come |
And when there is no browser at all — a report sent by e-mail, a desktop program, a server answering with a picture — the chart is drawn as an image.
“Small enough to send whole” is a matter of arithmetic. A point is 24 bytes (a time, a value and a weight, 8 bytes each), so ten million points are a 240 MB file, downloaded before the first frame and held in the browser’s memory. A day of readings once a second is 86,400 points, 2 MB: a file is the simple choice. Years of readings once a millisecond are hundreds of gigabytes: only tiles reach them.
Push them
Your page already has the points — computed, read from your own API, decoded from your own format. Hand them to the chart in time order, as many at a time as you like. The browser package’s example:
const chart = await createChart(canvas);
chart.setScale(2);
chart.push(t, v, w);
t, v and w are columns of integers; Your data
says how a reading becomes them, and why setScale(2).
A file
A series file holds a finished history in Nonius’s own format, NSB1.
The chart fetches it whole and reads its bytes where they land, with no
decoding step, so a file of millions of points is drawn as soon as it
has arrived.
const chart = await createChart(canvas);
await loadNsb(chart, await fetch(url));
The file is written once, from wherever the data is: a CSV, a Rust or .NET program, Node. Your data shows each.
Tiles from a server
When the history is too long to send, a server keeps it and sends tiles: small pieces cut for the view on screen, each about its pixels’ worth of bytes whatever the length of the history. Zoomed out on ten years, the chart reads summaries; zoomed into one second, the points of that second. The chart fetches tiles as a person pans and zooms.
import { attachTiles, createChart, fetchSeries, interact } from "nonius";
// A series on a Nonius server, drawn from its tiles: the chart holds only
// what its views need and fetches the rest as a person moves, however long
// the history. `url` is the series' address, `…/v1/series/{id}`.
export async function fromServer(canvas, url) {
// Asked for first, so the answer comes while the WebAssembly downloads.
const meta = fetchSeries(url);
const chart = await createChart(canvas);
// Opens the chart on the whole series.
const tiles = await attachTiles(chart, url, { meta });
const interaction = interact(chart, canvas, { tiles });
return { chart, tiles, interaction };
}
That is a whole module. fetchSeries is not awaited on purpose: the
chart takes the promise, and the answer arrives while the WebAssembly
downloads. interact is given the tiles, so that each view a person
moves to fetches the tiles it needs. url is the series’ address on
the server, http://host:port/v1/series/{id}. The server can be nonius-server,
from a series file, or your own, speaking the
Nonius protocol.
Serving shows how to run one.
A live feed
Points that are happening now come over a WebSocket, as numbered
frames sent by the Rust crate
nonius-live. The chart follows
the newest points; a frame the network loses is noticed by its number
and asked for again.
const chart = await createChart(canvas);
// A feed's values are integers and carry no scale: say how many decimal
// places they have (2 here: 10_050 is 100.50).
chart.setScale(2);
const stream = streamInto(chart, url);
An image
The same chart, drawn as a PNG on the CPU: no browser, no GPU. A
server answers GET /v1/series/{id}/png?from=…&to=…&width=…&height=…;
in .NET, a series draws itself:
// Light, for a box of 1200 × 400 CSS pixels at a pixel ratio of 2:
// the size is in device pixels, the ratio in thousandths.
byte[] image = series.StillPng(new Still(first, last + 1, 2400, 800, Theme.Light, RatioPermille: 2000));
File.WriteAllBytes(png, image);
and in Rust, nonius-render does. The image is the frame a browser
draws, pixel for pixel on the 2D canvas, so a report and a page agree.
Drawing shows each.
Not sure?
Start with a file. It is one fetch and one call, and the file it needs is what a server serves later: moving to tiles changes how the chart is fed, not the data.