@mapvx/web-js 1.2.2 → 1.3.0-dev.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/dist/cjs/domain/models/categories.js +23 -10
- package/dist/cjs/domain/models/categories.js.map +1 -1
- package/dist/cjs/domain/models/marker.js +86 -80
- package/dist/cjs/domain/models/marker.js.map +1 -1
- package/dist/cjs/domain/models/routeConfiguration.js +3 -1
- package/dist/cjs/domain/models/routeConfiguration.js.map +1 -1
- package/dist/cjs/index.js +13 -12
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/logger/logger.js +1 -1
- package/dist/cjs/logger/rollbar.js +1 -1
- package/dist/cjs/map/map.js +448 -4
- package/dist/cjs/map/map.js.map +1 -1
- package/dist/cjs/repository/requester.js +48 -65
- package/dist/cjs/repository/requester.js.map +1 -1
- package/dist/cjs/utils/3d.js +12 -0
- package/dist/cjs/utils/3d.js.map +1 -0
- package/dist/es/domain/models/categories.d.ts +34 -10
- package/dist/es/domain/models/categories.d.ts.map +1 -1
- package/dist/es/domain/models/categories.js +21 -9
- package/dist/es/domain/models/categories.js.map +1 -1
- package/dist/es/domain/models/mapConfig.d.ts +11 -0
- package/dist/es/domain/models/mapConfig.d.ts.map +1 -1
- package/dist/es/domain/models/marker.d.ts +8 -0
- package/dist/es/domain/models/marker.d.ts.map +1 -1
- package/dist/es/domain/models/marker.js +86 -80
- package/dist/es/domain/models/marker.js.map +1 -1
- package/dist/es/domain/models/routeConfiguration.d.ts +47 -0
- package/dist/es/domain/models/routeConfiguration.d.ts.map +1 -1
- package/dist/es/domain/models/routeConfiguration.js +3 -1
- package/dist/es/domain/models/routeConfiguration.js.map +1 -1
- package/dist/es/index.d.ts +12 -12
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +6 -6
- package/dist/es/index.js.map +1 -1
- package/dist/es/logger/logger.js +1 -1
- package/dist/es/logger/rollbar.js +1 -1
- package/dist/es/map/map.d.ts +27 -1
- package/dist/es/map/map.d.ts.map +1 -1
- package/dist/es/map/map.js +449 -5
- package/dist/es/map/map.js.map +1 -1
- package/dist/es/repository/requester.d.ts +12 -0
- package/dist/es/repository/requester.d.ts.map +1 -1
- package/dist/es/repository/requester.js +48 -65
- package/dist/es/repository/requester.js.map +1 -1
- package/dist/es/utils/3d.d.ts +2 -0
- package/dist/es/utils/3d.d.ts.map +1 -0
- package/dist/es/utils/3d.js +8 -0
- package/dist/es/utils/3d.js.map +1 -0
- package/dist/umd/index.js +154088 -2173
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/styles.css +32 -14
- package/dist/umd/styles.css.map +1 -1
- package/package.json +19 -5
package/dist/es/map/map.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MVTLayer } from "@deck.gl/geo-layers";
|
|
2
|
+
import { IconLayer, PathLayer, TextLayer } from "@deck.gl/layers";
|
|
3
|
+
import maplibregl, { LngLatBounds, Map, MercatorCoordinate, NavigationControl, setRTLTextPlugin, } from "maplibre-gl";
|
|
4
|
+
import * as THREE from "three";
|
|
5
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
2
6
|
import { Semaphore } from "../utils/semaphore";
|
|
3
7
|
/** Shared GeoJSON source holding every circle drawn through the circle API. */
|
|
4
8
|
const CIRCLE_SOURCE_ID = "mapvx-circles";
|
|
@@ -127,19 +131,20 @@ function convertPaddingToPixels(padding, containerWidth, containerHeight) {
|
|
|
127
131
|
right: parsePaddingValue(padding.right, containerWidth),
|
|
128
132
|
};
|
|
129
133
|
}
|
|
134
|
+
import { MapboxOverlay } from "@deck.gl/mapbox";
|
|
130
135
|
import { userLocationDataUrl } from "../assets/icons";
|
|
131
136
|
import { RouteController } from "../controllers/routeController";
|
|
132
137
|
import { rtlLanguages } from "../domain/models/_rtl";
|
|
133
138
|
import { InternalAnimationConfig, InternalAnimationDrawingConfig, } from "../domain/models/animation";
|
|
139
|
+
import { CIRCLE_RENDER_ORDERS, circleRing, cloneCircleRecord, MAPVX_BRAND_COLOR, resolveCircleConfig, } from "../domain/models/circle";
|
|
134
140
|
import { Loggeable } from "../domain/models/loggeable";
|
|
135
141
|
import { DEFAULT_TILE_CACHE_CONFIG, MAPLIBRE_MAX_TILE_CACHE_HARD_CAP, } from "../domain/models/mapConfig";
|
|
136
|
-
import { CIRCLE_RENDER_ORDERS, circleRing, cloneCircleRecord, MAPVX_BRAND_COLOR, resolveCircleConfig, } from "../domain/models/circle";
|
|
137
142
|
import { MarkerAttribute } from "../domain/models/marker";
|
|
138
143
|
import { MVXRoute } from "../domain/models/route";
|
|
139
144
|
import { InternalDrawRouteConfiguration, InternalGetRouteConfiguration, } from "../domain/models/routeConfiguration";
|
|
140
145
|
import { Repository } from "../repository/repository";
|
|
141
|
-
import { getBoundingBox } from "../utils/utils";
|
|
142
146
|
import { extractStepCoordinates } from "../utils/route-utils";
|
|
147
|
+
import { getBoundingBox } from "../utils/utils";
|
|
143
148
|
import { buildInteractionOptions, shouldDisableTouchRotation } from "./mapInteractionOptions";
|
|
144
149
|
/**
|
|
145
150
|
* Class to interact with the map.
|
|
@@ -156,7 +161,7 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
156
161
|
* @returns A new instance of MapVXMap.
|
|
157
162
|
*/
|
|
158
163
|
constructor(mapConfig, container, token) {
|
|
159
|
-
var _a, _b;
|
|
164
|
+
var _a, _b, _c;
|
|
160
165
|
super();
|
|
161
166
|
this.potentialParentPlaces = [];
|
|
162
167
|
this.innerFloors = [];
|
|
@@ -169,6 +174,22 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
169
174
|
this.hoveredId = "unselected";
|
|
170
175
|
this.failedTiles = new Set();
|
|
171
176
|
this.geoLocation = navigator.geolocation;
|
|
177
|
+
// 3d related variables
|
|
178
|
+
this.mode = "2D";
|
|
179
|
+
this.deckOverlay = undefined;
|
|
180
|
+
this.escalatorScene = undefined;
|
|
181
|
+
this.escalatorModelTemplate = undefined;
|
|
182
|
+
this.escalatorCenter = undefined;
|
|
183
|
+
this.escalatorCache = {};
|
|
184
|
+
this.ESCALATOR_MODEL_URL = "https://mapvx-glb-assets.s3.us-east-1.amazonaws.com/shared/escalator.glb";
|
|
185
|
+
/**
|
|
186
|
+
* Number of lights to use for the escalators
|
|
187
|
+
*
|
|
188
|
+
*/
|
|
189
|
+
this.ESCALATOR_LIGHTS = 3;
|
|
190
|
+
this.SPRITE_URL = "https://lazarillo.app/internal/maps/vector-style/cenco-cl-pe-co-ar-3D/sprites_cencosud";
|
|
191
|
+
this.spriteIconMapping = {};
|
|
192
|
+
this.spriteAtlasImage = new Image();
|
|
172
193
|
if (rtlLanguages.includes((_a = mapConfig.lang) !== null && _a !== void 0 ? _a : "")) {
|
|
173
194
|
this.setRTLSupport();
|
|
174
195
|
}
|
|
@@ -180,11 +201,16 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
180
201
|
this.watchPositionID = undefined;
|
|
181
202
|
this.onFloorChange = mapConfig.onFloorChange;
|
|
182
203
|
this.onParentPlaceChange = mapConfig.onParentPlaceChange;
|
|
204
|
+
this.mode = (_c = mapConfig.mode) !== null && _c !== void 0 ? _c : "2D";
|
|
205
|
+
this.initialCenter = mapConfig.center;
|
|
183
206
|
this.tileCacheConfig = (() => {
|
|
184
207
|
const merged = Object.assign(Object.assign({}, DEFAULT_TILE_CACHE_CONFIG), mapConfig.tileCache);
|
|
185
208
|
merged.maxTiles = Math.min(merged.maxTiles, MAPLIBRE_MAX_TILE_CACHE_HARD_CAP);
|
|
186
209
|
return merged;
|
|
187
210
|
})();
|
|
211
|
+
if (this.mode === "3D") {
|
|
212
|
+
this.loadSpriteAtlas().catch(console.error);
|
|
213
|
+
}
|
|
188
214
|
if (mapConfig.parentPlaceId != null) {
|
|
189
215
|
this.initialPlaceDetailSetUp(mapConfig.parentPlaceId, mapConfig.authToken);
|
|
190
216
|
}
|
|
@@ -312,6 +338,30 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
312
338
|
}), (_e = mapConfig.navigationPosition) !== null && _e !== void 0 ? _e : "top-right");
|
|
313
339
|
this.map.on("load", () => {
|
|
314
340
|
var _a;
|
|
341
|
+
if (this.mode === "3D") {
|
|
342
|
+
this.deckOverlay = new MapboxOverlay({ layers: [] });
|
|
343
|
+
this.map.addControl(this.deckOverlay);
|
|
344
|
+
this.hideIndoorSymbolLayers();
|
|
345
|
+
// Hide MapLibre icon layers — replaced by deck.gl IconLayer
|
|
346
|
+
this.map.setLayoutProperty("indoor-poi-logo", "visibility", "none");
|
|
347
|
+
const style = this.map.getStyle();
|
|
348
|
+
for (const layer of style.layers) {
|
|
349
|
+
if (layer.id.startsWith("indoor-logo-tiendas-ancla")) {
|
|
350
|
+
this.map.setLayoutProperty(layer.id, "visibility", "none");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// Add 3D escalator layer and hide 2D transportation line
|
|
354
|
+
this.map.addLayer(this.createEscalatorLayer());
|
|
355
|
+
this.map.setPaintProperty("indoor-transportation", "line-opacity", 0);
|
|
356
|
+
// Capture escalator positions when new tiles arrive (stops once cached)
|
|
357
|
+
this.map.on("sourcedata", (e) => {
|
|
358
|
+
if (e.sourceId === "indoorequal" &&
|
|
359
|
+
this.currentFloor &&
|
|
360
|
+
!this.escalatorCache[this.currentFloor]) {
|
|
361
|
+
this.placeEscalators();
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
315
365
|
this.whenStyleUpdates(style);
|
|
316
366
|
if (mapConfig.lang)
|
|
317
367
|
this.setLayersForLanguage(mapConfig.lang);
|
|
@@ -334,7 +384,6 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
334
384
|
(_a = mapConfig.onRotate) === null || _a === void 0 ? void 0 : _a.call(mapConfig, this.map.getBearing());
|
|
335
385
|
});
|
|
336
386
|
this.defaultClickListener();
|
|
337
|
-
return this.map;
|
|
338
387
|
}
|
|
339
388
|
setBaseFilters(style) {
|
|
340
389
|
if (!style)
|
|
@@ -463,6 +512,386 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
463
512
|
// Tile cache clear may fail if cache doesn't exist
|
|
464
513
|
}
|
|
465
514
|
}
|
|
515
|
+
// Start 3D related methods ------------------------------------------------------------
|
|
516
|
+
hideIndoorSymbolLayers() {
|
|
517
|
+
var _a, _b;
|
|
518
|
+
const style = this.map.getStyle();
|
|
519
|
+
for (const layer of style.layers) {
|
|
520
|
+
if (layer.type === "symbol" && layer.id.startsWith("indoor-")) {
|
|
521
|
+
const hasText = !!((_a = layer.layout) === null || _a === void 0 ? void 0 : _a["text-field"]);
|
|
522
|
+
const hasIcon = !!((_b = layer.layout) === null || _b === void 0 ? void 0 : _b["icon-image"]);
|
|
523
|
+
// Hide text-only layers (replaced by deck.gl)
|
|
524
|
+
if (hasText && !hasIcon) {
|
|
525
|
+
this.map.setLayoutProperty(layer.id, "visibility", "none");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
createEscalatorLayer() {
|
|
531
|
+
const refMercator = MercatorCoordinate.fromLngLat(this.initialCenter, 0);
|
|
532
|
+
const meterScale = refMercator.meterInMercatorCoordinateUnits();
|
|
533
|
+
const modelTransform = new THREE.Matrix4()
|
|
534
|
+
.makeTranslation(refMercator.x, refMercator.y, refMercator.z)
|
|
535
|
+
.scale(new THREE.Vector3(meterScale, -meterScale, meterScale))
|
|
536
|
+
.multiply(new THREE.Matrix4().makeRotationX(Math.PI / 2));
|
|
537
|
+
this.escalatorScene = new THREE.Scene();
|
|
538
|
+
const dirLight = new THREE.DirectionalLight(0xffffff, 2.0);
|
|
539
|
+
dirLight.position.set(50, 100, 80);
|
|
540
|
+
this.escalatorScene.add(dirLight);
|
|
541
|
+
const dirLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
|
|
542
|
+
dirLight2.position.set(-50, 80, -60);
|
|
543
|
+
this.escalatorScene.add(dirLight2);
|
|
544
|
+
this.escalatorScene.add(new THREE.AmbientLight(0xffffff, 1.5));
|
|
545
|
+
new GLTFLoader().load(this.ESCALATOR_MODEL_URL, (gltf) => {
|
|
546
|
+
this.escalatorModelTemplate = gltf.scene;
|
|
547
|
+
const box = new THREE.Box3().setFromObject(this.escalatorModelTemplate);
|
|
548
|
+
this.escalatorCenter = box.getCenter(new THREE.Vector3());
|
|
549
|
+
this.escalatorCenter.y = box.min.y; // align bottom to ground
|
|
550
|
+
this.placeEscalators();
|
|
551
|
+
});
|
|
552
|
+
let renderer = undefined;
|
|
553
|
+
let camera = undefined;
|
|
554
|
+
const tmpMatrix = new THREE.Matrix4();
|
|
555
|
+
return {
|
|
556
|
+
id: "3d-escalators",
|
|
557
|
+
type: "custom",
|
|
558
|
+
renderingMode: "3d",
|
|
559
|
+
onAdd(_map, gl) {
|
|
560
|
+
camera = new THREE.Camera();
|
|
561
|
+
renderer = new THREE.WebGLRenderer({
|
|
562
|
+
canvas: _map.getCanvas(),
|
|
563
|
+
context: gl,
|
|
564
|
+
antialias: true,
|
|
565
|
+
});
|
|
566
|
+
renderer.autoClear = false;
|
|
567
|
+
},
|
|
568
|
+
render: (gl, args) => {
|
|
569
|
+
var _a, _b;
|
|
570
|
+
if (!camera ||
|
|
571
|
+
!renderer ||
|
|
572
|
+
!this.escalatorModelTemplate ||
|
|
573
|
+
!this.escalatorScene ||
|
|
574
|
+
this.escalatorScene.children.length <= this.ESCALATOR_LIGHTS)
|
|
575
|
+
return;
|
|
576
|
+
const matrixData = (_b = (_a = args === null || args === void 0 ? void 0 : args.defaultProjectionData) === null || _a === void 0 ? void 0 : _a.mainMatrix) !== null && _b !== void 0 ? _b : args;
|
|
577
|
+
tmpMatrix.fromArray(matrixData).multiply(modelTransform);
|
|
578
|
+
camera.projectionMatrix.copy(tmpMatrix);
|
|
579
|
+
renderer.resetState();
|
|
580
|
+
renderer.render(this.escalatorScene, camera);
|
|
581
|
+
},
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
placeEscalators() {
|
|
585
|
+
if (!this.escalatorModelTemplate ||
|
|
586
|
+
!this.escalatorScene ||
|
|
587
|
+
!this.currentFloor ||
|
|
588
|
+
!this.escalatorCenter)
|
|
589
|
+
return;
|
|
590
|
+
// Try to capture positions if not cached yet
|
|
591
|
+
this.captureEscalatorPositions();
|
|
592
|
+
const positions = this.escalatorCache[this.currentFloor];
|
|
593
|
+
if (!positions)
|
|
594
|
+
return;
|
|
595
|
+
// Clear scene models (keep lights)
|
|
596
|
+
while (this.escalatorScene.children.length > this.ESCALATOR_LIGHTS) {
|
|
597
|
+
const lastChild = this.escalatorScene.children.at(-1);
|
|
598
|
+
if (lastChild) {
|
|
599
|
+
this.escalatorScene.remove(lastChild);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
const refMercator = MercatorCoordinate.fromLngLat(this.initialCenter, 0);
|
|
603
|
+
const meterScale = refMercator.meterInMercatorCoordinateUnits();
|
|
604
|
+
for (const pos of positions) {
|
|
605
|
+
const target = maplibregl.MercatorCoordinate.fromLngLat([pos.lng, pos.lat], 0);
|
|
606
|
+
const sx = (target.x - refMercator.x) / meterScale;
|
|
607
|
+
const sz = (target.y - refMercator.y) / meterScale;
|
|
608
|
+
const pivot = new THREE.Group();
|
|
609
|
+
pivot.position.set(sx, 0, sz);
|
|
610
|
+
pivot.rotation.y = Math.PI / 2 - pos.bearing;
|
|
611
|
+
pivot.scale.set(0.3, 0.3, 0.3);
|
|
612
|
+
const model = this.escalatorModelTemplate.clone();
|
|
613
|
+
model.position.set(-this.escalatorCenter.x, -this.escalatorCenter.y - 3, -this.escalatorCenter.z);
|
|
614
|
+
pivot.add(model);
|
|
615
|
+
this.escalatorScene.add(pivot);
|
|
616
|
+
}
|
|
617
|
+
this.map.triggerRepaint();
|
|
618
|
+
}
|
|
619
|
+
captureEscalatorPositions() {
|
|
620
|
+
if (this.currentFloor && this.escalatorCache[this.currentFloor])
|
|
621
|
+
return;
|
|
622
|
+
// queryRenderedFeatures uses exact rendered coordinates (not tile-approximated)
|
|
623
|
+
const features = this.map
|
|
624
|
+
.queryRenderedFeatures(undefined, {
|
|
625
|
+
layers: ["indoor-transportation"],
|
|
626
|
+
})
|
|
627
|
+
.filter((f) => { var _a; return ((_a = f.properties) === null || _a === void 0 ? void 0 : _a.floor_key) === this.currentFloor; });
|
|
628
|
+
if (!features.length)
|
|
629
|
+
return;
|
|
630
|
+
// Collect all midpoints first
|
|
631
|
+
const allMids = [];
|
|
632
|
+
for (const f of features) {
|
|
633
|
+
const coords = f.geometry.coordinates;
|
|
634
|
+
if (!coords || coords.length < 2)
|
|
635
|
+
continue;
|
|
636
|
+
const start = coords[0];
|
|
637
|
+
const end = coords[coords.length - 1];
|
|
638
|
+
const midLng = (start[0] + end[0]) / 2;
|
|
639
|
+
const midLat = (start[1] + end[1]) / 2;
|
|
640
|
+
const bearing = Math.atan2(end[0] - start[0], end[1] - start[1]);
|
|
641
|
+
allMids.push({ lng: midLng, lat: midLat, bearing });
|
|
642
|
+
}
|
|
643
|
+
// Merge points within ~15m of each other into one
|
|
644
|
+
const MERGE_DEG = 0.00015; // ~15m
|
|
645
|
+
const positions = [];
|
|
646
|
+
const used = new Set();
|
|
647
|
+
for (let i = 0; i < allMids.length; i++) {
|
|
648
|
+
if (used.has(i))
|
|
649
|
+
continue;
|
|
650
|
+
used.add(i);
|
|
651
|
+
const group = [allMids[i]];
|
|
652
|
+
for (let j = i + 1; j < allMids.length; j++) {
|
|
653
|
+
if (used.has(j))
|
|
654
|
+
continue;
|
|
655
|
+
const dLng = allMids[i].lng - allMids[j].lng;
|
|
656
|
+
const dLat = allMids[i].lat - allMids[j].lat;
|
|
657
|
+
if (Math.abs(dLng) < MERGE_DEG && Math.abs(dLat) < MERGE_DEG) {
|
|
658
|
+
used.add(j);
|
|
659
|
+
group.push(allMids[j]);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// Use average position and first bearing
|
|
663
|
+
const avgLng = group.reduce((s, p) => s + p.lng, 0) / group.length;
|
|
664
|
+
const avgLat = group.reduce((s, p) => s + p.lat, 0) / group.length;
|
|
665
|
+
positions.push({ lng: avgLng, lat: avgLat, bearing: group[0].bearing });
|
|
666
|
+
}
|
|
667
|
+
if (positions.length && this.currentFloor) {
|
|
668
|
+
this.escalatorCache[this.currentFloor] = positions;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
async loadSpriteAtlas() {
|
|
672
|
+
const [json, img] = await Promise.all([
|
|
673
|
+
fetch(`${this.SPRITE_URL}.json`).then((r) => r.json()),
|
|
674
|
+
new Promise((resolve, reject) => {
|
|
675
|
+
const image = new Image();
|
|
676
|
+
image.crossOrigin = "anonymous";
|
|
677
|
+
image.onload = () => resolve(image);
|
|
678
|
+
image.onerror = reject;
|
|
679
|
+
image.src = `${this.SPRITE_URL}.png`;
|
|
680
|
+
}),
|
|
681
|
+
]);
|
|
682
|
+
this.spriteIconMapping = json;
|
|
683
|
+
this.spriteAtlasImage = img;
|
|
684
|
+
}
|
|
685
|
+
makeMVTLayer(floorKey) {
|
|
686
|
+
return new MVTLayer({
|
|
687
|
+
id: "indoor-lines-elevated",
|
|
688
|
+
data: "https://tiles.mapvx.com/",
|
|
689
|
+
binary: false,
|
|
690
|
+
renderSubLayers: (props) => {
|
|
691
|
+
var _a;
|
|
692
|
+
const roomData = (props.data || []);
|
|
693
|
+
const roomFeatures = roomData.filter((f) => {
|
|
694
|
+
var _a, _b, _c;
|
|
695
|
+
return ((_a = f.properties) === null || _a === void 0 ? void 0 : _a.class) === "room" &&
|
|
696
|
+
((_b = f.properties) === null || _b === void 0 ? void 0 : _b.subclass) !== "empty" &&
|
|
697
|
+
((_c = f.properties) === null || _c === void 0 ? void 0 : _c.floor_key) === floorKey;
|
|
698
|
+
});
|
|
699
|
+
if (!roomFeatures.length)
|
|
700
|
+
return null;
|
|
701
|
+
const paths = [];
|
|
702
|
+
for (const f of roomFeatures) {
|
|
703
|
+
const z = ((_a = f.properties) === null || _a === void 0 ? void 0 : _a.height) ? Number(f.properties.height) : 5;
|
|
704
|
+
const geom = f.geometry;
|
|
705
|
+
const polygons = geom.type === "Polygon"
|
|
706
|
+
? [geom.coordinates]
|
|
707
|
+
: geom.type === "MultiPolygon"
|
|
708
|
+
? geom.coordinates
|
|
709
|
+
: [];
|
|
710
|
+
for (const rings of polygons) {
|
|
711
|
+
for (const ring of rings) {
|
|
712
|
+
paths.push({ path: ring.map(([lon, lat]) => [lon, lat, z]) });
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return new PathLayer(Object.assign(Object.assign({}, props), { id: `${props.id}-paths`, data: paths, getPath: (d) => d.path, getColor: [203, 203, 203], getWidth: 1.5, widthUnits: "pixels", pickable: false }));
|
|
717
|
+
},
|
|
718
|
+
updateTriggers: {
|
|
719
|
+
renderSubLayers: [floorKey],
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
makeLabelLayer(floorKey, useSdf = true) {
|
|
724
|
+
return new MVTLayer({
|
|
725
|
+
id: "indoor-labels",
|
|
726
|
+
data: "https://tiles.mapvx.com/",
|
|
727
|
+
binary: false,
|
|
728
|
+
minZoom: 19.5,
|
|
729
|
+
maxZoom: 24,
|
|
730
|
+
renderSubLayers: (props) => {
|
|
731
|
+
// Use area_name and poi Point features — unique per tile, no duplicates
|
|
732
|
+
// If logo-{name} image exists in sprite, icon has priority over text label
|
|
733
|
+
const labelData = (props.data || []);
|
|
734
|
+
const labelFeatures = labelData.filter((f) => {
|
|
735
|
+
var _a, _b, _c, _d, _e, _f;
|
|
736
|
+
if (((_a = f.properties) === null || _a === void 0 ? void 0 : _a.layerName) !== "area_name" && ((_b = f.properties) === null || _b === void 0 ? void 0 : _b.layerName) !== "poi")
|
|
737
|
+
return false;
|
|
738
|
+
if (((_c = f.geometry) === null || _c === void 0 ? void 0 : _c.type) !== "Point")
|
|
739
|
+
return false;
|
|
740
|
+
const name = ((_d = f.properties) === null || _d === void 0 ? void 0 : _d["name:latin"]) || ((_e = f.properties) === null || _e === void 0 ? void 0 : _e.name);
|
|
741
|
+
if (!name)
|
|
742
|
+
return false;
|
|
743
|
+
if (((_f = f.properties) === null || _f === void 0 ? void 0 : _f.floor_key) && f.properties.floor_key !== floorKey)
|
|
744
|
+
return false;
|
|
745
|
+
if (this.map.hasImage("logo-" + name))
|
|
746
|
+
return false; // icon has priority
|
|
747
|
+
return true;
|
|
748
|
+
});
|
|
749
|
+
if (!labelFeatures.length)
|
|
750
|
+
return null;
|
|
751
|
+
const textData = labelFeatures.map((f) => {
|
|
752
|
+
var _a, _b, _c;
|
|
753
|
+
return ({
|
|
754
|
+
position: [
|
|
755
|
+
...f.geometry.coordinates.slice(0, 2),
|
|
756
|
+
((_a = f.properties) === null || _a === void 0 ? void 0 : _a.height) ? Number(f.properties.height) : 5,
|
|
757
|
+
],
|
|
758
|
+
text: ((_b = f.properties) === null || _b === void 0 ? void 0 : _b["name:latin"]) || ((_c = f.properties) === null || _c === void 0 ? void 0 : _c.name) || "",
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
return new TextLayer(Object.assign(Object.assign({}, props), { id: `${props.id}-labels`, data: textData, getPosition: (d) => d.position, getText: (d) => d.text, getSize: 16, getColor: [145, 150, 155, 255], outlineColor: useSdf ? [255, 255, 255, 255] : [0, 0, 0, 0], outlineWidth: useSdf ? 4 : 0, fontSettings: { sdf: useSdf }, fontFamily: "Open Sans, sans-serif", fontWeight: 600, billboard: true, pickable: false, characterSet: "auto", wordBreak: "break-word", maxWidth: 150 }));
|
|
762
|
+
},
|
|
763
|
+
updateTriggers: {
|
|
764
|
+
renderSubLayers: [floorKey],
|
|
765
|
+
},
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
makePoiIconLayer(floorKey) {
|
|
769
|
+
if (!this.spriteIconMapping || !this.spriteAtlasImage)
|
|
770
|
+
return null;
|
|
771
|
+
return new MVTLayer({
|
|
772
|
+
id: "indoor-poi-icons",
|
|
773
|
+
data: "https://tiles.mapvx.com/",
|
|
774
|
+
binary: false,
|
|
775
|
+
minZoom: 17,
|
|
776
|
+
maxZoom: 24,
|
|
777
|
+
renderSubLayers: (props) => {
|
|
778
|
+
const poiData = (props.data || []);
|
|
779
|
+
const poiFeatures = poiData.filter((f) => {
|
|
780
|
+
var _a, _b, _c;
|
|
781
|
+
if (((_a = f.geometry) === null || _a === void 0 ? void 0 : _a.type) !== "Point")
|
|
782
|
+
return false;
|
|
783
|
+
if (((_b = f.properties) === null || _b === void 0 ? void 0 : _b.floor_key) && f.properties.floor_key !== floorKey)
|
|
784
|
+
return false;
|
|
785
|
+
const sub = (_c = f.properties) === null || _c === void 0 ? void 0 : _c.subclass;
|
|
786
|
+
return sub === "elevator" || sub === "toilets";
|
|
787
|
+
});
|
|
788
|
+
if (!poiFeatures.length)
|
|
789
|
+
return null;
|
|
790
|
+
const iconData = poiFeatures.map((f) => {
|
|
791
|
+
var _a, _b, _c;
|
|
792
|
+
const tags = ((_a = f.properties) === null || _a === void 0 ? void 0 : _a.tags) || "";
|
|
793
|
+
let icon;
|
|
794
|
+
if (tags.includes("wheelchair"))
|
|
795
|
+
icon = "accessible_toilets";
|
|
796
|
+
else if (tags.includes("changing_table"))
|
|
797
|
+
icon = "changing_table";
|
|
798
|
+
else
|
|
799
|
+
icon = ((_b = f.properties) === null || _b === void 0 ? void 0 : _b.subclass) || "";
|
|
800
|
+
return {
|
|
801
|
+
position: [
|
|
802
|
+
...f.geometry.coordinates.slice(0, 2),
|
|
803
|
+
((_c = f.properties) === null || _c === void 0 ? void 0 : _c.height) ? Number(f.properties.height) : 5,
|
|
804
|
+
],
|
|
805
|
+
icon,
|
|
806
|
+
};
|
|
807
|
+
});
|
|
808
|
+
return new IconLayer(Object.assign(Object.assign({}, props), { id: `${props.id}-poi-icons`, data: iconData, iconAtlas: this.spriteAtlasImage, iconMapping: this.spriteIconMapping, getIcon: (d) => d.icon, getPosition: (d) => d.position, getSize: 2, sizeUnits: "meters", sizeScale: 1, billboard: true, pickable: false }));
|
|
809
|
+
},
|
|
810
|
+
updateTriggers: {
|
|
811
|
+
renderSubLayers: [floorKey],
|
|
812
|
+
},
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
makeStoreLogoLayer(floorKey) {
|
|
816
|
+
if (!this.spriteIconMapping || !this.spriteAtlasImage)
|
|
817
|
+
return null;
|
|
818
|
+
return new MVTLayer({
|
|
819
|
+
id: "indoor-store-logos",
|
|
820
|
+
data: "https://tiles.mapvx.com/",
|
|
821
|
+
binary: false,
|
|
822
|
+
minZoom: 13,
|
|
823
|
+
maxZoom: 24,
|
|
824
|
+
renderSubLayers: (props) => {
|
|
825
|
+
const propsData = (props.data || []);
|
|
826
|
+
const logoFeatures = propsData.filter((f) => {
|
|
827
|
+
var _a, _b, _c;
|
|
828
|
+
if (((_a = f.geometry) === null || _a === void 0 ? void 0 : _a.type) !== "Point")
|
|
829
|
+
return false;
|
|
830
|
+
if (((_b = f.properties) === null || _b === void 0 ? void 0 : _b.floor_key) && f.properties.floor_key !== floorKey)
|
|
831
|
+
return false;
|
|
832
|
+
const name = (_c = f.properties) === null || _c === void 0 ? void 0 : _c.name;
|
|
833
|
+
if (!name)
|
|
834
|
+
return false;
|
|
835
|
+
return !!this.spriteIconMapping["logo-" + name];
|
|
836
|
+
});
|
|
837
|
+
if (!logoFeatures.length)
|
|
838
|
+
return null;
|
|
839
|
+
const logoData = logoFeatures.map((f) => {
|
|
840
|
+
var _a, _b, _c;
|
|
841
|
+
const name = (_a = f.properties) === null || _a === void 0 ? void 0 : _a.name;
|
|
842
|
+
const icon = "logo-" + name;
|
|
843
|
+
const rotation = ((_b = f.properties) === null || _b === void 0 ? void 0 : _b.rotation_icon) ? Number(f.properties.rotation_icon) : 0;
|
|
844
|
+
return {
|
|
845
|
+
position: [
|
|
846
|
+
...f.geometry.coordinates.slice(0, 2),
|
|
847
|
+
((_c = f.properties) === null || _c === void 0 ? void 0 : _c.height) ? Number(f.properties.height) : 5,
|
|
848
|
+
],
|
|
849
|
+
icon,
|
|
850
|
+
rotation,
|
|
851
|
+
};
|
|
852
|
+
});
|
|
853
|
+
return new IconLayer(Object.assign(Object.assign({}, props), { id: `${props.id}-store-logos`, data: logoData, iconAtlas: this.spriteAtlasImage, iconMapping: this.spriteIconMapping, getIcon: (d) => d.icon, getPosition: (d) => d.position, getAngle: (d) => -d.rotation, getSize: (d) => {
|
|
854
|
+
const LOGO_SCALE = {
|
|
855
|
+
"logo-Maxi-K": 0.4,
|
|
856
|
+
"logo-Ripley": 2,
|
|
857
|
+
"logo-Falabella": 2,
|
|
858
|
+
"logo-CasaIdeas": 2,
|
|
859
|
+
"logo-Sky Costanera": 0.6,
|
|
860
|
+
"logo-Easy": 1.4,
|
|
861
|
+
"logo-Jumbo": 2,
|
|
862
|
+
"logo-Decathlon": 1.4,
|
|
863
|
+
};
|
|
864
|
+
const m = this.spriteIconMapping[d.icon];
|
|
865
|
+
if (!m)
|
|
866
|
+
return 10;
|
|
867
|
+
const pr = m.pixelRatio;
|
|
868
|
+
const w = m.width / pr;
|
|
869
|
+
const h = m.height / pr;
|
|
870
|
+
const base = 10 * (h / Math.max(w, h));
|
|
871
|
+
return base * (LOGO_SCALE[d.icon] || 1);
|
|
872
|
+
}, sizeUnits: "meters", sizeScale: 1, billboard: false, pickable: false }));
|
|
873
|
+
},
|
|
874
|
+
updateTriggers: {
|
|
875
|
+
renderSubLayers: [floorKey],
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
updateDeckOverlay() {
|
|
880
|
+
if (!this.deckOverlay || !this.currentFloor)
|
|
881
|
+
return;
|
|
882
|
+
const layers = [
|
|
883
|
+
this.makeMVTLayer(this.currentFloor),
|
|
884
|
+
this.makeLabelLayer(this.currentFloor, false),
|
|
885
|
+
];
|
|
886
|
+
const poiLayer = this.makePoiIconLayer(this.currentFloor);
|
|
887
|
+
if (poiLayer)
|
|
888
|
+
layers.push(poiLayer);
|
|
889
|
+
const storeLayer = this.makeStoreLogoLayer(this.currentFloor);
|
|
890
|
+
if (storeLayer)
|
|
891
|
+
layers.push(storeLayer);
|
|
892
|
+
this.deckOverlay.setProps({ layers });
|
|
893
|
+
}
|
|
894
|
+
// End 3D related methods ------------------------------------------------------------
|
|
466
895
|
getCurrentFloor() {
|
|
467
896
|
var _a;
|
|
468
897
|
return (_a = this.currentFloor) !== null && _a !== void 0 ? _a : "";
|
|
@@ -482,6 +911,10 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
482
911
|
if (place) {
|
|
483
912
|
this.subPlacesLoad(place.mapvxId);
|
|
484
913
|
}
|
|
914
|
+
if (this.mode === "3D") {
|
|
915
|
+
this.updateDeckOverlay();
|
|
916
|
+
this.placeEscalators();
|
|
917
|
+
}
|
|
485
918
|
if (updateStyle) {
|
|
486
919
|
this.repository
|
|
487
920
|
.fetchAndParseMapStyle(place === null || place === void 0 ? void 0 : place.mapvxId)
|
|
@@ -510,6 +943,10 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
510
943
|
this.innerFloors = (_a = place === null || place === void 0 ? void 0 : place.innerFloors.sort((a, b) => a.index - b.index)) !== null && _a !== void 0 ? _a : [];
|
|
511
944
|
this.currentFloor =
|
|
512
945
|
(_e = (_c = floorId !== null && floorId !== void 0 ? floorId : (_b = this.innerFloors.find((floor) => floor.defaultFloor)) === null || _b === void 0 ? void 0 : _b.key) !== null && _c !== void 0 ? _c : (_d = this.innerFloors[0]) === null || _d === void 0 ? void 0 : _d.key) !== null && _e !== void 0 ? _e : "";
|
|
946
|
+
if (this.mode === "3D") {
|
|
947
|
+
this.updateDeckOverlay();
|
|
948
|
+
this.placeEscalators();
|
|
949
|
+
}
|
|
513
950
|
}
|
|
514
951
|
updateParentPlaceAndFloor(parentPlaceId, floorId, options) {
|
|
515
952
|
var _a, _b, _c, _d;
|
|
@@ -562,6 +999,9 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
562
999
|
this.routeController.addSourcesAndLayers();
|
|
563
1000
|
this.refreshCircles();
|
|
564
1001
|
this.filterByFloorKey(this.currentFloor);
|
|
1002
|
+
if (this.mode === "3D") {
|
|
1003
|
+
this.updateDeckOverlay();
|
|
1004
|
+
}
|
|
565
1005
|
}
|
|
566
1006
|
addMarker(marker) {
|
|
567
1007
|
var _a, _b;
|
|
@@ -990,6 +1430,10 @@ export class InternalMapVXMap extends Loggeable {
|
|
|
990
1430
|
if (this.innerFloors.every((floor) => floor.key !== floorKey) && floorKey !== "") {
|
|
991
1431
|
this.logEvent("invalidFloorKey", { floorKey: floorKeyString });
|
|
992
1432
|
}
|
|
1433
|
+
if (this.mode === "3D") {
|
|
1434
|
+
this.updateDeckOverlay();
|
|
1435
|
+
this.placeEscalators();
|
|
1436
|
+
}
|
|
993
1437
|
this.updateFiltersTo(floorKeyString);
|
|
994
1438
|
this.updateMarkersTo(floorKeyString);
|
|
995
1439
|
this.refreshCircles();
|