@fxi/zartigl 0.1.4 → 0.2.13
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 +250 -70
- package/dist/catalog.d.ts +103 -0
- package/dist/catalog.js +28 -0
- package/dist/zartigl.d.ts +1444 -0
- package/dist/zartigl.js +4563 -1875
- package/package.json +28 -10
- package/dist/data/catalog.json +0 -351
- package/dist/index.d.ts +0 -416
package/README.md
CHANGED
|
@@ -1,127 +1,307 @@
|
|
|
1
1
|
# zartigl
|
|
2
2
|
|
|
3
|
-
A MapLibre GL JS plugin for
|
|
3
|
+
A MapLibre GL JS plugin for exploring cloud-hosted Zarr geoscience data directly in the browser.
|
|
4
4
|
|
|
5
|
-
[](https://fxi.io/zartigl/)
|
|
6
|
+
|
|
7
|
+
Zartigl renders scalar rasters and vector particle fields from multidimensional Zarr stores without a dedicated tile server. It is aimed at expert analysis workflows where time, depth or pressure level, point inspection, and reproducible map state matter as much as the visual layer.
|
|
6
8
|
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- Browser-side Zarr v2 access with chunk-aware loading and LRU caching
|
|
12
|
+
- Scalar raster rendering with palettes, opacity, log scale, and value inspection
|
|
13
|
+
- Vector rendering with GPU particle advection and optional raster magnitude display
|
|
14
|
+
- Time and vertical-dimension controls for depth, elevation, level, or pressure-like axes
|
|
15
|
+
- Point queries for time series and vertical profiles when a point-series store is available
|
|
16
|
+
- Catalog-backed datasets with automatic Zarr/WMTS backend selection for scalar layers
|
|
17
|
+
- MapLibre custom layer integration, including Mercator and globe rendering paths
|
|
18
|
+
- Pre-rendered GeoVideo backends with true polar globe draping and portable masks
|
|
19
|
+
- No server component required for public CORS-enabled stores
|
|
15
20
|
|
|
16
|
-
## Quick
|
|
21
|
+
## Quick Start
|
|
17
22
|
|
|
18
23
|
```bash
|
|
19
|
-
npm install zartigl maplibre-gl
|
|
24
|
+
npm install @fxi/zartigl maplibre-gl
|
|
20
25
|
```
|
|
21
26
|
|
|
22
27
|
```ts
|
|
23
28
|
import maplibregl from "maplibre-gl";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
map
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
import { Zartigl } from "@fxi/zartigl";
|
|
30
|
+
import { catalog } from "@fxi/zartigl/catalog";
|
|
31
|
+
|
|
32
|
+
const map = new maplibregl.Map({
|
|
33
|
+
container: "map",
|
|
34
|
+
style: "https://demotiles.maplibre.org/style.json",
|
|
35
|
+
center: [0, 20],
|
|
36
|
+
zoom: 2,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const z = new Zartigl({
|
|
40
|
+
id: "zarr-layer",
|
|
41
|
+
map,
|
|
42
|
+
catalog,
|
|
43
|
+
backend: "auto",
|
|
44
|
+
// Optional: expose only the month ending at the latest available timestamp.
|
|
45
|
+
timeRange: { trailing: "P1M" },
|
|
46
|
+
geoVideo: { autoplay: false, loop: true, playbackRate: 1 },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await z.setLayer("ocean-current-velocity");
|
|
50
|
+
z.setTimeAndDepth(new Date("2025-01-01T00:00:00Z"), 0);
|
|
51
|
+
z.updateSettings({
|
|
52
|
+
palette: "rdylbu",
|
|
53
|
+
opacity: 0.9,
|
|
37
54
|
});
|
|
38
55
|
```
|
|
39
56
|
|
|
57
|
+
`setLayer()` resolves after the field store's consolidated metadata and coordinate axes have loaded. Metadata getters are therefore authoritative after the awaited call, including exact values for irregular time axes.
|
|
58
|
+
|
|
59
|
+
The root import does not bundle the catalog presets. Import catalog data from `@fxi/zartigl/catalog` only when you want the built-in catalog.
|
|
60
|
+
|
|
40
61
|
## Demo
|
|
41
62
|
|
|
63
|
+
The production demo is the default Vite app and is published at
|
|
64
|
+
[`fxi.io/zartigl`](https://fxi.io/zartigl/).
|
|
65
|
+
|
|
42
66
|
```bash
|
|
43
67
|
npm install
|
|
44
68
|
npm run dev
|
|
45
69
|
```
|
|
46
70
|
|
|
47
|
-
|
|
71
|
+
Build the same app that the public demo publishes:
|
|
48
72
|
|
|
49
|
-
|
|
73
|
+
```bash
|
|
74
|
+
npm run build:prod
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For the smaller public API demo:
|
|
50
78
|
|
|
51
79
|
```bash
|
|
52
|
-
|
|
53
|
-
uv run build_catalog.py
|
|
80
|
+
npm run dev:minimal
|
|
54
81
|
```
|
|
55
82
|
|
|
56
|
-
|
|
83
|
+
The demos read the built-in catalog, which points to public Copernicus Marine ARCO stores. No local data download is needed; visible chunks are fetched directly from cloud object storage or, for some scalar layers, from WMTS when that is the safer backend.
|
|
84
|
+
|
|
85
|
+
## Public API
|
|
86
|
+
|
|
87
|
+
The main entry point is the catalog-backed `Zartigl` facade:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const time = z.getTimeMeta();
|
|
91
|
+
const depth = z.getDepthMeta();
|
|
92
|
+
const legend = z.getLegend();
|
|
57
93
|
|
|
58
|
-
|
|
94
|
+
z.on("loaded", (meta) => {
|
|
95
|
+
console.log(meta.min, meta.max, meta.unit);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
z.on("status", (status) => {
|
|
99
|
+
// metadata → fetching → rendering → ready, or blocked/error
|
|
100
|
+
console.log(status.phase);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// GeoVideo uses native media playback and reports scientific time updates.
|
|
104
|
+
z.on("timeChange", (time) => console.log(new Date(time)));
|
|
105
|
+
z.on("playbackChange", (playing) => console.log({ playing }));
|
|
106
|
+
z.setPlaybackRate(2);
|
|
107
|
+
z.setLoop(false);
|
|
108
|
+
|
|
109
|
+
// Embedding applications can stop rendering and abort field requests while
|
|
110
|
+
// retaining the latest requested time/depth for one reload on resume.
|
|
111
|
+
z.suspend();
|
|
112
|
+
z.setTime(new Date("2025-02-01T00:00:00Z"));
|
|
113
|
+
z.resume();
|
|
114
|
+
|
|
115
|
+
const series = await z.queryTimeSeries({
|
|
116
|
+
longitude: 7.4,
|
|
117
|
+
latitude: 46.9,
|
|
118
|
+
depth: 0,
|
|
119
|
+
maxPoints: 256,
|
|
120
|
+
});
|
|
121
|
+
```
|
|
59
122
|
|
|
60
|
-
|
|
123
|
+
New layers initially select the latest advertised timestamp that is not in the
|
|
124
|
+
future. Forecast timestamps remain available through `getTimeMeta().values`.
|
|
125
|
+
Missing remote chunks are interpreted as Zarr fill data. A wholly unavailable
|
|
126
|
+
frame is not cached, so selecting it again can recover after an upstream store
|
|
127
|
+
finishes publishing it; a renderable sparse frame is cached normally.
|
|
61
128
|
|
|
62
|
-
|
|
129
|
+
`VectorLayer`, `ScalarLayer`, `ArcoLayer`, and `ZarrSource` remain exported for advanced use cases that need direct control over renderer internals. Ordinary MapLibre integrations should prefer `Zartigl`.
|
|
63
130
|
|
|
64
|
-
|
|
131
|
+
## Alternatives And Project Fit
|
|
65
132
|
|
|
66
|
-
|
|
67
|
-
- **Ocean surface waves** — Stokes drift or wave propagation direction fields
|
|
68
|
-
- **Ice drift** — sea-ice motion vectors from passive microwave retrievals
|
|
69
|
-
- **River discharge / flood routing** — depth-averaged U/V from hydrodynamic models (Delft3D, SCHISM)
|
|
70
|
-
- **Air quality transport** — horizontal advection of pollutant plumes from dispersion models
|
|
71
|
-
- **Climate projections** — CMIP6 ocean or atmosphere circulation at any time slice; compare historical vs. SSP scenarios
|
|
72
|
-
- **Groundwater flow** — Darcy velocity fields from subsurface models
|
|
73
|
-
- **Wildfire smoke** — wind-driven particle transport overlaid on active fire data
|
|
133
|
+
Zartigl overlaps with several useful projects. The goal is not to replace them, but to cover a specific MapX/ARCO workflow: public cloud Zarr stores, MapLibre integration, animated vector particles, scalar rasters, time/depth controls, point inspection, and reproducible widget configuration without a dedicated tile server.
|
|
74
134
|
|
|
75
|
-
The
|
|
135
|
+
The table below is a fit matrix based on documented project scope. "Not specified" means the feature is not a stated focus in the referenced project, not that it is impossible.
|
|
76
136
|
|
|
77
|
-
|
|
137
|
+
| Project | Best fit | Zarr raster | Vector particles | Time/depth UI | Query API | Polar/globe rendering | Notes |
|
|
138
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
139
|
+
| zartigl | MapX-ready ARCO-like ocean layers | Yes, Zarr v2 | Yes | Built in | Time series and vertical profiles when a point-series store is available | MapLibre globe/ECEF path for lon/lat scalar and vector layers, including over-pole views | Catalog defaults, WMTS fallback for selected scalar layers, MapX snippet generation |
|
|
140
|
+
| [Copernicus Marine MyOcean Pro](https://data.marine.copernicus.eu/viewer/expert) | Copernicus end-user exploration | Service-managed | Product-dependent | Application UI | Product UI, not a reusable library API | Service-managed | Reference viewer, not a MapLibre/WebGL library to embed in MapX |
|
|
141
|
+
| [@carbonplan/zarr-layer](https://github.com/carbonplan/zarr-layer) | General Zarr raster layers for MapLibre/Mapbox | Yes, Zarr v2/v3 | No, raster-focused | Selector API | GeoJSON queries | Full polar coverage documented for MapLibre with suitable untiled datasets | Strong closest alternative for raster Zarr; supports custom stores, CRS reprojection, custom fragment shaders |
|
|
142
|
+
| [@carbonplan/maps](https://github.com/carbonplan/maps) | React maps for prepared multidimensional rasters | Yes, prepared Zarr pyramids | No | React selector workflow | Not its main focus | Web Mercator-focused | Higher-level React framework; its core MapLibre path is documented as Web Mercator-only |
|
|
143
|
+
| [zarr-gl](https://github.com/carderne/zarr-gl) | Lightweight Zarr layer for Mapbox/MapLibre | Yes | No | Limited | Not specified | Not specified | The README now recommends using `@carbonplan/zarr-layer` instead |
|
|
144
|
+
| [maplibre-gl-wind](https://github.com/geoql/maplibre-gl-wind) | Wind particle layer for deck.gl/MapLibre | No | Yes | No | No | Not specified | Takes wind textures or point data, not Zarr metadata/chunks |
|
|
78
145
|
|
|
79
|
-
|
|
146
|
+
The closest general alternative is `@carbonplan/zarr-layer`. It has a broader documented scope for raster Zarr rendering: Zarr v2/v3, custom stores, CRS reprojection, custom fragment shaders, GeoJSON queries, and full polar coverage in MapLibre for suitable untiled datasets. If the requirement were only "draw a multidimensional Zarr raster on a MapLibre map", using or contributing to `@carbonplan/zarr-layer` would be a strong option.
|
|
80
147
|
|
|
81
|
-
|
|
148
|
+
Zartigl remains useful because it combines pieces that are not currently covered together by those projects:
|
|
82
149
|
|
|
83
|
-
|
|
150
|
+
- ARCO-oriented catalog entries and defaults for Copernicus Marine products.
|
|
151
|
+
- U/V vector products rendered as animated particles, with optional raster magnitude in the same layer.
|
|
152
|
+
- Time and vertical-axis metadata exposed as application controls.
|
|
153
|
+
- Depth/elevation labeling, ordering, and point inspection tuned for ocean products.
|
|
154
|
+
- MapLibre globe rendering for both scalar and vector fields, including polar scenes beyond the usual Web Mercator `85.0511°` latitude limit.
|
|
155
|
+
- GPU particle-state fallbacks for browser and hardware differences, including float, half-float, and `RGBA8` compatibility mode.
|
|
156
|
+
- A facade API that can generate MapX widget snippets and keep widget state reproducible.
|
|
84
157
|
|
|
85
|
-
|
|
158
|
+
deck.gl, Three.js, or `@carbonplan/zarr-layer` could still be useful integration targets. They would not remove the ARCO catalog, time/depth metadata, vector particle simulation, point-query workflow, or MapX widget-state work that zartigl currently owns.
|
|
86
159
|
|
|
87
|
-
|
|
160
|
+
## Catalog Metadata
|
|
88
161
|
|
|
162
|
+
To validate the built-in catalog:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm run catalog:validate
|
|
89
166
|
```
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
167
|
+
|
|
168
|
+
To add or update catalog entries:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
uv run scripts/catalog_builder/skills/list_layers.py
|
|
172
|
+
uv run scripts/catalog_builder/skills/search_products.py wave
|
|
173
|
+
uv run scripts/catalog_builder/skills/query_dataset.py <dataset_id>
|
|
174
|
+
uv run scripts/catalog_builder/skills/validate_catalog.py
|
|
93
175
|
```
|
|
94
176
|
|
|
95
|
-
|
|
177
|
+
This requires Python >= 3.12, [uv](https://docs.astral.sh/uv/), and a free [Copernicus Marine](https://data.marine.copernicus.eu/register) account.
|
|
96
178
|
|
|
97
|
-
|
|
179
|
+
## GeoVideo
|
|
98
180
|
|
|
99
|
-
|
|
181
|
+
GeoVideo pre-renders an expensive scalar Zarr timeline into a short, streamable
|
|
182
|
+
H.264 MP4 while retaining spatial bounds, scientific dates, provenance, units,
|
|
183
|
+
palette, and color domain in a sidecar manifest. Version 2 transports quantized
|
|
184
|
+
scalar values in luminance and a static lossless mask as a separate PNG, so the
|
|
185
|
+
browser can apply palette and scalar styling in WebGL. It is a visualization
|
|
186
|
+
backend only: point queries always use the authoritative Zarr point-series store.
|
|
100
187
|
|
|
101
|
-
|
|
188
|
+
Unlike MapLibre's native four-corner video source, `GeoVideoLayer` maps an
|
|
189
|
+
equirectangular scalar-luma texture through zartigl's subdivided lon/lat mesh.
|
|
190
|
+
Globe mode therefore reaches both poles. A separate lossless image supplies the
|
|
191
|
+
static validity mask without doubling the video width.
|
|
102
192
|
|
|
103
|
-
|
|
193
|
+
### Displaying a GeoVideo
|
|
104
194
|
|
|
105
|
-
|
|
195
|
+
The built-in catalog includes an Arctic sea-ice thickness animation. Select the
|
|
196
|
+
GeoVideo backend explicitly and enable autoplay when creating the `Zartigl`
|
|
197
|
+
instance:
|
|
106
198
|
|
|
107
|
-
|
|
199
|
+
```ts
|
|
200
|
+
import maplibregl from "maplibre-gl";
|
|
201
|
+
import { Zartigl } from "@fxi/zartigl";
|
|
202
|
+
import { catalog } from "@fxi/zartigl/catalog";
|
|
203
|
+
|
|
204
|
+
const map = new maplibregl.Map({
|
|
205
|
+
container: "map",
|
|
206
|
+
style: "https://demotiles.maplibre.org/style.json",
|
|
207
|
+
center: [-132.454826, 85.051129],
|
|
208
|
+
zoom: 0.511,
|
|
209
|
+
});
|
|
108
210
|
|
|
109
|
-
|
|
211
|
+
map.setProjection({ type: "globe" });
|
|
212
|
+
|
|
213
|
+
const z = new Zartigl({
|
|
214
|
+
map,
|
|
215
|
+
catalog,
|
|
216
|
+
backend: "geovideo",
|
|
217
|
+
geoVideo: {
|
|
218
|
+
autoplay: true,
|
|
219
|
+
loop: true,
|
|
220
|
+
playbackRate: 1,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
110
223
|
|
|
111
|
-
|
|
224
|
+
await z.setLayer("sea-ice-thickness");
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`backend: "geovideo"` selects the published GeoVideo manifest from the
|
|
228
|
+
catalog's `sea-ice-thickness` entry. Globe projection demonstrates its polar
|
|
229
|
+
coverage. `loop` and `playbackRate` are optional playback settings. Browsers
|
|
230
|
+
may restrict autoplay, so embedding applications should provide a user-gesture
|
|
231
|
+
fallback when necessary.
|
|
232
|
+
|
|
233
|
+
Try the configured instance directly in the [Arctic sea-ice GeoVideo example](https://fxi.io/zartigl/#eyJkIjoxMSwidCI6MTc4MTAwNDMyNTY3OC40OTY4LCJ2IjowLCJwIjoiaWNlIiwicyI6Imdlb3ZpZGVvIiwicHIiOiJnbG9iZSIsImMiOlstMTMyLjQ1NDgyNiw4NS4wNTExMjldLCJ6IjowLjUxMSwiYiI6MCwicGkiOjAsInBkIjowLjA1LCJzcCI6MSwiZiI6MC43LCJybSI6InBhcnRpY2xlcyIsIm9wIjowLjksImxzIjp0cnVlLCJ2YiI6LTEsImNkIjpudWxsLCJnYSI6dHJ1ZSwiZ2wiOnRydWUsImdyIjoxMH0=).
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --dry-run
|
|
237
|
+
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json
|
|
238
|
+
uv run scripts/geovideo/render.py scripts/geovideo/examples/sst-anomaly.json --upload
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The Arctic sea-ice configuration is available at
|
|
242
|
+
[`scripts/geovideo/examples/sea-ice-thickness-arctic.json`](scripts/geovideo/examples/sea-ice-thickness-arctic.json).
|
|
243
|
+
|
|
244
|
+
Generation requires FFmpeg. Upload credentials remain in `.env`; no private
|
|
245
|
+
key is bundled into the demo or written to a manifest. See
|
|
246
|
+
[`scripts/geovideo/README.md`](scripts/geovideo/README.md) for the configuration
|
|
247
|
+
and smoke-test workflow.
|
|
248
|
+
|
|
249
|
+
## Dataset Scope
|
|
250
|
+
|
|
251
|
+
The built-in catalog focuses on Copernicus Marine ARCO products, including ocean currents and scalar ocean/ice variables. Other public cloud-native Zarr v2 stores can be used when their store URLs and variables are described in a compatible catalog entry and runtime compatibility is verified. Time, vertical, spatial, and variable metadata are loaded live from consolidated Zarr metadata and coordinate chunks.
|
|
252
|
+
|
|
253
|
+
Good fits include:
|
|
254
|
+
|
|
255
|
+
- Ocean currents, Stokes drift, and wave fields
|
|
256
|
+
- Sea-ice drift and ice-related scalar products
|
|
257
|
+
- Atmospheric wind or pressure-level products
|
|
258
|
+
- Climate model outputs with time and vertical dimensions
|
|
259
|
+
- Hydrodynamic model rasters or vectors
|
|
260
|
+
|
|
261
|
+
Vector layers need either U/V component variables or a catalog derivation from direction and magnitude. Scalar layers need one numeric variable and enough coordinate metadata to locate chunks by longitude, latitude, time, and optional vertical dimension.
|
|
262
|
+
|
|
263
|
+
Catalog validation checks the JSON contract, not remote service behavior. For built-in catalog additions, use the ARCO-oriented catalog-builder scripts first. WMTS is only an optional scalar shortcut; for polar products, prefer Zarr unless WMTS coverage is verified to reach the poles.
|
|
264
|
+
|
|
265
|
+
## How It Works
|
|
266
|
+
|
|
267
|
+
### 1. Zarr Is A Multidimensional Raster Cube
|
|
112
268
|
|
|
113
|
-
|
|
269
|
+
Think of a Cloud Optimized GeoTIFF extended into longitude, latitude, time, and depth or pressure. The data is split into chunks. Zartigl requests only the chunks that intersect the current viewport at the selected time and vertical coordinate, similar to a COG tile request.
|
|
270
|
+
|
|
271
|
+
### 2. Chunks Become GPU Textures
|
|
272
|
+
|
|
273
|
+
Scalar chunks are normalized into a raster texture and colorized by the selected palette. By default the color domain follows the loaded frame; scalar layers can instead set `colorDomain: [min, max]` to keep colors comparable across viewports and dates. Vector chunks are converted into a two-channel field texture, with validity carried as a mask.
|
|
274
|
+
|
|
275
|
+
For vector U/V data:
|
|
276
|
+
|
|
277
|
+
```txt
|
|
278
|
+
R = eastward component
|
|
279
|
+
G = northward component
|
|
280
|
+
A = valid data mask
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 3. Particles Are Virtual Drifters
|
|
284
|
+
|
|
285
|
+
For vector particle rendering, thousands of virtual drifters are seeded across the viewport. Every frame, the GPU samples the vector texture, moves each particle, and respawns particles that leave the valid domain.
|
|
286
|
+
|
|
287
|
+
Particle state lives in GPU textures using a ping-pong framebuffer pattern, so positions do not need to round-trip through CPU arrays each frame. Positions are stored at full float precision where the GPU supports it (falling back to 16-bit packing), which keeps trails crisp when zoomed in. Motion is zoom-compensated so a single `speed` setting yields roughly constant *visual* speed from low to high zoom.
|
|
288
|
+
|
|
289
|
+
Trail history currently remains a screen-space texture. During camera movement it is faded aggressively rather than cleared every frame, which avoids flicker while limiting displaced trail artifacts on both Mercator and globe projections. A world-aligned or reprojected trail buffer is a possible experimental direction, but globe reprojection and polar continuity require additional work.
|
|
290
|
+
|
|
291
|
+
### 4. Queries Use The Same Source Logic
|
|
292
|
+
|
|
293
|
+
Point inspection, time-series sampling, and vertical-profile sampling reuse the same Zarr metadata, coordinate lookup, chunk decoding, and cache machinery as the renderer. The query API is intentionally separate from the visual interaction layer, so applications can build their own analysis UI.
|
|
294
|
+
|
|
295
|
+
## Build And Release Checks
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npm test
|
|
299
|
+
npm run build:prod
|
|
300
|
+
npm run build:lib
|
|
301
|
+
npm run catalog:validate
|
|
302
|
+
```
|
|
114
303
|
|
|
115
|
-
|
|
116
|
-
|---|---|---|
|
|
117
|
-
| Fetch | Request Zarr chunks for viewport + time + depth | COG tile request |
|
|
118
|
-
| Pack | Encode U/V floats into R/G/A texture | Raster → GPU image |
|
|
119
|
-
| Seed | Place N drifters randomly in viewport | Random point sampling |
|
|
120
|
-
| Advect | Each frame: sample velocity, move each drifter | Drift model at 60 fps |
|
|
121
|
-
| Mask | Discard drifters over land (A = 0) | Nodata mask |
|
|
122
|
-
| Fade | Dim previous frame slightly | Long-exposure photography |
|
|
123
|
-
| Color | Map speed to color ramp | Graduated color by value |
|
|
124
|
-
| Composite | Blend result over basemap | Raster layer blending |
|
|
304
|
+
`npm run release:check` runs the main validation path before publishing.
|
|
125
305
|
|
|
126
306
|
## License
|
|
127
307
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export declare interface Catalog {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
layers: CatalogLayer[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export declare const catalog: Catalog;
|
|
7
|
+
|
|
8
|
+
export declare interface CatalogGeoVideo {
|
|
9
|
+
id: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
manifestUrl: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare type CatalogLayer = (CatalogLayerBase & {
|
|
15
|
+
kind: "scalar";
|
|
16
|
+
variables: Extract<CatalogVariables, {
|
|
17
|
+
kind: "scalar";
|
|
18
|
+
}>;
|
|
19
|
+
}) | (CatalogLayerBase & {
|
|
20
|
+
kind: "vector";
|
|
21
|
+
variables: Extract<CatalogVariables, {
|
|
22
|
+
kind: "vector";
|
|
23
|
+
}>;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
declare interface CatalogLayerBase {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
category?: string;
|
|
31
|
+
tags?: string[];
|
|
32
|
+
dataset: {
|
|
33
|
+
id: string;
|
|
34
|
+
provider?: "copernicus" | string;
|
|
35
|
+
productId?: string;
|
|
36
|
+
};
|
|
37
|
+
stores: {
|
|
38
|
+
field: {
|
|
39
|
+
url: string;
|
|
40
|
+
};
|
|
41
|
+
pointSeries?: {
|
|
42
|
+
url: string;
|
|
43
|
+
};
|
|
44
|
+
wmts?: CatalogWmts;
|
|
45
|
+
};
|
|
46
|
+
derived?: {
|
|
47
|
+
geoVideos?: CatalogGeoVideo[];
|
|
48
|
+
};
|
|
49
|
+
defaults?: {
|
|
50
|
+
backend?: "zarr" | "geovideo" | "wmts";
|
|
51
|
+
palette?: string;
|
|
52
|
+
renderMode?: "particles" | "raster" | "raster+particles";
|
|
53
|
+
particles?: {
|
|
54
|
+
density?: number;
|
|
55
|
+
speed?: number;
|
|
56
|
+
fade?: number;
|
|
57
|
+
};
|
|
58
|
+
raster?: {
|
|
59
|
+
opacity?: number;
|
|
60
|
+
logScale?: boolean;
|
|
61
|
+
vibrance?: number;
|
|
62
|
+
/** Fixed physical-value domain for scalar color normalization. */
|
|
63
|
+
colorDomain?: [number, number];
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export declare type CatalogVariables = {
|
|
69
|
+
kind: "scalar";
|
|
70
|
+
value: string;
|
|
71
|
+
} | {
|
|
72
|
+
kind: "vector";
|
|
73
|
+
u?: string;
|
|
74
|
+
v?: string;
|
|
75
|
+
derivation?: CatalogVectorDerivation;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export declare interface CatalogVectorDerivation {
|
|
79
|
+
kind: "direction_magnitude";
|
|
80
|
+
direction_variable: string;
|
|
81
|
+
magnitude_variable: string;
|
|
82
|
+
direction_convention: "from" | "toward";
|
|
83
|
+
output_direction: "from" | "toward";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare interface CatalogWmts {
|
|
87
|
+
capabilities_url: string;
|
|
88
|
+
base_url: string;
|
|
89
|
+
layer: string;
|
|
90
|
+
tileMatrixSet: string;
|
|
91
|
+
format: string;
|
|
92
|
+
style?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export declare function formatTime(ms: number): string;
|
|
96
|
+
|
|
97
|
+
export declare function formatVertical(v: number, label: string, units?: string): string;
|
|
98
|
+
|
|
99
|
+
export declare function getCatalogLayer(id: string, data?: Catalog): CatalogLayer | undefined;
|
|
100
|
+
|
|
101
|
+
export declare function requireCatalogLayer(id: string, data?: Catalog): CatalogLayer;
|
|
102
|
+
|
|
103
|
+
export { }
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const t = /* @__PURE__ */ JSON.parse('[{"id":"ocean-current-velocity","label":"Ocean Current Velocity","description":"Eastward and northward sea water velocity from global 6-hourly physics analysis and forecast.","category":"Ocean","kind":"vector","dataset":{"id":"cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-009/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-009/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i_202406/geoChunked.zarr"}},"variables":{"kind":"vector","u":"uo","v":"vo"},"defaults":{"palette":"rdylbu","renderMode":"particles","particles":{"density":0.033,"speed":1,"fade":0.7},"raster":{"opacity":1,"logScale":true,"vibrance":0.39}}},{"id":"surface-wind","label":"Surface Wind","description":"Global hourly sea surface wind from scatterometer and model","category":"Atmosphere","kind":"vector","dataset":{"id":"cmems_obs-wind_glo_phy_nrt_l4_0.125deg_PT1H","provider":"copernicus","productId":"WIND_GLO_PHY_L4_NRT_012_004"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-050/arco/WIND_GLO_PHY_L4_NRT_012_004/cmems_obs-wind_glo_phy_nrt_l4_0.125deg_PT1H_202207/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-050/arco/WIND_GLO_PHY_L4_NRT_012_004/cmems_obs-wind_glo_phy_nrt_l4_0.125deg_PT1H_202207/geoChunked.zarr"}},"variables":{"kind":"vector","u":"eastward_wind","v":"northward_wind"},"defaults":{"palette":"rdylbu","renderMode":"particles","particles":{"density":0.004000000000000002,"speed":1,"fade":0.7},"raster":{"opacity":0.9,"logScale":true,"vibrance":1}}},{"id":"phytoplankton-chlorophyll","label":"Phytoplankton (Chlorophyll-a)","description":"Daily chlorophyll-a concentration from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m_202311/chl","tileMatrixSet":"EPSG:3857","format":"image/png","style":"cmap:algae,logScale"}},"variables":{"kind":"scalar","value":"chl"},"defaults":{"palette":"algae","raster":{"opacity":0.9,"logScale":true,"vibrance":0.2}}},{"id":"wave-stokes-drift","label":"Wave Stokes Drift","description":"Net horizontal wave-driven transport (Stokes drift) from global 3-hourly wave analysis — visualises wave energy propagation direction.","category":"Ocean","kind":"vector","dataset":{"id":"cmems_mod_glo_wav_anfc_0.083deg_PT3H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_WAV_001_027"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/geoChunked.zarr"}},"variables":{"kind":"vector","u":"VSDX","v":"VSDY"},"defaults":{"palette":"rdylbu","renderMode":"particles","particles":{"density":0.024,"speed":1,"fade":0.7},"raster":{"opacity":1,"logScale":true,"vibrance":1}}},{"id":"dissolved-inorganic-carbon","label":"Dissolved Inorganic Carbon","description":"Daily dissolved inorganic carbon concentration from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/dissic","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"dissic"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"ocean-ph","label":"Ocean pH","description":"Daily sea water pH reported on the total scale from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m_202311/ph","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"ph"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":false,"vibrance":0.1}}},{"id":"primary-production","label":"Primary Production","description":"Daily net primary production of biomass expressed as carbon per unit volume.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/nppv","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"nppv"},"defaults":{"palette":"algae","raster":{"opacity":0.9,"logScale":true,"vibrance":0.25}}},{"id":"dissolved-oxygen","label":"Dissolved Oxygen","description":"Daily dissolved molecular oxygen concentration from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m_202311/o2","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"o2"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"nitrate","label":"Nitrate","description":"Daily nitrate concentration from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/no3","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"no3"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":true,"vibrance":0.2}}},{"id":"phosphate","label":"Phosphate","description":"Daily phosphate concentration from global biogeochemistry analysis and forecast.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m_202311/po4","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"po4"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":true,"vibrance":0.2}}},{"id":"surface-co2","label":"Surface CO2","description":"Daily surface partial pressure of carbon dioxide in sea water.","category":"Biology","kind":"scalar","dataset":{"id":"cmems_mod_glo_bgc-co2_anfc_0.25deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_BGC_001_028"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-co2_anfc_0.25deg_P1D-m_202311/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-006/arco/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-co2_anfc_0.25deg_P1D-m_202311/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-co2_anfc_0.25deg_P1D-m_202311?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_BGC_001_028/cmems_mod_glo_bgc-co2_anfc_0.25deg_P1D-m_202311/spco2","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"spco2"},"defaults":{"palette":"balance","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"sea-ice-thickness","label":"Sea Ice Thickness","description":"Daily sea ice thickness from global physics analysis and forecast.","category":"Ice","kind":"scalar","dataset":{"id":"cmems_mod_glo_phy_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"}},"derived":{"geoVideos":[{"id":"sea-ice-thickness-arctic-six-months-2026-scalar-luma","label":"Arctic six-month thickness animation (February–August 2026)","manifestUrl":"https://sos-ch-gva-2.exo.io/zartigl/geovideo/sea-ice-thickness/79d55879af89/manifest.json"}]},"variables":{"kind":"scalar","value":"sithick"},"defaults":{"palette":"ice","raster":{"opacity":0.9,"logScale":false,"vibrance":0.1}}},{"id":"sea-water-temperature","label":"Sea Water Temperature","description":"Daily sea water potential temperature from global physics analysis and forecast.","category":"Ocean","kind":"scalar","dataset":{"id":"cmems_mod_glo_phy-thetao_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-012/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-thetao_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-012/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-thetao_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-thetao_anfc_0.083deg_P1D-m_202406?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-thetao_anfc_0.083deg_P1D-m_202406/thetao","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"thetao"},"defaults":{"palette":"thermal","raster":{"opacity":0.9,"logScale":false,"vibrance":0.2}}},{"id":"sea-surface-temperature-anomaly","label":"Sea Surface Temperature Anomaly","description":"Daily source-provided sea surface temperature anomaly from global physics analysis and forecast, suitable for visualizing El Niño and La Niña patterns.","category":"Ocean","tags":["el-nino","la-nina","enso","anomaly"],"kind":"scalar","dataset":{"id":"cmems_mod_glo_phy_anfc_0.083deg-sst-anomaly_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-014/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg-sst-anomaly_P1D-m_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-014/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg-sst-anomaly_P1D-m_202411/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg-sst-anomaly_P1D-m_202411?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg-sst-anomaly_P1D-m_202411/sea_surface_temperature_anomaly","tileMatrixSet":"EPSG:3857","format":"image/png"}},"derived":{"geoVideos":[{"id":"sst-anomaly-six-months-2026-scalar-luma","label":"Six-month scalar-luma animation (February–August 2026)","manifestUrl":"https://sos-ch-gva-2.exo.io/zartigl/geovideo/sea-surface-temperature-anomaly/5b560a8404a9/manifest.json"}]},"variables":{"kind":"scalar","value":"sea_surface_temperature_anomaly"},"defaults":{"backend":"zarr","palette":"balance","raster":{"opacity":0.9,"logScale":false,"vibrance":0.1,"colorDomain":[-3,3]}}},{"id":"sea-water-salinity","label":"Sea Water Salinity","description":"Daily sea water salinity from global physics analysis and forecast.","category":"Ocean","kind":"scalar","dataset":{"id":"cmems_mod_glo_phy-so_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-010/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-so_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-010/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-so_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-so_anfc_0.083deg_P1D-m_202406?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-so_anfc_0.083deg_P1D-m_202406/so","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"so"},"defaults":{"palette":"haline","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"upward-sea-water-velocity","label":"Upward Sea Water Velocity","description":"Daily upward sea water velocity from global physics analysis and forecast.","category":"Ocean","kind":"scalar","dataset":{"id":"cmems_mod_glo_phy-wcur_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-wcur_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-wcur_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-wcur_anfc_0.083deg_P1D-m_202406?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy-wcur_anfc_0.083deg_P1D-m_202406/wo","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"wo"},"defaults":{"palette":"rdylbu","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"mixed-layer-depth","label":"Mixed Layer Depth","description":"Daily ocean mixed layer thickness defined by sigma theta.","category":"Ocean","kind":"scalar","dataset":{"id":"cmems_mod_glo_phy_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"}},"variables":{"kind":"scalar","value":"mlotst"},"defaults":{"palette":"deep","raster":{"opacity":0.9,"logScale":false,"vibrance":0.15}}},{"id":"primary-swell-direction","label":"Primary Swell Direction","description":"Primary swell wave direction reported as the direction waves come from.","category":"Waves","kind":"scalar","dataset":{"id":"cmems_mod_glo_wav_anfc_0.083deg_PT3H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_WAV_001_027"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/VMDR_SW1","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"VMDR_SW1"},"defaults":{"palette":"curl","raster":{"opacity":0.9,"logScale":false,"vibrance":0.2}}},{"id":"primary-swell-period","label":"Primary Swell Period","description":"Mean period of the primary swell wave from spectral moments.","category":"Waves","kind":"scalar","dataset":{"id":"cmems_mod_glo_wav_anfc_0.083deg_PT3H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_WAV_001_027"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/VTM01_SW1","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"VTM01_SW1"},"defaults":{"palette":"rdylbu","raster":{"opacity":0.9,"logScale":false,"vibrance":0.2}}},{"id":"primary-swell-height","label":"Primary Swell Height","description":"Significant height of the primary swell wave.","category":"Waves","kind":"scalar","dataset":{"id":"cmems_mod_glo_wav_anfc_0.083deg_PT3H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_WAV_001_027"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/geoChunked.zarr"},"wmts":{"capabilities_url":"https://wmts.marine.copernicus.eu/teroWmts/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411?service=WMTS&request=GetCapabilities","base_url":"https://wmts.marine.copernicus.eu/teroWmts","layer":"GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/VHM0_SW1","tileMatrixSet":"EPSG:3857","format":"image/png"}},"variables":{"kind":"scalar","value":"VHM0_SW1"},"defaults":{"palette":"deep","raster":{"opacity":0.9,"logScale":false,"vibrance":0.2}}},{"id":"primary-swell-propagation","label":"Primary Swell Propagation","description":"Derived primary swell propagation using significant height as magnitude and from-direction converted to travel direction.","category":"Waves","kind":"vector","dataset":{"id":"cmems_mod_glo_wav_anfc_0.083deg_PT3H-i","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_WAV_001_027"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-015/arco/GLOBAL_ANALYSISFORECAST_WAV_001_027/cmems_mod_glo_wav_anfc_0.083deg_PT3H-i_202411/geoChunked.zarr"}},"variables":{"kind":"vector","derivation":{"kind":"direction_magnitude","direction_variable":"VMDR_SW1","magnitude_variable":"VHM0_SW1","direction_convention":"from","output_direction":"toward"}},"defaults":{"palette":"rdylbu","renderMode":"particles","particles":{"density":0.02,"speed":1,"fade":0.7},"raster":{"opacity":1,"logScale":false,"vibrance":0.45}}},{"id":"sea-ice-drift","label":"Sea Ice Drift","description":"Daily eastward and northward velocity of sea ice, tracing polar ice floe motion.","category":"Ice","kind":"vector","dataset":{"id":"cmems_mod_glo_phy_anfc_0.083deg_P1D-m","provider":"copernicus","productId":"GLOBAL_ANALYSISFORECAST_PHY_001_024"},"stores":{"field":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-time-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/timeChunked.zarr"},"pointSeries":{"url":"https://s3.waw3-1.cloudferro.com/mdl-arco-geo-013/arco/GLOBAL_ANALYSISFORECAST_PHY_001_024/cmems_mod_glo_phy_anfc_0.083deg_P1D-m_202406/geoChunked.zarr"}},"variables":{"kind":"vector","u":"usi","v":"vsi"},"defaults":{"backend":"zarr","palette":"ice","renderMode":"particles","particles":{"density":0.04,"speed":1,"fade":0.7},"raster":{"opacity":1,"logScale":false,"vibrance":0}}}]'), c = {
|
|
2
|
+
schemaVersion: 1,
|
|
3
|
+
layers: t
|
|
4
|
+
}, o = c;
|
|
5
|
+
function s(e, a = o) {
|
|
6
|
+
return a.layers.find((r) => r.id === e);
|
|
7
|
+
}
|
|
8
|
+
function i(e, a = o) {
|
|
9
|
+
const r = s(e, a);
|
|
10
|
+
if (!r)
|
|
11
|
+
throw new Error(`Unknown zartigl catalog layer: ${e}`);
|
|
12
|
+
return r;
|
|
13
|
+
}
|
|
14
|
+
function l(e) {
|
|
15
|
+
const a = new Date(e), r = (_) => String(_).padStart(2, "0");
|
|
16
|
+
return `${a.getUTCFullYear()}-${r(a.getUTCMonth() + 1)}-${r(a.getUTCDate())} ${r(a.getUTCHours())}:${r(a.getUTCMinutes())} UTC`;
|
|
17
|
+
}
|
|
18
|
+
function m(e, a, r) {
|
|
19
|
+
const _ = r?.trim();
|
|
20
|
+
return a === "pressure" ? `${Math.round(e)} ${_ || "hPa"}` : _ && _ !== "m" ? `${Number(e.toPrecision(5))} ${_}` : e < 10 ? `${e.toFixed(2)} m` : e < 100 ? `${e.toFixed(1)} m` : `${Math.round(e)} m`;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
o as catalog,
|
|
24
|
+
l as formatTime,
|
|
25
|
+
m as formatVertical,
|
|
26
|
+
s as getCatalogLayer,
|
|
27
|
+
i as requireCatalogLayer
|
|
28
|
+
};
|