@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.
- package/LICENSE +21 -0
- package/README.md +374 -0
- package/dist/core/index.cjs +1603 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +50 -0
- package/dist/core/index.d.ts +50 -0
- package/dist/core/index.js +1575 -0
- package/dist/core/index.js.map +1 -0
- package/dist/dclimate/index.cjs +1859 -0
- package/dist/dclimate/index.cjs.map +1 -0
- package/dist/dclimate/index.d.cts +80 -0
- package/dist/dclimate/index.d.ts +80 -0
- package/dist/dclimate/index.js +1856 -0
- package/dist/dclimate/index.js.map +1 -0
- package/dist/index.cjs +3071 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3038 -0
- package/dist/index.js.map +1 -0
- package/dist/jaxray-CZWT_ZgD.d.ts +57 -0
- package/dist/jaxray-D_mmLPHk.d.cts +57 -0
- package/dist/react/index.cjs +3953 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +71 -0
- package/dist/react/index.d.ts +71 -0
- package/dist/react/index.js +3945 -0
- package/dist/react/index.js.map +1 -0
- package/dist/renderers/index.cjs +903 -0
- package/dist/renderers/index.cjs.map +1 -0
- package/dist/renderers/index.d.cts +115 -0
- package/dist/renderers/index.d.ts +115 -0
- package/dist/renderers/index.js +899 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/types-DEZwfJNY.d.cts +210 -0
- package/dist/types-DEZwfJNY.d.ts +210 -0
- package/docs/README.md +12 -0
- package/docs/api-design.md +185 -0
- package/docs/architecture.md +246 -0
- package/docs/decision-record-renderer.md +144 -0
- package/docs/package-boundaries.md +50 -0
- package/docs/release-checklist.md +31 -0
- 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`.
|