Optionalinit: {Optionalaxes?: DatasetAxis[]Optionalaxis?: DatasetAxisOptionalbounds?: DatasetBoundsexpressed in crs
Optionalcrs?: stringnative CRS; null when unknown
Optionaldata?: anyinlined payload only (raster bytes or parsed GeoJSON)
Optionalformat?: "geotiff" | "geojson" | "kml" | "kmz" | "shp" | "hazus"Optionalid?: stringOptionalkind?: "raster" | "vector"Optionalmeta?: anyOptionalname?: stringOptionalresolveUrl?: (url: string) => stringurl root only: resolver applied to the URL at force time
Optionalselector?: anyan in-file selection passed to the materializer as root.select
(normally produced by select() off a selector ref, not passed by hand)
Optionalurl?: stringa URI root (set via Dataset.fromURL); leaves data null
The primary (first) selection axis, or null.
The footprint, in crs. Constructor-known for a root (or an op whose result is knowable upfront,
e.g. clip), null when it genuinely isn't (e.g. a fresh reproject() node — the real bounds
depend on what the warp actually produces). Once this node is FORCED, reads the real value off the
memoized result instead — so ds.reproject(crs).grid().then(() => ds2.bounds) (ds2 being the
reprojected node) reflects the true post-warp footprint rather than staying stuck at the
construction-time placeholder.
Has this node been forced (decoded/warped) yet?
Free-form metadata (GDAL legend/unit/noData, …). Same self-updating rule as bounds: once forced,
reads off the memoized result — which matters for raster ops like reproject whose reprojector
refreshes dimension fields (width/height) that the pre-force value can't know.
The in-file selection this Dataset forces with, or null.
Warnings collected when this node was forced (implicit reprojection, defaults, …).
Aspect — the downslope compass bearing via Horn's method (rasterOps.aspectGrid). Lazy.
Clip (crop) to a bbox — the footprint shrinks to the overlap, snapped to pixel edges. Lazy.
Save the original bytes/content to disk. Inline roots only (a URL root has no local bytes yet).
document is ambient, so this costs nothing in the import graph.
Force + assert vector.
Force + assert raster.
Reduce this raster's pixels grouped by another raster's values — a TERMINAL returning a table, not a Dataset. The third kind of reduction in the model:
| verb | collapses | grouped by | returns |
|---|---|---|---|
reduce(op) |
a selection axis | — | a Dataset (one grid) |
zonalStats(zones) |
space | geometry | a table |
groupBy(by) |
space | another raster's values | a table |
This is what "one variable as a series against another" means concretely — mean depth per land-use class, rainfall binned by elevation, a rating curve. It is a distinct verb rather than an overload because the grouping key comes from data, not from the axis model or from geometry.
by is conformed onto THIS Dataset's grid (the same LHS-conform rule combine uses), and a pixel
counts only where both rasters have a value.
await depth.groupBy(landuse); // one row per distinct land-use code
await rain.groupBy(dem, { bins: 10 }); // ten equal-width elevation bands
await rain.groupBy(dem, { bins: [0, 100, 500, 2000] });
a raster Dataset whose values define the groups
Optionalopts: {Hillshade — a shaded-relief illumination raster via Horn's method (rasterOps.hillshadeGrid). Lazy.
Optionalopts: {Force this node: decode/fetch the root (or force the parent and apply this op), memoize, return the decoded RasterGrid | VectorFeatures. Repeated calls reuse the memoized result.
Mask by a polygon: pixels outside the polygon become transparent (NaN) on force — or inside, with
{ invert }. Footprint unchanged. Lazy: builds a node; the transform runs at terminal.
a SpatialFilter, or a ring/multi-ring of {lat,lng}|[lat,lng]
Optionalopts: { invert?: boolean } = {}Rasterize this vector Dataset onto a new grid (vector→raster, the kind-changing op —
PACKAGE_ROADMAP §2 "vectorize/rasterize"). field burns each feature's property value; omit for a
constant burnValue. Bounds default to this Dataset's own footprint; width/height are required
(a vector carries no inherent pixel resolution). Lazy.
Reclassify pixel values by rules (see rasterOps.reclassifyGrid) — EITHER a range-rules array
([{min?,max?,value?}], first-match-wins; a rule with no value is a "keep matched pixel's
value" band) OR a single callback (value, index) => number|null|undefined called once per
valid pixel with its raw value and flat row-major index (row*width+col), returning the new
value directly — not limited to a contiguous range, and skips rule-matching entirely (one call
per pixel instead of a per-rule scan), so it's both the more general and the cheaper form once
you need more than a couple of simple ranges. Either form: returning null/undefined (or no
rule matching) → unmatched → transparent (default) or kept. Lazy.
⚠️ A callback does NOT survive toRecord() (structured-clone can't carry functions) — forcing it
(.grid()) works fine in-session, but toRecord() on this node (or a descendant of it) throws
naming the op, rather than silently dropping it. Use range rules for a chain you need to
persist/reload from Storage.
Optionalopts: { unmatched?: "nodata" | "keep" } = {}Reduce this Dataset's selection axis to ONE grid — collapse a temporal/vertical stack (e.g. a stage/time series) via a per-pixel reducer. Sugar over select()+combine(): resolves every axis entry to a child Dataset, then LHS-conforms/reduces them exactly like combine() (PACKAGE_ROADMAP §2 "3-D / aggregation", the payoff of the axes model). Lazy.
Optionalop: "min" | "max" | "sum" | "mean" = "mean"Optionalopts: { axis?: string | number; method?: string; variant?: string } = {}Drop the memoized decode (evictable cache — the slider's stale-load guard calls this).
Reproject to toCrs as a LAZY op. Returns a new Dataset; the warp runs only on force, dispatched
through the registered reprojector (this file imports no GDAL). An exact same-CRS request is a
no-op that returns this. Rasters only (vectors are EPSG:4326 by spec).
Resample onto a specific target grid — lazy: the resample runs on force, via geo/resample.js's
resampleGrid (also directly barrel-exported as resampleGrid/alignRasters, so a caller can use
either this Dataset-shaped convenience or the raw function on pixel arrays). target is either a
resample-native meta object { width, height, bw, bs, be, bn }, or anything grid-shaped —
{ width, height, bounds: {north,south,east,west} } — e.g. another (already-forced) Dataset's
.grid() result. method defaults to 'nearest' (pure-JS, always available); the GDAL-only
methods (cubic/lanczos/mode/min/max/med/q1/q3) need a resampler registered via
registerResampler (the escape hatch) or forcing throws a clear error. The result adopts the
target's footprint/resolution; crs is unchanged (this resamples, it does not reproject).
Optionalopts: { method?: string; noData?: number } = {}Resolve one selection-axis entry into a child Dataset (lazy). Sugar over selectAxisEntry: it picks
the entry, resolves its ref, and carries the entry's opaque meta. Returns null when no entry
matches. Kind-neutral: which variant (raster vs vector) is the caller's call.
The ref decides what kind of child comes back (see DatasetAxisEntry):
opts.variant) → a URL-rooted child, format
inferred from the URL. One file per entry.{ select: {…} }) → a child rooted on the SAME source as this Dataset
(its bytes or URL, plus resolver), carrying the selector for the materializer. One file, many
entries — a NetCDF/GRIB2/Zarr time axis.Either way the child has no axes of its own: it is one payload, not a series, so it forces
through load()/grid() like any other Dataset and every op chains off it normally.
Optionalopts: { axis?: string | number; base?: string; nearest?: boolean; variant?: string } = {}
Optionalaxis?: string | numberwhich axis (index or name) to look up on
Optionalbase?: stringURL prefix prepended to a resolved URL ref (ignored by selector refs)
Optionalnearest?: booleanfall back to the closest numeric coord on a miss
Optionalvariant?: stringrequired when the matched entry's ref has named URL variants (e.g. {raster, vector})
Look up an entry on one axis by coordinate. Exact match first; with { nearest: true } (default) and
a NUMERIC axis, falls back to the closest coord. axis selects which axis (index or name).
Optionalopts: { axis?: string | number; nearest?: boolean } = {}
Optionalaxis?: string | numberwhich axis (index or name) to look up on
Optionalnearest?: booleanfall back to the closest numeric coord on a miss
Narrow one axis to the window [from, to] — a series in, series out operation, which is what
separates it from select(). select(coord) resolves to ONE payload and hands back something
forceable; selectRange hands back another selection-axis Dataset, still lazy, still unforceable
on its own. That is the point: everything that works on the full series works on the window,
reduce() most of all — "the mean of these six hours" is selectRange(a, b).reduce('mean'),
with no new machinery on either side.
Both bounds are inclusive, and the comparison is a plain >=/<= on the entry coords, so it
is type-agnostic: numeric coords (epoch milliseconds, a stage in feet) compare numerically, and
ISO-8601 strings compare lexicographically, which for ISO-8601 is the same as chronologically.
Reversed bounds are swapped rather than rejected. Unlike select() there is no nearest-match: a
window is already tolerant of falling between samples, so a range narrower than the sampling
interval matches nothing and returns null — which is honest, where snapping would silently hand
back a wider span than asked for.
Coords are compared as given — Date.parse(iso) for the epoch-millisecond axes parseSciwrid
builds. The engine stays domain-neutral about what a coordinate means.
const storm = ds.selectRange(Date.parse('2023-08-29T00:00Z'), Date.parse('2023-08-30T00:00Z'));
storm.axis.entries.length; // just that day's steps
await storm.reduce('max').grid(); // peak rainfall WITHIN the window
storm.select(coord); // and one step out of it, as usual
inclusive lower bound
inclusive upper bound
Optionalopts: { axis?: string | number } = {}
Optionalaxis?: string | numberwhich axis (index or name) to narrow
a Dataset whose chosen axis holds only the matching entries; null when
the axis is missing/empty or nothing falls inside the window
Slope — per-pixel terrain steepness via Horn's method, computed in pure JS on the decoded grid (no GDAL — see rasterOps.slopeGrid; PACKAGE_ROADMAP §2 "terrain"). Lazy.
Optionalopts: {Metadata view (without the heavy data payload). Axes are lightweight (URLs), so they stay.
A structured-cloneable record for Storage.put(). Default: the SOURCE + op recipe (small) — a root
inline Dataset still serializes with data and round-trips exactly as before (back-compat); a URL
root carries url; a derived node nests its INPUT records under inputs with its op. Pass
{ storeMaterialized: true } to also embed the decoded RasterGrid/VectorFeatures (the node must be
materialized already — call await ds.load() first).
Optionalopts: { storeMaterialized?: boolean } = {}
OptionalstoreMaterialized?: booleanalso embed the decoded RasterGrid/VectorFeatures snapshot
The named variants available at one axis coordinate, or null when that entry has none.
Variants are not an axis and deliberately never became one, so they need their own way to be
discovered — previously the only way to learn an entry had them was to call select() without one
and read the thrown error, which is no way to build a picker.
Why not an axis (see DECISIONS §1.1): a variant switches the Dataset's kind — .tif gives a
raster in an unknown CRS, .kmz a vector in EPSG:4326 — while every genuine axis preserves kind,
CRS and bounds. It is a choice of encoding of the same datum, not a coordinate in the data.
ds.variantsAt(19.5); // → ['raster', 'vector'] (or null)
ds.select(19.5, { variant: 'raster' });
Optionalopts: { axis?: string | number; nearest?: boolean } = {}StaticformatsEvery format that can be decoded right now — built-ins plus anything registered. Build a file
picker's accept list from it, or check an upload before parsing.
StaticfromStaticfromRehydrate a record (recipe or materialized). Structured clone drops prototypes, so this is required.
StaticfromA URI-rooted Dataset. It fetches + decodes into a RasterGrid/VectorFeatures on FORCE — nothing happens now. Format/kind are inferred from the URL when not given. This is what folds the decoded Grid/Features back into Dataset: a URL Dataset IS the materialized value, lazily.
Optionalopts: {Optionalbounds?: DatasetBoundsOptionalcrs?: stringdefaults to 'EPSG:4326' for vector formats, null (unknown) for raster
Optionalformat?: "geotiff" | "geojson" | "kml" | "kmz" | "shp" | "hazus"inferred from the URL's extension when omitted
Optionalkind?: "raster" | "vector"inferred from format when omitted
Optionalmeta?: anyOptionalname?: stringdefaults to the URL's filename
OptionalresolveUrl?: (url: string) => stringa resolver (host CORS-proxy/mirror) applied to the URL at force time
StaticregisterA JIT fallback invoked at most once, on the first force that finds no reprojector registered — how the GDAL warp auto-loads with no setup call.
StaticregisterRegister the decoder for a format (e.g. 'geotiff', 'nc').
StaticregisterSupply the ONE warp implementation reproject() forces through.
StaticregisterSupply a resampler for the methods the pure-JS path doesn't implement (cubic/lanczos/…), which
resampleTo({ method }) otherwise throws on. Synchronous and pixel-level — GDAL's own richer
methods go through the warp seam instead (see geo/resample.js).
1-D sugar for a single selection axis