venue-js 1.0.0 → 1.1.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -144,7 +144,6 @@ var createPopulator = ({
144
144
  const populateDetail = (detail) => Promise.resolve(detail);
145
145
  const populateFootprint = (footprint) => Promise.resolve(footprint);
146
146
  const populateGeofence = (geofence) => Promise.resolve(geofence);
147
- const populateOpening = (opening) => Promise.resolve(opening);
148
147
  const populateRelationship = (relationship) => Promise.resolve(relationship);
149
148
  const populatePrivilege = (privilege) => Promise.resolve(privilege);
150
149
  const populateEvent = (event) => Promise.resolve(event);
@@ -220,8 +219,11 @@ var createPopulator = ({
220
219
  const unit = units.find(
221
220
  (unit2) => unit2.properties.category === "walkway" && unit2.properties.level_id === kiosk.properties.level_id && booleanWithin(kiosk, unit2)
222
221
  );
223
- const sections = await internalFilterByType("section");
224
- const section = sections.find((section2) => booleanWithin(anchor, section2));
222
+ let section = null;
223
+ if (anchor) {
224
+ const sections = await internalFilterByType("section");
225
+ section = sections.find((section2) => booleanWithin(anchor, section2));
226
+ }
225
227
  return {
226
228
  ...kiosk,
227
229
  properties: {
@@ -290,6 +292,18 @@ var createPopulator = ({
290
292
  }
291
293
  };
292
294
  };
295
+ const populateOpening = async (opening) => {
296
+ const venue = await internalFindById(opening.properties.venue_id);
297
+ const level = await internalFindById(opening.properties.level_id);
298
+ return {
299
+ ...opening,
300
+ properties: {
301
+ venue,
302
+ level: await populateLevel(level),
303
+ ordinal: level.properties.ordinal
304
+ }
305
+ };
306
+ };
293
307
  const populateSection = async (section) => {
294
308
  const venue = await internalFindById(section.properties.venue_id);
295
309
  const level = await internalFindById(section.properties.level_id);
@@ -307,17 +321,21 @@ var createPopulator = ({
307
321
  const venue = await internalFindById(unit.properties.venue_id);
308
322
  const level = await internalFindById(unit.properties.level_id);
309
323
  const sections = await internalFilterByType("section");
310
- const section = sections.find((section2) => booleanWithin(unit, section2));
311
- return {
312
- ...unit,
313
- properties: {
314
- ...unit.properties,
315
- venue,
316
- ordinal: level.properties.ordinal,
317
- level: await populateLevel(level),
318
- section: section ? await populateSection(section) : null
319
- }
320
- };
324
+ try {
325
+ const section = unit.geometry.type !== "MultiPolygon" ? sections.find((section2) => booleanWithin(unit, section2)) : null;
326
+ return {
327
+ ...unit,
328
+ properties: {
329
+ ...unit.properties,
330
+ venue,
331
+ ordinal: level.properties.ordinal,
332
+ level: await populateLevel(level),
333
+ section: section ? await populateSection(section) : null
334
+ }
335
+ };
336
+ } catch (err) {
337
+ console.log(`error finding section `, { unit, sections });
338
+ }
321
339
  };
322
340
  const populateVenue = (venue) => {
323
341
  return Promise.resolve(venue);
@@ -360,6 +378,26 @@ var createPopulator = ({
360
378
  };
361
379
  };
362
380
 
381
+ // src/data/utils/match-filters.ts
382
+ function isInFilter(filter) {
383
+ return typeof filter === "object" && filter !== null && "$in" in filter && Array.isArray(filter.$in);
384
+ }
385
+ var someIntersect = (a, b) => a.some((v) => b.includes(v));
386
+ function matchFilter(value, filter) {
387
+ if (Array.isArray(value)) {
388
+ if (isInFilter(filter)) return someIntersect(value, filter.$in);
389
+ return value.includes(filter);
390
+ } else {
391
+ if (isInFilter(filter)) return filter.$in.includes(value);
392
+ return value === filter;
393
+ }
394
+ }
395
+ function matchFilters(item, filters) {
396
+ return Object.entries(filters).every(([key, filter]) => {
397
+ return matchFilter(item.properties[key], filter);
398
+ });
399
+ }
400
+
363
401
  // src/data/getDataClient.ts
364
402
  var getDataClient = (options) => {
365
403
  const observers = /* @__PURE__ */ new Map();
@@ -435,7 +473,12 @@ var getDataClient = (options) => {
435
473
  queryKey: [featureType, "list", params],
436
474
  queryFn: async () => {
437
475
  const features = await internalFilterByType(featureType);
438
- return params.populate === true ? await Promise.all(features.map((f) => populator[featureType](f))) : features;
476
+ const filters = params.filters ?? {};
477
+ let result = features;
478
+ if (params.filters) {
479
+ result = features.filter((f) => matchFilters(f, filters));
480
+ }
481
+ return params.populate === true ? await Promise.all(result.map((f) => populator[featureType](f))) : result;
439
482
  },
440
483
  ...options2 ?? {}
441
484
  });
@@ -482,13 +525,11 @@ import {
482
525
  ui as ui3,
483
526
  Map as Map2,
484
527
  TileLayer,
485
- VectorLayer as VectorLayer2,
486
528
  Extent,
487
529
  LineString as LineString3,
488
- animation,
489
530
  Marker as Marker2,
490
- Coordinate as Coordinate3
491
- } from "maptalks";
531
+ Coordinate as Coordinate4
532
+ } from "maptalks-gl";
492
533
  import TWEEN2 from "@tweenjs/tween.js";
493
534
  import _6 from "lodash";
494
535
 
@@ -569,7 +610,7 @@ function isNumber(num) {
569
610
 
570
611
  // src/IndoorMap/IndoorMap.ts
571
612
  import turfDistance from "@turf/distance";
572
- import turfCenter2 from "@turf/center";
613
+ import turfCenter3 from "@turf/center";
573
614
 
574
615
  // ../../node_modules/@turf/meta/dist/esm/index.js
575
616
  function coordEach(geojson, callback, excludeWrapCoord) {
@@ -691,13 +732,12 @@ function bbox(geojson, options = {}) {
691
732
  });
692
733
  return result;
693
734
  }
694
- var turf_bbox_default = bbox;
735
+ var index_default = bbox;
695
736
 
696
737
  // src/IndoorMap/IndoorMap.ts
697
738
  import scale from "@turf/transform-scale";
698
739
  import bboxPolygon from "@turf/bbox-polygon";
699
740
  import { PerspectiveCamera } from "three";
700
- import { ThreeLayer as ThreeLayer4 } from "maptalks.three";
701
741
 
702
742
  // src/IndoorMap/constants.ts
703
743
  var defaultLayerOption = { enableAltitude: true };
@@ -1442,10 +1482,8 @@ var GeometryType = {
1442
1482
  var ORDINAL_HEIGHT = 0;
1443
1483
  var VENUE_Z_INDEX = 0;
1444
1484
  var LEVEL_Z_INDEX = 2;
1445
- var UNIT_Z_INDEX = 3;
1446
1485
  var FIXTURE_Z_INDEX = 4;
1447
1486
  var DECORATION_Z_INDEX = 4;
1448
- var KIOSK_Z_INDEX = 5;
1449
1487
  var OPENING_Z_INDEX = 3;
1450
1488
  var SECTION_Z_INDEX = 6;
1451
1489
  var USER_LOCATION_Z_INDEX = 99;
@@ -1533,77 +1571,6 @@ var SPRITE_HIGHLIGHT_MARKER_FEATURE = SPRITE_MARKER_FEATURE.map(
1533
1571
  );
1534
1572
  var OCCUPANT_TEXT_MARKER_CLASSNAME = "mtk-occupant-text-marker";
1535
1573
  var getAltitude = (properties) => Math.max(0, properties.ordinal * ORDINAL_HEIGHT || 0);
1536
- var createRoomUnit = (feature2, style, options) => {
1537
- const { geometry, id, properties } = feature2;
1538
- const { allowOverride, inheritFillColorToLine } = options;
1539
- const symbolStyle = { ...style };
1540
- if (allowOverride) _4.merge(symbolStyle, properties.style);
1541
- if (inheritFillColorToLine) symbolStyle.lineColor = symbolStyle.polygonFill;
1542
- return new GeometryType[geometry.type](geometry.coordinates, {
1543
- properties: {
1544
- id,
1545
- feature_type: "unit",
1546
- category: properties.category,
1547
- altitude: getAltitude(properties)
1548
- },
1549
- symbol: symbolStyle,
1550
- defaultSymbol: symbolStyle
1551
- });
1552
- };
1553
- var createAmenityUnit = (feature2, style, options = {}) => {
1554
- const { geometry, id, properties } = feature2;
1555
- const { allowOverride, inheritFillColorToLine } = options;
1556
- const symbolStyle = { ...style };
1557
- if (allowOverride) _4.merge(symbolStyle, properties.style);
1558
- if (inheritFillColorToLine) symbolStyle.lineColor = symbolStyle.polygonFill;
1559
- const area = new GeometryType[geometry.type](geometry.coordinates, {
1560
- properties: {
1561
- id,
1562
- feature_type: "unit",
1563
- category: properties.category,
1564
- altitude: getAltitude(properties)
1565
- },
1566
- symbol: symbolStyle,
1567
- defaultSymbol: symbolStyle
1568
- });
1569
- return area;
1570
- };
1571
- var createNonpublicUnit = (feature2, style, options = {}) => {
1572
- const { geometry, id, properties } = feature2;
1573
- const { allowOverride, inheritFillColorToLine } = options;
1574
- const symbolStyle = { ...style };
1575
- if (allowOverride) _4.merge(symbolStyle, properties.style);
1576
- if (inheritFillColorToLine) symbolStyle.lineColor = symbolStyle.polygonFill;
1577
- return new GeometryType[geometry.type](geometry.coordinates, {
1578
- properties: {
1579
- id,
1580
- feature_type: "unit",
1581
- category: properties.category,
1582
- altitude: getAltitude(properties)
1583
- },
1584
- symbol: {
1585
- ...style
1586
- }
1587
- });
1588
- };
1589
- var createWalkwayUnit = (feature2, style, options) => {
1590
- const { geometry, id, properties } = feature2;
1591
- const { allowOverride, inheritFillColorToLine } = options;
1592
- const symbolStyle = { ...style };
1593
- if (allowOverride) _4.merge(symbolStyle, properties.style);
1594
- if (inheritFillColorToLine) symbolStyle.lineColor = symbolStyle.polygonFill;
1595
- return new GeometryType[geometry.type](geometry.coordinates, {
1596
- properties: {
1597
- id,
1598
- feature_type: "unit",
1599
- category: properties.category,
1600
- altitude: getAltitude(properties)
1601
- },
1602
- symbol: {
1603
- ...symbolStyle
1604
- }
1605
- });
1606
- };
1607
1574
  var createWaterFixture = (feature2, style, options = {}) => {
1608
1575
  const { geometry, properties } = feature2;
1609
1576
  const { allowOverride, inheritFillColorToLine, zIndex } = options;
@@ -2155,62 +2122,6 @@ var styledFeatureGenerator = (mapTheme) => {
2155
2122
  zIndex: LEVEL_Z_INDEX
2156
2123
  });
2157
2124
  },
2158
- createUnit: (feature2) => {
2159
- const {
2160
- feature_type,
2161
- properties: { category }
2162
- } = feature2;
2163
- const elementStyle = getElementSymbol(`${feature_type}.${category}`);
2164
- const options = getElementOptions(`${feature_type}.${category}`);
2165
- const zIndex = UNIT_Z_INDEX;
2166
- switch (feature2.properties.category) {
2167
- case "room":
2168
- case "terrace":
2169
- case "unenclosedarea":
2170
- case "vegetation":
2171
- case "unspecified":
2172
- case "recreation":
2173
- return createRoomUnit(feature2, elementStyle, options).setZIndex(
2174
- zIndex
2175
- );
2176
- case "firstaid":
2177
- case "restroom":
2178
- case "restroom.female":
2179
- case "restroom.male":
2180
- case "escalator":
2181
- case "elevator":
2182
- case "stairs":
2183
- case "stairs.emergencyexit":
2184
- case "parking":
2185
- case "smokingarea":
2186
- case "mothersroom":
2187
- case "privatelounge":
2188
- case "shuttle":
2189
- case "fieldofplay":
2190
- return createAmenityUnit(feature2, elementStyle, options).setZIndex(
2191
- zIndex
2192
- );
2193
- case "nonpublic":
2194
- case "structure":
2195
- case "brick":
2196
- case "concrete":
2197
- case "drywall":
2198
- case "glass":
2199
- case "wood":
2200
- case "column":
2201
- case "opentobelow":
2202
- return createNonpublicUnit(feature2, elementStyle, options).setZIndex(
2203
- zIndex
2204
- );
2205
- case "walkway":
2206
- case "footbridge":
2207
- return createWalkwayUnit(feature2, elementStyle, options).setZIndex(
2208
- zIndex - 1
2209
- );
2210
- default:
2211
- break;
2212
- }
2213
- },
2214
2125
  createMarker: (feature2) => {
2215
2126
  try {
2216
2127
  const { geometry, properties } = feature2;
@@ -2395,32 +2306,6 @@ var styledFeatureGenerator = (mapTheme) => {
2395
2306
  symbol
2396
2307
  });
2397
2308
  },
2398
- createKiosk: (feature2) => {
2399
- const { geometry, feature_type, properties, id } = feature2;
2400
- const {
2401
- properties: { category }
2402
- } = feature2;
2403
- const elementStyle = getElementSymbol(`${feature_type}.${category}`);
2404
- const options = getElementOptions(
2405
- `${feature_type}.${category}.geometry.options`
2406
- );
2407
- const { allowOverride, inheritFillColorToLine } = options;
2408
- const symbolStyle = { ...elementStyle };
2409
- if (allowOverride) _4.merge(symbolStyle, properties.style);
2410
- if (inheritFillColorToLine)
2411
- symbolStyle.lineColor = symbolStyle.polygonFill;
2412
- return new GeometryType[geometry.type](geometry.coordinates, {
2413
- properties: {
2414
- id,
2415
- feature_type,
2416
- category: "kiosk",
2417
- altitude: getAltitude(properties)
2418
- },
2419
- symbol: symbolStyle,
2420
- defaultSymbol: symbolStyle,
2421
- zIndex: KIOSK_Z_INDEX
2422
- });
2423
- },
2424
2309
  createSection: (feature2) => {
2425
2310
  const { properties, feature_type } = feature2;
2426
2311
  const { category } = properties;
@@ -2793,43 +2678,6 @@ var styledFeatureGenerator = (mapTheme) => {
2793
2678
  markerProperties
2794
2679
  );
2795
2680
  },
2796
- create3DUnit: async (unit, threeLayer) => {
2797
- const { id, feature_type, properties } = unit;
2798
- const { category, ordinal, model3d } = properties;
2799
- const modelProperty = {
2800
- id,
2801
- feature_type,
2802
- category,
2803
- ordinal
2804
- };
2805
- const center2 = turfCenter(unit);
2806
- const coordinate = _4.get(center2, "geometry.coordinates");
2807
- const models = await create3DModels(
2808
- model3d,
2809
- coordinate,
2810
- modelProperty,
2811
- threeLayer
2812
- );
2813
- return models;
2814
- },
2815
- create3DKiosk: async (kiosk, threeLayer) => {
2816
- const { id, feature_type, properties } = kiosk;
2817
- const { ordinal, model3d } = properties;
2818
- const modelProperty = {
2819
- id,
2820
- feature_type,
2821
- ordinal
2822
- };
2823
- const center2 = turfCenter(kiosk);
2824
- const coordinate = _4.get(center2, "geometry.coordinates");
2825
- const models = await create3DModels(
2826
- model3d,
2827
- coordinate,
2828
- modelProperty,
2829
- threeLayer
2830
- );
2831
- return models;
2832
- },
2833
2681
  createVenue3DModel: async (venue, threeLayer) => {
2834
2682
  const { id, feature_type, properties } = venue;
2835
2683
  const { category, model3d } = properties;
@@ -2908,52 +2756,6 @@ var styledFeatureGenerator = (mapTheme) => {
2908
2756
  );
2909
2757
  return object;
2910
2758
  },
2911
- createExtrudedKiosk: (kiosk, threeLayer, options) => {
2912
- const extrudeHeight = _4.get(options, "height");
2913
- if (!extrudeHeight) return;
2914
- const kioskProperty = getFeatureProperties(kiosk);
2915
- const options3d = {
2916
- offset: -0.1,
2917
- altitude: _4.get(options, "altitude", 0)
2918
- };
2919
- const color = kioskProperty.defaultColor;
2920
- if (color === "transparent") return;
2921
- const material = new MeshLambertMaterial({
2922
- color,
2923
- transparent: true
2924
- });
2925
- const object = createExtrudePolygon(
2926
- kiosk.geometry,
2927
- threeLayer,
2928
- material,
2929
- extrudeHeight,
2930
- kioskProperty,
2931
- options3d
2932
- );
2933
- return object;
2934
- },
2935
- createExtrudedLevel: (level, threeLayer, options) => {
2936
- const extrudeHeight = _4.get(options, "height", 0);
2937
- if (!extrudeHeight) return;
2938
- const levelProperty = getFeatureProperties(level);
2939
- const options3d = { offset: 0 };
2940
- const color = levelProperty?.defaultColor || "#808080";
2941
- if (color === "transparent" || _4.isEmpty(level?.geometry?.coordinates))
2942
- return;
2943
- const material = new MeshLambertMaterial({
2944
- color,
2945
- transparent: true
2946
- });
2947
- const object = createExtrudePolygon(
2948
- level.geometry,
2949
- threeLayer,
2950
- material,
2951
- extrudeHeight,
2952
- levelProperty,
2953
- options3d
2954
- );
2955
- return object;
2956
- },
2957
2759
  createAmbientLight: (config) => {
2958
2760
  const { color: colorString = "0xffffff", intensity = 1 } = config;
2959
2761
  const color = parseInt(colorString, 16);
@@ -3213,175 +3015,252 @@ var CameraManager = class {
3213
3015
  };
3214
3016
  };
3215
3017
 
3216
- // src/IndoorMap/renderer/Element3DRenderer.ts
3217
- import * as maptalks5 from "maptalks";
3218
- import * as THREE2 from "three";
3018
+ // src/IndoorMap/renderer/RendererManager.ts
3019
+ import _min from "lodash/min";
3020
+ import { center as turfCenter2 } from "@turf/center";
3219
3021
  import { ThreeLayer as ThreeLayer3 } from "maptalks.three";
3220
- import turfBuffer2 from "@turf/buffer";
3221
- import { MeshLambertMaterial as MeshLambertMaterial3 } from "three";
3022
+ import * as THREE3 from "three";
3222
3023
 
3223
- // src/IndoorMap/object3d/Escalator.ts
3024
+ // src/IndoorMap/renderer/3d/Element3DRenderer.ts
3224
3025
  import * as maptalks4 from "maptalks";
3225
- import { BaseObject as BaseObject5 } from "maptalks.three";
3026
+ import * as THREE2 from "three";
3027
+ import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader";
3028
+ import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
3029
+ import turfBuffer2 from "@turf/buffer";
3030
+
3031
+ // src/IndoorMap/renderer/3d/element3DRendererOptions.ts
3032
+ var element3DRendererOptions = {
3033
+ unit: {
3034
+ default: { color: "#ffffff", height: 4 },
3035
+ byCategory: {
3036
+ walkway: { color: "#cccccc", height: 0.1 },
3037
+ terrace: { color: "#cccccc", height: 0.1 },
3038
+ unenclosedarea: { color: "#cccccc", height: 0.2 },
3039
+ nonpublic: { color: "#999999", height: 0.3 },
3040
+ escalator: { height: 0.2 },
3041
+ room: { color: "#ffffff", height: 2, bottomHeight: 0.12 }
3042
+ }
3043
+ },
3044
+ kiosk: {
3045
+ default: { color: "#666666", height: 0.6, bottomHeight: 0.12 }
3046
+ },
3047
+ fixture: {
3048
+ default: { color: "#ffffff", height: 0.5 },
3049
+ byCategory: {
3050
+ water: { color: "#ACD7EC", height: 0.1 },
3051
+ vegetation: { color: "#91C499", height: 0.5 }
3052
+ }
3053
+ }
3054
+ };
3055
+
3056
+ // src/IndoorMap/renderer/3d/objects/TextSpriteMarker.ts
3057
+ import { Coordinate as Coordinate2, Util as Util4 } from "maptalks";
3226
3058
  import * as THREE from "three";
3227
- import { largestRect as largestRect2 } from "d3plus-shape";
3228
- import { range as range2 } from "lodash-es";
3229
- var defaultRectAngleToCalc2 = range2(-90, 90, 2);
3230
- var Escalator = class extends BaseObject5 {
3231
- layer;
3232
- constructor(feature2, opts, layer) {
3059
+ import { BaseObject as BaseObject5 } from "maptalks.three";
3060
+ import { isNil, set } from "lodash";
3061
+
3062
+ // src/IndoorMap/renderer/utils/interpolateStops.ts
3063
+ var interpolateStops = ({ stops }, zoom) => {
3064
+ if (zoom <= stops[0][0]) return stops[0][1];
3065
+ if (zoom >= stops[stops.length - 1][0]) return stops[stops.length - 1][1];
3066
+ for (let i = 0; i < stops.length - 1; i++) {
3067
+ const [z1, v1] = stops[i];
3068
+ const [z2, v2] = stops[i + 1];
3069
+ if (zoom >= z1 && zoom <= z2) {
3070
+ const t = (zoom - z1) / (z2 - z1);
3071
+ return v1 + t * (v2 - v1);
3072
+ }
3073
+ }
3074
+ };
3075
+
3076
+ // src/IndoorMap/renderer/3d/objects/TextSpriteMarker.ts
3077
+ var OPTIONS4 = {
3078
+ // Texture options
3079
+ text: "",
3080
+ textAlign: "center",
3081
+ color: "#ffffff",
3082
+ fontFamily: "sans-serif",
3083
+ fontSize: 28,
3084
+ fontWeight: 400,
3085
+ background: "rgba(0, 0, 0, 0.2)",
3086
+ lineHeight: 32,
3087
+ padding: 8,
3088
+ strokeColor: "#000000",
3089
+ strokeWidth: 6,
3090
+ strokeStyle: "round",
3091
+ // Sprite options
3092
+ /* Overall scale multiplier */
3093
+ scale: 1,
3094
+ altitude: 0,
3095
+ opacity: 1
3096
+ };
3097
+ var TextSpriteMarker = class extends BaseObject5 {
3098
+ #altitudeOffset = 0;
3099
+ constructor(coordinate, options, layer, properties = {}) {
3100
+ options = Util4.extend({}, OPTIONS4, options, { layer });
3233
3101
  super();
3234
- this._initOptions({});
3235
- this.layer = layer;
3102
+ this._coordinate = new Coordinate2(coordinate);
3103
+ this._initOptions(options);
3236
3104
  this._createGroup();
3237
- const startAltitude = feature2.properties.ordinal * HEIGHT_METER;
3238
- const endAltitude = startAltitude + 0.5 * HEIGHT_METER;
3239
- this.buildMesh(feature2.geometry.coordinates[0], {
3240
- startAltitude,
3241
- endAltitude
3105
+ this.properties = { ...properties };
3106
+ const sprite = this._createSprite();
3107
+ this.getObject3d().add(sprite);
3108
+ this._updatePosition();
3109
+ this.type = "TextSpriteMarker";
3110
+ }
3111
+ getOptions() {
3112
+ return super.getOptions();
3113
+ }
3114
+ _createSprite() {
3115
+ const options = this.getOptions();
3116
+ const texture = this._createTextTexture(options.text, options);
3117
+ const material = new THREE.SpriteMaterial({
3118
+ map: texture,
3119
+ transparent: true,
3120
+ alphaTest: 0.1
3242
3121
  });
3243
- if (opts.props) this.setProperties(opts.props);
3122
+ const sprite = new THREE.Sprite(material);
3123
+ const w = texture.image.width;
3124
+ const h = texture.image.height;
3125
+ const base = 1 / 16;
3126
+ const normalizedScale = options.scale / this.getMap().getGLRes();
3127
+ sprite.scale.set(w * base * normalizedScale, h * base * normalizedScale, 1);
3128
+ this.#altitudeOffset = Math.max(
3129
+ h * base * options.scale * 0.5,
3130
+ 0.05
3131
+ // minimum lift in world units
3132
+ );
3133
+ return sprite;
3244
3134
  }
3245
- buildMesh(coordinates, { startAltitude, endAltitude }) {
3246
- const rect = largestRect2(coordinates, {
3247
- cache: true,
3248
- angle: defaultRectAngleToCalc2
3135
+ _createTextTexture(text, options = {}) {
3136
+ const {
3137
+ padding,
3138
+ fontSize,
3139
+ fontFamily,
3140
+ fontWeight,
3141
+ lineHeight,
3142
+ background,
3143
+ color,
3144
+ textAlign,
3145
+ strokeColor,
3146
+ strokeWidth,
3147
+ maxWidth
3148
+ } = options || {};
3149
+ const canvas = document.createElement("canvas");
3150
+ const ctx = canvas.getContext("2d");
3151
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
3152
+ const paragraphs = String(text).split("\n");
3153
+ const wrappedLines = [];
3154
+ paragraphs.forEach((paragraph) => {
3155
+ if (isNil(maxWidth) || isNaN(maxWidth)) {
3156
+ wrappedLines.push(paragraph);
3157
+ return;
3158
+ }
3159
+ const words = paragraph.split(/\s+/);
3160
+ let currentLine = "";
3161
+ words.forEach((word) => {
3162
+ const testLine = currentLine ? currentLine + " " + word : word;
3163
+ const testWidth = ctx.measureText(testLine).width;
3164
+ if (testWidth > maxWidth && currentLine) {
3165
+ wrappedLines.push(currentLine);
3166
+ currentLine = word;
3167
+ } else {
3168
+ currentLine = testLine;
3169
+ }
3170
+ });
3171
+ if (currentLine) {
3172
+ wrappedLines.push(currentLine);
3173
+ }
3249
3174
  });
3250
- const [p0, p1, p2, p3] = rect.points;
3251
- const startHeight = this.layer.altitudeToVector3(
3252
- startAltitude,
3253
- startAltitude
3254
- ).x;
3255
- const endHeight = this.layer.altitudeToVector3(endAltitude, endAltitude).x;
3256
- const anchorV = this.layer.coordinateToVector3(
3257
- new maptalks4.Coordinate(p0[0], p0[1]),
3258
- startHeight
3259
- );
3260
- const toLocal = (coord, alt) => {
3261
- const v = this.layer.coordinateToVector3(
3262
- new maptalks4.Coordinate(coord[0], coord[1]),
3263
- alt
3264
- );
3265
- return v.sub(anchorV);
3266
- };
3267
- const v0 = toLocal(p0, startHeight);
3268
- const v1 = toLocal(p1, endHeight);
3269
- const v2 = toLocal(p2, endHeight);
3270
- const v3 = toLocal(p3, startHeight);
3271
- const geom = new THREE.BufferGeometry();
3272
- const positions = new Float32Array([
3273
- v0.x,
3274
- v0.y,
3275
- v0.z,
3276
- v1.x,
3277
- v1.y,
3278
- v1.z,
3279
- v2.x,
3280
- v2.y,
3281
- v2.z,
3282
- v0.x,
3283
- v0.y,
3284
- v0.z,
3285
- v2.x,
3286
- v2.y,
3287
- v2.z,
3288
- v3.x,
3289
- v3.y,
3290
- v3.z
3291
- ]);
3292
- geom.setAttribute("position", new THREE.BufferAttribute(positions, 3));
3293
- const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1]);
3294
- geom.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
3295
- geom.computeVertexNormals();
3296
- const mat = new THREE.MeshLambertMaterial({
3297
- color: 11184810,
3298
- // top
3299
- emissive: 0,
3300
- side: THREE.DoubleSide
3301
- // visible from both sides (escalators are thin)
3175
+ const lines = wrappedLines.length ? wrappedLines : [""];
3176
+ const widest = Math.max(...lines.map((l) => ctx.measureText(l).width), 0);
3177
+ const finalWidth = (maxWidth ? Math.min(widest, maxWidth) : widest) + padding * 2;
3178
+ const finalHeight = lineHeight * lines.length + padding * 2;
3179
+ canvas.width = finalWidth;
3180
+ canvas.height = finalHeight;
3181
+ const ctx2 = canvas.getContext("2d");
3182
+ ctx2.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
3183
+ ctx2.textAlign = textAlign;
3184
+ if (background && background !== "transparent") {
3185
+ ctx2.fillStyle = background;
3186
+ ctx2.fillRect(0, 0, canvas.width, canvas.height);
3187
+ }
3188
+ lines.forEach((line, i) => {
3189
+ const y = padding + lineHeight * (i + 0.8);
3190
+ let x = padding;
3191
+ if (textAlign === "center") x = canvas.width / 2;
3192
+ if (textAlign === "right" || textAlign === "end")
3193
+ x = canvas.width - padding;
3194
+ if (strokeWidth > 0) {
3195
+ ctx2.lineWidth = strokeWidth;
3196
+ ctx2.lineJoin = "round";
3197
+ ctx2.miterLimit = 2;
3198
+ ctx2.strokeStyle = strokeColor;
3199
+ ctx2.strokeText(line, x, y);
3200
+ }
3201
+ ctx2.fillStyle = color;
3202
+ ctx2.fillText(line, x, y);
3302
3203
  });
3303
- this._createMesh(geom, mat);
3304
- this.getObject3d().position.copy(anchorV);
3204
+ const texture = new THREE.CanvasTexture(canvas);
3205
+ texture.needsUpdate = true;
3206
+ texture.minFilter = THREE.LinearFilter;
3207
+ return texture;
3208
+ }
3209
+ _updatePosition() {
3210
+ const options = this.getOptions();
3211
+ const layer = options.layer;
3212
+ if (!layer) return;
3213
+ const altitude = (options.altitude || 0) + this.#altitudeOffset;
3214
+ const z = layer.altitudeToVector3(altitude, altitude).x;
3215
+ const position = layer.coordinateToVector3(this._coordinate, z);
3216
+ set(this.properties, "default.position", position);
3217
+ this.getObject3d().position.copy(position);
3218
+ }
3219
+ _animation() {
3220
+ const layer = this.getLayer();
3221
+ if (!this.isAdd || !layer) return;
3222
+ if (this._visible === true) {
3223
+ const zoom = layer.map.getZoom();
3224
+ const object3d = this.getObject3d();
3225
+ const { opacity } = this.getOptions();
3226
+ let opacityValue;
3227
+ if (typeof opacity === "number") {
3228
+ opacityValue = opacity ?? 1;
3229
+ } else if (Array.isArray(opacity.stops)) {
3230
+ opacityValue = interpolateStops(opacity, zoom);
3231
+ } else {
3232
+ throw new Error(`Unknown opacity value ${opacity}`);
3233
+ }
3234
+ const visible = opacityValue > 0.5;
3235
+ object3d.visible = visible;
3236
+ }
3237
+ }
3238
+ setText(text) {
3239
+ const options = this.getOptions();
3240
+ options.text = text;
3241
+ const newSprite = this._createSprite();
3242
+ const group = this.getObject3d();
3243
+ group.children.forEach((child) => group.remove(child));
3244
+ group.add(newSprite);
3245
+ this._updatePosition();
3246
+ }
3247
+ setAltitude(altitude) {
3248
+ const bottomHeight = this.options.bottomHeight ?? 0;
3249
+ return super.setAltitude(altitude + bottomHeight + this.#altitudeOffset);
3305
3250
  }
3306
3251
  };
3307
3252
 
3308
- // src/IndoorMap/renderer/Element3DRenderer/utils/svg2material.ts
3309
- import { SpriteMaterial as SpriteMaterial4, TextureLoader as TextureLoader3 } from "three";
3310
- var svgToDataURL = (svgString, scaleFactor = 1) => {
3311
- const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
3312
- const url = URL.createObjectURL(svgBlob);
3313
- const img = new Image();
3314
- return new Promise((resolve, reject) => {
3315
- img.onload = function() {
3316
- const newWidth = img.width * scaleFactor;
3317
- const newHeight = img.height * scaleFactor;
3318
- const canvas = document.createElement("canvas");
3319
- canvas.width = newWidth;
3320
- canvas.height = newHeight;
3321
- const ctx = canvas.getContext("2d");
3322
- ctx.drawImage(img, 0, 0, newWidth, newHeight);
3323
- const pngDataUrl = canvas.toDataURL("image/png");
3324
- resolve(pngDataUrl);
3325
- };
3326
- img.onerror = function(error) {
3327
- reject(error);
3328
- };
3329
- img.src = url;
3330
- });
3331
- };
3332
- var createSVGPathFromMarkerSymbol2 = (style) => {
3333
- const {
3334
- markerWidth = 24,
3335
- markerDx = 0,
3336
- markerDy = 0,
3337
- // markerFill,
3338
- markerPath,
3339
- fill = "#000000"
3340
- } = style;
3341
- const scale2 = markerWidth / 24;
3342
- const strokeWidth = 2;
3343
- const halfStrokeWidth = 0.5 * strokeWidth;
3344
- if (Array.isArray(markerPath)) {
3345
- return markerPath.map(
3346
- ({ path, fill: fill2 }) => `<path d="${path}" style="transform:translate(${markerDx}px, ${markerDy}px) scale(${scale2})" fill="${fill2}" stroke="#ffffff" stroke-width="${strokeWidth}" />`
3347
- );
3348
- }
3349
- return `<path d="${markerPath}" style="transform:translate(${markerDx}px, ${markerDy}px) scale(${scale2})" fill="${fill}" />`;
3350
- };
3351
- var createSpriteMaterialByLabelSymbol2 = (labelSymbol) => {
3352
- const material = new SpriteMaterial4();
3353
- try {
3354
- const [base, icon] = labelSymbol ?? [{}, {}];
3355
- const { markerWidth: baseWidth = 24 } = base;
3356
- const { markerWidth: iconWidth = 24 } = icon;
3357
- const viewBoxDimension = Math.max(baseWidth, iconWidth);
3358
- const baseSVG = createSVGPathFromMarkerSymbol2(base);
3359
- const iconSVG = icon ? createSVGPathFromMarkerSymbol2(icon) : "";
3360
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${viewBoxDimension}" height="${viewBoxDimension}">${baseSVG}${iconSVG}</svg>`;
3361
- const textureLoader = new TextureLoader3();
3362
- const scaleFactor = 200 / 24;
3363
- svgToDataURL(svg, scaleFactor).then((png) => {
3364
- const texture = textureLoader.load(png, () => {
3365
- material.map = texture;
3366
- material.needsUpdate = true;
3367
- });
3368
- });
3369
- } catch (error) {
3370
- console.warn(`Error createSpriteMaterialByLabelSymbol: `, labelSymbol);
3371
- }
3372
- return material;
3373
- };
3374
-
3375
- // src/IndoorMap/renderer/Element3DRenderer.ts
3376
- var DEFAULT_POLYGON_OPTION = {
3377
- color: "#FFFFFF",
3378
- offset: 0,
3379
- altitude: 0
3253
+ // src/IndoorMap/renderer/3d/Element3DRenderer.ts
3254
+ var DEFAULT_POLYGON_OPTION = {
3255
+ color: "#FFFFFF",
3256
+ offset: 0,
3257
+ altitude: 0
3380
3258
  };
3381
3259
  var HEIGHT_METER = 4;
3260
+ var MULTIORDINAL_HEIGHT_METER = 9;
3382
3261
  var getGeometryOption = (feature2, options) => {
3383
3262
  try {
3384
- const option = options[feature2.feature_type];
3263
+ const option = options[feature2.feature_type] ?? element3DRendererOptions[feature2.feature_type];
3385
3264
  const category = feature2.properties.category;
3386
3265
  return (category && option.byCategory?.[category]) ?? option?.default ?? DEFAULT_POLYGON_OPTION;
3387
3266
  } catch (err) {
@@ -3392,34 +3271,20 @@ var Element3DRenderer = class extends EventTarget {
3392
3271
  options;
3393
3272
  map;
3394
3273
  threeLayer;
3395
- materialByColorMap;
3396
- materialByKey;
3274
+ dracoLoader;
3397
3275
  lineMaterial;
3276
+ materialByColorMap;
3277
+ markerRenderer;
3398
3278
  // Renderer is Ready
3399
3279
  isReady = false;
3400
- constructor(map, options) {
3280
+ constructor(map, options, layer) {
3401
3281
  super();
3402
3282
  this.options = options;
3403
3283
  this.map = map;
3404
- this.threeLayer = new ThreeLayer3("elements", {
3405
- forceRenderOnMoving: true,
3406
- forceRenderOnRotating: true
3407
- });
3408
- const _this = this;
3409
- this.threeLayer.prepareToDraw = function(gl, scene, camera) {
3410
- const ambientLight = new THREE2.AmbientLight(16777215, 0.3);
3411
- scene.add(ambientLight);
3412
- const dirColor = 16777215;
3413
- const dllight = new THREE2.DirectionalLight(dirColor, 0.8);
3414
- dllight.position.set(0, -10, 10).normalize();
3415
- scene.add(dllight);
3416
- const hemi = new THREE2.HemisphereLight(16777215, 4473924, 0.4);
3417
- scene.add(hemi);
3418
- this.isReady = true;
3419
- _this.dispatchEvent(new CustomEvent("threelayer:ready"));
3420
- };
3284
+ this.dracoLoader = new DRACOLoader();
3285
+ this.dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.7/");
3421
3286
  this.lineMaterial = new THREE2.LineBasicMaterial({ color: "#000" });
3422
- this.threeLayer.addTo(this.map);
3287
+ this.threeLayer = layer;
3423
3288
  this.render();
3424
3289
  }
3425
3290
  animation() {
@@ -3434,80 +3299,56 @@ var Element3DRenderer = class extends EventTarget {
3434
3299
  if (!this.materialByColorMap) this.materialByColorMap = /* @__PURE__ */ new Map();
3435
3300
  const existingMaterial = this.materialByColorMap.get(color);
3436
3301
  if (existingMaterial) return existingMaterial;
3437
- const created = new MeshLambertMaterial3({ color, transparent: true });
3302
+ const created = new THREE2.MeshLambertMaterial({ color, transparent: true });
3438
3303
  created.toneMapped = false;
3439
3304
  this.materialByColorMap.set(color, created);
3440
3305
  return created;
3441
3306
  }
3442
- getOrCreateIconMaterial(key) {
3443
- if (!this.materialByKey) this.materialByKey = /* @__PURE__ */ new Map();
3444
- const existingMaterial = this.materialByKey.get(key);
3445
- if (existingMaterial) return existingMaterial;
3446
- const baseSymbol = {
3447
- markerType: "path",
3448
- markerPath: [
3449
- {
3450
- path: "M20.775 1.2H1.225V20.35H8.215L11.3 22.8L14.385 20.35H20.775V1.2Z",
3451
- fill: "#ff0000"
3452
- }
3453
- ],
3454
- markerPathWidth: 24,
3455
- markerPathHeight: 24
3456
- };
3457
- const markerSymbol = {
3458
- markerType: "path",
3459
- markerPath: [],
3460
- // TODO: Get Path by featureType.category
3461
- // markerPath: [{ fill: "#FFFFFF", path: "M 19 3 H 5 c -1.1 0 -2 0.9 -2 2 v 14 c 0 1.1 0.9 2 2 2 h 14 c 1.1 0 2 -0.9 2 -2 V 5 c 0 -1.1 -0.9 -2 -2 -2 Z m -2 6 h -1.7 l -5 9 H 7 c -0.83 0 -1.5 -0.67 -1.5 -1.5 S 6.17 15 7 15 h 1.7 l 5 -9 H 17 c 0.83 0 1.5 0.67 1.5 1.5 S 17.83 9 17 9 Z" }],
3462
- markerPathWidth: 24,
3463
- markerPathHeight: 24,
3464
- markerWidth: 24,
3465
- markerHeight: 24,
3466
- markerDy: 1.5,
3467
- markerDx: 1.5
3468
- };
3469
- const created = createSpriteMaterialByLabelSymbol2([
3470
- baseSymbol,
3471
- markerSymbol
3472
- ]);
3473
- this.materialByKey.set(key, created);
3474
- return created;
3475
- }
3476
3307
  createGeometry = (feature2) => {
3477
- const options = getGeometryOption(feature2, this.options);
3478
- const offset = options?.offset ?? 0;
3308
+ const {
3309
+ offset = 0,
3310
+ height: heightOptions,
3311
+ bottomHeight: bottomHeightOptions,
3312
+ color: colorOptions,
3313
+ ...options
3314
+ } = getGeometryOption(feature2, this.options);
3315
+ const _this = this;
3479
3316
  const createPolygon = (geometry, feature3) => {
3480
3317
  const [outerRing, ...innerRings] = geometry.coordinates;
3481
3318
  const offsetFeature = offset !== 0 ? turfBuffer2(geometry, offset, { units: "meters" }) : feature3;
3482
- const color = options.color ?? feature3.properties.style.polygonFill ?? "#ffffff";
3319
+ const color = feature3.properties.style.polygonFill ?? colorOptions ?? "#ffffff";
3483
3320
  if (color === "transparent") return;
3484
3321
  const material = this.getOrCreateMaterialByColor(color);
3485
3322
  const altitude = feature3.properties.ordinal * HEIGHT_METER;
3486
- const bottomHeight = options.bottomHeight ?? 0;
3487
- const polygon = this.threeLayer.toExtrudePolygon(
3323
+ const height = feature3.properties.height ?? heightOptions ?? HEIGHT_METER;
3324
+ const bottomHeight = feature3.properties.bottomHeight ?? bottomHeightOptions ?? 0;
3325
+ const extrudedPolygon = this.threeLayer.toExtrudePolygon(
3488
3326
  offsetFeature,
3489
- { asynchronous: true, ...options, altitude },
3327
+ { asynchronous: true, ...options, height, bottomHeight, altitude },
3490
3328
  material
3491
3329
  );
3330
+ extrudedPolygon.on("click", (e) => {
3331
+ console.log(e.target.options.polygon.id);
3332
+ });
3492
3333
  const topLineStrings = [
3493
- new maptalks5.LineString(outerRing),
3494
- ...innerRings.map((innerRing) => new maptalks5.LineString(innerRing))
3334
+ new maptalks4.LineString(outerRing),
3335
+ ...innerRings.map((innerRing) => new maptalks4.LineString(innerRing))
3495
3336
  ];
3496
3337
  const topLines = this.threeLayer.toLines(
3497
3338
  topLineStrings,
3498
- { altitude: altitude + options.height + 1e-3, bottomHeight, interactive: false },
3339
+ { altitude, bottomHeight: bottomHeight + height + 1e-3, interactive: false },
3499
3340
  this.lineMaterial
3500
3341
  );
3501
3342
  const bottomLineStrings = [
3502
- new maptalks5.LineString(outerRing),
3503
- ...innerRings.map((innerRing) => new maptalks5.LineString(innerRing))
3343
+ new maptalks4.LineString(outerRing),
3344
+ ...innerRings.map((innerRing) => new maptalks4.LineString(innerRing))
3504
3345
  ];
3505
3346
  const bottomLines = this.threeLayer.toLines(
3506
3347
  bottomLineStrings,
3507
3348
  { altitude, bottomHeight, interactive: false },
3508
3349
  this.lineMaterial
3509
3350
  );
3510
- return [polygon, topLines, bottomLines];
3351
+ return [extrudedPolygon, topLines, bottomLines];
3511
3352
  };
3512
3353
  try {
3513
3354
  switch (feature2.geometry.type) {
@@ -3530,49 +3371,111 @@ var Element3DRenderer = class extends EventTarget {
3530
3371
  console.log(`error createGeometry`, { feature: feature2, options });
3531
3372
  }
3532
3373
  };
3533
- createMarker = (coordinates, ordinal, label) => {
3534
- const options = {
3535
- scale: 0.05,
3536
- altitude: ordinal * HEIGHT_METER,
3537
- // highlight: highlightOptions,
3538
- interactive: true
3539
- };
3540
- const material = this.getOrCreateIconMaterial(
3541
- "amenity.escalator"
3542
- );
3543
- const marker = new SpriteMarker(
3544
- coordinates,
3545
- options,
3546
- material,
3547
- this.threeLayer,
3548
- {}
3374
+ async createEscalator(f, coordinate, options) {
3375
+ const { direction: dir, angle } = options;
3376
+ const model = await this.loadModel3d({
3377
+ url: "https://dashboard.situm.com/uploads/3dmodels/demoaccount/new_escalator.glb",
3378
+ properties: {
3379
+ rotation: {
3380
+ x: 0.5 * Math.PI,
3381
+ // Rotate the model up (new_escalator.glb)
3382
+ y: 0,
3383
+ z: 0
3384
+ },
3385
+ position: { x: 0, y: 0, z: 0 },
3386
+ scale: 0.01
3387
+ }
3388
+ });
3389
+ model.rotation.y += dir === "up" ? Math.PI + angle : angle;
3390
+ const box = new THREE2.Box3().setFromObject(model);
3391
+ const pivotPoint = dir === "up" ? new THREE2.Vector3(0, 0, 0) : new THREE2.Vector3(
3392
+ 1 * (box.min.x + box.max.x),
3393
+ 1 * (box.min.y + box.max.y),
3394
+ 0.6 * box.max.z
3549
3395
  );
3550
- this.threeLayer.addMesh([marker]);
3551
- return marker;
3552
- };
3396
+ const pivot = new THREE2.Group();
3397
+ pivot.add(model);
3398
+ model.position.sub(pivotPoint);
3399
+ model.updateMatrixWorld(true);
3400
+ const altitude = f.properties.ordinal * HEIGHT_METER;
3401
+ const baseObjectModel = this.threeLayer.toModel(pivot, {
3402
+ coordinate,
3403
+ altitude
3404
+ });
3405
+ this.threeLayer.addMesh(baseObjectModel);
3406
+ return baseObjectModel;
3407
+ }
3408
+ async createTree(coordinate, ordinal) {
3409
+ const model = await this.loadModel3d({
3410
+ url: "https://dashboard.situm.com/uploads/3dmodels/demoaccount/arbol.glb",
3411
+ properties: {
3412
+ rotation: {
3413
+ x: 0.5 * Math.PI,
3414
+ // Rotate the model up (new_escalator.glb)
3415
+ y: 0,
3416
+ z: 0
3417
+ },
3418
+ position: { x: 0, y: 0, z: 0 },
3419
+ scale: 0.01
3420
+ }
3421
+ });
3422
+ const altitude = ordinal * HEIGHT_METER;
3423
+ const baseObjectModel = this.threeLayer.toModel(model, {
3424
+ coordinate,
3425
+ altitude
3426
+ });
3427
+ this.threeLayer.addMesh(baseObjectModel);
3428
+ return baseObjectModel;
3429
+ }
3553
3430
  createElement(f) {
3554
3431
  switch (f.feature_type) {
3555
- case "unit": {
3556
- switch (f.properties.category) {
3557
- case "escalator": {
3558
- const mesh = new Escalator(
3559
- f,
3560
- {
3561
- startAltitude: 0,
3562
- endAltitude: 0.06
3563
- },
3564
- this.threeLayer
3565
- );
3566
- this.threeLayer.addMesh([mesh]);
3567
- return [mesh];
3568
- }
3569
- }
3570
- break;
3571
- }
3572
3432
  default:
3573
3433
  return null;
3574
3434
  }
3575
3435
  }
3436
+ showElements(elements, ordinalDiff = 0) {
3437
+ elements.forEach((element) => {
3438
+ element.setAltitude(ordinalDiff * MULTIORDINAL_HEIGHT_METER);
3439
+ element.show();
3440
+ });
3441
+ }
3442
+ hideElements(elements, ordinalDiff = 0) {
3443
+ elements.forEach((element) => {
3444
+ try {
3445
+ element.hide();
3446
+ } catch (err) {
3447
+ console.log(`cannot hide`, err, element);
3448
+ }
3449
+ });
3450
+ }
3451
+ async loadModel3d(model3d) {
3452
+ const loader = new GLTFLoader2();
3453
+ loader.setDRACOLoader(this.dracoLoader);
3454
+ const { url, properties: modelProperties } = model3d;
3455
+ const gltf = await loader.loadAsync(url);
3456
+ const model = gltf.scene;
3457
+ model.rotation.x = modelProperties.rotation.x;
3458
+ model.rotation.y = modelProperties.rotation.y;
3459
+ model.position.x = modelProperties.position.x;
3460
+ model.position.y = modelProperties.position.y;
3461
+ model.position.z = modelProperties.position.z;
3462
+ const scale2 = modelProperties.scale;
3463
+ model.scale.set(scale2, scale2, scale2);
3464
+ return model;
3465
+ }
3466
+ createMarker = (coordinates, ordinal, text) => {
3467
+ const options = {
3468
+ // scale: 0.05,
3469
+ // altitude: ordinal * HEIGHT_METER,
3470
+ text
3471
+ // interactive: true,
3472
+ };
3473
+ const marker = new TextSpriteMarker(coordinates, options, this.threeLayer);
3474
+ this.threeLayer.addMesh([marker]);
3475
+ return marker;
3476
+ };
3477
+ removeMarker = () => {
3478
+ };
3576
3479
  render() {
3577
3480
  this.threeLayer._needsUpdate = !this.threeLayer._needsUpdate;
3578
3481
  if (this.threeLayer._needsUpdate) {
@@ -3582,9 +3485,34 @@ var Element3DRenderer = class extends EventTarget {
3582
3485
  }
3583
3486
  };
3584
3487
 
3585
- // src/IndoorMap/renderer/Element2DRenderer.ts
3586
- import * as maptalks6 from "maptalks";
3587
- var OCCUPANT_TEXT_MARKER_CLASSNAME2 = "mtk-occupant-text-marker";
3488
+ // src/IndoorMap/renderer/2d/Element2DRenderer.ts
3489
+ import * as maptalks5 from "maptalks";
3490
+
3491
+ // src/IndoorMap/renderer/2d/element2DRendererOptions.ts
3492
+ var element2DRendererOptions = {
3493
+ unit: {
3494
+ default: { symbol: { polygonFill: "#cccccc" } },
3495
+ byCategory: {
3496
+ room: { symbol: { polygonFill: "#fff" } },
3497
+ walkway: { symbol: { polygonFill: "#efefef", lineColor: "#dadada", lineWidth: 2 } },
3498
+ terrace: { symbol: { polygonFill: "#efefef" } },
3499
+ unenclosedarea: { symbol: { polygonFill: "#fff" } },
3500
+ nonpublic: { symbol: { polygonFill: "#999999" } }
3501
+ }
3502
+ },
3503
+ kiosk: {
3504
+ default: {}
3505
+ },
3506
+ fixture: {
3507
+ default: { symbol: { polygonFill: "#ffffff" } },
3508
+ byCategory: {
3509
+ water: { symbol: { polygonFill: "#ACD7EC" } },
3510
+ vegetation: { symbol: { polygonFill: "#91C499" } }
3511
+ }
3512
+ }
3513
+ };
3514
+
3515
+ // src/IndoorMap/renderer/2d/Element2DRenderer.ts
3588
3516
  var DEFAULT_POLYGON_OPTION2 = {
3589
3517
  zIndex: 0,
3590
3518
  symbol: {
@@ -3594,6 +3522,7 @@ var DEFAULT_POLYGON_OPTION2 = {
3594
3522
  lineWidth: 2
3595
3523
  }
3596
3524
  };
3525
+ var MULTIORDINAL_HEIGHT_METER2 = 10;
3597
3526
  var getGeometryProperties = (feature2) => ({
3598
3527
  // Core
3599
3528
  type: "Feature",
@@ -3603,11 +3532,11 @@ var getGeometryProperties = (feature2) => ({
3603
3532
  // Extra
3604
3533
  feature_type: feature2.feature_type,
3605
3534
  category: feature2.properties.category,
3606
- name: feature2.properties.name.en
3535
+ name: feature2.properties.name?.en
3607
3536
  });
3608
3537
  var getGeometryOption2 = (feature2, options) => {
3609
3538
  try {
3610
- const option = options[feature2.feature_type];
3539
+ const option = options[feature2.feature_type] ?? element2DRendererOptions[feature2.feature_type];
3611
3540
  const category = feature2.properties.category;
3612
3541
  return (category && option.byCategory?.[category]) ?? option?.default ?? DEFAULT_POLYGON_OPTION2;
3613
3542
  } catch (err) {
@@ -3624,18 +3553,17 @@ var Element2DRenderer = class extends EventTarget {
3624
3553
  super();
3625
3554
  this.options = options;
3626
3555
  this.map = map;
3627
- this.elementLayer = new maptalks6.VectorLayer("elements");
3556
+ this.elementLayer = new maptalks5.VectorLayer("elements");
3628
3557
  this.elementLayer.addTo(this.map);
3629
- this.markerLayer = new maptalks6.VectorLayer("markers");
3558
+ this.markerLayer = new maptalks5.VectorLayer("markers");
3630
3559
  this.markerLayer.addTo(this.map);
3631
3560
  this.isReady = true;
3632
- this.dispatchEvent(new CustomEvent("renderer:ready"));
3633
3561
  }
3634
3562
  createGeometry = (imdfFeature) => {
3635
3563
  const feature2 = getGeometryProperties(imdfFeature);
3636
3564
  const { symbol, ...options } = getGeometryOption2(imdfFeature, this.options);
3637
3565
  const altitude = feature2.properties.ordinal * 10;
3638
- const geometry = maptalks6.Geometry.fromJSON({
3566
+ const geometry = maptalks5.Geometry.fromJSON({
3639
3567
  feature: feature2,
3640
3568
  symbol,
3641
3569
  options: {
@@ -3646,42 +3574,235 @@ var Element2DRenderer = class extends EventTarget {
3646
3574
  });
3647
3575
  if (Array.isArray(geometry)) {
3648
3576
  geometry.forEach((g) => g.addTo(this.elementLayer));
3649
- return new maptalks6.GeometryCollection(geometry);
3577
+ return new maptalks5.GeometryCollection(geometry);
3650
3578
  } else {
3651
3579
  geometry.addTo(this.elementLayer);
3652
3580
  return geometry;
3653
3581
  }
3654
3582
  };
3655
- createMarker = (coordinates, ordinal, label) => {
3656
- const createStyledUIMarkerElement2 = ({ style: style2, textContent, className }) => {
3657
- const element = document.createElement("div");
3658
- for (const key in style2) {
3659
- element.style[key] = style2[key];
3660
- }
3661
- element.className = className;
3662
- element.textContent = textContent;
3663
- return element.outerHTML;
3664
- };
3665
- const style = {};
3583
+ async createEscalator(f, coordinates) {
3584
+ return Promise.resolve(null);
3585
+ }
3586
+ async createTree(f, coordinates) {
3587
+ return Promise.resolve(null);
3588
+ }
3589
+ createElement = (imdfFeature) => {
3590
+ switch (imdfFeature.feature_type) {
3591
+ default:
3592
+ return null;
3593
+ }
3594
+ };
3595
+ showElements(elements, ordinalDiff = 0) {
3596
+ elements.forEach((element) => {
3597
+ element.setAltitude(ordinalDiff * MULTIORDINAL_HEIGHT_METER2);
3598
+ element.show();
3599
+ });
3600
+ }
3601
+ hideElements(elements, ordinalDiff = 0) {
3602
+ elements.forEach((element) => {
3603
+ element.hide();
3604
+ });
3605
+ }
3606
+ };
3607
+
3608
+ // src/IndoorMap/renderer/2d/Marker2DRenderer.ts
3609
+ import * as maptalks6 from "maptalks";
3610
+ var Marker2DRenderer = class extends EventTarget {
3611
+ isReady = false;
3612
+ map;
3613
+ markerLayer;
3614
+ constructor(map) {
3615
+ super();
3616
+ }
3617
+ createMarker = (coordinates, ordinal, content) => {
3666
3618
  const marker = new maptalks6.ui.UIMarker(coordinates, {
3667
- content: createStyledUIMarkerElement2({
3668
- style,
3669
- textContent: label,
3670
- className: OCCUPANT_TEXT_MARKER_CLASSNAME2
3671
- }),
3619
+ content,
3672
3620
  collision: true,
3673
- collisionFadeIn: true
3674
- // altitude: getAltitude(f.properties) + markerHeight,
3621
+ collisionFadeIn: true,
3622
+ altitude: 0
3675
3623
  });
3676
3624
  marker.addTo(this.map);
3677
3625
  return marker;
3678
3626
  };
3679
- createElement = (imdfFeature) => {
3680
- switch (imdfFeature.feature_type) {
3681
- default:
3682
- return null;
3683
- }
3627
+ removeMarker = (marker) => {
3628
+ marker.remove();
3629
+ };
3630
+ showMarkers(elements, ordinalDiff = 0) {
3631
+ }
3632
+ hideMarkers(elements, ordinalDiff = 0) {
3633
+ }
3634
+ };
3635
+
3636
+ // src/IndoorMap/renderer/3d/Marker3DRenderer.ts
3637
+ import * as maptalks7 from "maptalks";
3638
+
3639
+ // src/IndoorMap/renderer/utils/svg2material.ts
3640
+ import { SpriteMaterial as SpriteMaterial5, TextureLoader as TextureLoader3 } from "three";
3641
+ var svgToDataURL = (svgString, scaleFactor = 1) => {
3642
+ const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
3643
+ const url = URL.createObjectURL(svgBlob);
3644
+ const img = new Image();
3645
+ return new Promise((resolve, reject) => {
3646
+ img.onload = function() {
3647
+ const newWidth = img.width * scaleFactor;
3648
+ const newHeight = img.height * scaleFactor;
3649
+ const canvas = document.createElement("canvas");
3650
+ canvas.width = newWidth;
3651
+ canvas.height = newHeight;
3652
+ const ctx = canvas.getContext("2d");
3653
+ ctx.drawImage(img, 0, 0, newWidth, newHeight);
3654
+ const pngDataUrl = canvas.toDataURL("image/png");
3655
+ resolve(pngDataUrl);
3656
+ };
3657
+ img.onerror = function(error) {
3658
+ reject(error);
3659
+ };
3660
+ img.src = url;
3661
+ });
3662
+ };
3663
+ var createSVGPathFromMarkerSymbol2 = (style) => {
3664
+ const {
3665
+ markerWidth = 24,
3666
+ markerDx = 0,
3667
+ markerDy = 0,
3668
+ // markerFill,
3669
+ markerPath,
3670
+ fill = "#000000"
3671
+ } = style;
3672
+ const scale2 = markerWidth / 24;
3673
+ const strokeWidth = 2;
3674
+ const halfStrokeWidth = 0.5 * strokeWidth;
3675
+ if (Array.isArray(markerPath)) {
3676
+ return markerPath.map(
3677
+ ({ path, fill: fill2 }) => `<path d="${path}" style="transform:translate(${markerDx}px, ${markerDy}px) scale(${scale2})" fill="${fill2}" stroke="#ffffff" stroke-width="${strokeWidth}" />`
3678
+ );
3679
+ }
3680
+ return `<path d="${markerPath}" style="transform:translate(${markerDx}px, ${markerDy}px) scale(${scale2})" fill="${fill}" />`;
3681
+ };
3682
+ var createSpriteMaterialByLabelSymbol2 = (labelSymbol) => {
3683
+ const material = new SpriteMaterial5();
3684
+ try {
3685
+ const [base, icon] = labelSymbol ?? [{}, {}];
3686
+ const { markerWidth: baseWidth = 24 } = base;
3687
+ const { markerWidth: iconWidth = 24 } = icon;
3688
+ const viewBoxDimension = Math.max(baseWidth, iconWidth);
3689
+ const baseSVG = createSVGPathFromMarkerSymbol2(base);
3690
+ const iconSVG = icon ? createSVGPathFromMarkerSymbol2(icon) : "";
3691
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${viewBoxDimension}" height="${viewBoxDimension}">${baseSVG}${iconSVG}</svg>`;
3692
+ const textureLoader = new TextureLoader3();
3693
+ const scaleFactor = 200 / 24;
3694
+ svgToDataURL(svg, scaleFactor).then((png) => {
3695
+ const texture = textureLoader.load(png, () => {
3696
+ material.map = texture;
3697
+ material.needsUpdate = true;
3698
+ });
3699
+ });
3700
+ } catch (error) {
3701
+ console.warn(`Error createSpriteMaterialByLabelSymbol: `, labelSymbol);
3702
+ }
3703
+ return material;
3704
+ };
3705
+
3706
+ // src/IndoorMap/renderer/3d/Marker3DRenderer.ts
3707
+ var HEIGHT_METER2 = 4;
3708
+ var MULTIORDINAL_HEIGHT_METER3 = 9;
3709
+ var Marker3DRenderer = class extends EventTarget {
3710
+ isReady = false;
3711
+ threeLayer;
3712
+ map;
3713
+ materialByKey;
3714
+ constructor(map, options, layer) {
3715
+ super();
3716
+ this.map = map;
3717
+ this.threeLayer = layer;
3718
+ }
3719
+ createTextMarker = (position, ordinal, label, options) => {
3720
+ const combinedOptions = {
3721
+ altitude: ordinal * HEIGHT_METER2,
3722
+ text: label,
3723
+ ...options ?? {}
3724
+ };
3725
+ const [lng, lat] = position;
3726
+ const marker = new TextSpriteMarker(new maptalks7.Coordinate(lng, lat), combinedOptions, this.threeLayer);
3727
+ this.threeLayer.addMesh([marker]);
3728
+ return marker;
3729
+ };
3730
+ createImageMarker = () => {
3731
+ };
3732
+ createMarker = (coordinates, ordinal, label, options) => {
3733
+ return this.createTextMarker(coordinates, ordinal, label, options);
3684
3734
  };
3735
+ removeMarker = (marker) => {
3736
+ marker.remove();
3737
+ };
3738
+ showMarkers(elements, ordinalDiff = 0) {
3739
+ elements.forEach((element) => {
3740
+ element.setAltitude(ordinalDiff * MULTIORDINAL_HEIGHT_METER3);
3741
+ element.show();
3742
+ });
3743
+ }
3744
+ hideMarkers(elements, ordinalDiff = 0) {
3745
+ elements.forEach((element) => {
3746
+ try {
3747
+ element.hide();
3748
+ } catch (err) {
3749
+ console.log(`cannot hide`, err, element);
3750
+ }
3751
+ });
3752
+ }
3753
+ /** Marker */
3754
+ getOrCreateIconMaterial(key) {
3755
+ if (!this.materialByKey) this.materialByKey = /* @__PURE__ */ new Map();
3756
+ const existingMaterial = this.materialByKey.get(key);
3757
+ if (existingMaterial) return existingMaterial;
3758
+ const baseSymbol = {
3759
+ markerType: "path",
3760
+ markerPath: [
3761
+ {
3762
+ path: "M20.775 1.2H1.225V20.35H8.215L11.3 22.8L14.385 20.35H20.775V1.2Z",
3763
+ fill: "#ff0000"
3764
+ }
3765
+ ],
3766
+ markerPathWidth: 24,
3767
+ markerPathHeight: 24
3768
+ };
3769
+ const markerSymbol = {
3770
+ markerType: "path",
3771
+ markerPath: [],
3772
+ // TODO: Get Path by featureType.category
3773
+ // markerPath: [{ fill: "#FFFFFF", path: "M 19 3 H 5 c -1.1 0 -2 0.9 -2 2 v 14 c 0 1.1 0.9 2 2 2 h 14 c 1.1 0 2 -0.9 2 -2 V 5 c 0 -1.1 -0.9 -2 -2 -2 Z m -2 6 h -1.7 l -5 9 H 7 c -0.83 0 -1.5 -0.67 -1.5 -1.5 S 6.17 15 7 15 h 1.7 l 5 -9 H 17 c 0.83 0 1.5 0.67 1.5 1.5 S 17.83 9 17 9 Z" }],
3774
+ markerPathWidth: 24,
3775
+ markerPathHeight: 24,
3776
+ markerWidth: 24,
3777
+ markerHeight: 24,
3778
+ markerDy: 1.5,
3779
+ markerDx: 1.5
3780
+ };
3781
+ const created = createSpriteMaterialByLabelSymbol2([
3782
+ baseSymbol,
3783
+ markerSymbol
3784
+ ]);
3785
+ this.materialByKey.set(key, created);
3786
+ return created;
3787
+ }
3788
+ };
3789
+
3790
+ // src/IndoorMap/renderer/utils/angleBetweenLineString.ts
3791
+ var getLineCenter = (line) => {
3792
+ let x = 0, y = 0;
3793
+ for (const [lx, ly] of line) {
3794
+ x += lx;
3795
+ y += ly;
3796
+ }
3797
+ const len = line.length;
3798
+ return [x / len, y / len];
3799
+ };
3800
+ var angleBetweenLineStrings = (line1, line2) => {
3801
+ const [x1, y1] = getLineCenter(line1);
3802
+ const [x2, y2] = getLineCenter(line2);
3803
+ const dx = x2 - x1;
3804
+ const dy = y2 - y1;
3805
+ return Math.atan2(dy, dx);
3685
3806
  };
3686
3807
 
3687
3808
  // src/IndoorMap/renderer/RendererManager.ts
@@ -3692,25 +3813,48 @@ var RendererManager = class extends EventTarget {
3692
3813
  #dataClient;
3693
3814
  /** Elements: Responsible for converting feature info elements and add to map */
3694
3815
  elementRenderer;
3816
+ markerRenderer;
3695
3817
  elementsMap;
3696
3818
  elementsByOrdinal;
3697
- constructor(map, options) {
3819
+ currentOrdinals;
3820
+ markersMap;
3821
+ markersByOrdinal;
3822
+ constructor(map, dataClient, options) {
3698
3823
  super();
3699
3824
  this.map = map;
3700
3825
  this.options = options;
3701
3826
  this.elementsMap = /* @__PURE__ */ new Map();
3702
3827
  this.elementsByOrdinal = /* @__PURE__ */ new Map();
3703
- this.elementRenderer = options.type === "2D" ? new Element2DRenderer(map, options.elements) : new Element3DRenderer(map, options.elements);
3704
- }
3705
- set dataClient(value) {
3706
- this.#dataClient = value;
3707
- if (this.elementRenderer.isReady) {
3708
- this.#createElements();
3709
- } else {
3710
- this.elementRenderer.addEventListener("threelayer:ready", (e) => {
3711
- this.dispatchEvent(new CustomEvent("renderermanager:ready"));
3712
- this.#createElements();
3828
+ this.markersMap = /* @__PURE__ */ new Map();
3829
+ this.markersByOrdinal = /* @__PURE__ */ new Map();
3830
+ this.#dataClient = dataClient;
3831
+ if (options.type === "3D") {
3832
+ const threeLayer = new ThreeLayer3("elements", {
3833
+ forceRenderOnMoving: true,
3834
+ forceRenderOnRotating: true
3713
3835
  });
3836
+ const _this = this;
3837
+ threeLayer.prepareToDraw = function(gl, scene, camera) {
3838
+ const ambientLight = new THREE3.AmbientLight(16777215, 0.3);
3839
+ scene.add(ambientLight);
3840
+ const dirColor = 16777215;
3841
+ const dllight = new THREE3.DirectionalLight(dirColor, 0.8);
3842
+ dllight.position.set(0, -10, 10).normalize();
3843
+ scene.add(dllight);
3844
+ const hemi = new THREE3.HemisphereLight(16777215, 4473924, 0.4);
3845
+ scene.add(hemi);
3846
+ _this.elementRenderer = new Element3DRenderer(map, options.elements, threeLayer);
3847
+ _this.markerRenderer = new Marker3DRenderer(map, {}, threeLayer);
3848
+ if (typeof options.onRendererReady === "function") {
3849
+ options.onRendererReady();
3850
+ }
3851
+ _this.#createElements();
3852
+ };
3853
+ threeLayer.addTo(this.map);
3854
+ } else {
3855
+ this.elementRenderer = new Element2DRenderer(map, options.elements);
3856
+ this.markerRenderer = new Marker2DRenderer(map);
3857
+ this.#createElements();
3714
3858
  }
3715
3859
  }
3716
3860
  getElementsByOrdinal = (ordinal) => {
@@ -3718,19 +3862,46 @@ var RendererManager = class extends EventTarget {
3718
3862
  if (!exist) this.elementsByOrdinal.set(ordinal, []);
3719
3863
  return this.elementsByOrdinal.get(ordinal);
3720
3864
  };
3865
+ getMarkersByOrdinal = (ordinal) => {
3866
+ const exist = this.markersByOrdinal.get(ordinal);
3867
+ if (!exist) this.markersByOrdinal.set(ordinal, []);
3868
+ return this.markersByOrdinal.get(ordinal);
3869
+ };
3870
+ addElementsToManager = (id, elements, ordinal) => {
3871
+ this.elementsMap.set(id, elements);
3872
+ elements.forEach((el) => {
3873
+ this.getElementsByOrdinal(ordinal).push(el);
3874
+ });
3875
+ const inOrdinal = Array.isArray(this.currentOrdinals) ? this.currentOrdinals.includes(ordinal) : ordinal === this.currentOrdinals;
3876
+ if (inOrdinal) {
3877
+ this.elementRenderer.showElements(elements, ordinal);
3878
+ } else {
3879
+ this.elementRenderer.hideElements(elements, ordinal);
3880
+ }
3881
+ };
3882
+ addMarkersToManager = (id, markers, ordinal) => {
3883
+ this.markersMap.set(id, markers);
3884
+ markers.forEach((el) => {
3885
+ this.getMarkersByOrdinal(ordinal).push(el);
3886
+ });
3887
+ const inOrdinal = Array.isArray(this.currentOrdinals) ? this.currentOrdinals.includes(ordinal) : ordinal === this.currentOrdinals;
3888
+ if (inOrdinal) {
3889
+ this.markerRenderer.showMarkers(markers, ordinal);
3890
+ } else {
3891
+ this.markerRenderer.hideMarkers(markers, ordinal);
3892
+ }
3893
+ };
3721
3894
  async #createElements() {
3722
3895
  const levels = await this.#dataClient.filterByType("level", {
3723
3896
  populate: true
3724
3897
  });
3898
+ const relationships = await this.#dataClient.filterByType("relationship");
3725
3899
  const fixtures = await this.#dataClient.filterByType("fixture", { populate: true });
3726
3900
  fixtures.forEach((fixture) => {
3727
3901
  const element = this.elementRenderer.createGeometry(fixture);
3728
3902
  if (element) {
3729
3903
  const _elements = Array.isArray(element) ? element : [element];
3730
- _elements.forEach((el) => {
3731
- this.elementsMap.set(fixture.id, el);
3732
- this.getElementsByOrdinal(fixture.properties.level.properties.ordinal).push(el);
3733
- });
3904
+ this.addElementsToManager(fixture.id, _elements, fixture.properties.level.properties.ordinal);
3734
3905
  }
3735
3906
  });
3736
3907
  const units = await this.#dataClient.filterByType("unit", {
@@ -3742,10 +3913,7 @@ var RendererManager = class extends EventTarget {
3742
3913
  const element = this.elementRenderer.createGeometry(unit);
3743
3914
  if (element) {
3744
3915
  const _elements = Array.isArray(element) ? element : [element];
3745
- _elements.forEach((el) => {
3746
- this.elementsMap.set(unit.id, el);
3747
- this.getElementsByOrdinal(unit.properties.level.properties.ordinal).push(el);
3748
- });
3916
+ this.addElementsToManager(unit.id, _elements, unit.properties.level.properties.ordinal);
3749
3917
  }
3750
3918
  });
3751
3919
  const kiosks = await this.#dataClient.filterByType("kiosk", {
@@ -3755,32 +3923,85 @@ var RendererManager = class extends EventTarget {
3755
3923
  const element = this.elementRenderer.createGeometry(kiosk);
3756
3924
  if (element) {
3757
3925
  const _elements = Array.isArray(element) ? element : [element];
3758
- _elements.forEach((el) => {
3759
- this.elementsMap.set(kiosk.id, el);
3760
- this.getElementsByOrdinal(kiosk.properties.level.properties.ordinal).push(el);
3761
- });
3926
+ this.addElementsToManager(kiosk.id, _elements, kiosk.properties.level.properties.ordinal);
3762
3927
  }
3763
3928
  });
3929
+ const escalators = units.filter((u) => u.properties.category === "escalator");
3930
+ for (const escalator of escalators) {
3931
+ try {
3932
+ const escalatorRelationships = relationships.filter((r) => (r.properties?.intermediary || []).some((inter) => inter.id === escalator.id));
3933
+ if (escalatorRelationships.length === 0) {
3934
+ throw new Error("Cannot find escalator relationship");
3935
+ }
3936
+ if (escalatorRelationships.length > 1) {
3937
+ throw new Error("Found more than one relationship");
3938
+ }
3939
+ const thisOrdinal = escalator.properties.ordinal;
3940
+ const relationship = escalatorRelationships[0];
3941
+ const bothOpeningIds = [relationship.properties.origin.id, relationship.properties.destination.id];
3942
+ const bothOpenings = await Promise.all(
3943
+ bothOpeningIds.map((id) => this.#dataClient.findById("opening", id, { populate: true }))
3944
+ );
3945
+ const thisLevelOpening = bothOpenings.find((opening) => opening.properties.ordinal === thisOrdinal);
3946
+ const thatLevelOpening = bothOpenings.find((opening) => opening.properties.ordinal !== thisOrdinal);
3947
+ const angle = angleBetweenLineStrings(thisLevelOpening.geometry.coordinates, thatLevelOpening.geometry.coordinates);
3948
+ const direction = thisOrdinal < thatLevelOpening.properties.ordinal ? "up" : "down";
3949
+ const escalatorEntryPoint = turfCenter2(thisLevelOpening).geometry.coordinates;
3950
+ const element = await this.elementRenderer.createEscalator(escalator, escalatorEntryPoint, { direction, angle });
3951
+ if (element) {
3952
+ const _elements = Array.isArray(element) ? element : [element];
3953
+ this.addElementsToManager(escalator.id, _elements, escalator.properties.ordinal);
3954
+ }
3955
+ } catch (err) {
3956
+ console.log(`cannot create escalator`, err);
3957
+ }
3958
+ }
3959
+ this.changeLevelByOrdinal(this.currentOrdinals);
3764
3960
  this.dispatchEvent(new CustomEvent("renderermanager:elements_created"));
3765
3961
  }
3766
3962
  changeLevelByOrdinal(targetOrdinal) {
3767
- for (const [ordinal, elements] of this.elementsByOrdinal) {
3768
- const inOrdinal = targetOrdinal === null ? true : Array.isArray(targetOrdinal) ? targetOrdinal.includes(ordinal) : ordinal === targetOrdinal;
3769
- if (inOrdinal) {
3770
- elements.forEach((element, i) => {
3771
- element.show();
3772
- });
3773
- } else {
3774
- elements.forEach((element) => {
3775
- element.hide();
3776
- });
3963
+ this.currentOrdinals = targetOrdinal;
3964
+ if (targetOrdinal === null) {
3965
+ const baseOrdinal = 0;
3966
+ for (const [ordinal, elements] of this.elementsByOrdinal) {
3967
+ this.elementRenderer.showElements(elements, ordinal - baseOrdinal);
3968
+ }
3969
+ for (const [ordinal, markers] of this.markersByOrdinal) {
3970
+ this.markerRenderer.showMarkers(markers, ordinal - baseOrdinal);
3971
+ }
3972
+ } else {
3973
+ const baseOrdinal = Array.isArray(targetOrdinal) ? _min(targetOrdinal) : targetOrdinal;
3974
+ for (const [ordinal, elements] of this.elementsByOrdinal) {
3975
+ const inOrdinal = Array.isArray(targetOrdinal) ? targetOrdinal.includes(ordinal) : ordinal === targetOrdinal;
3976
+ if (inOrdinal) {
3977
+ this.elementRenderer.showElements(elements, ordinal - baseOrdinal);
3978
+ } else {
3979
+ this.elementRenderer.hideElements(elements, ordinal - baseOrdinal);
3980
+ }
3981
+ }
3982
+ for (const [ordinal, markers] of this.markersByOrdinal) {
3983
+ const inOrdinal = Array.isArray(targetOrdinal) ? targetOrdinal.includes(ordinal) : ordinal === targetOrdinal;
3984
+ if (inOrdinal) {
3985
+ this.markerRenderer.showMarkers(markers, ordinal - baseOrdinal);
3986
+ } else {
3987
+ this.markerRenderer.hideMarkers(markers, ordinal - baseOrdinal);
3988
+ }
3777
3989
  }
3778
3990
  }
3779
3991
  }
3780
- createMarker(coordinate, ordinal, label) {
3781
- const marker = this.elementRenderer.createMarker(coordinate, ordinal, label);
3782
- this.elementsMap.set(label, marker);
3783
- this.getElementsByOrdinal(ordinal).push(marker);
3992
+ /**
3993
+ * ========================================================================
3994
+ * Markers
3995
+ * ======================================================================== */
3996
+ createMarker(coordinate, ordinal, text, options) {
3997
+ const marker = this.markerRenderer.createMarker(coordinate, ordinal, text, options);
3998
+ const markerId = `${this.markersMap.size + 1}`;
3999
+ this.addMarkersToManager(markerId, [marker], ordinal);
4000
+ }
4001
+ clearMarkers() {
4002
+ for (const [markerId, marker] of this.markersMap) {
4003
+ this.markerRenderer.removeMarker(marker);
4004
+ }
3784
4005
  }
3785
4006
  };
3786
4007
 
@@ -3855,6 +4076,7 @@ var IndoorMap = class extends EventTarget {
3855
4076
  center: INITIAL_CENTER,
3856
4077
  zoom: INITIAL_ZOOM,
3857
4078
  clickTimeThreshold: 600,
4079
+ centerCross: options.centerCross ?? false,
3858
4080
  baseLayer: new TileLayer("base", {
3859
4081
  urlTemplate: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
3860
4082
  subdomains: ["a", "b", "c", "d"],
@@ -3866,13 +4088,7 @@ var IndoorMap = class extends EventTarget {
3866
4088
  }),
3867
4089
  layers: []
3868
4090
  });
3869
- this.rendererManager = new RendererManager(this.map, options.renderer);
3870
- this.rendererManager.addEventListener("renderermanager:ready", (e) => {
3871
- this.dispatchEvent(new CustomEvent("renderer:ready"));
3872
- });
3873
- this.rendererManager.addEventListener("renderermanager:elements_created", (e) => {
3874
- this.dispatchEvent(new CustomEvent("renderer:elements_created"));
3875
- });
4091
+ this.rendererManager = new RendererManager(this.map, options.dataClient, options.renderer);
3876
4092
  this.camera = new CameraManager(this.map);
3877
4093
  this.locale = locale;
3878
4094
  this.pixelRatio = pixelRatio;
@@ -3885,12 +4101,14 @@ var IndoorMap = class extends EventTarget {
3885
4101
  set dataClient(value) {
3886
4102
  this.#dataClient = value;
3887
4103
  this.#dataClient.filterByType("venue").then((venues) => {
3888
- const venueCenters = turfCenter2(featureCollection(venues));
4104
+ const venueCenters = turfCenter3(featureCollection(venues));
3889
4105
  const [x, y] = venueCenters.geometry.coordinates;
3890
- const center2 = new Coordinate3(x, y);
4106
+ const center2 = new Coordinate4(x, y);
3891
4107
  this.camera.setView({ center: center2, pitch: 60, zoom: 19 });
3892
4108
  });
3893
- this.rendererManager.dataClient = this.#dataClient;
4109
+ }
4110
+ on(eventName, handler) {
4111
+ this.map.on(eventName, handler);
3894
4112
  }
3895
4113
  /**
3896
4114
  * Events
@@ -4011,31 +4229,6 @@ var IndoorMap = class extends EventTarget {
4011
4229
  );
4012
4230
  }
4013
4231
  }
4014
- #createLayers() {
4015
- const map = this.map;
4016
- if (!this.ordinals || this.ordinals.length === 0) return;
4017
- LAYERS.forEach((layerKey) => {
4018
- const layerID = layerKey;
4019
- const option = LAYER_OPTIONS[layerKey];
4020
- const layer = new VectorLayer2(layerID, option);
4021
- map.addLayer(layer);
4022
- });
4023
- const threeLayer = new ThreeLayer4("t_building", {
4024
- forceRenderOnMoving: true,
4025
- forceRenderOnRotating: true
4026
- });
4027
- this.threeLayer = threeLayer;
4028
- threeLayer.addTo(this.map);
4029
- }
4030
- #initMapElements() {
4031
- this.#createLayers();
4032
- const threeLayer = this.threeLayer;
4033
- if (threeLayer) {
4034
- this.threeLayer.prepareToDraw = async (gl, scene, camera) => {
4035
- await this.#createElements();
4036
- };
4037
- }
4038
- }
4039
4232
  handleClickElement = (e) => {
4040
4233
  if (this.#isClicked) return;
4041
4234
  this.#isClicked = true;
@@ -4047,29 +4240,22 @@ var IndoorMap = class extends EventTarget {
4047
4240
  setCenter(center2, padding) {
4048
4241
  this.map.setCenter(center2, padding);
4049
4242
  }
4050
- async #createElements() {
4243
+ async #legacy_createElements() {
4051
4244
  const {
4052
4245
  // 2D
4053
4246
  createVenue,
4054
- createLevel,
4055
- createUnit,
4056
4247
  createOpening,
4057
4248
  createSection,
4058
4249
  createFixture,
4059
- createKiosk,
4060
4250
  createOccupant,
4061
4251
  createDecoration,
4062
4252
  // 3D
4063
4253
  create3DFootprint,
4064
4254
  create3DGroundLabel,
4065
4255
  create3DBillboard,
4066
- create3DUnit,
4067
- create3DKiosk,
4068
4256
  createVenue3DModel,
4069
4257
  createExtrudedUnit,
4070
- createExtrudedLevel,
4071
4258
  create3DFixture,
4072
- createExtrudedKiosk,
4073
4259
  create3DAmenityMarker,
4074
4260
  create3DOccupantAmenityMarker,
4075
4261
  create3DOpeningMarker,
@@ -4126,53 +4312,6 @@ var IndoorMap = class extends EventTarget {
4126
4312
  });
4127
4313
  break;
4128
4314
  }
4129
- case "level": {
4130
- const extrudedLevel = createExtrudedLevel(
4131
- feature2,
4132
- this.threeLayer,
4133
- featureExtrudeConfig
4134
- );
4135
- if (extrudedLevel) {
4136
- extrudedLevel.on("click", this.handleClickElement);
4137
- object3ds.push(extrudedLevel);
4138
- } else {
4139
- geometry = createLevel(feature2).addTo(layer);
4140
- }
4141
- break;
4142
- }
4143
- case "unit": {
4144
- const models = await create3DUnit(feature2, this.threeLayer);
4145
- models.forEach((model) => {
4146
- model.on("click", this.handleClickElement);
4147
- object3ds.push(model);
4148
- this.#glbObjects.push(model);
4149
- });
4150
- const locatedLevel = feature2?.properties?.level;
4151
- const levelExtrudeConfig = getExtrudeConfigByFeature(
4152
- extrudeConfig,
4153
- locatedLevel
4154
- );
4155
- const levelHeight = levelExtrudeConfig?.height ?? 0;
4156
- const option = { ...featureExtrudeConfig, altitude: levelHeight };
4157
- const hasAddedModel3d = models?.length > 0;
4158
- if (hasAddedModel3d || !featureExtrudeConfig) {
4159
- geometry = createUnit(feature2)?.on("click", this.handleClickElement).addTo(layer);
4160
- } else {
4161
- const extrudedUnit = createExtrudedUnit(
4162
- feature2,
4163
- this.threeLayer,
4164
- option
4165
- );
4166
- extrudedUnit.on("click", this.handleClickElement);
4167
- extrudedUnit.on("touchstart", (e) => {
4168
- this.#touchStartTarget = e.target;
4169
- this.#touchStartPoint = e.containerPoint;
4170
- this.map.on("touchend", this.handleMapTouchEnd);
4171
- });
4172
- object3ds.push(extrudedUnit);
4173
- }
4174
- break;
4175
- }
4176
4315
  case "amenity": {
4177
4316
  if (feature2.properties.is_featured) {
4178
4317
  const billboardObj = create3DBillboard(feature2, this.threeLayer);
@@ -4195,7 +4334,7 @@ var IndoorMap = class extends EventTarget {
4195
4334
  case "opening": {
4196
4335
  switch (category) {
4197
4336
  case "emergencyexit":
4198
- const { geometry: geometry2 } = turfCenter2(feature2);
4337
+ const { geometry: geometry2 } = turfCenter3(feature2);
4199
4338
  const markerFeature = {
4200
4339
  ...feature2,
4201
4340
  geometry: geometry2
@@ -4311,34 +4450,6 @@ var IndoorMap = class extends EventTarget {
4311
4450
  }
4312
4451
  break;
4313
4452
  }
4314
- case "kiosk": {
4315
- const models = await create3DKiosk(feature2, this.threeLayer);
4316
- models.forEach((model) => {
4317
- model.on("click", this.handleClickElement);
4318
- object3ds.push(model);
4319
- this.#glbObjects.push(model);
4320
- });
4321
- const locatedLevel = feature2?.properties?.level;
4322
- const levelExtrudeConfig = getExtrudeConfigByFeature(
4323
- extrudeConfig,
4324
- locatedLevel
4325
- );
4326
- const levelHeight = _6.get(levelExtrudeConfig, "height", 0);
4327
- const option = { ...featureExtrudeConfig, altitude: levelHeight };
4328
- const hasAddedModel3d = models?.length > 0;
4329
- if (hasAddedModel3d || !featureExtrudeConfig) {
4330
- geometry = createKiosk(feature2)?.on("click", this.handleClickElement).addTo(layer);
4331
- } else {
4332
- const extrudedKiosk = createExtrudedKiosk(
4333
- feature2,
4334
- this.threeLayer,
4335
- option
4336
- );
4337
- extrudedKiosk.on("click", this.handleClickElement);
4338
- object3ds.push(extrudedKiosk);
4339
- }
4340
- break;
4341
- }
4342
4453
  case "footprint": {
4343
4454
  const objects = await create3DFootprint(
4344
4455
  feature2,
@@ -4349,7 +4460,7 @@ var IndoorMap = class extends EventTarget {
4349
4460
  object.on("click", () => {
4350
4461
  const {
4351
4462
  geometry: { coordinates }
4352
- } = turfCenter2(feature2);
4463
+ } = turfCenter3(feature2);
4353
4464
  this.camera.flyToAndZoomIn(coordinates, { pitch: 45 });
4354
4465
  });
4355
4466
  object3ds.push(object);
@@ -4417,7 +4528,6 @@ var IndoorMap = class extends EventTarget {
4417
4528
  );
4418
4529
  }
4419
4530
  });
4420
- this.render();
4421
4531
  this.#elements = elements;
4422
4532
  this.#elementsLoaded = true;
4423
4533
  this.#featuresInitted = true;
@@ -4428,39 +4538,6 @@ var IndoorMap = class extends EventTarget {
4428
4538
  const layer = this.map.getLayer(layerName);
4429
4539
  if (layer) layer.clear();
4430
4540
  };
4431
- #showGeometriesByOrdinal(ordinal) {
4432
- _6(this.#elements).filter((element) => !!_6.get(element, "geometry")).forEach(({ featureType, geometry, properties }) => {
4433
- const show = ordinal === null || properties?.ordinal === ordinal || featureType === "venue";
4434
- const layerName = _6.get(
4435
- LAYER_FEATURE_TYPE_OBJ,
4436
- featureType,
4437
- featureType
4438
- );
4439
- const layer = this.map.getLayer(layerName);
4440
- if (!layer) return;
4441
- if (geometry instanceof ui3.UIMarker)
4442
- return show ? geometry?.addTo(this.map) : geometry.remove();
4443
- show ? layer.addGeometry(geometry) : layer.removeGeometry(geometry);
4444
- if (geometry._animPlayer instanceof animation.Player) {
4445
- const animPlayer = geometry?._animPlayer;
4446
- show ? animPlayer.play() : animPlayer.cancel();
4447
- }
4448
- });
4449
- }
4450
- #showThreeObjectByOrdinal = (ordinal) => {
4451
- const threeLayer = this.threeLayer;
4452
- const objectsToAdd = [];
4453
- const objects = this.#object3ds || [];
4454
- objects?.forEach((baseObject) => {
4455
- const objectOrdinal = _6.get(baseObject, "properties.ordinal");
4456
- const featureType = _6.get(baseObject, "properties.feature_type");
4457
- const isAdd = _6.get(baseObject, "isAdd");
4458
- const show = Boolean(objectOrdinal === ordinal) || ALWAYS_VISIBLE_FEATURE_TYPES.includes(featureType);
4459
- if (show) return objectsToAdd.push(baseObject);
4460
- if (isAdd) return threeLayer.removeMesh(baseObject);
4461
- });
4462
- threeLayer.addMesh(objectsToAdd);
4463
- };
4464
4541
  /**
4465
4542
  * Change Level & animate to path / geometry / view / etc.
4466
4543
  * ================================== */
@@ -4468,8 +4545,8 @@ var IndoorMap = class extends EventTarget {
4468
4545
  this.rendererManager.changeLevelByOrdinal(ordinal);
4469
4546
  }
4470
4547
  getFeatureExtent = (feature2, scaleFactor = 1) => {
4471
- const [minX, minY, maxX, maxY] = turf_bbox_default(
4472
- scale(bboxPolygon(turf_bbox_default(feature2)), scaleFactor)
4548
+ const [minX, minY, maxX, maxY] = index_default(
4549
+ scale(bboxPolygon(index_default(feature2)), scaleFactor)
4473
4550
  );
4474
4551
  return new Extent(minX, minY, maxX, maxY);
4475
4552
  };
@@ -4512,12 +4589,12 @@ var IndoorMap = class extends EventTarget {
4512
4589
  );
4513
4590
  };
4514
4591
  //map animation
4515
- addAnimations(animation2) {
4516
- this.#animationsToRun.push(animation2);
4592
+ addAnimations(animation) {
4593
+ this.#animationsToRun.push(animation);
4517
4594
  }
4518
4595
  removeAnimationById(id) {
4519
4596
  this.#animationsToRun = this.#animationsToRun.filter(
4520
- (animation2) => animation2.id !== id
4597
+ (animation) => animation.id !== id
4521
4598
  );
4522
4599
  }
4523
4600
  clearAnimations() {
@@ -5146,5 +5223,4 @@ export {
5146
5223
  styledFeatureGenerator
5147
5224
  };
5148
5225
  //! Use outerHTML to return HTML string instead of element object to avoid DOM event warnings from Maptalks.js.
5149
- //! Prevent animating geometry from stopping after being removed and re-added to the layer.
5150
5226
  //# sourceMappingURL=index.mjs.map