@dclimate/zarr-map 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +374 -0
  3. package/dist/core/index.cjs +1603 -0
  4. package/dist/core/index.cjs.map +1 -0
  5. package/dist/core/index.d.cts +50 -0
  6. package/dist/core/index.d.ts +50 -0
  7. package/dist/core/index.js +1575 -0
  8. package/dist/core/index.js.map +1 -0
  9. package/dist/dclimate/index.cjs +1859 -0
  10. package/dist/dclimate/index.cjs.map +1 -0
  11. package/dist/dclimate/index.d.cts +80 -0
  12. package/dist/dclimate/index.d.ts +80 -0
  13. package/dist/dclimate/index.js +1856 -0
  14. package/dist/dclimate/index.js.map +1 -0
  15. package/dist/index.cjs +3071 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +5 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +3038 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/jaxray-CZWT_ZgD.d.ts +57 -0
  22. package/dist/jaxray-D_mmLPHk.d.cts +57 -0
  23. package/dist/react/index.cjs +3953 -0
  24. package/dist/react/index.cjs.map +1 -0
  25. package/dist/react/index.d.cts +71 -0
  26. package/dist/react/index.d.ts +71 -0
  27. package/dist/react/index.js +3945 -0
  28. package/dist/react/index.js.map +1 -0
  29. package/dist/renderers/index.cjs +903 -0
  30. package/dist/renderers/index.cjs.map +1 -0
  31. package/dist/renderers/index.d.cts +115 -0
  32. package/dist/renderers/index.d.ts +115 -0
  33. package/dist/renderers/index.js +899 -0
  34. package/dist/renderers/index.js.map +1 -0
  35. package/dist/types-DEZwfJNY.d.cts +210 -0
  36. package/dist/types-DEZwfJNY.d.ts +210 -0
  37. package/docs/README.md +12 -0
  38. package/docs/api-design.md +185 -0
  39. package/docs/architecture.md +246 -0
  40. package/docs/decision-record-renderer.md +144 -0
  41. package/docs/package-boundaries.md +50 -0
  42. package/docs/release-checklist.md +31 -0
  43. package/package.json +121 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dClimate
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,374 @@
1
+ # @dclimate/zarr-map
2
+
3
+ `@dclimate/zarr-map` is a TypeScript library for rendering
4
+ dClimate-compatible Zarr datasets on interactive maps. The package exists so a
5
+ website can pass a dataset request, variable, map config, time range, spatial
6
+ bounds, and visual options, then get a working MapLibre visualization without
7
+ solving Zarr metadata, IPFS/gateway access, selector normalization, or renderer
8
+ compatibility itself.
9
+
10
+ The package should feel like a light map-rendering wrapper over
11
+ `@dclimate/dclimate-client-js`, Jaxray-compatible dataset metadata/stores,
12
+ MapLibre, and the selected Zarr renderer. It should not become a full data
13
+ explorer application.
14
+
15
+ ## Product Direction
16
+
17
+ The primary use case is no-hassle dClimate map rendering:
18
+
19
+ ```tsx
20
+ <DClimateZarrMap
21
+ dataset={{
22
+ collection: "ecmwf_era5",
23
+ dataset: "temperature_2m",
24
+ variant: "finalized"
25
+ }}
26
+ gatewayUrl="https://ipfs-gateway.dclimate.net"
27
+ variable="2m_temperature"
28
+ map={mapConfig}
29
+ bounds={[-12, 35, 16, 60]}
30
+ timeRange={{
31
+ start: "2024-01-01T00:00:00Z",
32
+ end: "2024-01-07T23:00:00Z"
33
+ }}
34
+ colorScale={{
35
+ palette: "viridis",
36
+ domain: [260, 320],
37
+ unit: "K",
38
+ displayUnit: "C",
39
+ quantity: "temperature"
40
+ }}
41
+ />
42
+ ```
43
+
44
+ The package should support two usage modes:
45
+
46
+ 1. **dClimate mode**
47
+ - Accept a dClimate dataset query/config.
48
+ - Use `@dclimate/dclimate-client-js` to resolve and load datasets, including
49
+ spatial bounds and time-window selection when provided.
50
+ - Render selected variables on a MapLibre map with light controls such as a
51
+ legend, time selector, loading/error status, and inspect callback.
52
+
53
+ 2. **Generic grid-source mode**
54
+ - Accept a normalized `GridDataSource` object.
55
+ - Support Jaxray-compatible datasets through an adapter.
56
+ - Keep the renderer reusable outside dClimate datasets.
57
+
58
+ The preferred architecture is a generic core with first-class dClimate and
59
+ Jaxray adapters, but package features should stay close to rendering and
60
+ request-scoped data loading.
61
+
62
+ ## Product Boundaries
63
+
64
+ Include functionality that removes friction from map rendering:
65
+
66
+ - dClimate dataset resolution and bounded/time-window source loading.
67
+ - Normalized metadata contracts for Zarr/Jaxray sources.
68
+ - Selector normalization for time, band, and other non-spatial dimensions.
69
+ - MapLibre layer creation and updates.
70
+ - Renderer loading/error state.
71
+ - Light visual customization: palette/gradient, color domain, opacity, display
72
+ unit, legend, time selector, and inspect callbacks.
73
+ - Request preflight checks that estimate cells and bytes before oversized map
74
+ requests begin expensive bounded loading.
75
+
76
+ Avoid functionality that turns this package into an application:
77
+
78
+ - Marketplace purchase flow, wallet/order state, or decryption UX.
79
+ - CSV/JSON export.
80
+ - 2D chart rendering or chart aggregation.
81
+ - Dashboard layouts, modal workflows, generic form builders, or rich explorer
82
+ pages.
83
+ - Heavy map drawing/editing tools as core behavior.
84
+
85
+ If a feature is mostly about discovering, resolving, slicing, or decrypting
86
+ dClimate data rather than rendering it, prefer implementing it upstream in
87
+ `dclimate-client-js` or another dClimate data package and consuming it here.
88
+
89
+ ## Initial Technical Direction
90
+
91
+ - Base map: MapLibre GL JS by default.
92
+ - Optional compatibility: allow users to provide existing MapLibre or Mapbox map instances where practical.
93
+ - First renderer candidate: `@carbonplan/zarr-layer`.
94
+ - Avoid a custom WebGL raster renderer unless the research spike proves existing renderers cannot support target datasets.
95
+ - Keep `@dclimate/dclimate-client-js` optional for users who only need generic grid-source mode.
96
+ - Keep React UI primitives small. Prefer prop-driven rendering over bundled
97
+ explorer workflows.
98
+
99
+ ## Target Package Shape
100
+
101
+ ```txt
102
+ @dclimate/zarr-map
103
+ @dclimate/zarr-map/core
104
+ @dclimate/zarr-map/react
105
+ @dclimate/zarr-map/dclimate
106
+ ```
107
+
108
+ Planned responsibilities:
109
+
110
+ - `src/core`: framework-agnostic contracts, selectors, validation, color scales, and query helpers.
111
+ - `src/renderers`: renderer bridges, starting with MapLibre plus `@carbonplan/zarr-layer`.
112
+ - `src/dclimate`: dClimate client adapter and dataset discovery/loading helpers.
113
+ - `src/react`: React components such as `ZarrMap`, `DClimateZarrMap`, `TimeSlider`, and `Legend`.
114
+ - `docs`: architecture notes and decision records.
115
+ - `tests`: future unit and integration tests.
116
+ - `fixtures`: small fixture datasets or mocks for tests.
117
+ - `todo`: implementation tickets with acceptance criteria and dependencies.
118
+
119
+ ## MVP API Target
120
+
121
+ ```tsx
122
+ import { DClimateZarrMap } from "@dclimate/zarr-map/react";
123
+
124
+ export function App() {
125
+ return (
126
+ <DClimateZarrMap
127
+ dataset={{
128
+ collection: "ecmwf_era5",
129
+ dataset: "temperature_2m",
130
+ variant: "finalized"
131
+ }}
132
+ gatewayUrl="https://ipfs-gateway.dclimate.net"
133
+ variable="2m_temperature"
134
+ timeDimension="time"
135
+ initialSelector={{ time: "2024-01-01T00:00:00Z" }}
136
+ map={{
137
+ center: [-8.6, 39.5],
138
+ zoom: 5,
139
+ style: "https://demotiles.maplibre.org/style.json"
140
+ }}
141
+ bounds={[-12, 35, 16, 60]}
142
+ timeRange={{
143
+ start: "2024-01-01T00:00:00Z",
144
+ end: "2024-01-07T23:00:00Z"
145
+ }}
146
+ colorScale={{
147
+ palette: "viridis",
148
+ domain: [260, 320],
149
+ unit: "K"
150
+ }}
151
+ controls={{
152
+ timeSlider: true,
153
+ legend: true,
154
+ inspect: true
155
+ }}
156
+ />
157
+ );
158
+ }
159
+ ```
160
+
161
+ Generic mode should use the same renderer through a normalized source:
162
+
163
+ ```tsx
164
+ import { createJaxraySource } from "@dclimate/zarr-map/core";
165
+ import { ZarrMap } from "@dclimate/zarr-map/react";
166
+
167
+ const source = createJaxraySource(dataset);
168
+
169
+ <ZarrMap
170
+ source={source}
171
+ variable="precipitation"
172
+ selector={{ time: 0 }}
173
+ controls={{ timeSlider: true, legend: true }}
174
+ />;
175
+ ```
176
+
177
+ ## Install
178
+
179
+ ```bash
180
+ npm install @dclimate/zarr-map maplibre-gl @carbonplan/zarr-layer
181
+ ```
182
+
183
+ React users also install `react` and `react-dom`. dClimate mode additionally
184
+ uses the optional peers `@dclimate/dclimate-client-js` and `@dclimate/jaxray`.
185
+
186
+ ## Demo
187
+
188
+ - Demo URL after Vercel deployment: https://zarr-map-demo.vercel.app
189
+ - Demo source: https://github.com/dClimate/zarr-map-demo
190
+
191
+ The standalone Vite demo app consumes this package like an external user. For
192
+ local development before the first NPM publish, the demo can depend on the
193
+ package with a local `file:../zarr-map-visualizer` link.
194
+
195
+ ## Package Entrypoints
196
+
197
+ ```ts
198
+ import { createJaxraySource, queryPoint } from "@dclimate/zarr-map/core";
199
+ import { createMapLibreGridLayer } from "@dclimate/zarr-map/renderer";
200
+ import { createDClimateSource } from "@dclimate/zarr-map/dclimate";
201
+ import { DClimateZarrMap, ZarrMap } from "@dclimate/zarr-map/react";
202
+ ```
203
+
204
+ ## Generic Grid Mode
205
+
206
+ ```tsx
207
+ import { createJaxraySource } from "@dclimate/zarr-map/core";
208
+ import { ZarrMap } from "@dclimate/zarr-map/react";
209
+
210
+ const source = createJaxraySource(dataset, {
211
+ bounds: [-10, 35, 5, 45],
212
+ spatialDimensions: { x: "lon", y: "lat" }
213
+ });
214
+
215
+ <ZarrMap
216
+ colorScale={{ palette: "viridis", domain: [0, 50], unit: "mm" }}
217
+ controls={{ legend: true, timeSlider: true, inspect: true }}
218
+ map={{
219
+ center: [-8.6, 39.5],
220
+ zoom: 5,
221
+ style: "https://demotiles.maplibre.org/style.json"
222
+ }}
223
+ source={source}
224
+ variable="precipitation"
225
+ />;
226
+ ```
227
+
228
+ ## dClimate Mode
229
+
230
+ ```tsx
231
+ import { DClimateZarrMap } from "@dclimate/zarr-map/react";
232
+
233
+ <DClimateZarrMap
234
+ colorScale={{
235
+ palette: "viridis",
236
+ domain: [260, 320],
237
+ unit: "K",
238
+ displayUnit: "C",
239
+ quantity: "temperature"
240
+ }}
241
+ dataset={{
242
+ collection: "ecmwf_era5",
243
+ dataset: "temperature_2m",
244
+ variant: "finalized"
245
+ }}
246
+ gatewayUrl="https://ipfs-gateway.dclimate.net"
247
+ initialSelector={{ time: "2024-01-01T00:00:00.000Z" }}
248
+ bounds={[-12, 35, 16, 60]}
249
+ timeRange={{
250
+ start: "2024-01-01T00:00:00Z",
251
+ end: "2024-01-07T23:00:00Z"
252
+ }}
253
+ map={{
254
+ bounds: [
255
+ [-12, 35],
256
+ [16, 60]
257
+ ],
258
+ style: "https://demotiles.maplibre.org/style.json"
259
+ }}
260
+ variable="2m_temperature"
261
+ />;
262
+ ```
263
+
264
+ `bounds` and `timeRange` are convenience aliases for
265
+ `sourceOptions.selection.bounds` and `sourceOptions.selection.timeRange`. If a
266
+ caller needs lower-level dClimate selection options, it can pass
267
+ `sourceOptions.selection` directly. The aliases normalize into that same
268
+ selection object and do not create a separate selection model.
269
+
270
+ `map.bounds` and `map.maxBounds` are forwarded to MapLibre for viewport fitting
271
+ and pan limits.
272
+
273
+ ## Visual Customization
274
+
275
+ `colorScale.palette` accepts a built-in palette name or a custom color-stop
276
+ array such as `["#1d4ed8", "#f8fafc", "#b91c1c"]`. Renderer domains stay in
277
+ the source unit. Built-in names include `viridis`, `inferno`, `magma`,
278
+ `plasma`, `cividis`, `turbo`, `greys`, `temperature`, `precipitation`,
279
+ `humidity`, `vegetation`, and `wind`. The `magma`, `precipitation`, and
280
+ `vegetation` palettes start transparent at the lower bound for overlaying low,
281
+ dry, or no-vegetation cells on top of the basemap. To show a converted display
282
+ unit in legends and inspect results, include `unit`, `displayUnit`, and
283
+ `quantity`:
284
+
285
+ ```tsx
286
+ colorScale={{
287
+ palette: ["#1d4ed8", "#f8fafc", "#b91c1c"],
288
+ domain: [260, 320],
289
+ unit: "K",
290
+ displayUnit: "C",
291
+ quantity: "temperature"
292
+ }}
293
+ ```
294
+
295
+ Supported display conversions are temperature (`K`, `C`, `F`) and precipitation
296
+ (`m`, `mm`, `in`).
297
+
298
+ Inspect controls default to click-to-inspect with `controls={{ inspect: true }}`.
299
+ Use hover mode for a pointer-following tooltip:
300
+
301
+ ```tsx
302
+ <ZarrMap
303
+ ...
304
+ controls={{ inspect: { mode: "hover", debounceMs: 75 } }}
305
+ />
306
+ ```
307
+
308
+ ## Request Preflight
309
+
310
+ dClimate mode can estimate request size before bounded raster chunks are
311
+ materialized:
312
+
313
+ ```tsx
314
+ <DClimateZarrMap
315
+ ...
316
+ preflight={{ limits: { maxCells: 100_000, maxBytes: 256 * 1024 * 1024 } }}
317
+ onPreflight={(result) => {
318
+ console.log(result.cells, result.bytes, result.warnings);
319
+ }}
320
+ />
321
+ ```
322
+
323
+ The framework-independent helper is also exported from core as
324
+ `preflightGridRequest`.
325
+
326
+ The dClimate gateway is not generally down. On June 9, 2026,
327
+ `../dclimate-client-js-autoresearch` still opened the current STAC ERA5 dataset
328
+ through `@dclimate/dclimate-client-js` plus `@dclimate/jaxray`, and its smoke
329
+ benchmark succeeded against `https://ipfs-gateway.dclimate.net`.
330
+
331
+ Current validated fixture:
332
+
333
+ - collection: `ecmwf_era5`
334
+ - dataset: `temperature_2m`
335
+ - variant: `finalized`
336
+ - variable: `2m_temperature`
337
+ - CID: `bafyr4ifr5jtjeommsulg7srirdkskybj5pay3n4qyehecyzsesas5k2kd4`
338
+
339
+ The earlier blocked static CIDs should be treated as stale or unretrievable
340
+ fixtures, not evidence that the gateway or Jaxray are broken. dClimate loading
341
+ should call the real `DClimateClient.loadDataset({ request, options })` API and
342
+ unwrap the returned `[Dataset, metadata]` tuple.
343
+
344
+ ## Query Helpers
345
+
346
+ ```ts
347
+ import { queryGrid, queryPoint } from "@dclimate/zarr-map/core";
348
+
349
+ const clicked = await queryPoint(source, "temperature", [-8.6, 39.5], {
350
+ time: "2024-01-01T00:00:00.000Z"
351
+ });
352
+
353
+ const bounded = await queryGrid({
354
+ source,
355
+ variable: "temperature",
356
+ geometry: { type: "BoundingBox", bounds: [-9, 38, -8, 40] },
357
+ maxCells: 5000
358
+ });
359
+ ```
360
+
361
+ ## Development
362
+
363
+ ```bash
364
+ npm run typecheck
365
+ npm run format:check
366
+ npm run lint
367
+ npm run test
368
+ npm run build
369
+ ```
370
+
371
+ CI runs the same package checks on pushes and pull requests. The demo app has
372
+ its own build and deployment workflow in the separate `zarr-map-demo`
373
+ repository. Release expectations and known renderer limitations are documented
374
+ in `docs/release-checklist.md`.