@gustkit/mapbox-web 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SeaPeople
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,25 @@
1
+ # @gustkit/mapbox-web
2
+
3
+ **`GustKitMap`** — the default web weather map. Wraps Mapbox GL JS so hosts do not
4
+ copy layer recipes (ADR-016).
5
+
6
+ Peer: `mapbox-gl`. Data path: `@gustkit/client` `syncFieldSession` (same helper as
7
+ `GustKitOverlay` and `@gustkit/react-native/mapbox`).
8
+
9
+ ```tsx
10
+ import { GustKitMap } from "@gustkit/mapbox-web";
11
+
12
+ <GustKitMap
13
+ accessToken={token}
14
+ source={source}
15
+ product={product}
16
+ timeMs={timeMs}
17
+ layers={{ field: true, particles: true, isobars: true, arrows: false, centers: true }}
18
+ >
19
+ {/* host markers / boats via onMap, or HTML chrome */}
20
+ </GustKitMap>
21
+ ```
22
+
23
+ The component owns the Mapbox map, weather stack (field under labels, coastline,
24
+ particles, isobars, arrows, H/L), and `loadView` on camera idle. Pass token,
25
+ camera, layer toggles, and children. Use `onMap(map)` to add native layers (boats).
@@ -0,0 +1,56 @@
1
+ import { type ReactNode } from "react";
2
+ import { Product, type GustKitSource } from "@gustkit/client";
3
+ export interface GustKitMapLayers {
4
+ field?: boolean;
5
+ particles?: boolean;
6
+ isobars?: boolean;
7
+ arrows?: boolean;
8
+ centers?: boolean;
9
+ }
10
+ export interface GustKitMapHover {
11
+ lon: number;
12
+ lat: number;
13
+ x: number;
14
+ y: number;
15
+ }
16
+ export interface GustKitMapProps {
17
+ accessToken: string;
18
+ source: GustKitSource;
19
+ product: Product;
20
+ timeMs: number;
21
+ validTimes?: number[];
22
+ layers?: GustKitMapLayers;
23
+ opacity?: number;
24
+ center?: [number, number];
25
+ zoom?: number;
26
+ interactive?: boolean;
27
+ /** Field pad fraction. Live maps 0.5; locked postcards 0. */
28
+ pad?: number;
29
+ marginTiles?: number;
30
+ particleCount?: number;
31
+ particleColor?: string;
32
+ downsample?: number;
33
+ /** "grid" = live map; "sparse" = postcard (≤6 arrows). */
34
+ arrowMode?: "grid" | "sparse";
35
+ styleURL?: string;
36
+ className?: string;
37
+ children?: ReactNode;
38
+ /** Native Mapbox instance after style load — add boats, extra layers. */
39
+ onMap?: (map: mapboxgl.Map) => void;
40
+ onHover?: (info: GustKitMapHover | null) => void;
41
+ /**
42
+ * Press-and-hold on a point (mouse or touch). Fires once per gesture, and only
43
+ * if the pointer stayed put — a drag/pan is a map gesture, not a long press.
44
+ */
45
+ onLongPress?: (info: GustKitMapHover) => void;
46
+ /** Long-press duration in ms (default 450). */
47
+ longPressMs?: number;
48
+ onFieldReady?: (rasterMs: number, pixels: number) => void;
49
+ }
50
+ /**
51
+ * Default Mapbox weather map (ADR-016). Owns the Mapbox instance, the weather
52
+ * stack (field under labels, coastline, particles, isobars, arrows, centers),
53
+ * and `loadView` on camera idle via {@link syncFieldSession}.
54
+ */
55
+ export declare function GustKitMap({ accessToken, source, product, timeMs, validTimes, layers, opacity, center, zoom, interactive, pad, marginTiles, particleCount, particleColor, downsample, arrowMode, styleURL, className, children, onMap, onHover, onLongPress, longPressMs, onFieldReady, }: GustKitMapProps): import("react/jsx-runtime").JSX.Element;
56
+ //# sourceMappingURL=GustKitMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GustKitMap.d.ts","sourceRoot":"","sources":["../src/GustKitMap.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,EACL,OAAO,EAMP,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAwBzB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAgBD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,yEAAyE;IACzE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAoCD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,MAAM,EACN,OAAO,EACP,MAAM,EACN,UAAU,EACV,MAAW,EACX,OAAa,EACb,MAAkB,EAClB,IAAQ,EACR,WAAkB,EAClB,GAAG,EACH,WAAW,EACX,aAAoB,EACpB,aAAuC,EACvC,UAAc,EACd,SAAkB,EAClB,QAAwB,EACxB,SAAS,EACT,QAAQ,EACR,KAAK,EACL,OAAO,EACP,WAAW,EACX,WAAiB,EACjB,YAAY,GACb,EAAE,eAAe,2CAwXjB"}
@@ -0,0 +1,443 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import mapboxgl from "mapbox-gl";
4
+ import { CanvasParticleField } from "@gustkit/client/particles/canvas";
5
+ import { Product, hasColorField, syncFieldSession, vectorAt, } from "@gustkit/client";
6
+ import { fieldToDataUrl } from "./fieldPng.js";
7
+ import { paintArrowGrid, paintArrowSpots, sparseArrowSpots } from "./arrows.js";
8
+ import { ensureMapStyles } from "./styles.js";
9
+ const TRANSPARENT_PX = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=";
10
+ const EMPTY_FC = { type: "FeatureCollection", features: [] };
11
+ const DEFAULT_STYLE = "mapbox://styles/mapbox/dark-v11";
12
+ /**
13
+ * Basemap tints, 20% darker than the stock dark-v11 values.
14
+ *
15
+ * Counter-intuitive on a dark style, but we lighten LAND to #5b6474 so it reads
16
+ * under a translucent weather overlay — and dark-v11's borders sit at
17
+ * hsl(0 0% 38-41%), which is almost exactly that luminance, so they vanish into
18
+ * the landmass. Taking them down 20% restores the contrast the stock style gets
19
+ * from near-black land.
20
+ */
21
+ const LAND_FILL = "#5b6474";
22
+ const COASTLINE = "#434446"; // was #545558
23
+ const ADMIN_0 = "hsl(0, 0%, 33%)"; // country borders, was 41%
24
+ const ADMIN_1 = "hsl(0, 0%, 30%)"; // state/province borders, was 38%
25
+ /**
26
+ * Neighbour prefetch is a scroll-smoothness luxury: each extra valid time is a
27
+ * FULL extra field (12 packs / ~540KB at a basin-wide view). On a metered or slow
28
+ * link that competes with the first paint for the same pipe, so skip it there and
29
+ * let the visible step have the bandwidth to itself. Unknown link ⇒ prefetch, so
30
+ * desktop behaviour is unchanged.
31
+ */
32
+ function prefetchIsWorthIt() {
33
+ const c = navigator.connection;
34
+ if (!c)
35
+ return true;
36
+ if (c.saveData)
37
+ return false;
38
+ return !(c.effectiveType === "slow-2g" || c.effectiveType === "2g" || c.effectiveType === "3g");
39
+ }
40
+ function mapBounds(map) {
41
+ const b = map.getBounds();
42
+ return { west: b.getWest(), south: b.getSouth(), east: b.getEast(), north: b.getNorth() };
43
+ }
44
+ function quadOf(b) {
45
+ return [
46
+ [b.west, b.north],
47
+ [b.east, b.north],
48
+ [b.east, b.south],
49
+ [b.west, b.south],
50
+ ];
51
+ }
52
+ function canvasAdapter(map) {
53
+ return {
54
+ getBounds: () => mapBounds(map),
55
+ getSize: () => {
56
+ const el = map.getContainer();
57
+ return { width: el.clientWidth, height: el.clientHeight };
58
+ },
59
+ project: (lon, lat) => {
60
+ const p = map.project([lon, lat]);
61
+ return { x: p.x, y: p.y };
62
+ },
63
+ pxPerDeg: () => {
64
+ const c = map.getCenter();
65
+ const a = map.project([c.lng, c.lat]);
66
+ const d = map.project([c.lng + 1, c.lat]);
67
+ return Math.hypot(d.x - a.x, d.y - a.y) || 1;
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * Default Mapbox weather map (ADR-016). Owns the Mapbox instance, the weather
73
+ * stack (field under labels, coastline, particles, isobars, arrows, centers),
74
+ * and `loadView` on camera idle via {@link syncFieldSession}.
75
+ */
76
+ export function GustKitMap({ accessToken, source, product, timeMs, validTimes, layers = {}, opacity = 0.6, center = [-55, 32], zoom = 3, interactive = true, pad, marginTiles, particleCount = 1800, particleColor = "rgba(255,255,255,0.7)", downsample = 3, arrowMode = "grid", styleURL = DEFAULT_STYLE, className, children, onMap, onHover, onLongPress, longPressMs = 450, onFieldReady, }) {
77
+ const containerRef = useRef(null);
78
+ const mapRef = useRef(null);
79
+ const fieldRef = useRef(null);
80
+ const arrowsCanvasRef = useRef(null);
81
+ const markersRef = useRef([]);
82
+ const [ready, setReady] = useState(false);
83
+ const propsRef = useRef({
84
+ source,
85
+ product,
86
+ timeMs,
87
+ validTimes,
88
+ layers,
89
+ opacity,
90
+ pad,
91
+ marginTiles,
92
+ downsample,
93
+ arrowMode,
94
+ particleCount,
95
+ onFieldReady,
96
+ onHover,
97
+ onLongPress,
98
+ });
99
+ propsRef.current = {
100
+ source,
101
+ product,
102
+ timeMs,
103
+ validTimes,
104
+ layers,
105
+ opacity,
106
+ pad,
107
+ marginTiles,
108
+ downsample,
109
+ arrowMode,
110
+ particleCount,
111
+ onFieldReady,
112
+ onHover,
113
+ onLongPress,
114
+ };
115
+ useEffect(() => {
116
+ ensureMapStyles();
117
+ }, []);
118
+ useEffect(() => {
119
+ if (!containerRef.current || !accessToken)
120
+ return;
121
+ mapboxgl.accessToken = accessToken;
122
+ const map = new mapboxgl.Map({
123
+ container: containerRef.current,
124
+ style: styleURL,
125
+ center,
126
+ zoom,
127
+ interactive,
128
+ projection: "mercator",
129
+ attributionControl: false,
130
+ fadeDuration: interactive ? undefined : 0,
131
+ });
132
+ mapRef.current = map;
133
+ map.on("load", () => {
134
+ // Every one of these is best-effort: a caller can pass their own
135
+ // `styleURL`, and these ids are dark-v11's. A style without them keeps its
136
+ // own colours rather than throwing.
137
+ for (const [layer, prop, value] of [
138
+ ["land", "background-color", LAND_FILL],
139
+ ["admin-0-boundary", "line-color", ADMIN_0],
140
+ ["admin-1-boundary", "line-color", ADMIN_1],
141
+ ]) {
142
+ try {
143
+ map.setPaintProperty(layer, prop, value);
144
+ }
145
+ catch {
146
+ /* style layer id differs */
147
+ }
148
+ }
149
+ /**
150
+ * Insert our overlays BELOW the whole admin group, not just below
151
+ * `admin-0-boundary`.
152
+ *
153
+ * dark-v11 orders admin layers as [admin-1-bg, admin-0-bg, admin-1,
154
+ * admin-0, admin-0-disputed], so anchoring to `admin-0-boundary` dropped
155
+ * the weather raster on top of `admin-1-boundary` — country borders
156
+ * survived and every state/province line was painted over. Anchoring to
157
+ * the FIRST admin layer keeps them all above the weather.
158
+ */
159
+ const adminAnchor = map.getStyle().layers?.find((l) => l.id.startsWith("admin"))?.id;
160
+ const b0 = mapBounds(map);
161
+ map.addSource("gk-field", { type: "image", url: TRANSPARENT_PX, coordinates: quadOf(b0) });
162
+ map.addLayer({
163
+ id: "gk-field",
164
+ type: "raster",
165
+ source: "gk-field",
166
+ paint: { "raster-opacity": propsRef.current.opacity, "raster-resampling": "linear", "raster-fade-duration": 0 },
167
+ }, adminAnchor);
168
+ map.addLayer({
169
+ id: "gk-coastline",
170
+ type: "line",
171
+ source: "composite",
172
+ "source-layer": "water",
173
+ layout: { "line-join": "round" },
174
+ paint: { "line-color": COASTLINE, "line-width": 1.4, "line-opacity": 0.9 },
175
+ }, adminAnchor);
176
+ map.addSource("gk-isobars", { type: "geojson", data: EMPTY_FC });
177
+ map.addLayer({
178
+ id: "gk-isobars",
179
+ type: "line",
180
+ source: "gk-isobars",
181
+ layout: { "line-join": "round", "line-cap": "round" },
182
+ paint: {
183
+ "line-color": ["case", ["get", "major"], "#ffffff", "#c3d4e8"],
184
+ "line-width": ["case", ["get", "major"], 1.3, 0.6],
185
+ "line-opacity": 0.7,
186
+ },
187
+ });
188
+ const cc = map.getCanvasContainer();
189
+ const arrows = document.createElement("canvas");
190
+ arrows.className = "gk-overlay gk-arrows";
191
+ cc.appendChild(arrows);
192
+ arrowsCanvasRef.current = arrows;
193
+ const particles = document.createElement("canvas");
194
+ particles.className = "gk-overlay gk-particles";
195
+ cc.appendChild(particles);
196
+ const field = new CanvasParticleField(particles, canvasAdapter(map), {
197
+ sample: source.vectorSampler(product) ?? (() => null),
198
+ color: particleColor,
199
+ count: particleCount,
200
+ });
201
+ fieldRef.current = field;
202
+ field.start();
203
+ map.on("movestart", () => {
204
+ field.moveStart();
205
+ clearArrows();
206
+ });
207
+ map.on("moveend", () => {
208
+ field.moveEnd();
209
+ refreshField();
210
+ drawArrows();
211
+ });
212
+ map.on("resize", () => {
213
+ field.resize();
214
+ refreshField();
215
+ drawArrows();
216
+ });
217
+ map.on("mousemove", (e) => {
218
+ propsRef.current.onHover?.({ lon: e.lngLat.lng, lat: e.lngLat.lat, x: e.point.x, y: e.point.y });
219
+ });
220
+ map.on("mouseout", () => propsRef.current.onHover?.(null));
221
+ // Press-and-hold → point query. Cancelled by movement beyond a slop radius
222
+ // (that is a pan), by release, or by the map starting to move/zoom, so it
223
+ // never fires mid-gesture. Touch and mouse share one path.
224
+ let pressTimer = null;
225
+ let pressStart = null;
226
+ const SLOP_PX = 8;
227
+ const cancelPress = () => {
228
+ if (pressTimer)
229
+ clearTimeout(pressTimer);
230
+ pressTimer = null;
231
+ pressStart = null;
232
+ };
233
+ const beginPress = (e) => {
234
+ if (!propsRef.current.onLongPress)
235
+ return;
236
+ cancelPress();
237
+ const { lng, lat } = e.lngLat;
238
+ const point = e.point;
239
+ pressStart = { x: point.x, y: point.y };
240
+ pressTimer = setTimeout(() => {
241
+ pressTimer = null;
242
+ pressStart = null;
243
+ propsRef.current.onLongPress?.({ lon: lng, lat, x: point.x, y: point.y });
244
+ }, longPressMs);
245
+ };
246
+ const movePress = (e) => {
247
+ if (!pressStart)
248
+ return;
249
+ if (Math.hypot(e.point.x - pressStart.x, e.point.y - pressStart.y) > SLOP_PX)
250
+ cancelPress();
251
+ };
252
+ map.on("mousedown", beginPress);
253
+ map.on("touchstart", beginPress);
254
+ map.on("mousemove", movePress);
255
+ map.on("touchmove", movePress);
256
+ for (const ev of ["mouseup", "touchend", "touchcancel", "dragstart", "movestart", "zoomstart"]) {
257
+ map.on(ev, cancelPress);
258
+ }
259
+ // Suppress the browser context menu so a touch long-press feels native.
260
+ map.getCanvas().addEventListener("contextmenu", (ev) => ev.preventDefault());
261
+ if (!interactive) {
262
+ const ro = new ResizeObserver(() => {
263
+ map.resize();
264
+ fieldRef.current?.resize();
265
+ });
266
+ ro.observe(map.getContainer());
267
+ map._gkRo = ro;
268
+ }
269
+ onMap?.(map);
270
+ setReady(true);
271
+ refreshField();
272
+ drawArrows();
273
+ });
274
+ return () => {
275
+ fieldRef.current?.destroy();
276
+ markersRef.current.forEach((m) => m.remove());
277
+ markersRef.current = [];
278
+ const ro = map._gkRo;
279
+ ro?.disconnect();
280
+ map.remove();
281
+ mapRef.current = null;
282
+ };
283
+ // Camera identity — remount when the locked place changes.
284
+ // eslint-disable-next-line react-hooks/exhaustive-deps
285
+ }, [accessToken, styleURL, center[0], center[1], zoom, interactive]);
286
+ function refreshField() {
287
+ const map = mapRef.current;
288
+ const p = propsRef.current;
289
+ if (!map || !map.getLayer("gk-field"))
290
+ return;
291
+ const showField = (p.layers.field ?? true) && hasColorField(p.product);
292
+ map.setLayoutProperty("gk-field", "visibility", showField ? "visible" : "none");
293
+ const el = map.getContainer();
294
+ const w = el.clientWidth;
295
+ const h = el.clientHeight;
296
+ if (w < 2 || h < 2 || p.timeMs <= 0)
297
+ return;
298
+ const bounds = mapBounds(map);
299
+ if (bounds.west === bounds.east || bounds.north <= bounds.south)
300
+ return;
301
+ const z = map.getZoom();
302
+ const gen = (map._gkFieldGen ?? 0) + 1;
303
+ map._gkFieldGen = gen;
304
+ const apply = (session) => {
305
+ if (mapRef.current !== map)
306
+ return;
307
+ if (map._gkFieldGen !== gen)
308
+ return;
309
+ if (showField) {
310
+ const t0 = performance.now();
311
+ const url = fieldToDataUrl(session.field);
312
+ p.onFieldReady?.(Math.round(performance.now() - t0), session.field.width * session.field.height);
313
+ const src = map.getSource("gk-field");
314
+ if (url && src)
315
+ src.updateImage({ url, coordinates: quadOf(session.padded) });
316
+ map.setPaintProperty("gk-field", "raster-opacity", p.opacity);
317
+ }
318
+ drawArrows();
319
+ };
320
+ const req = {
321
+ source: p.source,
322
+ product: p.product,
323
+ timeMs: p.timeMs,
324
+ bounds,
325
+ zoom: z,
326
+ size: { width: w, height: h },
327
+ pad: p.pad,
328
+ downsample: p.downsample,
329
+ validTimes: p.validTimes,
330
+ prefetchDelayMs: 800,
331
+ prefetchNeighbours: prefetchIsWorthIt(),
332
+ viewOptions: p.marginTiles != null ? { marginTiles: p.marginTiles } : undefined,
333
+ onPartial: apply,
334
+ };
335
+ void syncFieldSession(req).then(apply);
336
+ }
337
+ function clearArrows() {
338
+ const c = arrowsCanvasRef.current;
339
+ if (!c)
340
+ return;
341
+ c.getContext("2d")?.clearRect(0, 0, c.width, c.height);
342
+ }
343
+ function drawArrows() {
344
+ const map = mapRef.current;
345
+ const canvas = arrowsCanvasRef.current;
346
+ const p = propsRef.current;
347
+ if (!map || !canvas)
348
+ return;
349
+ const el = map.getContainer();
350
+ const w = el.clientWidth;
351
+ const h = el.clientHeight;
352
+ const dpr = window.devicePixelRatio || 1;
353
+ canvas.width = Math.round(w * dpr);
354
+ canvas.height = Math.round(h * dpr);
355
+ canvas.style.width = `${w}px`;
356
+ canvas.style.height = `${h}px`;
357
+ const ctx = canvas.getContext("2d");
358
+ if (!ctx)
359
+ return;
360
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
361
+ ctx.clearRect(0, 0, w, h);
362
+ if (!p.layers.arrows || p.product === Product.Pressure || w < 2)
363
+ return;
364
+ if (p.arrowMode === "sparse") {
365
+ const magMax = p.product === Product.Wind ? 25 : 8;
366
+ const spots = sparseArrowSpots(w, h, (px, py) => {
367
+ const ll = map.unproject([px, py]);
368
+ return vectorAt(p.source, p.product, ll.lng, ll.lat);
369
+ });
370
+ paintArrowSpots(ctx, spots, magMax, 3);
371
+ }
372
+ else {
373
+ paintArrowGrid(ctx, w, h, p.source, p.product, (px, py) => {
374
+ const ll = map.unproject([px, py]);
375
+ return { lon: ll.lng, lat: ll.lat };
376
+ });
377
+ }
378
+ }
379
+ useEffect(() => {
380
+ if (ready)
381
+ refreshField();
382
+ // eslint-disable-next-line react-hooks/exhaustive-deps
383
+ }, [ready, source, product, timeMs, opacity]);
384
+ useEffect(() => {
385
+ if (ready)
386
+ drawArrows();
387
+ // eslint-disable-next-line react-hooks/exhaustive-deps
388
+ }, [ready, layers.arrows, product, arrowMode]);
389
+ useEffect(() => {
390
+ const field = fieldRef.current;
391
+ if (!ready || !field)
392
+ return;
393
+ field.setSampler(source.vectorSampler(product) ?? (() => null));
394
+ field.setEnabled((layers.particles ?? true) && product !== Product.Pressure);
395
+ }, [ready, source, product, timeMs, layers.particles]);
396
+ useEffect(() => {
397
+ const map = mapRef.current;
398
+ if (!ready || !map || !map.getLayer("gk-isobars"))
399
+ return;
400
+ const on = layers.isobars ?? false;
401
+ map.setLayoutProperty("gk-isobars", "visibility", on ? "visible" : "none");
402
+ if (!on)
403
+ return;
404
+ let cancelled = false;
405
+ void source.fetchIsobars(timeMs).then((gj) => {
406
+ if (cancelled)
407
+ return;
408
+ const src = map.getSource("gk-isobars");
409
+ src?.setData(gj ?? EMPTY_FC);
410
+ });
411
+ return () => {
412
+ cancelled = true;
413
+ };
414
+ }, [ready, source, timeMs, layers.isobars]);
415
+ useEffect(() => {
416
+ const map = mapRef.current;
417
+ if (!ready || !map)
418
+ return;
419
+ markersRef.current.forEach((m) => m.remove());
420
+ markersRef.current = [];
421
+ if (!layers.centers)
422
+ return;
423
+ let cancelled = false;
424
+ void source.fetchCenters(timeMs).then((centers) => {
425
+ if (cancelled)
426
+ return;
427
+ for (const c of centers) {
428
+ const el = document.createElement("div");
429
+ el.className = `gk-center gk-center-${c.kind === "H" ? "high" : "low"}`;
430
+ el.innerHTML = `<b>${c.kind}</b><span>${Math.round(c.pressureHpa)}</span>`;
431
+ markersRef.current.push(new mapboxgl.Marker({ element: el }).setLngLat([c.lon, c.lat]).addTo(map));
432
+ }
433
+ });
434
+ return () => {
435
+ cancelled = true;
436
+ };
437
+ }, [ready, source, timeMs, layers.centers]);
438
+ if (!accessToken) {
439
+ return (_jsxs("div", { className: "gk-notoken", children: ["Set a Mapbox public token (", _jsx("code", { children: "pk.\u2026" }), ") to load the map."] }));
440
+ }
441
+ return (_jsxs("div", { className: className ?? "gk-map-root", style: { position: "absolute", inset: 0 }, children: [_jsx("div", { ref: containerRef, className: "gk-map-canvas" }), children ? _jsx("div", { className: "gk-map-chrome", children: children }) : null] }));
442
+ }
443
+ //# sourceMappingURL=GustKitMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GustKitMap.js","sourceRoot":"","sources":["../src/GustKitMap.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type GustKitSource, type VectorSample } from "@gustkit/client";
2
+ import { Product } from "@gustkit/client";
3
+ export interface ArrowSpot {
4
+ x: number;
5
+ y: number;
6
+ dirU: number;
7
+ dirV: number;
8
+ mag: number;
9
+ }
10
+ /** 3×2 sample grid, inset from the postcard edges — at most `maxCount` arrows. */
11
+ export declare function sparseArrowSpots(w: number, h: number, sample: (px: number, py: number) => VectorSample | null, maxCount?: number): ArrowSpot[];
12
+ export declare function paintArrowSpots(ctx: CanvasRenderingContext2D, spots: ArrowSpot[], magMax: number, scale?: number): void;
13
+ export declare function paintArrowGrid(ctx: CanvasRenderingContext2D, w: number, h: number, source: GustKitSource, product: Product, unproject: (px: number, py: number) => {
14
+ lon: number;
15
+ lat: number;
16
+ }): void;
17
+ //# sourceMappingURL=arrows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrows.d.ts","sourceRoot":"","sources":["../src/arrows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,EACvD,QAAQ,SAAI,GACX,SAAS,EAAE,CAkBb;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,wBAAwB,EAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,SAAI,GACR,IAAI,CAgCN;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAClE,IAAI,CA4BN"}