@tileflow/dom 0.1.0-alpha.0 → 0.1.0-alpha.2
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 +42 -2
- package/REFERENCE.md +11 -0
- package/dist/index.js +578 -393
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@ the map, loads its manifest and style, keeps its theme, colours, camera, annotat
|
|
|
5
5
|
interactions current, and takes everything down again. It draws nothing of its own around the map:
|
|
6
6
|
marker, popup and tooltip views, loading and error states belong to your page.
|
|
7
7
|
|
|
8
|
-
Use it for plain pages and web components. The React, Vue and Svelte packages
|
|
9
|
-
those frameworks.
|
|
8
|
+
Use it for plain pages and web components. The React, Vue and Svelte packages run on it and are the
|
|
9
|
+
easier way in those frameworks.
|
|
10
10
|
|
|
11
11
|
> Related packages and guides: [documentation index](https://raw.githubusercontent.com/tileflow/tileflow-sdk/main/llms.txt).
|
|
12
12
|
|
|
@@ -104,6 +104,46 @@ settles with `null` once the image has loaded.
|
|
|
104
104
|
|
|
105
105
|
See [REFERENCE.md](REFERENCE.md) for every option and controller method.
|
|
106
106
|
|
|
107
|
+
## Extend the map
|
|
108
|
+
|
|
109
|
+
The map is a real MapLibre map: `ready` and `onLoad` give it, and `getMap()` returns it while it
|
|
110
|
+
exists. Add your own layers, sources, controls, events and MapLibre libraries to it.
|
|
111
|
+
|
|
112
|
+
<!-- docs:check -->
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import {createTileflowMap} from '@tileflow/dom';
|
|
116
|
+
|
|
117
|
+
const map = createTileflowMap(document.querySelector<HTMLElement>('#map')!, {
|
|
118
|
+
source: {map: 'madrid'},
|
|
119
|
+
onLoad(mapLibreMap) {
|
|
120
|
+
mapLibreMap.on('style.load', () => addRoute(mapLibreMap));
|
|
121
|
+
addRoute(mapLibreMap);
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
function addRoute(mapLibreMap: NonNullable<ReturnType<typeof map.getMap>>) {
|
|
126
|
+
if (mapLibreMap.getSource('route')) return;
|
|
127
|
+
mapLibreMap.addSource('route', {
|
|
128
|
+
type: 'geojson',
|
|
129
|
+
data: {
|
|
130
|
+
type: 'LineString',
|
|
131
|
+
coordinates: [
|
|
132
|
+
[-3.7038, 40.4168],
|
|
133
|
+
[-3.6921, 40.4138],
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
mapLibreMap.addLayer({id: 'route', type: 'line', source: 'route', paint: {'line-width': 4}});
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Tileflow owns the style it applied, its protocols, its hosted requests and session, and the
|
|
142
|
+
controls it adds. A theme change may replace the style while keeping the camera, so add your own
|
|
143
|
+
layers and sources again on `style.load`, as above, or pass them as `dataSources` and `dataOverlays`,
|
|
144
|
+
which the map puts back after every theme change. Keep the map's lifetime with the controller:
|
|
145
|
+
`destroy()` takes everything down, and `getMap().remove()` stops the controller too.
|
|
146
|
+
|
|
107
147
|
## Next steps
|
|
108
148
|
|
|
109
149
|
- [Show a popup on click](https://tileflow.dev/docs/examples/show-a-popup-on-click)
|
package/REFERENCE.md
CHANGED
|
@@ -73,6 +73,17 @@ with no renderer keeps the default view.
|
|
|
73
73
|
| `getInteractionState()`, `getRenderTargets()` | The open popup; the view elements the map handed out. |
|
|
74
74
|
| `destroy()` | Takes the map down, removes the capture attributes and stops every listener. Later calls do nothing. |
|
|
75
75
|
|
|
76
|
+
Use `getMap()` or the map passed to `onLoad` for application-owned MapLibre sources, layers,
|
|
77
|
+
controls and events. Tileflow owns the style; theme changes may replace it, so reinstall your
|
|
78
|
+
own layers on `style.load`, or use `dataSources` and `dataOverlays`. Calling the MapLibre map's
|
|
79
|
+
`remove()` also stops the Tileflow controller. Prefer `destroy()` for normal application cleanup.
|
|
80
|
+
Destroying during loading rejects `ready` and prevents a late response from mounting a map.
|
|
81
|
+
|
|
82
|
+
If optional interactions cannot start, the map remains mounted and reports an interaction
|
|
83
|
+
diagnostic. Changing their inputs retries initialization. A failed theme change keeps the
|
|
84
|
+
applied theme and its system-colour-scheme following; an unknown name does not cancel a valid
|
|
85
|
+
pending change. `getStatus()` reports the failure until the requested selection changes.
|
|
86
|
+
|
|
76
87
|
## `getTileflowMapInitialStatus(options, controlledInteractionState?)`
|
|
77
88
|
|
|
78
89
|
The status a map with these options has before it mounts: its declarative diagnostics, `error` or
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
// src/map.ts
|
|
2
2
|
import {
|
|
3
|
-
attachTileflowAtmosphere,
|
|
4
3
|
attachTileflowBranding,
|
|
4
|
+
createTileflowAppearanceBinding,
|
|
5
|
+
createTileflowContourRequestScope,
|
|
6
|
+
createTileflowNavigationControlBinding,
|
|
7
|
+
getTileflowSystemColorScheme,
|
|
8
|
+
reportTileflowMapError,
|
|
9
|
+
resolveTileflowCameraFit,
|
|
10
|
+
subscribeTileflowSystemColorScheme,
|
|
11
|
+
tileflowMaxPitch
|
|
12
|
+
} from "@tileflow/core/browser";
|
|
13
|
+
import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
|
|
14
|
+
import {
|
|
15
|
+
attachTileflowAtmosphere,
|
|
5
16
|
attachTileflowFairUseNotice,
|
|
6
17
|
attachTileflowHorizonQueries,
|
|
7
18
|
attachTileflowLabelPlacement,
|
|
@@ -9,26 +20,17 @@ import {
|
|
|
9
20
|
attachTileflowMapLifecycle,
|
|
10
21
|
attachTileflowNauticalLights,
|
|
11
22
|
attachTileflowNotices,
|
|
12
|
-
createTileflowAppearanceBinding,
|
|
13
|
-
createTileflowContourRequestScope,
|
|
14
|
-
createTileflowNavigationControlBinding,
|
|
15
23
|
createTileflowSessionStarter,
|
|
16
24
|
createTileflowThemeController,
|
|
17
25
|
createTileflowTransformRequest,
|
|
18
|
-
getTileflowSystemColorScheme,
|
|
19
26
|
loadTileflowRuntimeStyle,
|
|
20
27
|
loadTileflowStyleFonts,
|
|
21
28
|
registerTileflowContourProtocol,
|
|
22
29
|
registerTileflowPathGroundProtocol,
|
|
23
30
|
registerTileflowPmtilesProtocol,
|
|
24
31
|
registerTileflowWorldRequestBridge,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
subscribeTileflowSystemColorScheme,
|
|
28
|
-
tileflowCanvasContextAttributes,
|
|
29
|
-
tileflowMaxPitch
|
|
30
|
-
} from "@tileflow/core/browser";
|
|
31
|
-
import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
|
|
32
|
+
tileflowCanvasContextAttributes
|
|
33
|
+
} from "@tileflow/core/internal";
|
|
32
34
|
import {
|
|
33
35
|
createTileflowSessionController,
|
|
34
36
|
defaultTileflowManifestUrl,
|
|
@@ -181,6 +183,8 @@ function imageModeMapInteractionDiagnostic(annotationCount, bindingCount, hasAdd
|
|
|
181
183
|
|
|
182
184
|
// src/maplibre.ts
|
|
183
185
|
var bundlerWorkerUrlKey = /* @__PURE__ */ Symbol.for("tileflow.maplibre.workerUrl");
|
|
186
|
+
var importMapLibre = () => import("maplibre-gl");
|
|
187
|
+
var loadMapLibreModule = importMapLibre;
|
|
184
188
|
var mapLibrePromise;
|
|
185
189
|
var mapLibre;
|
|
186
190
|
var workerUrl;
|
|
@@ -202,7 +206,7 @@ function configureTileflowMapLibre(configuration) {
|
|
|
202
206
|
if (mapLibre) applyWorkerUrl(mapLibre);
|
|
203
207
|
}
|
|
204
208
|
function loadTileflowMapLibre() {
|
|
205
|
-
mapLibrePromise ??=
|
|
209
|
+
mapLibrePromise ??= loadMapLibreModule().then(resolveMapLibreModule).then(configureTileflowMapLibreModule).catch((error) => {
|
|
206
210
|
mapLibrePromise = void 0;
|
|
207
211
|
throw error;
|
|
208
212
|
});
|
|
@@ -337,6 +341,32 @@ function throwCollected(errors, message) {
|
|
|
337
341
|
if (errors.length > 1) throw new AggregateError(errors, message);
|
|
338
342
|
}
|
|
339
343
|
|
|
344
|
+
// src/scope.ts
|
|
345
|
+
function createTileflowResourceScope() {
|
|
346
|
+
const cleanups = [];
|
|
347
|
+
let disposed = false;
|
|
348
|
+
const scope = {
|
|
349
|
+
add(cleanup) {
|
|
350
|
+
if (disposed) runTileflowCleanups([cleanup]);
|
|
351
|
+
else cleanups.push(cleanup);
|
|
352
|
+
},
|
|
353
|
+
child() {
|
|
354
|
+
const child = createTileflowResourceScope();
|
|
355
|
+
scope.add(() => child.dispose());
|
|
356
|
+
return child;
|
|
357
|
+
},
|
|
358
|
+
dispose() {
|
|
359
|
+
if (disposed) return;
|
|
360
|
+
disposed = true;
|
|
361
|
+
runTileflowCleanups(cleanups.splice(0).reverse());
|
|
362
|
+
},
|
|
363
|
+
get disposed() {
|
|
364
|
+
return disposed;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
return scope;
|
|
368
|
+
}
|
|
369
|
+
|
|
340
370
|
// src/views.ts
|
|
341
371
|
function tileflowAnnotationViewContext(target, state) {
|
|
342
372
|
const { annotation } = target;
|
|
@@ -547,13 +577,20 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
547
577
|
let stateBridgeDiagnostic;
|
|
548
578
|
let rendererBridgeDiagnostic;
|
|
549
579
|
let viewBridgeDiagnostic;
|
|
580
|
+
let dataBridgeDiagnostic;
|
|
581
|
+
let interactionStartDiagnostic;
|
|
550
582
|
let annotationDiagnostics = [];
|
|
551
583
|
let semanticDiagnostics = [];
|
|
552
584
|
let reportedDeclarativeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
553
585
|
let imageSize = null;
|
|
554
586
|
let managedImage = null;
|
|
555
587
|
let imageReadinessRun = 0;
|
|
588
|
+
let themeMapIdentity;
|
|
589
|
+
let appliedTheme;
|
|
590
|
+
let failedTheme;
|
|
591
|
+
let validThemeSelection;
|
|
556
592
|
let map = null;
|
|
593
|
+
let mounted;
|
|
557
594
|
let scene;
|
|
558
595
|
let themeController;
|
|
559
596
|
let appearanceBinding;
|
|
@@ -644,6 +681,13 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
644
681
|
}));
|
|
645
682
|
const mapName = runtimeSource.map;
|
|
646
683
|
const manifestUrl = runtimeSource.manifestUrl ?? defaultTileflowManifestUrl;
|
|
684
|
+
const runtimeMapIdentity = `${manifestUrl}\0${mapName}`;
|
|
685
|
+
if (themeMapIdentity !== runtimeMapIdentity) {
|
|
686
|
+
themeMapIdentity = runtimeMapIdentity;
|
|
687
|
+
appliedTheme = void 0;
|
|
688
|
+
failedTheme = void 0;
|
|
689
|
+
validThemeSelection = void 0;
|
|
690
|
+
}
|
|
647
691
|
const preparedAnnotations = memo(
|
|
648
692
|
"annotations",
|
|
649
693
|
[annotations],
|
|
@@ -679,37 +723,75 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
679
723
|
const manifestMap = currentManifest.state === "ready" ? currentManifest.map : null;
|
|
680
724
|
const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
|
|
681
725
|
const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
|
|
726
|
+
const resolveSelection = (selection) => {
|
|
727
|
+
if (!manifestMap) {
|
|
728
|
+
return {
|
|
729
|
+
error: false,
|
|
730
|
+
name: selection && selection !== "system" ? selection : void 0
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
if (themeBlendKey) {
|
|
735
|
+
for (const name of JSON.parse(themeBlendKey)) {
|
|
736
|
+
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
error: false,
|
|
741
|
+
name: resolveTileflowRuntimeTheme(
|
|
742
|
+
manifestMap,
|
|
743
|
+
selection,
|
|
744
|
+
systemColorScheme,
|
|
745
|
+
themeBlendKey ? void 0 : timeOfDay
|
|
746
|
+
).name
|
|
747
|
+
};
|
|
748
|
+
} catch (cause) {
|
|
749
|
+
return { cause, error: true, name: void 0 };
|
|
750
|
+
}
|
|
751
|
+
};
|
|
682
752
|
const themeResolution = memo(
|
|
683
753
|
"themeResolution",
|
|
684
|
-
[
|
|
754
|
+
[
|
|
755
|
+
blendThemeName,
|
|
756
|
+
manifestMap,
|
|
757
|
+
(blendThemeName ?? theme) === "system" ? systemColorScheme : void 0,
|
|
758
|
+
theme,
|
|
759
|
+
themeBlendKey,
|
|
760
|
+
timeOfDay
|
|
761
|
+
],
|
|
762
|
+
() => resolveSelection(blendThemeName ?? theme)
|
|
763
|
+
);
|
|
764
|
+
const themeRequestFailed = failedTheme !== void 0 && failedTheme.selection === theme && failedTheme.theme === themeResolution.name;
|
|
765
|
+
const themeChoice = memo(
|
|
766
|
+
"themeChoice",
|
|
767
|
+
[
|
|
768
|
+
failedTheme,
|
|
769
|
+
manifestMap,
|
|
770
|
+
systemColorScheme,
|
|
771
|
+
themeBlendKey,
|
|
772
|
+
themeRequestFailed,
|
|
773
|
+
themeResolution,
|
|
774
|
+
timeOfDay,
|
|
775
|
+
validThemeSelection
|
|
776
|
+
],
|
|
685
777
|
() => {
|
|
686
|
-
|
|
687
|
-
if (
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
};
|
|
778
|
+
if (blendThemeName) return { resolution: themeResolution, selection: theme };
|
|
779
|
+
if (themeRequestFailed && failedTheme) {
|
|
780
|
+
const { fallback } = failedTheme;
|
|
781
|
+
const resolution = resolveSelection(fallback.selection);
|
|
782
|
+
return resolution.error || resolution.name === failedTheme.theme ? { resolution: { error: false, name: fallback.theme }, selection: fallback.theme } : { resolution, selection: fallback.selection };
|
|
692
783
|
}
|
|
693
|
-
|
|
694
|
-
if (themeBlendKey) {
|
|
695
|
-
for (const name of JSON.parse(themeBlendKey)) {
|
|
696
|
-
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
784
|
+
if (themeResolution.error) {
|
|
699
785
|
return {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
manifestMap,
|
|
703
|
-
selection,
|
|
704
|
-
systemColorScheme,
|
|
705
|
-
themeBlendKey ? void 0 : timeOfDay
|
|
706
|
-
).name
|
|
786
|
+
resolution: resolveSelection(validThemeSelection),
|
|
787
|
+
selection: validThemeSelection
|
|
707
788
|
};
|
|
708
|
-
} catch (cause) {
|
|
709
|
-
return { cause, error: true, name: void 0 };
|
|
710
789
|
}
|
|
790
|
+
return { resolution: themeResolution, selection: theme };
|
|
711
791
|
}
|
|
712
792
|
);
|
|
793
|
+
const themeSelection = themeChoice.selection;
|
|
794
|
+
const shownThemeResolution = themeChoice.resolution;
|
|
713
795
|
const manifestView = resolveTileflowRuntimeView({ manifestMap });
|
|
714
796
|
const manifestCenter = stable(
|
|
715
797
|
"manifestCenter",
|
|
@@ -750,15 +832,18 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
750
832
|
preparedInteractionState
|
|
751
833
|
})
|
|
752
834
|
);
|
|
753
|
-
const runtimeStyle =
|
|
835
|
+
const runtimeStyle = stable(
|
|
754
836
|
"runtimeStyle",
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
837
|
+
memo(
|
|
838
|
+
"runtimeStyle",
|
|
839
|
+
[isImageMode, manifestMap, runtimeSource, systemColorScheme, shownThemeResolution],
|
|
840
|
+
() => isImageMode || shownThemeResolution.error ? null : resolveTileflowRuntimeStyle({
|
|
841
|
+
colorScheme: systemColorScheme,
|
|
842
|
+
manifestMap,
|
|
843
|
+
source: runtimeSource,
|
|
844
|
+
theme: shownThemeResolution.name
|
|
845
|
+
})
|
|
846
|
+
)
|
|
762
847
|
);
|
|
763
848
|
const blendResolution = memo(
|
|
764
849
|
"blendResolution",
|
|
@@ -808,7 +893,7 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
808
893
|
zoom: resolvedZoom
|
|
809
894
|
}) : void 0)
|
|
810
895
|
);
|
|
811
|
-
const runtimeResolutionState = currentManifest.state !== "ready" ? currentManifest.state === "error" ? "error" : "loading" : themeResolution.error ? "error" : isImageMode ? runtimeImageUrl ? "idle" : options.imageUrl === void 0 && imageSize === null ? "loading" : "error" : runtimeStyle ? "idle" : "error";
|
|
896
|
+
const runtimeResolutionState = currentManifest.state !== "ready" ? currentManifest.state === "error" ? "error" : "loading" : themeResolution.error || themeRequestFailed ? "error" : isImageMode ? runtimeImageUrl ? "idle" : options.imageUrl === void 0 && imageSize === null ? "loading" : "error" : runtimeStyle ? "idle" : "error";
|
|
812
897
|
return {
|
|
813
898
|
annotationClusters,
|
|
814
899
|
blendResolution,
|
|
@@ -834,10 +919,14 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
834
919
|
resolvedInteractive,
|
|
835
920
|
resolvedPitch,
|
|
836
921
|
resolvedZoom,
|
|
837
|
-
runtimeMapIdentity
|
|
922
|
+
runtimeMapIdentity,
|
|
838
923
|
runtimeResolutionState,
|
|
839
924
|
runtimeSource,
|
|
925
|
+
manifestReady: manifestMap !== null,
|
|
840
926
|
runtimeStyle,
|
|
927
|
+
shownThemeResolution,
|
|
928
|
+
themeRequestFailed,
|
|
929
|
+
themeSelection,
|
|
841
930
|
themeBlendHold: themeBlend?.hold === true,
|
|
842
931
|
themeBlendKey,
|
|
843
932
|
themeBlendPosition: themeBlend?.position,
|
|
@@ -964,8 +1053,14 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
964
1053
|
image.removeEventListener("error", fail);
|
|
965
1054
|
};
|
|
966
1055
|
});
|
|
967
|
-
|
|
968
|
-
|
|
1056
|
+
if (d.manifestReady && !d.themeResolution.error && !d.themeRequestFailed) {
|
|
1057
|
+
validThemeSelection = options.theme;
|
|
1058
|
+
}
|
|
1059
|
+
if (failedTheme && (failedTheme.selection !== options.theme || failedTheme.theme !== d.themeResolution.name)) {
|
|
1060
|
+
failedTheme = void 0;
|
|
1061
|
+
}
|
|
1062
|
+
effect("systemColorScheme", [d.themeSelection], () => {
|
|
1063
|
+
if (d.themeSelection !== "system") return;
|
|
969
1064
|
systemColorScheme = getTileflowSystemColorScheme();
|
|
970
1065
|
invalidate();
|
|
971
1066
|
return subscribeTileflowSystemColorScheme((scheme) => {
|
|
@@ -1040,7 +1135,25 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1040
1135
|
"reconcileData",
|
|
1041
1136
|
[d.annotationClusters, d.dataOverlays, d.dataSources, d.preparedAnnotations],
|
|
1042
1137
|
() => {
|
|
1043
|
-
dataRuntime
|
|
1138
|
+
if (dataRuntime) reconcileData(dataRuntime);
|
|
1139
|
+
}
|
|
1140
|
+
);
|
|
1141
|
+
effect(
|
|
1142
|
+
"restartInteractions",
|
|
1143
|
+
[
|
|
1144
|
+
d.annotationClusters,
|
|
1145
|
+
d.dataOverlays,
|
|
1146
|
+
d.dataSources,
|
|
1147
|
+
d.hasMarkerRenderer,
|
|
1148
|
+
d.hasPopupRenderer,
|
|
1149
|
+
d.hasTooltipRenderer,
|
|
1150
|
+
d.preparedAnnotations,
|
|
1151
|
+
d.preparedInteractions
|
|
1152
|
+
],
|
|
1153
|
+
() => {
|
|
1154
|
+
if (interactionStartDiagnostic && mounted && !mounted.scope.disposed) {
|
|
1155
|
+
attachInteractions(mounted);
|
|
1156
|
+
}
|
|
1044
1157
|
}
|
|
1045
1158
|
);
|
|
1046
1159
|
effect("reconcileInteractions", [d.preparedInteractions], () => {
|
|
@@ -1100,23 +1213,13 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1100
1213
|
],
|
|
1101
1214
|
() => d.isImageMode ? void 0 : mountMap()
|
|
1102
1215
|
);
|
|
1103
|
-
effect("theme", [d.runtimeStyle, d.themeBlendKey
|
|
1216
|
+
effect("theme", [d.runtimeStyle, d.themeBlendKey], () => {
|
|
1104
1217
|
const controller = themeController;
|
|
1105
1218
|
if (!controller || d.themeBlendKey) return;
|
|
1106
|
-
if (d.themeResolution.error) {
|
|
1107
|
-
void controller.setTheme(controller.getCurrent());
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
1219
|
if (!d.runtimeStyle || controller.getCurrent() === d.runtimeStyle && !controller.getBlend()) {
|
|
1111
1220
|
return;
|
|
1112
1221
|
}
|
|
1113
|
-
|
|
1114
|
-
invalidate();
|
|
1115
|
-
void controller.setTheme(d.runtimeStyle, { transition: options.themeTransition }).then((result) => {
|
|
1116
|
-
if (result.status === "failed") {
|
|
1117
|
-
reportError("theme", result.error ?? new Error("The map theme could not be changed."));
|
|
1118
|
-
}
|
|
1119
|
-
});
|
|
1222
|
+
requestTheme(controller, d.runtimeStyle, d.themeSelection);
|
|
1120
1223
|
});
|
|
1121
1224
|
const themeResolutionCause = d.themeResolution.error ? d.themeResolution.cause : void 0;
|
|
1122
1225
|
effect("themeError", [d.themeResolution.error, themeResolutionCause], () => {
|
|
@@ -1172,6 +1275,19 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1172
1275
|
}
|
|
1173
1276
|
invalidate();
|
|
1174
1277
|
}
|
|
1278
|
+
function reconcileData(runtime) {
|
|
1279
|
+
try {
|
|
1280
|
+
dataBridgeDiagnostic = void 0;
|
|
1281
|
+
runtime.reconcile(dataInput);
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
console.error("Failed to reconcile Tileflow data layers", error);
|
|
1284
|
+
dataBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
1285
|
+
"Tileflow data layers could not be reconciled."
|
|
1286
|
+
);
|
|
1287
|
+
reportDiagnostic(dataBridgeDiagnostic);
|
|
1288
|
+
}
|
|
1289
|
+
invalidate();
|
|
1290
|
+
}
|
|
1175
1291
|
function reconcileInteractions(runtime, bindings) {
|
|
1176
1292
|
try {
|
|
1177
1293
|
semanticBridgeDiagnostic = void 0;
|
|
@@ -1203,10 +1319,10 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1203
1319
|
if (!initialRuntimeStyle) return void 0;
|
|
1204
1320
|
activeRuntimeResource = initialRuntimeStyle;
|
|
1205
1321
|
captureState = "loading";
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
const { mapOptions, resolvedInteractive } = current;
|
|
1322
|
+
const scope = createTileflowResourceScope();
|
|
1323
|
+
const { mapOptions } = current;
|
|
1209
1324
|
const resourceLifetime = new AbortController();
|
|
1325
|
+
scope.add(() => resourceLifetime.abort());
|
|
1210
1326
|
const session = createTileflowSessionController({ source: sessionSource });
|
|
1211
1327
|
const resourceTransform = createTileflowTransformRequest({
|
|
1212
1328
|
getAnalytics: () => current.resolvedAnalytics,
|
|
@@ -1228,337 +1344,400 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1228
1344
|
return prepared;
|
|
1229
1345
|
})
|
|
1230
1346
|
]).then(([maplibregl, prepared]) => {
|
|
1231
|
-
if (
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
runTileflowCleanups([...setupCleanups].reverse());
|
|
1240
|
-
} finally {
|
|
1241
|
-
map = null;
|
|
1242
|
-
interactionRuntime = void 0;
|
|
1243
|
-
semanticRuntime = void 0;
|
|
1244
|
-
interactionCoordinator = void 0;
|
|
1245
|
-
annotationDiagnostics = [];
|
|
1246
|
-
annotationRenderTargets = [];
|
|
1247
|
-
annotationBridgeDiagnostic = void 0;
|
|
1248
|
-
semanticDiagnostics = [];
|
|
1249
|
-
semanticRenderTargets = [];
|
|
1250
|
-
semanticBridgeDiagnostic = void 0;
|
|
1251
|
-
stateBridgeDiagnostic = void 0;
|
|
1252
|
-
rendererBridgeDiagnostic = void 0;
|
|
1253
|
-
invalidate();
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
const sessionStarter = createTileflowSessionStarter({
|
|
1257
|
-
getSessionId: () => session.sessionId,
|
|
1258
|
-
sessionId: session.sessionId,
|
|
1259
|
-
source: sessionSource
|
|
1260
|
-
});
|
|
1261
|
-
const fairUseNotice = attachTileflowFairUseNotice(container);
|
|
1262
|
-
registerCleanup(() => fairUseNotice.dispose());
|
|
1263
|
-
registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1264
|
-
registerTileflowPathGroundProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1265
|
-
registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1266
|
-
const worldRequestBridge = registerTileflowWorldRequestBridge({
|
|
1267
|
-
addProtocol: maplibregl.addProtocol,
|
|
1268
|
-
onNotice: fairUseNotice.update
|
|
1269
|
-
});
|
|
1270
|
-
registerCleanup(() => worldRequestBridge.dispose());
|
|
1271
|
-
const contourRequestScope = createTileflowContourRequestScope({
|
|
1272
|
-
transformRequest: (url, resourceType) => transformRequest(
|
|
1273
|
-
url,
|
|
1274
|
-
resourceType
|
|
1275
|
-
)
|
|
1276
|
-
});
|
|
1277
|
-
registerCleanup(() => contourRequestScope.dispose());
|
|
1278
|
-
const transformRequest = createTileflowTransformRequest({
|
|
1279
|
-
contourRequestScope,
|
|
1280
|
-
getAnalytics: () => current.resolvedAnalytics,
|
|
1281
|
-
sessionController: session,
|
|
1282
|
-
sessionId: session.sessionId,
|
|
1283
|
-
transformRequest: mapOptions?.transformRequest ?? void 0,
|
|
1284
|
-
worldRequestBridge
|
|
1285
|
-
});
|
|
1286
|
-
const mapLibreMap = new maplibregl.Map({
|
|
1287
|
-
...mapOptions,
|
|
1288
|
-
canvasContextAttributes: tileflowCanvasContextAttributes(
|
|
1289
|
-
mapOptions?.canvasContextAttributes
|
|
1290
|
-
),
|
|
1291
|
-
container,
|
|
1292
|
-
style: prepared.style,
|
|
1293
|
-
bearing: current.resolvedBearing,
|
|
1294
|
-
center: current.resolvedCenter,
|
|
1295
|
-
pitch: current.resolvedPitch,
|
|
1296
|
-
zoom: current.resolvedZoom,
|
|
1297
|
-
interactive: resolvedInteractive,
|
|
1298
|
-
attributionControl: mapOptions?.attributionControl ?? { compact: true },
|
|
1299
|
-
// Tileflow's compiled styles are validated when they are built.
|
|
1300
|
-
validateStyle: mapOptions?.validateStyle ?? false,
|
|
1301
|
-
maxPitch: mapOptions?.maxPitch ?? tileflowMaxPitch,
|
|
1302
|
-
transformRequest
|
|
1303
|
-
});
|
|
1304
|
-
map = mapLibreMap;
|
|
1305
|
-
registerCleanup(() => mapLibreMap.remove());
|
|
1306
|
-
registerCleanup(attachTileflowAtmosphere(mapLibreMap));
|
|
1307
|
-
registerCleanup(attachTileflowBranding(mapLibreMap));
|
|
1308
|
-
registerCleanup(attachTileflowNotices(mapLibreMap));
|
|
1309
|
-
registerCleanup(attachTileflowNauticalLights(mapLibreMap));
|
|
1310
|
-
registerCleanup(attachTileflowLabelPlacement(mapLibreMap));
|
|
1311
|
-
registerCleanup(attachTileflowHorizonQueries(mapLibreMap));
|
|
1312
|
-
const controller = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
1313
|
-
initial: prepared.family ? prepared : initialRuntimeStyle,
|
|
1314
|
-
initialStyle: prepared.style,
|
|
1315
|
-
styleLoadOptions,
|
|
1316
|
-
map: mapLibreMap,
|
|
1317
|
-
onTransition(transition) {
|
|
1318
|
-
options.onThemeChange?.(transition);
|
|
1319
|
-
themeTransitionState = transition.phase === "preloading" || transition.phase === "applying" ? "loading" : transition.phase === "error" ? "error" : "idle";
|
|
1320
|
-
invalidate();
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
if (controller) {
|
|
1324
|
-
themeController = controller;
|
|
1325
|
-
registerCleanup(() => {
|
|
1326
|
-
controller.dispose();
|
|
1327
|
-
if (themeController === controller) themeController = void 0;
|
|
1328
|
-
});
|
|
1329
|
-
const appearance = createTileflowAppearanceBinding({
|
|
1330
|
-
controller,
|
|
1331
|
-
// CSS variables in `colors` are read where the map sits.
|
|
1332
|
-
element: container,
|
|
1333
|
-
map: mapLibreMap,
|
|
1334
|
-
onError: (error, part) => reportError(part, error)
|
|
1335
|
-
});
|
|
1336
|
-
appearanceBinding = appearance;
|
|
1337
|
-
registerCleanup(() => {
|
|
1338
|
-
appearance.dispose();
|
|
1339
|
-
if (appearanceBinding === appearance) appearanceBinding = void 0;
|
|
1340
|
-
});
|
|
1341
|
-
appearance.update(
|
|
1342
|
-
{ colors: options.colors, emphasis: options.emphasis },
|
|
1343
|
-
{ transition: { duration: 0 } }
|
|
1344
|
-
);
|
|
1347
|
+
if (scope.disposed || destroyed) return;
|
|
1348
|
+
createMap(scope, maplibregl, prepared, { initialRuntimeStyle, session, styleLoadOptions });
|
|
1349
|
+
}).catch((error) => {
|
|
1350
|
+
const cancelled = scope.disposed || destroyed;
|
|
1351
|
+
try {
|
|
1352
|
+
scope.dispose();
|
|
1353
|
+
} catch (cleanupError) {
|
|
1354
|
+
console.error("Failed to release a Tileflow map that could not start", cleanupError);
|
|
1345
1355
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1356
|
+
if (cancelled) return;
|
|
1357
|
+
reportError("runtime", error);
|
|
1358
|
+
captureState = "error";
|
|
1359
|
+
invalidate();
|
|
1360
|
+
});
|
|
1361
|
+
return () => scope.dispose();
|
|
1362
|
+
}
|
|
1363
|
+
function createMap(scope, maplibregl, prepared, context) {
|
|
1364
|
+
const { initialRuntimeStyle, session, styleLoadOptions } = context;
|
|
1365
|
+
const { mapOptions, resolvedInteractive } = current;
|
|
1366
|
+
scope.add(() => {
|
|
1367
|
+
map = null;
|
|
1368
|
+
mounted = void 0;
|
|
1369
|
+
appliedTheme = void 0;
|
|
1370
|
+
invalidate();
|
|
1371
|
+
});
|
|
1372
|
+
const sessionStarter = createTileflowSessionStarter({
|
|
1373
|
+
getSessionId: () => session.sessionId,
|
|
1374
|
+
sessionId: session.sessionId,
|
|
1375
|
+
source: sessionSource
|
|
1376
|
+
});
|
|
1377
|
+
const fairUseNotice = attachTileflowFairUseNotice(container);
|
|
1378
|
+
scope.add(() => fairUseNotice.dispose());
|
|
1379
|
+
registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1380
|
+
registerTileflowPathGroundProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1381
|
+
registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
|
|
1382
|
+
const worldRequestBridge = registerTileflowWorldRequestBridge({
|
|
1383
|
+
addProtocol: maplibregl.addProtocol,
|
|
1384
|
+
onNotice: fairUseNotice.update
|
|
1385
|
+
});
|
|
1386
|
+
scope.add(() => worldRequestBridge.dispose());
|
|
1387
|
+
const contourRequestScope = createTileflowContourRequestScope({
|
|
1388
|
+
transformRequest: (url, resourceType) => transformRequest(
|
|
1389
|
+
url,
|
|
1390
|
+
resourceType
|
|
1391
|
+
)
|
|
1392
|
+
});
|
|
1393
|
+
scope.add(() => contourRequestScope.dispose());
|
|
1394
|
+
const transformRequest = createTileflowTransformRequest({
|
|
1395
|
+
contourRequestScope,
|
|
1396
|
+
getAnalytics: () => current.resolvedAnalytics,
|
|
1397
|
+
sessionController: session,
|
|
1398
|
+
sessionId: session.sessionId,
|
|
1399
|
+
transformRequest: mapOptions?.transformRequest ?? void 0,
|
|
1400
|
+
worldRequestBridge
|
|
1401
|
+
});
|
|
1402
|
+
const mapLibreMap = new maplibregl.Map({
|
|
1403
|
+
...mapOptions,
|
|
1404
|
+
canvasContextAttributes: tileflowCanvasContextAttributes(mapOptions?.canvasContextAttributes),
|
|
1405
|
+
container,
|
|
1406
|
+
style: prepared.style,
|
|
1407
|
+
bearing: current.resolvedBearing,
|
|
1408
|
+
center: current.resolvedCenter,
|
|
1409
|
+
pitch: current.resolvedPitch,
|
|
1410
|
+
zoom: current.resolvedZoom,
|
|
1411
|
+
interactive: resolvedInteractive,
|
|
1412
|
+
attributionControl: mapOptions?.attributionControl ?? { compact: true },
|
|
1413
|
+
// Tileflow's compiled styles are validated when they are built.
|
|
1414
|
+
validateStyle: mapOptions?.validateStyle ?? false,
|
|
1415
|
+
maxPitch: mapOptions?.maxPitch ?? tileflowMaxPitch,
|
|
1416
|
+
transformRequest
|
|
1417
|
+
});
|
|
1418
|
+
map = mapLibreMap;
|
|
1419
|
+
let removedByMapLibre = false;
|
|
1420
|
+
scope.add(() => {
|
|
1421
|
+
if (!removedByMapLibre) mapLibreMap.remove();
|
|
1422
|
+
});
|
|
1423
|
+
const handleRemove = () => {
|
|
1424
|
+
removedByMapLibre = true;
|
|
1425
|
+
if (map !== mapLibreMap) return;
|
|
1426
|
+
map = null;
|
|
1427
|
+
queueMicrotask(teardown);
|
|
1428
|
+
};
|
|
1429
|
+
mapLibreMap.on("remove", handleRemove);
|
|
1430
|
+
scope.add(() => mapLibreMap.off("remove", handleRemove));
|
|
1431
|
+
scope.add(attachTileflowAtmosphere(mapLibreMap));
|
|
1432
|
+
scope.add(attachTileflowBranding(mapLibreMap));
|
|
1433
|
+
scope.add(attachTileflowNotices(mapLibreMap));
|
|
1434
|
+
scope.add(attachTileflowNauticalLights(mapLibreMap));
|
|
1435
|
+
scope.add(attachTileflowLabelPlacement(mapLibreMap));
|
|
1436
|
+
scope.add(attachTileflowHorizonQueries(mapLibreMap));
|
|
1437
|
+
let lifecycle;
|
|
1438
|
+
const controller = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
1439
|
+
initial: prepared.family ? prepared : initialRuntimeStyle,
|
|
1440
|
+
initialStyle: prepared.style,
|
|
1441
|
+
styleLoadOptions,
|
|
1442
|
+
map: mapLibreMap,
|
|
1443
|
+
onTransition(transition) {
|
|
1444
|
+
if (themeController !== controller) return;
|
|
1445
|
+
if (transition.phase === "ready") {
|
|
1446
|
+
lifecycle?.invalidate("loading");
|
|
1447
|
+
mapLibreMap.triggerRepaint();
|
|
1374
1448
|
}
|
|
1449
|
+
options.onThemeChange?.(transition);
|
|
1450
|
+
themeTransitionState = transition.phase === "preloading" || transition.phase === "applying" ? "loading" : transition.phase === "error" ? "error" : "idle";
|
|
1451
|
+
invalidate();
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
if (controller) {
|
|
1455
|
+
themeController = controller;
|
|
1456
|
+
scope.add(() => {
|
|
1457
|
+
controller.dispose();
|
|
1458
|
+
if (themeController === controller) themeController = void 0;
|
|
1375
1459
|
});
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
}) => wrapMapLibrePositioned(
|
|
1382
|
-
new maplibregl.Popup({
|
|
1383
|
-
closeButton: false,
|
|
1384
|
-
closeOnClick: false,
|
|
1385
|
-
closeOnMove: false,
|
|
1386
|
-
focusAfterOpen: false,
|
|
1387
|
-
offset: kind === "popup" ? 24 : 18
|
|
1388
|
-
}).setDOMContent(overlayContainer)
|
|
1389
|
-
);
|
|
1390
|
-
const annotationRuntime = createTileflowMapLibreDomRuntime({
|
|
1391
|
-
createMarker({ annotation, element }) {
|
|
1392
|
-
const marker = new maplibregl.Marker({ element });
|
|
1393
|
-
element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
|
|
1394
|
-
return wrapMapLibrePositioned(marker);
|
|
1395
|
-
},
|
|
1396
|
-
createOverlay,
|
|
1397
|
-
customMarker: current.hasMarkerRenderer,
|
|
1398
|
-
customPopup: current.hasPopupRenderer,
|
|
1399
|
-
customTooltip: current.hasTooltipRenderer,
|
|
1400
|
-
document: container.ownerDocument,
|
|
1401
|
-
interactionState: coordinator.getInteractionState(),
|
|
1460
|
+
appliedTheme = { selection: current.themeSelection, theme: initialRuntimeStyle.theme };
|
|
1461
|
+
const appearance = createTileflowAppearanceBinding({
|
|
1462
|
+
controller,
|
|
1463
|
+
// CSS variables in `colors` are read where the map sits.
|
|
1464
|
+
element: container,
|
|
1402
1465
|
map: mapLibreMap,
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
updateMarker(_marker, { annotation, element }) {
|
|
1406
|
-
element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
|
|
1466
|
+
onError: (error, part) => {
|
|
1467
|
+
if (appearanceBinding === appearance) reportError(part, error);
|
|
1407
1468
|
}
|
|
1408
1469
|
});
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
document: container.ownerDocument,
|
|
1414
|
-
map: mapLibreMap,
|
|
1415
|
-
onDiagnostic: reportDiagnostic,
|
|
1416
|
-
onInteractionEvent(event) {
|
|
1417
|
-
options.onInteractionEvent?.(event);
|
|
1418
|
-
},
|
|
1419
|
-
setHiddenAnnotations: annotationRuntime.setHiddenAnnotations
|
|
1420
|
-
});
|
|
1421
|
-
dataRuntime = mapDataRuntime;
|
|
1422
|
-
registerCleanup(() => {
|
|
1423
|
-
mapDataRuntime.dispose();
|
|
1424
|
-
if (dataRuntime === mapDataRuntime) dataRuntime = void 0;
|
|
1470
|
+
appearanceBinding = appearance;
|
|
1471
|
+
scope.add(() => {
|
|
1472
|
+
appearance.dispose();
|
|
1473
|
+
if (appearanceBinding === appearance) appearanceBinding = void 0;
|
|
1425
1474
|
});
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
customPopup: current.hasPopupRenderer,
|
|
1430
|
-
customTooltip: current.hasTooltipRenderer,
|
|
1431
|
-
document: container.ownerDocument,
|
|
1432
|
-
interactionState: coordinator.getInteractionState(),
|
|
1433
|
-
map: mapLibreMap,
|
|
1434
|
-
onDiagnostic: reportDiagnostic,
|
|
1435
|
-
onInteractionStateChange: coordinator.requestInteractionState,
|
|
1436
|
-
poiMap: createTileflowMapLibrePoiMap(mapLibreMap),
|
|
1437
|
-
requestFrame: (callback) => browserWindow.requestAnimationFrame(() => callback())
|
|
1438
|
-
});
|
|
1439
|
-
semanticRuntime = featureRuntime;
|
|
1440
|
-
registerCleanup(() => featureRuntime.dispose());
|
|
1441
|
-
registerCleanup(coordinator.attach("annotation", annotationRuntime));
|
|
1442
|
-
registerCleanup(coordinator.attach("semantic", featureRuntime));
|
|
1443
|
-
registerCleanup(
|
|
1444
|
-
annotationRuntime.subscribeEvents((event) => options.onInteractionEvent?.(event))
|
|
1445
|
-
);
|
|
1446
|
-
registerCleanup(
|
|
1447
|
-
annotationRuntime.subscribeDiagnostics((diagnostics) => {
|
|
1448
|
-
annotationDiagnostics = diagnostics;
|
|
1449
|
-
invalidate();
|
|
1450
|
-
})
|
|
1451
|
-
);
|
|
1452
|
-
registerCleanup(
|
|
1453
|
-
annotationRuntime.subscribeRenderTargets((targets) => {
|
|
1454
|
-
annotationRenderTargets = targets;
|
|
1455
|
-
invalidate();
|
|
1456
|
-
})
|
|
1457
|
-
);
|
|
1458
|
-
registerCleanup(
|
|
1459
|
-
featureRuntime.subscribeEvents((event) => {
|
|
1460
|
-
if (event.target.kind === "annotation") return;
|
|
1461
|
-
options.onInteractionEvent?.({ ...event, target: event.target });
|
|
1462
|
-
})
|
|
1463
|
-
);
|
|
1464
|
-
registerCleanup(
|
|
1465
|
-
featureRuntime.subscribeDiagnostics((diagnostics) => {
|
|
1466
|
-
semanticDiagnostics = diagnostics;
|
|
1467
|
-
invalidate();
|
|
1468
|
-
})
|
|
1469
|
-
);
|
|
1470
|
-
registerCleanup(
|
|
1471
|
-
featureRuntime.subscribeRenderTargets((targets) => {
|
|
1472
|
-
semanticRenderTargets = targets;
|
|
1473
|
-
invalidate();
|
|
1474
|
-
})
|
|
1475
|
+
appearance.update(
|
|
1476
|
+
{ colors: options.colors, emphasis: options.emphasis },
|
|
1477
|
+
{ transition: { duration: 0 } }
|
|
1475
1478
|
);
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1479
|
+
}
|
|
1480
|
+
const mapScene = attachTileflowMap3D({
|
|
1481
|
+
map: mapLibreMap,
|
|
1482
|
+
...controller ? { controller } : {},
|
|
1483
|
+
vegetation: options.vegetation,
|
|
1484
|
+
viewMode: options.viewMode ?? "3d",
|
|
1485
|
+
load: () => import("@tileflow/core/3d"),
|
|
1486
|
+
onError: (error) => {
|
|
1487
|
+
if (scene === mapScene) reportError("scene", error);
|
|
1488
|
+
},
|
|
1489
|
+
// Direct tile readers use HTTP, not MapLibre's custom World protocol.
|
|
1490
|
+
transformRequest: createTileflowTransformRequest({
|
|
1491
|
+
getAnalytics: () => current.resolvedAnalytics,
|
|
1492
|
+
sessionController: session,
|
|
1493
|
+
sessionId: session.sessionId,
|
|
1494
|
+
transformRequest: (url) => mapOptions?.transformRequest?.(
|
|
1495
|
+
url,
|
|
1496
|
+
"Tile"
|
|
1497
|
+
)
|
|
1498
|
+
})
|
|
1499
|
+
});
|
|
1500
|
+
scene = mapScene;
|
|
1501
|
+
scope.add(() => {
|
|
1502
|
+
mapScene.dispose();
|
|
1503
|
+
if (scene === mapScene) scene = void 0;
|
|
1504
|
+
});
|
|
1505
|
+
mounted = { map: mapLibreMap, maplibregl, scope };
|
|
1506
|
+
attachInteractions(mounted);
|
|
1507
|
+
const navigation = createTileflowNavigationControlBinding({
|
|
1508
|
+
createControl: (controlOptions) => new maplibregl.NavigationControl(controlOptions),
|
|
1509
|
+
map: mapLibreMap
|
|
1510
|
+
});
|
|
1511
|
+
navigationBinding = navigation;
|
|
1512
|
+
scope.add(() => {
|
|
1513
|
+
navigation.dispose();
|
|
1514
|
+
if (navigationBinding === navigation) navigationBinding = void 0;
|
|
1515
|
+
});
|
|
1516
|
+
navigation.update(current.resolvedInteractive ? options.navigationControl : false);
|
|
1517
|
+
const initialFit = current.cameraFit;
|
|
1518
|
+
appliedCameraFitKey = initialFit.key;
|
|
1519
|
+
if (initialFit.error) reportError("camera", new Error(initialFit.error));
|
|
1520
|
+
if (initialFit.fit) fitCameraTo(mapLibreMap, initialFit.fit, 0);
|
|
1521
|
+
const resizeObserver = new ResizeObserver(() => mapLibreMap.resize());
|
|
1522
|
+
scope.add(() => resizeObserver.disconnect());
|
|
1523
|
+
resizeObserver.observe(container);
|
|
1524
|
+
const mapLifecycle = attachTileflowMapLifecycle({
|
|
1525
|
+
getSession: () => {
|
|
1526
|
+
const analyticsForLoad = current.resolvedAnalytics;
|
|
1527
|
+
return {
|
|
1528
|
+
analytics: analyticsForLoad,
|
|
1529
|
+
styleId: analyticsForLoad?.styleId ?? (typeof initialRuntimeStyle.style === "string" ? initialRuntimeStyle.style : current.mapName)
|
|
1530
|
+
};
|
|
1531
|
+
},
|
|
1532
|
+
map: mapLibreMap,
|
|
1533
|
+
onLoad: (loadedMap) => {
|
|
1534
|
+
settleReady({ map: loadedMap });
|
|
1535
|
+
options.onLoad?.(loadedMap);
|
|
1536
|
+
},
|
|
1537
|
+
scheduler: {
|
|
1538
|
+
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
1539
|
+
requestFrame: (callback) => browserWindow.requestAnimationFrame(callback)
|
|
1540
|
+
},
|
|
1541
|
+
sessionStarter,
|
|
1542
|
+
setState: (state) => {
|
|
1543
|
+
if (map !== mapLibreMap) return;
|
|
1544
|
+
captureState = state;
|
|
1545
|
+
invalidate();
|
|
1546
|
+
},
|
|
1547
|
+
subscribe: (subscribedMap, event, listener) => {
|
|
1548
|
+
const subscription = subscribedMap.on(event, listener);
|
|
1549
|
+
return () => subscription.unsubscribe();
|
|
1542
1550
|
}
|
|
1551
|
+
});
|
|
1552
|
+
lifecycle = mapLifecycle;
|
|
1553
|
+
scope.add(() => {
|
|
1554
|
+
lifecycle = void 0;
|
|
1555
|
+
mapLifecycle.dispose();
|
|
1556
|
+
});
|
|
1557
|
+
const latestRuntimeStyle = current.runtimeStyle;
|
|
1558
|
+
const latestBlend = current.blendResolution.styles && current.themeBlendPosition !== void 0 ? {
|
|
1559
|
+
hold: current.themeBlendHold,
|
|
1560
|
+
position: current.themeBlendPosition,
|
|
1561
|
+
themes: current.blendResolution.styles
|
|
1562
|
+
} : null;
|
|
1563
|
+
if (controller && latestBlend) {
|
|
1564
|
+
if (latestRuntimeStyle) activeRuntimeResource = latestRuntimeStyle;
|
|
1565
|
+
void controller.setBlend(latestBlend, { transition: options.themeTransition });
|
|
1566
|
+
} else if (controller && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
|
|
1567
|
+
requestTheme(controller, latestRuntimeStyle, current.themeSelection);
|
|
1568
|
+
}
|
|
1569
|
+
invalidate();
|
|
1570
|
+
}
|
|
1571
|
+
function attachInteractions(target) {
|
|
1572
|
+
const { map: mapLibreMap, maplibregl } = target;
|
|
1573
|
+
const scope = target.scope.child();
|
|
1574
|
+
scope.add(() => {
|
|
1575
|
+
interactionRuntime = void 0;
|
|
1576
|
+
semanticRuntime = void 0;
|
|
1577
|
+
interactionCoordinator = void 0;
|
|
1578
|
+
dataRuntime = void 0;
|
|
1579
|
+
annotationDiagnostics = [];
|
|
1580
|
+
annotationRenderTargets = [];
|
|
1581
|
+
annotationBridgeDiagnostic = void 0;
|
|
1582
|
+
semanticDiagnostics = [];
|
|
1583
|
+
semanticRenderTargets = [];
|
|
1584
|
+
semanticBridgeDiagnostic = void 0;
|
|
1585
|
+
stateBridgeDiagnostic = void 0;
|
|
1586
|
+
rendererBridgeDiagnostic = void 0;
|
|
1587
|
+
dataBridgeDiagnostic = void 0;
|
|
1543
1588
|
invalidate();
|
|
1544
|
-
})
|
|
1545
|
-
|
|
1589
|
+
});
|
|
1590
|
+
try {
|
|
1591
|
+
startInteractions(scope, mapLibreMap, maplibregl);
|
|
1592
|
+
interactionStartDiagnostic = void 0;
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
console.error("Failed to start Tileflow map interactions", error);
|
|
1546
1595
|
try {
|
|
1547
|
-
|
|
1596
|
+
scope.dispose();
|
|
1548
1597
|
} catch (cleanupError) {
|
|
1549
|
-
|
|
1598
|
+
console.error("Failed to release Tileflow map interactions", cleanupError);
|
|
1550
1599
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1600
|
+
interactionStartDiagnostic = createOverlayFailureDiagnostic(
|
|
1601
|
+
"Tileflow map interactions could not start."
|
|
1602
|
+
);
|
|
1603
|
+
reportDiagnostic(interactionStartDiagnostic);
|
|
1604
|
+
}
|
|
1605
|
+
invalidate();
|
|
1606
|
+
}
|
|
1607
|
+
function startInteractions(scope, mapLibreMap, maplibregl) {
|
|
1608
|
+
const coordinator = createTileflowMapLibreInteractionCoordinator({
|
|
1609
|
+
...controlledInteractionOwnership ? { interactionState: coordinatedInteractionState } : { defaultInteractionState: coordinatedInteractionState },
|
|
1610
|
+
onInteractionStateChange(state) {
|
|
1611
|
+
if (interactionCoordinator !== coordinator) return;
|
|
1612
|
+
if (!controlledInteractionOwnership) coordinatedInteractionState = state;
|
|
1613
|
+
options.onInteractionStateChange?.(state);
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
interactionCoordinator = coordinator;
|
|
1617
|
+
scope.add(() => coordinator.dispose());
|
|
1618
|
+
const createOverlay = ({
|
|
1619
|
+
container: overlayContainer,
|
|
1620
|
+
kind
|
|
1621
|
+
}) => wrapMapLibrePositioned(
|
|
1622
|
+
new maplibregl.Popup({
|
|
1623
|
+
closeButton: false,
|
|
1624
|
+
closeOnClick: false,
|
|
1625
|
+
closeOnMove: false,
|
|
1626
|
+
focusAfterOpen: false,
|
|
1627
|
+
offset: kind === "popup" ? 24 : 18
|
|
1628
|
+
}).setDOMContent(overlayContainer)
|
|
1629
|
+
);
|
|
1630
|
+
const annotationRuntime = createTileflowMapLibreDomRuntime({
|
|
1631
|
+
createMarker({ annotation, element }) {
|
|
1632
|
+
const marker = new maplibregl.Marker({ element });
|
|
1633
|
+
element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
|
|
1634
|
+
return wrapMapLibrePositioned(marker);
|
|
1635
|
+
},
|
|
1636
|
+
createOverlay,
|
|
1637
|
+
customMarker: current.hasMarkerRenderer,
|
|
1638
|
+
customPopup: current.hasPopupRenderer,
|
|
1639
|
+
customTooltip: current.hasTooltipRenderer,
|
|
1640
|
+
document: container.ownerDocument,
|
|
1641
|
+
interactionState: coordinator.getInteractionState(),
|
|
1642
|
+
map: mapLibreMap,
|
|
1643
|
+
onInteractionStateChange: coordinator.requestInteractionState,
|
|
1644
|
+
onDiagnostic: reportDiagnostic,
|
|
1645
|
+
updateMarker(_marker, { annotation, element }) {
|
|
1646
|
+
element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
interactionRuntime = annotationRuntime;
|
|
1650
|
+
scope.add(() => annotationRuntime.dispose());
|
|
1651
|
+
const mapDataRuntime = createTileflowMapLibreDataRuntime({
|
|
1652
|
+
createMarker: ({ element }) => wrapMapLibrePositioned(new maplibregl.Marker({ element })),
|
|
1653
|
+
document: container.ownerDocument,
|
|
1654
|
+
map: mapLibreMap,
|
|
1655
|
+
onDiagnostic: reportDiagnostic,
|
|
1656
|
+
onInteractionEvent(event) {
|
|
1657
|
+
options.onInteractionEvent?.(event);
|
|
1658
|
+
},
|
|
1659
|
+
setHiddenAnnotations: annotationRuntime.setHiddenAnnotations
|
|
1660
|
+
});
|
|
1661
|
+
dataRuntime = mapDataRuntime;
|
|
1662
|
+
scope.add(() => mapDataRuntime.dispose());
|
|
1663
|
+
const featureRuntime = createTileflowMapLibreSemanticDomRuntime({
|
|
1664
|
+
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
1665
|
+
createOverlay,
|
|
1666
|
+
customPopup: current.hasPopupRenderer,
|
|
1667
|
+
customTooltip: current.hasTooltipRenderer,
|
|
1668
|
+
document: container.ownerDocument,
|
|
1669
|
+
interactionState: coordinator.getInteractionState(),
|
|
1670
|
+
map: mapLibreMap,
|
|
1671
|
+
onDiagnostic: reportDiagnostic,
|
|
1672
|
+
onInteractionStateChange: coordinator.requestInteractionState,
|
|
1673
|
+
poiMap: createTileflowMapLibrePoiMap(mapLibreMap),
|
|
1674
|
+
requestFrame: (callback) => browserWindow.requestAnimationFrame(() => callback())
|
|
1675
|
+
});
|
|
1676
|
+
semanticRuntime = featureRuntime;
|
|
1677
|
+
scope.add(() => featureRuntime.dispose());
|
|
1678
|
+
scope.add(coordinator.attach("annotation", annotationRuntime));
|
|
1679
|
+
scope.add(coordinator.attach("semantic", featureRuntime));
|
|
1680
|
+
scope.add(annotationRuntime.subscribeEvents((event) => options.onInteractionEvent?.(event)));
|
|
1681
|
+
scope.add(
|
|
1682
|
+
annotationRuntime.subscribeDiagnostics((diagnostics) => {
|
|
1683
|
+
annotationDiagnostics = diagnostics;
|
|
1684
|
+
invalidate();
|
|
1685
|
+
})
|
|
1686
|
+
);
|
|
1687
|
+
scope.add(
|
|
1688
|
+
annotationRuntime.subscribeRenderTargets((targets) => {
|
|
1689
|
+
annotationRenderTargets = targets;
|
|
1690
|
+
invalidate();
|
|
1691
|
+
})
|
|
1692
|
+
);
|
|
1693
|
+
scope.add(
|
|
1694
|
+
featureRuntime.subscribeEvents((event) => {
|
|
1695
|
+
if (event.target.kind === "annotation") return;
|
|
1696
|
+
options.onInteractionEvent?.({ ...event, target: event.target });
|
|
1697
|
+
})
|
|
1698
|
+
);
|
|
1699
|
+
scope.add(
|
|
1700
|
+
featureRuntime.subscribeDiagnostics((diagnostics) => {
|
|
1701
|
+
semanticDiagnostics = diagnostics;
|
|
1554
1702
|
invalidate();
|
|
1703
|
+
})
|
|
1704
|
+
);
|
|
1705
|
+
scope.add(
|
|
1706
|
+
featureRuntime.subscribeRenderTargets((targets) => {
|
|
1707
|
+
semanticRenderTargets = targets;
|
|
1708
|
+
invalidate();
|
|
1709
|
+
})
|
|
1710
|
+
);
|
|
1711
|
+
annotationDiagnostics = annotationRuntime.getDiagnostics();
|
|
1712
|
+
annotationRenderTargets = annotationRuntime.getRenderTargets();
|
|
1713
|
+
semanticDiagnostics = featureRuntime.getDiagnostics();
|
|
1714
|
+
semanticRenderTargets = featureRuntime.getRenderTargets();
|
|
1715
|
+
reconcileAnnotations(annotationRuntime, annotationsForRuntime);
|
|
1716
|
+
reconcileData(mapDataRuntime);
|
|
1717
|
+
const initialInteractions = current.preparedInteractions;
|
|
1718
|
+
if (initialInteractions.ok) reconcileInteractions(featureRuntime, initialInteractions.bindings);
|
|
1719
|
+
}
|
|
1720
|
+
function requestTheme(controller, style, selection) {
|
|
1721
|
+
const requested = { selection: options.theme, theme: current.themeResolution.name };
|
|
1722
|
+
const standingIn = current.themeRequestFailed || current.themeResolution.error;
|
|
1723
|
+
activeRuntimeResource = style;
|
|
1724
|
+
invalidate();
|
|
1725
|
+
void controller.setTheme(style, { transition: options.themeTransition }).then((result) => {
|
|
1726
|
+
if (destroyed || themeController !== controller) return;
|
|
1727
|
+
if (result.status === "applied") {
|
|
1728
|
+
appliedTheme = { selection, theme: result.theme };
|
|
1729
|
+
if (!standingIn) failedTheme = void 0;
|
|
1730
|
+
} else if (result.status === "failed") {
|
|
1731
|
+
const shown = controller.getCurrent().theme;
|
|
1732
|
+
const applied = appliedTheme ?? { selection: shown, theme: shown };
|
|
1733
|
+
failedTheme = {
|
|
1734
|
+
...requested,
|
|
1735
|
+
fallback: standingIn ? { selection: applied.theme, theme: applied.theme } : applied
|
|
1736
|
+
};
|
|
1737
|
+
reportError("theme", result.error ?? new Error("The map theme could not be changed."));
|
|
1555
1738
|
}
|
|
1739
|
+
invalidate();
|
|
1556
1740
|
});
|
|
1557
|
-
return () => {
|
|
1558
|
-
cancelled = true;
|
|
1559
|
-
resourceLifetime.abort();
|
|
1560
|
-
disposeMap?.();
|
|
1561
|
-
};
|
|
1562
1741
|
}
|
|
1563
1742
|
function computeStatus() {
|
|
1564
1743
|
const d = current;
|
|
@@ -1569,6 +1748,8 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1569
1748
|
...stateBridgeDiagnostic ? [stateBridgeDiagnostic] : [],
|
|
1570
1749
|
...rendererBridgeDiagnostic ? [rendererBridgeDiagnostic] : [],
|
|
1571
1750
|
...viewBridgeDiagnostic ? [viewBridgeDiagnostic] : [],
|
|
1751
|
+
...dataBridgeDiagnostic ? [dataBridgeDiagnostic] : [],
|
|
1752
|
+
...interactionStartDiagnostic ? [interactionStartDiagnostic] : [],
|
|
1572
1753
|
...annotationDiagnostics,
|
|
1573
1754
|
...semanticDiagnostics
|
|
1574
1755
|
];
|
|
@@ -1585,7 +1766,7 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1585
1766
|
return {
|
|
1586
1767
|
diagnostics,
|
|
1587
1768
|
readiness: parts.includes("error") ? "error" : parts.every((part) => part === "idle") ? "idle" : "loading",
|
|
1588
|
-
theme: d.
|
|
1769
|
+
theme: d.shownThemeResolution.name,
|
|
1589
1770
|
imageUrl: d.isImageMode ? d.runtimeImageUrl : void 0
|
|
1590
1771
|
};
|
|
1591
1772
|
}
|
|
@@ -1619,27 +1800,31 @@ function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
|
1619
1800
|
}
|
|
1620
1801
|
reconcile();
|
|
1621
1802
|
}
|
|
1803
|
+
function teardown() {
|
|
1804
|
+
if (destroyed) return;
|
|
1805
|
+
destroyed = true;
|
|
1806
|
+
settleReady({ error: new DOMException("The Tileflow map was destroyed.", "AbortError") });
|
|
1807
|
+
try {
|
|
1808
|
+
runTileflowCleanups([() => viewHost.dispose(), () => reconciler.dispose()]);
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
console.error("Failed to release a Tileflow map", error);
|
|
1811
|
+
} finally {
|
|
1812
|
+
map = null;
|
|
1813
|
+
mounted = void 0;
|
|
1814
|
+
if (statusElement) {
|
|
1815
|
+
for (const attribute of statusAttributes) statusElement.removeAttribute(attribute);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1622
1819
|
reconcile();
|
|
1623
1820
|
return {
|
|
1624
1821
|
container,
|
|
1625
|
-
destroy
|
|
1626
|
-
if (destroyed) return;
|
|
1627
|
-
destroyed = true;
|
|
1628
|
-
settleReady({ error: new DOMException("The Tileflow map was destroyed.", "AbortError") });
|
|
1629
|
-
try {
|
|
1630
|
-
viewHost.dispose();
|
|
1631
|
-
reconciler.dispose();
|
|
1632
|
-
} finally {
|
|
1633
|
-
if (statusElement) {
|
|
1634
|
-
for (const attribute of statusAttributes) statusElement.removeAttribute(attribute);
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
},
|
|
1822
|
+
destroy: teardown,
|
|
1638
1823
|
getInteractionState: () => interactionCoordinator?.getInteractionState() ?? coordinatedInteractionState,
|
|
1639
1824
|
getMap: () => map,
|
|
1640
1825
|
getRenderTargets: () => lastRenderTargets,
|
|
1641
1826
|
getStatus: () => lastStatus ?? computeStatus(),
|
|
1642
|
-
getThemeController: () => themeController ?? null,
|
|
1827
|
+
getThemeController: () => map ? themeController ?? null : null,
|
|
1643
1828
|
ready,
|
|
1644
1829
|
setAnnotations: (annotations) => update({ annotations }),
|
|
1645
1830
|
setBounds: (bounds, fitBoundsOptions) => update(fitBoundsOptions === void 0 ? { bounds } : { bounds, fitBoundsOptions }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/dom",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Mount a Tileflow map in any DOM element, without a framework.",
|
|
6
6
|
"type": "module",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
44
|
-
"@tileflow/interactions": ">=0.1.0-alpha.
|
|
43
|
+
"@tileflow/core": ">=0.1.0-alpha.52 <0.1.0-beta.0",
|
|
44
|
+
"@tileflow/interactions": ">=0.1.0-alpha.11 <0.1.0-beta.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"maplibre-gl": ">=6.4.1 <7"
|