teda-weather 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/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # teda-weather
2
+
3
+ Standalone TypeScript library scaffold for the teda-weather runtime adapters.
4
+
5
+ Compatibility baseline fixtures live under `fixtures/compat/` and capture acceptance-ready public contract payloads.
6
+
7
+ ## Migration from `visual-sdk`
8
+
9
+ Use `teda-weather` as the standalone replacement for the old `visual-sdk` runtime entry surface.
10
+
11
+ - `Client` is now a shared, non-instantiable base class only. Do not call `new Client()`.
12
+ - Real integrations must use the engine-specific entrypoints:
13
+ - `import { MapboxClient } from "teda-weather/mapbox"`
14
+ - `import { MapLibreClient } from "teda-weather/maplibre"`
15
+ - Unsupported legacy adapters and facade-style compatibility shims are not included in the first phase.
@@ -0,0 +1,534 @@
1
+ import {
2
+ Client,
3
+ ColormapLayer,
4
+ ColormapTileLayer,
5
+ FeatherLayer,
6
+ ParticleAnimationLayer,
7
+ createCanvasOverlay,
8
+ mountCanvasOverlay,
9
+ syncCanvasLikeData,
10
+ syncCanvasOverlaySize,
11
+ unmountCanvasOverlay
12
+ } from "./chunk-Y3HFQZGE.js";
13
+
14
+ // src/adapters/maplibre/maplibre-client.ts
15
+ var mountIdSequence = 0;
16
+ var MapLibreClient = class extends Client {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.defaultOptionsEnabled = false;
20
+ this.cleanupHandles = [];
21
+ this.destroyed = false;
22
+ }
23
+ useDefault(value) {
24
+ this.assertNotDestroyed();
25
+ if (typeof value === "boolean") {
26
+ this.defaultOptionsEnabled = value;
27
+ }
28
+ if (typeof value === "undefined" || this.defaultOptionsEnabled) {
29
+ this.ensureDefaultLayers();
30
+ }
31
+ return this.defaultOptionsEnabled;
32
+ }
33
+ setColorMapTileOption(option) {
34
+ this.assertNotDestroyed();
35
+ this.ensureDefaultLayers();
36
+ this.colorMapTileLayer?.setOption(option);
37
+ }
38
+ clearColorMapTile() {
39
+ this.assertNotDestroyed();
40
+ this.ensureDefaultLayers();
41
+ const layer = this.colorMapTileLayer;
42
+ const canvas = layer?.overlay.canvas;
43
+ if (!canvas) {
44
+ return;
45
+ }
46
+ layer?.source?.clear?.();
47
+ canvas.data = new Uint8ClampedArray(canvas.width * canvas.height * 4);
48
+ syncCanvasLikeData(canvas);
49
+ delete layer?.source;
50
+ }
51
+ getColorMapTileCanvas() {
52
+ this.assertNotDestroyed();
53
+ this.ensureDefaultLayers();
54
+ return this.colorMapTileLayer?.overlay.canvas;
55
+ }
56
+ setColorMapOption(option) {
57
+ this.assertNotDestroyed();
58
+ this.ensureDefaultLayers();
59
+ this.colorMapLayer?.setOption(option);
60
+ }
61
+ getColorMapCanvas() {
62
+ this.assertNotDestroyed();
63
+ this.ensureDefaultLayers();
64
+ return this.colorMapLayer?.overlay.canvas;
65
+ }
66
+ decodeColorMapTile(pixel, extent, worldZoom) {
67
+ this.assertNotDestroyed();
68
+ this.ensureDefaultLayers();
69
+ if (!this.colorMapTileLayer?.source) {
70
+ return null;
71
+ }
72
+ return this.colorMapTileLayer?.decodeFromCanvasPixel(
73
+ pixel,
74
+ extent,
75
+ worldZoom
76
+ );
77
+ }
78
+ decodeColorMapTileValue(pixel, extent, worldZoom) {
79
+ return this.decodeColorMapTile(pixel, extent, worldZoom);
80
+ }
81
+ decodeColorMapTileValueByLngLat(lngLat, worldZoom) {
82
+ this.assertNotDestroyed();
83
+ this.ensureDefaultLayers();
84
+ if (!this.colorMapTileLayer?.source) {
85
+ return null;
86
+ }
87
+ return this.colorMapTileLayer?.source?.decodeTextByLngLat(
88
+ lngLat,
89
+ worldZoom
90
+ );
91
+ }
92
+ initColorMapTileLayer(map, mapDiv, layerConfig) {
93
+ this.assertNotDestroyed();
94
+ if (this.tileMount) {
95
+ this.ensureMountedResources(this.tileMount, layerConfig);
96
+ return;
97
+ }
98
+ const mountedLayer = this.mountTileLayer(map, mapDiv, layerConfig);
99
+ this.colorMapTileLayer = mountedLayer.layer;
100
+ this.tileMount = mountedLayer;
101
+ }
102
+ initColorMapLayer(map, mapDiv, layerConfig) {
103
+ this.assertNotDestroyed();
104
+ if (this.colorMount) {
105
+ this.ensureMountedResources(this.colorMount, layerConfig);
106
+ return;
107
+ }
108
+ const mountedLayer = this.mountColorLayer(map, mapDiv, layerConfig);
109
+ this.colorMapLayer = mountedLayer.layer;
110
+ this.colorMount = mountedLayer;
111
+ }
112
+ initParticleLayer(map, mapDiv, layerConfig) {
113
+ this.assertNotDestroyed();
114
+ if (this.particleMount) {
115
+ this.ensureMountedResources(this.particleMount, layerConfig);
116
+ return;
117
+ }
118
+ const mountedLayer = this.mountParticleLayer(map, mapDiv, layerConfig);
119
+ this.particleLayer = mountedLayer.layer;
120
+ this.particleMount = mountedLayer;
121
+ }
122
+ initFeatherLayer(map, mapDiv, layerConfig) {
123
+ this.assertNotDestroyed();
124
+ if (this.featherMount) {
125
+ this.ensureMountedResources(this.featherMount, layerConfig);
126
+ return;
127
+ }
128
+ const mountedLayer = this.mountFeatherLayer(map, mapDiv, layerConfig);
129
+ this.featherLayer = mountedLayer.layer;
130
+ this.featherMount = mountedLayer;
131
+ }
132
+ renderColorMapTileFunction(map = this.tileMount?.map, mapDiv = this.tileMount?.container, weatherOptionOrOnerror, layerConfig) {
133
+ this.assertNotDestroyed();
134
+ const mountedLayer = this.resolveTileMount(map, mapDiv, layerConfig);
135
+ if (!mountedLayer) {
136
+ return;
137
+ }
138
+ this.colorMapTileLayer = mountedLayer.layer;
139
+ const weatherOption = normalizeWeatherOption(
140
+ weatherOptionOrOnerror
141
+ );
142
+ if (weatherOption) {
143
+ mountedLayer.layer.setOption(weatherOption);
144
+ }
145
+ syncCanvasOverlaySize(mountedLayer.overlay, mountedLayer.overlay.container);
146
+ let renderedCanvas = mountedLayer.overlay.canvas;
147
+ if (mountedLayer.layer.source) {
148
+ renderedCanvas = this.renderTileCanvas(mountedLayer);
149
+ copyCanvasLike(renderedCanvas, mountedLayer.overlay);
150
+ }
151
+ this.syncMountedSource(mountedLayer);
152
+ return mountedLayer.overlay.canvas;
153
+ }
154
+ renderColorMapFunction(map = this.colorMount?.map, mapDiv = this.colorMount?.container, weatherOptionOrOnerror, layerConfig) {
155
+ this.assertNotDestroyed();
156
+ const mountedLayer = this.resolveColorMount(map, mapDiv, layerConfig);
157
+ if (!mountedLayer) {
158
+ return;
159
+ }
160
+ this.colorMapLayer = mountedLayer.layer;
161
+ const weatherOption = normalizeWeatherOption(
162
+ weatherOptionOrOnerror
163
+ );
164
+ if (weatherOption) {
165
+ mountedLayer.layer.setOption(weatherOption);
166
+ }
167
+ syncCanvasOverlaySize(mountedLayer.overlay, mountedLayer.overlay.container);
168
+ let renderedCanvas = mountedLayer.overlay.canvas;
169
+ if (mountedLayer.layer.source) {
170
+ renderedCanvas = this.renderColorCanvas(mountedLayer);
171
+ copyCanvasLike(renderedCanvas, mountedLayer.overlay);
172
+ }
173
+ this.syncMountedSource(mountedLayer);
174
+ return mountedLayer.overlay.canvas;
175
+ }
176
+ renderParticleFunction(map = this.particleMount?.map, mapDiv = this.particleMount?.container, weatherOptionOrOnerror, layerConfig) {
177
+ this.assertNotDestroyed();
178
+ const mountedLayer = this.resolveParticleMount(map, mapDiv, layerConfig);
179
+ if (!mountedLayer) {
180
+ return;
181
+ }
182
+ this.particleLayer = mountedLayer.layer;
183
+ const weatherOption = normalizeWeatherOption(
184
+ weatherOptionOrOnerror
185
+ );
186
+ if (weatherOption) {
187
+ mountedLayer.layer.setOption(weatherOption);
188
+ }
189
+ syncCanvasOverlaySize(mountedLayer.overlay, mountedLayer.overlay.container);
190
+ this.setParticleView(mountedLayer);
191
+ mountedLayer.layer.start();
192
+ this.syncMountedSource(mountedLayer);
193
+ return mountedLayer.overlay.canvas;
194
+ }
195
+ renderFeatherFunction(map = this.featherMount?.map, mapDiv = this.featherMount?.container, weatherOptionOrOnerror, layerConfig) {
196
+ this.assertNotDestroyed();
197
+ const mountedLayer = this.resolveFeatherMount(map, mapDiv, layerConfig);
198
+ if (!mountedLayer) {
199
+ return;
200
+ }
201
+ this.featherLayer = mountedLayer.layer;
202
+ const weatherOption = normalizeWeatherOption(
203
+ weatherOptionOrOnerror
204
+ );
205
+ if (weatherOption) {
206
+ mountedLayer.layer.setOption(weatherOption);
207
+ }
208
+ syncCanvasOverlaySize(mountedLayer.overlay, mountedLayer.overlay.container);
209
+ this.renderFeatherCanvas(mountedLayer);
210
+ this.syncMountedSource(mountedLayer);
211
+ return mountedLayer.overlay.canvas;
212
+ }
213
+ startParticle(config) {
214
+ this.assertNotDestroyed();
215
+ this.ensureDefaultLayers();
216
+ const mountedLayer = this.particleMount;
217
+ if (mountedLayer) {
218
+ syncCanvasOverlaySize(mountedLayer.overlay, mountedLayer.overlay.container);
219
+ this.setParticleView(mountedLayer);
220
+ const canvas = mountedLayer.layer.start(config);
221
+ this.syncMountedSource(mountedLayer);
222
+ return canvas;
223
+ }
224
+ return this.particleLayer?.start(config);
225
+ }
226
+ clearParticle() {
227
+ this.assertNotDestroyed();
228
+ this.ensureDefaultLayers();
229
+ this.particleLayer?.stop();
230
+ const canvas = this.particleLayer?.overlay.canvas;
231
+ if (!canvas) {
232
+ return;
233
+ }
234
+ canvas.data = new Uint8ClampedArray(canvas.width * canvas.height * 4);
235
+ syncCanvasLikeData(canvas);
236
+ }
237
+ destroy() {
238
+ if (this.destroyed) {
239
+ return;
240
+ }
241
+ this.destroyed = true;
242
+ this.colorMapTileLayer?.source?.clear?.();
243
+ this.colorMapLayer?.source?.clear?.();
244
+ this.featherLayer?.destroy?.();
245
+ this.particleLayer?.destroy?.();
246
+ for (const cleanupHandle of this.cleanupHandles.splice(0)) {
247
+ cleanupHandle();
248
+ }
249
+ this.colorMapTileLayer = void 0;
250
+ this.colorMapLayer = void 0;
251
+ this.featherLayer = void 0;
252
+ this.particleLayer = void 0;
253
+ this.tileMount = void 0;
254
+ this.colorMount = void 0;
255
+ this.featherMount = void 0;
256
+ this.particleMount = void 0;
257
+ }
258
+ assertNotDestroyed() {
259
+ if (this.destroyed) {
260
+ throw new Error("MapLibreClient has been destroyed");
261
+ }
262
+ }
263
+ resolveTileMount(map, mapDiv, layerConfig) {
264
+ if (this.tileMount || !map || !mapDiv) {
265
+ return this.tileMount;
266
+ }
267
+ const mountedLayer = this.mountTileLayer(map, mapDiv, layerConfig);
268
+ this.tileMount = mountedLayer;
269
+ this.colorMapTileLayer = mountedLayer.layer;
270
+ return mountedLayer;
271
+ }
272
+ resolveColorMount(map, mapDiv, layerConfig) {
273
+ if (this.colorMount || !map || !mapDiv) {
274
+ return this.colorMount;
275
+ }
276
+ const mountedLayer = this.mountColorLayer(map, mapDiv, layerConfig);
277
+ this.colorMount = mountedLayer;
278
+ this.colorMapLayer = mountedLayer.layer;
279
+ return mountedLayer;
280
+ }
281
+ resolveParticleMount(map, mapDiv, layerConfig) {
282
+ if (this.particleMount || !map || !mapDiv) {
283
+ return this.particleMount;
284
+ }
285
+ const mountedLayer = this.mountParticleLayer(map, mapDiv, layerConfig);
286
+ this.particleMount = mountedLayer;
287
+ this.particleLayer = mountedLayer.layer;
288
+ return mountedLayer;
289
+ }
290
+ resolveFeatherMount(map, mapDiv, layerConfig) {
291
+ if (this.featherMount || !map || !mapDiv) {
292
+ return this.featherMount;
293
+ }
294
+ const mountedLayer = this.mountFeatherLayer(map, mapDiv, layerConfig);
295
+ this.featherMount = mountedLayer;
296
+ this.featherLayer = mountedLayer.layer;
297
+ return mountedLayer;
298
+ }
299
+ mountTileLayer(map, container, layerConfig) {
300
+ return this.mountLayer({
301
+ map,
302
+ container,
303
+ layer: new ColormapTileLayer({
304
+ sourceDependencies: { retainQueryBuffer: true }
305
+ }),
306
+ sourceIdBase: "teda-weather-color-map-tile-source",
307
+ defaultLayerIdBase: "teda-weather-color-map-tile-layer",
308
+ layerConfig,
309
+ render: () => this.renderColorMapTileFunction()
310
+ });
311
+ }
312
+ mountColorLayer(map, container, layerConfig) {
313
+ return this.mountLayer({
314
+ map,
315
+ container,
316
+ layer: new ColormapLayer(),
317
+ sourceIdBase: "teda-weather-color-map-source",
318
+ defaultLayerIdBase: "teda-weather-color-map-layer",
319
+ layerConfig,
320
+ render: () => this.renderColorMapFunction()
321
+ });
322
+ }
323
+ mountParticleLayer(map, container, layerConfig) {
324
+ return this.mountLayer({
325
+ map,
326
+ container,
327
+ layer: new ParticleAnimationLayer(),
328
+ sourceIdBase: "teda-weather-particle-source",
329
+ defaultLayerIdBase: "teda-weather-particle-layer",
330
+ layerConfig,
331
+ render: () => this.startParticle()
332
+ });
333
+ }
334
+ mountFeatherLayer(map, container, layerConfig) {
335
+ return this.mountLayer({
336
+ map,
337
+ container,
338
+ layer: new FeatherLayer(),
339
+ sourceIdBase: "teda-weather-feather-source",
340
+ defaultLayerIdBase: "teda-weather-feather-layer",
341
+ layerConfig,
342
+ render: () => this.renderFeatherFunction()
343
+ });
344
+ }
345
+ mountLayer(options) {
346
+ const overlay = createCanvasOverlay(options.container);
347
+ const mountedLayer = Object.assign(options.layer, { overlay });
348
+ attachOverlayCanvas(mountedLayer, overlay.canvas);
349
+ const mountId = ++mountIdSequence;
350
+ const sourceId = `${options.sourceIdBase}-${mountId}`;
351
+ const layerId = options.layerConfig?.layerId ?? options.layerConfig?.id ?? `${options.defaultLayerIdBase}-${mountId}`;
352
+ const syncAndRender = () => {
353
+ syncCanvasOverlaySize(overlay, options.container);
354
+ options.render();
355
+ };
356
+ mountCanvasOverlay(overlay, options.container);
357
+ this.ensureSource(options.map, sourceId, overlay);
358
+ this.ensureLayer(options.map, sourceId, layerId, options.layerConfig);
359
+ options.map.on("move", syncAndRender);
360
+ options.map.on("resize", syncAndRender);
361
+ this.cleanupHandles.push(
362
+ () => options.map.off("move", syncAndRender),
363
+ () => options.map.off("resize", syncAndRender),
364
+ () => options.map.removeLayer?.(layerId),
365
+ () => options.map.removeSource?.(sourceId),
366
+ () => unmountCanvasOverlay(overlay)
367
+ );
368
+ return {
369
+ map: options.map,
370
+ overlay,
371
+ container: options.container,
372
+ layer: mountedLayer,
373
+ sourceId,
374
+ layerId
375
+ };
376
+ }
377
+ ensureDefaultLayers() {
378
+ if (!this.colorMapTileLayer) {
379
+ this.colorMapTileLayer = Object.assign(
380
+ new ColormapTileLayer({
381
+ sourceDependencies: { retainQueryBuffer: true }
382
+ }),
383
+ {
384
+ overlay: createCanvasOverlay({ offsetWidth: 1, offsetHeight: 1 })
385
+ }
386
+ );
387
+ }
388
+ if (!this.colorMapLayer) {
389
+ this.colorMapLayer = Object.assign(new ColormapLayer(), {
390
+ overlay: createCanvasOverlay({ offsetWidth: 1, offsetHeight: 1 })
391
+ });
392
+ }
393
+ if (!this.featherLayer) {
394
+ const overlay = createCanvasOverlay({ offsetWidth: 1, offsetHeight: 1 });
395
+ this.featherLayer = Object.assign(new FeatherLayer(), { overlay });
396
+ attachOverlayCanvas(this.featherLayer, overlay.canvas);
397
+ }
398
+ if (!this.particleLayer) {
399
+ const overlay = createCanvasOverlay({ offsetWidth: 1, offsetHeight: 1 });
400
+ this.particleLayer = Object.assign(new ParticleAnimationLayer(), {
401
+ overlay
402
+ });
403
+ attachOverlayCanvas(this.particleLayer, overlay.canvas);
404
+ }
405
+ }
406
+ renderTileCanvas(mountedLayer) {
407
+ const viewState = getMapViewState(mountedLayer.map, mountedLayer.container);
408
+ return mountedLayer.layer.renderExtent(
409
+ viewState.extent,
410
+ viewState.zoom,
411
+ viewState.viewSize,
412
+ viewState.pixelRatio
413
+ );
414
+ }
415
+ renderColorCanvas(mountedLayer) {
416
+ const viewState = getMapViewState(mountedLayer.map, mountedLayer.container);
417
+ return mountedLayer.layer.renderExtent(
418
+ viewState.extent,
419
+ viewState.zoom,
420
+ viewState.viewSize,
421
+ viewState.pixelRatio
422
+ );
423
+ }
424
+ renderFeatherCanvas(mountedLayer) {
425
+ const viewState = getMapViewState(mountedLayer.map, mountedLayer.container);
426
+ return mountedLayer.layer.renderExtent(
427
+ viewState.extent,
428
+ viewState.zoom,
429
+ viewState.viewSize,
430
+ viewState.pixelRatio
431
+ );
432
+ }
433
+ setParticleView(mountedLayer) {
434
+ if (!mountedLayer) {
435
+ return;
436
+ }
437
+ const viewState = getMapViewState(mountedLayer.map, mountedLayer.container);
438
+ mountedLayer.layer.setView(
439
+ viewState.extent,
440
+ viewState.zoom,
441
+ viewState.viewSize
442
+ );
443
+ }
444
+ syncMountedSource(mountedLayer) {
445
+ const coordinates = getCanvasCoordinates(mountedLayer.map);
446
+ const source = this.ensureMountedResources(mountedLayer);
447
+ if (source.setCoordinates) {
448
+ source.setCoordinates(coordinates);
449
+ return;
450
+ }
451
+ source.coordinates = coordinates;
452
+ }
453
+ ensureMountedResources(mountedLayer, layerConfig) {
454
+ this.ensureSource(
455
+ mountedLayer.map,
456
+ mountedLayer.sourceId,
457
+ mountedLayer.overlay
458
+ );
459
+ this.ensureLayer(
460
+ mountedLayer.map,
461
+ mountedLayer.sourceId,
462
+ mountedLayer.layerId,
463
+ layerConfig
464
+ );
465
+ return mountedLayer.map.getSource(mountedLayer.sourceId) ?? {};
466
+ }
467
+ ensureSource(map, sourceId, overlay, coordinates = getCanvasCoordinates(map)) {
468
+ if (map.getSource(sourceId)) {
469
+ return;
470
+ }
471
+ map.addSource(sourceId, {
472
+ type: "canvas",
473
+ canvas: overlay.canvas,
474
+ coordinates,
475
+ animate: true
476
+ });
477
+ }
478
+ ensureLayer(map, sourceId, layerId, layerConfig) {
479
+ if (map.getLayer(layerId)) {
480
+ return;
481
+ }
482
+ map.addLayer(
483
+ {
484
+ id: layerId,
485
+ type: "raster",
486
+ source: sourceId,
487
+ paint: layerConfig?.paint,
488
+ layout: layerConfig?.layout
489
+ },
490
+ layerConfig?.beforeId
491
+ );
492
+ }
493
+ };
494
+ function getCanvasCoordinates(map) {
495
+ const bounds = map.getBounds();
496
+ return [
497
+ [bounds.getWest(), bounds.getNorth()],
498
+ [bounds.getEast(), bounds.getNorth()],
499
+ [bounds.getEast(), bounds.getSouth()],
500
+ [bounds.getWest(), bounds.getSouth()]
501
+ ];
502
+ }
503
+ function getMapViewState(map, container) {
504
+ const bounds = map.getBounds();
505
+ return {
506
+ extent: [
507
+ bounds.getWest(),
508
+ bounds.getSouth(),
509
+ bounds.getEast(),
510
+ bounds.getNorth()
511
+ ],
512
+ zoom: map.getZoom() + 1,
513
+ viewSize: [container.offsetWidth ?? 1, container.offsetHeight ?? 1],
514
+ pixelRatio: typeof globalThis.devicePixelRatio === "number" ? globalThis.devicePixelRatio : 1
515
+ };
516
+ }
517
+ function attachOverlayCanvas(layer, canvas) {
518
+ layer.setCanvas?.(canvas);
519
+ }
520
+ function copyCanvasLike(source, overlay) {
521
+ overlay.width = source.width;
522
+ overlay.height = source.height;
523
+ overlay.canvas.width = source.width;
524
+ overlay.canvas.height = source.height;
525
+ overlay.canvas.data = new Uint8ClampedArray(source.data);
526
+ syncCanvasLikeData(overlay.canvas);
527
+ }
528
+ function normalizeWeatherOption(value) {
529
+ return typeof value === "function" ? void 0 : value;
530
+ }
531
+
532
+ export {
533
+ MapLibreClient
534
+ };