@tileflow/dom 0.0.0-stage → 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +110 -2
- package/REFERENCE.md +80 -0
- package/dist/index.d.ts +198 -0
- package/dist/index.js +1768 -0
- package/package.json +57 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,1768 @@
|
|
|
1
|
+
// src/map.ts
|
|
2
|
+
import {
|
|
3
|
+
attachTileflowAtmosphere,
|
|
4
|
+
attachTileflowBranding,
|
|
5
|
+
attachTileflowFairUseNotice,
|
|
6
|
+
attachTileflowHorizonQueries,
|
|
7
|
+
attachTileflowLabelPlacement,
|
|
8
|
+
attachTileflowMap3D,
|
|
9
|
+
attachTileflowMapLifecycle,
|
|
10
|
+
attachTileflowNauticalLights,
|
|
11
|
+
attachTileflowNotices,
|
|
12
|
+
createTileflowAppearanceBinding,
|
|
13
|
+
createTileflowContourRequestScope,
|
|
14
|
+
createTileflowNavigationControlBinding,
|
|
15
|
+
createTileflowSessionStarter,
|
|
16
|
+
createTileflowThemeController,
|
|
17
|
+
createTileflowTransformRequest,
|
|
18
|
+
getTileflowSystemColorScheme,
|
|
19
|
+
loadTileflowRuntimeStyle,
|
|
20
|
+
loadTileflowStyleFonts,
|
|
21
|
+
registerTileflowContourProtocol,
|
|
22
|
+
registerTileflowPathGroundProtocol,
|
|
23
|
+
registerTileflowPmtilesProtocol,
|
|
24
|
+
registerTileflowWorldRequestBridge,
|
|
25
|
+
reportTileflowMapError,
|
|
26
|
+
resolveTileflowCameraFit,
|
|
27
|
+
subscribeTileflowSystemColorScheme,
|
|
28
|
+
tileflowCanvasContextAttributes,
|
|
29
|
+
tileflowMaxPitch
|
|
30
|
+
} from "@tileflow/core/browser";
|
|
31
|
+
import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
|
|
32
|
+
import {
|
|
33
|
+
createTileflowSessionController,
|
|
34
|
+
defaultTileflowManifestUrl,
|
|
35
|
+
defaultTileflowRuntimeView,
|
|
36
|
+
loadTileflowManifest,
|
|
37
|
+
mergeTileflowAnalytics,
|
|
38
|
+
nearestTileflowBlendTheme,
|
|
39
|
+
normalizeTileflowRuntimeCenter,
|
|
40
|
+
normalizeTileflowStaticImageSize,
|
|
41
|
+
resolveTileflowManifestMap,
|
|
42
|
+
resolveTileflowRuntimeStyle,
|
|
43
|
+
resolveTileflowRuntimeTheme,
|
|
44
|
+
resolveTileflowRuntimeThemeBlend,
|
|
45
|
+
resolveTileflowRuntimeView,
|
|
46
|
+
resolveTileflowStaticImageUrl
|
|
47
|
+
} from "@tileflow/core/runtime";
|
|
48
|
+
import {
|
|
49
|
+
tileflowInteractionStateSchema as tileflowInteractionStateSchema2,
|
|
50
|
+
unusedTileflowRendererDiagnostics
|
|
51
|
+
} from "@tileflow/interactions";
|
|
52
|
+
import {
|
|
53
|
+
createTileflowMapLibreDataRuntime,
|
|
54
|
+
createTileflowMapLibreDomRuntime,
|
|
55
|
+
createTileflowMapLibreInteractionCoordinator,
|
|
56
|
+
createTileflowMapLibreSemanticDomRuntime
|
|
57
|
+
} from "@tileflow/interactions/maplibre";
|
|
58
|
+
|
|
59
|
+
// src/inputs.ts
|
|
60
|
+
import { validateTileflowBrowserThemeBlendSelection } from "@tileflow/core/browser";
|
|
61
|
+
import {
|
|
62
|
+
isTileflowTimeOfDay,
|
|
63
|
+
validateTileflowRuntimeSource,
|
|
64
|
+
validateTileflowThemeSelection
|
|
65
|
+
} from "@tileflow/core/runtime";
|
|
66
|
+
import {
|
|
67
|
+
initialTileflowInteractionState,
|
|
68
|
+
tileflowInteractionStateSchema,
|
|
69
|
+
validateTileflowAnnotations,
|
|
70
|
+
validateTileflowInteractionBindings
|
|
71
|
+
} from "@tileflow/interactions";
|
|
72
|
+
function prepareTileflowAnnotations(annotations) {
|
|
73
|
+
const candidates = annotations ?? [];
|
|
74
|
+
const titles = new Map(candidates.map((annotation) => [annotation.id, annotation.ariaLabel]));
|
|
75
|
+
const validation = validateTileflowAnnotations(candidates);
|
|
76
|
+
if (!validation.ok) {
|
|
77
|
+
return { annotations: [], diagnostics: validation.diagnostics, ok: false, titles };
|
|
78
|
+
}
|
|
79
|
+
return { annotations: candidates, diagnostics: [], ok: true, titles };
|
|
80
|
+
}
|
|
81
|
+
function prepareTileflowInteractionBindings(interactions) {
|
|
82
|
+
const validation = validateTileflowInteractionBindings(interactions ?? []);
|
|
83
|
+
return validation.ok ? { bindings: validation.bindings, diagnostics: [], ok: true } : { diagnostics: validation.diagnostics, ok: false };
|
|
84
|
+
}
|
|
85
|
+
function prepareTileflowInteractionState(interactionState, defaultInteractionState, controlledOwnership = interactionState !== void 0) {
|
|
86
|
+
const controlled = interactionState !== void 0;
|
|
87
|
+
if (controlled && defaultInteractionState !== void 0) {
|
|
88
|
+
return invalidState(
|
|
89
|
+
controlled,
|
|
90
|
+
"Tileflow Map accepts interactionState or defaultInteractionState, not both."
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (controlled !== controlledOwnership) {
|
|
94
|
+
return invalidState(
|
|
95
|
+
controlled,
|
|
96
|
+
"Tileflow interaction state ownership cannot switch between controlled and uncontrolled."
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const statePath = controlled ? "/interactionState" : "/defaultInteractionState";
|
|
100
|
+
const parsed = tileflowInteractionStateSchema.safeParse(
|
|
101
|
+
interactionState ?? defaultInteractionState ?? initialTileflowInteractionState
|
|
102
|
+
);
|
|
103
|
+
if (parsed.success) return { controlled, diagnostics: [], ok: true, state: parsed.data };
|
|
104
|
+
return {
|
|
105
|
+
controlled,
|
|
106
|
+
diagnostics: parsed.error.issues.map((issue) => ({
|
|
107
|
+
code: "INVALID_DOCUMENT",
|
|
108
|
+
level: "error",
|
|
109
|
+
message: issue.message,
|
|
110
|
+
path: `${statePath}${issue.path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("")}`
|
|
111
|
+
})),
|
|
112
|
+
ok: false,
|
|
113
|
+
state: initialTileflowInteractionState
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function invalidState(controlled, message) {
|
|
117
|
+
return {
|
|
118
|
+
controlled,
|
|
119
|
+
diagnostics: [{ code: "INVALID_DOCUMENT", level: "error", message, path: "" }],
|
|
120
|
+
ok: false,
|
|
121
|
+
state: initialTileflowInteractionState
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function assertTileflowMapStyleOptions(input) {
|
|
125
|
+
const error = styleOptionsError(input);
|
|
126
|
+
if (error) throw new TypeError(`Invalid Tileflow map options: ${error}`);
|
|
127
|
+
}
|
|
128
|
+
function styleOptionsError(input) {
|
|
129
|
+
const sourceValidation = validateTileflowRuntimeSource(input.source);
|
|
130
|
+
if (!sourceValidation.ok) return sourceValidation.error;
|
|
131
|
+
if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
|
|
132
|
+
return 'theme must be a concrete portable theme name or "system"';
|
|
133
|
+
}
|
|
134
|
+
if (input.timeOfDay !== void 0 && !isTileflowTimeOfDay(input.timeOfDay)) {
|
|
135
|
+
return "timeOfDay must be day, dawn, dusk, or night";
|
|
136
|
+
}
|
|
137
|
+
if (input.themeBlend !== void 0 && !validateTileflowBrowserThemeBlendSelection(input.themeBlend)) {
|
|
138
|
+
return "themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold";
|
|
139
|
+
}
|
|
140
|
+
if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
|
|
141
|
+
return "themeTransition.duration must be a number of milliseconds from 0 to 5000";
|
|
142
|
+
}
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
function isThemeTransition(value) {
|
|
146
|
+
if (!value || typeof value !== "object") return false;
|
|
147
|
+
const { duration } = value;
|
|
148
|
+
return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
|
|
149
|
+
}
|
|
150
|
+
function createOverlayFailureDiagnostic(message) {
|
|
151
|
+
return { code: "OVERLAY_FAILURE", level: "error", message, path: "" };
|
|
152
|
+
}
|
|
153
|
+
function tileflowInteractionDiagnosticKey(diagnostic) {
|
|
154
|
+
return [
|
|
155
|
+
diagnostic.code,
|
|
156
|
+
diagnostic.level,
|
|
157
|
+
diagnostic.path ?? "",
|
|
158
|
+
diagnostic.target ? JSON.stringify(diagnostic.target) : "",
|
|
159
|
+
diagnostic.message
|
|
160
|
+
].join("\0");
|
|
161
|
+
}
|
|
162
|
+
function imageModeAnnotationDiagnostic(annotationCount) {
|
|
163
|
+
return annotationCount > 0 ? {
|
|
164
|
+
code: "UNSUPPORTED_MODE",
|
|
165
|
+
level: "error",
|
|
166
|
+
message: 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.',
|
|
167
|
+
path: ""
|
|
168
|
+
} : void 0;
|
|
169
|
+
}
|
|
170
|
+
function imageModeMapInteractionDiagnostic(annotationCount, bindingCount, hasAdditionalInteractionConfiguration = false) {
|
|
171
|
+
if (bindingCount === 0 && !hasAdditionalInteractionConfiguration) {
|
|
172
|
+
return imageModeAnnotationDiagnostic(annotationCount);
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
code: "UNSUPPORTED_MODE",
|
|
176
|
+
level: "error",
|
|
177
|
+
message: annotationCount > 0 && bindingCount > 0 ? 'Tileflow annotations and interactions require mode="interactive"; image mode cannot render interactive overlays.' : annotationCount > 0 ? 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.' : bindingCount > 0 ? 'Tileflow interactions require mode="interactive"; image mode cannot render interactive overlays.' : 'Tileflow interaction state, callbacks, and renderers require mode="interactive"; image mode cannot render interactive overlays.',
|
|
178
|
+
path: ""
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/maplibre.ts
|
|
183
|
+
var bundlerWorkerUrlKey = /* @__PURE__ */ Symbol.for("tileflow.maplibre.workerUrl");
|
|
184
|
+
var mapLibrePromise;
|
|
185
|
+
var mapLibre;
|
|
186
|
+
var workerUrl;
|
|
187
|
+
var appliedWorkerUrl;
|
|
188
|
+
function configureTileflowMapLibre(configuration) {
|
|
189
|
+
const nextWorkerUrl = configuration.workerUrl;
|
|
190
|
+
if (!isWorkerUrl(nextWorkerUrl)) {
|
|
191
|
+
throw new TypeError("Tileflow MapLibre workerUrl must be a non-empty string.");
|
|
192
|
+
}
|
|
193
|
+
if (workerUrl !== void 0 && workerUrl !== nextWorkerUrl) {
|
|
194
|
+
throw new Error("Tileflow MapLibre workerUrl is already configured.");
|
|
195
|
+
}
|
|
196
|
+
if (appliedWorkerUrl !== void 0 && appliedWorkerUrl !== nextWorkerUrl) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"Tileflow MapLibre workerUrl is already configured: a map started with the bundler-provided worker. Configure the worker before the first map."
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
workerUrl = nextWorkerUrl;
|
|
202
|
+
if (mapLibre) applyWorkerUrl(mapLibre);
|
|
203
|
+
}
|
|
204
|
+
function loadTileflowMapLibre() {
|
|
205
|
+
mapLibrePromise ??= import("maplibre-gl").then(resolveMapLibreModule).then(configureTileflowMapLibreModule).catch((error) => {
|
|
206
|
+
mapLibrePromise = void 0;
|
|
207
|
+
throw error;
|
|
208
|
+
});
|
|
209
|
+
return mapLibrePromise;
|
|
210
|
+
}
|
|
211
|
+
function resolveMapLibreModule(module) {
|
|
212
|
+
const defaultExport = module.default;
|
|
213
|
+
return defaultExport ?? module;
|
|
214
|
+
}
|
|
215
|
+
function configureTileflowMapLibreModule(maplibregl) {
|
|
216
|
+
applyWorkerUrl(maplibregl);
|
|
217
|
+
mapLibre = maplibregl;
|
|
218
|
+
return maplibregl;
|
|
219
|
+
}
|
|
220
|
+
function applyWorkerUrl(maplibregl) {
|
|
221
|
+
const nextWorkerUrl = workerUrl ?? readBundlerWorkerUrl();
|
|
222
|
+
if (nextWorkerUrl === void 0 || nextWorkerUrl === appliedWorkerUrl) return;
|
|
223
|
+
maplibregl.setWorkerUrl(nextWorkerUrl);
|
|
224
|
+
appliedWorkerUrl = nextWorkerUrl;
|
|
225
|
+
}
|
|
226
|
+
function readBundlerWorkerUrl() {
|
|
227
|
+
const value = globalThis[bundlerWorkerUrlKey];
|
|
228
|
+
return isWorkerUrl(value) ? value : void 0;
|
|
229
|
+
}
|
|
230
|
+
function isWorkerUrl(value) {
|
|
231
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/reconcile.ts
|
|
235
|
+
function createTileflowReconciler() {
|
|
236
|
+
const memos = /* @__PURE__ */ new Map();
|
|
237
|
+
const effects = /* @__PURE__ */ new Map();
|
|
238
|
+
const stableValues = /* @__PURE__ */ new Map();
|
|
239
|
+
let pending = [];
|
|
240
|
+
return {
|
|
241
|
+
commit() {
|
|
242
|
+
const batch = pending;
|
|
243
|
+
pending = [];
|
|
244
|
+
const errors = [];
|
|
245
|
+
for (const { key } of batch) {
|
|
246
|
+
const slot = effects.get(key);
|
|
247
|
+
const cleanup = slot?.cleanup;
|
|
248
|
+
if (slot) slot.cleanup = void 0;
|
|
249
|
+
if (!cleanup) continue;
|
|
250
|
+
try {
|
|
251
|
+
cleanup();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
errors.push(error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const { key, run } of batch) {
|
|
257
|
+
const slot = effects.get(key);
|
|
258
|
+
if (!slot) continue;
|
|
259
|
+
try {
|
|
260
|
+
const cleanup = run();
|
|
261
|
+
slot.cleanup = typeof cleanup === "function" ? cleanup : void 0;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
errors.push(error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
throwCollected(errors, "Tileflow map update failed.");
|
|
267
|
+
},
|
|
268
|
+
dispose() {
|
|
269
|
+
pending = [];
|
|
270
|
+
const cleanups = [...effects.values()].map((slot) => slot.cleanup).filter((cleanup) => cleanup !== void 0).reverse();
|
|
271
|
+
effects.clear();
|
|
272
|
+
memos.clear();
|
|
273
|
+
stableValues.clear();
|
|
274
|
+
runTileflowCleanups(cleanups);
|
|
275
|
+
},
|
|
276
|
+
effect(key, deps, run) {
|
|
277
|
+
const slot = effects.get(key);
|
|
278
|
+
if (slot && sameDependencies(slot.deps, deps)) return;
|
|
279
|
+
if (slot) slot.deps = deps;
|
|
280
|
+
else effects.set(key, { deps });
|
|
281
|
+
pending = pending.filter((entry) => entry.key !== key);
|
|
282
|
+
pending.push({ key, run });
|
|
283
|
+
},
|
|
284
|
+
memo(key, deps, compute) {
|
|
285
|
+
const slot = memos.get(key);
|
|
286
|
+
if (slot && sameDependencies(slot.deps, deps)) return slot.value;
|
|
287
|
+
const value = compute();
|
|
288
|
+
memos.set(key, { deps, value });
|
|
289
|
+
return value;
|
|
290
|
+
},
|
|
291
|
+
stable(key, value) {
|
|
292
|
+
if (stableValues.has(key)) {
|
|
293
|
+
const previous = stableValues.get(key);
|
|
294
|
+
if (areEquivalentTileflowOptionValues(previous, value)) return previous;
|
|
295
|
+
}
|
|
296
|
+
stableValues.set(key, value);
|
|
297
|
+
return value;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function sameDependencies(left, right) {
|
|
302
|
+
return left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
|
|
303
|
+
}
|
|
304
|
+
function areEquivalentTileflowOptionValues(left, right, seen = /* @__PURE__ */ new WeakMap()) {
|
|
305
|
+
if (Object.is(left, right)) return true;
|
|
306
|
+
if (!left || !right || typeof left !== "object" || typeof right !== "object") return false;
|
|
307
|
+
const seenRight = seen.get(left);
|
|
308
|
+
if (seenRight) return seenRight === right;
|
|
309
|
+
seen.set(left, right);
|
|
310
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
311
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => areEquivalentTileflowOptionValues(value, right[index], seen));
|
|
312
|
+
}
|
|
313
|
+
if (!isPlainObject(left) || !isPlainObject(right)) return false;
|
|
314
|
+
const leftKeys = Object.keys(left);
|
|
315
|
+
const rightKeys = Object.keys(right);
|
|
316
|
+
return leftKeys.length === rightKeys.length && leftKeys.every(
|
|
317
|
+
(key) => Object.hasOwn(right, key) && areEquivalentTileflowOptionValues(left[key], right[key], seen)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
function isPlainObject(value) {
|
|
321
|
+
const prototype = Object.getPrototypeOf(value);
|
|
322
|
+
return prototype === Object.prototype || prototype === null;
|
|
323
|
+
}
|
|
324
|
+
function runTileflowCleanups(cleanups) {
|
|
325
|
+
const errors = [];
|
|
326
|
+
for (const cleanup of cleanups) {
|
|
327
|
+
try {
|
|
328
|
+
cleanup();
|
|
329
|
+
} catch (error) {
|
|
330
|
+
errors.push(error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
throwCollected(errors, "Tileflow map cleanup failed.");
|
|
334
|
+
}
|
|
335
|
+
function throwCollected(errors, message) {
|
|
336
|
+
if (errors.length === 1) throw errors[0];
|
|
337
|
+
if (errors.length > 1) throw new AggregateError(errors, message);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/views.ts
|
|
341
|
+
function tileflowAnnotationViewContext(target, state) {
|
|
342
|
+
const { annotation } = target;
|
|
343
|
+
const content = target.kind === "marker" ? annotation.marker?.content : target.kind === "tooltip" ? annotation.tooltip?.content : annotation.popup?.content;
|
|
344
|
+
return {
|
|
345
|
+
annotation,
|
|
346
|
+
close: target.close,
|
|
347
|
+
...content ? { content } : {},
|
|
348
|
+
target: { annotation, coordinate: annotation.coordinate, kind: "annotation" },
|
|
349
|
+
...content?.kind === "view" ? { viewName: content.name } : {},
|
|
350
|
+
...target.kind === "marker" && state ? { state } : {}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function tileflowSemanticViewContext(target) {
|
|
354
|
+
return {
|
|
355
|
+
close: target.close,
|
|
356
|
+
content: target.content,
|
|
357
|
+
target: target.target,
|
|
358
|
+
...target.viewName ? { viewName: target.viewName } : {}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function createTileflowMapViewHost(options) {
|
|
362
|
+
const mounted = /* @__PURE__ */ new Map();
|
|
363
|
+
let signature = "[]";
|
|
364
|
+
let settledSignature = "[]";
|
|
365
|
+
let frames = [];
|
|
366
|
+
let disposed = false;
|
|
367
|
+
const cancelFrames = () => {
|
|
368
|
+
for (const frame of frames) options.window.cancelAnimationFrame(frame);
|
|
369
|
+
frames = [];
|
|
370
|
+
};
|
|
371
|
+
const draw = (view, context) => {
|
|
372
|
+
view.context = context;
|
|
373
|
+
try {
|
|
374
|
+
if (view.instance && view.instance.update) {
|
|
375
|
+
view.instance.update(context);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
view.instance?.destroy?.();
|
|
379
|
+
view.element.replaceChildren();
|
|
380
|
+
view.instance = view.renderer(view.element, context);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
view.instance = void 0;
|
|
383
|
+
options.onError(error);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
const unmount = (view) => {
|
|
387
|
+
view.detachPointer?.();
|
|
388
|
+
try {
|
|
389
|
+
view.instance?.destroy?.();
|
|
390
|
+
} catch (error) {
|
|
391
|
+
options.onError(error);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const markerContext = (view) => tileflowAnnotationViewContext(view.target, {
|
|
395
|
+
...view.pointer,
|
|
396
|
+
selected: view.selected
|
|
397
|
+
});
|
|
398
|
+
const watchPointer = (view) => {
|
|
399
|
+
const element = view.element.closest('[data-tileflow-interaction="marker"]');
|
|
400
|
+
if (!element) return void 0;
|
|
401
|
+
const set = (patch) => {
|
|
402
|
+
const next = { ...view.pointer, ...patch };
|
|
403
|
+
if (next.focused === view.pointer.focused && next.hovered === view.pointer.hovered) return;
|
|
404
|
+
view.pointer = next;
|
|
405
|
+
draw(view, markerContext(view));
|
|
406
|
+
};
|
|
407
|
+
const enter = () => set({ hovered: true });
|
|
408
|
+
const leave = () => set({ hovered: false });
|
|
409
|
+
const focus = () => set({ focused: true });
|
|
410
|
+
const blur = () => set({ focused: false });
|
|
411
|
+
element.addEventListener("pointerenter", enter);
|
|
412
|
+
element.addEventListener("pointerleave", leave);
|
|
413
|
+
element.addEventListener("focusin", focus);
|
|
414
|
+
element.addEventListener("focusout", blur);
|
|
415
|
+
return () => {
|
|
416
|
+
element.removeEventListener("pointerenter", enter);
|
|
417
|
+
element.removeEventListener("pointerleave", leave);
|
|
418
|
+
element.removeEventListener("focusin", focus);
|
|
419
|
+
element.removeEventListener("focusout", blur);
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
return {
|
|
423
|
+
dispose() {
|
|
424
|
+
disposed = true;
|
|
425
|
+
cancelFrames();
|
|
426
|
+
for (const view of mounted.values()) unmount(view);
|
|
427
|
+
mounted.clear();
|
|
428
|
+
},
|
|
429
|
+
isSettled: () => settledSignature === signature,
|
|
430
|
+
sync(targets, views) {
|
|
431
|
+
if (disposed) return;
|
|
432
|
+
const selected = new Set(
|
|
433
|
+
targets.annotations.filter((target) => target.kind === "popup").map((target) => target.annotation.id)
|
|
434
|
+
);
|
|
435
|
+
const next = /* @__PURE__ */ new Map();
|
|
436
|
+
for (const target of targets.annotations) {
|
|
437
|
+
next.set(`annotation:${target.key}`, { kind: "annotation", target });
|
|
438
|
+
}
|
|
439
|
+
for (const target of targets.semantic) {
|
|
440
|
+
next.set(`semantic:${target.key}`, { kind: "semantic", target });
|
|
441
|
+
}
|
|
442
|
+
for (const [key, view] of mounted) {
|
|
443
|
+
if (next.has(key)) continue;
|
|
444
|
+
unmount(view);
|
|
445
|
+
mounted.delete(key);
|
|
446
|
+
}
|
|
447
|
+
for (const [key, entry] of next) {
|
|
448
|
+
const viewKind = entry.target.kind;
|
|
449
|
+
const renderer = views[viewKind];
|
|
450
|
+
const isMarker = entry.kind === "annotation" && viewKind === "marker";
|
|
451
|
+
const isSelected = isMarker && selected.has(
|
|
452
|
+
entry.target.annotation.id
|
|
453
|
+
);
|
|
454
|
+
const existing = mounted.get(key);
|
|
455
|
+
if (!renderer) {
|
|
456
|
+
if (existing) {
|
|
457
|
+
unmount(existing);
|
|
458
|
+
mounted.delete(key);
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (existing && existing.renderer === renderer && existing.target === entry.target && existing.selected === isSelected) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (existing && existing.renderer !== renderer) {
|
|
466
|
+
unmount(existing);
|
|
467
|
+
mounted.delete(key);
|
|
468
|
+
}
|
|
469
|
+
const view = mounted.get(key) ?? {
|
|
470
|
+
context: void 0,
|
|
471
|
+
element: entry.target.container,
|
|
472
|
+
instance: void 0,
|
|
473
|
+
pointer: { focused: false, hovered: false },
|
|
474
|
+
renderer,
|
|
475
|
+
selected: isSelected,
|
|
476
|
+
target: entry.target
|
|
477
|
+
};
|
|
478
|
+
view.element = entry.target.container;
|
|
479
|
+
view.selected = isSelected;
|
|
480
|
+
view.target = entry.target;
|
|
481
|
+
if (!mounted.has(key)) {
|
|
482
|
+
mounted.set(key, view);
|
|
483
|
+
if (isMarker) view.detachPointer = watchPointer(view);
|
|
484
|
+
}
|
|
485
|
+
draw(
|
|
486
|
+
view,
|
|
487
|
+
entry.kind === "semantic" ? tileflowSemanticViewContext(entry.target) : isMarker ? markerContext(view) : tileflowAnnotationViewContext(
|
|
488
|
+
entry.target
|
|
489
|
+
)
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const nextSignature = JSON.stringify([...mounted.keys()].sort());
|
|
493
|
+
if (nextSignature === signature) return;
|
|
494
|
+
signature = nextSignature;
|
|
495
|
+
cancelFrames();
|
|
496
|
+
const first = options.window.requestAnimationFrame(() => {
|
|
497
|
+
const second = options.window.requestAnimationFrame(() => {
|
|
498
|
+
frames = [];
|
|
499
|
+
if (disposed) return;
|
|
500
|
+
settledSignature = nextSignature;
|
|
501
|
+
options.onChange();
|
|
502
|
+
});
|
|
503
|
+
frames = [second];
|
|
504
|
+
});
|
|
505
|
+
frames = [first];
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/map.ts
|
|
511
|
+
var noInteractions = [];
|
|
512
|
+
var statusAttributes = [
|
|
513
|
+
"data-tileflow-capture-id",
|
|
514
|
+
"data-tileflow-map",
|
|
515
|
+
"data-tileflow-state",
|
|
516
|
+
"data-tileflow-theme"
|
|
517
|
+
];
|
|
518
|
+
function createTileflowMap(container, initialOptions, createOptions = {}) {
|
|
519
|
+
if (!container || typeof container !== "object" || !container.ownerDocument) {
|
|
520
|
+
throw new TypeError("createTileflowMap needs an HTML element to mount the map in.");
|
|
521
|
+
}
|
|
522
|
+
const ownerWindow = container.ownerDocument.defaultView;
|
|
523
|
+
if (!ownerWindow) throw new Error("A Tileflow map needs a browser document.");
|
|
524
|
+
const browserWindow = ownerWindow;
|
|
525
|
+
assertTileflowMapStyleOptions(initialOptions);
|
|
526
|
+
if (initialOptions.views && initialOptions.renderTargets) {
|
|
527
|
+
throw new TypeError("A Tileflow map takes views or renderTargets, not both.");
|
|
528
|
+
}
|
|
529
|
+
const sessionSource = createOptions.sessionSource ?? "dom";
|
|
530
|
+
const statusElement = createOptions.statusElement === void 0 ? container : createOptions.statusElement;
|
|
531
|
+
const captureId = normalizeTileflowCaptureId(createOptions.captureId);
|
|
532
|
+
const controlledInteractionOwnership = initialOptions.interactionState !== void 0;
|
|
533
|
+
let options = { ...initialOptions };
|
|
534
|
+
let destroyed = false;
|
|
535
|
+
let reconciling = false;
|
|
536
|
+
let dirty = false;
|
|
537
|
+
let scheduled = false;
|
|
538
|
+
let manifestResolution = { key: "", map: null, state: "loading" };
|
|
539
|
+
let systemColorScheme = "light";
|
|
540
|
+
let captureState = "loading";
|
|
541
|
+
let themeTransitionState = "idle";
|
|
542
|
+
let activeRuntimeResource;
|
|
543
|
+
let annotationRenderTargets = [];
|
|
544
|
+
let semanticRenderTargets = [];
|
|
545
|
+
let annotationBridgeDiagnostic;
|
|
546
|
+
let semanticBridgeDiagnostic;
|
|
547
|
+
let stateBridgeDiagnostic;
|
|
548
|
+
let rendererBridgeDiagnostic;
|
|
549
|
+
let viewBridgeDiagnostic;
|
|
550
|
+
let annotationDiagnostics = [];
|
|
551
|
+
let semanticDiagnostics = [];
|
|
552
|
+
let reportedDeclarativeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
553
|
+
let imageSize = null;
|
|
554
|
+
let managedImage = null;
|
|
555
|
+
let imageReadinessRun = 0;
|
|
556
|
+
let map = null;
|
|
557
|
+
let scene;
|
|
558
|
+
let themeController;
|
|
559
|
+
let appearanceBinding;
|
|
560
|
+
let navigationBinding;
|
|
561
|
+
let interactionRuntime;
|
|
562
|
+
let semanticRuntime;
|
|
563
|
+
let interactionCoordinator;
|
|
564
|
+
let dataRuntime;
|
|
565
|
+
let appliedCameraFitKey;
|
|
566
|
+
let lastStatus;
|
|
567
|
+
let current;
|
|
568
|
+
let annotationsForRuntime = [];
|
|
569
|
+
let annotationTitles = /* @__PURE__ */ new Map();
|
|
570
|
+
let coordinatedInteractionState = prepareTileflowInteractionState(
|
|
571
|
+
initialOptions.interactionState,
|
|
572
|
+
initialOptions.defaultInteractionState
|
|
573
|
+
).state;
|
|
574
|
+
let dataInput = {};
|
|
575
|
+
let resolveReady;
|
|
576
|
+
let rejectReady;
|
|
577
|
+
let readySettled = false;
|
|
578
|
+
const ready = new Promise((resolve, reject) => {
|
|
579
|
+
resolveReady = resolve;
|
|
580
|
+
rejectReady = reject;
|
|
581
|
+
});
|
|
582
|
+
ready.catch(() => void 0);
|
|
583
|
+
const settleReady = (outcome) => {
|
|
584
|
+
if (readySettled) return;
|
|
585
|
+
readySettled = true;
|
|
586
|
+
if ("map" in outcome) resolveReady(outcome.map);
|
|
587
|
+
else rejectReady(outcome.error);
|
|
588
|
+
};
|
|
589
|
+
const reconciler = createTileflowReconciler();
|
|
590
|
+
const reportError = (code, cause) => {
|
|
591
|
+
reportTileflowMapError(options.onError, code, cause);
|
|
592
|
+
if (code === "manifest" || code === "runtime") {
|
|
593
|
+
settleReady({ error: cause instanceof Error ? cause : new Error(String(cause)) });
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const reportDiagnostic = (diagnostic) => options.onInteractionDiagnostic?.(diagnostic);
|
|
597
|
+
const viewHost = createTileflowMapViewHost({
|
|
598
|
+
onChange: () => invalidate(),
|
|
599
|
+
onError(error) {
|
|
600
|
+
console.error("Failed to render a Tileflow map view", error);
|
|
601
|
+
viewBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
602
|
+
"A Tileflow map view could not be rendered."
|
|
603
|
+
);
|
|
604
|
+
reportDiagnostic(viewBridgeDiagnostic);
|
|
605
|
+
invalidate();
|
|
606
|
+
},
|
|
607
|
+
window: browserWindow
|
|
608
|
+
});
|
|
609
|
+
function invalidate() {
|
|
610
|
+
if (destroyed) return;
|
|
611
|
+
if (reconciling) {
|
|
612
|
+
dirty = true;
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (scheduled) return;
|
|
616
|
+
scheduled = true;
|
|
617
|
+
queueMicrotask(() => {
|
|
618
|
+
scheduled = false;
|
|
619
|
+
reconcile();
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function derive() {
|
|
623
|
+
const memo = reconciler.memo;
|
|
624
|
+
const stable = reconciler.stable;
|
|
625
|
+
const {
|
|
626
|
+
annotationClusters,
|
|
627
|
+
annotations,
|
|
628
|
+
bounds,
|
|
629
|
+
dataOverlays,
|
|
630
|
+
dataSources,
|
|
631
|
+
defaultInteractionState,
|
|
632
|
+
fitAnnotations,
|
|
633
|
+
fitBoundsOptions,
|
|
634
|
+
interactionState,
|
|
635
|
+
source,
|
|
636
|
+
theme,
|
|
637
|
+
themeBlend,
|
|
638
|
+
timeOfDay
|
|
639
|
+
} = options;
|
|
640
|
+
const interactions = options.interactions ?? noInteractions;
|
|
641
|
+
const runtimeSource = memo("runtimeSource", [source.manifestUrl, source.map], () => ({
|
|
642
|
+
manifestUrl: source.manifestUrl,
|
|
643
|
+
map: source.map
|
|
644
|
+
}));
|
|
645
|
+
const mapName = runtimeSource.map;
|
|
646
|
+
const manifestUrl = runtimeSource.manifestUrl ?? defaultTileflowManifestUrl;
|
|
647
|
+
const preparedAnnotations = memo(
|
|
648
|
+
"annotations",
|
|
649
|
+
[annotations],
|
|
650
|
+
() => prepareTileflowAnnotations(annotations)
|
|
651
|
+
);
|
|
652
|
+
const preparedInteractions = memo(
|
|
653
|
+
"interactions",
|
|
654
|
+
[interactions],
|
|
655
|
+
() => prepareTileflowInteractionBindings(interactions)
|
|
656
|
+
);
|
|
657
|
+
const preparedInteractionState = memo(
|
|
658
|
+
"interactionState",
|
|
659
|
+
[defaultInteractionState, interactionState],
|
|
660
|
+
() => prepareTileflowInteractionState(
|
|
661
|
+
interactionState,
|
|
662
|
+
defaultInteractionState,
|
|
663
|
+
controlledInteractionOwnership
|
|
664
|
+
)
|
|
665
|
+
);
|
|
666
|
+
const cameraFit = memo(
|
|
667
|
+
"cameraFit",
|
|
668
|
+
[annotations, bounds, fitAnnotations, fitBoundsOptions],
|
|
669
|
+
() => resolveTileflowCameraFit({
|
|
670
|
+
annotations: annotations ?? [],
|
|
671
|
+
bounds,
|
|
672
|
+
fitAnnotations,
|
|
673
|
+
fitBoundsOptions
|
|
674
|
+
})
|
|
675
|
+
);
|
|
676
|
+
const mapOptions = stable("mapOptions", options.mapOptions);
|
|
677
|
+
const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
|
|
678
|
+
const currentManifest = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
|
|
679
|
+
const manifestMap = currentManifest.state === "ready" ? currentManifest.map : null;
|
|
680
|
+
const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
|
|
681
|
+
const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
|
|
682
|
+
const themeResolution = memo(
|
|
683
|
+
"themeResolution",
|
|
684
|
+
[blendThemeName, manifestMap, systemColorScheme, theme, themeBlendKey, timeOfDay],
|
|
685
|
+
() => {
|
|
686
|
+
const selection = blendThemeName ?? theme;
|
|
687
|
+
if (!manifestMap) {
|
|
688
|
+
return {
|
|
689
|
+
error: false,
|
|
690
|
+
name: selection && selection !== "system" ? selection : void 0
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
if (themeBlendKey) {
|
|
695
|
+
for (const name of JSON.parse(themeBlendKey)) {
|
|
696
|
+
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
error: false,
|
|
701
|
+
name: resolveTileflowRuntimeTheme(
|
|
702
|
+
manifestMap,
|
|
703
|
+
selection,
|
|
704
|
+
systemColorScheme,
|
|
705
|
+
themeBlendKey ? void 0 : timeOfDay
|
|
706
|
+
).name
|
|
707
|
+
};
|
|
708
|
+
} catch (cause) {
|
|
709
|
+
return { cause, error: true, name: void 0 };
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
);
|
|
713
|
+
const manifestView = resolveTileflowRuntimeView({ manifestMap });
|
|
714
|
+
const manifestCenter = stable(
|
|
715
|
+
"manifestCenter",
|
|
716
|
+
normalizeTileflowRuntimeCenter(manifestView.center)
|
|
717
|
+
);
|
|
718
|
+
const resolvedCenter = stable(
|
|
719
|
+
"center",
|
|
720
|
+
options.center ?? mapOptions?.center ?? manifestCenter
|
|
721
|
+
);
|
|
722
|
+
const resolvedZoom = options.zoom ?? mapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
|
|
723
|
+
const resolvedBearing = mapOptions?.bearing ?? manifestView.bearing;
|
|
724
|
+
const resolvedPitch = mapOptions?.pitch ?? manifestView.pitch;
|
|
725
|
+
const resolvedInteractive = options.interactive ?? mapOptions?.interactive ?? true;
|
|
726
|
+
const isImageMode = options.mode === "image";
|
|
727
|
+
const renderers = tileflowMapRenderers(options);
|
|
728
|
+
const { hasMarkerRenderer, hasPopupRenderer, hasTooltipRenderer } = renderers;
|
|
729
|
+
const hasInteractionConfiguration = hasTileflowMapInteractionConfiguration(options, renderers);
|
|
730
|
+
const declarativeDiagnostics = memo(
|
|
731
|
+
"declarativeDiagnostics",
|
|
732
|
+
[
|
|
733
|
+
hasInteractionConfiguration,
|
|
734
|
+
hasPopupRenderer,
|
|
735
|
+
hasTooltipRenderer,
|
|
736
|
+
interactions,
|
|
737
|
+
isImageMode,
|
|
738
|
+
preparedAnnotations,
|
|
739
|
+
preparedInteractions,
|
|
740
|
+
preparedInteractionState
|
|
741
|
+
],
|
|
742
|
+
() => tileflowMapDeclarativeDiagnostics({
|
|
743
|
+
hasInteractionConfiguration,
|
|
744
|
+
hasPopupRenderer,
|
|
745
|
+
hasTooltipRenderer,
|
|
746
|
+
interactions,
|
|
747
|
+
isImageMode,
|
|
748
|
+
preparedAnnotations,
|
|
749
|
+
preparedInteractions,
|
|
750
|
+
preparedInteractionState
|
|
751
|
+
})
|
|
752
|
+
);
|
|
753
|
+
const runtimeStyle = memo(
|
|
754
|
+
"runtimeStyle",
|
|
755
|
+
[isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution],
|
|
756
|
+
() => isImageMode || themeResolution.error ? null : resolveTileflowRuntimeStyle({
|
|
757
|
+
colorScheme: systemColorScheme,
|
|
758
|
+
manifestMap,
|
|
759
|
+
source: runtimeSource,
|
|
760
|
+
theme: themeResolution.name
|
|
761
|
+
})
|
|
762
|
+
);
|
|
763
|
+
const blendResolution = memo(
|
|
764
|
+
"blendResolution",
|
|
765
|
+
[isImageMode, manifestMap, runtimeSource, themeBlendKey],
|
|
766
|
+
() => {
|
|
767
|
+
if (isImageMode || !manifestMap || !themeBlendKey) return { styles: null };
|
|
768
|
+
try {
|
|
769
|
+
return {
|
|
770
|
+
styles: resolveTileflowRuntimeThemeBlend({
|
|
771
|
+
blend: { position: 0, themes: JSON.parse(themeBlendKey) },
|
|
772
|
+
manifestMap,
|
|
773
|
+
source: runtimeSource
|
|
774
|
+
})
|
|
775
|
+
};
|
|
776
|
+
} catch (cause) {
|
|
777
|
+
return { cause, styles: null };
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
const resolvedAnalytics = memo(
|
|
782
|
+
"analytics",
|
|
783
|
+
[options.analytics, runtimeStyle],
|
|
784
|
+
() => mergeTileflowAnalytics(options.analytics, runtimeStyle?.analytics)
|
|
785
|
+
);
|
|
786
|
+
const staticImageCenter = stable(
|
|
787
|
+
"staticImageCenter",
|
|
788
|
+
normalizeTileflowRuntimeCenter(options.center ?? mapOptions?.center, manifestCenter)
|
|
789
|
+
);
|
|
790
|
+
const runtimeImageUrl = memo(
|
|
791
|
+
"runtimeImageUrl",
|
|
792
|
+
[
|
|
793
|
+
imageSize,
|
|
794
|
+
options.imageUrl,
|
|
795
|
+
isImageMode,
|
|
796
|
+
manifestMap,
|
|
797
|
+
staticImageCenter,
|
|
798
|
+
resolvedZoom,
|
|
799
|
+
systemColorScheme,
|
|
800
|
+
themeResolution
|
|
801
|
+
],
|
|
802
|
+
() => options.imageUrl ?? (isImageMode && !themeResolution.error ? resolveTileflowStaticImageUrl({
|
|
803
|
+
center: staticImageCenter,
|
|
804
|
+
colorScheme: systemColorScheme,
|
|
805
|
+
imageSize,
|
|
806
|
+
manifestMap,
|
|
807
|
+
theme: themeResolution.name,
|
|
808
|
+
zoom: resolvedZoom
|
|
809
|
+
}) : void 0)
|
|
810
|
+
);
|
|
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";
|
|
812
|
+
return {
|
|
813
|
+
annotationClusters,
|
|
814
|
+
blendResolution,
|
|
815
|
+
isImageMode,
|
|
816
|
+
runtimeImageUrl,
|
|
817
|
+
cameraFit,
|
|
818
|
+
dataOverlays,
|
|
819
|
+
dataSources,
|
|
820
|
+
declarativeDiagnostics,
|
|
821
|
+
hasMarkerRenderer,
|
|
822
|
+
hasPopupRenderer,
|
|
823
|
+
hasTooltipRenderer,
|
|
824
|
+
manifestRequestKey,
|
|
825
|
+
manifestUrl,
|
|
826
|
+
mapName,
|
|
827
|
+
mapOptions,
|
|
828
|
+
preparedAnnotations,
|
|
829
|
+
preparedInteractions,
|
|
830
|
+
preparedInteractionState,
|
|
831
|
+
resolvedAnalytics,
|
|
832
|
+
resolvedBearing,
|
|
833
|
+
resolvedCenter,
|
|
834
|
+
resolvedInteractive,
|
|
835
|
+
resolvedPitch,
|
|
836
|
+
resolvedZoom,
|
|
837
|
+
runtimeMapIdentity: `${manifestUrl}\0${runtimeSource.map}`,
|
|
838
|
+
runtimeResolutionState,
|
|
839
|
+
runtimeSource,
|
|
840
|
+
runtimeStyle,
|
|
841
|
+
themeBlendHold: themeBlend?.hold === true,
|
|
842
|
+
themeBlendKey,
|
|
843
|
+
themeBlendPosition: themeBlend?.position,
|
|
844
|
+
themeResolution
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function reconcile() {
|
|
848
|
+
if (destroyed) return;
|
|
849
|
+
reconciling = true;
|
|
850
|
+
try {
|
|
851
|
+
do {
|
|
852
|
+
dirty = false;
|
|
853
|
+
pass();
|
|
854
|
+
} while (dirty && !destroyed);
|
|
855
|
+
} finally {
|
|
856
|
+
reconciling = false;
|
|
857
|
+
}
|
|
858
|
+
emitStatus();
|
|
859
|
+
}
|
|
860
|
+
function pass() {
|
|
861
|
+
const effect = reconciler.effect;
|
|
862
|
+
const d = derive();
|
|
863
|
+
current = d;
|
|
864
|
+
if (d.preparedAnnotations.ok) {
|
|
865
|
+
annotationsForRuntime = d.preparedAnnotations.annotations;
|
|
866
|
+
annotationTitles = d.preparedAnnotations.titles;
|
|
867
|
+
}
|
|
868
|
+
dataInput = {
|
|
869
|
+
annotationClusters: d.annotationClusters,
|
|
870
|
+
annotations: d.preparedAnnotations.annotations,
|
|
871
|
+
dataOverlays: d.dataOverlays,
|
|
872
|
+
dataSources: d.dataSources
|
|
873
|
+
};
|
|
874
|
+
effect("scene", [options.vegetation, options.viewMode ?? "3d"], () => {
|
|
875
|
+
scene?.setVegetation(options.vegetation);
|
|
876
|
+
scene?.setViewMode(options.viewMode ?? "3d");
|
|
877
|
+
});
|
|
878
|
+
effect("imageSize", [d.isImageMode, options.imageUrl], () => {
|
|
879
|
+
imageSize = null;
|
|
880
|
+
invalidate();
|
|
881
|
+
if (!d.isImageMode || options.imageUrl) return;
|
|
882
|
+
const updateImageSize = () => {
|
|
883
|
+
const next = normalizeTileflowStaticImageSize({
|
|
884
|
+
height: container.clientHeight,
|
|
885
|
+
width: container.clientWidth
|
|
886
|
+
});
|
|
887
|
+
if (imageSize?.height === next.height && imageSize.width === next.width) return;
|
|
888
|
+
imageSize = next;
|
|
889
|
+
invalidate();
|
|
890
|
+
};
|
|
891
|
+
const resizeObserver = new ResizeObserver(updateImageSize);
|
|
892
|
+
resizeObserver.observe(container);
|
|
893
|
+
updateImageSize();
|
|
894
|
+
return () => resizeObserver.disconnect();
|
|
895
|
+
});
|
|
896
|
+
const ownsImage = d.isImageMode && options.image === void 0;
|
|
897
|
+
effect("managedImage", [ownsImage], () => {
|
|
898
|
+
if (!ownsImage) return;
|
|
899
|
+
const image = container.ownerDocument.createElement("img");
|
|
900
|
+
image.decoding = "async";
|
|
901
|
+
image.style.cssText = "display: block; height: 100%; object-fit: cover; width: 100%;";
|
|
902
|
+
managedImage = image;
|
|
903
|
+
invalidate();
|
|
904
|
+
return () => {
|
|
905
|
+
image.remove();
|
|
906
|
+
if (managedImage === image) managedImage = null;
|
|
907
|
+
};
|
|
908
|
+
});
|
|
909
|
+
const imageElement = ownsImage ? managedImage : options.image ?? null;
|
|
910
|
+
const alt = options.alt ?? "";
|
|
911
|
+
const imageLoading = options.imageLoading ?? "eager";
|
|
912
|
+
effect("managedImageAttributes", [managedImage, d.runtimeImageUrl, alt, imageLoading], () => {
|
|
913
|
+
const image = managedImage;
|
|
914
|
+
if (!image) return;
|
|
915
|
+
image.alt = alt;
|
|
916
|
+
image.loading = imageLoading;
|
|
917
|
+
if (!d.runtimeImageUrl) {
|
|
918
|
+
image.remove();
|
|
919
|
+
image.removeAttribute("src");
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
if (image.getAttribute("src") !== d.runtimeImageUrl) image.src = d.runtimeImageUrl;
|
|
923
|
+
if (!image.isConnected) container.append(image);
|
|
924
|
+
});
|
|
925
|
+
effect("imageReadiness", [d.isImageMode, d.runtimeImageUrl, imageElement], () => {
|
|
926
|
+
if (!d.isImageMode) return;
|
|
927
|
+
imageReadinessRun += 1;
|
|
928
|
+
activeRuntimeResource = d.runtimeImageUrl;
|
|
929
|
+
captureState = "loading";
|
|
930
|
+
invalidate();
|
|
931
|
+
const image = imageElement;
|
|
932
|
+
if (!image) return;
|
|
933
|
+
const markReady = async () => {
|
|
934
|
+
const run = ++imageReadinessRun;
|
|
935
|
+
try {
|
|
936
|
+
if (typeof image.decode === "function") await image.decode();
|
|
937
|
+
} catch {
|
|
938
|
+
if (!image.complete || image.naturalWidth === 0) {
|
|
939
|
+
if (imageReadinessRun === run) {
|
|
940
|
+
captureState = "error";
|
|
941
|
+
invalidate();
|
|
942
|
+
}
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (imageReadinessRun !== run) return;
|
|
947
|
+
captureState = "idle";
|
|
948
|
+
invalidate();
|
|
949
|
+
};
|
|
950
|
+
const fail = () => {
|
|
951
|
+
imageReadinessRun += 1;
|
|
952
|
+
captureState = "error";
|
|
953
|
+
invalidate();
|
|
954
|
+
};
|
|
955
|
+
const load = () => void markReady();
|
|
956
|
+
image.addEventListener("load", load);
|
|
957
|
+
image.addEventListener("error", fail);
|
|
958
|
+
if (image.complete && image.getAttribute("src")) {
|
|
959
|
+
if (image.naturalWidth === 0) fail();
|
|
960
|
+
else void markReady();
|
|
961
|
+
}
|
|
962
|
+
return () => {
|
|
963
|
+
image.removeEventListener("load", load);
|
|
964
|
+
image.removeEventListener("error", fail);
|
|
965
|
+
};
|
|
966
|
+
});
|
|
967
|
+
effect("systemColorScheme", [options.theme], () => {
|
|
968
|
+
if (options.theme !== "system") return;
|
|
969
|
+
systemColorScheme = getTileflowSystemColorScheme();
|
|
970
|
+
invalidate();
|
|
971
|
+
return subscribeTileflowSystemColorScheme((scheme) => {
|
|
972
|
+
systemColorScheme = scheme;
|
|
973
|
+
invalidate();
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
effect("declarativeDiagnostics", [d.declarativeDiagnostics], () => {
|
|
977
|
+
const previousKeys = reportedDeclarativeDiagnosticKeys;
|
|
978
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
979
|
+
for (const diagnostic of d.declarativeDiagnostics) {
|
|
980
|
+
const key = tileflowInteractionDiagnosticKey(diagnostic);
|
|
981
|
+
nextKeys.add(key);
|
|
982
|
+
if (!previousKeys.has(key)) reportDiagnostic(diagnostic);
|
|
983
|
+
}
|
|
984
|
+
reportedDeclarativeDiagnosticKeys = nextKeys;
|
|
985
|
+
});
|
|
986
|
+
effect("manifest", [d.manifestRequestKey, d.manifestUrl, d.mapName], () => {
|
|
987
|
+
let cancelled = false;
|
|
988
|
+
const { manifestRequestKey, manifestUrl, mapName } = d;
|
|
989
|
+
manifestResolution = { key: manifestRequestKey, map: null, state: "loading" };
|
|
990
|
+
invalidate();
|
|
991
|
+
loadTileflowManifest(manifestUrl).then((manifest) => {
|
|
992
|
+
if (cancelled) return;
|
|
993
|
+
const resolvedMap = manifest && mapName ? resolveTileflowManifestMap(manifest, mapName) : null;
|
|
994
|
+
if (!resolvedMap) {
|
|
995
|
+
reportError(
|
|
996
|
+
"manifest",
|
|
997
|
+
new Error(
|
|
998
|
+
manifest ? `The Tileflow manifest at ${manifestUrl} has no map named "${mapName}".` : `No Tileflow manifest was found at ${manifestUrl}.`
|
|
999
|
+
)
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
manifestResolution = {
|
|
1003
|
+
key: manifestRequestKey,
|
|
1004
|
+
map: resolvedMap,
|
|
1005
|
+
state: resolvedMap ? "ready" : "error"
|
|
1006
|
+
};
|
|
1007
|
+
invalidate();
|
|
1008
|
+
}).catch((error) => {
|
|
1009
|
+
if (cancelled) return;
|
|
1010
|
+
reportError("manifest", error);
|
|
1011
|
+
manifestResolution = { key: manifestRequestKey, map: null, state: "error" };
|
|
1012
|
+
invalidate();
|
|
1013
|
+
});
|
|
1014
|
+
return () => {
|
|
1015
|
+
cancelled = true;
|
|
1016
|
+
};
|
|
1017
|
+
});
|
|
1018
|
+
effect("camera", [d.resolvedBearing, d.resolvedCenter, d.resolvedPitch, d.resolvedZoom], () => {
|
|
1019
|
+
map?.jumpTo({
|
|
1020
|
+
bearing: d.resolvedBearing,
|
|
1021
|
+
center: d.resolvedCenter,
|
|
1022
|
+
pitch: d.resolvedPitch,
|
|
1023
|
+
zoom: d.resolvedZoom
|
|
1024
|
+
});
|
|
1025
|
+
});
|
|
1026
|
+
effect("navigationControl", [options.navigationControl, d.resolvedInteractive], () => {
|
|
1027
|
+
navigationBinding?.update(d.resolvedInteractive ? options.navigationControl : false);
|
|
1028
|
+
});
|
|
1029
|
+
effect("cameraFit", [d.cameraFit], () => {
|
|
1030
|
+
if (!map || appliedCameraFitKey === d.cameraFit.key) return;
|
|
1031
|
+
appliedCameraFitKey = d.cameraFit.key;
|
|
1032
|
+
if (d.cameraFit.error) reportError("camera", new Error(d.cameraFit.error));
|
|
1033
|
+
if (d.cameraFit.fit) fitCameraTo(map, d.cameraFit.fit);
|
|
1034
|
+
});
|
|
1035
|
+
effect("reconcileAnnotations", [d.preparedAnnotations], () => {
|
|
1036
|
+
if (!interactionRuntime || !d.preparedAnnotations.ok) return;
|
|
1037
|
+
reconcileAnnotations(interactionRuntime, d.preparedAnnotations.annotations);
|
|
1038
|
+
});
|
|
1039
|
+
effect(
|
|
1040
|
+
"reconcileData",
|
|
1041
|
+
[d.annotationClusters, d.dataOverlays, d.dataSources, d.preparedAnnotations],
|
|
1042
|
+
() => {
|
|
1043
|
+
dataRuntime?.reconcile(dataInput);
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
1046
|
+
effect("reconcileInteractions", [d.preparedInteractions], () => {
|
|
1047
|
+
if (!semanticRuntime || !d.preparedInteractions.ok) return;
|
|
1048
|
+
reconcileInteractions(semanticRuntime, d.preparedInteractions.bindings);
|
|
1049
|
+
});
|
|
1050
|
+
effect("controlledInteractionState", [d.preparedInteractionState], () => {
|
|
1051
|
+
const prepared = d.preparedInteractionState;
|
|
1052
|
+
if (!prepared.controlled || !prepared.ok) return;
|
|
1053
|
+
try {
|
|
1054
|
+
stateBridgeDiagnostic = void 0;
|
|
1055
|
+
interactionCoordinator?.setInteractionState(prepared.state);
|
|
1056
|
+
coordinatedInteractionState = prepared.state;
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
console.error("Failed to update Tileflow interaction state", error);
|
|
1059
|
+
stateBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
1060
|
+
"Tileflow interaction state could not be updated."
|
|
1061
|
+
);
|
|
1062
|
+
reportDiagnostic(stateBridgeDiagnostic);
|
|
1063
|
+
}
|
|
1064
|
+
invalidate();
|
|
1065
|
+
});
|
|
1066
|
+
effect(
|
|
1067
|
+
"customRenderers",
|
|
1068
|
+
[d.hasMarkerRenderer, d.hasPopupRenderer, d.hasTooltipRenderer],
|
|
1069
|
+
() => {
|
|
1070
|
+
if (!interactionRuntime && !semanticRuntime) return;
|
|
1071
|
+
try {
|
|
1072
|
+
rendererBridgeDiagnostic = void 0;
|
|
1073
|
+
interactionRuntime?.setCustomRenderers({
|
|
1074
|
+
marker: d.hasMarkerRenderer,
|
|
1075
|
+
popup: d.hasPopupRenderer,
|
|
1076
|
+
tooltip: d.hasTooltipRenderer
|
|
1077
|
+
});
|
|
1078
|
+
semanticRuntime?.setCustomRenderers({
|
|
1079
|
+
popup: d.hasPopupRenderer,
|
|
1080
|
+
tooltip: d.hasTooltipRenderer
|
|
1081
|
+
});
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
console.error("Failed to update Tileflow interaction renderers", error);
|
|
1084
|
+
rendererBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
1085
|
+
"Tileflow interaction renderers could not be updated."
|
|
1086
|
+
);
|
|
1087
|
+
reportDiagnostic(rendererBridgeDiagnostic);
|
|
1088
|
+
}
|
|
1089
|
+
invalidate();
|
|
1090
|
+
}
|
|
1091
|
+
);
|
|
1092
|
+
effect(
|
|
1093
|
+
"mount",
|
|
1094
|
+
[
|
|
1095
|
+
d.isImageMode,
|
|
1096
|
+
d.resolvedInteractive,
|
|
1097
|
+
d.runtimeMapIdentity,
|
|
1098
|
+
d.runtimeStyle !== null || d.themeResolution.error && map !== null,
|
|
1099
|
+
d.mapOptions
|
|
1100
|
+
],
|
|
1101
|
+
() => d.isImageMode ? void 0 : mountMap()
|
|
1102
|
+
);
|
|
1103
|
+
effect("theme", [d.runtimeStyle, d.themeBlendKey, d.themeResolution.error], () => {
|
|
1104
|
+
const controller = themeController;
|
|
1105
|
+
if (!controller || d.themeBlendKey) return;
|
|
1106
|
+
if (d.themeResolution.error) {
|
|
1107
|
+
void controller.setTheme(controller.getCurrent());
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
if (!d.runtimeStyle || controller.getCurrent() === d.runtimeStyle && !controller.getBlend()) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
activeRuntimeResource = d.runtimeStyle;
|
|
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
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
const themeResolutionCause = d.themeResolution.error ? d.themeResolution.cause : void 0;
|
|
1122
|
+
effect("themeError", [d.themeResolution.error, themeResolutionCause], () => {
|
|
1123
|
+
if (d.themeResolution.error) reportError("theme", themeResolutionCause);
|
|
1124
|
+
});
|
|
1125
|
+
effect("blendError", [d.blendResolution.cause], () => {
|
|
1126
|
+
if (d.blendResolution.cause !== void 0) reportError("blend", d.blendResolution.cause);
|
|
1127
|
+
});
|
|
1128
|
+
effect("appearance", [options.colors, options.emphasis], () => {
|
|
1129
|
+
appearanceBinding?.update(
|
|
1130
|
+
{ colors: options.colors, emphasis: options.emphasis },
|
|
1131
|
+
{ transition: options.themeTransition }
|
|
1132
|
+
);
|
|
1133
|
+
});
|
|
1134
|
+
effect(
|
|
1135
|
+
"blend",
|
|
1136
|
+
[d.blendResolution.styles, d.runtimeStyle, d.themeBlendHold, d.themeBlendPosition],
|
|
1137
|
+
() => {
|
|
1138
|
+
const controller = themeController;
|
|
1139
|
+
const styles = d.blendResolution.styles;
|
|
1140
|
+
const position = d.themeBlendPosition;
|
|
1141
|
+
if (!controller || !styles || position === void 0) return;
|
|
1142
|
+
if (d.runtimeStyle) {
|
|
1143
|
+
activeRuntimeResource = d.runtimeStyle;
|
|
1144
|
+
invalidate();
|
|
1145
|
+
}
|
|
1146
|
+
void controller.setBlend(
|
|
1147
|
+
{ hold: d.themeBlendHold, position, themes: styles },
|
|
1148
|
+
{ transition: options.themeTransition }
|
|
1149
|
+
).then((result) => {
|
|
1150
|
+
if (result.status === "failed") {
|
|
1151
|
+
reportError(
|
|
1152
|
+
"blend",
|
|
1153
|
+
result.error ?? new Error("The map themes could not be blended.")
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
);
|
|
1159
|
+
reconciler.commit();
|
|
1160
|
+
syncViews();
|
|
1161
|
+
}
|
|
1162
|
+
function reconcileAnnotations(runtime, annotations) {
|
|
1163
|
+
try {
|
|
1164
|
+
annotationBridgeDiagnostic = void 0;
|
|
1165
|
+
runtime.reconcile(annotations);
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
console.error("Failed to reconcile Tileflow annotations", error);
|
|
1168
|
+
annotationBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
1169
|
+
"Tileflow annotations could not be reconciled."
|
|
1170
|
+
);
|
|
1171
|
+
reportDiagnostic(annotationBridgeDiagnostic);
|
|
1172
|
+
}
|
|
1173
|
+
invalidate();
|
|
1174
|
+
}
|
|
1175
|
+
function reconcileInteractions(runtime, bindings) {
|
|
1176
|
+
try {
|
|
1177
|
+
semanticBridgeDiagnostic = void 0;
|
|
1178
|
+
runtime.reconcile(bindings);
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
console.error("Failed to reconcile Tileflow semantic interactions", error);
|
|
1181
|
+
semanticBridgeDiagnostic = createOverlayFailureDiagnostic(
|
|
1182
|
+
"Tileflow semantic interactions could not be reconciled."
|
|
1183
|
+
);
|
|
1184
|
+
reportDiagnostic(semanticBridgeDiagnostic);
|
|
1185
|
+
}
|
|
1186
|
+
invalidate();
|
|
1187
|
+
}
|
|
1188
|
+
let lastRenderTargets = {
|
|
1189
|
+
annotations: annotationRenderTargets,
|
|
1190
|
+
semantic: semanticRenderTargets
|
|
1191
|
+
};
|
|
1192
|
+
function syncViews() {
|
|
1193
|
+
if (lastRenderTargets.annotations !== annotationRenderTargets || lastRenderTargets.semantic !== semanticRenderTargets) {
|
|
1194
|
+
lastRenderTargets = { annotations: annotationRenderTargets, semantic: semanticRenderTargets };
|
|
1195
|
+
options.renderTargets?.onChange(lastRenderTargets);
|
|
1196
|
+
}
|
|
1197
|
+
viewHost.sync(lastRenderTargets, options.renderTargets ? {} : options.views ?? {});
|
|
1198
|
+
}
|
|
1199
|
+
function mountMap() {
|
|
1200
|
+
themeTransitionState = "idle";
|
|
1201
|
+
invalidate();
|
|
1202
|
+
const initialRuntimeStyle = current.runtimeStyle;
|
|
1203
|
+
if (!initialRuntimeStyle) return void 0;
|
|
1204
|
+
activeRuntimeResource = initialRuntimeStyle;
|
|
1205
|
+
captureState = "loading";
|
|
1206
|
+
let cancelled = false;
|
|
1207
|
+
let disposeMap;
|
|
1208
|
+
const { mapOptions, resolvedInteractive } = current;
|
|
1209
|
+
const resourceLifetime = new AbortController();
|
|
1210
|
+
const session = createTileflowSessionController({ source: sessionSource });
|
|
1211
|
+
const resourceTransform = createTileflowTransformRequest({
|
|
1212
|
+
getAnalytics: () => current.resolvedAnalytics,
|
|
1213
|
+
sessionController: session,
|
|
1214
|
+
sessionId: session.sessionId,
|
|
1215
|
+
transformRequest: mapOptions?.transformRequest ?? void 0
|
|
1216
|
+
});
|
|
1217
|
+
const styleLoadOptions = {
|
|
1218
|
+
signal: resourceLifetime.signal,
|
|
1219
|
+
transformRequest: (url, type) => resourceTransform(
|
|
1220
|
+
url,
|
|
1221
|
+
type
|
|
1222
|
+
)
|
|
1223
|
+
};
|
|
1224
|
+
void Promise.all([
|
|
1225
|
+
loadTileflowMapLibre(),
|
|
1226
|
+
loadTileflowRuntimeStyle(initialRuntimeStyle, styleLoadOptions).then(async (prepared) => {
|
|
1227
|
+
await loadTileflowStyleFonts(prepared.style, { fontFaces: prepared.fontFaces });
|
|
1228
|
+
return prepared;
|
|
1229
|
+
})
|
|
1230
|
+
]).then(([maplibregl, prepared]) => {
|
|
1231
|
+
if (cancelled || destroyed) return;
|
|
1232
|
+
const setupCleanups = [];
|
|
1233
|
+
let setupDisposed = false;
|
|
1234
|
+
const registerCleanup = (cleanup) => setupCleanups.push(cleanup);
|
|
1235
|
+
disposeMap = () => {
|
|
1236
|
+
if (setupDisposed) return;
|
|
1237
|
+
setupDisposed = true;
|
|
1238
|
+
try {
|
|
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
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
const mapScene = attachTileflowMap3D({
|
|
1347
|
+
map: mapLibreMap,
|
|
1348
|
+
...controller ? { controller } : {},
|
|
1349
|
+
vegetation: options.vegetation,
|
|
1350
|
+
viewMode: options.viewMode ?? "3d",
|
|
1351
|
+
load: () => import("@tileflow/core/3d"),
|
|
1352
|
+
onError: (error) => reportError("scene", error),
|
|
1353
|
+
// Direct tile readers use HTTP, not MapLibre's custom World protocol.
|
|
1354
|
+
transformRequest: createTileflowTransformRequest({
|
|
1355
|
+
getAnalytics: () => current.resolvedAnalytics,
|
|
1356
|
+
sessionController: session,
|
|
1357
|
+
sessionId: session.sessionId,
|
|
1358
|
+
transformRequest: (url) => mapOptions?.transformRequest?.(
|
|
1359
|
+
url,
|
|
1360
|
+
"Tile"
|
|
1361
|
+
)
|
|
1362
|
+
})
|
|
1363
|
+
});
|
|
1364
|
+
scene = mapScene;
|
|
1365
|
+
registerCleanup(() => {
|
|
1366
|
+
mapScene.dispose();
|
|
1367
|
+
if (scene === mapScene) scene = void 0;
|
|
1368
|
+
});
|
|
1369
|
+
const coordinator = createTileflowMapLibreInteractionCoordinator({
|
|
1370
|
+
...controlledInteractionOwnership ? { interactionState: coordinatedInteractionState } : { defaultInteractionState: coordinatedInteractionState },
|
|
1371
|
+
onInteractionStateChange(state) {
|
|
1372
|
+
if (!controlledInteractionOwnership) coordinatedInteractionState = state;
|
|
1373
|
+
options.onInteractionStateChange?.(state);
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
interactionCoordinator = coordinator;
|
|
1377
|
+
registerCleanup(() => coordinator.dispose());
|
|
1378
|
+
const createOverlay = ({
|
|
1379
|
+
container: overlayContainer,
|
|
1380
|
+
kind
|
|
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(),
|
|
1402
|
+
map: mapLibreMap,
|
|
1403
|
+
onInteractionStateChange: coordinator.requestInteractionState,
|
|
1404
|
+
onDiagnostic: reportDiagnostic,
|
|
1405
|
+
updateMarker(_marker, { annotation, element }) {
|
|
1406
|
+
element.title = annotationTitles.get(annotation.id) ?? annotation.ariaLabel;
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
interactionRuntime = annotationRuntime;
|
|
1410
|
+
registerCleanup(() => annotationRuntime.dispose());
|
|
1411
|
+
const mapDataRuntime = createTileflowMapLibreDataRuntime({
|
|
1412
|
+
createMarker: ({ element }) => wrapMapLibrePositioned(new maplibregl.Marker({ element })),
|
|
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;
|
|
1425
|
+
});
|
|
1426
|
+
const featureRuntime = createTileflowMapLibreSemanticDomRuntime({
|
|
1427
|
+
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
1428
|
+
createOverlay,
|
|
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
|
+
);
|
|
1476
|
+
annotationDiagnostics = annotationRuntime.getDiagnostics();
|
|
1477
|
+
annotationRenderTargets = annotationRuntime.getRenderTargets();
|
|
1478
|
+
semanticDiagnostics = featureRuntime.getDiagnostics();
|
|
1479
|
+
semanticRenderTargets = featureRuntime.getRenderTargets();
|
|
1480
|
+
reconcileAnnotations(annotationRuntime, annotationsForRuntime);
|
|
1481
|
+
mapDataRuntime.reconcile(dataInput);
|
|
1482
|
+
const initialInteractions = current.preparedInteractions;
|
|
1483
|
+
if (initialInteractions.ok)
|
|
1484
|
+
reconcileInteractions(featureRuntime, initialInteractions.bindings);
|
|
1485
|
+
const navigation = createTileflowNavigationControlBinding({
|
|
1486
|
+
createControl: (controlOptions) => new maplibregl.NavigationControl(controlOptions),
|
|
1487
|
+
map: mapLibreMap
|
|
1488
|
+
});
|
|
1489
|
+
navigationBinding = navigation;
|
|
1490
|
+
registerCleanup(() => {
|
|
1491
|
+
navigation.dispose();
|
|
1492
|
+
if (navigationBinding === navigation) navigationBinding = void 0;
|
|
1493
|
+
});
|
|
1494
|
+
navigation.update(current.resolvedInteractive ? options.navigationControl : false);
|
|
1495
|
+
const initialFit = current.cameraFit;
|
|
1496
|
+
appliedCameraFitKey = initialFit.key;
|
|
1497
|
+
if (initialFit.error) reportError("camera", new Error(initialFit.error));
|
|
1498
|
+
if (initialFit.fit) fitCameraTo(mapLibreMap, initialFit.fit, 0);
|
|
1499
|
+
const resizeObserver = new ResizeObserver(() => mapLibreMap.resize());
|
|
1500
|
+
registerCleanup(() => resizeObserver.disconnect());
|
|
1501
|
+
resizeObserver.observe(container);
|
|
1502
|
+
const lifecycle = attachTileflowMapLifecycle({
|
|
1503
|
+
getSession: () => {
|
|
1504
|
+
const analyticsForLoad = current.resolvedAnalytics;
|
|
1505
|
+
return {
|
|
1506
|
+
analytics: analyticsForLoad,
|
|
1507
|
+
styleId: analyticsForLoad?.styleId ?? (typeof initialRuntimeStyle.style === "string" ? initialRuntimeStyle.style : current.mapName)
|
|
1508
|
+
};
|
|
1509
|
+
},
|
|
1510
|
+
map: mapLibreMap,
|
|
1511
|
+
onLoad: (loadedMap) => {
|
|
1512
|
+
settleReady({ map: loadedMap });
|
|
1513
|
+
options.onLoad?.(loadedMap);
|
|
1514
|
+
},
|
|
1515
|
+
scheduler: {
|
|
1516
|
+
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
1517
|
+
requestFrame: (callback) => browserWindow.requestAnimationFrame(callback)
|
|
1518
|
+
},
|
|
1519
|
+
sessionStarter,
|
|
1520
|
+
setState: (state) => {
|
|
1521
|
+
captureState = state;
|
|
1522
|
+
invalidate();
|
|
1523
|
+
},
|
|
1524
|
+
subscribe: (subscribedMap, event, listener) => {
|
|
1525
|
+
const subscription = subscribedMap.on(event, listener);
|
|
1526
|
+
return () => subscription.unsubscribe();
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
registerCleanup(() => lifecycle.dispose());
|
|
1530
|
+
const latestRuntimeStyle = current.runtimeStyle;
|
|
1531
|
+
const latestBlend = current.blendResolution.styles && current.themeBlendPosition !== void 0 ? {
|
|
1532
|
+
hold: current.themeBlendHold,
|
|
1533
|
+
position: current.themeBlendPosition,
|
|
1534
|
+
themes: current.blendResolution.styles
|
|
1535
|
+
} : null;
|
|
1536
|
+
if (controller && latestBlend) {
|
|
1537
|
+
if (latestRuntimeStyle) activeRuntimeResource = latestRuntimeStyle;
|
|
1538
|
+
void controller.setBlend(latestBlend, { transition: options.themeTransition });
|
|
1539
|
+
} else if (controller && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
|
|
1540
|
+
activeRuntimeResource = latestRuntimeStyle;
|
|
1541
|
+
void controller.setTheme(latestRuntimeStyle, { transition: options.themeTransition });
|
|
1542
|
+
}
|
|
1543
|
+
invalidate();
|
|
1544
|
+
}).catch((error) => {
|
|
1545
|
+
resourceLifetime.abort();
|
|
1546
|
+
try {
|
|
1547
|
+
disposeMap?.();
|
|
1548
|
+
} catch (cleanupError) {
|
|
1549
|
+
reportError("runtime", cleanupError);
|
|
1550
|
+
}
|
|
1551
|
+
if (!cancelled && !destroyed) {
|
|
1552
|
+
reportError("runtime", error);
|
|
1553
|
+
captureState = "error";
|
|
1554
|
+
invalidate();
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
return () => {
|
|
1558
|
+
cancelled = true;
|
|
1559
|
+
resourceLifetime.abort();
|
|
1560
|
+
disposeMap?.();
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function computeStatus() {
|
|
1564
|
+
const d = current;
|
|
1565
|
+
const diagnostics = [
|
|
1566
|
+
...d.declarativeDiagnostics,
|
|
1567
|
+
...annotationBridgeDiagnostic ? [annotationBridgeDiagnostic] : [],
|
|
1568
|
+
...semanticBridgeDiagnostic ? [semanticBridgeDiagnostic] : [],
|
|
1569
|
+
...stateBridgeDiagnostic ? [stateBridgeDiagnostic] : [],
|
|
1570
|
+
...rendererBridgeDiagnostic ? [rendererBridgeDiagnostic] : [],
|
|
1571
|
+
...viewBridgeDiagnostic ? [viewBridgeDiagnostic] : [],
|
|
1572
|
+
...annotationDiagnostics,
|
|
1573
|
+
...semanticDiagnostics
|
|
1574
|
+
];
|
|
1575
|
+
const interactionReadiness = diagnostics.some(
|
|
1576
|
+
(diagnostic) => diagnostic.level === "error"
|
|
1577
|
+
) ? "error" : viewHost.isSettled() ? "idle" : "loading";
|
|
1578
|
+
const currentCaptureState = activeRuntimeResource === (d.isImageMode ? d.runtimeImageUrl : d.runtimeStyle) ? captureState : "loading";
|
|
1579
|
+
const parts = [
|
|
1580
|
+
currentCaptureState,
|
|
1581
|
+
interactionReadiness,
|
|
1582
|
+
d.runtimeResolutionState,
|
|
1583
|
+
themeTransitionState
|
|
1584
|
+
];
|
|
1585
|
+
return {
|
|
1586
|
+
diagnostics,
|
|
1587
|
+
readiness: parts.includes("error") ? "error" : parts.every((part) => part === "idle") ? "idle" : "loading",
|
|
1588
|
+
theme: d.themeResolution.name,
|
|
1589
|
+
imageUrl: d.isImageMode ? d.runtimeImageUrl : void 0
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
function emitStatus() {
|
|
1593
|
+
if (destroyed) return;
|
|
1594
|
+
const status = computeStatus();
|
|
1595
|
+
if (lastStatus && lastStatus.readiness === status.readiness && lastStatus.theme === status.theme && lastStatus.imageUrl === status.imageUrl && lastStatus.diagnostics.length === status.diagnostics.length && lastStatus.diagnostics.every((diagnostic, index) => diagnostic === status.diagnostics[index])) {
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
lastStatus = status;
|
|
1599
|
+
if (current.isImageMode && status.readiness === "idle") settleReady({ map: null });
|
|
1600
|
+
if (statusElement) {
|
|
1601
|
+
setAttribute(statusElement, "data-tileflow-capture-id", captureId);
|
|
1602
|
+
setAttribute(statusElement, "data-tileflow-map", current.mapName);
|
|
1603
|
+
setAttribute(statusElement, "data-tileflow-theme", status.theme);
|
|
1604
|
+
setAttribute(statusElement, "data-tileflow-state", status.readiness);
|
|
1605
|
+
}
|
|
1606
|
+
options.onStatusChange?.(status);
|
|
1607
|
+
}
|
|
1608
|
+
function update(next) {
|
|
1609
|
+
if (destroyed) return;
|
|
1610
|
+
const merged = { ...options, ...next };
|
|
1611
|
+
assertTileflowMapStyleOptions(merged);
|
|
1612
|
+
if (merged.views && merged.renderTargets) {
|
|
1613
|
+
throw new TypeError("A Tileflow map takes views or renderTargets, not both.");
|
|
1614
|
+
}
|
|
1615
|
+
options = merged;
|
|
1616
|
+
if (reconciling) {
|
|
1617
|
+
dirty = true;
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
reconcile();
|
|
1621
|
+
}
|
|
1622
|
+
reconcile();
|
|
1623
|
+
return {
|
|
1624
|
+
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
|
+
},
|
|
1638
|
+
getInteractionState: () => interactionCoordinator?.getInteractionState() ?? coordinatedInteractionState,
|
|
1639
|
+
getMap: () => map,
|
|
1640
|
+
getRenderTargets: () => lastRenderTargets,
|
|
1641
|
+
getStatus: () => lastStatus ?? computeStatus(),
|
|
1642
|
+
getThemeController: () => themeController ?? null,
|
|
1643
|
+
ready,
|
|
1644
|
+
setAnnotations: (annotations) => update({ annotations }),
|
|
1645
|
+
setBounds: (bounds, fitBoundsOptions) => update(fitBoundsOptions === void 0 ? { bounds } : { bounds, fitBoundsOptions }),
|
|
1646
|
+
setColors: (colors) => update({ colors }),
|
|
1647
|
+
setEmphasis: (emphasis) => update({ emphasis }),
|
|
1648
|
+
setInteractions: (interactions) => update({ interactions }),
|
|
1649
|
+
setInteractionState(state) {
|
|
1650
|
+
if (controlledInteractionOwnership) {
|
|
1651
|
+
update({ interactionState: state });
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
const parsed = tileflowInteractionStateSchema2.parse(state);
|
|
1655
|
+
coordinatedInteractionState = parsed;
|
|
1656
|
+
interactionCoordinator?.setInteractionState(parsed);
|
|
1657
|
+
},
|
|
1658
|
+
setTheme: (theme) => update({ theme }),
|
|
1659
|
+
setThemeBlend: (themeBlend) => update({ themeBlend }),
|
|
1660
|
+
setView: (view) => update(view),
|
|
1661
|
+
setViews: (views) => update({ views }),
|
|
1662
|
+
update
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
function tileflowMapRenderers(options) {
|
|
1666
|
+
const external = options.renderTargets;
|
|
1667
|
+
return external ? {
|
|
1668
|
+
hasMarkerRenderer: external.marker === true,
|
|
1669
|
+
hasPopupRenderer: external.popup === true,
|
|
1670
|
+
hasTooltipRenderer: external.tooltip === true
|
|
1671
|
+
} : {
|
|
1672
|
+
hasMarkerRenderer: options.views?.marker !== void 0,
|
|
1673
|
+
hasPopupRenderer: options.views?.popup !== void 0,
|
|
1674
|
+
hasTooltipRenderer: options.views?.tooltip !== void 0
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
function hasTileflowMapInteractionConfiguration(options, renderers) {
|
|
1678
|
+
return options.annotationClusters !== void 0 || options.annotations !== void 0 || options.dataOverlays !== void 0 || options.dataSources !== void 0 || options.defaultInteractionState !== void 0 || options.interactionState !== void 0 || options.interactions !== void 0 || options.onInteractionDiagnostic !== void 0 || options.onInteractionEvent !== void 0 || options.onInteractionStateChange !== void 0 || renderers.hasMarkerRenderer || renderers.hasPopupRenderer || renderers.hasTooltipRenderer;
|
|
1679
|
+
}
|
|
1680
|
+
function tileflowMapDeclarativeDiagnostics(input) {
|
|
1681
|
+
const modeDiagnostic = input.isImageMode ? imageModeMapInteractionDiagnostic(
|
|
1682
|
+
input.preparedAnnotations.annotations.length,
|
|
1683
|
+
input.interactions.length,
|
|
1684
|
+
input.hasInteractionConfiguration
|
|
1685
|
+
) : void 0;
|
|
1686
|
+
return [
|
|
1687
|
+
...modeDiagnostic ? [modeDiagnostic] : [],
|
|
1688
|
+
...input.preparedAnnotations.diagnostics,
|
|
1689
|
+
...input.preparedInteractions.diagnostics,
|
|
1690
|
+
...input.preparedInteractionState.diagnostics,
|
|
1691
|
+
...input.isImageMode ? [] : unusedTileflowRendererDiagnostics({
|
|
1692
|
+
annotations: input.preparedAnnotations.annotations,
|
|
1693
|
+
bindings: input.interactions,
|
|
1694
|
+
hasPopupRenderer: input.hasPopupRenderer,
|
|
1695
|
+
hasTooltipRenderer: input.hasTooltipRenderer
|
|
1696
|
+
})
|
|
1697
|
+
];
|
|
1698
|
+
}
|
|
1699
|
+
function getTileflowMapInitialStatus(options, controlledInteractionOwnership = options.interactionState !== void 0) {
|
|
1700
|
+
const interactions = options.interactions ?? noInteractions;
|
|
1701
|
+
const renderers = tileflowMapRenderers(options);
|
|
1702
|
+
const isImageMode = options.mode === "image";
|
|
1703
|
+
const diagnostics = tileflowMapDeclarativeDiagnostics({
|
|
1704
|
+
hasInteractionConfiguration: hasTileflowMapInteractionConfiguration(options, renderers),
|
|
1705
|
+
hasPopupRenderer: renderers.hasPopupRenderer,
|
|
1706
|
+
hasTooltipRenderer: renderers.hasTooltipRenderer,
|
|
1707
|
+
interactions,
|
|
1708
|
+
isImageMode,
|
|
1709
|
+
preparedAnnotations: prepareTileflowAnnotations(options.annotations),
|
|
1710
|
+
preparedInteractions: prepareTileflowInteractionBindings(interactions),
|
|
1711
|
+
preparedInteractionState: prepareTileflowInteractionState(
|
|
1712
|
+
options.interactionState,
|
|
1713
|
+
options.defaultInteractionState,
|
|
1714
|
+
controlledInteractionOwnership
|
|
1715
|
+
)
|
|
1716
|
+
});
|
|
1717
|
+
const selection = options.themeBlend ? nearestTileflowBlendTheme(options.themeBlend) : options.theme;
|
|
1718
|
+
return {
|
|
1719
|
+
diagnostics,
|
|
1720
|
+
imageUrl: isImageMode ? options.imageUrl : void 0,
|
|
1721
|
+
readiness: diagnostics.some((diagnostic) => diagnostic.level === "error") ? "error" : "loading",
|
|
1722
|
+
theme: selection && selection !== "system" ? selection : void 0
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function setAttribute(element, name, value) {
|
|
1726
|
+
if (value === void 0) element.removeAttribute(name);
|
|
1727
|
+
else if (element.getAttribute(name) !== value) element.setAttribute(name, value);
|
|
1728
|
+
}
|
|
1729
|
+
function fitCameraTo(map, fit, duration) {
|
|
1730
|
+
map.fitBounds(fit.bounds, {
|
|
1731
|
+
...fit.options.maxZoom === void 0 ? {} : { maxZoom: fit.options.maxZoom },
|
|
1732
|
+
...fit.options.padding === void 0 ? {} : { padding: fit.options.padding },
|
|
1733
|
+
...duration !== void 0 ? { duration } : fit.options.duration === void 0 ? {} : { duration: fit.options.duration }
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
function wrapMapLibrePositioned(positioned) {
|
|
1737
|
+
return {
|
|
1738
|
+
addTo(map) {
|
|
1739
|
+
positioned.addTo(map);
|
|
1740
|
+
},
|
|
1741
|
+
remove() {
|
|
1742
|
+
positioned.remove();
|
|
1743
|
+
},
|
|
1744
|
+
setLngLat(coordinate) {
|
|
1745
|
+
positioned.setLngLat([coordinate[0], coordinate[1]]);
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
function createTileflowMapLibrePoiMap(map) {
|
|
1750
|
+
const poiMap = map;
|
|
1751
|
+
return {
|
|
1752
|
+
getStyle: () => map.getStyle(),
|
|
1753
|
+
on(event, listener) {
|
|
1754
|
+
return poiMap.on(event, listener);
|
|
1755
|
+
},
|
|
1756
|
+
queryRenderedFeatures(point, queryOptions) {
|
|
1757
|
+
return map.queryRenderedFeatures(point, {
|
|
1758
|
+
layers: [...queryOptions.layers]
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
export {
|
|
1764
|
+
configureTileflowMapLibre,
|
|
1765
|
+
createTileflowMap,
|
|
1766
|
+
getTileflowMapInitialStatus,
|
|
1767
|
+
loadTileflowMapLibre
|
|
1768
|
+
};
|