@fxi/zartigl 0.1.3 → 0.1.4
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 +64 -0
- package/dist/data/catalog.json +260 -17
- package/dist/index.d.ts +155 -21
- package/dist/zartigl.js +1846 -1495
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -59,6 +59,70 @@ This requires Python >= 3.12, [uv](https://docs.astral.sh/uv/), and a free [Cope
|
|
|
59
59
|
|
|
60
60
|
[GLOBAL_ANALYSISFORECAST_PHY_001_024](https://data.marine.copernicus.eu/product/GLOBAL_ANALYSISFORECAST_PHY_001_024) from Copernicus Marine Service — `uo`/`vo` surface current velocity at ~0.083deg resolution, streamed as ARCO Zarr directly from Copernicus S3.
|
|
61
61
|
|
|
62
|
+
## Other use cases
|
|
63
|
+
|
|
64
|
+
Any 2D vector field stored as U/V components in a Zarr store works as a source. Examples:
|
|
65
|
+
|
|
66
|
+
- **Atmospheric wind** — reanalysis products (ERA5, MERRA-2) or NWP model output; visualise jet streams, storm tracks, or surface winds at any pressure level
|
|
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
|
|
74
|
+
|
|
75
|
+
The only requirement is a Zarr v2 store with two named float variables representing the east and north components of the field.
|
|
76
|
+
|
|
77
|
+
## How it works
|
|
78
|
+
|
|
79
|
+
A plain-language walkthrough for GIS analysts unfamiliar with WebGL or Zarr.
|
|
80
|
+
|
|
81
|
+
### 1. Zarr is a tiled raster cube
|
|
82
|
+
|
|
83
|
+
Think of a Cloud Optimized GeoTIFF extended into four dimensions: longitude × latitude × depth × time. The data is split into chunks — here: 1 time step × 1 depth level × 512 rows × 2048 columns. Zartigl requests only the chunks that intersect the current viewport at the selected time and depth, exactly like a COG tile request.
|
|
84
|
+
|
|
85
|
+
### 2. Velocity becomes a two-channel raster
|
|
86
|
+
|
|
87
|
+
Each chunk holds two float arrays — **U** (eastward m/s) and **V** (northward m/s). These are normalized and packed into the R and G channels of an image uploaded to the GPU:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
R = eastward velocity (0 → max west, 128 → still, 255 → max east)
|
|
91
|
+
G = northward velocity
|
|
92
|
+
A = 255 (ocean) | 0 (land / nodata) ← validity mask
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The result is a global velocity raster in GPU memory. The alpha channel is a nodata mask.
|
|
96
|
+
|
|
97
|
+
### 3. Particles are virtual drifters
|
|
98
|
+
|
|
99
|
+
Thousands of virtual oceanographic drifters are seeded randomly across the viewport. Every frame (~60 fps), the GPU runs this logic for all of them simultaneously:
|
|
100
|
+
|
|
101
|
+
> *Where am I? → sample the velocity raster at my position → move a tiny step in that direction → if I drift off-screen or hit land (A = 0), teleport to a random ocean pixel inside the viewport.*
|
|
102
|
+
|
|
103
|
+
Same logic as a drift model — just running on 65 000+ drifters at 60 frames per second.
|
|
104
|
+
|
|
105
|
+
### 4. Trails are faded time-lapse
|
|
106
|
+
|
|
107
|
+
Before each new frame, the previous frame is multiplied by ~0.9999. Particles leave a fading streak — like a long-exposure photograph of drifters. Streak length encodes time; color encodes speed magnitude via a configurable color ramp.
|
|
108
|
+
|
|
109
|
+
### 5. Particle state lives in GPU memory
|
|
110
|
+
|
|
111
|
+
Drifter positions are never stored in a CPU array. They are encoded as pixel colors in a GPU texture (X → red, Y → blue, sub-pixel precision in the low byte). Two textures alternate each frame — read from A, write to B, swap — so the GPU never waits on itself. This is the **ping-pong** pattern.
|
|
112
|
+
|
|
113
|
+
### Pipeline at a glance
|
|
114
|
+
|
|
115
|
+
| Step | What happens | GIS analogy |
|
|
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 |
|
|
125
|
+
|
|
62
126
|
## License
|
|
63
127
|
|
|
64
128
|
MIT
|
package/dist/data/catalog.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-
|
|
3
|
-
"
|
|
2
|
+
"generated": "2026-03-18T12:00:00.000000+00:00",
|
|
3
|
+
"views": [
|
|
4
4
|
{
|
|
5
|
-
"id": "
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
"id": "ocean-current-velocity",
|
|
6
|
+
"label": "Ocean Current Velocity",
|
|
7
|
+
"description": "Surface ocean currents — global analysis/forecast, 6-hourly",
|
|
8
|
+
"category": "Ocean",
|
|
9
|
+
"type": "vector",
|
|
10
|
+
"source_dataset": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i",
|
|
11
|
+
"zarr_url_geo": "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",
|
|
12
|
+
"zarr_url_time": "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",
|
|
13
|
+
"variable_u": "uo",
|
|
14
|
+
"variable_v": "vo",
|
|
15
|
+
"variable_meta": {
|
|
16
|
+
"standard_name": "sea_water_velocity",
|
|
17
|
+
"units": "m s-1"
|
|
18
18
|
},
|
|
19
19
|
"dimensions": {
|
|
20
20
|
"depth": {
|
|
@@ -95,13 +95,256 @@
|
|
|
95
95
|
},
|
|
96
96
|
"time": {
|
|
97
97
|
"axis": "t",
|
|
98
|
-
"size":
|
|
98
|
+
"size": 5585,
|
|
99
99
|
"min": 1654041600000.0,
|
|
100
|
-
"max":
|
|
100
|
+
"max": 1774656000000.0,
|
|
101
101
|
"step": 21600000.0,
|
|
102
102
|
"chunk_size": 1,
|
|
103
103
|
"units": "milliseconds since 1970-01-01 00:00:00Z (no leap seconds)"
|
|
104
104
|
}
|
|
105
|
+
},
|
|
106
|
+
"vertical_label": "depth",
|
|
107
|
+
"defaults": {
|
|
108
|
+
"palette": "rdylbu",
|
|
109
|
+
"renderMode": "particles",
|
|
110
|
+
"particleDensity": 0.033,
|
|
111
|
+
"speedMin": 0.14,
|
|
112
|
+
"speedMax": 1.11,
|
|
113
|
+
"fadeMin": 0.9,
|
|
114
|
+
"fadeMax": 0.9913000000000001,
|
|
115
|
+
"dropRate": 0.003,
|
|
116
|
+
"dropRateBump": 0,
|
|
117
|
+
"opacity": 1,
|
|
118
|
+
"logScale": true,
|
|
119
|
+
"vibrance": 0.39
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "surface-wind",
|
|
124
|
+
"label": "Surface Wind",
|
|
125
|
+
"description": "Global hourly sea surface wind from scatterometer and model",
|
|
126
|
+
"category": "Atmosphere",
|
|
127
|
+
"type": "vector",
|
|
128
|
+
"source_dataset": "cmems_obs-wind_glo_phy_nrt_l4_0.125deg_PT1H",
|
|
129
|
+
"zarr_url_geo": "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",
|
|
130
|
+
"zarr_url_time": "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",
|
|
131
|
+
"variable_u": "eastward_wind",
|
|
132
|
+
"variable_v": "northward_wind",
|
|
133
|
+
"variable_meta": {
|
|
134
|
+
"standard_name": "wind_velocity",
|
|
135
|
+
"units": "m s-1"
|
|
136
|
+
},
|
|
137
|
+
"dimensions": {
|
|
138
|
+
"time": {
|
|
139
|
+
"axis": "t",
|
|
140
|
+
"size": 20352,
|
|
141
|
+
"min": 1700438400000.0,
|
|
142
|
+
"max": 1773702000000.0,
|
|
143
|
+
"step": 3600000.0,
|
|
144
|
+
"chunk_size": 1,
|
|
145
|
+
"units": "milliseconds since 1970-01-01 00:00:00Z (no leap seconds)"
|
|
146
|
+
},
|
|
147
|
+
"latitude": {
|
|
148
|
+
"axis": "y",
|
|
149
|
+
"size": 1440,
|
|
150
|
+
"min": -89.9375,
|
|
151
|
+
"max": 89.9375,
|
|
152
|
+
"step": 0.125,
|
|
153
|
+
"chunk_size": 512,
|
|
154
|
+
"units": "degrees_north"
|
|
155
|
+
},
|
|
156
|
+
"longitude": {
|
|
157
|
+
"axis": "x",
|
|
158
|
+
"size": 2880,
|
|
159
|
+
"min": -179.9375,
|
|
160
|
+
"max": 179.9375,
|
|
161
|
+
"step": 0.125,
|
|
162
|
+
"chunk_size": 1024,
|
|
163
|
+
"units": "degrees_east"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"defaults": {
|
|
167
|
+
"palette": "rdylbu",
|
|
168
|
+
"renderMode": "particles",
|
|
169
|
+
"particleDensity": 0.004000000000000002,
|
|
170
|
+
"speedMin": 0.010000000000000005,
|
|
171
|
+
"speedMax": 0.07000000000000002,
|
|
172
|
+
"fadeMin": 0.9174,
|
|
173
|
+
"fadeMax": 0.9793000000000001,
|
|
174
|
+
"dropRate": 0.005,
|
|
175
|
+
"dropRateBump": 0,
|
|
176
|
+
"opacity": 0.9,
|
|
177
|
+
"logScale": true,
|
|
178
|
+
"vibrance": 1
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "phytoplankton-chlorophyll",
|
|
183
|
+
"label": "Phytoplankton (Chlorophyll-a)",
|
|
184
|
+
"description": "Daily surface chlorophyll-a concentration",
|
|
185
|
+
"category": "Biology",
|
|
186
|
+
"type": "scalar",
|
|
187
|
+
"source_dataset": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m",
|
|
188
|
+
"zarr_url_geo": "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",
|
|
189
|
+
"zarr_url_time": "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",
|
|
190
|
+
"variable": "chl",
|
|
191
|
+
"variable_meta": {
|
|
192
|
+
"standard_name": "mass_concentration_of_chlorophyll_a_in_sea_water",
|
|
193
|
+
"units": "mg m-3"
|
|
194
|
+
},
|
|
195
|
+
"dimensions": {
|
|
196
|
+
"time": {
|
|
197
|
+
"axis": "t",
|
|
198
|
+
"size": 1608,
|
|
199
|
+
"min": 1635724800000.0,
|
|
200
|
+
"max": 1774569600000.0,
|
|
201
|
+
"step": 86400000.0,
|
|
202
|
+
"chunk_size": 1,
|
|
203
|
+
"units": "milliseconds since 1970-01-01 00:00:00Z (no leap seconds)"
|
|
204
|
+
},
|
|
205
|
+
"depth": {
|
|
206
|
+
"axis": "z",
|
|
207
|
+
"values": [
|
|
208
|
+
5727.91650390625,
|
|
209
|
+
5274.7841796875,
|
|
210
|
+
4833.29052734375,
|
|
211
|
+
4405.22412109375,
|
|
212
|
+
3992.48388671875,
|
|
213
|
+
3597.031982421875,
|
|
214
|
+
3220.8203125,
|
|
215
|
+
2865.70263671875,
|
|
216
|
+
2533.336181640625,
|
|
217
|
+
2225.077880859375,
|
|
218
|
+
1941.8934326171875,
|
|
219
|
+
1684.284423828125,
|
|
220
|
+
1452.2509765625,
|
|
221
|
+
1245.2911376953125,
|
|
222
|
+
1062.439697265625,
|
|
223
|
+
902.3392944335938,
|
|
224
|
+
763.3330688476562,
|
|
225
|
+
643.5668334960938,
|
|
226
|
+
541.0889282226562,
|
|
227
|
+
453.937744140625,
|
|
228
|
+
380.2130126953125,
|
|
229
|
+
318.12744140625,
|
|
230
|
+
266.0402526855469,
|
|
231
|
+
222.4751739501953,
|
|
232
|
+
186.1255645751953,
|
|
233
|
+
155.85072326660156,
|
|
234
|
+
130.66598510742188,
|
|
235
|
+
109.72927856445312,
|
|
236
|
+
92.3260726928711,
|
|
237
|
+
77.85385131835938,
|
|
238
|
+
65.8072738647461,
|
|
239
|
+
55.76428985595703,
|
|
240
|
+
47.373687744140625,
|
|
241
|
+
40.344051361083984,
|
|
242
|
+
34.43415451049805,
|
|
243
|
+
29.44472885131836,
|
|
244
|
+
25.211408615112305,
|
|
245
|
+
21.59881591796875,
|
|
246
|
+
18.495559692382812,
|
|
247
|
+
15.810072898864746,
|
|
248
|
+
13.467138290405273,
|
|
249
|
+
11.40500259399414,
|
|
250
|
+
9.572997093200684,
|
|
251
|
+
7.92956018447876,
|
|
252
|
+
6.440614223480225,
|
|
253
|
+
5.078223705291748,
|
|
254
|
+
3.8194947242736816,
|
|
255
|
+
2.6456685066223145,
|
|
256
|
+
1.5413753986358643,
|
|
257
|
+
0.4940253794193268
|
|
258
|
+
],
|
|
259
|
+
"size": 50,
|
|
260
|
+
"chunk_size": 1,
|
|
261
|
+
"units": "m"
|
|
262
|
+
},
|
|
263
|
+
"latitude": {
|
|
264
|
+
"axis": "y",
|
|
265
|
+
"size": 681,
|
|
266
|
+
"min": -80.0,
|
|
267
|
+
"max": 90.0,
|
|
268
|
+
"step": 0.25,
|
|
269
|
+
"chunk_size": 681,
|
|
270
|
+
"units": "degrees_north"
|
|
271
|
+
},
|
|
272
|
+
"longitude": {
|
|
273
|
+
"axis": "x",
|
|
274
|
+
"size": 1440,
|
|
275
|
+
"min": -180.0,
|
|
276
|
+
"max": 179.75,
|
|
277
|
+
"step": 0.25,
|
|
278
|
+
"chunk_size": 1440,
|
|
279
|
+
"units": "degrees_east"
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
"vertical_label": "depth",
|
|
283
|
+
"defaults": {
|
|
284
|
+
"palette": "matter",
|
|
285
|
+
"renderMode": "raster",
|
|
286
|
+
"opacity": 0.9,
|
|
287
|
+
"logScale": true,
|
|
288
|
+
"vibrance": 0.2
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
,{
|
|
292
|
+
"id": "wave-stokes-drift",
|
|
293
|
+
"label": "Wave Stokes Drift",
|
|
294
|
+
"description": "Net horizontal wave-driven transport (Stokes drift) from global 3-hourly wave analysis — visualises wave energy propagation direction.",
|
|
295
|
+
"category": "Ocean",
|
|
296
|
+
"type": "vector",
|
|
297
|
+
"source_dataset": "cmems_mod_glo_wav_anfc_0.083deg_PT3H-i",
|
|
298
|
+
"zarr_url_geo": "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",
|
|
299
|
+
"zarr_url_time": "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",
|
|
300
|
+
"variable_u": "VSDX",
|
|
301
|
+
"variable_v": "VSDY",
|
|
302
|
+
"variable_meta": {
|
|
303
|
+
"standard_name": "sea_surface_wave_stokes_drift_velocity",
|
|
304
|
+
"units": "m s-1"
|
|
305
|
+
},
|
|
306
|
+
"dimensions": {
|
|
307
|
+
"time": {
|
|
308
|
+
"axis": "t",
|
|
309
|
+
"size": 9944,
|
|
310
|
+
"min": 1667271600000,
|
|
311
|
+
"max": 1774656000000,
|
|
312
|
+
"step": 10800000,
|
|
313
|
+
"chunk_size": 1,
|
|
314
|
+
"units": "milliseconds since 1970-01-01 00:00:00Z (no leap seconds)"
|
|
315
|
+
},
|
|
316
|
+
"latitude": {
|
|
317
|
+
"axis": "y",
|
|
318
|
+
"size": 2041,
|
|
319
|
+
"min": -80.0,
|
|
320
|
+
"max": 90.0,
|
|
321
|
+
"step": 0.08333333333333333,
|
|
322
|
+
"chunk_size": 512,
|
|
323
|
+
"units": "degrees_north"
|
|
324
|
+
},
|
|
325
|
+
"longitude": {
|
|
326
|
+
"axis": "x",
|
|
327
|
+
"size": 4320,
|
|
328
|
+
"min": -180.0,
|
|
329
|
+
"max": 179.91666666666663,
|
|
330
|
+
"step": 0.08333333333333333,
|
|
331
|
+
"chunk_size": 2048,
|
|
332
|
+
"units": "degrees_east"
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"defaults": {
|
|
336
|
+
"palette": "rdylbu",
|
|
337
|
+
"renderMode": "particles",
|
|
338
|
+
"particleDensity": 0.024,
|
|
339
|
+
"speedMin": 0.010000000000000005,
|
|
340
|
+
"speedMax": 1.59,
|
|
341
|
+
"fadeMin": 0.975,
|
|
342
|
+
"fadeMax": 0.9935,
|
|
343
|
+
"dropRate": 0.003,
|
|
344
|
+
"dropRateBump": 0,
|
|
345
|
+
"opacity": 1,
|
|
346
|
+
"logScale": true,
|
|
347
|
+
"vibrance": 1
|
|
105
348
|
}
|
|
106
349
|
}
|
|
107
350
|
]
|