@opendfieldmap/map 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.
@@ -0,0 +1,1027 @@
1
+ // packages/map/src/runtime.ts
2
+ import L4 from "leaflet";
3
+ import "leaflet.markercluster";
4
+ import {
5
+ createOEMPointUrl,
6
+ fetchOEMJson,
7
+ fromOEMLeafletPosition,
8
+ getOEMRegion,
9
+ normalizeOEMLocale,
10
+ resolveOEMAsset,
11
+ toOEMLeafletPosition
12
+ } from "@opendfieldmap/core";
13
+
14
+ // packages/map/src/atlos/smoothTileLayer.ts
15
+ import L from "leaflet";
16
+ var SmoothTileLayer = class extends L.TileLayer {
17
+ _setZoomTransform(level, center, zoom) {
18
+ const map = this._map;
19
+ if (!map)
20
+ return;
21
+ const scale = map.getZoomScale(zoom, level.zoom);
22
+ const translate = level.origin.multiplyBy(scale).subtract(map._getNewPixelOrigin(center, zoom));
23
+ if (L.Browser.any3d) {
24
+ L.DomUtil.setTransform(level.el, translate, scale);
25
+ } else {
26
+ L.DomUtil.setPosition(level.el, translate);
27
+ }
28
+ }
29
+ };
30
+
31
+ // packages/map/src/atlos/smoothWheelZoom.ts
32
+ import L2 from "leaflet";
33
+ import { WHEEL_GESTURE_IDLE_MS, WheelInputRouter } from "trackpad-input";
34
+ var ZOOM_PER_WHEEL_PIXEL = 3e-3;
35
+ var TRACKPAD_PINCH_ZOOM_PER_PIXEL = 0.01;
36
+ var INERTIA_INITIAL_FACTOR = 0.07;
37
+ var INERTIA_MAX_STEP = 8e-3;
38
+ var INERTIA_FRICTION_PER_FRAME = 0.72;
39
+ var INERTIA_STOP_THRESHOLD = 25e-5;
40
+ var FRAME_DURATION = 1e3 / 60;
41
+ var TRACKPAD_OVERDRAG_MAX_PX = 96;
42
+ var TRACKPAD_OVERDRAG_RESISTANCE = 0.65;
43
+ var TRACKPAD_OVERDRAG_SETTLE_THRESHOLD_PX = 80;
44
+ var TRACKPAD_OVERDRAG_SETTLE_MS = 48;
45
+ var CONTINUOUS_ZOOM_EVENT_DATA = {
46
+ pinch: true,
47
+ round: false
48
+ };
49
+ var installSubpixelPixelOrigin = (map) => {
50
+ map._getNewPixelOrigin = function(center, zoom) {
51
+ return this.project(center, zoom).subtract(this.getSize().divideBy(2)).add(this._getMapPanePos());
52
+ };
53
+ map.latLngToLayerPoint = function(latLng) {
54
+ return this.project(latLng, this.getZoom()).subtract(this.getPixelOrigin());
55
+ };
56
+ };
57
+ var SmoothWheelZoom = class {
58
+ map;
59
+ container;
60
+ inertiaEnabled;
61
+ panEnabled;
62
+ zoomEnabled;
63
+ wheelInputRouter;
64
+ zoomFrame = null;
65
+ panFrame = null;
66
+ endTimer = null;
67
+ panTailTimer = null;
68
+ targetZoom = null;
69
+ anchorPoint = null;
70
+ anchorLatLng = null;
71
+ zoomVelocity = 0;
72
+ lastZoomInputTime = null;
73
+ inertiaStep = 0;
74
+ lastFrameTime = null;
75
+ pendingPanOffset = L2.point(0, 0);
76
+ panRawCenterPoint = null;
77
+ gestureMode = null;
78
+ gestureActive = false;
79
+ trackpadDragging = false;
80
+ overdragSettling = false;
81
+ disposed = false;
82
+ constructor(map, options = {}) {
83
+ this.map = map;
84
+ this.container = map.getContainer();
85
+ this.inertiaEnabled = options.enableInertia ?? true;
86
+ this.panEnabled = options.panEnabled ?? true;
87
+ this.zoomEnabled = options.zoomEnabled ?? true;
88
+ this.wheelInputRouter = new WheelInputRouter({
89
+ onPan: (event) => this.handleRoutedWheel("pan", event),
90
+ onZoom: (event) => this.handleRoutedWheel("zoom", event)
91
+ });
92
+ installSubpixelPixelOrigin(this.map);
93
+ map.scrollWheelZoom.disable();
94
+ this.container.addEventListener("wheel", this.handleWheel, {
95
+ passive: false
96
+ });
97
+ map.once("unload", this.dispose);
98
+ }
99
+ dispose = () => {
100
+ if (this.disposed)
101
+ return;
102
+ this.disposed = true;
103
+ this.container.removeEventListener("wheel", this.handleWheel);
104
+ this.map.off("unload", this.dispose);
105
+ this.clearScheduledWork();
106
+ };
107
+ handleWheel = (event) => {
108
+ event.preventDefault();
109
+ event.stopPropagation();
110
+ this.finishLeafletZoomAnimation();
111
+ const gestureMode = this.wheelInputRouter.route(event);
112
+ if (gestureMode === "pending" && this.overdragSettling) {
113
+ this.clearOverdragSettlement();
114
+ }
115
+ };
116
+ handleRoutedWheel(gestureMode, event) {
117
+ if (gestureMode === "pan" && !this.panEnabled || gestureMode === "zoom" && !this.zoomEnabled)
118
+ return;
119
+ if (this.overdragSettling && gestureMode === "pan") {
120
+ this.schedulePanTailRelease();
121
+ return;
122
+ }
123
+ if (this.overdragSettling)
124
+ this.clearOverdragSettlement();
125
+ this.routeWheelEvent(gestureMode, event);
126
+ }
127
+ routeWheelEvent(gestureMode, event) {
128
+ if (this.gestureActive && this.gestureMode !== null && this.gestureMode !== gestureMode) {
129
+ this.finishGesture();
130
+ }
131
+ this.gestureMode = gestureMode;
132
+ if (gestureMode === "pan") {
133
+ this.handleTrackpadPan(event);
134
+ return;
135
+ }
136
+ this.handleZoom(event);
137
+ }
138
+ handleZoom(event) {
139
+ const wheelDelta = L2.DomEvent.getWheelDelta(event);
140
+ if (!wheelDelta)
141
+ return;
142
+ const zoomDelta = event.ctrlKey && event.deltaMode === 0 ? -event.deltaY * TRACKPAD_PINCH_ZOOM_PER_PIXEL : wheelDelta * ZOOM_PER_WHEEL_PIXEL;
143
+ const currentTarget = this.targetZoom ?? this.map.getZoom();
144
+ const nextTarget = this.map._limitZoom(currentTarget + zoomDelta);
145
+ this.updateAnchor(event);
146
+ this.targetZoom = nextTarget;
147
+ if (this.inertiaEnabled) {
148
+ this.updateZoomVelocity(nextTarget - currentTarget, performance.now());
149
+ this.inertiaStep = Math.max(-INERTIA_MAX_STEP, Math.min(INERTIA_MAX_STEP, this.zoomVelocity * INERTIA_INITIAL_FACTOR));
150
+ } else {
151
+ this.inertiaStep = 0;
152
+ }
153
+ if (nextTarget !== this.map.getZoom())
154
+ this.startGesture(true);
155
+ if (this.gestureActive && nextTarget !== this.map.getZoom() && this.zoomFrame === null) {
156
+ this.zoomFrame = requestAnimationFrame(this.applyZoomFrame);
157
+ }
158
+ this.scheduleGestureEnd();
159
+ }
160
+ handleTrackpadPan(event) {
161
+ const offset = L2.point(event.deltaX, event.deltaY);
162
+ if (offset.x === 0 && offset.y === 0) {
163
+ if (this.gestureActive && !this.overdragSettling) {
164
+ this.scheduleGestureEnd();
165
+ }
166
+ return;
167
+ }
168
+ const wasActive = this.gestureActive;
169
+ this.startGesture(false);
170
+ if (!wasActive) {
171
+ this.trackpadDragging = true;
172
+ this.map.fire("dragstart");
173
+ }
174
+ this.pendingPanOffset = this.pendingPanOffset.add(offset);
175
+ if (this.panFrame === null) {
176
+ this.panFrame = requestAnimationFrame(this.applyPanFrame);
177
+ }
178
+ if (!this.overdragSettling)
179
+ this.scheduleGestureEnd();
180
+ }
181
+ startGesture(zoomChanged) {
182
+ if (this.gestureActive)
183
+ return;
184
+ this.map._stop();
185
+ this.map._moveStart(zoomChanged, false);
186
+ this.gestureActive = true;
187
+ }
188
+ updateAnchor(event) {
189
+ const point = this.map.mouseEventToContainerPoint(event);
190
+ if (this.anchorPoint?.equals(point) && this.anchorLatLng !== null) {
191
+ return;
192
+ }
193
+ this.anchorPoint = point;
194
+ this.anchorLatLng = this.continuousContainerPointToLatLng(point);
195
+ }
196
+ continuousContainerPointToLatLng(point) {
197
+ const centerPoint = this.map.getSize().divideBy(2);
198
+ const center = this.map.getCenter();
199
+ const zoom = this.map.getZoom();
200
+ return this.map.unproject(this.map.project(center, zoom).add(point.subtract(centerPoint)), zoom);
201
+ }
202
+ updateZoomVelocity(delta, timestamp) {
203
+ if (this.lastZoomInputTime === null || delta * this.zoomVelocity <= 0 || timestamp - this.lastZoomInputTime > WHEEL_GESTURE_IDLE_MS) {
204
+ this.zoomVelocity = delta;
205
+ } else {
206
+ const elapsed = Math.max(8, Math.min(40, timestamp - this.lastZoomInputTime));
207
+ const frameDelta = delta * (FRAME_DURATION / elapsed);
208
+ this.zoomVelocity = this.zoomVelocity * 0.65 + frameDelta * 0.35;
209
+ }
210
+ this.lastZoomInputTime = timestamp;
211
+ }
212
+ applyZoomFrame = (timestamp) => {
213
+ this.zoomFrame = null;
214
+ if (!this.gestureActive)
215
+ return;
216
+ const elapsed = this.lastFrameTime === null ? FRAME_DURATION : Math.max(8, Math.min(34, timestamp - this.lastFrameTime));
217
+ this.lastFrameTime = timestamp;
218
+ const currentZoom = this.map.getZoom();
219
+ const directTarget = this.targetZoom;
220
+ let zoom = directTarget ?? currentZoom;
221
+ this.targetZoom = null;
222
+ if (this.inertiaEnabled && directTarget === null) {
223
+ if (Math.abs(this.inertiaStep) < INERTIA_STOP_THRESHOLD) {
224
+ this.inertiaStep = 0;
225
+ } else {
226
+ const zoomBeforeInertia = zoom;
227
+ zoom = this.map._limitZoom(zoom + this.inertiaStep * (elapsed / FRAME_DURATION));
228
+ if (zoom === zoomBeforeInertia) {
229
+ this.inertiaStep = 0;
230
+ } else {
231
+ this.inertiaStep *= Math.pow(INERTIA_FRICTION_PER_FRAME, elapsed / FRAME_DURATION);
232
+ }
233
+ }
234
+ }
235
+ if (zoom !== currentZoom) {
236
+ this.moveToZoom(zoom);
237
+ }
238
+ if (this.targetZoom !== null || this.inertiaEnabled && Math.abs(this.inertiaStep) >= INERTIA_STOP_THRESHOLD) {
239
+ this.zoomFrame = requestAnimationFrame(this.applyZoomFrame);
240
+ return;
241
+ }
242
+ this.lastFrameTime = null;
243
+ if (this.endTimer === null) {
244
+ this.finishGesture();
245
+ }
246
+ };
247
+ applyPanFrame = () => {
248
+ this.panFrame = null;
249
+ if (!this.gestureActive || this.gestureMode !== "pan")
250
+ return;
251
+ const offset = this.pendingPanOffset;
252
+ this.pendingPanOffset = L2.point(0, 0);
253
+ if (offset.x !== 0 || offset.y !== 0) {
254
+ const overdragAmount = this.applyConstrainedPan(offset);
255
+ this.map.fire("move").fire("drag");
256
+ if (overdragAmount >= TRACKPAD_OVERDRAG_SETTLE_THRESHOLD_PX) {
257
+ this.beginOverdragSettlement();
258
+ }
259
+ }
260
+ if (this.pendingPanOffset.x !== 0 || this.pendingPanOffset.y !== 0) {
261
+ this.panFrame = requestAnimationFrame(this.applyPanFrame);
262
+ return;
263
+ }
264
+ if (this.endTimer === null) {
265
+ this.finishGesture();
266
+ }
267
+ };
268
+ applyConstrainedPan(offset) {
269
+ const zoom = this.map.getZoom();
270
+ const currentCenterPoint = this.map.project(this.map.getCenter(), zoom);
271
+ this.panRawCenterPoint ??= currentCenterPoint;
272
+ this.panRawCenterPoint = this.panRawCenterPoint.add(offset);
273
+ const configuredBounds = this.map.options.maxBounds;
274
+ if (!configuredBounds) {
275
+ this.map._rawPanBy(offset);
276
+ return 0;
277
+ }
278
+ const maxBounds = configuredBounds instanceof L2.LatLngBounds ? configuredBounds : L2.latLngBounds(configuredBounds);
279
+ const rawCenter = this.map.unproject(this.panRawCenterPoint, zoom);
280
+ const limitedCenter = this.map._limitCenter(rawCenter, zoom, maxBounds);
281
+ const limitedCenterPoint = this.map.project(limitedCenter, zoom);
282
+ const rawOverdrag = this.panRawCenterPoint.subtract(limitedCenterPoint);
283
+ const visualOverdrag = L2.point(this.resistOverdrag(rawOverdrag.x), this.resistOverdrag(rawOverdrag.y));
284
+ const visualCenterPoint = limitedCenterPoint.add(visualOverdrag);
285
+ const visualOffset = visualCenterPoint.subtract(currentCenterPoint);
286
+ if (visualOffset.x !== 0 || visualOffset.y !== 0) {
287
+ this.map._rawPanBy(visualOffset);
288
+ }
289
+ return Math.max(Math.abs(visualOverdrag.x), Math.abs(visualOverdrag.y));
290
+ }
291
+ resistOverdrag(value) {
292
+ if (value === 0)
293
+ return 0;
294
+ const magnitude = TRACKPAD_OVERDRAG_MAX_PX * (1 - Math.exp(-Math.abs(value) * TRACKPAD_OVERDRAG_RESISTANCE / TRACKPAD_OVERDRAG_MAX_PX));
295
+ return Math.sign(value) * magnitude;
296
+ }
297
+ beginOverdragSettlement() {
298
+ if (this.overdragSettling)
299
+ return;
300
+ this.overdragSettling = true;
301
+ this.schedulePanTailRelease();
302
+ if (this.endTimer !== null)
303
+ window.clearTimeout(this.endTimer);
304
+ this.endTimer = window.setTimeout(this.handleGestureEnd, TRACKPAD_OVERDRAG_SETTLE_MS);
305
+ }
306
+ schedulePanTailRelease() {
307
+ if (this.panTailTimer !== null) {
308
+ window.clearTimeout(this.panTailTimer);
309
+ }
310
+ this.panTailTimer = window.setTimeout(() => {
311
+ this.panTailTimer = null;
312
+ this.overdragSettling = false;
313
+ }, WHEEL_GESTURE_IDLE_MS);
314
+ }
315
+ clearOverdragSettlement() {
316
+ this.overdragSettling = false;
317
+ if (this.panTailTimer !== null) {
318
+ window.clearTimeout(this.panTailTimer);
319
+ this.panTailTimer = null;
320
+ }
321
+ }
322
+ moveToZoom(zoom) {
323
+ if (this.anchorPoint === null || this.anchorLatLng === null)
324
+ return;
325
+ const centerPoint = this.map.getSize().divideBy(2);
326
+ const cursorOffset = this.anchorPoint.subtract(centerPoint);
327
+ let center = this.map.unproject(this.map.project(this.anchorLatLng, zoom).subtract(cursorOffset), zoom);
328
+ const configuredBounds = this.map.options.maxBounds;
329
+ if (configuredBounds) {
330
+ const maxBounds = configuredBounds instanceof L2.LatLngBounds ? configuredBounds : L2.latLngBounds(configuredBounds);
331
+ center = this.map._limitCenter(center, zoom, maxBounds);
332
+ }
333
+ this.map._move(center, zoom, CONTINUOUS_ZOOM_EVENT_DATA);
334
+ }
335
+ scheduleGestureEnd() {
336
+ if (this.endTimer !== null) {
337
+ window.clearTimeout(this.endTimer);
338
+ }
339
+ this.endTimer = window.setTimeout(this.handleGestureEnd, WHEEL_GESTURE_IDLE_MS);
340
+ }
341
+ handleGestureEnd = () => {
342
+ this.endTimer = null;
343
+ if (this.zoomFrame === null && this.panFrame === null && this.targetZoom === null && this.pendingPanOffset.x === 0 && this.pendingPanOffset.y === 0 && (!this.inertiaEnabled || Math.abs(this.inertiaStep) < INERTIA_STOP_THRESHOLD)) {
344
+ this.finishGesture();
345
+ }
346
+ };
347
+ finishGesture() {
348
+ if (this.endTimer !== null) {
349
+ window.clearTimeout(this.endTimer);
350
+ this.endTimer = null;
351
+ }
352
+ if (this.zoomFrame !== null) {
353
+ cancelAnimationFrame(this.zoomFrame);
354
+ this.zoomFrame = null;
355
+ }
356
+ if (this.panFrame !== null) {
357
+ cancelAnimationFrame(this.panFrame);
358
+ this.panFrame = null;
359
+ }
360
+ if (!this.gestureActive) {
361
+ this.resetVisualState();
362
+ return;
363
+ }
364
+ const wasTrackpadDrag = this.trackpadDragging;
365
+ this.gestureActive = false;
366
+ this.trackpadDragging = false;
367
+ if (wasTrackpadDrag)
368
+ this.map.fire("dragend");
369
+ this.map._moveEnd(this.gestureMode === "zoom");
370
+ this.resetVisualState();
371
+ }
372
+ finishLeafletZoomAnimation() {
373
+ if (this.map._animatingZoom) {
374
+ this.map._onZoomTransitionEnd?.();
375
+ }
376
+ }
377
+ resetVisualState() {
378
+ this.targetZoom = null;
379
+ this.anchorPoint = null;
380
+ this.anchorLatLng = null;
381
+ this.zoomVelocity = 0;
382
+ this.lastZoomInputTime = null;
383
+ this.inertiaStep = 0;
384
+ this.lastFrameTime = null;
385
+ this.pendingPanOffset = L2.point(0, 0);
386
+ this.panRawCenterPoint = null;
387
+ this.gestureMode = null;
388
+ this.trackpadDragging = false;
389
+ }
390
+ clearScheduledWork() {
391
+ if (this.zoomFrame !== null) {
392
+ cancelAnimationFrame(this.zoomFrame);
393
+ this.zoomFrame = null;
394
+ }
395
+ if (this.panFrame !== null) {
396
+ cancelAnimationFrame(this.panFrame);
397
+ this.panFrame = null;
398
+ }
399
+ if (this.endTimer !== null) {
400
+ window.clearTimeout(this.endTimer);
401
+ this.endTimer = null;
402
+ }
403
+ this.clearOverdragSettlement();
404
+ this.gestureActive = false;
405
+ this.resetVisualState();
406
+ this.wheelInputRouter.dispose();
407
+ }
408
+ };
409
+ var enableSmoothWheelZoom = (map, options) => new SmoothWheelZoom(map, options);
410
+
411
+ // packages/map/src/atlos/mapOverdrag.ts
412
+ import L3 from "leaflet";
413
+ var toMapBounds = (bounds) => {
414
+ if (!bounds)
415
+ return null;
416
+ if (bounds instanceof L3.LatLngBounds)
417
+ return bounds;
418
+ return Array.isArray(bounds) && bounds.length === 2 ? L3.latLngBounds(bounds[0], bounds[1]) : null;
419
+ };
420
+ var isMapOverdragged = (map, bounds) => {
421
+ const constrainedMap = map;
422
+ const center = map.getCenter();
423
+ const constrainedCenter = constrainedMap._limitCenter(center, map.getZoom(), bounds);
424
+ return map.project(center, map.getZoom()).distanceTo(map.project(constrainedCenter, map.getZoom())) > 1;
425
+ };
426
+
427
+ // packages/map/src/tileVersion.ts
428
+ var lookupOEMTile = (coverage, floor, zoom, x, y) => {
429
+ const ranges = coverage[String(zoom)]?.[floor.id]?.[String(y)] ?? [];
430
+ let offset = 0;
431
+ for (let index = 0; index < ranges.length; index += 2) {
432
+ const start = ranges[index];
433
+ const end = ranges[index + 1];
434
+ if (x >= start && x <= end) {
435
+ return {
436
+ covered: true,
437
+ version: floor.tileVersions?.[String(zoom)]?.[String(y)]?.[offset + x - start]
438
+ };
439
+ }
440
+ offset += end - start + 1;
441
+ }
442
+ return { covered: false };
443
+ };
444
+ var appendOEMTileVersion = (url, version) => version ? `${url}${url.includes("?") ? "&" : "?"}v=${encodeURIComponent(version)}` : url;
445
+
446
+ // packages/map/src/assets/ghicon.svg
447
+ var ghicon_default = '<svg width="83" height="81" viewBox="0 0 83 81" xmlns="http://www.w3.org/2000/svg">\n<path fill-rule="evenodd" clip-rule="evenodd" d="M41.3763 0C18.4963 0 0 18.5625 0 41.5268C0 59.8835 11.8512 75.422 28.292 80.9215C30.3475 81.335 31.1004 80.028 31.1004 78.9286C31.1004 77.9659 31.0327 74.666 31.0327 71.2277C19.5228 73.7032 17.1259 66.2774 17.1259 66.2774C15.2762 61.4647 12.5355 60.2277 12.5355 60.2277C8.76836 57.6838 12.8099 57.6838 12.8099 57.6838C16.9887 57.9589 19.1815 61.9464 19.1815 61.9464C22.8801 68.2712 28.84 66.4841 31.2376 65.3839C31.5798 62.7024 32.6766 60.8462 33.8411 59.8151C24.6612 58.8524 15.0027 55.2774 15.0027 39.3263C15.0027 34.7887 16.6457 31.0762 19.2492 28.1888C18.8385 27.1578 17.3995 22.8943 19.6608 17.188C19.6608 17.188 23.1545 16.0878 31.0318 21.4507C34.4044 20.5417 37.8825 20.0792 41.3763 20.0753C44.87 20.0753 48.4314 20.5571 51.72 21.4507C59.5982 16.0878 63.0919 17.188 63.0919 17.188C65.3532 22.8943 63.9134 27.1578 63.5026 28.1888C66.1747 31.0762 67.75 34.7887 67.75 39.3263C67.75 55.2774 58.0915 58.7832 48.843 59.8151C50.3505 61.1213 51.6514 63.596 51.6514 67.5152C51.6514 73.0839 51.5837 77.5533 51.5837 78.9277C51.5837 80.028 52.3374 81.335 54.3921 80.9224C70.8329 75.4211 82.6841 59.8835 82.6841 41.5268C82.7518 18.5625 64.1878 0 41.3763 0Z"/>\n</svg>\n';
448
+
449
+ // packages/map/src/runtime.ts
450
+ var mounted = /* @__PURE__ */ new WeakSet();
451
+ var CLUSTER_SUBCATEGORIES = /* @__PURE__ */ new Set(["boss", "collection", "mob", "natural", "valuable", "exploration"]);
452
+ var FEATURE_NAMES = ["points", "labels", "boundaries"];
453
+ var BRAND_URL = "https://oem.re/";
454
+ var GITHUB_URL = "https://github.com/Terra-Online/OEM-SDK";
455
+ var TERMS_URL = "https://blog.opendfieldmap.org/docs/tos#intellectual-property-and-copyright";
456
+ var cloneFilter = (filter) => ({
457
+ types: filter.types ? [...filter.types] : void 0,
458
+ subregions: filter.subregions ? [...filter.subregions] : void 0,
459
+ floorOnly: filter.floorOnly
460
+ });
461
+ var sameList = (left, right) => left === right || !!left && !!right && left.length === right.length && left.every((value, index) => value === right[index]);
462
+ var sameFilter = (left, right) => left.floorOnly === right.floorOnly && sameList(left.types, right.types) && sameList(left.subregions, right.subregions);
463
+ var OEMMarker = class extends L4.Marker {
464
+ update() {
465
+ const marker = this;
466
+ if (marker._icon && marker._map) marker._setPos(marker._map.latLngToLayerPoint(marker._latlng));
467
+ return this;
468
+ }
469
+ _animateZoom(event) {
470
+ const marker = this;
471
+ if (marker._map) marker._setPos(marker._map._latLngToNewLayerPoint(marker._latlng, event.zoom, event.center));
472
+ }
473
+ };
474
+ var CoveredTileLayer = class extends SmoothTileLayer {
475
+ constructor(url, options, coverage, floor) {
476
+ super(url, options);
477
+ this.coverage = coverage;
478
+ this.floor = floor;
479
+ }
480
+ getTileUrl(coords) {
481
+ const tile = lookupOEMTile(this.coverage, this.floor, coords.z, coords.x, coords.y);
482
+ return appendOEMTileVersion(super.getTileUrl(coords), tile.version);
483
+ }
484
+ _isValidTile(coords) {
485
+ const prototype = L4.GridLayer.prototype;
486
+ return prototype._isValidTile.call(this, coords) && lookupOEMTile(this.coverage, this.floor, coords.z, coords.x, coords.y).covered;
487
+ }
488
+ };
489
+ var OEM = class {
490
+ constructor(container, manifest, options) {
491
+ this.container = container;
492
+ this.manifest = manifest;
493
+ this.options = options;
494
+ if (mounted.has(container)) throw new Error("This container already hosts an OEM instance");
495
+ this.region = getOEMRegion(manifest, options.view?.regionId ?? options.regionId ?? manifest.defaultRegionId);
496
+ this.filter = cloneFilter(options.pointFilter ?? {});
497
+ this.markerClustering = options.markerClustering ?? true;
498
+ const initialFloor = options.view?.floorId ?? options.floorId ?? "M";
499
+ this.validateFloor(initialFloor);
500
+ if (options.view) this.validatePosition(options.view);
501
+ this.locale = options.locale ?? manifest.fallbackLocale;
502
+ this.resolvedLocale = normalizeOEMLocale(this.locale, Object.keys(manifest.locales), manifest.fallbackLocale);
503
+ this.root = document.createElement("div");
504
+ this.root.className = "mapRoot";
505
+ this.root.dataset.theme = options.theme ?? "light";
506
+ container.append(this.root);
507
+ mounted.add(container);
508
+ this.map = L4.map(this.root, {
509
+ crs: L4.CRS.Simple,
510
+ minZoom: 0,
511
+ maxZoom: 3,
512
+ zoomControl: false,
513
+ attributionControl: false,
514
+ dragging: !options.lockDrag,
515
+ touchZoom: !options.lockZoom,
516
+ boxZoom: !options.lockZoom,
517
+ keyboard: !options.lockZoom,
518
+ doubleClickZoom: false,
519
+ scrollWheelZoom: false,
520
+ zoomAnimation: true,
521
+ markerZoomAnimation: true,
522
+ fadeAnimation: true,
523
+ zoomSnap: 0,
524
+ zoomDelta: 0.25
525
+ });
526
+ this.wheel = enableSmoothWheelZoom(this.map, {
527
+ enableInertia: true,
528
+ panEnabled: !options.lockDrag,
529
+ zoomEnabled: !options.lockZoom
530
+ });
531
+ const pane = this.map.createPane("placeLabels");
532
+ pane.style.zIndex = "650";
533
+ pane.style.pointerEvents = "none";
534
+ this.pointsLayer.addTo(this.map);
535
+ this.labelsLayer.addTo(this.map);
536
+ this.boundariesLayer.addTo(this.map);
537
+ const credit = document.createElement("div");
538
+ credit.className = "attribution";
539
+ const github = document.createElement("a");
540
+ github.className = "attributionGithub";
541
+ github.href = GITHUB_URL;
542
+ github.target = "_blank";
543
+ github.rel = "noopener noreferrer";
544
+ github.setAttribute("aria-label", "GitHub");
545
+ github.innerHTML = ghicon_default;
546
+ const githubSvg = github.querySelector("svg");
547
+ githubSvg?.setAttribute("aria-hidden", "true");
548
+ githubSvg?.setAttribute("focusable", "false");
549
+ this.attributionBrand = document.createElement("a");
550
+ this.attributionBrand.className = "attributionBrand";
551
+ this.attributionBrand.href = BRAND_URL;
552
+ const separator = document.createElement("span");
553
+ separator.className = "attributionSeparator";
554
+ separator.textContent = "\xB7";
555
+ separator.setAttribute("aria-hidden", "true");
556
+ this.attributionLink = document.createElement("a");
557
+ this.attributionLink.className = "attributionLink";
558
+ this.attributionLink.href = TERMS_URL;
559
+ this.attributionLink.target = "_blank";
560
+ this.attributionLink.rel = "noopener noreferrer";
561
+ credit.append(github, this.attributionBrand, separator, this.attributionLink);
562
+ this.updateAttribution();
563
+ this.root.append(credit);
564
+ this.applyRegion(options.view);
565
+ this.setFloor(initialFloor);
566
+ this.map.on("moveend zoomend", this.emitView);
567
+ this.map.on("move drag", () => {
568
+ const bounds = toMapBounds(this.map.options.maxBounds);
569
+ this.root.classList.toggle("overdrag", !!bounds && isMapOverdragged(this.map, bounds));
570
+ });
571
+ this.map.on("moveend dragend zoomstart", () => this.root.classList.remove("overdrag"));
572
+ if (typeof ResizeObserver !== "undefined") {
573
+ this.observer = new ResizeObserver(() => this.resize());
574
+ this.observer.observe(container);
575
+ }
576
+ options.signal?.addEventListener("abort", this.destroy, { once: true });
577
+ }
578
+ destroyed = false;
579
+ map;
580
+ root;
581
+ region;
582
+ floorId = "M";
583
+ features = {};
584
+ listeners = /* @__PURE__ */ new Map();
585
+ requests = /* @__PURE__ */ new Map();
586
+ baseTiles;
587
+ floorTiles;
588
+ pointsLayer = L4.layerGroup();
589
+ pointClusters = /* @__PURE__ */ new Map();
590
+ labelsLayer = L4.layerGroup();
591
+ boundariesLayer = L4.layerGroup();
592
+ points = [];
593
+ types = {};
594
+ labels = [];
595
+ visibleLabelType;
596
+ messages = {};
597
+ filter = {};
598
+ markerClustering;
599
+ locale;
600
+ resolvedLocale;
601
+ attributionBrand;
602
+ attributionLink;
603
+ observer;
604
+ updatingView = false;
605
+ emittedView;
606
+ wheel;
607
+ /** Prevents calls against a released map instance. */
608
+ assertAlive() {
609
+ if (this.destroyed) throw new Error("OEM instance has been destroyed");
610
+ }
611
+ validateFloor(id) {
612
+ if (!this.region.floors.some((floor) => floor.id === id)) throw new Error(`Unknown floor ${id} in ${this.region.id}`);
613
+ }
614
+ validatePosition(position) {
615
+ toOEMLeafletPosition(position, this.region);
616
+ }
617
+ position(latlng) {
618
+ return fromOEMLeafletPosition(latlng.lat, latlng.lng, this.region, this.floorId);
619
+ }
620
+ emit(event, payload) {
621
+ this.listeners.get(event)?.forEach((handler) => handler(payload));
622
+ if (event === "error") this.options.onError?.(payload);
623
+ }
624
+ /** Emits one resolved view for duplicate Leaflet move and zoom events. */
625
+ emitView = () => {
626
+ if (this.destroyed || this.updatingView) return;
627
+ const view = this.getView();
628
+ if (this.emittedView && view.regionId === this.emittedView.regionId && view.floorId === this.emittedView.floorId && view.x === this.emittedView.x && view.y === this.emittedView.y && view.zoom === this.emittedView.zoom) return;
629
+ this.emittedView = view;
630
+ this.emit("viewchange", view);
631
+ this.renderLabels();
632
+ };
633
+ /** Resolves the compact attribution without inheriting the map control font. */
634
+ updateAttribution() {
635
+ const messages = this.manifest.controls[this.resolvedLocale] ?? this.manifest.controls[this.manifest.fallbackLocale];
636
+ if (!messages) throw new Error("Missing OEM attribution messages");
637
+ this.attributionBrand.textContent = messages.brandName;
638
+ this.attributionLink.textContent = messages.termsOfService;
639
+ }
640
+ on(event, handler) {
641
+ this.assertAlive();
642
+ const handlers = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
643
+ handlers.add(handler);
644
+ this.listeners.set(event, handlers);
645
+ return () => {
646
+ handlers.delete(handler);
647
+ };
648
+ }
649
+ /** Rebuilds the base map constraints and main tile layer for one region. */
650
+ applyRegion(view) {
651
+ this.updatingView = true;
652
+ this.baseTiles?.remove();
653
+ this.floorTiles?.remove();
654
+ this.floorTiles = void 0;
655
+ this.floorId = "M";
656
+ this.map.setMaxBounds(L4.latLngBounds([]));
657
+ this.map.setMinZoom(this.region.minZoom);
658
+ this.map.setMaxZoom(this.region.maxZoom);
659
+ const target = view ?? this.region.initialView;
660
+ this.map.setView(toOEMLeafletPosition(target, this.region), this.clampZoom(target.zoom), { animate: false });
661
+ this.map.setMaxBounds(this.regionBounds());
662
+ this.baseTiles = this.makeTiles("M").addTo(this.map);
663
+ this.updatingView = false;
664
+ }
665
+ /** Converts the region's published pixel extent to Simple CRS bounds. */
666
+ regionBounds() {
667
+ const { x, y } = this.region.boundsOffset;
668
+ return L4.latLngBounds(
669
+ toOEMLeafletPosition({ regionId: this.region.id, x, y }, this.region),
670
+ toOEMLeafletPosition({ regionId: this.region.id, x: x + this.region.dimensions[0], y: y + this.region.dimensions[1] }, this.region)
671
+ );
672
+ }
673
+ /** Creates a coverage-aware layer for one region floor. */
674
+ makeTiles(floorId) {
675
+ const floor = this.region.floors.find((entry) => entry.id === floorId);
676
+ const regionId = this.region.id;
677
+ const layer = new CoveredTileLayer(resolveOEMAsset(this.options.resources.baseUrl, floor.tileTemplate), {
678
+ tileSize: this.region.tileSize,
679
+ noWrap: true,
680
+ bounds: this.regionBounds(),
681
+ maxNativeZoom: this.region.maxNativeZoom,
682
+ maxZoom: Math.ceil(this.region.maxZoom)
683
+ }, this.region.coverage, floor);
684
+ layer.on("load", () => {
685
+ if (!this.destroyed && this.region.id === regionId) this.emit("load", { regionId, floorId });
686
+ });
687
+ layer.on("tileerror", () => this.emit("error", new Error(`Tile load failed: ${regionId}/${floorId}`)));
688
+ return layer;
689
+ }
690
+ clampZoom(zoom) {
691
+ if (!Number.isFinite(zoom)) throw new Error("Zoom must be finite");
692
+ return Math.max(this.region.minZoom, Math.min(this.region.maxZoom, zoom));
693
+ }
694
+ /** Returns the current view in the OEM pixel coordinate system. */
695
+ getView() {
696
+ this.assertAlive();
697
+ return { ...this.position(this.map.getCenter()), zoom: this.map.getZoom() };
698
+ }
699
+ /** Sets the view without exposing Leaflet's coordinate objects. */
700
+ setView(view) {
701
+ this.assertAlive();
702
+ this.validatePosition(view);
703
+ const zoom = this.clampZoom(view.zoom);
704
+ if (view.floorId) this.setFloor(view.floorId);
705
+ this.map.setView(toOEMLeafletPosition(view, this.region), zoom, { animate: false });
706
+ }
707
+ /** Changes zoom around the current center with an optional native transition. */
708
+ setZoom(zoom, options = {}) {
709
+ this.assertAlive();
710
+ this.map.setZoom(this.clampZoom(zoom), { animate: options.animate ?? false });
711
+ }
712
+ /** Fits the map to an OEM pixel-coordinate extent. */
713
+ fitBounds(bounds) {
714
+ this.assertAlive();
715
+ this.map.fitBounds(L4.latLngBounds(toOEMLeafletPosition(bounds[0], this.region), toOEMLeafletPosition(bounds[1], this.region)), { animate: false });
716
+ }
717
+ /** Switches regions and reloads only the features currently enabled. */
718
+ async setRegion(regionId) {
719
+ this.assertAlive();
720
+ const region = getOEMRegion(this.manifest, regionId);
721
+ if (region === this.region) return;
722
+ this.cancelRequests();
723
+ this.region = region;
724
+ this.points = [];
725
+ this.labels = [];
726
+ this.visibleLabelType = void 0;
727
+ this.clearPointLayers();
728
+ this.labelsLayer.clearLayers();
729
+ this.boundariesLayer.clearLayers();
730
+ this.applyRegion();
731
+ this.emit("regionchange", { regionId });
732
+ this.emit("floorchange", { floorId: "M" });
733
+ this.emitView();
734
+ await this.loadFeatures();
735
+ }
736
+ /** Switches the rendered floor while retaining the current region view. */
737
+ setFloor(floorId) {
738
+ this.assertAlive();
739
+ this.validateFloor(floorId);
740
+ if (floorId === this.floorId) return;
741
+ this.floorTiles?.remove();
742
+ this.floorTiles = void 0;
743
+ this.floorId = floorId;
744
+ const base = this.baseTiles?.getContainer();
745
+ if (base) base.style.filter = floorId === "M" ? "brightness(1)" : "brightness(0.5)";
746
+ if (floorId !== "M") this.floorTiles = this.makeTiles(floorId).addTo(this.map);
747
+ this.renderPoints();
748
+ this.emit("floorchange", { floorId });
749
+ this.emitView();
750
+ }
751
+ /** Changes label language and applies the manifest fallback chain. */
752
+ async setLocale(locale) {
753
+ this.assertAlive();
754
+ const resolved = normalizeOEMLocale(locale, Object.keys(this.manifest.locales), this.manifest.fallbackLocale);
755
+ const changed = resolved !== this.resolvedLocale;
756
+ this.locale = locale;
757
+ if (!changed) return;
758
+ this.resolvedLocale = resolved;
759
+ this.updateAttribution();
760
+ if (this.features.labels) await this.loadFeature("labels");
761
+ }
762
+ /** Returns both the requested and resolved locale values. */
763
+ getLocale() {
764
+ return { requested: this.locale, resolved: this.resolvedLocale };
765
+ }
766
+ /** Changes only this instance's theme attribute. */
767
+ setTheme(theme) {
768
+ this.assertAlive();
769
+ this.root.dataset.theme = theme;
770
+ }
771
+ /** Enables or disables static layers and cancels disabled layer requests. */
772
+ async setFeatures(features) {
773
+ this.assertAlive();
774
+ const loads = [];
775
+ for (const feature of FEATURE_NAMES) {
776
+ if (features[feature] === void 0 || features[feature] === this.features[feature]) continue;
777
+ this.features[feature] = features[feature];
778
+ this.cancelRequest(feature);
779
+ if (features[feature]) loads.push(this.loadFeature(feature));
780
+ else if (feature === "points") {
781
+ this.points = [];
782
+ this.clearPointLayers();
783
+ } else if (feature === "labels") {
784
+ this.labels = [];
785
+ this.visibleLabelType = void 0;
786
+ this.labelsLayer.clearLayers();
787
+ } else this.boundariesLayer.clearLayers();
788
+ }
789
+ await Promise.all(loads);
790
+ }
791
+ cancelRequest(feature) {
792
+ if (!this.requests.has(feature)) return;
793
+ this.requests.get(feature).abort();
794
+ this.requests.delete(feature);
795
+ this.emit("loading", { feature, loading: false });
796
+ }
797
+ cancelRequests() {
798
+ for (const feature of this.requests.keys()) this.cancelRequest(feature);
799
+ }
800
+ async loadFeatures() {
801
+ await Promise.all(FEATURE_NAMES.filter((feature) => this.features[feature]).map((feature) => this.loadFeature(feature)));
802
+ }
803
+ /** Loads one feature with a request token so stale responses are ignored. */
804
+ async loadFeature(feature) {
805
+ this.cancelRequest(feature);
806
+ const request = new AbortController();
807
+ this.requests.set(feature, request);
808
+ this.emit("loading", { feature, loading: true });
809
+ const read = (ref) => fetchOEMJson(resolveOEMAsset(this.options.resources.baseUrl, ref.path), request.signal);
810
+ try {
811
+ if (feature === "points") {
812
+ const [groups, types] = await Promise.all([
813
+ Promise.all(this.region.points.map((ref) => read(ref))),
814
+ Object.keys(this.types).length ? this.types : read(this.manifest.types)
815
+ ]);
816
+ if (request.signal.aborted) return;
817
+ this.points = groups.flat();
818
+ this.types = types;
819
+ this.renderPoints();
820
+ } else if (feature === "labels") {
821
+ const [labels, messages] = await Promise.all([
822
+ this.region.labels ? read(this.region.labels) : Promise.resolve([]),
823
+ this.manifest.locales[this.resolvedLocale] ? read(this.manifest.locales[this.resolvedLocale]) : Promise.resolve({})
824
+ ]);
825
+ if (request.signal.aborted) return;
826
+ this.labels = labels;
827
+ this.messages = messages;
828
+ this.renderLabels(true);
829
+ } else {
830
+ const boundaries = this.region.boundaries ? await read(this.region.boundaries) : [];
831
+ if (request.signal.aborted) return;
832
+ this.renderBoundaries(boundaries);
833
+ }
834
+ } catch (error) {
835
+ if (!request.signal.aborted) {
836
+ this.features[feature] = false;
837
+ this.emit("error", error instanceof Error ? error : new Error(String(error)));
838
+ throw error;
839
+ }
840
+ } finally {
841
+ if (this.requests.get(feature) === request) {
842
+ this.requests.delete(feature);
843
+ this.emit("loading", { feature, loading: false });
844
+ }
845
+ }
846
+ }
847
+ /** Applies a client-side filter without making another network request. */
848
+ setPointFilter(filter) {
849
+ this.assertAlive();
850
+ const next = cloneFilter(filter);
851
+ if (sameFilter(this.filter, next)) return;
852
+ this.filter = next;
853
+ this.renderPoints();
854
+ }
855
+ /** Enables or disables Atlos-style marker clustering without reloading point data. */
856
+ setMarkerClustering(enabled) {
857
+ this.assertAlive();
858
+ if (enabled === this.markerClustering) return;
859
+ this.markerClustering = enabled;
860
+ this.renderPoints();
861
+ }
862
+ clearPointLayers() {
863
+ this.pointsLayer.clearLayers();
864
+ for (const group of this.pointClusters.values()) {
865
+ group.clearLayers();
866
+ group.remove();
867
+ }
868
+ this.pointClusters.clear();
869
+ }
870
+ /** Builds the shared Atlos marker composition for points and cluster summaries. */
871
+ createMarkerVisual(type, point, count) {
872
+ const inner = document.createElement(point ? "a" : "div");
873
+ inner.className = type.noFrame ? "noFrameInner" : "markerInner";
874
+ if (point) {
875
+ const link = inner;
876
+ link.href = createOEMPointUrl(point.id);
877
+ link.target = "_blank";
878
+ link.rel = "noopener noreferrer";
879
+ inner.classList.toggle("offLayer", point.position.floorId !== this.floorId);
880
+ if (point.tier) inner.dataset.tier = point.position.floorId;
881
+ }
882
+ if (count !== void 0) inner.classList.add("clusterMarker");
883
+ const image = document.createElement("img");
884
+ image.src = resolveOEMAsset(this.options.resources.baseUrl, type.icon);
885
+ image.alt = type.key;
886
+ image.draggable = false;
887
+ if (type.noFrame) {
888
+ image.className = "noFrameImage";
889
+ inner.append(image);
890
+ } else {
891
+ const frame = document.createElement("div");
892
+ frame.className = "frameImage";
893
+ frame.append(image);
894
+ inner.append(frame);
895
+ }
896
+ if (type.subIcon) {
897
+ const sub = document.createElement("div");
898
+ sub.className = "subIconContainer";
899
+ const subImage = document.createElement("img");
900
+ subImage.className = "subIcon";
901
+ subImage.src = resolveOEMAsset(this.options.resources.baseUrl, type.subIcon);
902
+ subImage.alt = "";
903
+ sub.append(subImage);
904
+ inner.append(sub);
905
+ }
906
+ if (count !== void 0) {
907
+ const badge = document.createElement("span");
908
+ badge.className = "clusterCount";
909
+ badge.textContent = String(count);
910
+ inner.append(badge);
911
+ }
912
+ return inner;
913
+ }
914
+ createPointMarker(point, type) {
915
+ return new OEMMarker(toOEMLeafletPosition(point.position, this.region), {
916
+ interactive: true,
917
+ keyboard: false,
918
+ bubblingMouseEvents: false,
919
+ icon: L4.divIcon({
920
+ html: this.createMarkerVisual(type, point),
921
+ className: `${type.noFrame ? "noFrameMarkerIcon" : "frameMarkerIcon"} incompleteMarker`,
922
+ iconSize: type.noFrame ? [50, 50] : [32, 32],
923
+ iconAnchor: type.noFrame ? [25, 25] : [16, 32]
924
+ })
925
+ });
926
+ }
927
+ createPointCluster(type) {
928
+ return L4.markerClusterGroup({
929
+ showCoverageOnHover: false,
930
+ zoomToBoundsOnClick: !this.options.lockZoom,
931
+ spiderfyOnMaxZoom: !this.options.lockZoom,
932
+ disableClusteringAtZoom: 2,
933
+ maxClusterRadius: 60,
934
+ iconCreateFunction: (cluster) => L4.divIcon({
935
+ html: this.createMarkerVisual(type, void 0, cluster.getChildCount()),
936
+ className: `${type.noFrame ? "noFrameMarkerIcon" : "frameMarkerIcon"} markerClusterCustom`,
937
+ iconSize: type.noFrame ? [50, 50] : [32, 32],
938
+ iconAnchor: type.noFrame ? [25, 25] : [16, 32]
939
+ })
940
+ });
941
+ }
942
+ /** Rebuilds visible markers and groups eligible types with Atlos clustering rules. */
943
+ renderPoints() {
944
+ this.clearPointLayers();
945
+ const types = this.filter.types ? new Set(this.filter.types) : void 0;
946
+ const subregions = this.filter.subregions ? new Set(this.filter.subregions) : void 0;
947
+ for (const point of this.points) {
948
+ if (types && !types.has(point.type)) continue;
949
+ if (subregions && !subregions.has(point.subregionId)) continue;
950
+ if (this.filter.floorOnly && point.position.floorId !== this.floorId) continue;
951
+ const type = this.types[point.type];
952
+ if (!type) continue;
953
+ const marker = this.createPointMarker(point, type);
954
+ if (this.markerClustering && CLUSTER_SUBCATEGORIES.has(type.category.sub)) {
955
+ let group = this.pointClusters.get(type.key);
956
+ if (!group) {
957
+ group = this.createPointCluster(type);
958
+ this.pointClusters.set(type.key, group);
959
+ }
960
+ group.addLayer(marker);
961
+ } else marker.addTo(this.pointsLayer);
962
+ }
963
+ for (const group of this.pointClusters.values()) if (group.getLayers().length) group.addTo(this.map);
964
+ }
965
+ /** Renders Atlos-style fill and dashed stroke layers for published subregions. */
966
+ renderBoundaries(boundaries) {
967
+ this.boundariesLayer.clearLayers();
968
+ for (const boundary of boundaries) {
969
+ const rings = boundary.rings.map((ring) => ring.map((position) => toOEMLeafletPosition(position, this.region)));
970
+ L4.polygon(rings, {
971
+ color: "transparent",
972
+ fillOpacity: 0.2,
973
+ interactive: false,
974
+ className: "subregionBoundaryFill"
975
+ }).addTo(this.boundariesLayer);
976
+ L4.polygon(rings, {
977
+ weight: 2,
978
+ opacity: 0.8,
979
+ fill: false,
980
+ interactive: false,
981
+ className: "subregionBoundaryStroke"
982
+ }).addTo(this.boundariesLayer);
983
+ }
984
+ }
985
+ /** Renders Atlos site or subregion labels according to the current zoom. */
986
+ renderLabels(force = false) {
987
+ const showSub = this.map.getZoom() <= 0.25 && this.labels.some((label) => label.type === "sub");
988
+ const visibleType = showSub ? "sub" : "site";
989
+ if (!force && visibleType === this.visibleLabelType) return;
990
+ this.visibleLabelType = visibleType;
991
+ this.labelsLayer.clearLayers();
992
+ for (const label of this.labels) {
993
+ if (label.type !== visibleType) continue;
994
+ const inner = document.createElement("div");
995
+ inner.className = label.type === "sub" ? "innerSub" : "innerSite";
996
+ inner.textContent = this.messages[label.textKey] ?? label.id.split("/").at(-1) ?? label.id;
997
+ new OEMMarker(toOEMLeafletPosition(label.position, this.region), {
998
+ pane: "placeLabels",
999
+ interactive: false,
1000
+ keyboard: false,
1001
+ icon: L4.divIcon({ className: "mapLabel", html: inner, iconSize: [0, 0] })
1002
+ }).addTo(this.labelsLayer);
1003
+ }
1004
+ }
1005
+ /** Recalculates Leaflet dimensions after the host element changes size. */
1006
+ resize() {
1007
+ if (!this.destroyed) this.map.invalidateSize({ animate: false });
1008
+ }
1009
+ /** Releases listeners, observers, pending requests, layers and DOM nodes. */
1010
+ destroy = () => {
1011
+ if (this.destroyed) return;
1012
+ this.destroyed = true;
1013
+ this.cancelRequests();
1014
+ this.observer?.disconnect();
1015
+ this.wheel.dispose();
1016
+ this.options.signal?.removeEventListener("abort", this.destroy);
1017
+ this.listeners.clear();
1018
+ this.map.remove();
1019
+ this.root.remove();
1020
+ this.points = [];
1021
+ mounted.delete(this.container);
1022
+ };
1023
+ };
1024
+ export {
1025
+ OEM
1026
+ };
1027
+ //# sourceMappingURL=runtime-ROM65Z24.js.map