@fxi/zartigl 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -12
- package/dist/backdrop-DctkJ45Q.js +79 -0
- package/dist/catalog.d.ts +175 -16
- package/dist/catalog.js +263 -58
- package/dist/engine.d.ts +918 -0
- package/dist/engine.js +82 -0
- package/dist/geovideo-index-CMGVia5M.js +1148 -0
- package/dist/story.d.ts +398 -0
- package/dist/story.js +561 -0
- package/dist/zartigl.d.ts +524 -52
- package/dist/zartigl.js +3815 -2679
- package/package.json +33 -4
package/README.md
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
# zartigl
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cloud-native ocean and climate data on a MapLibre map: a browser runtime, a
|
|
4
|
+
curated catalog, and a pipeline that pre-renders long timelines as streamable
|
|
5
|
+
video.
|
|
4
6
|
|
|
5
7
|
[](https://fxi.io/zartigl/)
|
|
6
8
|
[](https://fxi.io/zartigl/story/)
|
|
7
9
|
|
|
8
|
-
Zartigl
|
|
10
|
+
Zartigl started as a MapLibre plugin that renders scalar rasters and vector
|
|
11
|
+
particle fields straight from multidimensional Zarr stores, without a tile
|
|
12
|
+
server. It now also maintains the data side that makes those layers fast and
|
|
13
|
+
reproducible: a provider-neutral catalog of Copernicus Marine products, and
|
|
14
|
+
GeoVideo archives that turn years of daily or hourly fields into chunked H.264
|
|
15
|
+
videos played back continuously in the browser. It is aimed at expert
|
|
16
|
+
workflows where time, depth or pressure level, point inspection, and
|
|
17
|
+
reproducible map state matter as much as the visual layer, and it powers the
|
|
18
|
+
ARCO layers of [MapX](https://app.mapx.org).
|
|
19
|
+
|
|
20
|
+
## What Is In This Repository
|
|
21
|
+
|
|
22
|
+
| Part | Location | Role |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| Runtime library | `src/lib/` | `@fxi/zartigl`: the `Zartigl` facade, Zarr/WMTS/GeoVideo layers, shaders, particle simulation, point queries |
|
|
25
|
+
| Catalog | `src/catalog/`, `scripts/catalog_builder/` | `@fxi/zartigl/catalog` presets and the tools that discover, describe, and validate entries |
|
|
26
|
+
| GeoVideo pipeline | `scripts/geovideo/`, `.github/workflows/geovideo.yml` | Single renders and incremental archives published to S3, rendered daily on a self-hosted runner |
|
|
27
|
+
| Demos | `src/demo-prod/`, `src/demo-story/` | Technical demo and narrative story, deployed to GitHub Pages |
|
|
28
|
+
| MapX integration | `src/mapx/` | Widget and standalone snippet generation for reproducible MapX views |
|
|
9
29
|
|
|
10
30
|
## Features
|
|
11
31
|
|
|
@@ -16,7 +36,9 @@ Zartigl renders scalar rasters and vector particle fields from multidimensional
|
|
|
16
36
|
- Point queries for time series and vertical profiles when a point-series store is available
|
|
17
37
|
- UUID-addressed catalog entries with independently selectable Zarr, WMTS, and GeoVideo sources
|
|
18
38
|
- MapLibre custom layer integration, including Mercator and globe rendering paths
|
|
19
|
-
- Pre-rendered GeoVideo
|
|
39
|
+
- Pre-rendered scalar and vector GeoVideo with true polar globe draping and portable masks
|
|
40
|
+
- GeoVideo archives split into calendar chunks, with the next chunk preloaded so
|
|
41
|
+
playback runs across the whole record without gaps
|
|
20
42
|
- No server component required for public CORS-enabled stores
|
|
21
43
|
|
|
22
44
|
## Quick Start
|
|
@@ -48,8 +70,8 @@ const z = new Zartigl({
|
|
|
48
70
|
timeRange: { trailing: "P1M" },
|
|
49
71
|
time: new Date("2025-01-01T00:00:00Z"),
|
|
50
72
|
depth: 0,
|
|
51
|
-
|
|
52
|
-
geoVideo: { autoplay: false, loop: true,
|
|
73
|
+
style: { palette: "rdylbu", raster: { opacity: 0.9 } },
|
|
74
|
+
geoVideo: { autoplay: false, loop: true, stepsPerSecond: 4 },
|
|
53
75
|
});
|
|
54
76
|
|
|
55
77
|
z.on("status", (status) => console.log(status.phase));
|
|
@@ -59,10 +81,76 @@ await z.init();
|
|
|
59
81
|
Use `await z.update({ ... })` to change any subset of the active configuration.
|
|
60
82
|
Pass `layer` and `source` together when both should switch atomically.
|
|
61
83
|
|
|
84
|
+
`source` takes a source id or a preference:
|
|
85
|
+
|
|
86
|
+
- `"geovideo"`: fast; surface level and GeoVideo dates only.
|
|
87
|
+
- `"zarr"`: all dates and depths; heavier on the client.
|
|
88
|
+
- `"auto"`: GeoVideo, falling back to Zarr for other dates and depths; the
|
|
89
|
+
layer style always applies. WMTS only when the entry has neither.
|
|
90
|
+
- `"wmts"`: rendered by the server, palette and legend included; no
|
|
91
|
+
particles, and the layer style has no effect.
|
|
92
|
+
|
|
62
93
|
`init()` and layer/source updates resolve after consolidated metadata and coordinate axes have loaded. Metadata getters are therefore authoritative afterward, including exact values for irregular time axes.
|
|
63
94
|
|
|
64
95
|
The root import does not bundle the catalog presets. Import catalog data from `@fxi/zartigl/catalog` only when you want the built-in catalog.
|
|
65
96
|
|
|
97
|
+
## Layer style
|
|
98
|
+
|
|
99
|
+
A layer style has the same shape everywhere: catalog `defaults`, the
|
|
100
|
+
`Zartigl` `style` option and `update({ style })`, story views, projversor,
|
|
101
|
+
the MapX `ArcoMapLegend` and the snippets the demo exports.
|
|
102
|
+
|
|
103
|
+
```jsonc
|
|
104
|
+
{
|
|
105
|
+
"palette": "thermal",
|
|
106
|
+
"renderMode": "particles", // or "raster", "raster+particles"
|
|
107
|
+
"particles": {
|
|
108
|
+
"density": 0.03,
|
|
109
|
+
"speed": 1,
|
|
110
|
+
"fade": 0.7,
|
|
111
|
+
"grade": "vibrant",
|
|
112
|
+
"backdrop": { "enabled": true, "color": "#000000", "opacity": 0.3 }
|
|
113
|
+
},
|
|
114
|
+
"raster": { "opacity": 0.9, "logScale": false, "vibrance": 0.2, "colorDomain": null }
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`particles.grade` is `"off"`, `"vibrant"`, or a tuned vibrant grade
|
|
119
|
+
`{ exposure, contrast, saturation, highlights }`; it is replaced as a whole.
|
|
120
|
+
|
|
121
|
+
`particles.backdrop` paints a flat color under the particle trails, on the
|
|
122
|
+
valid cells of the field only (the ocean, not the land), so particles stay
|
|
123
|
+
readable over a light basemap. It is off by default; an object is on unless
|
|
124
|
+
`enabled` is false, and its keys left out take the defaults (`color:
|
|
125
|
+
"#000000"`, `#rgb` or `#rrggbb`; `opacity: 0.5`). Like the grade, it is
|
|
126
|
+
replaced as a whole.
|
|
127
|
+
|
|
128
|
+
Keys merge one by one: engine defaults (`LAYER_STYLE_DEFAULTS`), then the
|
|
129
|
+
catalog entry `defaults`, then the engine's own layer style, then the view.
|
|
130
|
+
From `@fxi/zartigl/catalog`: `resolveEntryStyle(entry, ...styles)` gives the
|
|
131
|
+
style a layer is drawn with, `mergeStyles` patches an override,
|
|
132
|
+
`diffStyle` keeps what differs from a resolved style, and `parseLayerStyle`
|
|
133
|
+
validates, with the path of the offending key. `z.getStyle()` returns the
|
|
134
|
+
style drawn, `z.getStyleOverride()` the override.
|
|
135
|
+
|
|
136
|
+
The view style is an override that replaces the previous one:
|
|
137
|
+
`update({ style })` and projversor `apply({ style })` return every style key
|
|
138
|
+
to the layer's own, then apply it (`{}` clears it). Without `style`, the
|
|
139
|
+
override stays, across layers too. Scalar layers take no `particles` and only
|
|
140
|
+
the `"raster"` render mode.
|
|
141
|
+
|
|
142
|
+
`particles.speed` is always the particle speed; playback speed lives
|
|
143
|
+
elsewhere (`geoVideo.stepsPerSecond`, or a projversor view `playback.speed`).
|
|
144
|
+
In projversor, playback also scales the particles by `particleSpeedFactor`:
|
|
145
|
+
0.325 at 1×, 1 at 5×.
|
|
146
|
+
|
|
147
|
+
`settings` carries engine tuning only (`particleState`,
|
|
148
|
+
`rgba8MaxParticleZoom`). Flat style keys in it (`particleDensity`,
|
|
149
|
+
`opacity`, `colorGrade`…) are deprecated until 1.0: they patch the style
|
|
150
|
+
override, `style` winning, with one warning per key
|
|
151
|
+
(`splitLegacySettings`, `LEGACY_STYLE_SETTINGS`). A custom palette ramp
|
|
152
|
+
stays a setting.
|
|
153
|
+
|
|
66
154
|
## Demo
|
|
67
155
|
|
|
68
156
|
The production demo is the default Vite app and is published at
|
|
@@ -114,7 +202,8 @@ z.on("status", (status) => {
|
|
|
114
202
|
// GeoVideo uses native media playback and reports scientific time updates.
|
|
115
203
|
z.on("timeChange", (time) => console.log(new Date(time)));
|
|
116
204
|
z.on("playbackChange", (playing) => console.log({ playing }));
|
|
117
|
-
|
|
205
|
+
// Speed in time steps per second means the same on every artifact.
|
|
206
|
+
await z.update({ geoVideo: { stepsPerSecond: 16, loop: false } });
|
|
118
207
|
|
|
119
208
|
// Embedding applications can stop rendering and abort field requests while
|
|
120
209
|
// retaining the latest requested time/depth for one reload on resume.
|
|
@@ -138,6 +227,24 @@ finishes publishing it; a renderable sparse frame is cached normally.
|
|
|
138
227
|
|
|
139
228
|
`VectorLayer`, `ScalarLayer`, `GeoVideoLayer`, and `ZarrSource` remain exported for advanced use cases that need direct control over renderer internals. Ordinary MapLibre integrations should prefer `Zartigl`.
|
|
140
229
|
|
|
230
|
+
### Entries without MapLibre
|
|
231
|
+
|
|
232
|
+
Three entries need no MapLibre (`maplibre-gl` is an optional peer):
|
|
233
|
+
|
|
234
|
+
- `@fxi/zartigl/engine`: GeoVideo manifests, indexes, decoders and archive
|
|
235
|
+
timelines, palettes and color ramps, color grade, backdrop and trail fade,
|
|
236
|
+
WebGL helpers and the shared GLSL chunks as strings (`VIBRANCE_GLSL`,
|
|
237
|
+
`COLOR_GRADE_GLSL`, `RANDOM_GLSL`).
|
|
238
|
+
- `@fxi/zartigl/catalog`: the built-in catalog, named palettes and styles.
|
|
239
|
+
- `@fxi/zartigl/story`: the story runtime, with JSON documents, localized
|
|
240
|
+
copy and scroll activation.
|
|
241
|
+
|
|
242
|
+
[projversor](https://github.com/fxi/projversor) (`@fxi/projversor`), the
|
|
243
|
+
renderer of GeoVideo archives on a sphere that morphs between world
|
|
244
|
+
projections, is built on these entries in its own repository, with its demos.
|
|
245
|
+
zartigl never imports it. It replaces the former `@fxi/zartigl/projversor`
|
|
246
|
+
entry: the same names are exported by `@fxi/projversor/zartigl`.
|
|
247
|
+
|
|
141
248
|
## Alternatives And Project Fit
|
|
142
249
|
|
|
143
250
|
Zartigl overlaps with several useful projects. The goal is not to replace them, but to cover a specific cloud-native geoscience workflow: public Zarr stores and compatible WMTS/GeoVideo sources, MapLibre integration, animated vector particles, scalar rasters, time/depth controls, point inspection, and reproducible widget configuration without a dedicated tile server.
|
|
@@ -235,17 +342,53 @@ const z = new Zartigl({
|
|
|
235
342
|
geoVideo: {
|
|
236
343
|
autoplay: true,
|
|
237
344
|
loop: true,
|
|
238
|
-
|
|
345
|
+
stepsPerSecond: 8,
|
|
239
346
|
},
|
|
240
347
|
});
|
|
241
348
|
|
|
242
349
|
await z.init();
|
|
243
350
|
```
|
|
244
351
|
|
|
245
|
-
The source UUID selects the entry's published GeoVideo
|
|
246
|
-
coverage. `loop` and `
|
|
247
|
-
may restrict autoplay, so embedding applications
|
|
248
|
-
fallback when necessary.
|
|
352
|
+
The source UUID selects the entry's published GeoVideo. Globe projection
|
|
353
|
+
demonstrates its polar coverage. `loop` and `stepsPerSecond` are optional
|
|
354
|
+
playback settings. Browsers may restrict autoplay, so embedding applications
|
|
355
|
+
should provide a user-gesture fallback when necessary.
|
|
356
|
+
|
|
357
|
+
### Archives
|
|
358
|
+
|
|
359
|
+
Most catalog entries carry a GeoVideo archive source: an `indexUrl` pointing to
|
|
360
|
+
an `index.json` that lists immutable calendar chunks (monthly, yearly, or per
|
|
361
|
+
decade, depending on cadence). zartigl reads the index at runtime, revalidates
|
|
362
|
+
it every few minutes, and expands it into one source per chunk, so newly
|
|
363
|
+
published chunks appear without a catalog release. Times outside the published
|
|
364
|
+
chunks, deeper levels, and point queries fall back to Zarr.
|
|
365
|
+
|
|
366
|
+
During playback the next chunk's manifest, media, and mask load while the
|
|
367
|
+
current one plays; at its end the layer swaps media in place and keeps playing,
|
|
368
|
+
so a whole archive plays as one continuous timeline.
|
|
369
|
+
|
|
370
|
+
Archives are produced by `scripts/geovideo/archive.py` from the policies in
|
|
371
|
+
[`scripts/geovideo/archive.json`](scripts/geovideo/archive.json). Every product
|
|
372
|
+
is archived over its full upstream time axis; only event scenes, such as the
|
|
373
|
+
regional Cyclone Chido archive, keep a fixed window. The
|
|
374
|
+
[`GeoVideo archive`](.github/workflows/geovideo.yml) workflow renders pending
|
|
375
|
+
chunks daily, and on demand with a longer budget, on the self-hosted `bigproc`
|
|
376
|
+
runner. Published chunks are never dropped from an index, so an archive keeps
|
|
377
|
+
periods that upstream later removes.
|
|
378
|
+
|
|
379
|
+
Follow progress against the published indexes. The command reads the upstream
|
|
380
|
+
time axes and the indexes with the S3 credentials in `.env`, writes nothing,
|
|
381
|
+
and can run while the workflow renders:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
npm run geovideo:progress
|
|
385
|
+
# 80/500 Mediterranean Significant Wave Height
|
|
386
|
+
# Total 971/5347 chunks (18.2%); 32/61 archives complete
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`npm run geovideo:plan` prints the same plan as JSON, chunk by chunk.
|
|
390
|
+
|
|
391
|
+
### Single Renders
|
|
249
392
|
|
|
250
393
|
```bash
|
|
251
394
|
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --dry-run
|
|
@@ -253,7 +396,9 @@ uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json
|
|
|
253
396
|
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --upload
|
|
254
397
|
```
|
|
255
398
|
|
|
256
|
-
|
|
399
|
+
`render.py` (scalar) and `render_vector.py` (vector) produce one standalone
|
|
400
|
+
artifact from a configuration in `scripts/geovideo/examples/`, such as the
|
|
401
|
+
hourly Cyclone Chido wind scene used by the story. The Arctic sea-ice example is
|
|
257
402
|
available at
|
|
258
403
|
[`scripts/geovideo/examples/sea-ice-thickness-arctic.json`](scripts/geovideo/examples/sea-ice-thickness-arctic.json).
|
|
259
404
|
|
|
@@ -320,6 +465,7 @@ npm test
|
|
|
320
465
|
npm run build:prod
|
|
321
466
|
npm run build:lib
|
|
322
467
|
npm run catalog:validate
|
|
468
|
+
npm run test:geovideo
|
|
323
469
|
```
|
|
324
470
|
|
|
325
471
|
`npm run release:check` runs the main validation path before publishing.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const i = {
|
|
2
|
+
exposure: 0.8,
|
|
3
|
+
contrast: 0.6,
|
|
4
|
+
saturation: 0.4,
|
|
5
|
+
highlights: 0.6
|
|
6
|
+
}, l = Object.freeze({
|
|
7
|
+
enabled: !1,
|
|
8
|
+
...i
|
|
9
|
+
}), s = Object.freeze({
|
|
10
|
+
enabled: !0,
|
|
11
|
+
...i
|
|
12
|
+
}), u = {
|
|
13
|
+
exposure: [0, 4],
|
|
14
|
+
contrast: [0, 1],
|
|
15
|
+
saturation: [-1, 1],
|
|
16
|
+
highlights: [0, 1]
|
|
17
|
+
}, a = u;
|
|
18
|
+
function f(e) {
|
|
19
|
+
if (!e)
|
|
20
|
+
return { ...l };
|
|
21
|
+
const o = {
|
|
22
|
+
...s,
|
|
23
|
+
enabled: e.enabled ?? !0
|
|
24
|
+
};
|
|
25
|
+
for (const n of Object.keys(a)) {
|
|
26
|
+
const t = e[n];
|
|
27
|
+
if (typeof t == "number" && Number.isFinite(t)) {
|
|
28
|
+
const [r, b] = a[n];
|
|
29
|
+
o[n] = Math.min(b, Math.max(r, t));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return o;
|
|
33
|
+
}
|
|
34
|
+
function h(e) {
|
|
35
|
+
return [e.exposure, e.contrast, e.saturation, e.highlights];
|
|
36
|
+
}
|
|
37
|
+
function m(e) {
|
|
38
|
+
return e === void 0 || e === "off" ? { ...l } : e === "vibrant" ? { ...s } : f({ ...e, enabled: !0 });
|
|
39
|
+
}
|
|
40
|
+
function y(e) {
|
|
41
|
+
const o = f(e);
|
|
42
|
+
if (!o.enabled)
|
|
43
|
+
return "off";
|
|
44
|
+
const n = Object.keys(a);
|
|
45
|
+
return n.every((t) => o[t] === s[t]) ? "vibrant" : Object.fromEntries(n.map((t) => [t, o[t]]));
|
|
46
|
+
}
|
|
47
|
+
const c = Object.freeze({
|
|
48
|
+
enabled: !1,
|
|
49
|
+
color: "#000000",
|
|
50
|
+
opacity: 0.5
|
|
51
|
+
}), d = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i, O = (e) => typeof e == "string" && d.test(e);
|
|
52
|
+
function R(e) {
|
|
53
|
+
if (!e)
|
|
54
|
+
return { ...c };
|
|
55
|
+
const o = { ...c, enabled: !0 };
|
|
56
|
+
return typeof e.enabled == "boolean" && (o.enabled = e.enabled), O(e.color) && (o.color = e.color), typeof e.opacity == "number" && Number.isFinite(e.opacity) && (o.opacity = Math.min(1, Math.max(0, e.opacity))), o;
|
|
57
|
+
}
|
|
58
|
+
function p(e) {
|
|
59
|
+
const o = e.color.slice(1), n = o.length === 3 ? o.split("").map((r) => r + r).join("") : o, t = (r) => parseInt(n.slice(r, r + 2), 16) / 255;
|
|
60
|
+
return [
|
|
61
|
+
t(0),
|
|
62
|
+
t(2),
|
|
63
|
+
t(4),
|
|
64
|
+
e.enabled ? e.opacity : 0
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
c as B,
|
|
69
|
+
l as C,
|
|
70
|
+
R as a,
|
|
71
|
+
p as b,
|
|
72
|
+
h as c,
|
|
73
|
+
y as d,
|
|
74
|
+
s as e,
|
|
75
|
+
u as f,
|
|
76
|
+
m as g,
|
|
77
|
+
O as i,
|
|
78
|
+
f as r
|
|
79
|
+
};
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solid color under the particle trails, drawn on valid data cells only: it
|
|
3
|
+
* keeps particles readable over a light basemap. Off by default; an object
|
|
4
|
+
* without `enabled` is on.
|
|
5
|
+
*/
|
|
6
|
+
export declare interface BackdropStyle {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** Hex color, `#rgb` or `#rrggbb`. */
|
|
9
|
+
color: string;
|
|
10
|
+
opacity: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
export declare interface Catalog {
|
|
2
14
|
schemaVersion: 2;
|
|
3
15
|
defaultLocale: string;
|
|
@@ -15,22 +27,10 @@ export declare interface CatalogEntry {
|
|
|
15
27
|
tags?: string[];
|
|
16
28
|
kind: "scalar" | "vector";
|
|
17
29
|
sources: CatalogSource[];
|
|
18
|
-
|
|
30
|
+
/** Default source, plus the layer's base style. */
|
|
31
|
+
defaults: LayerStyleInput & {
|
|
19
32
|
sourceId: string;
|
|
20
33
|
querySourceId?: string;
|
|
21
|
-
palette?: string;
|
|
22
|
-
renderMode?: "particles" | "raster" | "raster+particles";
|
|
23
|
-
particles?: {
|
|
24
|
-
density?: number;
|
|
25
|
-
speed?: number;
|
|
26
|
-
fade?: number;
|
|
27
|
-
};
|
|
28
|
-
raster?: {
|
|
29
|
-
opacity?: number;
|
|
30
|
-
logScale?: boolean;
|
|
31
|
-
vibrance?: number;
|
|
32
|
-
colorDomain?: [number, number];
|
|
33
|
-
};
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -39,9 +39,21 @@ export declare interface CatalogEntrySelector extends CatalogSearchOptions {
|
|
|
39
39
|
variableId?: string;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* An incremental archive of calendar-aligned artifacts listed by a mutable
|
|
44
|
+
* index; zartigl resolves it to the chunk covering the requested time.
|
|
45
|
+
*/
|
|
46
|
+
declare interface CatalogGeoVideoArchiveSource extends CatalogSourceBase {
|
|
47
|
+
type: "geovideo";
|
|
48
|
+
indexUrl: string;
|
|
49
|
+
manifestUrl?: never;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One published artifact. */
|
|
42
53
|
export declare interface CatalogGeoVideoSource extends CatalogSourceBase {
|
|
43
54
|
type: "geovideo";
|
|
44
55
|
manifestUrl: string;
|
|
56
|
+
indexUrl?: never;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
export declare interface CatalogSearchOptions {
|
|
@@ -51,7 +63,7 @@ export declare interface CatalogSearchOptions {
|
|
|
51
63
|
provider?: string;
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource;
|
|
66
|
+
export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource | CatalogGeoVideoArchiveSource;
|
|
55
67
|
|
|
56
68
|
declare interface CatalogSourceBase {
|
|
57
69
|
id: string;
|
|
@@ -114,6 +126,9 @@ export declare interface CatalogZarrSource extends CatalogSourceBase {
|
|
|
114
126
|
variables: CatalogVariables;
|
|
115
127
|
}
|
|
116
128
|
|
|
129
|
+
/** The keys of `style` whose value differs from `base`'s. */
|
|
130
|
+
export declare function diffStyle(style: LayerStyleInput, base: LayerStyle): LayerStyleInput;
|
|
131
|
+
|
|
117
132
|
export declare function findCatalogEntries(selector: CatalogEntrySelector, data?: Catalog): CatalogEntry[];
|
|
118
133
|
|
|
119
134
|
export declare function formatTime(ms: number): string;
|
|
@@ -122,14 +137,158 @@ export declare function formatVertical(v: number, label: string, units?: string)
|
|
|
122
137
|
|
|
123
138
|
export declare function getCatalogEntry(id: string, data?: Catalog): CatalogEntry | undefined;
|
|
124
139
|
|
|
140
|
+
/** Vibrant grade values; each one left out takes the vibrant preset's. */
|
|
141
|
+
export declare interface GradeTuning {
|
|
142
|
+
/** Mid-tone lift [0, 4]. */
|
|
143
|
+
exposure?: number;
|
|
144
|
+
/** S-curve strength [0, 1]. */
|
|
145
|
+
contrast?: number;
|
|
146
|
+
/** Vibrance-weighted saturation [-1, 1]. */
|
|
147
|
+
saturation?: number;
|
|
148
|
+
/** Push of the bright half toward white [0, 1]. */
|
|
149
|
+
highlights?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Engine fallbacks, under the catalog `defaults`. */
|
|
153
|
+
export declare const LAYER_STYLE_DEFAULTS: Readonly<LayerStyle>;
|
|
154
|
+
|
|
155
|
+
export declare interface LayerStyle {
|
|
156
|
+
/** Palette id; undefined lets the engine pick its own. */
|
|
157
|
+
palette?: string;
|
|
158
|
+
renderMode: StyleRenderMode;
|
|
159
|
+
particles: ParticleStyle;
|
|
160
|
+
raster: RasterStyle;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** A partial style: any key left out takes the value below it. */
|
|
164
|
+
export declare interface LayerStyleInput {
|
|
165
|
+
palette?: string;
|
|
166
|
+
renderMode?: StyleRenderMode;
|
|
167
|
+
particles?: ParticleStyleInput;
|
|
168
|
+
raster?: Partial<RasterStyle>;
|
|
169
|
+
}
|
|
170
|
+
|
|
125
171
|
export declare type LocalizedText = Record<string, string>;
|
|
126
172
|
|
|
127
|
-
|
|
173
|
+
/**
|
|
174
|
+
* One partial style from several, later inputs winning key by key: patch an
|
|
175
|
+
* override without resolving defaults.
|
|
176
|
+
*/
|
|
177
|
+
export declare function mergeStyles(...inputs: (LayerStyleInput | undefined)[]): LayerStyleInput;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Validate a style, throwing an error that names the offending path, e.g.
|
|
181
|
+
* `views.swell.config.style.particles.density must be a number in (0, 0.2]`.
|
|
182
|
+
*/
|
|
183
|
+
export declare function parseLayerStyle(value: unknown, path: string, options?: ParseLayerStyleOptions): LayerStyleInput;
|
|
184
|
+
|
|
185
|
+
/** Which keys a caller supports, and for which layer kind. */
|
|
186
|
+
export declare interface ParseLayerStyleOptions {
|
|
187
|
+
kind?: "scalar" | "vector";
|
|
188
|
+
/** Keys left out are rejected as unsupported; all when omitted. */
|
|
189
|
+
supports?: {
|
|
190
|
+
palette?: boolean;
|
|
191
|
+
renderMode?: boolean;
|
|
192
|
+
particles?: readonly (keyof ParticleStyle)[];
|
|
193
|
+
raster?: readonly (keyof RasterStyle)[];
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Style keys, for checks against other definitions of the same shape. */
|
|
198
|
+
export declare const PARTICLE_STYLE_KEYS: (keyof ParticleStyle)[];
|
|
199
|
+
|
|
200
|
+
/** Particle trail color grade: a preset, or the vibrant one tuned. */
|
|
201
|
+
export declare type ParticleGrade = "off" | "vibrant" | GradeTuning;
|
|
202
|
+
|
|
203
|
+
export declare interface ParticleStyle {
|
|
204
|
+
/** Particles per device pixel, (0, 0.2]. */
|
|
205
|
+
density: number;
|
|
206
|
+
/** Css px per frame for the fastest value of the field, (0, 10]. */
|
|
207
|
+
speed: number;
|
|
208
|
+
/** Trail length, [0, 1] (longer when higher). */
|
|
209
|
+
fade: number;
|
|
210
|
+
/** Trail color grade; replaced as a whole, not merged. */
|
|
211
|
+
grade: ParticleGrade;
|
|
212
|
+
/** Solid color under the trails; replaced as a whole, not merged. */
|
|
213
|
+
backdrop: BackdropStyle;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Partial particle style; a partial backdrop takes the defaults' keys. */
|
|
217
|
+
export declare type ParticleStyleInput = Partial<Omit<ParticleStyle, "backdrop">> & {
|
|
218
|
+
backdrop?: Partial<BackdropStyle>;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Preferred render source: GeoVideo, then Zarr, then WMTS (scalar), skipping
|
|
223
|
+
* fixed-period sources that cannot show the requested time. Several GeoVideo
|
|
224
|
+
* periods may coexist; the latest covering one wins. GeoVideo is a
|
|
225
|
+
* lightweight display transport; Zarr stays the fallback and query source.
|
|
226
|
+
*/
|
|
227
|
+
export declare function pickPreferredSource(entry: CatalogEntry, request?: SourceTimeRequest): CatalogSource;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* First source type in `priority` that the entry provides, preferring sources
|
|
231
|
+
* covering the request, then the latest period. Undefined when no type matches.
|
|
232
|
+
*/
|
|
233
|
+
export declare function pickSourceByPriority(entry: CatalogEntry, priority: readonly CatalogSource["type"][], request?: SourceTimeRequest): CatalogSource | undefined;
|
|
234
|
+
|
|
235
|
+
export declare const RASTER_STYLE_KEYS: (keyof RasterStyle)[];
|
|
236
|
+
|
|
237
|
+
export declare interface RasterStyle {
|
|
238
|
+
opacity: number;
|
|
239
|
+
logScale: boolean;
|
|
240
|
+
/** Vibrance adjustment, [-1, 1]. */
|
|
241
|
+
vibrance: number;
|
|
242
|
+
/** Fixed physical-value domain; null follows the data. */
|
|
243
|
+
colorDomain: [number, number] | null;
|
|
244
|
+
}
|
|
128
245
|
|
|
129
246
|
export declare function requireCatalogEntry(id: string, data?: Catalog): CatalogEntry;
|
|
130
247
|
|
|
248
|
+
/**
|
|
249
|
+
* The style a catalog layer is drawn with: its own style, then `inputs`.
|
|
250
|
+
* Scalar layers draw a raster.
|
|
251
|
+
*/
|
|
252
|
+
export declare function resolveEntryStyle(entry: StyledEntry | undefined, ...inputs: (LayerStyleInput | undefined)[]): LayerStyle;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Merge styles over {@link LAYER_STYLE_DEFAULTS}, later inputs winning key by
|
|
256
|
+
* key. Usual order: catalog `defaults`, engine style, view.
|
|
257
|
+
*/
|
|
258
|
+
export declare function resolveLayerStyle(...inputs: (LayerStyleInput | undefined)[]): LayerStyle;
|
|
259
|
+
|
|
131
260
|
export declare function resolveLocalizedText(value: LocalizedText | undefined, locale: string, defaultLocale?: string): string;
|
|
132
261
|
|
|
133
262
|
export declare function searchCatalog(query: string, options?: CatalogSearchOptions, data?: Catalog): CatalogEntry[];
|
|
134
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Whether a source can show the requested time. Only fixed-period sources
|
|
266
|
+
* (rendered artifacts such as GeoVideo) declare hard bounds; live sources are
|
|
267
|
+
* assumed to cover any request and are bounded by their own metadata.
|
|
268
|
+
*/
|
|
269
|
+
export declare function sourceCoversTime(source: CatalogSource, request?: SourceTimeRequest): boolean;
|
|
270
|
+
|
|
271
|
+
/** Requested instant and/or window, in epoch milliseconds. */
|
|
272
|
+
export declare interface SourceTimeRequest {
|
|
273
|
+
time?: number;
|
|
274
|
+
start?: number;
|
|
275
|
+
end?: number;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** A catalog entry, or anything with its kind and default style. */
|
|
279
|
+
export declare interface StyledEntry {
|
|
280
|
+
kind: "scalar" | "vector";
|
|
281
|
+
defaults?: LayerStyleInput;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** `style` without the keys a layer of `kind` cannot draw. */
|
|
285
|
+
export declare function styleForKind(style: LayerStyleInput, kind: StyledEntry["kind"] | undefined): LayerStyleInput;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Layer style: how a layer looks, with the same keys in the catalog
|
|
289
|
+
* `defaults`, the engines and story views. Playback (how time moves) is not
|
|
290
|
+
* part of it, so `particles.speed` is always the particle speed.
|
|
291
|
+
*/
|
|
292
|
+
export declare type StyleRenderMode = "particles" | "raster" | "raster+particles";
|
|
293
|
+
|
|
135
294
|
export { }
|