solid-map-gl 2.0.1 → 2.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/README.md +13 -1
- package/dist/index.d.ts +194 -4
- package/dist/index.js +7 -7
- package/dist/index.jsx +499 -94
- package/package.json +1 -1
package/dist/index.jsx
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
// src/components/MapGL/index.tsx
|
|
2
2
|
import {
|
|
3
3
|
createSignal,
|
|
4
|
-
createEffect,
|
|
4
|
+
createEffect as createEffect2,
|
|
5
5
|
onMount,
|
|
6
6
|
onCleanup,
|
|
7
7
|
on
|
|
8
8
|
} from "solid-js";
|
|
9
|
+
import { isServer } from "solid-js/web";
|
|
9
10
|
|
|
10
11
|
// src/components/MapProvider/index.tsx
|
|
11
|
-
import { createContext, useContext } from "solid-js";
|
|
12
|
+
import { createContext, useContext, createEffect } from "solid-js";
|
|
12
13
|
import { createStore } from "solid-js/store";
|
|
13
|
-
var defaultState = {
|
|
14
|
+
var defaultState = {
|
|
15
|
+
map: null,
|
|
16
|
+
mapLib: null,
|
|
17
|
+
isMapLibre: false,
|
|
18
|
+
constants: {},
|
|
19
|
+
themeVersion: 0
|
|
20
|
+
};
|
|
14
21
|
var MapContext = createContext([defaultState]);
|
|
15
22
|
var useMapContext = () => useContext(MapContext);
|
|
16
23
|
var MapProvider = (props) => {
|
|
@@ -18,6 +25,10 @@ var MapProvider = (props) => {
|
|
|
18
25
|
props.map && setState("map", props.map);
|
|
19
26
|
props.mapLib && setState("mapLib", props.mapLib);
|
|
20
27
|
setState("isMapLibre", !!props.isMapLibre);
|
|
28
|
+
setState("constants", props.constants || {});
|
|
29
|
+
setState("themeVersion", props.themeVersion || 0);
|
|
30
|
+
createEffect(() => setState("constants", props.constants || {}));
|
|
31
|
+
createEffect(() => setState("themeVersion", props.themeVersion || 0));
|
|
21
32
|
return <MapContext.Provider value={[state]}>{props.children}</MapContext.Provider>;
|
|
22
33
|
};
|
|
23
34
|
|
|
@@ -218,6 +229,79 @@ var rasterStyleList = {
|
|
|
218
229
|
}
|
|
219
230
|
};
|
|
220
231
|
|
|
232
|
+
// src/tilesSettled.ts
|
|
233
|
+
var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
234
|
+
var hasActiveFadeTransition = (map) => {
|
|
235
|
+
try {
|
|
236
|
+
return !!map.style?.hasTransitions?.();
|
|
237
|
+
} catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function pollTilesLoaded(map, timeout) {
|
|
242
|
+
return new Promise((resolve) => {
|
|
243
|
+
const deadline = Date.now() + timeout;
|
|
244
|
+
const poll = () => {
|
|
245
|
+
if (map.areTilesLoaded()) {
|
|
246
|
+
resolve(true);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (Date.now() >= deadline) {
|
|
250
|
+
resolve(false);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
setTimeout(poll, 100);
|
|
254
|
+
};
|
|
255
|
+
poll();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async function settleAfterIdle(map, options = {}) {
|
|
259
|
+
const { timeout = 1e4, fadeMargin = 400 } = options;
|
|
260
|
+
await pollTilesLoaded(map, timeout);
|
|
261
|
+
map.triggerRepaint();
|
|
262
|
+
await nextFrame();
|
|
263
|
+
await nextFrame();
|
|
264
|
+
if (fadeMargin > 0) {
|
|
265
|
+
const fadeDeadline = Date.now() + Math.max(fadeMargin, 2e3);
|
|
266
|
+
while (hasActiveFadeTransition(map) && Date.now() < fadeDeadline) {
|
|
267
|
+
map.triggerRepaint();
|
|
268
|
+
await nextFrame();
|
|
269
|
+
}
|
|
270
|
+
await new Promise((resolve) => setTimeout(resolve, fadeMargin));
|
|
271
|
+
map.triggerRepaint();
|
|
272
|
+
await nextFrame();
|
|
273
|
+
await nextFrame();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function waitForIdleAndSettle(map, options = {}) {
|
|
277
|
+
return new Promise((resolve) => {
|
|
278
|
+
map.once("idle", () => {
|
|
279
|
+
settleAfterIdle(map, options).then(resolve);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function disableRasterFade(map) {
|
|
284
|
+
for (const layer of map.getStyle()?.layers ?? []) {
|
|
285
|
+
try {
|
|
286
|
+
map.setPaintProperty(layer.id, "raster-fade-duration", 0);
|
|
287
|
+
} catch {
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/offscreenCapture.ts
|
|
293
|
+
function createCapturer(map, settleOptions) {
|
|
294
|
+
return {
|
|
295
|
+
map,
|
|
296
|
+
waitUntilSettled: () => waitForIdleAndSettle(map, settleOptions),
|
|
297
|
+
capture: (type = "image/jpeg", quality = 0.92) => map.getCanvas().toDataURL(type, quality),
|
|
298
|
+
captureWhenSettled: async (type = "image/jpeg", quality = 0.92) => {
|
|
299
|
+
await waitForIdleAndSettle(map, settleOptions);
|
|
300
|
+
return map.getCanvas().toDataURL(type, quality);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
221
305
|
// src/components/MapGL/index.tsx
|
|
222
306
|
var MapGL = (props) => {
|
|
223
307
|
let map;
|
|
@@ -226,9 +310,11 @@ var MapGL = (props) => {
|
|
|
226
310
|
let mutationObserver;
|
|
227
311
|
let mapLib;
|
|
228
312
|
let isMapLibre = false;
|
|
313
|
+
let tilesLoadedGeneration = 0;
|
|
229
314
|
const [mapLoaded, setMapLoaded] = createSignal(null);
|
|
315
|
+
const [themeVersion, setThemeVersion] = createSignal(0);
|
|
230
316
|
const [darkMode, setDarkMode] = createSignal(
|
|
231
|
-
|
|
317
|
+
!isServer && (window.matchMedia("(prefers-color-scheme: dark)").matches || document.body.classList.contains("dark"))
|
|
232
318
|
);
|
|
233
319
|
let interruptingEase = false;
|
|
234
320
|
let expectingOwnEcho = false;
|
|
@@ -257,7 +343,11 @@ var MapGL = (props) => {
|
|
|
257
343
|
projection: props.options?.projection,
|
|
258
344
|
container: mapRef,
|
|
259
345
|
style: getStyle(props.options?.style, props.darkStyle),
|
|
260
|
-
fitBoundsOptions: { padding: props.viewport?.padding }
|
|
346
|
+
fitBoundsOptions: { padding: props.viewport?.padding },
|
|
347
|
+
// Required for capture()/toDataURL() to read back anything from the canvas at all — forced
|
|
348
|
+
// regardless of `options` since getting this wrong doesn't degrade capture, it silently
|
|
349
|
+
// breaks it (an all-transparent/blank image, no error).
|
|
350
|
+
...props.offscreen ? { preserveDrawingBuffer: true } : {}
|
|
261
351
|
});
|
|
262
352
|
map.debug = props.debug;
|
|
263
353
|
map.debugEvents = props.debugEvents;
|
|
@@ -289,9 +379,30 @@ var MapGL = (props) => {
|
|
|
289
379
|
}
|
|
290
380
|
}
|
|
291
381
|
});
|
|
382
|
+
if (props.onTilesLoaded) {
|
|
383
|
+
const timeout = props.tilesLoadedTimeout ?? 1e4;
|
|
384
|
+
const fadeMargin = props.tilesLoadedFadeMargin ?? 400;
|
|
385
|
+
map.on("idle", () => {
|
|
386
|
+
const generation = ++tilesLoadedGeneration;
|
|
387
|
+
settleAfterIdle(map, { timeout, fadeMargin }).then(() => {
|
|
388
|
+
if (generation !== tilesLoadedGeneration) return;
|
|
389
|
+
debug("Tiles settled");
|
|
390
|
+
props.onTilesLoaded();
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
}
|
|
292
394
|
map.once("load", () => {
|
|
293
395
|
setMapLoaded(map);
|
|
294
396
|
debug("Map loaded");
|
|
397
|
+
if (props.offscreen) {
|
|
398
|
+
if (props.offscreen.disableRasterFade !== false) disableRasterFade(map);
|
|
399
|
+
props.onCapturerReady?.(
|
|
400
|
+
createCapturer(map, {
|
|
401
|
+
timeout: props.tilesLoadedTimeout ?? 1e4,
|
|
402
|
+
fadeMargin: props.tilesLoadedFadeMargin ?? 400
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
}
|
|
295
406
|
["mousedown", "touchstart", "wheel"].forEach(
|
|
296
407
|
(event) => map.on(
|
|
297
408
|
event,
|
|
@@ -304,16 +415,19 @@ var MapGL = (props) => {
|
|
|
304
415
|
(evt) => !evt.rotate && props.onUserInteraction?.(false)
|
|
305
416
|
)
|
|
306
417
|
);
|
|
307
|
-
const darkTheme =
|
|
418
|
+
const darkTheme = !isServer && window.matchMedia("(prefers-color-scheme: dark)");
|
|
308
419
|
darkTheme?.addEventListener("change", () => {
|
|
309
420
|
setDarkMode(darkTheme.matches);
|
|
421
|
+
setThemeVersion((v) => v + 1);
|
|
310
422
|
debug("Set dark theme to:", darkTheme.matches?.toString());
|
|
311
423
|
});
|
|
312
424
|
mutationObserver = new MutationObserver(() => {
|
|
313
|
-
const darkTheme2 = document.body.classList.contains("dark");
|
|
425
|
+
const darkTheme2 = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark");
|
|
314
426
|
setDarkMode(darkTheme2);
|
|
427
|
+
setThemeVersion((v) => v + 1);
|
|
315
428
|
debug("Set theme to:", darkTheme2);
|
|
316
429
|
});
|
|
430
|
+
mutationObserver.observe(document.documentElement, { attributes: true });
|
|
317
431
|
mutationObserver.observe(document.body, { attributes: true });
|
|
318
432
|
if (!props.disableResize) {
|
|
319
433
|
resizeObserver = new ResizeObserver(() => {
|
|
@@ -368,7 +482,7 @@ var MapGL = (props) => {
|
|
|
368
482
|
});
|
|
369
483
|
});
|
|
370
484
|
});
|
|
371
|
-
|
|
485
|
+
createEffect2(
|
|
372
486
|
on(
|
|
373
487
|
() => props.viewport,
|
|
374
488
|
(vp) => {
|
|
@@ -397,7 +511,7 @@ var MapGL = (props) => {
|
|
|
397
511
|
{ defer: true }
|
|
398
512
|
)
|
|
399
513
|
);
|
|
400
|
-
|
|
514
|
+
createEffect2(() => {
|
|
401
515
|
const loadedMap = mapLoaded();
|
|
402
516
|
if (typeof loadedMap?.setConfigProperty !== "function") {
|
|
403
517
|
if (loadedMap && props.config && Object.keys(props.config).length) {
|
|
@@ -415,13 +529,13 @@ var MapGL = (props) => {
|
|
|
415
529
|
debug(`Set Config (${id}:${key}) to:`, value);
|
|
416
530
|
}
|
|
417
531
|
});
|
|
418
|
-
|
|
532
|
+
createEffect2(() => {
|
|
419
533
|
const proj = props.options?.projection;
|
|
420
534
|
if (!map || !proj) return;
|
|
421
535
|
map.setProjection(proj);
|
|
422
536
|
debug("Set Projection to:", proj);
|
|
423
537
|
});
|
|
424
|
-
|
|
538
|
+
createEffect2(() => {
|
|
425
539
|
const cur = props.cursorStyle;
|
|
426
540
|
if (!map || !cur) return;
|
|
427
541
|
map.getCanvas().style.cursor = cur;
|
|
@@ -436,7 +550,7 @@ var MapGL = (props) => {
|
|
|
436
550
|
});
|
|
437
551
|
return list;
|
|
438
552
|
};
|
|
439
|
-
|
|
553
|
+
createEffect2((prev) => {
|
|
440
554
|
const style = getStyle(props.options?.style, props.darkStyle);
|
|
441
555
|
if (map?.isStyleLoaded() && prev !== style) {
|
|
442
556
|
const oldStyle = map.getStyle();
|
|
@@ -469,7 +583,7 @@ var MapGL = (props) => {
|
|
|
469
583
|
"showPadding",
|
|
470
584
|
"showOverdrawInspector"
|
|
471
585
|
].forEach((item) => {
|
|
472
|
-
|
|
586
|
+
createEffect2(() => {
|
|
473
587
|
const prop = props[item];
|
|
474
588
|
if (!map || !prop) return;
|
|
475
589
|
map[item] = prop;
|
|
@@ -479,6 +593,7 @@ var MapGL = (props) => {
|
|
|
479
593
|
onCleanup(() => {
|
|
480
594
|
resizeObserver?.disconnect();
|
|
481
595
|
mutationObserver?.disconnect();
|
|
596
|
+
tilesLoadedGeneration++;
|
|
482
597
|
map?.remove();
|
|
483
598
|
debug("Map removed");
|
|
484
599
|
});
|
|
@@ -487,9 +602,22 @@ var MapGL = (props) => {
|
|
|
487
602
|
id={props.id}
|
|
488
603
|
class={props?.class}
|
|
489
604
|
classList={props?.classList}
|
|
490
|
-
style={
|
|
605
|
+
style={props.offscreen ? {
|
|
606
|
+
position: "fixed",
|
|
607
|
+
left: "-99999px",
|
|
608
|
+
top: "-99999px",
|
|
609
|
+
width: `${props.offscreen.width}px`,
|
|
610
|
+
height: `${props.offscreen.height}px`,
|
|
611
|
+
...props.style
|
|
612
|
+
} : { width: "100%", height: "100%", ...props.style }}
|
|
613
|
+
>
|
|
614
|
+
{mapLoaded() && <MapProvider
|
|
615
|
+
map={mapLoaded()}
|
|
616
|
+
mapLib={mapLib}
|
|
617
|
+
isMapLibre={isMapLibre}
|
|
618
|
+
constants={props.constants}
|
|
619
|
+
themeVersion={themeVersion()}
|
|
491
620
|
>
|
|
492
|
-
{mapLoaded() && <MapProvider map={mapLoaded()} mapLib={mapLib} isMapLibre={isMapLibre}>
|
|
493
621
|
<style>{`.overlay{position:relative;width:100%;height:100%;pointer-events:none}:where(.overlay>*){pointer-events:auto}`}</style>
|
|
494
622
|
<div class="overlay">{props.children}</div>
|
|
495
623
|
</MapProvider>}
|
|
@@ -499,7 +627,7 @@ var MapGL = (props) => {
|
|
|
499
627
|
// src/components/Source/index.tsx
|
|
500
628
|
import {
|
|
501
629
|
onCleanup as onCleanup2,
|
|
502
|
-
createEffect as
|
|
630
|
+
createEffect as createEffect3,
|
|
503
631
|
createContext as createContext2,
|
|
504
632
|
useContext as useContext2,
|
|
505
633
|
createUniqueId
|
|
@@ -537,14 +665,14 @@ var Source = (props) => {
|
|
|
537
665
|
debug("Add Source:", props.id);
|
|
538
666
|
switch (anySource().type) {
|
|
539
667
|
case "geojson":
|
|
540
|
-
|
|
668
|
+
createEffect3(() => {
|
|
541
669
|
const data = anySource().data;
|
|
542
670
|
ctx.map.getSource(props.id).setData(data || {});
|
|
543
671
|
debug("Update GeoJSON Data:", props.id);
|
|
544
672
|
});
|
|
545
673
|
break;
|
|
546
674
|
case "image":
|
|
547
|
-
|
|
675
|
+
createEffect3(() => {
|
|
548
676
|
const url = anySource().url;
|
|
549
677
|
const coords = anySource().coordinates;
|
|
550
678
|
ctx.map.getSource(props.id).updateImage(url, coords);
|
|
@@ -552,7 +680,7 @@ var Source = (props) => {
|
|
|
552
680
|
});
|
|
553
681
|
break;
|
|
554
682
|
case "vector":
|
|
555
|
-
|
|
683
|
+
createEffect3(() => {
|
|
556
684
|
const url = anySource().url;
|
|
557
685
|
const tiles = anySource().tiles;
|
|
558
686
|
const source = ctx.map.getSource(props.id);
|
|
@@ -561,7 +689,7 @@ var Source = (props) => {
|
|
|
561
689
|
});
|
|
562
690
|
break;
|
|
563
691
|
case "raster":
|
|
564
|
-
|
|
692
|
+
createEffect3(() => {
|
|
565
693
|
const src = lookup(anySource().url);
|
|
566
694
|
const source = ctx.map.getSource(props.id);
|
|
567
695
|
src.url ? source.setUrl(src.url) : source.setTiles(src.tiles);
|
|
@@ -584,7 +712,7 @@ var Source = (props) => {
|
|
|
584
712
|
// src/components/Layer/index.tsx
|
|
585
713
|
import {
|
|
586
714
|
onCleanup as onCleanup3,
|
|
587
|
-
createEffect as
|
|
715
|
+
createEffect as createEffect4,
|
|
588
716
|
createMemo,
|
|
589
717
|
createUniqueId as createUniqueId2
|
|
590
718
|
} from "solid-js";
|
|
@@ -653,7 +781,70 @@ var layoutStyles = [
|
|
|
653
781
|
"text-writing-mode"
|
|
654
782
|
];
|
|
655
783
|
|
|
784
|
+
// src/colors.ts
|
|
785
|
+
var probe;
|
|
786
|
+
var toRgbString = (cssColor) => {
|
|
787
|
+
probe ??= document.body.appendChild(document.createElement("div"));
|
|
788
|
+
probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none";
|
|
789
|
+
probe.style.color = "";
|
|
790
|
+
probe.style.color = cssColor;
|
|
791
|
+
return probe.style.color ? getComputedStyle(probe).color : void 0;
|
|
792
|
+
};
|
|
793
|
+
var tailwindName = /^[a-z]+-\d{2,3}$/;
|
|
794
|
+
var nativelySupported = /^(rgb|rgba|hsl|hsla)\(/i;
|
|
795
|
+
var cssColorFunction = /^[a-z-]+\(/i;
|
|
796
|
+
var classProbe;
|
|
797
|
+
var bgClassPair = /^bg-[a-z]+-\d{2,3}(?:\s+dark:bg-[a-z]+-\d{2,3})?$/;
|
|
798
|
+
var resolveClassPair = (value) => {
|
|
799
|
+
classProbe ??= document.body.appendChild(document.createElement("div"));
|
|
800
|
+
classProbe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none";
|
|
801
|
+
classProbe.className = value;
|
|
802
|
+
const bg = getComputedStyle(classProbe).backgroundColor;
|
|
803
|
+
classProbe.className = "";
|
|
804
|
+
return bg && bg !== "rgba(0, 0, 0, 0)" ? bg : value;
|
|
805
|
+
};
|
|
806
|
+
var resolveColor = (value) => {
|
|
807
|
+
if (bgClassPair.test(value)) return resolveClassPair(value);
|
|
808
|
+
if (tailwindName.test(value)) {
|
|
809
|
+
const raw = getComputedStyle(document.documentElement).getPropertyValue(`--color-${value}`).trim();
|
|
810
|
+
return raw && toRgbString(raw) || value;
|
|
811
|
+
}
|
|
812
|
+
if (cssColorFunction.test(value) && !nativelySupported.test(value)) {
|
|
813
|
+
return toRgbString(value) || value;
|
|
814
|
+
}
|
|
815
|
+
return value;
|
|
816
|
+
};
|
|
817
|
+
var toRgbaComponents = (value) => {
|
|
818
|
+
const rgb = toRgbString(resolveColor(value)) ?? value;
|
|
819
|
+
const match = rgb.match(/rgba?\(([^)]+)\)/i);
|
|
820
|
+
if (!match) return [0, 0, 0, 1];
|
|
821
|
+
const [r, g, b, a = 1] = match[1].split(",").map(Number);
|
|
822
|
+
return [r, g, b, a];
|
|
823
|
+
};
|
|
824
|
+
|
|
656
825
|
// src/components/Layer/index.tsx
|
|
826
|
+
var PULSE_DEFAULTS = {
|
|
827
|
+
property: "icon-halo-width",
|
|
828
|
+
from: 0,
|
|
829
|
+
to: 8,
|
|
830
|
+
duration: 1500,
|
|
831
|
+
waveform: "out"
|
|
832
|
+
};
|
|
833
|
+
var PULSE_RAMP_FRACTION = 0.75;
|
|
834
|
+
var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
|
|
835
|
+
var pulseShape = (elapsed, duration, waveform) => {
|
|
836
|
+
const phase = elapsed % duration / duration;
|
|
837
|
+
if (waveform === "in-out") return (1 - Math.cos(phase * Math.PI * 2)) / 2;
|
|
838
|
+
const ramp = Math.min(phase / PULSE_RAMP_FRACTION, 1);
|
|
839
|
+
return easeOutQuad(ramp);
|
|
840
|
+
};
|
|
841
|
+
var lerpColor = (from, to, t) => {
|
|
842
|
+
const r = Math.round(from[0] + (to[0] - from[0]) * t);
|
|
843
|
+
const g = Math.round(from[1] + (to[1] - from[1]) * t);
|
|
844
|
+
const b = Math.round(from[2] + (to[2] - from[2]) * t);
|
|
845
|
+
const a = from[3] + (to[3] - from[3]) * t;
|
|
846
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
847
|
+
};
|
|
657
848
|
var diff = (newProps = {}, prevProps = {}) => {
|
|
658
849
|
const keys = /* @__PURE__ */ new Set([...Object.keys(newProps), ...Object.keys(prevProps)]);
|
|
659
850
|
return [...keys].reduce((acc, key) => {
|
|
@@ -665,26 +856,43 @@ var diff = (newProps = {}, prevProps = {}) => {
|
|
|
665
856
|
}, []);
|
|
666
857
|
};
|
|
667
858
|
var newKey = (key, type) => (key.startsWith(type) || key.startsWith("icon") || key.startsWith("text") ? "" : type + "-") + key.replace(/[A-Z]/g, (s) => "-" + s.toLowerCase());
|
|
668
|
-
var
|
|
859
|
+
var resolveColor2 = (key, value) => key.endsWith("color") && typeof value === "string" ? resolveColor(value) : value;
|
|
860
|
+
var constantRef = /^@(.+)$/;
|
|
861
|
+
var resolveConstant = (key, value, constants, debug) => {
|
|
862
|
+
if (typeof value !== "string") return value;
|
|
863
|
+
const match = value.match(constantRef);
|
|
864
|
+
if (!match) return value;
|
|
865
|
+
const resolved = constants[match[1]];
|
|
866
|
+
if (resolved === void 0) {
|
|
867
|
+
debug(`Unknown constant "@${match[1]}" referenced by "${key}" \u2014 leaving unresolved`);
|
|
868
|
+
return value;
|
|
869
|
+
}
|
|
870
|
+
return resolved;
|
|
871
|
+
};
|
|
872
|
+
var updateStyle = (oldStyle, constants = {}, debug = () => {
|
|
873
|
+
}) => {
|
|
669
874
|
if (!oldStyle) return;
|
|
670
875
|
let layout = {};
|
|
671
876
|
let paint = {};
|
|
672
877
|
let style = {};
|
|
878
|
+
const resolvePaint = (key, value) => resolveColor2(key, resolveConstant(key, value, constants, debug));
|
|
673
879
|
Object.entries(oldStyle).forEach(([key, value]) => {
|
|
674
880
|
if (baseStyle.includes(key)) style[key] = value;
|
|
675
881
|
else {
|
|
676
882
|
const nk = newKey(key, oldStyle.type);
|
|
677
|
-
layoutStyles.includes(nk) ? layout[nk] = value : paint[nk] = value;
|
|
883
|
+
layoutStyles.includes(nk) ? layout[nk] = resolveConstant(nk, value, constants, debug) : paint[nk] = resolvePaint(nk, value);
|
|
678
884
|
}
|
|
679
885
|
});
|
|
680
886
|
if (oldStyle.paint)
|
|
681
|
-
Object.entries(oldStyle.paint).forEach(
|
|
682
|
-
|
|
683
|
-
|
|
887
|
+
Object.entries(oldStyle.paint).forEach(([key, value]) => {
|
|
888
|
+
const nk = newKey(key, oldStyle.type);
|
|
889
|
+
paint[nk] = resolvePaint(nk, value);
|
|
890
|
+
});
|
|
684
891
|
if (oldStyle.layout)
|
|
685
|
-
Object.entries(oldStyle.layout).forEach(
|
|
686
|
-
|
|
687
|
-
|
|
892
|
+
Object.entries(oldStyle.layout).forEach(([key, value]) => {
|
|
893
|
+
const nk = newKey(key, oldStyle.type);
|
|
894
|
+
layout[nk] = resolveConstant(nk, value, constants, debug);
|
|
895
|
+
});
|
|
688
896
|
return { ...style, paint, layout };
|
|
689
897
|
};
|
|
690
898
|
var Layer = (props) => {
|
|
@@ -699,7 +907,7 @@ var Layer = (props) => {
|
|
|
699
907
|
);
|
|
700
908
|
ctx.map.addLayer(
|
|
701
909
|
props.customLayer || {
|
|
702
|
-
...updateStyle(props.style),
|
|
910
|
+
...updateStyle(props.style, ctx.constants, debug),
|
|
703
911
|
id: layerId,
|
|
704
912
|
source: sourceId,
|
|
705
913
|
// `slot` is Mapbox Standard-Style-only — MapLibre has no equivalent (see docs/dev-notes.md)
|
|
@@ -723,8 +931,9 @@ var Layer = (props) => {
|
|
|
723
931
|
});
|
|
724
932
|
}
|
|
725
933
|
});
|
|
726
|
-
|
|
727
|
-
|
|
934
|
+
createEffect4((prev) => {
|
|
935
|
+
ctx.themeVersion;
|
|
936
|
+
const style = updateStyle(props.style, ctx.constants, debug);
|
|
728
937
|
if (style === prev) return;
|
|
729
938
|
if (style.layout !== prev?.layout)
|
|
730
939
|
diff(style.layout, prev?.layout).forEach(
|
|
@@ -740,8 +949,8 @@ var Layer = (props) => {
|
|
|
740
949
|
ctx.map.setFilter(layerId, style.filter, { validate: false });
|
|
741
950
|
debug("Update Layer Style:", layerId);
|
|
742
951
|
return style;
|
|
743
|
-
}, updateStyle(props.style));
|
|
744
|
-
|
|
952
|
+
}, updateStyle(props.style, ctx.constants, debug));
|
|
953
|
+
createEffect4((prev) => {
|
|
745
954
|
if (props.visible === prev) return;
|
|
746
955
|
ctx.map.setLayoutProperty(
|
|
747
956
|
layerId,
|
|
@@ -752,19 +961,19 @@ var Layer = (props) => {
|
|
|
752
961
|
debug(`Update Visibility (${layerId}):`, props.visible.toString());
|
|
753
962
|
return props.visible;
|
|
754
963
|
}, props.visible);
|
|
755
|
-
|
|
964
|
+
createEffect4((prev) => {
|
|
756
965
|
if (getBeforeId() === prev) return prev;
|
|
757
966
|
ctx.map.moveLayer(layerId, getBeforeId());
|
|
758
967
|
debug(`Update Layer Z-Index (${layerId}):`, getBeforeId());
|
|
759
968
|
return getBeforeId();
|
|
760
969
|
}, getBeforeId());
|
|
761
|
-
|
|
970
|
+
createEffect4(async () => {
|
|
762
971
|
if (!props.filter) return;
|
|
763
972
|
!ctx.map.isStyleLoaded() && await ctx.map.once("styledata");
|
|
764
973
|
ctx.map.setFilter(layerId, props.filter);
|
|
765
974
|
debug(`Update Filter (${layerId}):`, props.filter);
|
|
766
975
|
});
|
|
767
|
-
|
|
976
|
+
createEffect4(async () => {
|
|
768
977
|
if (!props.featureState || props.featureState.id === null) return;
|
|
769
978
|
!ctx.map.isStyleLoaded() && await ctx.map.once("styledata");
|
|
770
979
|
ctx.map.removeFeatureState({
|
|
@@ -780,6 +989,44 @@ var Layer = (props) => {
|
|
|
780
989
|
props.featureState.state
|
|
781
990
|
);
|
|
782
991
|
});
|
|
992
|
+
createEffect4(() => {
|
|
993
|
+
if (!props.pulse) return;
|
|
994
|
+
const configs = props.pulse === true ? [{}] : Array.isArray(props.pulse) ? props.pulse : [props.pulse];
|
|
995
|
+
const resolved = configs.map((config) => {
|
|
996
|
+
const { property, from, to, duration, waveform } = {
|
|
997
|
+
...PULSE_DEFAULTS,
|
|
998
|
+
...config
|
|
999
|
+
};
|
|
1000
|
+
const isColor = typeof from === "string" || typeof to === "string";
|
|
1001
|
+
const [a, b] = waveform === "in" ? [to, from] : [from, to];
|
|
1002
|
+
return {
|
|
1003
|
+
property,
|
|
1004
|
+
duration,
|
|
1005
|
+
waveform,
|
|
1006
|
+
isColor,
|
|
1007
|
+
fromNum: isColor ? 0 : a,
|
|
1008
|
+
toNum: isColor ? 0 : b,
|
|
1009
|
+
fromRgba: isColor ? toRgbaComponents(a) : null,
|
|
1010
|
+
toRgba: isColor ? toRgbaComponents(b) : null
|
|
1011
|
+
};
|
|
1012
|
+
});
|
|
1013
|
+
const start = performance.now();
|
|
1014
|
+
let frameId;
|
|
1015
|
+
const loop = (now) => {
|
|
1016
|
+
const elapsed = now - start;
|
|
1017
|
+
resolved.forEach((cfg) => {
|
|
1018
|
+
const t = pulseShape(elapsed, cfg.duration, cfg.waveform);
|
|
1019
|
+
const value = cfg.isColor ? lerpColor(cfg.fromRgba, cfg.toRgba, t) : cfg.fromNum + (cfg.toNum - cfg.fromNum) * t;
|
|
1020
|
+
ctx.map.setPaintProperty(layerId, cfg.property, value, {
|
|
1021
|
+
validate: false
|
|
1022
|
+
});
|
|
1023
|
+
});
|
|
1024
|
+
frameId = window.requestAnimationFrame(loop);
|
|
1025
|
+
};
|
|
1026
|
+
frameId = window.requestAnimationFrame(loop);
|
|
1027
|
+
debug(`Start Pulse (${layerId}):`, props.pulse);
|
|
1028
|
+
onCleanup3(() => window.cancelAnimationFrame(frameId));
|
|
1029
|
+
});
|
|
783
1030
|
onCleanup3(() => ctx.map?.getLayer(layerId) && ctx.map?.removeLayer(layerId));
|
|
784
1031
|
return props.children;
|
|
785
1032
|
};
|
|
@@ -934,10 +1181,10 @@ var Layer3D = (props) => {
|
|
|
934
1181
|
};
|
|
935
1182
|
|
|
936
1183
|
// src/components/Atmosphere/index.tsx
|
|
937
|
-
import { onCleanup as onCleanup5, createEffect as
|
|
1184
|
+
import { onCleanup as onCleanup5, createEffect as createEffect5 } from "solid-js";
|
|
938
1185
|
var Atmosphere = (props) => {
|
|
939
1186
|
const [ctx] = useMapContext();
|
|
940
|
-
|
|
1187
|
+
createEffect5(() => {
|
|
941
1188
|
if (ctx.isMapLibre) ctx.map.setSky?.(props.style || {});
|
|
942
1189
|
else ctx.map.setFog(props.style || {});
|
|
943
1190
|
});
|
|
@@ -953,7 +1200,7 @@ var Atmosphere = (props) => {
|
|
|
953
1200
|
|
|
954
1201
|
// src/components/Terrain/index.tsx
|
|
955
1202
|
import {
|
|
956
|
-
createEffect as
|
|
1203
|
+
createEffect as createEffect6,
|
|
957
1204
|
createUniqueId as createUniqueId4,
|
|
958
1205
|
onCleanup as onCleanup6
|
|
959
1206
|
} from "solid-js";
|
|
@@ -970,7 +1217,7 @@ var Terrain = (props) => {
|
|
|
970
1217
|
});
|
|
971
1218
|
ctx.map.sourceIdList.push(sourceId);
|
|
972
1219
|
}
|
|
973
|
-
|
|
1220
|
+
createEffect6(() => {
|
|
974
1221
|
ctx.map.setTerrain({
|
|
975
1222
|
exaggeration: props.exaggeration || 1,
|
|
976
1223
|
source: props?.source || sourceId
|
|
@@ -981,13 +1228,13 @@ var Terrain = (props) => {
|
|
|
981
1228
|
};
|
|
982
1229
|
|
|
983
1230
|
// src/components/Control/index.tsx
|
|
984
|
-
import { createSignal as createSignal3, createEffect as
|
|
1231
|
+
import { createSignal as createSignal3, createEffect as createEffect8, splitProps, untrack } from "solid-js";
|
|
985
1232
|
|
|
986
1233
|
// src/lib/createMapControl.ts
|
|
987
|
-
import { createEffect as
|
|
1234
|
+
import { createEffect as createEffect7, onCleanup as onCleanup7 } from "solid-js";
|
|
988
1235
|
var useControlPosition = (control, position) => {
|
|
989
1236
|
const [ctx] = useMapContext();
|
|
990
|
-
|
|
1237
|
+
createEffect7(() => {
|
|
991
1238
|
const c = control();
|
|
992
1239
|
const pos = position?.();
|
|
993
1240
|
if (!c) return;
|
|
@@ -1014,7 +1261,7 @@ var Control = (props) => {
|
|
|
1014
1261
|
["logo", ctx.mapLib.LogoControl],
|
|
1015
1262
|
["terrain", ctx.mapLib.TerrainControl]
|
|
1016
1263
|
]);
|
|
1017
|
-
|
|
1264
|
+
createEffect8(() => {
|
|
1018
1265
|
untrack(
|
|
1019
1266
|
() => control() && ctx.map.hasControl(control()) && ctx.map.removeControl(control())
|
|
1020
1267
|
);
|
|
@@ -1027,7 +1274,7 @@ var Control = (props) => {
|
|
|
1027
1274
|
};
|
|
1028
1275
|
|
|
1029
1276
|
// src/components/Marker/index.tsx
|
|
1030
|
-
import { onCleanup as onCleanup8, createEffect as
|
|
1277
|
+
import { onCleanup as onCleanup8, createEffect as createEffect9, splitProps as splitProps2 } from "solid-js";
|
|
1031
1278
|
var Marker = (props) => {
|
|
1032
1279
|
if (!props.lngLat) throw new Error("Marker - lngLat is required");
|
|
1033
1280
|
const [update, create_popup, create_marker] = splitProps2(
|
|
@@ -1039,7 +1286,7 @@ var Marker = (props) => {
|
|
|
1039
1286
|
let marker = null;
|
|
1040
1287
|
let popup = null;
|
|
1041
1288
|
const LNGLAT = update.lngLat;
|
|
1042
|
-
|
|
1289
|
+
createEffect9(() => {
|
|
1043
1290
|
if (!ctx.map) return;
|
|
1044
1291
|
popup?.remove();
|
|
1045
1292
|
popup = new ctx.mapLib.Popup({
|
|
@@ -1047,31 +1294,31 @@ var Marker = (props) => {
|
|
|
1047
1294
|
focusAfterOpen: false,
|
|
1048
1295
|
...create_popup.popup
|
|
1049
1296
|
}).on("open", () => create_popup.onOpen?.()).on("close", () => create_popup.onClose?.());
|
|
1050
|
-
|
|
1297
|
+
createEffect9(() => {
|
|
1051
1298
|
if (update.children === void 0) return;
|
|
1052
1299
|
typeof update.children === "string" ? popup?.setHTML(update.children) : popup?.setDOMContent(update.children);
|
|
1053
1300
|
marker?.setPopup(popup);
|
|
1054
1301
|
});
|
|
1055
|
-
|
|
1302
|
+
createEffect9(
|
|
1056
1303
|
() => update.showPopup !== popup?.isOpen() && marker?.togglePopup()
|
|
1057
1304
|
);
|
|
1058
1305
|
});
|
|
1059
|
-
|
|
1306
|
+
createEffect9(() => {
|
|
1060
1307
|
if (!ctx.map) return;
|
|
1061
1308
|
marker?.remove();
|
|
1062
1309
|
marker = new ctx.mapLib.Marker(create_marker.options).on("dragstart", () => create_marker.onDragStart?.()).on("dragend", () => create_marker.onDragEnd?.()).on("drag", () => create_marker.onDrag?.(marker?.getLngLat().toArray())).setPopup(popup).setLngLat(LNGLAT).addTo(ctx.map);
|
|
1063
|
-
|
|
1310
|
+
createEffect9(
|
|
1064
1311
|
() => update.showPopup !== popup?.isOpen() && marker?.togglePopup()
|
|
1065
1312
|
);
|
|
1066
1313
|
});
|
|
1067
|
-
|
|
1068
|
-
|
|
1314
|
+
createEffect9(() => marker?.setLngLat(update.lngLat));
|
|
1315
|
+
createEffect9(() => marker?.setDraggable(update.draggable));
|
|
1069
1316
|
onCleanup8(() => marker?.remove());
|
|
1070
1317
|
return null;
|
|
1071
1318
|
};
|
|
1072
1319
|
|
|
1073
1320
|
// src/components/Popup/index.tsx
|
|
1074
|
-
import { onCleanup as onCleanup9, createEffect as
|
|
1321
|
+
import { onCleanup as onCleanup9, createEffect as createEffect10, splitProps as splitProps3 } from "solid-js";
|
|
1075
1322
|
var Popup = (props) => {
|
|
1076
1323
|
if (!props.trackPointer && !props.lngLat)
|
|
1077
1324
|
throw new Error("Popup - lngLat or trackPointer is required");
|
|
@@ -1082,7 +1329,7 @@ var Popup = (props) => {
|
|
|
1082
1329
|
]);
|
|
1083
1330
|
const [ctx] = useMapContext();
|
|
1084
1331
|
let popup;
|
|
1085
|
-
|
|
1332
|
+
createEffect10(() => {
|
|
1086
1333
|
if (!ctx.map) return;
|
|
1087
1334
|
popup?.remove();
|
|
1088
1335
|
popup = new ctx.mapLib.Popup({
|
|
@@ -1090,10 +1337,10 @@ var Popup = (props) => {
|
|
|
1090
1337
|
focusAfterOpen: false,
|
|
1091
1338
|
...create.options
|
|
1092
1339
|
}).on("open", () => create.onOpen?.()).on("close", () => create.onClose?.()).addTo(ctx.map);
|
|
1093
|
-
|
|
1340
|
+
createEffect10(
|
|
1094
1341
|
() => update.trackPointer ? popup.trackPointer() : popup.setLngLat(update.lngLat)
|
|
1095
1342
|
);
|
|
1096
|
-
|
|
1343
|
+
createEffect10(
|
|
1097
1344
|
() => typeof update.children === "string" ? popup.setHTML(update.children) : popup.setDOMContent(update.children)
|
|
1098
1345
|
);
|
|
1099
1346
|
});
|
|
@@ -1105,63 +1352,189 @@ var Popup = (props) => {
|
|
|
1105
1352
|
import {
|
|
1106
1353
|
onCleanup as onCleanup10,
|
|
1107
1354
|
createSignal as createSignal4,
|
|
1108
|
-
createEffect as
|
|
1355
|
+
createEffect as createEffect11,
|
|
1109
1356
|
untrack as untrack2
|
|
1110
1357
|
} from "solid-js";
|
|
1358
|
+
|
|
1359
|
+
// src/components/Image/sdf.ts
|
|
1360
|
+
var INF = 1e20;
|
|
1361
|
+
var ALPHA_TABLE = new Float64Array(256);
|
|
1362
|
+
for (let i = 0; i < 256; i++) {
|
|
1363
|
+
const d = 0.5 - Math.pow(i / 255, 1 / 2.2);
|
|
1364
|
+
ALPHA_TABLE[i] = d * Math.abs(d);
|
|
1365
|
+
}
|
|
1366
|
+
ALPHA_TABLE[255] = -INF;
|
|
1367
|
+
function edt1d(grid, offset, stride, length3, f, v, z) {
|
|
1368
|
+
v[0] = 0;
|
|
1369
|
+
z[0] = -INF;
|
|
1370
|
+
z[1] = INF;
|
|
1371
|
+
f[0] = grid[offset];
|
|
1372
|
+
for (let q = 1, k = 0, s = 0; q < length3; q++) {
|
|
1373
|
+
f[q] = grid[offset + q * stride];
|
|
1374
|
+
const q2 = q * q;
|
|
1375
|
+
do {
|
|
1376
|
+
const r = v[k];
|
|
1377
|
+
s = (f[q] - f[r] + q2 - r * r) / (q - r) / 2;
|
|
1378
|
+
} while (s <= z[k] && --k > -1);
|
|
1379
|
+
k++;
|
|
1380
|
+
v[k] = q;
|
|
1381
|
+
z[k] = s;
|
|
1382
|
+
z[k + 1] = INF;
|
|
1383
|
+
}
|
|
1384
|
+
for (let q = 0, k = 0; q < length3; q++) {
|
|
1385
|
+
while (z[k + 1] < q) k++;
|
|
1386
|
+
const r = v[k];
|
|
1387
|
+
const qr = q - r;
|
|
1388
|
+
grid[offset + q * stride] = f[r] + qr * qr;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
function edt(data, width, height, f, v, z) {
|
|
1392
|
+
for (let x = 0; x < width; x++) edt1d(data, x, width, height, f, v, z);
|
|
1393
|
+
for (let y = 0; y < height; y++) edt1d(data, y * width, 1, width, f, v, z);
|
|
1394
|
+
}
|
|
1395
|
+
var sdfPadding = ({ radius = 8 } = {}) => Math.ceil(radius);
|
|
1396
|
+
function toSDF(imageData, { radius = 8, cutoff = 0.25 } = {}) {
|
|
1397
|
+
const { width, height } = imageData;
|
|
1398
|
+
const len = width * height;
|
|
1399
|
+
const gridOuter = new Float64Array(len).fill(INF);
|
|
1400
|
+
const gridInner = new Float64Array(len);
|
|
1401
|
+
const size = Math.max(width, height);
|
|
1402
|
+
const f = new Float64Array(size);
|
|
1403
|
+
const v = new Uint16Array(size);
|
|
1404
|
+
const z = new Float64Array(size + 1);
|
|
1405
|
+
const src = imageData.data;
|
|
1406
|
+
for (let i = 0, j = 3; i < len; i++, j += 4) {
|
|
1407
|
+
const a = src[j];
|
|
1408
|
+
if (a === 0) continue;
|
|
1409
|
+
const t = ALPHA_TABLE[a];
|
|
1410
|
+
gridOuter[i] = Math.max(0, t);
|
|
1411
|
+
gridInner[i] = Math.max(0, -t);
|
|
1412
|
+
}
|
|
1413
|
+
edt(gridOuter, width, height, f, v, z);
|
|
1414
|
+
edt(gridInner, width, height, f, v, z);
|
|
1415
|
+
const scale = 255 / radius;
|
|
1416
|
+
const base = 255 * (1 - cutoff);
|
|
1417
|
+
const out = new Uint8ClampedArray(len * 4);
|
|
1418
|
+
for (let i = 0, j = 0; i < len; i++, j += 4) {
|
|
1419
|
+
const d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
|
|
1420
|
+
const value = Math.max(0, Math.min(255, Math.round(base - scale * d)));
|
|
1421
|
+
out[j] = out[j + 1] = out[j + 2] = 255;
|
|
1422
|
+
out[j + 3] = value;
|
|
1423
|
+
}
|
|
1424
|
+
return { width, height, data: out };
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/components/Image/shapes.ts
|
|
1111
1428
|
var PATTERN = {
|
|
1112
1429
|
diagonal_l: { size: 20, path: "M20 0 0 20M-10 10 10-10M10 30 30 10" },
|
|
1113
1430
|
diagonal_r: { size: 20, path: "M0 0 20 20M30 10 10-10M10 30-10 10" },
|
|
1114
1431
|
horizontal: { size: 14, path: "M7 0V20" },
|
|
1115
1432
|
vertical: { size: 14, path: "M0 7H20" },
|
|
1433
|
+
// A single centered "+" per tile, tiling into a repeating grid of crosses — not to be
|
|
1434
|
+
// confused with `SYMBOL.cross` (a single bold "+" icon) below.
|
|
1116
1435
|
cross: { size: 18, path: "M9 0V18M0 9H18" },
|
|
1117
1436
|
hash: { size: 30, path: "M15 0 30 15 15 30 0 15Z" },
|
|
1118
1437
|
chevron_h: { size: 20, path: "M-5 5 0 10 10 0 20 10 25 5M0 30 10 20 20 30" },
|
|
1119
1438
|
chevron_v: { size: 20, path: "M5-5 10 0 0 10 10 20 5 25M25 15 20 10 25 5" },
|
|
1120
1439
|
square: { size: 20, path: "M8 8H12V12H8Z", fill: true },
|
|
1121
1440
|
hex: { size: 50, path: "M0 0V50L50 25ZM50 0V50L0 25ZM25 0V50" },
|
|
1441
|
+
// Two arcs swept almost all the way around (`A2.5 2.5 0 1 1 ...` to a point a hair past the
|
|
1442
|
+
// start) rather than a `<circle>`, since this path is later stroked/filled via `Path2D`,
|
|
1443
|
+
// which has no dedicated circle primitive. A single dot per tile — not to be confused with
|
|
1444
|
+
// `SYMBOL.circle` (a single full-bleed circle icon) below.
|
|
1122
1445
|
circle: {
|
|
1123
1446
|
size: 25,
|
|
1124
1447
|
path: "M8 10A2.5 2.5 0 118.01 10M18 15A2.5 2.5 0 1018.01 15",
|
|
1125
1448
|
fill: true
|
|
1126
|
-
}
|
|
1449
|
+
},
|
|
1450
|
+
// `horizontal` and `vertical` combined into one crossed-line tile.
|
|
1451
|
+
grid: { size: 20, path: "M10 0V20M0 10H20" },
|
|
1452
|
+
// Two rows of offset (running-bond) bricks per tile, so the offset continues correctly
|
|
1453
|
+
// when tiles stack — row two's joints fall at the quarter-points, not centered, to match
|
|
1454
|
+
// a real brick course rather than just checkerboarding.
|
|
1455
|
+
brick: {
|
|
1456
|
+
size: 40,
|
|
1457
|
+
path: "M0 0H40M0 20H40M0 40H40M0 0V20M20 0V20M40 0V20M10 20V40M30 20V40"
|
|
1458
|
+
},
|
|
1459
|
+
// Two wavy horizontal lines per tile (quadratic-bezier "hump" repeated via the `T` shorthand),
|
|
1460
|
+
// for the wave/water fill texture common in geologic and land-cover maps.
|
|
1461
|
+
wave: { size: 20, path: "M0 5Q5 0 10 5T20 5M0 15Q5 10 10 15T20 15" }
|
|
1127
1462
|
};
|
|
1128
1463
|
var patternList = Object.keys(PATTERN);
|
|
1464
|
+
var svg = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">${body}</svg>`;
|
|
1465
|
+
var SYMBOL = {
|
|
1466
|
+
square: svg('<rect x="8" y="8" width="48" height="48" fill="#000"/>'),
|
|
1467
|
+
circle: svg('<circle cx="32" cy="32" r="28" fill="#000"/>'),
|
|
1468
|
+
triangle: svg('<polygon points="32,6 60,56 4,56" fill="#000"/>'),
|
|
1469
|
+
diamond: svg('<polygon points="32,4 60,32 32,60 4,32" fill="#000"/>'),
|
|
1470
|
+
pentagon: svg('<polygon points="32,4 59,23 48,55 16,55 5,23" fill="#000"/>'),
|
|
1471
|
+
hexagon: svg('<polygon points="32,4 56,18 56,46 32,60 8,46 8,18" fill="#000"/>'),
|
|
1472
|
+
// A square with corners cut at 2/7 of its side, the standard construction for a regular
|
|
1473
|
+
// octagon (stop-sign proportions) without resorting to trig.
|
|
1474
|
+
octagon: svg(
|
|
1475
|
+
'<polygon points="20,4 44,4 60,20 60,44 44,60 20,60 4,44 4,20" fill="#000"/>'
|
|
1476
|
+
),
|
|
1477
|
+
cross: svg(
|
|
1478
|
+
'<polygon points="20,4 44,4 44,20 60,20 60,44 44,44 44,60 20,60 20,44 4,44 4,20 20,20" fill="#000"/>'
|
|
1479
|
+
),
|
|
1480
|
+
// The same 12-point "+" as `cross` above, rotated 45° about the center.
|
|
1481
|
+
x: svg(
|
|
1482
|
+
'<polygon points="43,4 60,21 49,32 60,43 43,60 32,49 21,60 4,43 15,32 4,21 21,4 32,15" fill="#000"/>'
|
|
1483
|
+
),
|
|
1484
|
+
// A regular 5-pointed star: outer vertices every 72° (radius 30) alternating with inner
|
|
1485
|
+
// vertices every 72° offset by 36° (radius ~11.5, the usual ratio for a balanced star).
|
|
1486
|
+
star: svg(
|
|
1487
|
+
'<polygon points="32,2 39,23 61,23 43,36 50,56 32,44 14,56 21,36 4,23 25,23" fill="#000"/>'
|
|
1488
|
+
)
|
|
1489
|
+
};
|
|
1490
|
+
var symbolList = Object.keys(SYMBOL);
|
|
1491
|
+
var wrapSymbolPath = (d) => svg(`<path d="${d}" fill="#000"/>`);
|
|
1492
|
+
|
|
1493
|
+
// src/components/Image/index.tsx
|
|
1129
1494
|
var MGL_Image = (props) => {
|
|
1130
1495
|
const [ctx] = useMapContext();
|
|
1131
1496
|
const [size, setSize] = createSignal4({ width: 0, height: 0 });
|
|
1497
|
+
let latestImage = null;
|
|
1132
1498
|
const debug = (text, value) => {
|
|
1133
1499
|
ctx.map.debug && console.debug("%c[MapGL]", "color: #10b981", text, value || "");
|
|
1134
1500
|
};
|
|
1501
|
+
const handleStyleLoad = () => {
|
|
1502
|
+
if (!latestImage || !ctx.map || ctx.map.hasImage(props.id)) return;
|
|
1503
|
+
ctx.map.addImage(props.id, latestImage.data, latestImage.ops);
|
|
1504
|
+
debug("Re-Add Image:", props.id);
|
|
1505
|
+
};
|
|
1506
|
+
ctx.map.on("style.load", handleStyleLoad);
|
|
1135
1507
|
onCleanup10(() => {
|
|
1508
|
+
ctx.map?.off("style.load", handleStyleLoad);
|
|
1136
1509
|
ctx.map?.hasImage(props.id) && ctx.map?.removeImage(props.id);
|
|
1137
1510
|
debug("Remove Image:", props.id);
|
|
1138
1511
|
});
|
|
1139
|
-
|
|
1512
|
+
createEffect11(() => {
|
|
1140
1513
|
if (!props.id) throw new Error("Image - ID is required");
|
|
1141
|
-
if (!props.source && !props.pattern)
|
|
1142
|
-
throw new Error("Image - Image or
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1514
|
+
if (!props.source && !props.pattern && !props.symbol)
|
|
1515
|
+
throw new Error("Image - Image, Pattern or Symbol is required");
|
|
1516
|
+
const pixelRatio = Math.max(1, Math.round(window.devicePixelRatio || 2));
|
|
1517
|
+
const ops = props.pattern ? { pixelRatio, ...props.options } : props.sdf ? { ...props.options, sdf: true } : props.options;
|
|
1518
|
+
const symbolSource = props.symbol && (SYMBOL[props.symbol] || (props.symbol.trimStart().startsWith("<svg") ? props.symbol : wrapSymbolPath(props.symbol)));
|
|
1519
|
+
_loadImage(props.source || symbolSource || _createPattern(props.pattern, pixelRatio), (data, autoPixelRatio) => {
|
|
1520
|
+
const finalOps = autoPixelRatio ? { ...ops, pixelRatio: autoPixelRatio } : ops;
|
|
1145
1521
|
const { width, height } = data;
|
|
1146
1522
|
if (ctx.map && !ctx.map.hasImage(props.id))
|
|
1147
|
-
ctx.map.addImage(props.id, data,
|
|
1523
|
+
ctx.map.addImage(props.id, data, finalOps);
|
|
1148
1524
|
if (!props.pattern && untrack2(() => width === size().width && height === size().height)) {
|
|
1149
1525
|
ctx.map.updateImage(props.id, data);
|
|
1150
1526
|
ctx.map.triggerRepaint();
|
|
1151
1527
|
} else {
|
|
1152
1528
|
ctx.map.removeImage(props.id);
|
|
1153
|
-
ctx.map.addImage(props.id, data,
|
|
1529
|
+
ctx.map.addImage(props.id, data, finalOps);
|
|
1154
1530
|
}
|
|
1155
1531
|
setSize({ width, height });
|
|
1532
|
+
latestImage = { data, ops: finalOps };
|
|
1156
1533
|
debug("Add Image:", props.id);
|
|
1157
|
-
ctx.map.on("style.load", () => {
|
|
1158
|
-
if (ctx.map.hasImage(props.id)) return;
|
|
1159
|
-
ctx.map.addImage(props.id, data, ops);
|
|
1160
|
-
debug("Re-Add Image:", props.id);
|
|
1161
|
-
});
|
|
1162
1534
|
});
|
|
1163
1535
|
});
|
|
1164
1536
|
const _loadImage = (image, callback) => {
|
|
1537
|
+
const sdfOpts = props.sdf ? typeof props.sdf === "object" ? props.sdf : {} : null;
|
|
1165
1538
|
if (typeof image == "string" && image?.trimStart().startsWith("<svg")) {
|
|
1166
1539
|
image = new DOMParser().parseFromString(image, "image/svg+xml").childNodes[0];
|
|
1167
1540
|
}
|
|
@@ -1184,36 +1557,63 @@ var MGL_Image = (props) => {
|
|
|
1184
1557
|
const img = new Image();
|
|
1185
1558
|
img.crossOrigin = "Anonymous";
|
|
1186
1559
|
img.onload = () => {
|
|
1560
|
+
const floorScale = Math.max(1, 50 / Math.min(img.width, img.height));
|
|
1561
|
+
const pixelRatio = props.options?.pixelRatio ?? Math.max(1, Math.round(window.devicePixelRatio || 1));
|
|
1562
|
+
const scale = floorScale * pixelRatio;
|
|
1563
|
+
const padding = sdfOpts ? sdfPadding(sdfOpts) * pixelRatio : 0;
|
|
1564
|
+
const drawWidth = Math.round(img.width * scale);
|
|
1565
|
+
const drawHeight = Math.round(img.height * scale);
|
|
1187
1566
|
const canvas = document.createElement("canvas");
|
|
1188
|
-
canvas.width =
|
|
1189
|
-
canvas.height =
|
|
1567
|
+
canvas.width = drawWidth + padding * 2;
|
|
1568
|
+
canvas.height = drawHeight + padding * 2;
|
|
1190
1569
|
const ctx2 = canvas.getContext("2d");
|
|
1191
1570
|
ctx2.imageSmoothingEnabled = true;
|
|
1192
|
-
ctx2.drawImage(img,
|
|
1193
|
-
|
|
1571
|
+
ctx2.drawImage(img, padding, padding, drawWidth, drawHeight);
|
|
1572
|
+
const imageData2 = ctx2.getImageData(0, 0, canvas.width, canvas.height);
|
|
1573
|
+
return callback(
|
|
1574
|
+
sdfOpts ? toSDF(imageData2, sdfOpts) : imageData2,
|
|
1575
|
+
pixelRatio
|
|
1576
|
+
);
|
|
1194
1577
|
};
|
|
1195
|
-
img.
|
|
1578
|
+
img.onerror = (event) => console.error(`[MapGL] Image "${props.id}" failed to load:`, event);
|
|
1579
|
+
img.src = image.trimStart().startsWith("<svg") ? "data:image/svg+xml;charset=utf-8," + encodeURIComponent(image) : image;
|
|
1196
1580
|
} else {
|
|
1197
|
-
return callback(imageData);
|
|
1581
|
+
if (!sdfOpts) return callback(imageData);
|
|
1582
|
+
const { width, height } = imageData;
|
|
1583
|
+
const padding = sdfPadding(sdfOpts);
|
|
1584
|
+
const canvas = document.createElement("canvas");
|
|
1585
|
+
canvas.width = width + padding * 2;
|
|
1586
|
+
canvas.height = height + padding * 2;
|
|
1587
|
+
const c = canvas.getContext("2d");
|
|
1588
|
+
c.imageSmoothingEnabled = true;
|
|
1589
|
+
if ("data" in imageData) {
|
|
1590
|
+
c.putImageData(imageData, padding, padding);
|
|
1591
|
+
} else {
|
|
1592
|
+
c.drawImage(imageData, padding, padding, width, height);
|
|
1593
|
+
}
|
|
1594
|
+
const pixels = c.getImageData(0, 0, canvas.width, canvas.height);
|
|
1595
|
+
return callback(toSDF(pixels, sdfOpts));
|
|
1198
1596
|
}
|
|
1199
1597
|
});
|
|
1200
1598
|
};
|
|
1201
|
-
const _createPattern = (pattern) => {
|
|
1599
|
+
const _createPattern = (pattern, pixelRatio = 2) => {
|
|
1202
1600
|
const p = PATTERN[pattern.type];
|
|
1601
|
+
const size2 = p.size * pixelRatio;
|
|
1203
1602
|
return {
|
|
1204
|
-
width:
|
|
1205
|
-
height:
|
|
1206
|
-
data: new Uint8Array(
|
|
1603
|
+
width: size2,
|
|
1604
|
+
height: size2,
|
|
1605
|
+
data: new Uint8Array(size2 * size2 * 4),
|
|
1207
1606
|
onAdd: function() {
|
|
1208
|
-
|
|
1209
|
-
canvas.width = canvas.height =
|
|
1607
|
+
const canvas = document.createElement("canvas");
|
|
1608
|
+
canvas.width = canvas.height = size2;
|
|
1210
1609
|
this.ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
1610
|
+
this.ctx.scale(pixelRatio, pixelRatio);
|
|
1211
1611
|
},
|
|
1212
1612
|
render: function() {
|
|
1213
1613
|
this.ctx.fillStyle = pattern.background || "transparent";
|
|
1214
|
-
this.ctx.fillRect(0, 0,
|
|
1614
|
+
this.ctx.fillRect(0, 0, p.size, p.size);
|
|
1215
1615
|
this.ctx.strokeStyle = this.ctx.fillStyle = pattern.color || "black";
|
|
1216
|
-
this.ctx.lineWidth = pattern.
|
|
1616
|
+
this.ctx.lineWidth = pattern.lineWidth || 1;
|
|
1217
1617
|
this.ctx.stroke(new Path2D(p.path));
|
|
1218
1618
|
if (p.fill) this.ctx.fill(new Path2D(p.path));
|
|
1219
1619
|
this.data = this.ctx.getImageData(0, 0, this.width, this.height).data;
|
|
@@ -1225,7 +1625,7 @@ var MGL_Image = (props) => {
|
|
|
1225
1625
|
};
|
|
1226
1626
|
|
|
1227
1627
|
// src/components/Camera/index.tsx
|
|
1228
|
-
import { createEffect as
|
|
1628
|
+
import { createEffect as createEffect12, onCleanup as onCleanup11, createSignal as createSignal5 } from "solid-js";
|
|
1229
1629
|
var lerp = (a, b, t) => a.map((_, idx) => (1 - t) * a[idx] + t * b[idx]);
|
|
1230
1630
|
var slerp = (a, b, t) => {
|
|
1231
1631
|
const dotProduct = a.map((_, idx) => a[idx] * b[idx]).reduce((m, n) => m + n);
|
|
@@ -1272,7 +1672,7 @@ var Camera = (props) => {
|
|
|
1272
1672
|
isReverse = !isReverse;
|
|
1273
1673
|
animationTime = isReverse ? animationTime - 1e-3 : animationTime + 1e-3;
|
|
1274
1674
|
};
|
|
1275
|
-
|
|
1675
|
+
createEffect12(() => {
|
|
1276
1676
|
props.translate && window.requestAnimationFrame(frame);
|
|
1277
1677
|
});
|
|
1278
1678
|
const options = { duration: 1e3, easing: (n) => n };
|
|
@@ -1307,7 +1707,7 @@ var Camera = (props) => {
|
|
|
1307
1707
|
const onEnd = () => props.rotateGlobe ? rotateGlobe() : rotateViewport();
|
|
1308
1708
|
ctx.map.on("moveend", onEnd).on("dragend", onEnd);
|
|
1309
1709
|
let originalCenter;
|
|
1310
|
-
|
|
1710
|
+
createEffect12(() => {
|
|
1311
1711
|
if (props.rotateGlobe == void 0) return;
|
|
1312
1712
|
if (props.rotateGlobe) {
|
|
1313
1713
|
originalCenter = ctx.map.getCenter();
|
|
@@ -1316,7 +1716,7 @@ var Camera = (props) => {
|
|
|
1316
1716
|
props.resetWhenStopped ? ctx.map.stop().easeTo({ center: originalCenter }) : ctx.map.stop();
|
|
1317
1717
|
}
|
|
1318
1718
|
});
|
|
1319
|
-
|
|
1719
|
+
createEffect12(() => {
|
|
1320
1720
|
if (props.rotateViewport == void 0) return;
|
|
1321
1721
|
props.rotateViewport ? rotateViewport() : props.resetWhenStopped ? ctx.map.stop().resetNorthPitch() : ctx.map.stop();
|
|
1322
1722
|
});
|
|
@@ -1325,10 +1725,10 @@ var Camera = (props) => {
|
|
|
1325
1725
|
};
|
|
1326
1726
|
|
|
1327
1727
|
// src/components/Light/index.tsx
|
|
1328
|
-
import { onCleanup as onCleanup12, createEffect as
|
|
1728
|
+
import { onCleanup as onCleanup12, createEffect as createEffect13 } from "solid-js";
|
|
1329
1729
|
var Light = (props) => {
|
|
1330
1730
|
const [ctx] = useMapContext();
|
|
1331
|
-
|
|
1731
|
+
createEffect13(() => ctx.map.setLight(props.style || {}));
|
|
1332
1732
|
onCleanup12(() => ctx.map?.setLight(null));
|
|
1333
1733
|
return null;
|
|
1334
1734
|
};
|
|
@@ -2285,7 +2685,7 @@ var Draw = (props) => {
|
|
|
2285
2685
|
};
|
|
2286
2686
|
|
|
2287
2687
|
// src/components/DeckOverlay/index.tsx
|
|
2288
|
-
import { createEffect as
|
|
2688
|
+
import { createEffect as createEffect14 } from "solid-js";
|
|
2289
2689
|
var DeckOverlay = (props) => {
|
|
2290
2690
|
const [ctx] = useMapContext();
|
|
2291
2691
|
let overlay;
|
|
@@ -2294,7 +2694,7 @@ var DeckOverlay = (props) => {
|
|
|
2294
2694
|
return overlay;
|
|
2295
2695
|
};
|
|
2296
2696
|
useControlPosition(control);
|
|
2297
|
-
|
|
2697
|
+
createEffect14(() => overlay?.setProps(props.props || {}));
|
|
2298
2698
|
return null;
|
|
2299
2699
|
};
|
|
2300
2700
|
export {
|
|
@@ -2313,8 +2713,13 @@ export {
|
|
|
2313
2713
|
Source,
|
|
2314
2714
|
Terrain,
|
|
2315
2715
|
MapGL as default,
|
|
2716
|
+
disableRasterFade,
|
|
2316
2717
|
patternList,
|
|
2718
|
+
settleAfterIdle,
|
|
2719
|
+
symbolList,
|
|
2720
|
+
toSDF,
|
|
2317
2721
|
useMapContext,
|
|
2318
2722
|
useScene,
|
|
2319
|
-
useSourceId
|
|
2723
|
+
useSourceId,
|
|
2724
|
+
waitForIdleAndSettle
|
|
2320
2725
|
};
|