@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
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var GridError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
cause;
|
|
5
|
+
dimension;
|
|
6
|
+
variable;
|
|
7
|
+
sourceId;
|
|
8
|
+
context;
|
|
9
|
+
constructor(details) {
|
|
10
|
+
super(details.message);
|
|
11
|
+
this.name = "GridError";
|
|
12
|
+
this.code = details.code;
|
|
13
|
+
this.cause = details.cause;
|
|
14
|
+
this.dimension = details.dimension;
|
|
15
|
+
this.variable = details.variable;
|
|
16
|
+
this.sourceId = details.sourceId;
|
|
17
|
+
this.context = details.context;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// src/core/color.ts
|
|
22
|
+
var builtinPalettes = {
|
|
23
|
+
viridis: ["#440154", "#31688e", "#35b779", "#fde725"],
|
|
24
|
+
inferno: ["#000004", "#57106e", "#bc3754", "#f98e09", "#fcffa4"],
|
|
25
|
+
magma: ["#000004", "#3b0f70", "#8c2981", "#de4968", "#fe9f6d", "#fcfdbf"],
|
|
26
|
+
plasma: ["#0d0887", "#7e03a8", "#cc4778", "#f89540", "#f0f921"],
|
|
27
|
+
cividis: ["#00204c", "#31446b", "#666870", "#958f78", "#c6b866", "#ffe945"],
|
|
28
|
+
turbo: ["#30123b", "#466be3", "#35ab6b", "#faba39", "#7a0403"],
|
|
29
|
+
greys: ["#000000", "#777777", "#ffffff"],
|
|
30
|
+
grays: ["#000000", "#777777", "#ffffff"],
|
|
31
|
+
temperature: ["#2c5ab4", "#67b0f0", "#e0e4dc", "#ffb266", "#cd4434"],
|
|
32
|
+
precipitation: ["#bae6fd", "#60a5fa", "#2563eb", "#4f46e5", "#6d28d9"],
|
|
33
|
+
humidity: ["#374151", "#2d5a6e", "#1890b0", "#2dd4bf", "#99f6e4"],
|
|
34
|
+
vegetation: ["#f7fee7", "#bef264", "#4ade80", "#15803d", "#064e3b"],
|
|
35
|
+
wind: ["#e2e8f0", "#7dd3fc", "#2dd4bf", "#14b8a6"]
|
|
36
|
+
};
|
|
37
|
+
function validateColorScale(colorScale) {
|
|
38
|
+
const [min, max] = colorScale.domain;
|
|
39
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || min >= max) {
|
|
40
|
+
throw new GridError({
|
|
41
|
+
code: "INVALID_COLOR_SCALE",
|
|
42
|
+
message: "Color scale domain must be finite and ordered as [min, max].",
|
|
43
|
+
context: { domain: colorScale.domain }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!colorScale.palette) {
|
|
47
|
+
throw new GridError({
|
|
48
|
+
code: "INVALID_COLOR_SCALE",
|
|
49
|
+
message: "Color scale must include a palette name or color stop array."
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
paletteToRendererColorMap(colorScale.palette);
|
|
53
|
+
return colorScale;
|
|
54
|
+
}
|
|
55
|
+
function colorScaleToRendererOptions(colorScale) {
|
|
56
|
+
if (!colorScale) {
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
validateColorScale(colorScale);
|
|
60
|
+
return {
|
|
61
|
+
colormap: paletteToRendererColorMap(colorScale.palette),
|
|
62
|
+
clim: colorScale.domain
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function paletteToRendererColorMap(palette) {
|
|
66
|
+
if (typeof palette !== "string") {
|
|
67
|
+
if (palette.length < 2) {
|
|
68
|
+
throw new GridError({
|
|
69
|
+
code: "INVALID_COLOR_SCALE",
|
|
70
|
+
message: "Custom color palettes must include at least two color stops.",
|
|
71
|
+
context: { palette }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const colors2 = palette.map((color) => color.trim());
|
|
75
|
+
const invalid = colors2.find((color) => color.length === 0);
|
|
76
|
+
if (invalid !== void 0) {
|
|
77
|
+
throw new GridError({
|
|
78
|
+
code: "INVALID_COLOR_SCALE",
|
|
79
|
+
message: "Custom color palettes cannot include empty color stops.",
|
|
80
|
+
context: { palette }
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return colors2;
|
|
84
|
+
}
|
|
85
|
+
const normalized = palette.toLowerCase();
|
|
86
|
+
const colors = builtinPalettes[normalized];
|
|
87
|
+
if (!colors) {
|
|
88
|
+
throw new GridError({
|
|
89
|
+
code: "INVALID_COLOR_SCALE",
|
|
90
|
+
message: `Unsupported renderer palette "${palette}". Use one of: ${Object.keys(builtinPalettes).join(", ")}.`,
|
|
91
|
+
context: { palette }
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return colors;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/core/validation.ts
|
|
98
|
+
var xAliases = /* @__PURE__ */ new Set(["x", "lon", "lng", "longitude"]);
|
|
99
|
+
var yAliases = /* @__PURE__ */ new Set(["y", "lat", "latitude"]);
|
|
100
|
+
var numericDtypes = /* @__PURE__ */ new Set([
|
|
101
|
+
"int8",
|
|
102
|
+
"uint8",
|
|
103
|
+
"int16",
|
|
104
|
+
"uint16",
|
|
105
|
+
"int32",
|
|
106
|
+
"uint32",
|
|
107
|
+
"float32",
|
|
108
|
+
"float64",
|
|
109
|
+
"i1",
|
|
110
|
+
"u1",
|
|
111
|
+
"i2",
|
|
112
|
+
"u2",
|
|
113
|
+
"i4",
|
|
114
|
+
"u4",
|
|
115
|
+
"f4",
|
|
116
|
+
"f8"
|
|
117
|
+
]);
|
|
118
|
+
function findVariable(source, variableName) {
|
|
119
|
+
return source.variables.find(
|
|
120
|
+
(variable) => variable.name === variableName || variable.path === variableName
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
function findDimension(source, dimensionName) {
|
|
124
|
+
return source.dimensions.find((dimension) => dimension.name === dimensionName);
|
|
125
|
+
}
|
|
126
|
+
function inferSpatialDimensions(source, variableName = source.variables[0]?.name) {
|
|
127
|
+
const variable = variableName ? findVariable(source, variableName) : source.variables[0];
|
|
128
|
+
if (source.spatialDimensions) {
|
|
129
|
+
return variableIncludesSpatialDimensions(variable, source.spatialDimensions) ? source.spatialDimensions : void 0;
|
|
130
|
+
}
|
|
131
|
+
const candidateNames = variable?.dimensions ?? source.dimensions.map((dimension) => dimension.name);
|
|
132
|
+
let x;
|
|
133
|
+
let y;
|
|
134
|
+
for (const name of candidateNames) {
|
|
135
|
+
const dimension = findDimension(source, name);
|
|
136
|
+
const normalizedName = name.toLowerCase();
|
|
137
|
+
const standardName = String(dimension?.standardName ?? "").toLowerCase();
|
|
138
|
+
const kind = dimension?.kind;
|
|
139
|
+
if (!x && (kind === "x" || xAliases.has(normalizedName) || standardName === "longitude")) {
|
|
140
|
+
x = name;
|
|
141
|
+
}
|
|
142
|
+
if (!y && (kind === "y" || yAliases.has(normalizedName) || standardName === "latitude")) {
|
|
143
|
+
y = name;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return x && y ? { x, y } : void 0;
|
|
147
|
+
}
|
|
148
|
+
function variableIncludesSpatialDimensions(variable, spatialDimensions) {
|
|
149
|
+
if (!variable) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return variable.dimensions.includes(spatialDimensions.x) && variable.dimensions.includes(spatialDimensions.y);
|
|
153
|
+
}
|
|
154
|
+
function validateGridDataSource(source, options = {}) {
|
|
155
|
+
const errors = [];
|
|
156
|
+
const warnings = [];
|
|
157
|
+
if (source.variables.length === 0) {
|
|
158
|
+
errors.push(
|
|
159
|
+
new GridError({
|
|
160
|
+
code: "MISSING_VARIABLE",
|
|
161
|
+
message: "GridDataSource must expose at least one variable.",
|
|
162
|
+
sourceId: source.id
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const variable = options.variable ? findVariable(source, options.variable) : source.variables[0];
|
|
167
|
+
if (options.variable && !variable) {
|
|
168
|
+
errors.push(
|
|
169
|
+
new GridError({
|
|
170
|
+
code: "MISSING_VARIABLE",
|
|
171
|
+
message: `Variable "${options.variable}" is not present in the grid source.`,
|
|
172
|
+
sourceId: source.id,
|
|
173
|
+
variable: options.variable
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
for (const dimension of source.dimensions) {
|
|
178
|
+
if (!Number.isInteger(dimension.size) || dimension.size <= 0) {
|
|
179
|
+
errors.push(
|
|
180
|
+
new GridError({
|
|
181
|
+
code: "UNSUPPORTED_DIMENSION",
|
|
182
|
+
message: `Dimension "${dimension.name}" must have a positive integer size.`,
|
|
183
|
+
dimension: dimension.name,
|
|
184
|
+
sourceId: source.id
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (dimension.coordinates && dimension.coordinates.length !== dimension.size) {
|
|
189
|
+
errors.push(
|
|
190
|
+
new GridError({
|
|
191
|
+
code: "MISSING_COORDINATES",
|
|
192
|
+
message: `Dimension "${dimension.name}" has ${dimension.coordinates.length} coordinates for size ${dimension.size}.`,
|
|
193
|
+
dimension: dimension.name,
|
|
194
|
+
sourceId: source.id
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (variable) {
|
|
200
|
+
if (!isNumericDType(variable.dtype)) {
|
|
201
|
+
errors.push(
|
|
202
|
+
new GridError({
|
|
203
|
+
code: "UNSUPPORTED_DTYPE",
|
|
204
|
+
message: `Variable "${variable.name}" has unsupported dtype "${variable.dtype}".`,
|
|
205
|
+
variable: variable.name,
|
|
206
|
+
sourceId: source.id
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (variable.dimensions.length !== variable.shape.length) {
|
|
211
|
+
errors.push(
|
|
212
|
+
new GridError({
|
|
213
|
+
code: "MISSING_DIMENSION",
|
|
214
|
+
message: `Variable "${variable.name}" has ${variable.dimensions.length} dimensions but ${variable.shape.length} shape entries.`,
|
|
215
|
+
variable: variable.name,
|
|
216
|
+
sourceId: source.id
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
variable.dimensions.forEach((dimensionName, index) => {
|
|
221
|
+
const dimension = findDimension(source, dimensionName);
|
|
222
|
+
if (!dimension) {
|
|
223
|
+
errors.push(
|
|
224
|
+
new GridError({
|
|
225
|
+
code: "MISSING_DIMENSION",
|
|
226
|
+
message: `Variable "${variable.name}" references missing dimension "${dimensionName}".`,
|
|
227
|
+
dimension: dimensionName,
|
|
228
|
+
variable: variable.name,
|
|
229
|
+
sourceId: source.id
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const expectedSize = variable.shape[index];
|
|
235
|
+
if (expectedSize !== dimension.size) {
|
|
236
|
+
warnings.push(
|
|
237
|
+
`Variable "${variable.name}" shape for dimension "${dimensionName}" is ${expectedSize}, but the dimension size is ${dimension.size}.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const spatialDimensions = inferSpatialDimensions(source, variable.name);
|
|
242
|
+
if (!spatialDimensions) {
|
|
243
|
+
errors.push(
|
|
244
|
+
new GridError({
|
|
245
|
+
code: "MISSING_SPATIAL_DIMENSIONS",
|
|
246
|
+
message: `Variable "${variable.name}" needs latitude/longitude or x/y spatial dimensions before it can render.`,
|
|
247
|
+
variable: variable.name,
|
|
248
|
+
sourceId: source.id
|
|
249
|
+
})
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (options.requireRenderable && !source.bounds) {
|
|
253
|
+
errors.push(
|
|
254
|
+
new GridError({
|
|
255
|
+
code: "MISSING_BOUNDS",
|
|
256
|
+
message: "Renderable grid sources must include explicit [west, south, east, north] bounds.",
|
|
257
|
+
variable: variable.name,
|
|
258
|
+
sourceId: source.id
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
ok: errors.length === 0,
|
|
265
|
+
errors,
|
|
266
|
+
warnings
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function assertValidGridDataSource(source, options = {}) {
|
|
270
|
+
const result = validateGridDataSource(source, options);
|
|
271
|
+
if (!result.ok) {
|
|
272
|
+
throw result.errors[0];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function isNumericDType(dtype) {
|
|
276
|
+
return numericDtypes.has(dtype.toLowerCase()) || /^[<>|]?[ifu]\d+$/.test(dtype.toLowerCase());
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/core/selectors.ts
|
|
280
|
+
function normalizeSelectors(source, variableName, selectors = {}) {
|
|
281
|
+
const variable = findVariable(source, variableName);
|
|
282
|
+
if (!variable) {
|
|
283
|
+
throw new GridError({
|
|
284
|
+
code: "MISSING_VARIABLE",
|
|
285
|
+
message: `Variable "${variableName}" is not present in the grid source.`,
|
|
286
|
+
variable: variableName,
|
|
287
|
+
sourceId: source.id
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const normalized = {};
|
|
291
|
+
for (const dimensionName of variable.dimensions) {
|
|
292
|
+
const dimension = findDimension(source, dimensionName);
|
|
293
|
+
if (!dimension) {
|
|
294
|
+
throw new GridError({
|
|
295
|
+
code: "MISSING_DIMENSION",
|
|
296
|
+
message: `Selector normalization cannot find dimension "${dimensionName}".`,
|
|
297
|
+
dimension: dimensionName,
|
|
298
|
+
variable: variableName,
|
|
299
|
+
sourceId: source.id
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
const input = selectors[dimensionName];
|
|
303
|
+
if (input === void 0) {
|
|
304
|
+
normalized[dimensionName] = defaultSelectorForDimension(dimension);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
normalized[dimensionName] = normalizeSelectorValue(dimension, input, source.id);
|
|
308
|
+
}
|
|
309
|
+
return normalized;
|
|
310
|
+
}
|
|
311
|
+
function normalizeSelectorValue(dimension, input, sourceId) {
|
|
312
|
+
if (input instanceof Date) {
|
|
313
|
+
return { kind: "coordinate", value: input.toISOString() };
|
|
314
|
+
}
|
|
315
|
+
if (typeof input !== "object") {
|
|
316
|
+
return normalizeCoordinateSelector(dimension, input, sourceId);
|
|
317
|
+
}
|
|
318
|
+
if (input.kind === "coordinate") {
|
|
319
|
+
const value = input.value instanceof Date ? input.value.toISOString() : input.value;
|
|
320
|
+
return normalizeCoordinateSelector(dimension, value, sourceId);
|
|
321
|
+
}
|
|
322
|
+
if (input.kind === "index") {
|
|
323
|
+
assertIndexInRange(dimension, input.index, sourceId);
|
|
324
|
+
const value = dimension.coordinates?.[input.index];
|
|
325
|
+
return {
|
|
326
|
+
kind: "index",
|
|
327
|
+
index: input.index,
|
|
328
|
+
value: value instanceof Date ? value.toISOString() : value
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (input.kind === "isoTime") {
|
|
332
|
+
const value = findCoordinateForIso(dimension, input.iso);
|
|
333
|
+
return value === void 0 ? { kind: "isoTime", iso: input.iso } : { kind: "isoTime", iso: input.iso, value };
|
|
334
|
+
}
|
|
335
|
+
throw new GridError({
|
|
336
|
+
code: "UNSUPPORTED_SELECTOR",
|
|
337
|
+
message: `Unsupported selector for dimension "${dimension.name}".`,
|
|
338
|
+
dimension: dimension.name,
|
|
339
|
+
sourceId
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
function toRendererSelectors(selectors) {
|
|
343
|
+
const output = {};
|
|
344
|
+
for (const [dimensionName, selector] of Object.entries(selectors)) {
|
|
345
|
+
if (selector.kind === "index") {
|
|
346
|
+
output[dimensionName] = { selected: selector.index, type: "index" };
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (selector.kind === "isoTime") {
|
|
350
|
+
output[dimensionName] = {
|
|
351
|
+
selected: selector.value ?? selector.iso,
|
|
352
|
+
type: "value"
|
|
353
|
+
};
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
output[dimensionName] = { selected: selector.value, type: "value" };
|
|
357
|
+
}
|
|
358
|
+
return output;
|
|
359
|
+
}
|
|
360
|
+
function defaultSelectorForDimension(dimension) {
|
|
361
|
+
const firstValue = dimension.coordinates?.[0];
|
|
362
|
+
if (firstValue !== void 0) {
|
|
363
|
+
return {
|
|
364
|
+
kind: "coordinate",
|
|
365
|
+
value: firstValue instanceof Date ? firstValue.toISOString() : firstValue
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
return { kind: "index", index: 0 };
|
|
369
|
+
}
|
|
370
|
+
function normalizeCoordinateSelector(dimension, value, sourceId) {
|
|
371
|
+
if (dimension.coordinates && !dimension.coordinates.some((coordinate) => sameCoordinate(coordinate, value))) {
|
|
372
|
+
throw new GridError({
|
|
373
|
+
code: "UNSUPPORTED_SELECTOR",
|
|
374
|
+
message: `Selector value "${String(value)}" does not match coordinates for dimension "${dimension.name}".`,
|
|
375
|
+
dimension: dimension.name,
|
|
376
|
+
sourceId
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return { kind: "coordinate", value };
|
|
380
|
+
}
|
|
381
|
+
function assertIndexInRange(dimension, index, sourceId) {
|
|
382
|
+
if (!Number.isInteger(index) || index < 0 || index >= dimension.size) {
|
|
383
|
+
throw new GridError({
|
|
384
|
+
code: "UNSUPPORTED_SELECTOR",
|
|
385
|
+
message: `Index selector ${index} is outside dimension "${dimension.name}" size ${dimension.size}.`,
|
|
386
|
+
dimension: dimension.name,
|
|
387
|
+
sourceId
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function findCoordinateForIso(dimension, iso) {
|
|
392
|
+
return dimension.coordinates?.map((coordinate) => decodeTimeCoordinate(coordinate, dimension.units)).find((coordinate) => coordinate.iso === iso || coordinate.value === iso)?.value;
|
|
393
|
+
}
|
|
394
|
+
function decodeTimeCoordinate(coordinate, units) {
|
|
395
|
+
if (coordinate instanceof Date) {
|
|
396
|
+
return { value: coordinate.toISOString(), iso: coordinate.toISOString() };
|
|
397
|
+
}
|
|
398
|
+
if (typeof coordinate === "string") {
|
|
399
|
+
const date = new Date(coordinate);
|
|
400
|
+
return Number.isNaN(date.getTime()) ? { value: coordinate } : { value: coordinate, iso: date.toISOString() };
|
|
401
|
+
}
|
|
402
|
+
const cfTime = decodeCfTime(coordinate, units);
|
|
403
|
+
return cfTime ? { value: coordinate, iso: cfTime } : { value: coordinate };
|
|
404
|
+
}
|
|
405
|
+
function decodeCfTime(value, units) {
|
|
406
|
+
if (!units) {
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
const match = /^(seconds|minutes|hours|days) since ([0-9]{4}-[0-9]{2}-[0-9]{2})(?:[ T]([0-9:.Z+-]+))?/i.exec(
|
|
410
|
+
units
|
|
411
|
+
);
|
|
412
|
+
if (!match) {
|
|
413
|
+
return void 0;
|
|
414
|
+
}
|
|
415
|
+
const unit = match[1]?.toLowerCase();
|
|
416
|
+
const date = match[2];
|
|
417
|
+
const time = match[3] ?? "00:00:00Z";
|
|
418
|
+
const origin = /* @__PURE__ */ new Date(`${date}T${time.replace(/Z?$/, "Z")}`);
|
|
419
|
+
if (Number.isNaN(origin.getTime())) {
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
const multiplier = unit === "seconds" ? 1e3 : unit === "minutes" ? 6e4 : unit === "hours" ? 36e5 : 864e5;
|
|
423
|
+
return new Date(origin.getTime() + value * multiplier).toISOString();
|
|
424
|
+
}
|
|
425
|
+
function sameCoordinate(left, right) {
|
|
426
|
+
const normalizedLeft = left instanceof Date ? left.toISOString() : left;
|
|
427
|
+
return normalizedLeft === right || String(normalizedLeft) === String(right);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/renderers/maplibre.ts
|
|
431
|
+
function createMapLibreGridLayer(options) {
|
|
432
|
+
return new MapLibreGridLayerController(options);
|
|
433
|
+
}
|
|
434
|
+
function buildZarrLayerOptions(options) {
|
|
435
|
+
assertValidGridDataSource(options.source, {
|
|
436
|
+
variable: options.variable,
|
|
437
|
+
requireRenderable: true
|
|
438
|
+
});
|
|
439
|
+
const variable = options.source.variables.find(
|
|
440
|
+
(candidate) => candidate.name === options.variable || candidate.path === options.variable
|
|
441
|
+
);
|
|
442
|
+
if (!variable) {
|
|
443
|
+
throw new GridError({
|
|
444
|
+
code: "MISSING_VARIABLE",
|
|
445
|
+
message: `Variable "${options.variable}" is not present in the grid source.`,
|
|
446
|
+
variable: options.variable,
|
|
447
|
+
sourceId: options.source.id
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const spatialDimensions = inferSpatialDimensions(options.source, variable.name);
|
|
451
|
+
if (!spatialDimensions) {
|
|
452
|
+
throw new GridError({
|
|
453
|
+
code: "MISSING_SPATIAL_DIMENSIONS",
|
|
454
|
+
message: `Variable "${variable.name}" needs spatial dimensions before rendering.`,
|
|
455
|
+
variable: variable.name,
|
|
456
|
+
sourceId: options.source.id
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
const colorOptions = colorScaleToRendererOptions(options.colorScale);
|
|
460
|
+
const rendererVariable = variable.path ?? variable.name;
|
|
461
|
+
const normalizedSelectors = normalizeSelectors(options.source, variable.name, options.selectors);
|
|
462
|
+
const rendererSelectors = omitSpatialSelectors(normalizedSelectors, spatialDimensions);
|
|
463
|
+
const yDimension = options.source.dimensions.find(
|
|
464
|
+
(dimension) => dimension.name === spatialDimensions.y
|
|
465
|
+
);
|
|
466
|
+
const shaderOptions = buildColorScaleShaderOptions(options.colorScale, rendererVariable);
|
|
467
|
+
return {
|
|
468
|
+
id: options.layerId ?? `zarr-map-${options.source.id ?? variable.name}`,
|
|
469
|
+
source: options.source.source,
|
|
470
|
+
store: options.source.store,
|
|
471
|
+
variable: rendererVariable,
|
|
472
|
+
selector: toRendererSelectors(rendererSelectors),
|
|
473
|
+
spatialDimensions: { lon: spatialDimensions.x, lat: spatialDimensions.y },
|
|
474
|
+
zarrVersion: options.source.zarrVersion,
|
|
475
|
+
crs: options.source.crs,
|
|
476
|
+
proj4: options.source.proj4,
|
|
477
|
+
bounds: options.source.bounds,
|
|
478
|
+
fillValue: variable.fillValue,
|
|
479
|
+
colormap: colorOptions?.colormap,
|
|
480
|
+
clim: colorOptions?.clim,
|
|
481
|
+
opacity: options.opacity,
|
|
482
|
+
latIsAscending: yDimension?.ascending,
|
|
483
|
+
customFrag: shaderOptions?.customFrag,
|
|
484
|
+
uniforms: shaderOptions?.uniforms,
|
|
485
|
+
onLoad: () => {
|
|
486
|
+
options.onLoadingChange?.(false);
|
|
487
|
+
options.onLoadingStateChange?.(completeLoadingState());
|
|
488
|
+
},
|
|
489
|
+
onLoadingStateChange: (state) => {
|
|
490
|
+
const loadingState = normalizeLoadingState(state);
|
|
491
|
+
options.onLoadingChange?.(loadingState.loading);
|
|
492
|
+
options.onLoadingStateChange?.(loadingState);
|
|
493
|
+
},
|
|
494
|
+
onError: (error) => options.onError?.(
|
|
495
|
+
new GridError({
|
|
496
|
+
code: "CHUNK_LOAD_FAILED",
|
|
497
|
+
message: "Zarr renderer reported a metadata or chunk loading error.",
|
|
498
|
+
cause: error,
|
|
499
|
+
sourceId: options.source.id,
|
|
500
|
+
variable: options.variable
|
|
501
|
+
})
|
|
502
|
+
)
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function buildColorScaleShaderOptions(colorScale, rendererVariable) {
|
|
506
|
+
const transparentPalette = transparentPaletteKind(colorScale);
|
|
507
|
+
if (!transparentPalette || !isValidGlslIdentifier(rendererVariable)) {
|
|
508
|
+
return void 0;
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
customFrag: transparentPalette === "vegetation" ? vegetationOverlayShader(rendererVariable) : transparentPalette === "magma" ? magmaTransparentLowerBoundShader(rendererVariable) : precipitationOverlayShader(rendererVariable),
|
|
512
|
+
uniforms: {}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function precipitationOverlayShader(rendererVariable) {
|
|
516
|
+
return `
|
|
517
|
+
if (${rendererVariable} <= 0.0) {
|
|
518
|
+
discard;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
float rescaled = clamp((${rendererVariable} - clim.x) / (clim.y - clim.x), 0.0, 1.0);
|
|
522
|
+
vec4 c = texture(colormap, vec2(rescaled, 0.5));
|
|
523
|
+
float precipAlpha = opacity * mix(0.22, 1.0, rescaled);
|
|
524
|
+
fragColor = vec4(c.rgb, precipAlpha);
|
|
525
|
+
fragColor.rgb *= fragColor.a;
|
|
526
|
+
`;
|
|
527
|
+
}
|
|
528
|
+
function vegetationOverlayShader(rendererVariable) {
|
|
529
|
+
return `
|
|
530
|
+
float vegetationRange = clim.y - clim.x;
|
|
531
|
+
float noVegetationCutoff = clim.x + vegetationRange * 0.04;
|
|
532
|
+
if (${rendererVariable} <= noVegetationCutoff) {
|
|
533
|
+
discard;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
float rescaled = clamp((${rendererVariable} - clim.x) / vegetationRange, 0.0, 1.0);
|
|
537
|
+
vec4 c = texture(colormap, vec2(rescaled, 0.5));
|
|
538
|
+
float vegetationAlpha = opacity * mix(0.14, 1.0, smoothstep(0.04, 0.85, rescaled));
|
|
539
|
+
fragColor = vec4(c.rgb, vegetationAlpha);
|
|
540
|
+
fragColor.rgb *= fragColor.a;
|
|
541
|
+
`;
|
|
542
|
+
}
|
|
543
|
+
function magmaTransparentLowerBoundShader(rendererVariable) {
|
|
544
|
+
return `
|
|
545
|
+
float rescaled = clamp((${rendererVariable} - clim.x) / (clim.y - clim.x), 0.0, 1.0);
|
|
546
|
+
vec4 c = texture(colormap, vec2(rescaled, 0.5));
|
|
547
|
+
float magmaAlpha = opacity * smoothstep(0.0, 0.2, rescaled);
|
|
548
|
+
fragColor = vec4(c.rgb, magmaAlpha);
|
|
549
|
+
fragColor.rgb *= fragColor.a;
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
function transparentPaletteKind(colorScale) {
|
|
553
|
+
if (typeof colorScale?.palette !== "string") {
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
const palette = colorScale.palette.toLowerCase();
|
|
557
|
+
if (palette === "magma" || palette === "precipitation" || palette === "vegetation") {
|
|
558
|
+
return palette;
|
|
559
|
+
}
|
|
560
|
+
return void 0;
|
|
561
|
+
}
|
|
562
|
+
function isValidGlslIdentifier(value) {
|
|
563
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
|
|
564
|
+
}
|
|
565
|
+
function colorScaleShaderSignature(options) {
|
|
566
|
+
const variable = options.source.variables.find(
|
|
567
|
+
(candidate) => candidate.name === options.variable || candidate.path === options.variable
|
|
568
|
+
);
|
|
569
|
+
const rendererVariable = variable?.path ?? variable?.name ?? options.variable;
|
|
570
|
+
return buildColorScaleShaderOptions(options.colorScale, rendererVariable)?.customFrag ?? "";
|
|
571
|
+
}
|
|
572
|
+
var MapLibreGridLayerController = class {
|
|
573
|
+
id;
|
|
574
|
+
ownsMap;
|
|
575
|
+
options;
|
|
576
|
+
map;
|
|
577
|
+
layer;
|
|
578
|
+
removed = false;
|
|
579
|
+
constructor(options) {
|
|
580
|
+
this.id = options.layerId ?? `zarr-map-${options.source.id ?? options.variable}`;
|
|
581
|
+
this.options = { ...options, layerId: this.id };
|
|
582
|
+
this.map = options.map;
|
|
583
|
+
this.ownsMap = !options.map;
|
|
584
|
+
}
|
|
585
|
+
async mount() {
|
|
586
|
+
if (this.layer || this.removed) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
this.options.onLoadingChange?.(true);
|
|
590
|
+
this.options.onLoadingStateChange?.({
|
|
591
|
+
loading: true,
|
|
592
|
+
metadata: false,
|
|
593
|
+
chunks: false,
|
|
594
|
+
percent: 5,
|
|
595
|
+
stage: "map"
|
|
596
|
+
});
|
|
597
|
+
try {
|
|
598
|
+
this.map ??= await this.createMap();
|
|
599
|
+
await this.whenMapReady(this.map);
|
|
600
|
+
this.options.onLoadingStateChange?.({
|
|
601
|
+
loading: true,
|
|
602
|
+
metadata: false,
|
|
603
|
+
chunks: false,
|
|
604
|
+
percent: 25,
|
|
605
|
+
stage: "layer"
|
|
606
|
+
});
|
|
607
|
+
this.layer = await (this.options.layerFactory ?? createDefaultZarrLayer)(
|
|
608
|
+
buildZarrLayerOptions(this.options)
|
|
609
|
+
);
|
|
610
|
+
this.map.addLayer(this.layer, this.options.beforeId);
|
|
611
|
+
this.options.onLoadingStateChange?.({
|
|
612
|
+
loading: true,
|
|
613
|
+
metadata: false,
|
|
614
|
+
chunks: true,
|
|
615
|
+
percent: 65,
|
|
616
|
+
stage: "chunks"
|
|
617
|
+
});
|
|
618
|
+
} catch (error) {
|
|
619
|
+
this.options.onLoadingChange?.(false);
|
|
620
|
+
this.handleError("RENDERER_SETUP_FAILED", "Failed to create the MapLibre Zarr layer.", error);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async update(update) {
|
|
624
|
+
const nextOptions = {
|
|
625
|
+
...this.options,
|
|
626
|
+
...update
|
|
627
|
+
};
|
|
628
|
+
if (!this.layer) {
|
|
629
|
+
this.options = nextOptions;
|
|
630
|
+
await this.mount();
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
if (update.source && update.source !== this.options.source) {
|
|
635
|
+
await this.replaceLayer(nextOptions);
|
|
636
|
+
this.options = nextOptions;
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (colorScaleShaderSignature(nextOptions) !== colorScaleShaderSignature(this.options)) {
|
|
640
|
+
await this.replaceLayer(nextOptions);
|
|
641
|
+
this.options = nextOptions;
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
this.options = nextOptions;
|
|
645
|
+
if (update.colorScale) {
|
|
646
|
+
const colorOptions = colorScaleToRendererOptions(update.colorScale);
|
|
647
|
+
this.layer.setColormap?.(colorOptions?.colormap ?? []);
|
|
648
|
+
this.layer.setClim?.(colorOptions?.clim ?? update.colorScale.domain);
|
|
649
|
+
}
|
|
650
|
+
if (typeof update.opacity === "number") {
|
|
651
|
+
this.layer.setOpacity?.(update.opacity);
|
|
652
|
+
}
|
|
653
|
+
if (update.variable) {
|
|
654
|
+
this.layer.setVariable?.(update.variable);
|
|
655
|
+
}
|
|
656
|
+
if (update.selectors || update.variable || update.source) {
|
|
657
|
+
const variable = update.variable ?? this.options.variable;
|
|
658
|
+
const source = update.source ?? this.options.source;
|
|
659
|
+
const spatialDimensions = inferSpatialDimensions(source, variable);
|
|
660
|
+
const normalizedSelectors = normalizeSelectors(source, variable, this.options.selectors);
|
|
661
|
+
const rendererSelectors = spatialDimensions ? omitSpatialSelectors(normalizedSelectors, spatialDimensions) : normalizedSelectors;
|
|
662
|
+
this.layer.setSelector?.(toRendererSelectors(rendererSelectors));
|
|
663
|
+
}
|
|
664
|
+
} catch (error) {
|
|
665
|
+
this.handleError("RENDERER_SETUP_FAILED", "Failed to update the MapLibre Zarr layer.", error);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async replaceLayer(nextOptions) {
|
|
669
|
+
if (!this.map) {
|
|
670
|
+
this.options = nextOptions;
|
|
671
|
+
await this.mount();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
this.options.onLoadingChange?.(true);
|
|
675
|
+
this.options.onLoadingStateChange?.({
|
|
676
|
+
loading: true,
|
|
677
|
+
metadata: false,
|
|
678
|
+
chunks: false,
|
|
679
|
+
percent: 5,
|
|
680
|
+
stage: "layer"
|
|
681
|
+
});
|
|
682
|
+
const previousLayer = this.layer;
|
|
683
|
+
if (previousLayer && this.map.getLayer(previousLayer.id)) {
|
|
684
|
+
this.map.removeLayer(previousLayer.id);
|
|
685
|
+
}
|
|
686
|
+
if (previousLayer?.id && this.map.getSource?.(previousLayer.id)) {
|
|
687
|
+
this.map.removeSource?.(previousLayer.id);
|
|
688
|
+
}
|
|
689
|
+
this.layer = void 0;
|
|
690
|
+
try {
|
|
691
|
+
this.options.onLoadingStateChange?.({
|
|
692
|
+
loading: true,
|
|
693
|
+
metadata: false,
|
|
694
|
+
chunks: false,
|
|
695
|
+
percent: 35,
|
|
696
|
+
stage: "layer"
|
|
697
|
+
});
|
|
698
|
+
const nextLayer = await (nextOptions.layerFactory ?? createDefaultZarrLayer)(
|
|
699
|
+
buildZarrLayerOptions(nextOptions)
|
|
700
|
+
);
|
|
701
|
+
this.layer = nextLayer;
|
|
702
|
+
this.map.addLayer(nextLayer, nextOptions.beforeId);
|
|
703
|
+
this.options.onLoadingStateChange?.({
|
|
704
|
+
loading: true,
|
|
705
|
+
metadata: false,
|
|
706
|
+
chunks: true,
|
|
707
|
+
percent: 65,
|
|
708
|
+
stage: "chunks"
|
|
709
|
+
});
|
|
710
|
+
} catch (error) {
|
|
711
|
+
this.options.onLoadingChange?.(false);
|
|
712
|
+
this.handleError("RENDERER_SETUP_FAILED", "Failed to update the MapLibre Zarr layer.", error);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
remove() {
|
|
716
|
+
if (this.removed) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
this.removed = true;
|
|
720
|
+
if (this.layer && this.map?.getLayer(this.layer.id)) {
|
|
721
|
+
this.map.removeLayer(this.layer.id);
|
|
722
|
+
}
|
|
723
|
+
if (this.layer?.id && this.map?.getSource?.(this.layer.id)) {
|
|
724
|
+
this.map.removeSource?.(this.layer.id);
|
|
725
|
+
}
|
|
726
|
+
if (this.ownsMap) {
|
|
727
|
+
this.map?.remove?.();
|
|
728
|
+
}
|
|
729
|
+
this.layer = void 0;
|
|
730
|
+
this.map = void 0;
|
|
731
|
+
}
|
|
732
|
+
async query(geometry) {
|
|
733
|
+
if (!this.layer?.queryData) {
|
|
734
|
+
throw new GridError({
|
|
735
|
+
code: "UNSUPPORTED_GEOMETRY",
|
|
736
|
+
message: "The active renderer layer does not expose queryData."
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
return this.layer.queryData(geometry);
|
|
740
|
+
}
|
|
741
|
+
getMap() {
|
|
742
|
+
return this.map;
|
|
743
|
+
}
|
|
744
|
+
getLayer() {
|
|
745
|
+
return this.layer;
|
|
746
|
+
}
|
|
747
|
+
async createMap() {
|
|
748
|
+
if (!this.options.mapConfig) {
|
|
749
|
+
throw new GridError({
|
|
750
|
+
code: "RENDERER_SETUP_FAILED",
|
|
751
|
+
message: "Provide either an existing map instance or mapConfig to create one."
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
if (this.options.mapFactory) {
|
|
755
|
+
return this.options.mapFactory(this.options.mapConfig);
|
|
756
|
+
}
|
|
757
|
+
const maplibre = await import('maplibre-gl');
|
|
758
|
+
return new maplibre.Map(
|
|
759
|
+
this.options.mapConfig
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
async whenMapReady(map) {
|
|
763
|
+
if (this.isStyleReady(map) || !map.once) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
if (map.isStyleLoaded) {
|
|
767
|
+
await new Promise((resolve) => {
|
|
768
|
+
let resolved = false;
|
|
769
|
+
const resolveWhenStyleReady = () => {
|
|
770
|
+
if (resolved) {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (this.isStyleReady(map)) {
|
|
774
|
+
resolved = true;
|
|
775
|
+
resolve();
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
map.once?.("styledata", resolveWhenStyleReady);
|
|
779
|
+
};
|
|
780
|
+
map.once?.("styledata", resolveWhenStyleReady);
|
|
781
|
+
map.once?.("load", resolveWhenStyleReady);
|
|
782
|
+
});
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (!map.loaded || map.loaded()) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
await new Promise((resolve) => {
|
|
789
|
+
map.once?.("load", () => resolve());
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
isStyleReady(map) {
|
|
793
|
+
return map.isStyleLoaded?.() ?? map.loaded?.() ?? true;
|
|
794
|
+
}
|
|
795
|
+
handleError(code, message, cause) {
|
|
796
|
+
const gridError = cause instanceof GridError ? cause : new GridError({
|
|
797
|
+
code,
|
|
798
|
+
message: errorMessageWithCause(message, cause),
|
|
799
|
+
cause,
|
|
800
|
+
sourceId: this.options.source.id,
|
|
801
|
+
variable: this.options.variable
|
|
802
|
+
});
|
|
803
|
+
this.options.onError?.(gridError);
|
|
804
|
+
throw gridError;
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
function errorMessageWithCause(message, cause) {
|
|
808
|
+
if (cause instanceof Error && cause.message) {
|
|
809
|
+
return `${message} ${cause.message}`;
|
|
810
|
+
}
|
|
811
|
+
return message;
|
|
812
|
+
}
|
|
813
|
+
function omitSpatialSelectors(selectors, spatialDimensions) {
|
|
814
|
+
return Object.fromEntries(
|
|
815
|
+
Object.entries(selectors).filter(([dimensionName]) => {
|
|
816
|
+
return dimensionName !== spatialDimensions.x && dimensionName !== spatialDimensions.y;
|
|
817
|
+
})
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
function normalizeLoadingState(state) {
|
|
821
|
+
if (typeof state === "boolean") {
|
|
822
|
+
return {
|
|
823
|
+
loading: state,
|
|
824
|
+
metadata: state,
|
|
825
|
+
chunks: false,
|
|
826
|
+
percent: state ? 35 : 100,
|
|
827
|
+
stage: state ? "metadata" : "complete"
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
const loading = Boolean(state.loading ?? state.metadata ?? state.chunks);
|
|
831
|
+
const metadata = Boolean(state.metadata);
|
|
832
|
+
const chunks = Boolean(state.chunks);
|
|
833
|
+
return {
|
|
834
|
+
loading,
|
|
835
|
+
metadata,
|
|
836
|
+
chunks,
|
|
837
|
+
stage: state.stage ?? loadingStage(loading, metadata, chunks),
|
|
838
|
+
percent: typeof state.percent === "number" ? Math.max(0, Math.min(100, state.percent)) : loading ? loadingPercent(metadata, chunks) : 100
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
function loadingStage(loading, metadata, chunks) {
|
|
842
|
+
if (!loading) {
|
|
843
|
+
return "complete";
|
|
844
|
+
}
|
|
845
|
+
if (chunks) {
|
|
846
|
+
return "chunks";
|
|
847
|
+
}
|
|
848
|
+
if (metadata) {
|
|
849
|
+
return "metadata";
|
|
850
|
+
}
|
|
851
|
+
return "layer";
|
|
852
|
+
}
|
|
853
|
+
function loadingPercent(metadata, chunks) {
|
|
854
|
+
if (chunks) {
|
|
855
|
+
return 85;
|
|
856
|
+
}
|
|
857
|
+
if (metadata) {
|
|
858
|
+
return 45;
|
|
859
|
+
}
|
|
860
|
+
return 65;
|
|
861
|
+
}
|
|
862
|
+
function completeLoadingState() {
|
|
863
|
+
return {
|
|
864
|
+
loading: false,
|
|
865
|
+
metadata: false,
|
|
866
|
+
chunks: false,
|
|
867
|
+
percent: 100,
|
|
868
|
+
stage: "complete"
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
var zarrLayerConstructorPromise;
|
|
872
|
+
async function preloadDefaultZarrLayer() {
|
|
873
|
+
await loadDefaultZarrLayerConstructor();
|
|
874
|
+
}
|
|
875
|
+
async function createDefaultZarrLayer(options) {
|
|
876
|
+
const ZarrLayer = await loadDefaultZarrLayerConstructor();
|
|
877
|
+
return new ZarrLayer(options);
|
|
878
|
+
}
|
|
879
|
+
async function loadDefaultZarrLayerConstructor() {
|
|
880
|
+
zarrLayerConstructorPromise ??= import('@carbonplan/zarr-layer').then((module) => {
|
|
881
|
+
const zarrLayerModule = module;
|
|
882
|
+
const ZarrLayer = zarrLayerModule.ZarrLayer ?? zarrLayerModule.default;
|
|
883
|
+
if (!ZarrLayer) {
|
|
884
|
+
throw new GridError({
|
|
885
|
+
code: "RENDERER_SETUP_FAILED",
|
|
886
|
+
message: "@carbonplan/zarr-layer did not export ZarrLayer."
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
return ZarrLayer;
|
|
890
|
+
}).catch((error) => {
|
|
891
|
+
zarrLayerConstructorPromise = void 0;
|
|
892
|
+
throw error;
|
|
893
|
+
});
|
|
894
|
+
return zarrLayerConstructorPromise;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
export { buildZarrLayerOptions, createMapLibreGridLayer, preloadDefaultZarrLayer };
|
|
898
|
+
//# sourceMappingURL=index.js.map
|
|
899
|
+
//# sourceMappingURL=index.js.map
|