@mapvx/web-js 1.4.0-dev.0 → 1.4.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +10 -9
  3. package/dist/cjs/assets/icons.js +8 -10
  4. package/dist/cjs/assets/icons.js.map +1 -1
  5. package/dist/cjs/controllers/routeController.js +27 -2
  6. package/dist/cjs/controllers/routeController.js.map +1 -1
  7. package/dist/cjs/domain/models/_internal.js +1 -2
  8. package/dist/cjs/domain/models/_internal.js.map +1 -1
  9. package/dist/cjs/domain/models/animation.js +2 -2
  10. package/dist/cjs/domain/models/animation.js.map +1 -1
  11. package/dist/cjs/domain/models/categories.js +3 -4
  12. package/dist/cjs/domain/models/categories.js.map +1 -1
  13. package/dist/cjs/domain/models/circle.js +5 -5
  14. package/dist/cjs/domain/models/circle.js.map +1 -1
  15. package/dist/cjs/domain/models/mapConfig.js +1 -1
  16. package/dist/cjs/domain/models/mapConfig.js.map +1 -1
  17. package/dist/cjs/domain/models/marker.js +80 -86
  18. package/dist/cjs/domain/models/marker.js.map +1 -1
  19. package/dist/cjs/domain/models/multilanguage.js +1 -2
  20. package/dist/cjs/domain/models/multilanguage.js.map +1 -1
  21. package/dist/cjs/domain/models/place.js.map +1 -1
  22. package/dist/cjs/domain/models/property.js.map +1 -1
  23. package/dist/cjs/domain/models/route.js.map +1 -1
  24. package/dist/cjs/domain/models/routeInstance.js +1 -2
  25. package/dist/cjs/domain/models/routeInstance.js.map +1 -1
  26. package/dist/cjs/index.js +12 -12
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/infrastructure/cache/CacheManager.js.map +1 -1
  29. package/dist/cjs/infrastructure/cache/LRUCache.js.map +1 -1
  30. package/dist/cjs/infrastructure/cache/PersistentCache.js.map +1 -1
  31. package/dist/cjs/infrastructure/cache/cacheConfig.js +10 -10
  32. package/dist/cjs/infrastructure/cache/cacheConfig.js.map +1 -1
  33. package/dist/cjs/infrastructure/http/FetchHttpClient.js.map +1 -1
  34. package/dist/cjs/infrastructure/logging/CountlyLogger.js.map +1 -1
  35. package/dist/cjs/logger/logger.js +1 -1
  36. package/dist/cjs/logger/logger.js.map +1 -1
  37. package/dist/cjs/logger/rollbar.js +3 -3
  38. package/dist/cjs/logger/rollbar.js.map +1 -1
  39. package/dist/cjs/map/fitCamera.js +124 -0
  40. package/dist/cjs/map/fitCamera.js.map +1 -0
  41. package/dist/cjs/map/map.js +177 -31
  42. package/dist/cjs/map/map.js.map +1 -1
  43. package/dist/cjs/map/mapInteractionOptions.js +2 -3
  44. package/dist/cjs/map/mapInteractionOptions.js.map +1 -1
  45. package/dist/cjs/repository/repository.js.map +1 -1
  46. package/dist/cjs/repository/requester.js.map +1 -1
  47. package/dist/cjs/sdk.js +2 -3
  48. package/dist/cjs/sdk.js.map +1 -1
  49. package/dist/cjs/utils/3d.js +42 -8
  50. package/dist/cjs/utils/3d.js.map +1 -1
  51. package/dist/cjs/utils/error.js +1 -2
  52. package/dist/cjs/utils/error.js.map +1 -1
  53. package/dist/cjs/utils/images.js +1 -2
  54. package/dist/cjs/utils/images.js.map +1 -1
  55. package/dist/cjs/utils/loadGlb.js +3 -4
  56. package/dist/cjs/utils/loadGlb.js.map +1 -1
  57. package/dist/cjs/utils/mapvxHostname.js +1 -2
  58. package/dist/cjs/utils/mapvxHostname.js.map +1 -1
  59. package/dist/cjs/utils/nav-functions.js +4 -4
  60. package/dist/cjs/utils/nav-functions.js.map +1 -1
  61. package/dist/cjs/utils/opening-hours-helper.js +2 -2
  62. package/dist/cjs/utils/opening-hours-helper.js.map +1 -1
  63. package/dist/cjs/utils/preconnect.js +2 -2
  64. package/dist/cjs/utils/preconnect.js.map +1 -1
  65. package/dist/cjs/utils/route-utils.js +1 -2
  66. package/dist/cjs/utils/route-utils.js.map +1 -1
  67. package/dist/cjs/utils/semaphore.js.map +1 -1
  68. package/dist/cjs/utils/three.js +17 -7
  69. package/dist/cjs/utils/three.js.map +1 -1
  70. package/dist/cjs/utils/update-css.js +2 -3
  71. package/dist/cjs/utils/update-css.js.map +1 -1
  72. package/dist/cjs/utils/utils.js +5 -6
  73. package/dist/cjs/utils/utils.js.map +1 -1
  74. package/dist/es/assets/icons.d.ts +4 -4
  75. package/dist/es/assets/icons.d.ts.map +1 -1
  76. package/dist/es/assets/icons.js +6 -8
  77. package/dist/es/assets/icons.js.map +1 -1
  78. package/dist/es/controllers/routeController.d.ts +8 -0
  79. package/dist/es/controllers/routeController.d.ts.map +1 -1
  80. package/dist/es/controllers/routeController.js +27 -2
  81. package/dist/es/controllers/routeController.js.map +1 -1
  82. package/dist/es/domain/models/animation.d.ts +3 -3
  83. package/dist/es/domain/models/animation.js +2 -2
  84. package/dist/es/domain/models/animation.js.map +1 -1
  85. package/dist/es/domain/models/circle.d.ts.map +1 -1
  86. package/dist/es/domain/models/circle.js.map +1 -1
  87. package/dist/es/domain/models/mapConfig.js +1 -1
  88. package/dist/es/domain/models/mapConfig.js.map +1 -1
  89. package/dist/es/domain/models/marker.d.ts +0 -8
  90. package/dist/es/domain/models/marker.d.ts.map +1 -1
  91. package/dist/es/domain/models/marker.js +80 -86
  92. package/dist/es/domain/models/marker.js.map +1 -1
  93. package/dist/es/domain/models/place.js.map +1 -1
  94. package/dist/es/domain/models/property.js.map +1 -1
  95. package/dist/es/domain/models/route.js.map +1 -1
  96. package/dist/es/domain/models/scene3D.d.ts +34 -4
  97. package/dist/es/domain/models/scene3D.d.ts.map +1 -1
  98. package/dist/es/index.d.ts +10 -10
  99. package/dist/es/index.d.ts.map +1 -1
  100. package/dist/es/index.js +5 -5
  101. package/dist/es/index.js.map +1 -1
  102. package/dist/es/infrastructure/cache/CacheManager.js.map +1 -1
  103. package/dist/es/infrastructure/cache/LRUCache.js.map +1 -1
  104. package/dist/es/infrastructure/cache/PersistentCache.js.map +1 -1
  105. package/dist/es/infrastructure/cache/cacheConfig.js +8 -8
  106. package/dist/es/infrastructure/cache/cacheConfig.js.map +1 -1
  107. package/dist/es/infrastructure/http/FetchHttpClient.js.map +1 -1
  108. package/dist/es/infrastructure/logging/CountlyLogger.js.map +1 -1
  109. package/dist/es/interfaces/routeCacheResponse.d.ts.map +1 -1
  110. package/dist/es/logger/logger.js +1 -1
  111. package/dist/es/logger/logger.js.map +1 -1
  112. package/dist/es/logger/rollbar.js +1 -1
  113. package/dist/es/map/fitCamera.d.ts +94 -0
  114. package/dist/es/map/fitCamera.d.ts.map +1 -0
  115. package/dist/es/map/fitCamera.js +120 -0
  116. package/dist/es/map/fitCamera.js.map +1 -0
  117. package/dist/es/map/map.d.ts +65 -6
  118. package/dist/es/map/map.d.ts.map +1 -1
  119. package/dist/es/map/map.js +161 -25
  120. package/dist/es/map/map.js.map +1 -1
  121. package/dist/es/map/mapInteractionOptions.js.map +1 -1
  122. package/dist/es/repository/repository.js.map +1 -1
  123. package/dist/es/repository/requester.js.map +1 -1
  124. package/dist/es/sdk.d.ts.map +1 -1
  125. package/dist/es/sdk.js +0 -1
  126. package/dist/es/sdk.js.map +1 -1
  127. package/dist/es/utils/3d.d.ts +32 -1
  128. package/dist/es/utils/3d.d.ts.map +1 -1
  129. package/dist/es/utils/3d.js +31 -0
  130. package/dist/es/utils/3d.js.map +1 -1
  131. package/dist/es/utils/error.js.map +1 -1
  132. package/dist/es/utils/loadGlb.js.map +1 -1
  133. package/dist/es/utils/mapvxHostname.js.map +1 -1
  134. package/dist/es/utils/opening-hours-helper.js.map +1 -1
  135. package/dist/es/utils/preconnect.js.map +1 -1
  136. package/dist/es/utils/route-utils.js.map +1 -1
  137. package/dist/es/utils/semaphore.js.map +1 -1
  138. package/dist/es/utils/update-css.js +1 -1
  139. package/dist/es/utils/update-css.js.map +1 -1
  140. package/dist/es/utils/utils.js.map +1 -1
  141. package/dist/umd/index.js +51135 -40072
  142. package/dist/umd/index.js.map +1 -1
  143. package/dist/umd/styles.css +14 -32
  144. package/dist/umd/styles.css.map +1 -1
  145. package/dist/umd/styles.js +3 -3
  146. package/dist/umd/styles.js.map +1 -1
  147. package/package.json +36 -24
  148. package/dist/cjs/assets/route_animation_icon.svg +0 -15
  149. package/dist/cjs/assets/user-dot-icon.svg +0 -3
  150. package/dist/es/assets/route_animation_icon.svg +0 -15
  151. package/dist/es/assets/user-dot-icon.svg +0 -3
@@ -135,17 +135,18 @@ import { userLocationDataUrl } from "../assets/icons";
135
135
  import { RouteController } from "../controllers/routeController";
136
136
  import { rtlLanguages } from "../domain/models/_rtl";
137
137
  import { InternalAnimationConfig, InternalAnimationDrawingConfig, } from "../domain/models/animation";
138
+ import { CIRCLE_RENDER_ORDERS, circleRing, cloneCircleRecord, MAPVX_BRAND_COLOR, resolveCircleConfig, } from "../domain/models/circle";
138
139
  import { Loggeable } from "../domain/models/loggeable";
139
140
  import { DEFAULT_TILE_CACHE_CONFIG, MAPLIBRE_MAX_TILE_CACHE_HARD_CAP, } from "../domain/models/mapConfig";
140
- import { CIRCLE_RENDER_ORDERS, circleRing, cloneCircleRecord, MAPVX_BRAND_COLOR, resolveCircleConfig, } from "../domain/models/circle";
141
141
  import { MarkerAttribute } from "../domain/models/marker";
142
142
  import { MVXRoute } from "../domain/models/route";
143
143
  import { InternalDrawRouteConfiguration, InternalGetRouteConfiguration, } from "../domain/models/routeConfiguration";
144
144
  import { Repository } from "../repository/repository";
145
- import { hexToRgb, isPlacementVisibleOnFloor, mergeGlbScale, mergeRotation3D, opacityForZoom, resolveModels3DLights, resolveZoomTransparency, } from "../utils/3d";
145
+ import { effectiveModelOpacity, hexToRgb, isPlacementVisibleOnFloor, mergeGlbScale, mergeRotation3D, pointInFootprint, resolveModels3DLights, resolveRouteTransparency, resolveZoomTransparency, } from "../utils/3d";
146
146
  import { addScene3DLights, computeGlbBottomAnchor, loadGlbRoot } from "../utils/loadGlb";
147
147
  import { extractStepCoordinates } from "../utils/route-utils";
148
148
  import { getBoundingBox } from "../utils/utils";
149
+ import { computeOrientedCamera, refineCameraForPitch } from "./fitCamera";
149
150
  import { buildInteractionOptions, shouldDisableTouchRotation } from "./mapInteractionOptions";
150
151
  /**
151
152
  * Class to interact with the map.
@@ -188,6 +189,13 @@ export class InternalMapVXMap extends Loggeable {
188
189
  this.scene3DGlbLightCount = 0;
189
190
  /** Container group holding all placed GLB pivots; rebuilt on each floor change. */
190
191
  this.scene3DModelsGroup = undefined;
192
+ /**
193
+ * Route points currently used for GLB route-transparency collision, tagged with the floor on
194
+ * which each point's route segment is visible. Stored as coordinates (not pivot references) so
195
+ * they survive `placeScene3DModels` rebuilds on floor change. Only points matching the current
196
+ * floor participate, so a model only fades when a route is visibly crossing it on that floor.
197
+ */
198
+ this.scene3DRouteCollisionPoints = [];
191
199
  this.spriteIconMapping = {};
192
200
  this.spriteAtlasImage = new Image();
193
201
  if (rtlLanguages.includes((_a = mapConfig.lang) !== null && _a !== void 0 ? _a : "")) {
@@ -345,7 +353,7 @@ export class InternalMapVXMap extends Loggeable {
345
353
  // Hide MapLibre icon layers — replaced by deck.gl IconLayer
346
354
  this.hideLayers((_a = this.scene3DConfig) === null || _a === void 0 ? void 0 : _a.hideLayers);
347
355
  const models = (_b = this.scene3DConfig) === null || _b === void 0 ? void 0 : _b.models;
348
- const glbModelsActive = models !== undefined && models.enabled !== false && models.assets.length > 0;
356
+ const glbModelsActive = models !== undefined && models.assets.length > 0;
349
357
  if (glbModelsActive) {
350
358
  this.map.addLayer(this.createGlbCustomLayer());
351
359
  }
@@ -365,7 +373,7 @@ export class InternalMapVXMap extends Loggeable {
365
373
  });
366
374
  this.map.on("zoom", () => {
367
375
  if (this.is3DSceneActive()) {
368
- this.applyModelZoomOpacity();
376
+ this.applyModelOpacity();
369
377
  }
370
378
  });
371
379
  this.map.on("zoomend", () => {
@@ -525,7 +533,7 @@ export class InternalMapVXMap extends Loggeable {
525
533
  .multiply(new THREE.Matrix4().makeRotationX(Math.PI / 2));
526
534
  this.scene3DGlbScene = new THREE.Scene();
527
535
  const models = (_a = this.scene3DConfig) === null || _a === void 0 ? void 0 : _a.models;
528
- if (models && models.enabled !== false && models.assets.length > 0) {
536
+ if (models && models.assets.length > 0) {
529
537
  const lights = resolveModels3DLights(models);
530
538
  this.scene3DGlbLightCount = addScene3DLights(this.scene3DGlbScene, lights);
531
539
  }
@@ -588,7 +596,7 @@ export class InternalMapVXMap extends Loggeable {
588
596
  async loadScene3DGlbTemplates() {
589
597
  var _a;
590
598
  const models = (_a = this.scene3DConfig) === null || _a === void 0 ? void 0 : _a.models;
591
- if (!models || models.enabled === false || models.assets.length === 0) {
599
+ if (!models || models.assets.length === 0) {
592
600
  return;
593
601
  }
594
602
  this.scene3DGlbRootByUrl.clear();
@@ -609,12 +617,12 @@ export class InternalMapVXMap extends Loggeable {
609
617
  }
610
618
  }
611
619
  placeScene3DModels() {
612
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
620
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
613
621
  if (!this.scene3DGlbScene) {
614
622
  return;
615
623
  }
616
624
  const models = (_a = this.scene3DConfig) === null || _a === void 0 ? void 0 : _a.models;
617
- if (!models || models.enabled === false) {
625
+ if (!models) {
618
626
  return;
619
627
  }
620
628
  if (this.scene3DModelsGroup) {
@@ -656,15 +664,18 @@ export class InternalMapVXMap extends Loggeable {
656
664
  this.logWarning(new Error(`Invalid scene3D placement position (assetId=${placement.assetId}, floorKey=${floorKey})`));
657
665
  continue;
658
666
  }
659
- const target = maplibregl.MercatorCoordinate.fromLngLat([placement.position.lng, placement.position.lat], (_j = placement.position.altitude) !== null && _j !== void 0 ? _j : 0);
667
+ const target = maplibregl.MercatorCoordinate.fromLngLat([
668
+ placement.position.lng,
669
+ placement.position.lat,
670
+ ]);
660
671
  const sx = (target.x - refMercator.x) / meterScale;
661
672
  const sz = (target.y - refMercator.y) / meterScale;
662
- const ax = (_l = (_k = asset.defaultModelOffset) === null || _k === void 0 ? void 0 : _k.x) !== null && _l !== void 0 ? _l : 0;
663
- const ay = (_o = (_m = asset.defaultModelOffset) === null || _m === void 0 ? void 0 : _m.y) !== null && _o !== void 0 ? _o : 0;
664
- const az = (_q = (_p = asset.defaultModelOffset) === null || _p === void 0 ? void 0 : _p.z) !== null && _q !== void 0 ? _q : 0;
665
- const px = (_s = (_r = placement.offset) === null || _r === void 0 ? void 0 : _r.x) !== null && _s !== void 0 ? _s : 0;
666
- const py = (_u = (_t = placement.offset) === null || _t === void 0 ? void 0 : _t.y) !== null && _u !== void 0 ? _u : 0;
667
- const pz = (_w = (_v = placement.offset) === null || _v === void 0 ? void 0 : _v.z) !== null && _w !== void 0 ? _w : 0;
673
+ const ax = (_k = (_j = asset.defaultModelOffset) === null || _j === void 0 ? void 0 : _j.x) !== null && _k !== void 0 ? _k : 0;
674
+ const ay = (_m = (_l = asset.defaultModelOffset) === null || _l === void 0 ? void 0 : _l.y) !== null && _m !== void 0 ? _m : 0;
675
+ const az = (_p = (_o = asset.defaultModelOffset) === null || _o === void 0 ? void 0 : _o.z) !== null && _p !== void 0 ? _p : 0;
676
+ const px = (_r = (_q = placement.offset) === null || _q === void 0 ? void 0 : _q.x) !== null && _r !== void 0 ? _r : 0;
677
+ const py = (_t = (_s = placement.offset) === null || _s === void 0 ? void 0 : _s.y) !== null && _t !== void 0 ? _t : 0;
678
+ const pz = (_v = (_u = placement.offset) === null || _u === void 0 ? void 0 : _u.z) !== null && _v !== void 0 ? _v : 0;
668
679
  const pivot = new THREE.Group();
669
680
  pivot.position.set(sx + ax + px, ay + py, sz + az + pz);
670
681
  const mergedRot = mergeRotation3D(asset.defaultRotation, placement.rotation);
@@ -686,29 +697,109 @@ export class InternalMapVXMap extends Loggeable {
686
697
  model.position.set(-template.center.x, -template.center.y, -template.center.z);
687
698
  // Stash the resolved zoom-transparency config so it can be re-applied on every zoom change.
688
699
  pivot.userData.zoomTransparency = resolveZoomTransparency(models, asset, placement);
700
+ // Stash route-transparency config (may be undefined => feature off for this model) so a
701
+ // route crossing this model can fade it independently of zoom.
702
+ pivot.userData.routeTransparency = resolveRouteTransparency(models, asset, placement);
703
+ // Precompute the model footprint in the pivot's local x/z frame while `model` has no parent
704
+ // (its world matrix equals its local matrix), for cheap point-in-building collision tests.
705
+ // After `pivot.add(model)` the world matrix would fold in the pivot transform, giving a loose
706
+ // world-axis box instead; `pivot.worldToLocal` at test time undoes the pivot transform.
707
+ const localBox = new THREE.Box3().setFromObject(model);
708
+ pivot.userData.footprint = {
709
+ minX: localBox.min.x,
710
+ maxX: localBox.max.x,
711
+ minZ: localBox.min.z,
712
+ maxZ: localBox.max.z,
713
+ };
689
714
  pivot.add(model);
690
715
  modelsGroup.add(pivot);
691
716
  }
692
717
  this.scene3DModelsGroup = modelsGroup;
693
718
  this.scene3DGlbScene.add(modelsGroup);
694
- this.applyModelZoomOpacity();
719
+ // Recompute route collision against the freshly rebuilt pivots (footprints/floor changed),
720
+ // then apply the combined opacity.
721
+ this.recomputeRouteAffectedModels();
722
+ this.applyModelOpacity();
695
723
  this.map.triggerRepaint();
696
724
  }
697
725
  /**
698
- * Applies zoom-based transparency to placed GLB models, using each placement's resolved
699
- * {@link ZoomTransparencyConfig} stored in `pivot.userData.zoomTransparency`. Hard threshold:
700
- * fully opaque below the configured zoom, configured opacity at/above it. Only triggers a
701
- * repaint when at least one material actually changed.
726
+ * Sets the route path used for GLB route-transparency collision and re-evaluates which models a
727
+ * route currently crosses. Points are tagged with the floor on which their route segment is
728
+ * visible; only points on the current floor participate, so a model never fades unless a route is
729
+ * visibly crossing it on the displayed floor. Pass an empty array to clear.
730
+ *
731
+ * No-op unless the 3D scene is active.
732
+ */
733
+ setRouteCollisionPath(points) {
734
+ if (!this.is3DSceneActive()) {
735
+ return;
736
+ }
737
+ this.scene3DRouteCollisionPoints = points;
738
+ this.recomputeRouteAffectedModels();
739
+ this.applyModelOpacity();
740
+ }
741
+ /**
742
+ * Recomputes `pivot.userData.routeAffected` for every placed model by testing the current-floor
743
+ * route collision points against each model's precomputed footprint. Cheap point-in-box tests in
744
+ * the pivot's local x/z frame; runs only when the route path or the placed models change, never
745
+ * per zoom frame.
746
+ */
747
+ recomputeRouteAffectedModels() {
748
+ var _a;
749
+ if (!this.scene3DModelsGroup) {
750
+ return;
751
+ }
752
+ const floorKey = (_a = this.currentFloor) !== null && _a !== void 0 ? _a : "";
753
+ const points = this.scene3DRouteCollisionPoints.filter((p) => p.floorKey === floorKey);
754
+ // Convert each route point into the models group's local frame (metres relative to
755
+ // initialCenter), exactly as placements are positioned in `placeScene3DModels`. The group's
756
+ // own transform (globalOffset) is applied via `localToWorld` at test time, so route points and
757
+ // model pivots share one frame regardless of the collective offset.
758
+ const refMercator = MercatorCoordinate.fromLngLat(this.initialCenter, 0);
759
+ const meterScale = refMercator.meterInMercatorCoordinateUnits();
760
+ const groupLocalPoints = points.map((p) => {
761
+ const target = MercatorCoordinate.fromLngLat([p.lng, p.lat], 0);
762
+ const x = (target.x - refMercator.x) / meterScale;
763
+ const z = (target.y - refMercator.y) / meterScale;
764
+ return new THREE.Vector3(x, 0, z);
765
+ });
766
+ this.scene3DModelsGroup.updateMatrixWorld(true);
767
+ const local = new THREE.Vector3();
768
+ for (const pivot of this.scene3DModelsGroup.children) {
769
+ const routeCfg = pivot.userData.routeTransparency;
770
+ const footprint = pivot.userData.footprint;
771
+ // Only models with a route-transparency config participate (feature activation gate).
772
+ if (!routeCfg || !footprint || groupLocalPoints.length === 0) {
773
+ pivot.userData.routeAffected = false;
774
+ continue;
775
+ }
776
+ pivot.userData.routeAffected = groupLocalPoints.some((gp) => {
777
+ // group-local -> world (applies globalOffset) -> pivot-local (undoes position/rotation/
778
+ // scale), then test its x/z against the footprint. y is ignored (floor-level footprint).
779
+ local.copy(gp);
780
+ this.scene3DModelsGroup.localToWorld(local);
781
+ pivot.worldToLocal(local);
782
+ return pointInFootprint(footprint, local.x, local.z);
783
+ });
784
+ }
785
+ }
786
+ /**
787
+ * Applies transparency to placed GLB models, combining zoom-based transparency
788
+ * ({@link ZoomTransparencyConfig}) with route-based transparency ({@link RouteTransparencyConfig})
789
+ * for models a route currently crosses (`pivot.userData.routeAffected`). The more transparent of
790
+ * the two wins. Only triggers a repaint when at least one material actually changed.
702
791
  */
703
- applyModelZoomOpacity() {
792
+ applyModelOpacity() {
704
793
  if (!this.scene3DModelsGroup) {
705
794
  return;
706
795
  }
707
796
  const zoom = this.getZoomLevel();
708
797
  let changed = false;
709
798
  for (const pivot of this.scene3DModelsGroup.children) {
710
- const cfg = pivot.userData.zoomTransparency;
711
- const opacity = opacityForZoom(cfg, zoom);
799
+ const zoomCfg = pivot.userData.zoomTransparency;
800
+ const routeCfg = pivot.userData.routeTransparency;
801
+ const routeAffected = pivot.userData.routeAffected === true;
802
+ const opacity = effectiveModelOpacity(zoomCfg, routeCfg, zoom, routeAffected);
712
803
  pivot.traverse((obj) => {
713
804
  const mesh = obj;
714
805
  if (!mesh.isMesh) {
@@ -1745,6 +1836,52 @@ export class InternalMapVXMap extends Loggeable {
1745
1836
  this.map.fitBounds(bounds, fitOptions);
1746
1837
  }
1747
1838
  }
1839
+ easeToCoordinates(coordinates, options) {
1840
+ var _a;
1841
+ if (coordinates.length === 0) {
1842
+ return;
1843
+ }
1844
+ const onComplete = options === null || options === void 0 ? void 0 : options.onComplete;
1845
+ // Get container dimensions for percentage-based padding calculations
1846
+ const container = this.map.getContainer();
1847
+ const containerWidth = container.clientWidth;
1848
+ const containerHeight = container.clientHeight;
1849
+ // Convert padding values with units to pixels
1850
+ const padding = convertPaddingToPixels(options === null || options === void 0 ? void 0 : options.padding, containerWidth, containerHeight);
1851
+ if (coordinates.length === 1) {
1852
+ const [coord] = coordinates;
1853
+ if (onComplete) {
1854
+ void this.map.once("moveend", onComplete);
1855
+ }
1856
+ this.map.easeTo(Object.assign(Object.assign({}, options), { center: { lat: coord.lat, lng: coord.lng }, padding }));
1857
+ return;
1858
+ }
1859
+ const bearing = (options === null || options === void 0 ? void 0 : options.bearing) !== undefined ? options.bearing : this.map.getBearing();
1860
+ const pitch = (options === null || options === void 0 ? void 0 : options.pitch) !== undefined ? options.pitch : this.map.getPitch();
1861
+ const cameraParams = {
1862
+ bearing,
1863
+ padding,
1864
+ containerWidth,
1865
+ containerHeight,
1866
+ maxZoom: (_a = options === null || options === void 0 ? void 0 : options.maxZoom) !== null && _a !== void 0 ? _a : this.map.getMaxZoom(),
1867
+ minZoom: this.map.getMinZoom(),
1868
+ };
1869
+ // Orthographic oriented fit (exact for pitch 0). maplibre's fitBounds only
1870
+ // fits a north-up LngLatBounds, which wastes viewport space for diagonal
1871
+ // geometries under a bearing.
1872
+ let camera = computeOrientedCamera(coordinates, cameraParams);
1873
+ // With a tilted camera the orthographic zoom is off due to perspective, so
1874
+ // refine it against maplibre's real projection on a cloned transform.
1875
+ if (pitch > 0) {
1876
+ camera = refineCameraForPitch(this.map.transform.clone(), coordinates, camera, Object.assign(Object.assign({}, cameraParams), { pitch }));
1877
+ }
1878
+ if (onComplete) {
1879
+ void this.map.once("moveend", onComplete);
1880
+ }
1881
+ this.map.easeTo(Object.assign(Object.assign({}, options), { center: camera.center, zoom: camera.zoom, bearing,
1882
+ pitch,
1883
+ padding }));
1884
+ }
1748
1885
  fitRouteByPlace(route, placeId, options) {
1749
1886
  // Filter steps that belong to the specified place
1750
1887
  const stepsInPlace = route.steps.filter((step) => step.startInsidePlace === placeId || step.endInsidePlace === placeId);
@@ -1798,7 +1935,7 @@ export class InternalMapVXMap extends Loggeable {
1798
1935
  throw new Error("Error: Failed to add route");
1799
1936
  }
1800
1937
  }
1801
- updateRouteProgress(routeId, position, behindStyle = { type: "Solid", color: "#757575" }) {
1938
+ updateRouteProgress(routeId, position, behindStyle = { type: "Solid", color: "#276EF1" }) {
1802
1939
  try {
1803
1940
  const behindConfig = new InternalDrawRouteConfiguration({
1804
1941
  routeStyle: behindStyle,
@@ -1854,7 +1991,6 @@ export class InternalMapVXMap extends Loggeable {
1854
1991
  }
1855
1992
  return "";
1856
1993
  }
1857
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1858
1994
  removePopOver(id) {
1859
1995
  throw Error("Not implemented");
1860
1996
  }