venue-js 1.4.0-next.17 → 1.4.0-next.19

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.
@@ -272,8 +272,8 @@ var safeFetchFeature = async (featureType, params) => {
272
272
  };
273
273
 
274
274
  // src/data/utils/geometry-validator.ts
275
- var isValidCoordinate = (point) => {
276
- return point.length === 2 && point.every((coord) => typeof coord === "number");
275
+ var isValidCoordinate = (point2) => {
276
+ return point2.length === 2 && point2.every((coord) => typeof coord === "number");
277
277
  };
278
278
  function isValidLinearRingCoordinates(ring) {
279
279
  if (ring.length < 4) {
@@ -281,16 +281,16 @@ function isValidLinearRingCoordinates(ring) {
281
281
  }
282
282
  return ring.every(isValidCoordinate) && ring[0][0] === ring[ring.length - 1][0] && ring[0][1] === ring[ring.length - 1][1];
283
283
  }
284
- var isValidPolygonCoordinates = (polygon) => {
285
- if (Array.isArray(polygon[0]) && (polygon[0].length === 0 || typeof polygon[0][0] === "number")) {
286
- return isValidLinearRingCoordinates(polygon);
284
+ var isValidPolygonCoordinates = (polygon2) => {
285
+ if (Array.isArray(polygon2[0]) && (polygon2[0].length === 0 || typeof polygon2[0][0] === "number")) {
286
+ return isValidLinearRingCoordinates(polygon2);
287
287
  }
288
- if (Array.isArray(polygon) && polygon.length > 0 && Array.isArray(polygon[0])) {
289
- if (!isValidLinearRingCoordinates(polygon[0])) {
288
+ if (Array.isArray(polygon2) && polygon2.length > 0 && Array.isArray(polygon2[0])) {
289
+ if (!isValidLinearRingCoordinates(polygon2[0])) {
290
290
  return false;
291
291
  }
292
- for (let i = 1; i < polygon.length; i++) {
293
- if (!isValidLinearRingCoordinates(polygon[i])) {
292
+ for (let i = 1; i < polygon2.length; i++) {
293
+ if (!isValidLinearRingCoordinates(polygon2[i])) {
294
294
  return false;
295
295
  }
296
296
  }
@@ -301,16 +301,16 @@ var isValidPolygonCoordinates = (polygon) => {
301
301
  var isValidMultiPolygonCoordinates = (multipolygon) => {
302
302
  return multipolygon.every(isValidPolygonCoordinates);
303
303
  };
304
- var isValidLineStringCoordinates = (lineString) => {
305
- if (!Array.isArray(lineString) || lineString.length < 2) {
304
+ var isValidLineStringCoordinates = (lineString2) => {
305
+ if (!Array.isArray(lineString2) || lineString2.length < 2) {
306
306
  return false;
307
307
  }
308
- const firstPoint = lineString[0];
309
- const lastPoint = lineString[lineString.length - 1];
308
+ const firstPoint = lineString2[0];
309
+ const lastPoint = lineString2[lineString2.length - 1];
310
310
  if (firstPoint[0] === lastPoint[0] && firstPoint[1] === lastPoint[1]) {
311
311
  return false;
312
312
  }
313
- return lineString.every(isValidCoordinate);
313
+ return lineString2.every(isValidCoordinate);
314
314
  };
315
315
  var isValidMultiPolygon = (geometry) => {
316
316
  const { type, coordinates } = geometry;
@@ -410,6 +410,19 @@ var findContainingUnit = (poi, units) => {
410
410
  );
411
411
  return unit;
412
412
  };
413
+ var findContainingUnitAtPoint = (point2, levelId, units) => {
414
+ const unit = units.find(
415
+ (unit2) => {
416
+ try {
417
+ return unit2.properties.level_id === levelId && booleanPointInPolygon(point2, unit2);
418
+ } catch (e) {
419
+ console.log(`Cannot find containing unit of (point: ${point2}, levelId: ${levelId}):`, e.message);
420
+ return false;
421
+ }
422
+ }
423
+ );
424
+ return unit;
425
+ };
413
426
 
414
427
  // src/data/populator/index.ts
415
428
  var createPopulator = ({
@@ -582,6 +595,19 @@ var createPopulator = ({
582
595
  }
583
596
  };
584
597
  };
598
+ const populateSection = async (section) => {
599
+ const venue = await internalFindById(section.properties.venue_id);
600
+ const level = await internalFindById(section.properties.level_id);
601
+ return {
602
+ ...section,
603
+ properties: {
604
+ ...section.properties,
605
+ venue,
606
+ level: await populateLevel(level),
607
+ ordinal: level.properties.ordinal
608
+ }
609
+ };
610
+ };
585
611
  const populateRelationship = async (relationship) => {
586
612
  const originId = relationship.properties.origin?.id;
587
613
  const destinationId = relationship.properties.destination?.id;
@@ -599,19 +625,6 @@ var createPopulator = ({
599
625
  }
600
626
  };
601
627
  };
602
- const populateSection = async (section) => {
603
- const venue = await internalFindById(section.properties.venue_id);
604
- const level = await internalFindById(section.properties.level_id);
605
- return {
606
- ...section,
607
- properties: {
608
- ...section.properties,
609
- venue,
610
- level: await populateLevel(level),
611
- ordinal: level.properties.ordinal
612
- }
613
- };
614
- };
615
628
  const populateUnit = async (unit) => {
616
629
  const venue = await internalFindById(unit.properties.venue_id);
617
630
  const level = await internalFindById(unit.properties.level_id);
@@ -659,7 +672,7 @@ var createPopulator = ({
659
672
  console.log(`error finding level`, { model3d, level });
660
673
  }
661
674
  };
662
- const populateFeature = (feature) => Promise.resolve(feature);
675
+ const populateFeature = (feature2) => Promise.resolve(feature2);
663
676
  return {
664
677
  address: populateAddress,
665
678
  building: populateBuilding,
@@ -688,1366 +701,1642 @@ var createPopulator = ({
688
701
  };
689
702
  };
690
703
 
691
- // ../../node_modules/fuse.js/dist/fuse.mjs
692
- function isArray(value) {
693
- return !Array.isArray ? getTag(value) === "[object Array]" : Array.isArray(value);
694
- }
695
- var INFINITY = 1 / 0;
696
- function baseToString(value) {
697
- if (typeof value == "string") {
698
- return value;
699
- }
700
- let result = value + "";
701
- return result == "0" && 1 / value == -INFINITY ? "-0" : result;
702
- }
703
- function toString(value) {
704
- return value == null ? "" : baseToString(value);
705
- }
706
- function isString(value) {
707
- return typeof value === "string";
708
- }
709
- function isNumber(value) {
710
- return typeof value === "number";
711
- }
712
- function isBoolean(value) {
713
- return value === true || value === false || isObjectLike(value) && getTag(value) == "[object Boolean]";
714
- }
715
- function isObject(value) {
716
- return typeof value === "object";
717
- }
718
- function isObjectLike(value) {
719
- return isObject(value) && value !== null;
720
- }
721
- function isDefined(value) {
722
- return value !== void 0 && value !== null;
723
- }
724
- function isBlank(value) {
725
- return !value.trim().length;
726
- }
727
- function getTag(value) {
728
- return value == null ? value === void 0 ? "[object Undefined]" : "[object Null]" : Object.prototype.toString.call(value);
729
- }
730
- var INCORRECT_INDEX_TYPE = "Incorrect 'index' type";
731
- var LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = (key) => `Invalid value for key ${key}`;
732
- var PATTERN_LENGTH_TOO_LARGE = (max) => `Pattern length exceeds max of ${max}.`;
733
- var MISSING_KEY_PROPERTY = (name) => `Missing ${name} property in key`;
734
- var INVALID_KEY_WEIGHT_VALUE = (key) => `Property 'weight' in key '${key}' must be a positive integer`;
735
- var hasOwn = Object.prototype.hasOwnProperty;
736
- var KeyStore = class {
737
- constructor(keys) {
738
- this._keys = [];
739
- this._keyMap = {};
740
- let totalWeight = 0;
741
- keys.forEach((key) => {
742
- let obj = createKey(key);
743
- this._keys.push(obj);
744
- this._keyMap[obj.id] = obj;
745
- totalWeight += obj.weight;
746
- });
747
- this._keys.forEach((key) => {
748
- key.weight /= totalWeight;
749
- });
750
- }
751
- get(keyId) {
752
- return this._keyMap[keyId];
753
- }
754
- keys() {
755
- return this._keys;
756
- }
757
- toJSON() {
758
- return JSON.stringify(this._keys);
704
+ // src/data/search/getSearchClient.ts
705
+ import Fuse from "fuse.js";
706
+
707
+ // src/data/search/utils/sanitizeInput.ts
708
+ var sanitizeInput = (str) => str.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\-–—_./()]+/g, "").normalize("NFC").trim();
709
+
710
+ // src/data/search/getSearchClient.ts
711
+ var getSearchClient = ({ occupants, amenities }) => {
712
+ const fuseAmenities = new Fuse(amenities, {
713
+ threshold: 0.2,
714
+ keys: [
715
+ { name: "properties.name", "weight": 1, getFn: (obj) => Object.values(obj.properties.name || {}) },
716
+ { name: "properties.category", "weight": 1 }
717
+ ]
718
+ });
719
+ const fuseOccupants = new Fuse(occupants, {
720
+ threshold: 0.25,
721
+ // 0.2 is too strict (can't find Mo-Mo Paradise with "momo" search string)
722
+ includeScore: true,
723
+ shouldSort: true,
724
+ keys: [
725
+ { name: "properties.name", "weight": 4, getFn: (obj) => Object.values(obj.properties.name || {}) },
726
+ { name: "properties.keywords", "weight": 0.5 },
727
+ { name: "properties.category", "weight": 0.25 },
728
+ { name: "properties.local_category_names", "weight": 0.25 },
729
+ { name: "properties.description", "weight": 0.25, getFn: (occ) => Object.values(occ.properties.description || {}) },
730
+ { name: "properties.unit_name", "weight": 0.25 },
731
+ { name: "properties.kiosk_name", "weight": 0.25 }
732
+ ]
733
+ });
734
+ const search = (value) => {
735
+ const sanitizedValue = sanitizeInput(value);
736
+ const matchedAmenities = fuseAmenities.search(sanitizedValue);
737
+ const matchedOccupants = fuseOccupants.search(sanitizedValue);
738
+ return [...matchedAmenities, ...matchedOccupants];
739
+ };
740
+ return {
741
+ search
742
+ };
743
+ };
744
+
745
+ // src/data/navigate/getNavigateClient.ts
746
+ import { booleanPointInPolygon as booleanPointInPolygon4 } from "@turf/boolean-point-in-polygon";
747
+
748
+ // src/data/navigate/graph/prepare.ts
749
+ import _6 from "lodash";
750
+ import DijstraGraph from "node-dijkstra";
751
+ import { distance as distance2 } from "@turf/distance";
752
+ import { center as turfCenter3 } from "@turf/center";
753
+
754
+ // src/data/navigate/graph/nodemap/createTraversalNodeMap.ts
755
+ import { distance as turfDistance } from "@turf/distance";
756
+ import { center as turfCenter } from "@turf/center";
757
+ import _ from "lodash";
758
+
759
+ // src/data/navigate/graph/constants.ts
760
+ var ROOM_BASEDISTANCE = 1e3;
761
+ var TERRACE_BASEDISTANCE = 1e3;
762
+ var ESCALATOR_BASEDISTANCE = 200;
763
+ var RAMP_BASEDISTANCE = 200;
764
+ var ELEVATOR_BASEDISTANCE = 500;
765
+ var STAIR_BASEDISTANCE = 1e5;
766
+ var BASE_POI_BASEDISTANCE = 9999999;
767
+ var DEFAULT_UNIT_BASEDISTANCE_OPTIONS = {
768
+ default: { baseDistance: 0 },
769
+ byCategory: {
770
+ room: { baseDistance: ROOM_BASEDISTANCE },
771
+ terrace: { baseDistance: TERRACE_BASEDISTANCE },
772
+ escalator: { baseDistance: ESCALATOR_BASEDISTANCE, scaleDistanceByLevel: false },
773
+ ramp: { baseDistance: RAMP_BASEDISTANCE, scaleDistanceByLevel: false },
774
+ elevator: { baseDistance: ELEVATOR_BASEDISTANCE, scaleDistanceByLevel: false },
775
+ stairs: {
776
+ baseDistance: STAIR_BASEDISTANCE,
777
+ scaleDistanceByLevel: true
778
+ },
779
+ "stairs.emergencyexit": {
780
+ baseDistance: STAIR_BASEDISTANCE,
781
+ scaleDistanceByLevel: true
782
+ }
759
783
  }
760
784
  };
761
- function createKey(key) {
762
- let path = null;
763
- let id = null;
764
- let src = null;
765
- let weight = 1;
766
- let getFn = null;
767
- if (isString(key) || isArray(key)) {
768
- src = key;
769
- path = createKeyPath(key);
770
- id = createKeyId(key);
771
- } else {
772
- if (!hasOwn.call(key, "name")) {
773
- throw new Error(MISSING_KEY_PROPERTY("name"));
785
+
786
+ // src/data/navigate/graph/utils/getDistanceOption.ts
787
+ var getDistanceOptions = (options, category) => {
788
+ if (!options) return DEFAULT_UNIT_BASEDISTANCE_OPTIONS.byCategory[category];
789
+ return (category && options.byCategory?.[category]) ?? DEFAULT_UNIT_BASEDISTANCE_OPTIONS.byCategory[category] ?? options?.default ?? DEFAULT_UNIT_BASEDISTANCE_OPTIONS.default;
790
+ };
791
+
792
+ // src/data/utils/trace.ts
793
+ var trace = (namespace, text, ms, color) => {
794
+ console.log(`[${namespace}] %c${text.padEnd(90)} ${ms !== void 0 ? `${ms.toFixed(1).padStart(6)} ms` : ``}`, color ? `color: ${color}` : void 0);
795
+ };
796
+
797
+ // src/data/navigate/graph/nodemap/createTraversalNodeMap.ts
798
+ var createTraversalNodeMap = (unitOpenings, options) => {
799
+ const { units } = options.data;
800
+ let counter = 0;
801
+ const calculateFeatureDistanceWithinUnit = (features, distanceOptions) => {
802
+ let relationshipGraph = {};
803
+ for (let currentIndex = 0; currentIndex < features.length; currentIndex++) {
804
+ const isLastItem = currentIndex + 1 === features.length;
805
+ if (isLastItem) break;
806
+ for (let j = currentIndex + 1; j < features.length; j++) {
807
+ const opening = features[currentIndex];
808
+ const feature2 = features[j];
809
+ try {
810
+ const distance5 = turfDistance(
811
+ turfCenter(opening.geometry),
812
+ turfCenter(feature2.geometry),
813
+ { units: "meters" }
814
+ ) + (distanceOptions?.baseDistance ?? 0);
815
+ if (opening.id === feature2.id) continue;
816
+ _.set(relationshipGraph, `${opening.id}.${feature2.id}`, distance5);
817
+ _.set(relationshipGraph, `${feature2.id}.${opening.id}`, distance5);
818
+ counter++;
819
+ } catch (error) {
820
+ continue;
821
+ }
822
+ }
774
823
  }
775
- const name = key.name;
776
- src = name;
777
- if (hasOwn.call(key, "weight")) {
778
- weight = key.weight;
779
- if (weight <= 0) {
780
- throw new Error(INVALID_KEY_WEIGHT_VALUE(name));
824
+ return relationshipGraph;
825
+ };
826
+ const t0 = performance.now();
827
+ const nodeMap = _.reduce(
828
+ unitOpenings,
829
+ (acc, openings, unitId) => {
830
+ const unit = units.find((unit2) => unit2.id === unitId);
831
+ const unitDistanceOption = getDistanceOptions(options.unitDistanceOptions, unit.properties.category);
832
+ return _.merge(
833
+ acc,
834
+ calculateFeatureDistanceWithinUnit(openings, unitDistanceOption)
835
+ );
836
+ },
837
+ {}
838
+ );
839
+ const t1 = performance.now();
840
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} traversal relationships`, t1 - t0);
841
+ return nodeMap;
842
+ };
843
+
844
+ // src/data/navigate/graph/nodemap/createElevatorNodeMap.ts
845
+ import _2 from "lodash";
846
+ var createElevatorNodeMap = (elevatorLikeRelationships, unitOpenings, options) => {
847
+ const t0 = performance.now();
848
+ const { levels, units } = options.data;
849
+ const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "elevator");
850
+ const {
851
+ baseDistance = ELEVATOR_BASEDISTANCE,
852
+ scaleDistanceByLevel = false
853
+ } = distanceOptions;
854
+ let elevatorNodeMap = {};
855
+ let counter = 0;
856
+ for (const relationship of elevatorLikeRelationships) {
857
+ try {
858
+ const {
859
+ origin: originTypeAndId,
860
+ intermediary,
861
+ destination: destinationTypeAndId
862
+ } = relationship.properties;
863
+ const origin = units.find((unit) => unit.id === originTypeAndId.id);
864
+ if (!origin) return;
865
+ const originOpenings = compact(unitOpenings[origin.id]);
866
+ const originLevel = levels.find((level) => level.id === origin.properties.level_id);
867
+ const destination = units.find((unit) => unit.id === destinationTypeAndId.id);
868
+ const destinationOpenings = unitOpenings[destination.id];
869
+ const destinationOpeningAndLevels = destinationOpenings.map((opening) => {
870
+ const level = levels.find((level2) => level2.id === destination.properties.level_id);
871
+ return { opening, level };
872
+ });
873
+ const intermediaryOpeningAndLevels = intermediary.map((unitTypeAndId) => {
874
+ const openings = unitOpenings[unitTypeAndId.id];
875
+ const unit = units.find((unit2) => unit2.id === unitTypeAndId.id);
876
+ const level = levels.find((level2) => level2.id === unit.properties.level_id);
877
+ return openings.map((opening) => ({ opening, level }));
878
+ }).flat();
879
+ const connections = compact([...intermediaryOpeningAndLevels, ...destinationOpeningAndLevels]);
880
+ if (!originOpenings || originOpenings.length === 0) return;
881
+ for (const originOpening of originOpenings) {
882
+ for (const connection of connections) {
883
+ const { opening, level } = connection;
884
+ let distance5 = baseDistance;
885
+ if (scaleDistanceByLevel) {
886
+ const originOrdinal = originLevel.properties.ordinal;
887
+ const connectionOrdinal = level.properties.ordinal;
888
+ const levelDifference = Math.abs(originOrdinal - connectionOrdinal);
889
+ if (levelDifference > 0) distance5 *= levelDifference;
890
+ }
891
+ _2.set(elevatorNodeMap, `${originOpening.id}.${opening.id}`, distance5);
892
+ _2.set(elevatorNodeMap, `${opening.id}.${originOpening.id}`, distance5);
893
+ counter++;
894
+ }
781
895
  }
896
+ } catch (err) {
897
+ console.log(err);
898
+ console.log("cannot create elevatorNodeMap for ", { relationship });
782
899
  }
783
- path = createKeyPath(name);
784
- id = createKeyId(name);
785
- getFn = key.getFn;
786
900
  }
787
- return { path, id, weight, src, getFn };
788
- }
789
- function createKeyPath(key) {
790
- return isArray(key) ? key : key.split(".");
791
- }
792
- function createKeyId(key) {
793
- return isArray(key) ? key.join(".") : key;
794
- }
795
- function get(obj, path) {
796
- let list = [];
797
- let arr = false;
798
- const deepGet = (obj2, path2, index) => {
799
- if (!isDefined(obj2)) {
800
- return;
901
+ const t1 = performance.now();
902
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} escalator relationships`, t1 - t0);
903
+ return elevatorNodeMap;
904
+ };
905
+
906
+ // src/data/navigate/graph/nodemap/createEscalatorNodeMap.ts
907
+ import set from "lodash/set";
908
+ var createEscalatorNodeMap = (relationships, options) => {
909
+ const t0 = performance.now();
910
+ const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "escalator");
911
+ let counter = 0;
912
+ let nodeMap = {};
913
+ for (const relationship of relationships) {
914
+ const {
915
+ properties: { direction, origin, destination }
916
+ } = relationship;
917
+ set(nodeMap, `${origin.id}.${destination.id}`, distanceOptions.baseDistance);
918
+ if (direction === "undirected") {
919
+ set(nodeMap, `${destination.id}.${origin.id}`, distanceOptions.baseDistance);
801
920
  }
802
- if (!path2[index]) {
803
- list.push(obj2);
804
- } else {
805
- let key = path2[index];
806
- const value = obj2[key];
807
- if (!isDefined(value)) {
808
- return;
809
- }
810
- if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) {
811
- list.push(toString(value));
812
- } else if (isArray(value)) {
813
- arr = true;
814
- for (let i = 0, len = value.length; i < len; i += 1) {
815
- deepGet(value[i], path2, index + 1);
921
+ counter++;
922
+ }
923
+ const t1 = performance.now();
924
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} escalator relationships`, t1 - t0);
925
+ return nodeMap;
926
+ };
927
+
928
+ // src/data/navigate/graph/nodemap/createRampNodeMap.ts
929
+ import set2 from "lodash/set";
930
+ var createRampNodeMap = (relationships, options) => {
931
+ const t0 = performance.now();
932
+ const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "ramp");
933
+ let counter = 0;
934
+ let nodeMap = {};
935
+ relationships.forEach((relationship) => {
936
+ const {
937
+ properties: { origin, destination }
938
+ } = relationship;
939
+ set2(nodeMap, `${origin.id}.${destination.id}`, distanceOptions.baseDistance);
940
+ set2(nodeMap, `${destination.id}.${origin.id}`, distanceOptions.baseDistance);
941
+ counter++;
942
+ });
943
+ const t1 = performance.now();
944
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} ramp relationships`, t1 - t0);
945
+ return nodeMap;
946
+ };
947
+
948
+ // src/data/navigate/graph/nodemap/createStairNodeMap.ts
949
+ import _3 from "lodash";
950
+ var createStairNodeMap = (elevatorLikeRelationships, unitOpenings, options) => {
951
+ const t0 = performance.now();
952
+ const { levels = [], openings = [], units = [] } = options.data;
953
+ const { baseDistance, scaleDistanceByLevel } = getDistanceOptions(options.unitDistanceOptions, "stairs");
954
+ let elevatorNodeMap = {};
955
+ let counter = 0;
956
+ for (const relationship of elevatorLikeRelationships) {
957
+ try {
958
+ const {
959
+ origin: { id: originId },
960
+ intermediary,
961
+ destination: { id: destinationId }
962
+ } = relationship.properties;
963
+ const origin = openings.find((opening) => opening.id === originId);
964
+ if (!origin) return;
965
+ const originLevel = levels.find((level) => level.id === origin.properties.level_id);
966
+ const destination = openings.find((opening) => opening.id === destinationId);
967
+ const destinationOpeningAndLevel = {
968
+ opening: destination,
969
+ level: levels.find((level) => level.id === destination.properties.level_id)
970
+ };
971
+ const intermediaryOpeningAndLevels = intermediary.map((unitTypeAndId) => {
972
+ const openings2 = unitOpenings[unitTypeAndId.id];
973
+ const unit = units.find((unit2) => unit2.id === unitTypeAndId.id);
974
+ const level = levels.find((level2) => level2.id === unit.properties.level_id);
975
+ return openings2.map((opening) => ({ opening, level }));
976
+ }).flat();
977
+ const connections = [...intermediaryOpeningAndLevels, destinationOpeningAndLevel];
978
+ if (!origin) return;
979
+ for (const connection of connections) {
980
+ const { opening, level } = connection;
981
+ let distance5 = baseDistance;
982
+ if (scaleDistanceByLevel) {
983
+ const originOrdinal = originLevel.properties.ordinal;
984
+ const connectionOrdinal = level.properties.ordinal;
985
+ const levelDifference = Math.abs(originOrdinal - connectionOrdinal);
986
+ if (levelDifference > 0) distance5 *= levelDifference;
816
987
  }
817
- } else if (path2.length) {
818
- deepGet(value, path2, index + 1);
988
+ _3.set(elevatorNodeMap, `${origin.id}.${opening.id}`, distance5);
989
+ _3.set(elevatorNodeMap, `${opening.id}.${origin.id}`, distance5);
990
+ counter++;
819
991
  }
992
+ } catch (err) {
993
+ console.warn(
994
+ "Failed to create stairNodeMap",
995
+ {
996
+ relationshipId: relationship.id,
997
+ featureType: relationship.feature_type,
998
+ error: err instanceof Error ? err.message : err,
999
+ stack: err instanceof Error ? err.stack : void 0
1000
+ }
1001
+ );
820
1002
  }
821
- };
822
- deepGet(obj, isString(path) ? path.split(".") : path, 0);
823
- return arr ? list : list[0];
824
- }
825
- var MatchOptions = {
826
- // Whether the matches should be included in the result set. When `true`, each record in the result
827
- // set will include the indices of the matched characters.
828
- // These can consequently be used for highlighting purposes.
829
- includeMatches: false,
830
- // When `true`, the matching function will continue to the end of a search pattern even if
831
- // a perfect match has already been located in the string.
832
- findAllMatches: false,
833
- // Minimum number of characters that must be matched before a result is considered a match
834
- minMatchCharLength: 1
1003
+ }
1004
+ const t1 = performance.now();
1005
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} stairs relationships`, t1 - t0);
1006
+ return elevatorNodeMap;
1007
+ };
1008
+
1009
+ // src/data/navigate/graph/nodemap/createOccupantNodeMap.ts
1010
+ import _4 from "lodash";
1011
+ var createOccupantNodeMap = (occupants) => {
1012
+ const t0 = performance.now();
1013
+ let nodeMap = {};
1014
+ let counter = 0;
1015
+ occupants.forEach((occupant) => {
1016
+ const { unit_id, unit_ids = [], kiosk_id, kiosk_ids = [] } = occupant.properties;
1017
+ const occupantRoomIds = compact([unit_id, ...unit_ids]);
1018
+ const occupantKioskIds = compact([kiosk_id, ...kiosk_ids]);
1019
+ for (const roomId of occupantRoomIds) {
1020
+ _4.set(nodeMap, `${roomId}.${occupant.id}`, 1e-3);
1021
+ _4.set(nodeMap, `${occupant.id}.${roomId}`, 1e-3);
1022
+ counter++;
1023
+ }
1024
+ for (const kioskId of occupantKioskIds) {
1025
+ _4.set(nodeMap, `${kioskId}.${occupant.id}`, 1e-3);
1026
+ _4.set(nodeMap, `${occupant.id}.${kioskId}`, 1e-3);
1027
+ counter++;
1028
+ }
1029
+ });
1030
+ const t1 = performance.now();
1031
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} occupants relationships`, t1 - t0);
1032
+ return nodeMap;
835
1033
  };
836
- var BasicOptions = {
837
- // When `true`, the algorithm continues searching to the end of the input even if a perfect
838
- // match is found before the end of the same input.
839
- isCaseSensitive: false,
840
- // When `true`, the algorithm will ignore diacritics (accents) in comparisons
841
- ignoreDiacritics: false,
842
- // When true, the matching function will continue to the end of a search pattern even if
843
- includeScore: false,
844
- // List of properties that will be searched. This also supports nested properties.
845
- keys: [],
846
- // Whether to sort the result list, by score
847
- shouldSort: true,
848
- // Default sort function: sort by ascending score, ascending index
849
- sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1
1034
+
1035
+ // src/data/navigate/graph/nodemap/createPOINodeMaps.ts
1036
+ import { center as turfCenter2 } from "@turf/center";
1037
+ import { distance } from "@turf/distance";
1038
+ import _5 from "lodash";
1039
+ var createPOINodeMap = (features, getFeatureUnit, unitOpenings) => {
1040
+ const t0 = performance.now();
1041
+ let nodeMap = {};
1042
+ let counter = 0;
1043
+ features.forEach((feat) => {
1044
+ try {
1045
+ const locatedOnUnitId = getFeatureUnit(feat);
1046
+ const openings = unitOpenings[locatedOnUnitId];
1047
+ const center7 = turfCenter2(feat);
1048
+ for (const opening of openings) {
1049
+ try {
1050
+ const openingCenter = turfCenter2(opening);
1051
+ const dis = distance(center7, openingCenter, { units: "meters" }) + BASE_POI_BASEDISTANCE;
1052
+ _5.set(nodeMap, `${opening.id}.${feat.id}`, dis);
1053
+ _5.set(nodeMap, `${feat.id}.${opening.id}`, dis);
1054
+ counter++;
1055
+ } catch (err) {
1056
+ console.log(err, opening);
1057
+ }
1058
+ }
1059
+ } catch (err) {
1060
+ console.log(err);
1061
+ console.log(`cannot connect poi to openings`, err.message, { feat });
1062
+ }
1063
+ });
1064
+ const type = features.length > 0 ? features[0].feature_type : "-";
1065
+ const t1 = performance.now();
1066
+ trace("nav", ` \u2502 \u251C\u2500 add ${counter} ${type} relationships`, t1 - t0);
1067
+ return nodeMap;
850
1068
  };
851
- var FuzzyOptions = {
852
- // Approximately where in the text is the pattern expected to be found?
853
- location: 0,
854
- // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match
855
- // (of both letters and location), a threshold of '1.0' would match anything.
856
- threshold: 0.6,
857
- // Determines how close the match must be to the fuzzy location (specified above).
858
- // An exact letter match which is 'distance' characters away from the fuzzy location
859
- // would score as a complete mismatch. A distance of '0' requires the match be at
860
- // the exact location specified, a threshold of '1000' would require a perfect match
861
- // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
862
- distance: 100
1069
+
1070
+ // src/data/navigate/graph/utils/mergeNodeMap.ts
1071
+ var mergeNodeMap = (nodeMaps) => {
1072
+ const out = {};
1073
+ for (const nodeMap of nodeMaps) {
1074
+ for (const from in nodeMap) {
1075
+ out[from] = {
1076
+ ...out[from] ?? {},
1077
+ ...nodeMap[from]
1078
+ };
1079
+ }
1080
+ }
1081
+ return out;
863
1082
  };
864
- var AdvancedOptions = {
865
- // When `true`, it enables the use of unix-like search commands
866
- useExtendedSearch: false,
867
- // The get function to use when fetching an object's properties.
868
- // The default will search nested paths *ie foo.bar.baz*
869
- getFn: get,
870
- // When `true`, search will ignore `location` and `distance`, so it won't matter
871
- // where in the string the pattern appears.
872
- // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score
873
- ignoreLocation: false,
874
- // When `true`, the calculation for the relevance score (used for sorting) will
875
- // ignore the field-length norm.
876
- // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm
877
- ignoreFieldNorm: false,
878
- // The weight to determine how much field length norm effects scoring.
879
- fieldNormWeight: 1
1083
+
1084
+ // src/data/navigate/graph/utils/createUnitOpenings.ts
1085
+ import uniqBy from "lodash/uniqBy";
1086
+ var createUnitOpenings = (relationships, units, openings) => {
1087
+ const openingConnections = {};
1088
+ const relationshipMap = /* @__PURE__ */ new Map();
1089
+ relationships.forEach((relationship) => {
1090
+ const originId = relationship.properties.origin?.id || null;
1091
+ const destinationId = relationship.properties.destination?.id || null;
1092
+ if (!relationshipMap.has(originId)) {
1093
+ relationshipMap.set(originId, []);
1094
+ }
1095
+ if (!relationshipMap.has(destinationId)) {
1096
+ relationshipMap.set(destinationId, []);
1097
+ }
1098
+ relationshipMap.get(originId).push(relationship);
1099
+ relationshipMap.get(destinationId).push(relationship);
1100
+ });
1101
+ units.forEach((unit) => {
1102
+ const unitId = unit.id;
1103
+ const connectedRelationshop = relationshipMap.get(unitId) || [];
1104
+ const relationshipIntermediaryTypeAndId = connectedRelationshop.map(
1105
+ (relationship) => relationship.properties.intermediary[0]
1106
+ // Assuming intermediary is always an array
1107
+ );
1108
+ const relationshipIntermediary = relationshipIntermediaryTypeAndId.map(({ id }) => {
1109
+ return openings.find((opening) => opening.id === id);
1110
+ });
1111
+ openingConnections[unitId] = uniqBy(
1112
+ [...openingConnections[unitId] || [], ...relationshipIntermediary],
1113
+ "id"
1114
+ );
1115
+ });
1116
+ return openingConnections;
880
1117
  };
881
- var Config = {
882
- ...BasicOptions,
883
- ...MatchOptions,
884
- ...FuzzyOptions,
885
- ...AdvancedOptions
1118
+
1119
+ // src/data/navigate/parsers.ts
1120
+ var parseOrdinalCoordinate = (id) => {
1121
+ return id.slice(0, -1).split(",").map(Number);
886
1122
  };
887
- var SPACE = /[^ ]+/g;
888
- function norm(weight = 1, mantissa = 3) {
889
- const cache = /* @__PURE__ */ new Map();
890
- const m = Math.pow(10, mantissa);
891
- return {
892
- get(value) {
893
- const numTokens = value.match(SPACE).length;
894
- if (cache.has(numTokens)) {
895
- return cache.get(numTokens);
1123
+
1124
+ // src/data/navigate/graph/prepare.ts
1125
+ var prepareGraph = (options) => {
1126
+ const {
1127
+ data: {
1128
+ amenities = [],
1129
+ anchors = [],
1130
+ occupants = [],
1131
+ relationships = [],
1132
+ openings = [],
1133
+ units = [],
1134
+ kiosks = [],
1135
+ levels = []
1136
+ }
1137
+ } = options;
1138
+ const {
1139
+ traversal: traversalRelationships = [],
1140
+ escalator: escalatorRelationships = [],
1141
+ ramp: rampRelationships = [],
1142
+ elevator: elevatorRelationships = [],
1143
+ stairs: stairsRelationships = []
1144
+ } = _6.groupBy(relationships, "properties.category");
1145
+ const unitOpenings = createUnitOpenings(traversalRelationships, units, openings);
1146
+ const traversalNodeMap = createTraversalNodeMap(unitOpenings, options);
1147
+ const escalatorNodeMap = createEscalatorNodeMap(escalatorRelationships, options);
1148
+ const rampNodeMap = createRampNodeMap(rampRelationships, options);
1149
+ const elevatorNodeMap = createElevatorNodeMap(
1150
+ elevatorRelationships,
1151
+ unitOpenings,
1152
+ options
1153
+ );
1154
+ const stairNodeMap = createStairNodeMap(
1155
+ stairsRelationships,
1156
+ unitOpenings,
1157
+ options
1158
+ );
1159
+ const amenityNodeMap = createPOINodeMap(amenities, (amenity) => amenity.properties.unit_ids[0], unitOpenings);
1160
+ const anchorsNodeMap = createPOINodeMap(anchors, (anchor) => anchor.properties.unit_id, unitOpenings);
1161
+ const walkwayUnits = units.filter((unit) => unit.properties.category === "walkway");
1162
+ const kioskNodeMap = createPOINodeMap(kiosks, (kiosk) => findContainingUnit(kiosk, walkwayUnits)?.id, unitOpenings);
1163
+ const unitNodeMap = createPOINodeMap(units, (unit) => unit.id, unitOpenings);
1164
+ const occupantNodeMap = createOccupantNodeMap(occupants);
1165
+ const defaultGraph = new DijstraGraph(mergeNodeMap([
1166
+ traversalNodeMap,
1167
+ escalatorNodeMap,
1168
+ rampNodeMap,
1169
+ elevatorNodeMap,
1170
+ stairNodeMap,
1171
+ amenityNodeMap,
1172
+ anchorsNodeMap,
1173
+ kioskNodeMap,
1174
+ unitNodeMap,
1175
+ occupantNodeMap
1176
+ ]));
1177
+ const accessibleGraph = new DijstraGraph(mergeNodeMap([
1178
+ traversalNodeMap,
1179
+ rampNodeMap,
1180
+ elevatorNodeMap,
1181
+ amenityNodeMap,
1182
+ anchorsNodeMap,
1183
+ kioskNodeMap,
1184
+ unitNodeMap,
1185
+ occupantNodeMap
1186
+ ]));
1187
+ const addCoordinateOrdinalNode = (params, locatedOnUnit) => {
1188
+ const [lat, lng, ordinal] = parseOrdinalCoordinate(params);
1189
+ if (locatedOnUnit) {
1190
+ const openings2 = unitOpenings[locatedOnUnit.id];
1191
+ for (const opening of openings2) {
1192
+ const openingCenter = turfCenter3(opening);
1193
+ const dis = distance2([lat, lng], openingCenter, { units: "meters" });
1194
+ defaultGraph.addNode(params, { [opening.id]: dis }).addNode(opening.id, { [params]: dis });
1195
+ accessibleGraph.addNode(params, { [opening.id]: dis }).addNode(opening.id, { [params]: dis });
896
1196
  }
897
- const norm2 = 1 / Math.pow(numTokens, 0.5 * weight);
898
- const n = parseFloat(Math.round(norm2 * m) / m);
899
- cache.set(numTokens, n);
900
- return n;
901
- },
902
- clear() {
903
- cache.clear();
904
1197
  }
905
1198
  };
1199
+ return { defaultGraph, accessibleGraph, unitOpenings, addCoordinateOrdinalNode };
1200
+ };
1201
+
1202
+ // src/data/navigate/steps/createStepUtils.ts
1203
+ import { capitalize } from "lodash";
1204
+ import _intersectionBy from "lodash/intersectionBy";
1205
+ import { center as center6 } from "@turf/center";
1206
+
1207
+ // src/data/navigate/description/describe.ts
1208
+ var t = (template, locale, options) => {
1209
+ return template.replace(`{{intermediary}}`, options.intermediary ?? "").replace(`{{toward}}`, options.toward?.[locale] ?? "").replace(`{{landmark}}`, options.landmark?.[locale] ?? "");
1210
+ };
1211
+ var describeVerticalStep = (fromLevel, toLevel, intermediary) => {
1212
+ const dir = fromLevel.properties.ordinal < toLevel.properties.ordinal ? "up" : "down";
1213
+ const template = `Take the {{intermediary}} ${dir} to {{toward}}`;
1214
+ return {
1215
+ template,
1216
+ text: t(template, "en", { intermediary, toward: toLevel.properties.name })
1217
+ };
1218
+ };
1219
+ var describeHorizontalStep = (intermediary, toward, landmark) => {
1220
+ const template = `Follow the path ${intermediary === "walkway" ? `along the walkway` : `through {{intermediary}}`} ${toward ? `toward {{toward}}` : ``} ${landmark ? `near {{landmark}}` : ``}`.trim();
1221
+ return {
1222
+ text: t(template, "en", { intermediary, toward, landmark }),
1223
+ template
1224
+ };
1225
+ };
1226
+
1227
+ // src/data/navigate/steps/path/index.ts
1228
+ import _7 from "lodash";
1229
+
1230
+ // src/data/navigate/constants.ts
1231
+ var OBSTACLE_FEATURE_TYPES = [
1232
+ "kiosk"
1233
+ /* , "fixture" */
1234
+ ];
1235
+ var OBSTACLE_CATEGORIES = [
1236
+ "fixture.water",
1237
+ "fixture.stage",
1238
+ "nonpublic",
1239
+ "opentobelow",
1240
+ "elevator",
1241
+ "escalator",
1242
+ "stairs",
1243
+ "stairs.emergencyexit",
1244
+ "room",
1245
+ "unspecified",
1246
+ "structure",
1247
+ "brick",
1248
+ "concrete",
1249
+ "drywall",
1250
+ "glass",
1251
+ "wood",
1252
+ "column"
1253
+ ];
1254
+ var WALKABLE_CATEGORY = [
1255
+ "walkway",
1256
+ "parking",
1257
+ "room",
1258
+ "terrace",
1259
+ "unenclosedarea",
1260
+ "vegetation",
1261
+ "unspecified"
1262
+ ];
1263
+
1264
+ // node_modules/@turf/helpers/dist/esm/index.js
1265
+ var earthRadius = 63710088e-1;
1266
+ var factors = {
1267
+ centimeters: earthRadius * 100,
1268
+ centimetres: earthRadius * 100,
1269
+ degrees: 360 / (2 * Math.PI),
1270
+ feet: earthRadius * 3.28084,
1271
+ inches: earthRadius * 39.37,
1272
+ kilometers: earthRadius / 1e3,
1273
+ kilometres: earthRadius / 1e3,
1274
+ meters: earthRadius,
1275
+ metres: earthRadius,
1276
+ miles: earthRadius / 1609.344,
1277
+ millimeters: earthRadius * 1e3,
1278
+ millimetres: earthRadius * 1e3,
1279
+ nauticalmiles: earthRadius / 1852,
1280
+ radians: 1,
1281
+ yards: earthRadius * 1.0936
1282
+ };
1283
+ function feature(geom, properties, options = {}) {
1284
+ const feat = { type: "Feature" };
1285
+ if (options.id === 0 || options.id) {
1286
+ feat.id = options.id;
1287
+ }
1288
+ if (options.bbox) {
1289
+ feat.bbox = options.bbox;
1290
+ }
1291
+ feat.properties = properties || {};
1292
+ feat.geometry = geom;
1293
+ return feat;
906
1294
  }
907
- var FuseIndex = class {
908
- constructor({
909
- getFn = Config.getFn,
910
- fieldNormWeight = Config.fieldNormWeight
911
- } = {}) {
912
- this.norm = norm(fieldNormWeight, 3);
913
- this.getFn = getFn;
914
- this.isCreated = false;
915
- this.setIndexRecords();
1295
+ function point(coordinates, properties, options = {}) {
1296
+ if (!coordinates) {
1297
+ throw new Error("coordinates is required");
916
1298
  }
917
- setSources(docs = []) {
918
- this.docs = docs;
1299
+ if (!Array.isArray(coordinates)) {
1300
+ throw new Error("coordinates must be an Array");
919
1301
  }
920
- setIndexRecords(records = []) {
921
- this.records = records;
1302
+ if (coordinates.length < 2) {
1303
+ throw new Error("coordinates must be at least 2 numbers long");
922
1304
  }
923
- setKeys(keys = []) {
924
- this.keys = keys;
925
- this._keysMap = {};
926
- keys.forEach((key, idx) => {
927
- this._keysMap[key.id] = idx;
928
- });
1305
+ if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {
1306
+ throw new Error("coordinates must contain numbers");
929
1307
  }
930
- create() {
931
- if (this.isCreated || !this.docs.length) {
932
- return;
1308
+ const geom = {
1309
+ type: "Point",
1310
+ coordinates
1311
+ };
1312
+ return feature(geom, properties, options);
1313
+ }
1314
+ function polygon(coordinates, properties, options = {}) {
1315
+ for (const ring of coordinates) {
1316
+ if (ring.length < 4) {
1317
+ throw new Error(
1318
+ "Each LinearRing of a Polygon must have 4 or more Positions."
1319
+ );
933
1320
  }
934
- this.isCreated = true;
935
- if (isString(this.docs[0])) {
936
- this.docs.forEach((doc, docIndex) => {
937
- this._addString(doc, docIndex);
938
- });
939
- } else {
940
- this.docs.forEach((doc, docIndex) => {
941
- this._addObject(doc, docIndex);
942
- });
1321
+ if (ring[ring.length - 1].length !== ring[0].length) {
1322
+ throw new Error("First and last Position are not equivalent.");
943
1323
  }
944
- this.norm.clear();
945
- }
946
- // Adds a doc to the end of the index
947
- add(doc) {
948
- const idx = this.size();
949
- if (isString(doc)) {
950
- this._addString(doc, idx);
951
- } else {
952
- this._addObject(doc, idx);
1324
+ for (let j = 0; j < ring[ring.length - 1].length; j++) {
1325
+ if (ring[ring.length - 1][j] !== ring[0][j]) {
1326
+ throw new Error("First and last Position are not equivalent.");
1327
+ }
953
1328
  }
954
1329
  }
955
- // Removes the doc at the specified index of the index
956
- removeAt(idx) {
957
- this.records.splice(idx, 1);
958
- for (let i = idx, len = this.size(); i < len; i += 1) {
959
- this.records[i].i -= 1;
960
- }
1330
+ const geom = {
1331
+ type: "Polygon",
1332
+ coordinates
1333
+ };
1334
+ return feature(geom, properties, options);
1335
+ }
1336
+ function lineString(coordinates, properties, options = {}) {
1337
+ if (coordinates.length < 2) {
1338
+ throw new Error("coordinates must be an array of two or more positions");
961
1339
  }
962
- getValueForItemAtKeyId(item, keyId) {
963
- return item[this._keysMap[keyId]];
1340
+ const geom = {
1341
+ type: "LineString",
1342
+ coordinates
1343
+ };
1344
+ return feature(geom, properties, options);
1345
+ }
1346
+ function featureCollection(features, options = {}) {
1347
+ const fc = { type: "FeatureCollection" };
1348
+ if (options.id) {
1349
+ fc.id = options.id;
964
1350
  }
965
- size() {
966
- return this.records.length;
1351
+ if (options.bbox) {
1352
+ fc.bbox = options.bbox;
967
1353
  }
968
- _addString(doc, docIndex) {
969
- if (!isDefined(doc) || isBlank(doc)) {
970
- return;
1354
+ fc.features = features;
1355
+ return fc;
1356
+ }
1357
+ function isNumber(num) {
1358
+ return !isNaN(num) && num !== null && !Array.isArray(num);
1359
+ }
1360
+ function isObject(input) {
1361
+ return input !== null && typeof input === "object" && !Array.isArray(input);
1362
+ }
1363
+
1364
+ // node_modules/@turf/invariant/dist/esm/index.js
1365
+ function getCoord(coord) {
1366
+ if (!coord) {
1367
+ throw new Error("coord is required");
1368
+ }
1369
+ if (!Array.isArray(coord)) {
1370
+ if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
1371
+ return [...coord.geometry.coordinates];
1372
+ }
1373
+ if (coord.type === "Point") {
1374
+ return [...coord.coordinates];
971
1375
  }
972
- let record = {
973
- v: doc,
974
- i: docIndex,
975
- n: this.norm.get(doc)
976
- };
977
- this.records.push(record);
978
- }
979
- _addObject(doc, docIndex) {
980
- let record = { i: docIndex, $: {} };
981
- this.keys.forEach((key, keyIndex) => {
982
- let value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
983
- if (!isDefined(value)) {
984
- return;
985
- }
986
- if (isArray(value)) {
987
- let subRecords = [];
988
- const stack = [{ nestedArrIndex: -1, value }];
989
- while (stack.length) {
990
- const { nestedArrIndex, value: value2 } = stack.pop();
991
- if (!isDefined(value2)) {
992
- continue;
993
- }
994
- if (isString(value2) && !isBlank(value2)) {
995
- let subRecord = {
996
- v: value2,
997
- i: nestedArrIndex,
998
- n: this.norm.get(value2)
999
- };
1000
- subRecords.push(subRecord);
1001
- } else if (isArray(value2)) {
1002
- value2.forEach((item, k) => {
1003
- stack.push({
1004
- nestedArrIndex: k,
1005
- value: item
1006
- });
1007
- });
1008
- } else ;
1009
- }
1010
- record.$[keyIndex] = subRecords;
1011
- } else if (isString(value) && !isBlank(value)) {
1012
- let subRecord = {
1013
- v: value,
1014
- n: this.norm.get(value)
1015
- };
1016
- record.$[keyIndex] = subRecord;
1017
- }
1018
- });
1019
- this.records.push(record);
1020
1376
  }
1021
- toJSON() {
1022
- return {
1023
- keys: this.keys,
1024
- records: this.records
1025
- };
1377
+ if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
1378
+ return [...coord];
1026
1379
  }
1027
- };
1028
- function createIndex(keys, docs, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
1029
- const myIndex = new FuseIndex({ getFn, fieldNormWeight });
1030
- myIndex.setKeys(keys.map(createKey));
1031
- myIndex.setSources(docs);
1032
- myIndex.create();
1033
- return myIndex;
1380
+ throw new Error("coord must be GeoJSON Point or an Array of numbers");
1034
1381
  }
1035
- function parseIndex(data, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
1036
- const { keys, records } = data;
1037
- const myIndex = new FuseIndex({ getFn, fieldNormWeight });
1038
- myIndex.setKeys(keys);
1039
- myIndex.setIndexRecords(records);
1040
- return myIndex;
1382
+ function getGeom(geojson) {
1383
+ if (geojson.type === "Feature") {
1384
+ return geojson.geometry;
1385
+ }
1386
+ return geojson;
1041
1387
  }
1042
- function computeScore$1(pattern, {
1043
- errors = 0,
1044
- currentLocation = 0,
1045
- expectedLocation = 0,
1046
- distance = Config.distance,
1047
- ignoreLocation = Config.ignoreLocation
1048
- } = {}) {
1049
- const accuracy = errors / pattern.length;
1050
- if (ignoreLocation) {
1051
- return accuracy;
1388
+ function getType(geojson, _name) {
1389
+ if (geojson.type === "FeatureCollection") {
1390
+ return "FeatureCollection";
1052
1391
  }
1053
- const proximity = Math.abs(expectedLocation - currentLocation);
1054
- if (!distance) {
1055
- return proximity ? 1 : accuracy;
1392
+ if (geojson.type === "GeometryCollection") {
1393
+ return "GeometryCollection";
1056
1394
  }
1057
- return accuracy + proximity / distance;
1395
+ if (geojson.type === "Feature" && geojson.geometry !== null) {
1396
+ return geojson.geometry.type;
1397
+ }
1398
+ return geojson.type;
1058
1399
  }
1059
- function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) {
1060
- let indices = [];
1061
- let start = -1;
1062
- let end = -1;
1063
- let i = 0;
1064
- for (let len = matchmask.length; i < len; i += 1) {
1065
- let match = matchmask[i];
1066
- if (match && start === -1) {
1067
- start = i;
1068
- } else if (!match && start !== -1) {
1069
- end = i - 1;
1070
- if (end - start + 1 >= minMatchCharLength) {
1071
- indices.push([start, end]);
1400
+
1401
+ // src/data/navigate/steps/path/index.ts
1402
+ import difference from "@turf/difference";
1403
+ import envelope from "@turf/envelope";
1404
+ import booleanOverlap from "@turf/boolean-overlap";
1405
+ import booleanIntersects from "@turf/boolean-intersects";
1406
+
1407
+ // ../../node_modules/@turf/meta/dist/esm/index.js
1408
+ function coordEach(geojson, callback, excludeWrapCoord) {
1409
+ if (geojson === null) return;
1410
+ var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
1411
+ for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
1412
+ geometryMaybeCollection = isFeatureCollection ? geojson.features[featureIndex].geometry : isFeature ? geojson.geometry : geojson;
1413
+ isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
1414
+ stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
1415
+ for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
1416
+ var multiFeatureIndex = 0;
1417
+ var geometryIndex = 0;
1418
+ geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
1419
+ if (geometry === null) continue;
1420
+ coords = geometry.coordinates;
1421
+ var geomType = geometry.type;
1422
+ wrapShrink = excludeWrapCoord && (geomType === "Polygon" || geomType === "MultiPolygon") ? 1 : 0;
1423
+ switch (geomType) {
1424
+ case null:
1425
+ break;
1426
+ case "Point":
1427
+ if (callback(
1428
+ coords,
1429
+ coordIndex,
1430
+ featureIndex,
1431
+ multiFeatureIndex,
1432
+ geometryIndex
1433
+ ) === false)
1434
+ return false;
1435
+ coordIndex++;
1436
+ multiFeatureIndex++;
1437
+ break;
1438
+ case "LineString":
1439
+ case "MultiPoint":
1440
+ for (j = 0; j < coords.length; j++) {
1441
+ if (callback(
1442
+ coords[j],
1443
+ coordIndex,
1444
+ featureIndex,
1445
+ multiFeatureIndex,
1446
+ geometryIndex
1447
+ ) === false)
1448
+ return false;
1449
+ coordIndex++;
1450
+ if (geomType === "MultiPoint") multiFeatureIndex++;
1451
+ }
1452
+ if (geomType === "LineString") multiFeatureIndex++;
1453
+ break;
1454
+ case "Polygon":
1455
+ case "MultiLineString":
1456
+ for (j = 0; j < coords.length; j++) {
1457
+ for (k = 0; k < coords[j].length - wrapShrink; k++) {
1458
+ if (callback(
1459
+ coords[j][k],
1460
+ coordIndex,
1461
+ featureIndex,
1462
+ multiFeatureIndex,
1463
+ geometryIndex
1464
+ ) === false)
1465
+ return false;
1466
+ coordIndex++;
1467
+ }
1468
+ if (geomType === "MultiLineString") multiFeatureIndex++;
1469
+ if (geomType === "Polygon") geometryIndex++;
1470
+ }
1471
+ if (geomType === "Polygon") multiFeatureIndex++;
1472
+ break;
1473
+ case "MultiPolygon":
1474
+ for (j = 0; j < coords.length; j++) {
1475
+ geometryIndex = 0;
1476
+ for (k = 0; k < coords[j].length; k++) {
1477
+ for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
1478
+ if (callback(
1479
+ coords[j][k][l],
1480
+ coordIndex,
1481
+ featureIndex,
1482
+ multiFeatureIndex,
1483
+ geometryIndex
1484
+ ) === false)
1485
+ return false;
1486
+ coordIndex++;
1487
+ }
1488
+ geometryIndex++;
1489
+ }
1490
+ multiFeatureIndex++;
1491
+ }
1492
+ break;
1493
+ case "GeometryCollection":
1494
+ for (j = 0; j < geometry.geometries.length; j++)
1495
+ if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false)
1496
+ return false;
1497
+ break;
1498
+ default:
1499
+ throw new Error("Unknown Geometry Type");
1072
1500
  }
1073
- start = -1;
1074
1501
  }
1075
1502
  }
1076
- if (matchmask[i - 1] && i - start >= minMatchCharLength) {
1077
- indices.push([start, i - 1]);
1078
- }
1079
- return indices;
1080
1503
  }
1081
- var MAX_BITS = 32;
1082
- function search(text, pattern, patternAlphabet, {
1083
- location = Config.location,
1084
- distance = Config.distance,
1085
- threshold = Config.threshold,
1086
- findAllMatches = Config.findAllMatches,
1087
- minMatchCharLength = Config.minMatchCharLength,
1088
- includeMatches = Config.includeMatches,
1089
- ignoreLocation = Config.ignoreLocation
1090
- } = {}) {
1091
- if (pattern.length > MAX_BITS) {
1092
- throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS));
1093
- }
1094
- const patternLen = pattern.length;
1095
- const textLen = text.length;
1096
- const expectedLocation = Math.max(0, Math.min(location, textLen));
1097
- let currentThreshold = threshold;
1098
- let bestLocation = expectedLocation;
1099
- const computeMatches = minMatchCharLength > 1 || includeMatches;
1100
- const matchMask = computeMatches ? Array(textLen) : [];
1101
- let index;
1102
- while ((index = text.indexOf(pattern, bestLocation)) > -1) {
1103
- let score = computeScore$1(pattern, {
1104
- currentLocation: index,
1105
- expectedLocation,
1106
- distance,
1107
- ignoreLocation
1108
- });
1109
- currentThreshold = Math.min(score, currentThreshold);
1110
- bestLocation = index + patternLen;
1111
- if (computeMatches) {
1112
- let i = 0;
1113
- while (i < patternLen) {
1114
- matchMask[index + i] = 1;
1115
- i += 1;
1116
- }
1117
- }
1118
- }
1119
- bestLocation = -1;
1120
- let lastBitArr = [];
1121
- let finalScore = 1;
1122
- let binMax = patternLen + textLen;
1123
- const mask = 1 << patternLen - 1;
1124
- for (let i = 0; i < patternLen; i += 1) {
1125
- let binMin = 0;
1126
- let binMid = binMax;
1127
- while (binMin < binMid) {
1128
- const score2 = computeScore$1(pattern, {
1129
- errors: i,
1130
- currentLocation: expectedLocation + binMid,
1131
- expectedLocation,
1132
- distance,
1133
- ignoreLocation
1134
- });
1135
- if (score2 <= currentThreshold) {
1136
- binMin = binMid;
1137
- } else {
1138
- binMax = binMid;
1139
- }
1140
- binMid = Math.floor((binMax - binMin) / 2 + binMin);
1504
+
1505
+ // ../../node_modules/@turf/bbox/dist/esm/index.js
1506
+ function bbox(geojson, options = {}) {
1507
+ if (geojson.bbox != null && true !== options.recompute) {
1508
+ return geojson.bbox;
1509
+ }
1510
+ const result = [Infinity, Infinity, -Infinity, -Infinity];
1511
+ coordEach(geojson, (coord) => {
1512
+ if (result[0] > coord[0]) {
1513
+ result[0] = coord[0];
1141
1514
  }
1142
- binMax = binMid;
1143
- let start = Math.max(1, expectedLocation - binMid + 1);
1144
- let finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
1145
- let bitArr = Array(finish + 2);
1146
- bitArr[finish + 1] = (1 << i) - 1;
1147
- for (let j = finish; j >= start; j -= 1) {
1148
- let currentLocation = j - 1;
1149
- let charMatch = patternAlphabet[text.charAt(currentLocation)];
1150
- if (computeMatches) {
1151
- matchMask[currentLocation] = +!!charMatch;
1152
- }
1153
- bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch;
1154
- if (i) {
1155
- bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1];
1156
- }
1157
- if (bitArr[j] & mask) {
1158
- finalScore = computeScore$1(pattern, {
1159
- errors: i,
1160
- currentLocation,
1161
- expectedLocation,
1162
- distance,
1163
- ignoreLocation
1164
- });
1165
- if (finalScore <= currentThreshold) {
1166
- currentThreshold = finalScore;
1167
- bestLocation = currentLocation;
1168
- if (bestLocation <= expectedLocation) {
1169
- break;
1170
- }
1171
- start = Math.max(1, 2 * expectedLocation - bestLocation);
1172
- }
1173
- }
1515
+ if (result[1] > coord[1]) {
1516
+ result[1] = coord[1];
1174
1517
  }
1175
- const score = computeScore$1(pattern, {
1176
- errors: i + 1,
1177
- currentLocation: expectedLocation,
1178
- expectedLocation,
1179
- distance,
1180
- ignoreLocation
1181
- });
1182
- if (score > currentThreshold) {
1183
- break;
1518
+ if (result[2] < coord[0]) {
1519
+ result[2] = coord[0];
1184
1520
  }
1185
- lastBitArr = bitArr;
1186
- }
1187
- const result = {
1188
- isMatch: bestLocation >= 0,
1189
- // Count exact matches (those with a score of 0) to be "almost" exact
1190
- score: Math.max(1e-3, finalScore)
1191
- };
1192
- if (computeMatches) {
1193
- const indices = convertMaskToIndices(matchMask, minMatchCharLength);
1194
- if (!indices.length) {
1195
- result.isMatch = false;
1196
- } else if (includeMatches) {
1197
- result.indices = indices;
1521
+ if (result[3] < coord[1]) {
1522
+ result[3] = coord[1];
1198
1523
  }
1199
- }
1524
+ });
1200
1525
  return result;
1201
1526
  }
1202
- function createPatternAlphabet(pattern) {
1203
- let mask = {};
1204
- for (let i = 0, len = pattern.length; i < len; i += 1) {
1205
- const char = pattern.charAt(i);
1206
- mask[char] = (mask[char] || 0) | 1 << len - i - 1;
1207
- }
1208
- return mask;
1209
- }
1210
- var stripDiacritics = String.prototype.normalize ? ((str) => str.normalize("NFD").replace(/[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D3-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C04\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA8FF\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F]/g, "")) : ((str) => str);
1211
- var BitapSearch = class {
1212
- constructor(pattern, {
1213
- location = Config.location,
1214
- threshold = Config.threshold,
1215
- distance = Config.distance,
1216
- includeMatches = Config.includeMatches,
1217
- findAllMatches = Config.findAllMatches,
1218
- minMatchCharLength = Config.minMatchCharLength,
1219
- isCaseSensitive = Config.isCaseSensitive,
1220
- ignoreDiacritics = Config.ignoreDiacritics,
1221
- ignoreLocation = Config.ignoreLocation
1222
- } = {}) {
1223
- this.options = {
1224
- location,
1225
- threshold,
1226
- distance,
1227
- includeMatches,
1228
- findAllMatches,
1229
- minMatchCharLength,
1230
- isCaseSensitive,
1231
- ignoreDiacritics,
1232
- ignoreLocation
1233
- };
1234
- pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
1235
- pattern = ignoreDiacritics ? stripDiacritics(pattern) : pattern;
1236
- this.pattern = pattern;
1237
- this.chunks = [];
1238
- if (!this.pattern.length) {
1239
- return;
1240
- }
1241
- const addChunk = (pattern2, startIndex) => {
1242
- this.chunks.push({
1243
- pattern: pattern2,
1244
- alphabet: createPatternAlphabet(pattern2),
1245
- startIndex
1246
- });
1247
- };
1248
- const len = this.pattern.length;
1249
- if (len > MAX_BITS) {
1250
- let i = 0;
1251
- const remainder = len % MAX_BITS;
1252
- const end = len - remainder;
1253
- while (i < end) {
1254
- addChunk(this.pattern.substr(i, MAX_BITS), i);
1255
- i += MAX_BITS;
1256
- }
1257
- if (remainder) {
1258
- const startIndex = len - MAX_BITS;
1259
- addChunk(this.pattern.substr(startIndex), startIndex);
1260
- }
1261
- } else {
1262
- addChunk(this.pattern, 0);
1263
- }
1264
- }
1265
- searchIn(text) {
1266
- const { isCaseSensitive, ignoreDiacritics, includeMatches } = this.options;
1267
- text = isCaseSensitive ? text : text.toLowerCase();
1268
- text = ignoreDiacritics ? stripDiacritics(text) : text;
1269
- if (this.pattern === text) {
1270
- let result2 = {
1271
- isMatch: true,
1272
- score: 0
1273
- };
1274
- if (includeMatches) {
1275
- result2.indices = [[0, text.length - 1]];
1527
+ var index_default = bbox;
1528
+
1529
+ // src/data/navigate/steps/path/turf/shortestPath.ts
1530
+ import booleanPointInPolygon3 from "@turf/boolean-point-in-polygon";
1531
+ import distance3 from "@turf/distance";
1532
+ import scale from "@turf/transform-scale";
1533
+ import union from "@turf/union";
1534
+ import bboxPolygon from "@turf/bbox-polygon";
1535
+ import { cleanCoords } from "@turf/clean-coords";
1536
+ import PF from "pathfinding";
1537
+ import set3 from "lodash/set";
1538
+
1539
+ // src/data/navigate/steps/path/turf/stringPull.ts
1540
+ function stringPull(grid, path) {
1541
+ const isWalkable = (x, y) => grid.isInside(x, y) && grid.isWalkableAt(x, y);
1542
+ function hasLOS(a, b) {
1543
+ let x0 = a[0], y0 = a[1];
1544
+ const x1 = b[0], y1 = b[1];
1545
+ if (!isWalkable(x0, y0) || !isWalkable(x1, y1)) return false;
1546
+ const dx = Math.abs(x1 - x0);
1547
+ const dy = Math.abs(y1 - y0);
1548
+ const sx = x0 < x1 ? 1 : -1;
1549
+ const sy = y0 < y1 ? 1 : -1;
1550
+ let err = dx - dy;
1551
+ while (true) {
1552
+ if (!isWalkable(x0, y0)) return false;
1553
+ if (x0 === x1 && y0 === y1) break;
1554
+ const e2 = err * 2;
1555
+ let nx = x0;
1556
+ let ny = y0;
1557
+ let movedX = false;
1558
+ let movedY = false;
1559
+ if (e2 > -dy) {
1560
+ err -= dy;
1561
+ nx += sx;
1562
+ movedX = true;
1276
1563
  }
1277
- return result2;
1278
- }
1279
- const {
1280
- location,
1281
- distance,
1282
- threshold,
1283
- findAllMatches,
1284
- minMatchCharLength,
1285
- ignoreLocation
1286
- } = this.options;
1287
- let allIndices = [];
1288
- let totalScore = 0;
1289
- let hasMatches = false;
1290
- this.chunks.forEach(({ pattern, alphabet, startIndex }) => {
1291
- const { isMatch, score, indices } = search(text, pattern, alphabet, {
1292
- location: location + startIndex,
1293
- distance,
1294
- threshold,
1295
- findAllMatches,
1296
- minMatchCharLength,
1297
- includeMatches,
1298
- ignoreLocation
1299
- });
1300
- if (isMatch) {
1301
- hasMatches = true;
1564
+ if (e2 < dx) {
1565
+ err += dx;
1566
+ ny += sy;
1567
+ movedY = true;
1302
1568
  }
1303
- totalScore += score;
1304
- if (isMatch && indices) {
1305
- allIndices = [...allIndices, ...indices];
1569
+ if (movedX && movedY) {
1570
+ if (!isWalkable(nx, y0) || !isWalkable(x0, ny)) return false;
1306
1571
  }
1307
- });
1308
- let result = {
1309
- isMatch: hasMatches,
1310
- score: hasMatches ? totalScore / this.chunks.length : 1
1311
- };
1312
- if (hasMatches && includeMatches) {
1313
- result.indices = allIndices;
1572
+ x0 = nx;
1573
+ y0 = ny;
1314
1574
  }
1315
- return result;
1316
- }
1317
- };
1318
- var BaseMatch = class {
1319
- constructor(pattern) {
1320
- this.pattern = pattern;
1321
- }
1322
- static isMultiMatch(pattern) {
1323
- return getMatch(pattern, this.multiRegex);
1324
- }
1325
- static isSingleMatch(pattern) {
1326
- return getMatch(pattern, this.singleRegex);
1575
+ return true;
1327
1576
  }
1328
- search() {
1577
+ if (path.length <= 2) return path;
1578
+ const out = [path[0]];
1579
+ let i = 0;
1580
+ while (i < path.length - 1) {
1581
+ let best = i + 1;
1582
+ for (let j = i + 2; j < path.length; j++) {
1583
+ if (hasLOS(path[i], path[j])) best = j;
1584
+ else break;
1585
+ }
1586
+ out.push(path[best]);
1587
+ i = best;
1329
1588
  }
1330
- };
1331
- function getMatch(pattern, exp) {
1332
- const matches = pattern.match(exp);
1333
- return matches ? matches[1] : null;
1589
+ return out;
1334
1590
  }
1335
- var ExactMatch = class extends BaseMatch {
1336
- constructor(pattern) {
1337
- super(pattern);
1338
- }
1339
- static get type() {
1340
- return "exact";
1341
- }
1342
- static get multiRegex() {
1343
- return /^="(.*)"$/;
1344
- }
1345
- static get singleRegex() {
1346
- return /^=(.*)$/;
1347
- }
1348
- search(text) {
1349
- const isMatch = text === this.pattern;
1350
- return {
1351
- isMatch,
1352
- score: isMatch ? 0 : 1,
1353
- indices: [0, this.pattern.length - 1]
1354
- };
1355
- }
1356
- };
1357
- var InverseExactMatch = class extends BaseMatch {
1358
- constructor(pattern) {
1359
- super(pattern);
1360
- }
1361
- static get type() {
1362
- return "inverse-exact";
1363
- }
1364
- static get multiRegex() {
1365
- return /^!"(.*)"$/;
1366
- }
1367
- static get singleRegex() {
1368
- return /^!(.*)$/;
1369
- }
1370
- search(text) {
1371
- const index = text.indexOf(this.pattern);
1372
- const isMatch = index === -1;
1373
- return {
1374
- isMatch,
1375
- score: isMatch ? 0 : 1,
1376
- indices: [0, text.length - 1]
1377
- };
1378
- }
1379
- };
1380
- var PrefixExactMatch = class extends BaseMatch {
1381
- constructor(pattern) {
1382
- super(pattern);
1383
- }
1384
- static get type() {
1385
- return "prefix-exact";
1386
- }
1387
- static get multiRegex() {
1388
- return /^\^"(.*)"$/;
1389
- }
1390
- static get singleRegex() {
1391
- return /^\^(.*)$/;
1392
- }
1393
- search(text) {
1394
- const isMatch = text.startsWith(this.pattern);
1395
- return {
1396
- isMatch,
1397
- score: isMatch ? 0 : 1,
1398
- indices: [0, this.pattern.length - 1]
1399
- };
1400
- }
1401
- };
1402
- var InversePrefixExactMatch = class extends BaseMatch {
1403
- constructor(pattern) {
1404
- super(pattern);
1405
- }
1406
- static get type() {
1407
- return "inverse-prefix-exact";
1408
- }
1409
- static get multiRegex() {
1410
- return /^!\^"(.*)"$/;
1411
- }
1412
- static get singleRegex() {
1413
- return /^!\^(.*)$/;
1414
- }
1415
- search(text) {
1416
- const isMatch = !text.startsWith(this.pattern);
1417
- return {
1418
- isMatch,
1419
- score: isMatch ? 0 : 1,
1420
- indices: [0, text.length - 1]
1421
- };
1422
- }
1423
- };
1424
- var SuffixExactMatch = class extends BaseMatch {
1425
- constructor(pattern) {
1426
- super(pattern);
1427
- }
1428
- static get type() {
1429
- return "suffix-exact";
1430
- }
1431
- static get multiRegex() {
1432
- return /^"(.*)"\$$/;
1433
- }
1434
- static get singleRegex() {
1435
- return /^(.*)\$$/;
1436
- }
1437
- search(text) {
1438
- const isMatch = text.endsWith(this.pattern);
1439
- return {
1440
- isMatch,
1441
- score: isMatch ? 0 : 1,
1442
- indices: [text.length - this.pattern.length, text.length - 1]
1443
- };
1444
- }
1445
- };
1446
- var InverseSuffixExactMatch = class extends BaseMatch {
1447
- constructor(pattern) {
1448
- super(pattern);
1449
- }
1450
- static get type() {
1451
- return "inverse-suffix-exact";
1452
- }
1453
- static get multiRegex() {
1454
- return /^!"(.*)"\$$/;
1455
- }
1456
- static get singleRegex() {
1457
- return /^!(.*)\$$/;
1458
- }
1459
- search(text) {
1460
- const isMatch = !text.endsWith(this.pattern);
1461
- return {
1462
- isMatch,
1463
- score: isMatch ? 0 : 1,
1464
- indices: [0, text.length - 1]
1465
- };
1466
- }
1467
- };
1468
- var FuzzyMatch = class extends BaseMatch {
1469
- constructor(pattern, {
1470
- location = Config.location,
1471
- threshold = Config.threshold,
1472
- distance = Config.distance,
1473
- includeMatches = Config.includeMatches,
1474
- findAllMatches = Config.findAllMatches,
1475
- minMatchCharLength = Config.minMatchCharLength,
1476
- isCaseSensitive = Config.isCaseSensitive,
1477
- ignoreDiacritics = Config.ignoreDiacritics,
1478
- ignoreLocation = Config.ignoreLocation
1479
- } = {}) {
1480
- super(pattern);
1481
- this._bitapSearch = new BitapSearch(pattern, {
1482
- location,
1483
- threshold,
1484
- distance,
1485
- includeMatches,
1486
- findAllMatches,
1487
- minMatchCharLength,
1488
- isCaseSensitive,
1489
- ignoreDiacritics,
1490
- ignoreLocation
1491
- });
1492
- }
1493
- static get type() {
1494
- return "fuzzy";
1495
- }
1496
- static get multiRegex() {
1497
- return /^"(.*)"$/;
1498
- }
1499
- static get singleRegex() {
1500
- return /^(.*)$/;
1501
- }
1502
- search(text) {
1503
- return this._bitapSearch.searchIn(text);
1504
- }
1505
- };
1506
- var IncludeMatch = class extends BaseMatch {
1507
- constructor(pattern) {
1508
- super(pattern);
1509
- }
1510
- static get type() {
1511
- return "include";
1512
- }
1513
- static get multiRegex() {
1514
- return /^'"(.*)"$/;
1515
- }
1516
- static get singleRegex() {
1517
- return /^'(.*)$/;
1518
- }
1519
- search(text) {
1520
- let location = 0;
1521
- let index;
1522
- const indices = [];
1523
- const patternLen = this.pattern.length;
1524
- while ((index = text.indexOf(this.pattern, location)) > -1) {
1525
- location = index + patternLen;
1526
- indices.push([index, location - 1]);
1591
+
1592
+ // src/data/navigate/steps/path/turf/pruneSmallAngle.ts
1593
+ function pruneSmallAngles(path, minDeg = 10) {
1594
+ if (path.length <= 2) return path;
1595
+ const out = [path[0]];
1596
+ for (let i = 1; i < path.length - 1; i++) {
1597
+ const a = out[out.length - 1];
1598
+ const b = path[i];
1599
+ const c = path[i + 1];
1600
+ const abx = b[0] - a[0], aby = b[1] - a[1];
1601
+ const bcx = c[0] - b[0], bcy = c[1] - b[1];
1602
+ const dot = abx * bcx + aby * bcy;
1603
+ const ab = Math.hypot(abx, aby);
1604
+ const bc = Math.hypot(bcx, bcy);
1605
+ const angle = Math.acos(dot / (ab * bc)) * 180 / Math.PI;
1606
+ if (angle > minDeg) out.push(b);
1607
+ }
1608
+ out.push(path[path.length - 1]);
1609
+ return out;
1610
+ }
1611
+
1612
+ // src/data/navigate/steps/path/turf/pruneShortSegments.ts
1613
+ function pruneShortSegments(path, minLen = 5) {
1614
+ const out = [path[0]];
1615
+ for (let i = 1; i < path.length; i++) {
1616
+ const [x0, y0] = out[out.length - 1];
1617
+ const [x1, y1] = path[i];
1618
+ if (Math.hypot(x1 - x0, y1 - y0) >= minLen) {
1619
+ out.push(path[i]);
1527
1620
  }
1528
- const isMatch = !!indices.length;
1529
- return {
1530
- isMatch,
1531
- score: isMatch ? 0 : 1,
1532
- indices
1533
- };
1534
1621
  }
1535
- };
1536
- var searchers = [
1537
- ExactMatch,
1538
- IncludeMatch,
1539
- PrefixExactMatch,
1540
- InversePrefixExactMatch,
1541
- InverseSuffixExactMatch,
1542
- SuffixExactMatch,
1543
- InverseExactMatch,
1544
- FuzzyMatch
1545
- ];
1546
- var searchersLen = searchers.length;
1547
- var SPACE_RE = / +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/;
1548
- var OR_TOKEN = "|";
1549
- function parseQuery(pattern, options = {}) {
1550
- return pattern.split(OR_TOKEN).map((item) => {
1551
- let query = item.trim().split(SPACE_RE).filter((item2) => item2 && !!item2.trim());
1552
- let results = [];
1553
- for (let i = 0, len = query.length; i < len; i += 1) {
1554
- const queryItem = query[i];
1555
- let found = false;
1556
- let idx = -1;
1557
- while (!found && ++idx < searchersLen) {
1558
- const searcher = searchers[idx];
1559
- let token = searcher.isMultiMatch(queryItem);
1560
- if (token) {
1561
- results.push(new searcher(token, options));
1562
- found = true;
1563
- }
1564
- }
1565
- if (found) {
1566
- continue;
1567
- }
1568
- idx = -1;
1569
- while (++idx < searchersLen) {
1570
- const searcher = searchers[idx];
1571
- let token = searcher.isSingleMatch(queryItem);
1572
- if (token) {
1573
- results.push(new searcher(token, options));
1574
- break;
1575
- }
1576
- }
1577
- }
1578
- return results;
1579
- });
1622
+ return out;
1580
1623
  }
1581
- var MultiMatchSet = /* @__PURE__ */ new Set([FuzzyMatch.type, IncludeMatch.type]);
1582
- var ExtendedSearch = class {
1583
- constructor(pattern, {
1584
- isCaseSensitive = Config.isCaseSensitive,
1585
- ignoreDiacritics = Config.ignoreDiacritics,
1586
- includeMatches = Config.includeMatches,
1587
- minMatchCharLength = Config.minMatchCharLength,
1588
- ignoreLocation = Config.ignoreLocation,
1589
- findAllMatches = Config.findAllMatches,
1590
- location = Config.location,
1591
- threshold = Config.threshold,
1592
- distance = Config.distance
1593
- } = {}) {
1594
- this.query = null;
1595
- this.options = {
1596
- isCaseSensitive,
1597
- ignoreDiacritics,
1598
- includeMatches,
1599
- minMatchCharLength,
1600
- findAllMatches,
1601
- ignoreLocation,
1602
- location,
1603
- threshold,
1604
- distance
1605
- };
1606
- pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
1607
- pattern = ignoreDiacritics ? stripDiacritics(pattern) : pattern;
1608
- this.pattern = pattern;
1609
- this.query = parseQuery(this.pattern, this.options);
1610
- }
1611
- static condition(_, options) {
1612
- return options.useExtendedSearch;
1613
- }
1614
- searchIn(text) {
1615
- const query = this.query;
1616
- if (!query) {
1617
- return {
1618
- isMatch: false,
1619
- score: 1
1620
- };
1621
- }
1622
- const { includeMatches, isCaseSensitive, ignoreDiacritics } = this.options;
1623
- text = isCaseSensitive ? text : text.toLowerCase();
1624
- text = ignoreDiacritics ? stripDiacritics(text) : text;
1625
- let numMatches = 0;
1626
- let allIndices = [];
1627
- let totalScore = 0;
1628
- for (let i = 0, qLen = query.length; i < qLen; i += 1) {
1629
- const searchers2 = query[i];
1630
- allIndices.length = 0;
1631
- numMatches = 0;
1632
- for (let j = 0, pLen = searchers2.length; j < pLen; j += 1) {
1633
- const searcher = searchers2[j];
1634
- const { isMatch, indices, score } = searcher.search(text);
1635
- if (isMatch) {
1636
- numMatches += 1;
1637
- totalScore += score;
1638
- if (includeMatches) {
1639
- const type = searcher.constructor.type;
1640
- if (MultiMatchSet.has(type)) {
1641
- allIndices = [...allIndices, ...indices];
1642
- } else {
1643
- allIndices.push(indices);
1644
- }
1645
- }
1646
- } else {
1647
- totalScore = 0;
1648
- numMatches = 0;
1649
- allIndices.length = 0;
1650
- break;
1651
- }
1652
- }
1653
- if (numMatches) {
1654
- let result = {
1655
- isMatch: true,
1656
- score: totalScore / numMatches
1657
- };
1658
- if (includeMatches) {
1659
- result.indices = allIndices;
1660
- }
1661
- return result;
1624
+
1625
+ // src/data/navigate/steps/path/turf/clearance.ts
1626
+ function buildClearanceGrid(matrix) {
1627
+ const h = matrix.length;
1628
+ const w = matrix[0].length;
1629
+ const INF = 1e9;
1630
+ const dist = Array.from({ length: h }, () => Array(w).fill(INF));
1631
+ const q = [];
1632
+ for (let y = 0; y < h; y++) {
1633
+ for (let x = 0; x < w; x++) {
1634
+ if (matrix[y][x] === 1) {
1635
+ dist[y][x] = 0;
1636
+ q.push([x, y]);
1662
1637
  }
1663
1638
  }
1664
- return {
1665
- isMatch: false,
1666
- score: 1
1667
- };
1668
1639
  }
1669
- };
1670
- var registeredSearchers = [];
1671
- function register(...args) {
1672
- registeredSearchers.push(...args);
1673
- }
1674
- function createSearcher(pattern, options) {
1675
- for (let i = 0, len = registeredSearchers.length; i < len; i += 1) {
1676
- let searcherClass = registeredSearchers[i];
1677
- if (searcherClass.condition(pattern, options)) {
1678
- return new searcherClass(pattern, options);
1640
+ const dirs = [
1641
+ [1, 0],
1642
+ [-1, 0],
1643
+ [0, 1],
1644
+ [0, -1],
1645
+ [1, 1],
1646
+ [1, -1],
1647
+ [-1, 1],
1648
+ [-1, -1]
1649
+ ];
1650
+ let qi = 0;
1651
+ while (qi < q.length) {
1652
+ const [x, y] = q[qi++];
1653
+ const d0 = dist[y][x];
1654
+ for (const [dx, dy] of dirs) {
1655
+ const nx = x + dx, ny = y + dy;
1656
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
1657
+ const nd = d0 + 1;
1658
+ if (nd < dist[ny][nx]) {
1659
+ dist[ny][nx] = nd;
1660
+ q.push([nx, ny]);
1661
+ }
1679
1662
  }
1680
1663
  }
1681
- return new BitapSearch(pattern, options);
1664
+ return dist;
1682
1665
  }
1683
- var LogicalOperator = {
1684
- AND: "$and",
1685
- OR: "$or"
1686
- };
1687
- var KeyType = {
1688
- PATH: "$path",
1689
- PATTERN: "$val"
1690
- };
1691
- var isExpression = (query) => !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]);
1692
- var isPath = (query) => !!query[KeyType.PATH];
1693
- var isLeaf = (query) => !isArray(query) && isObject(query) && !isExpression(query);
1694
- var convertToExplicit = (query) => ({
1695
- [LogicalOperator.AND]: Object.keys(query).map((key) => ({
1696
- [key]: query[key]
1697
- }))
1698
- });
1699
- function parse(query, options, { auto = true } = {}) {
1700
- const next = (query2) => {
1701
- let keys = Object.keys(query2);
1702
- const isQueryPath = isPath(query2);
1703
- if (!isQueryPath && keys.length > 1 && !isExpression(query2)) {
1704
- return next(convertToExplicit(query2));
1705
- }
1706
- if (isLeaf(query2)) {
1707
- const key = isQueryPath ? query2[KeyType.PATH] : keys[0];
1708
- const pattern = isQueryPath ? query2[KeyType.PATTERN] : query2[key];
1709
- if (!isString(pattern)) {
1710
- throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key));
1711
- }
1712
- const obj = {
1713
- keyId: createKeyId(key),
1714
- pattern
1715
- };
1716
- if (auto) {
1717
- obj.searcher = createSearcher(pattern, options);
1666
+ function snapPointToClearancePeak(p, clearance, isWalkableCell, radius = 4) {
1667
+ const [px, py] = p;
1668
+ let best = p;
1669
+ let bestScore = clearance[py]?.[px] ?? -Infinity;
1670
+ for (let dy = -radius; dy <= radius; dy++) {
1671
+ for (let dx = -radius; dx <= radius; dx++) {
1672
+ const x = px + dx;
1673
+ const y = py + dy;
1674
+ if (!isWalkableCell(x, y)) continue;
1675
+ const score = clearance[y][x];
1676
+ const penalty = Math.hypot(dx, dy) * 5e-3;
1677
+ const finalScore = score - penalty;
1678
+ if (finalScore > bestScore) {
1679
+ bestScore = finalScore;
1680
+ best = [x, y];
1718
1681
  }
1719
- return obj;
1720
1682
  }
1721
- let node = {
1722
- children: [],
1723
- operator: keys[0]
1724
- };
1725
- keys.forEach((key) => {
1726
- const value = query2[key];
1727
- if (isArray(value)) {
1728
- value.forEach((item) => {
1729
- node.children.push(next(item));
1730
- });
1731
- }
1732
- });
1733
- return node;
1734
- };
1735
- if (!isExpression(query)) {
1736
- query = convertToExplicit(query);
1737
1683
  }
1738
- return next(query);
1684
+ return best;
1739
1685
  }
1740
- function computeScore(results, { ignoreFieldNorm = Config.ignoreFieldNorm }) {
1741
- results.forEach((result) => {
1742
- let totalScore = 1;
1743
- result.matches.forEach(({ key, norm: norm2, score }) => {
1744
- const weight = key ? key.weight : null;
1745
- totalScore *= Math.pow(
1746
- score === 0 && weight ? Number.EPSILON : score,
1747
- (weight || 1) * (ignoreFieldNorm ? 1 : norm2)
1748
- );
1749
- });
1750
- result.score = totalScore;
1751
- });
1686
+ function centerlineSnapPath(path, clearance, isWalkableCell, radius = 4) {
1687
+ const snapped = path.map((p) => snapPointToClearancePeak(p, clearance, isWalkableCell, radius));
1688
+ return snapped;
1752
1689
  }
1753
- function transformMatches(result, data) {
1754
- const matches = result.matches;
1755
- data.matches = [];
1756
- if (!isDefined(matches)) {
1757
- return;
1690
+
1691
+ // src/data/navigate/steps/path/turf/shortestPath.ts
1692
+ function shortestPath(start, end, options) {
1693
+ options = options || {};
1694
+ if (!isObject(options)) throw new Error("options is invalid");
1695
+ let resolution = options.resolution;
1696
+ const smoothenPath = options.smoothenPath;
1697
+ let obstacles = options.obstacles || featureCollection([]);
1698
+ if (!start) throw new Error("start is required");
1699
+ if (!end) throw new Error("end is required");
1700
+ if (resolution && !isNumber(resolution) || resolution <= 0)
1701
+ throw new Error("options.resolution must be a number, greater than 0");
1702
+ const startCoord = getCoord(start);
1703
+ const endCoord = getCoord(end);
1704
+ start = point(startCoord);
1705
+ end = point(endCoord);
1706
+ switch (getType(obstacles)) {
1707
+ case "FeatureCollection":
1708
+ if (obstacles.features.length === 0)
1709
+ return lineString([startCoord, endCoord]);
1710
+ break;
1711
+ case "Polygon":
1712
+ obstacles = featureCollection([feature(getGeom(obstacles))]);
1713
+ break;
1714
+ default:
1715
+ throw new Error("invalid obstacles");
1716
+ }
1717
+ const collection = obstacles;
1718
+ collection.features.push(start, end);
1719
+ const box = index_default(scale(bboxPolygon(index_default(collection)), 1.15));
1720
+ if (!resolution) {
1721
+ const width = distance3([box[0], box[1]], [box[2], box[1]], options);
1722
+ resolution = width / 100;
1723
+ }
1724
+ collection.features.pop();
1725
+ collection.features.pop();
1726
+ const [west, south, east, north] = box;
1727
+ const xFraction = resolution / distance3([west, south], [east, south], options);
1728
+ const cellWidth = xFraction * (east - west);
1729
+ const yFraction = resolution / distance3([west, south], [west, north], options);
1730
+ const cellHeight = yFraction * (north - south);
1731
+ const bboxHorizontalSide = east - west;
1732
+ const bboxVerticalSide = north - south;
1733
+ const columns = Math.floor(bboxHorizontalSide / cellWidth);
1734
+ const rows = Math.floor(bboxVerticalSide / cellHeight);
1735
+ const deltaX = (bboxHorizontalSide - columns * cellWidth) / 2;
1736
+ const deltaY = (bboxVerticalSide - rows * cellHeight) / 2;
1737
+ let closestToStart = null, closestToEnd = null, minDistStart = Infinity, minDistEnd = Infinity, currentY = north - deltaY, currentX = west + deltaX, row = 0, column = 0, distStart, distEnd, pt, isInsideObstacle;
1738
+ const roundLoopY = Math.ceil((currentY - south) / cellHeight);
1739
+ const roundLoopX = Math.ceil((east - currentX) / cellWidth);
1740
+ let totalRounds = roundLoopX * roundLoopY;
1741
+ const pointMatrix = [];
1742
+ const matrix = [];
1743
+ const obstacleTotal = collection.features.length;
1744
+ const obstacleFeatures = collection.features;
1745
+ let combinedObstacle = obstacleFeatures[0];
1746
+ let obstacleIndex = 0;
1747
+ for (obstacleIndex = 0; obstacleIndex < obstacleTotal; obstacleIndex++) {
1748
+ const nextObstacleFeature = obstacleFeatures[obstacleIndex + 1];
1749
+ if (!nextObstacleFeature) continue;
1750
+ try {
1751
+ combinedObstacle = union(
1752
+ featureCollection([combinedObstacle, nextObstacleFeature])
1753
+ );
1754
+ } catch (e) {
1755
+ console.log(e);
1756
+ }
1758
1757
  }
1759
- matches.forEach((match) => {
1760
- if (!isDefined(match.indices) || !match.indices.length) {
1761
- return;
1758
+ while (totalRounds--) {
1759
+ pt = point([currentX, currentY]);
1760
+ isInsideObstacle = booleanPointInPolygon3(pt, combinedObstacle);
1761
+ set3(matrix, `[${row}][${column}]`, isInsideObstacle ? 1 : 0);
1762
+ set3(pointMatrix, `[${row}][${column}]`, `${currentX}|${currentY}`);
1763
+ distStart = distance3(pt, start);
1764
+ if (!isInsideObstacle && distStart < minDistStart) {
1765
+ minDistStart = distStart;
1766
+ closestToStart = { x: column, y: row };
1762
1767
  }
1763
- const { indices, value } = match;
1764
- let obj = {
1765
- indices,
1766
- value
1767
- };
1768
- if (match.key) {
1769
- obj.key = match.key.src;
1768
+ distEnd = distance3(pt, end);
1769
+ if (!isInsideObstacle && distEnd < minDistEnd) {
1770
+ minDistEnd = distEnd;
1771
+ closestToEnd = { x: column, y: row };
1770
1772
  }
1771
- if (match.idx > -1) {
1772
- obj.refIndex = match.idx;
1773
+ if (column < roundLoopX) {
1774
+ currentX += cellWidth;
1775
+ column++;
1776
+ continue;
1773
1777
  }
1774
- data.matches.push(obj);
1775
- });
1776
- }
1777
- function transformScore(result, data) {
1778
- data.score = result.score;
1779
- }
1780
- function format(results, docs, {
1781
- includeMatches = Config.includeMatches,
1782
- includeScore = Config.includeScore
1783
- } = {}) {
1784
- const transformers = [];
1785
- if (includeMatches) transformers.push(transformMatches);
1786
- if (includeScore) transformers.push(transformScore);
1787
- return results.map((result) => {
1788
- const { idx } = result;
1789
- const data = {
1790
- item: docs[idx],
1791
- refIndex: idx
1792
- };
1793
- if (transformers.length) {
1794
- transformers.forEach((transformer) => {
1795
- transformer(result, data);
1796
- });
1778
+ if (row < roundLoopY) {
1779
+ currentY -= cellHeight;
1780
+ currentX = west + deltaX;
1781
+ column = 0;
1782
+ row++;
1797
1783
  }
1798
- return data;
1784
+ }
1785
+ const finder = new PF.AStarFinder({
1786
+ allowDiagonal: true,
1787
+ dontCrossCorners: true,
1788
+ heuristic: PF.Heuristic.euclidean
1789
+ });
1790
+ const grid = new PF.Grid(matrix);
1791
+ const startOnMatrix = [closestToStart.x, closestToStart.y];
1792
+ const endOnMatrix = [closestToEnd.x, closestToEnd.y];
1793
+ let result = finder.findPath(...startOnMatrix, ...endOnMatrix, grid);
1794
+ if (result.length > 0) {
1795
+ result = stringPull(grid, result);
1796
+ const clearanceGrid = buildClearanceGrid(matrix);
1797
+ const isWalkable = (x, y) => grid.isInside(x, y) && grid.isWalkableAt(x, y);
1798
+ result = centerlineSnapPath(result, clearanceGrid, isWalkable);
1799
+ result = stringPull(grid, result);
1800
+ result = pruneSmallAngles(result);
1801
+ result = pruneShortSegments(result);
1802
+ }
1803
+ result.pop();
1804
+ result.shift();
1805
+ const path = [startCoord];
1806
+ result.forEach((coord) => {
1807
+ const coords = pointMatrix[coord[1]][coord[0]].split("|");
1808
+ path.push([+coords[0], +coords[1]]);
1799
1809
  });
1810
+ path.push(endCoord);
1811
+ return cleanCoords(lineString(path));
1800
1812
  }
1801
- var Fuse = class {
1802
- constructor(docs, options = {}, index) {
1803
- this.options = { ...Config, ...options };
1804
- if (this.options.useExtendedSearch && false) {
1805
- throw new Error(EXTENDED_SEARCH_UNAVAILABLE);
1806
- }
1807
- this._keyStore = new KeyStore(this.options.keys);
1808
- this.setCollection(docs, index);
1809
- }
1810
- setCollection(docs, index) {
1811
- this._docs = docs;
1812
- if (index && !(index instanceof FuseIndex)) {
1813
- throw new Error(INCORRECT_INDEX_TYPE);
1814
- }
1815
- this._myIndex = index || createIndex(this.options.keys, this._docs, {
1816
- getFn: this.options.getFn,
1817
- fieldNormWeight: this.options.fieldNormWeight
1813
+ var shortestPath_default = shortestPath;
1814
+
1815
+ // src/data/navigate/steps/path/index.ts
1816
+ var createStepPathUtils = (options) => {
1817
+ const resolution = options.resolution ?? 88e-5;
1818
+ const { units, kiosks, fixtures } = options.data;
1819
+ const possibleObstacleFeatures = [...units, ...kiosks, ...fixtures];
1820
+ const filterObstaclesByOrdinal = (levelId) => {
1821
+ return possibleObstacleFeatures.filter(({ feature_type, properties, geometry }) => {
1822
+ return properties.level_id === levelId && ["Polygon", "MultiPolygon"].includes(geometry.type) && (OBSTACLE_FEATURE_TYPES.includes(feature_type) || "category" in properties && OBSTACLE_CATEGORIES.includes(properties.category));
1818
1823
  });
1819
- }
1820
- add(doc) {
1821
- if (!isDefined(doc)) {
1822
- return;
1824
+ };
1825
+ const findObstaclesFromWalkway = (intermediaryUnit, exceptionIds = []) => {
1826
+ const result = featureCollection([]);
1827
+ if (!intermediaryUnit) return result;
1828
+ const walkwayLevelId = intermediaryUnit.properties.level_id;
1829
+ const obstacleOnLevel = filterObstaclesByOrdinal(walkwayLevelId).filter(
1830
+ (obstacle) => !exceptionIds.includes(obstacle.id)
1831
+ );
1832
+ const relatedObstacleWithIntermediary = obstacleOnLevel.reduce(
1833
+ (obstacles, feature2) => {
1834
+ if (
1835
+ // Prevent detecting itself as an obstacle
1836
+ // Ex. Unable to draw a line to amenity located with in a room as room is also consider as obstacle
1837
+ feature2.id !== intermediaryUnit.id && (booleanOverlap(intermediaryUnit, feature2) || booleanIntersects(intermediaryUnit, feature2))
1838
+ ) {
1839
+ const polygons = getType(feature2) === "Polygon" ? [polygon(feature2.geometry.coordinates, { id: feature2.id })] : feature2.geometry.coordinates.map((ring) => polygon(ring, { id: feature2.id }));
1840
+ obstacles.push(...polygons);
1841
+ }
1842
+ return obstacles;
1843
+ },
1844
+ []
1845
+ );
1846
+ const intermediaryExtends = envelope(intermediaryUnit);
1847
+ const walkwayPerimeter = difference(
1848
+ featureCollection([intermediaryExtends, intermediaryUnit])
1849
+ );
1850
+ result.features.push(...relatedObstacleWithIntermediary, walkwayPerimeter);
1851
+ return result;
1852
+ };
1853
+ const findPathOnArea = (originPoint, destinationPoint, options2) => {
1854
+ const { obstacles = featureCollection([]), resolution: resolution2, properties } = options2 || {};
1855
+ const stepPath = shortestPath_default(originPoint, destinationPoint, {
1856
+ obstacles,
1857
+ smoothenPath: false,
1858
+ resolution: resolution2
1859
+ });
1860
+ stepPath.properties = properties;
1861
+ return stepPath;
1862
+ };
1863
+ const findStepPath = (from, to, intermediaries) => {
1864
+ const t0 = performance.now();
1865
+ const relatedWalkablePlatform = intermediaries.find(
1866
+ (feature2) => WALKABLE_CATEGORY.includes(feature2.properties.category)
1867
+ );
1868
+ const exceptionFeatureIds = [];
1869
+ const obstacles = findObstaclesFromWalkway(
1870
+ relatedWalkablePlatform,
1871
+ _7.compact(exceptionFeatureIds)
1872
+ );
1873
+ const line = findPathOnArea(from, to, {
1874
+ resolution,
1875
+ obstacles
1876
+ });
1877
+ return line.geometry.coordinates;
1878
+ };
1879
+ return {
1880
+ findStepPath
1881
+ };
1882
+ };
1883
+
1884
+ // src/data/navigate/landmark/createLandmarkUtils.ts
1885
+ import { center as center3 } from "@turf/center";
1886
+ import distance4 from "@turf/distance";
1887
+ var NEARBY_DISTANCE = 30;
1888
+ var createLandmarkUtils = (options) => {
1889
+ const { data, findByIdSync } = options;
1890
+ const { occupants } = data;
1891
+ const occupantToLandmark = (occupant) => {
1892
+ const locationType = occupant.properties.unit_id ? "unit" : "kiosk";
1893
+ const locationId = locationType === "unit" ? occupant.properties.unit_id : occupant.properties.kiosk_id;
1894
+ const location = locationType === "unit" ? findByIdSync(locationId) : findByIdSync(locationId);
1895
+ const level = findByIdSync(location.properties.level_id);
1896
+ return {
1897
+ name: occupant.properties.name,
1898
+ point: center3(location.geometry).geometry.coordinates,
1899
+ level_id: location.properties.level_id,
1900
+ is_priority: occupant.properties.is_landmark,
1901
+ ordinal: level.properties.ordinal
1902
+ };
1903
+ };
1904
+ const landmarks = [
1905
+ ...occupants.map(occupantToLandmark)
1906
+ ];
1907
+ const findNearbyLandmarks = (point2, levelId) => {
1908
+ if (point2 === null || levelId === null) return [];
1909
+ return landmarks.map((landmark) => {
1910
+ const landmarkAndDistance = {
1911
+ landmark,
1912
+ d: distance4(point2, landmark.point, { units: "meters" })
1913
+ };
1914
+ return landmarkAndDistance;
1915
+ }).filter(({ landmark, d }) => d <= NEARBY_DISTANCE && landmark.level_id === levelId).sort((a, b) => {
1916
+ const aPriority = a.landmark.is_priority ? 0 : 1;
1917
+ const bPriority = b.landmark.is_priority ? 0 : 1;
1918
+ if (aPriority !== bPriority) return aPriority - bPriority;
1919
+ return a.d - b.d;
1920
+ }).map(({ landmark }) => landmark);
1921
+ };
1922
+ const findNearestLandmark = (point2, levelId) => {
1923
+ const nearbyLandmarks = findNearbyLandmarks(point2, levelId);
1924
+ const nearestLandmark = nearbyLandmarks.length > 0 ? nearbyLandmarks[0] : null;
1925
+ return nearestLandmark;
1926
+ };
1927
+ return { findNearbyLandmarks, findNearestLandmark };
1928
+ };
1929
+
1930
+ // src/data/navigate/steps/utils/extractStartPoint.ts
1931
+ import { center as center4 } from "@turf/center";
1932
+
1933
+ // src/data/navigate/steps/utils/featureIdGuard.ts
1934
+ var isOccupant = (id) => !!id && id.startsWith("occupant-");
1935
+ var isUnit = (id) => !!id && id.startsWith("unit-");
1936
+ var isKiosk = (id) => !!id && id.startsWith("kiosk-");
1937
+ var isOpening = (id) => !!id && id.startsWith("opening-");
1938
+
1939
+ // src/data/navigate/type-guard.ts
1940
+ function isCoordinateOrdinalString(id) {
1941
+ return /^-?\d+(\.\d+)?,-?\d+(\.\d+)?,-?\d+(\.\d+)?o$/.test(id);
1942
+ }
1943
+
1944
+ // src/data/navigate/steps/utils/extractStartPoint.ts
1945
+ var extractStartPoint = (path, options) => {
1946
+ const { findByIdSync } = options;
1947
+ const [a, b, c] = path;
1948
+ if (isOccupant(a) && isUnit(b) && isOpening(c)) {
1949
+ const occ = findByIdSync(a);
1950
+ const opening = findByIdSync(c);
1951
+ const level = findByIdSync(opening.properties.level_id);
1952
+ return [
1953
+ {
1954
+ id: occ.id,
1955
+ type: "start",
1956
+ name: occ.properties.name,
1957
+ point: center4(opening).geometry.coordinates,
1958
+ levelId: opening.properties.level_id,
1959
+ ordinal: level.properties.ordinal,
1960
+ source: { type: "opening", id: opening.id }
1961
+ },
1962
+ path.slice(3)
1963
+ ];
1964
+ }
1965
+ if (isOccupant(a) && isKiosk(b)) {
1966
+ const occ = findByIdSync(a);
1967
+ const kiosk = findByIdSync(c);
1968
+ const level = findByIdSync(kiosk.properties.level_id);
1969
+ return [
1970
+ {
1971
+ id: occ.id,
1972
+ type: "start",
1973
+ name: occ.properties.name,
1974
+ point: center4(kiosk).geometry.coordinates,
1975
+ levelId: kiosk.properties.level_id,
1976
+ ordinal: level.properties.ordinal,
1977
+ source: { type: "kiosk", id: kiosk.id }
1978
+ },
1979
+ path.slice(2)
1980
+ ];
1981
+ }
1982
+ if (isCoordinateOrdinalString(a) && isOpening(b)) {
1983
+ const [lat, lng, ordinal] = parseOrdinalCoordinate(a);
1984
+ const opening = findByIdSync(b);
1985
+ return [
1986
+ {
1987
+ id: a,
1988
+ type: "start",
1989
+ name: { en: `Your location` },
1990
+ point: [lng, lat],
1991
+ levelId: opening.properties.level_id,
1992
+ ordinal,
1993
+ source: { type: "opening", id: opening.id }
1994
+ },
1995
+ path.slice(1)
1996
+ ];
1997
+ }
1998
+ return [null, path];
1999
+ };
2000
+
2001
+ // src/data/navigate/steps/utils/extractEndPint.ts
2002
+ import { center as center5 } from "@turf/center";
2003
+ var extractEndPoint = (path, options) => {
2004
+ const { findByIdSync } = options;
2005
+ const [c, b, a] = path.slice(-3);
2006
+ if (isOccupant(a) && isUnit(b) && isOpening(c)) {
2007
+ const occ = findByIdSync(a);
2008
+ const opening = findByIdSync(c);
2009
+ const level = findByIdSync(opening.properties.level_id);
2010
+ return [
2011
+ {
2012
+ id: occ.id,
2013
+ type: "end",
2014
+ name: occ.properties.name,
2015
+ point: center5(opening).geometry.coordinates,
2016
+ levelId: opening.properties.level_id,
2017
+ ordinal: level.properties.ordinal,
2018
+ source: { type: "opening", id: opening.id }
2019
+ },
2020
+ path.slice(0, -3)
2021
+ ];
2022
+ }
2023
+ if (isOccupant(a) && isKiosk(b)) {
2024
+ const occ = findByIdSync(a);
2025
+ const kiosk = findByIdSync(c);
2026
+ const level = findByIdSync(kiosk.properties.level_id);
2027
+ return [
2028
+ {
2029
+ id: occ.id,
2030
+ type: "end",
2031
+ name: occ.properties.name,
2032
+ point: center5(kiosk).geometry.coordinates,
2033
+ levelId: kiosk.properties.level_id,
2034
+ ordinal: level.properties.ordinal,
2035
+ source: { type: "kiosk", id: kiosk.id }
2036
+ },
2037
+ path.slice(0, -2)
2038
+ ];
2039
+ }
2040
+ if (isCoordinateOrdinalString(a)) {
2041
+ const [lat, lng, ordinal] = parseOrdinalCoordinate(a);
2042
+ const opening = findByIdSync(b);
2043
+ return [
2044
+ {
2045
+ id: a,
2046
+ type: "end",
2047
+ name: { en: `Your location` },
2048
+ point: [lng, lat],
2049
+ levelId: opening.properties.level_id,
2050
+ ordinal,
2051
+ source: { type: "opening", id: opening.id }
2052
+ },
2053
+ path.slice(0, -2)
2054
+ ];
2055
+ }
2056
+ return [null, path];
2057
+ };
2058
+
2059
+ // src/data/navigate/steps/utils/combineWalkwaySteps.ts
2060
+ import uniq from "lodash/uniq";
2061
+ var combineWalkwaySteps = (steps) => {
2062
+ let result = [];
2063
+ for (let i = 0; i < steps.length; i++) {
2064
+ const thisStep = steps[i];
2065
+ if (i === steps.length - 1) {
2066
+ result.push(thisStep);
2067
+ continue;
1823
2068
  }
1824
- this._docs.push(doc);
1825
- this._myIndex.add(doc);
1826
- }
1827
- remove(predicate = () => false) {
1828
- const results = [];
1829
- for (let i = 0, len = this._docs.length; i < len; i += 1) {
1830
- const doc = this._docs[i];
1831
- if (predicate(doc, i)) {
1832
- this.removeAt(i);
1833
- i -= 1;
1834
- len -= 1;
1835
- results.push(doc);
1836
- }
2069
+ const nextStep = steps[i + 1];
2070
+ if (thisStep.intermediaryCategory === "walkway" && nextStep.intermediaryCategory === "walkway" && thisStep.from.source.type === "opening" && nextStep.from.source.type === "opening") {
2071
+ console.log({ i, len: steps.length, thisStep, nextStep });
2072
+ result.push({
2073
+ from: thisStep.from,
2074
+ to: nextStep.to,
2075
+ levelIds: uniq([...thisStep.levelIds, ...nextStep.levelIds]),
2076
+ ordinals: uniq([...thisStep.ordinals, ...nextStep.ordinals]),
2077
+ intermediaryCategory: "walkway",
2078
+ description: nextStep.description,
2079
+ path: [
2080
+ ...thisStep.path,
2081
+ ...nextStep.path
2082
+ ]
2083
+ });
2084
+ i++;
2085
+ } else {
2086
+ result.push(thisStep);
1837
2087
  }
1838
- return results;
1839
2088
  }
1840
- removeAt(idx) {
1841
- this._docs.splice(idx, 1);
1842
- this._myIndex.removeAt(idx);
1843
- }
1844
- getIndex() {
1845
- return this._myIndex;
1846
- }
1847
- search(query, { limit = -1 } = {}) {
1848
- const {
1849
- includeMatches,
1850
- includeScore,
1851
- shouldSort,
1852
- sortFn,
1853
- ignoreFieldNorm
1854
- } = this.options;
1855
- let results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query);
1856
- computeScore(results, { ignoreFieldNorm });
1857
- if (shouldSort) {
1858
- results.sort(sortFn);
2089
+ return result;
2090
+ };
2091
+
2092
+ // src/data/navigate/steps/createStepUtils.ts
2093
+ var createStepUtils = (options) => {
2094
+ const { data: { units, relationships }, findByIdSync } = options;
2095
+ const landmarkUtils = createLandmarkUtils(options);
2096
+ const stepPathUtils = createStepPathUtils({ ...options, resolution: 88e-5 });
2097
+ const findUnitBetweenOpenings = (originId, destinationId) => {
2098
+ const origin = findByIdSync(originId);
2099
+ const destination = findByIdSync(destinationId);
2100
+ const matchedOne = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(origin.id));
2101
+ const matchOneUnits = [matchedOne.properties.origin, matchedOne.properties.destination];
2102
+ const matchedTwo = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(destination.id));
2103
+ const matchTwoUnits = [matchedTwo.properties.origin, matchedTwo.properties.destination];
2104
+ const unitIds = _intersectionBy(matchOneUnits, matchTwoUnits, "id");
2105
+ return unitIds.map(({ id }) => findByIdSync(id));
2106
+ };
2107
+ const findHorizontalIntermediary = (from, to) => {
2108
+ if (from.source.type !== "opening") {
2109
+ const unit = findContainingUnitAtPoint(from.point, from.levelId, units);
2110
+ return unit ? [unit] : [];
1859
2111
  }
1860
- if (isNumber(limit) && limit > -1) {
1861
- results = results.slice(0, limit);
2112
+ if (to.source.type !== "opening") {
2113
+ const unit = findContainingUnitAtPoint(to.point, to.levelId, units);
2114
+ return unit ? [unit] : [];
1862
2115
  }
1863
- return format(results, this._docs, {
1864
- includeMatches,
1865
- includeScore
2116
+ return findUnitBetweenOpenings(from.source.id, to.source.id);
2117
+ };
2118
+ const findVerticalIntermediary = (from, to) => {
2119
+ const firstOpeningId = from.source.id;
2120
+ const secondOpeningId = to.source.id;
2121
+ const relationship = relationships.find((rel) => {
2122
+ return rel.properties.origin?.id === firstOpeningId && rel.properties.destination?.id === secondOpeningId || rel.properties.origin?.id === secondOpeningId && rel.properties.destination?.id === firstOpeningId;
1866
2123
  });
1867
- }
1868
- _searchStringList(query) {
1869
- const searcher = createSearcher(query, this.options);
1870
- const { records } = this._myIndex;
1871
- const results = [];
1872
- records.forEach(({ v: text, i: idx, n: norm2 }) => {
1873
- if (!isDefined(text)) {
1874
- return;
1875
- }
1876
- const { isMatch, score, indices } = searcher.searchIn(text);
1877
- if (isMatch) {
1878
- results.push({
1879
- item: text,
1880
- idx,
1881
- matches: [{ score, value: text, norm: norm2, indices }]
1882
- });
1883
- }
2124
+ const intermediaryTypeAndId = relationship.properties.intermediary;
2125
+ return intermediaryTypeAndId.map(({ id }) => findByIdSync(id));
2126
+ };
2127
+ const formatCategoryLabel = (category) => {
2128
+ return capitalize(category);
2129
+ };
2130
+ const getToward = (from, to, intermediary) => {
2131
+ if (to.type === "end") return to.name;
2132
+ const intermediaryIds = intermediary.map((int) => int.id);
2133
+ const relationship = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(to.source.id));
2134
+ if (!relationship) return to.name;
2135
+ const candidates = [relationship.properties.origin.id, relationship.properties.destination.id];
2136
+ const nextUnitId = candidates.filter((id) => !intermediaryIds.includes(id))[0];
2137
+ if (!nextUnitId) return to.name;
2138
+ const nextUnit = findByIdSync(nextUnitId);
2139
+ return { en: formatCategoryLabel(`${nextUnit.properties.category}`) };
2140
+ };
2141
+ const toWaypoints = (path) => {
2142
+ const [startPoint, middleAndEndPoints] = extractStartPoint(path, options);
2143
+ const [endPoint, middlePoints] = extractEndPoint(middleAndEndPoints, options);
2144
+ const waypoints = middlePoints.map((openingId) => {
2145
+ const opening = findByIdSync(openingId);
2146
+ const level = findByIdSync(opening.properties.level_id);
2147
+ const coordinates = center6(opening).geometry.coordinates;
2148
+ const landmark = landmarkUtils.findNearestLandmark(coordinates, opening.properties.level_id);
2149
+ return {
2150
+ id: `${opening.properties.level_id}:${openingId}`,
2151
+ type: "between",
2152
+ point: coordinates,
2153
+ name: null,
2154
+ levelId: opening.properties.level_id,
2155
+ ordinal: level.properties.ordinal,
2156
+ hint: landmark ? { kind: "landmark", name: landmark.name } : void 0,
2157
+ source: { type: "opening", id: opening.id }
2158
+ };
1884
2159
  });
1885
- return results;
1886
- }
1887
- _searchLogical(query) {
1888
- const expression = parse(query, this.options);
1889
- const evaluate = (node, item, idx) => {
1890
- if (!node.children) {
1891
- const { keyId, searcher } = node;
1892
- const matches = this._findMatches({
1893
- key: this._keyStore.get(keyId),
1894
- value: this._myIndex.getValueForItemAtKeyId(item, keyId),
1895
- searcher
1896
- });
1897
- if (matches && matches.length) {
1898
- return [
1899
- {
1900
- idx,
1901
- item,
1902
- matches
1903
- }
1904
- ];
1905
- }
1906
- return [];
1907
- }
1908
- const res = [];
1909
- for (let i = 0, len = node.children.length; i < len; i += 1) {
1910
- const child = node.children[i];
1911
- const result = evaluate(child, item, idx);
1912
- if (result.length) {
1913
- res.push(...result);
1914
- } else if (node.operator === LogicalOperator.AND) {
1915
- return [];
1916
- }
1917
- }
1918
- return res;
2160
+ return [startPoint, ...waypoints, endPoint];
2161
+ };
2162
+ const createHorizontalStep = (from, to) => {
2163
+ const intermediary = findHorizontalIntermediary(from, to);
2164
+ const intermediaryCategories = intermediary.map((unit) => unit.properties.category);
2165
+ const intermediaryCategory = intermediaryCategories.length > 0 ? intermediaryCategories[0] : "unspecified";
2166
+ const toward = getToward(from, to, intermediary);
2167
+ const landmark = to.hint?.kind === "landmark" ? to.hint.name : null;
2168
+ const path = stepPathUtils.findStepPath(from.point, to.point, intermediary);
2169
+ const step = {
2170
+ from,
2171
+ to,
2172
+ levelIds: [from.levelId],
2173
+ ordinals: [from.ordinal],
2174
+ intermediaryCategory,
2175
+ description: describeHorizontalStep(intermediaryCategory, toward, landmark),
2176
+ path: path.map((coord) => [...coord, from.ordinal * 9])
1919
2177
  };
1920
- const records = this._myIndex.records;
1921
- const resultMap = {};
1922
- const results = [];
1923
- records.forEach(({ $: item, i: idx }) => {
1924
- if (isDefined(item)) {
1925
- let expResults = evaluate(expression, item, idx);
1926
- if (expResults.length) {
1927
- if (!resultMap[idx]) {
1928
- resultMap[idx] = { idx, item, matches: [] };
1929
- results.push(resultMap[idx]);
1930
- }
1931
- expResults.forEach(({ matches }) => {
1932
- resultMap[idx].matches.push(...matches);
1933
- });
1934
- }
1935
- }
1936
- });
1937
- return results;
1938
- }
1939
- _searchObjectList(query) {
1940
- const searcher = createSearcher(query, this.options);
1941
- const { keys, records } = this._myIndex;
1942
- const results = [];
1943
- records.forEach(({ $: item, i: idx }) => {
1944
- if (!isDefined(item)) {
1945
- return;
1946
- }
1947
- let matches = [];
1948
- keys.forEach((key, keyIndex) => {
1949
- matches.push(
1950
- ...this._findMatches({
1951
- key,
1952
- value: item[keyIndex],
1953
- searcher
1954
- })
1955
- );
1956
- });
1957
- if (matches.length) {
1958
- results.push({
1959
- idx,
1960
- item,
1961
- matches
1962
- });
1963
- }
1964
- });
1965
- return results;
1966
- }
1967
- _findMatches({ key, value, searcher }) {
1968
- if (!isDefined(value)) {
1969
- return [];
1970
- }
1971
- let matches = [];
1972
- if (isArray(value)) {
1973
- value.forEach(({ v: text, i: idx, n: norm2 }) => {
1974
- if (!isDefined(text)) {
1975
- return;
1976
- }
1977
- const { isMatch, score, indices } = searcher.searchIn(text);
1978
- if (isMatch) {
1979
- matches.push({
1980
- score,
1981
- key,
1982
- value: text,
1983
- idx,
1984
- norm: norm2,
1985
- indices
1986
- });
1987
- }
1988
- });
1989
- } else {
1990
- const { v: text, n: norm2 } = value;
1991
- const { isMatch, score, indices } = searcher.searchIn(text);
1992
- if (isMatch) {
1993
- matches.push({ score, key, value: text, norm: norm2, indices });
1994
- }
2178
+ return step;
2179
+ };
2180
+ const createVerticalStep = (from, to) => {
2181
+ const intermediary = findVerticalIntermediary(from, to);
2182
+ const intermediaryCategories = intermediary.map((unit) => unit.properties.category);
2183
+ const intermediaryCategory = intermediaryCategories.length > 0 ? intermediaryCategories[0] : "unspecified";
2184
+ const fromLevel = findByIdSync(from.levelId);
2185
+ const toLevel = findByIdSync(to.levelId);
2186
+ return {
2187
+ from,
2188
+ to,
2189
+ levelIds: [from.levelId, to.levelId],
2190
+ ordinals: [from.ordinal, to.ordinal],
2191
+ intermediaryCategory,
2192
+ description: describeVerticalStep(fromLevel, toLevel, intermediaryCategory),
2193
+ path: [
2194
+ [...from.point, from.ordinal * 9],
2195
+ [...to.point, to.ordinal * 9]
2196
+ ]
2197
+ };
2198
+ };
2199
+ const isVertical = (from, to) => {
2200
+ const fromLevel = findByIdSync(from.levelId);
2201
+ const toLevel = findByIdSync(to.levelId);
2202
+ return !!fromLevel && !!toLevel && fromLevel.properties.ordinal !== toLevel.properties.ordinal;
2203
+ };
2204
+ const toSteps = (waypoints) => {
2205
+ let steps = [];
2206
+ const t0_allSteps = performance.now();
2207
+ for (let i = 0; i < waypoints.length - 1; i++) {
2208
+ const from = waypoints[i];
2209
+ const to = waypoints[i + 1];
2210
+ const t0 = performance.now();
2211
+ const step = isVertical(from, to) ? createVerticalStep(from, to) : createHorizontalStep(from, to);
2212
+ steps.push(step);
2213
+ const t1 = performance.now();
2214
+ trace("nav", ` \u2502 \u251C\u2500 #${i} ${from.id.padEnd(25)} \u2192 ${`(${step.intermediaryCategory})`.padEnd(12)} \u2192 ${to.id}`, t1 - t0);
2215
+ trace("nav", ` \u2502 \u2502 ${step.description.text}`, void 0, "#bada55");
1995
2216
  }
1996
- return matches;
1997
- }
2217
+ const simplifySteps = combineWalkwaySteps(steps);
2218
+ const t1_allSteps = performance.now();
2219
+ trace("nav", " \u2502 \u2514\u2500 Total ", t1_allSteps - t0_allSteps);
2220
+ return simplifySteps;
2221
+ };
2222
+ return {
2223
+ // createSteps,
2224
+ toWaypoints,
2225
+ toSteps
2226
+ };
1998
2227
  };
1999
- Fuse.version = "7.1.0";
2000
- Fuse.createIndex = createIndex;
2001
- Fuse.parseIndex = parseIndex;
2002
- Fuse.config = Config;
2003
- {
2004
- Fuse.parseQuery = parse;
2005
- }
2006
- {
2007
- register(ExtendedSearch);
2008
- }
2009
2228
 
2010
- // src/data/search/utils/sanitizeInput.ts
2011
- var sanitizeInput = (str) => str.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\-–—_./()]+/g, "").normalize("NFC").trim();
2229
+ // src/data/navigate/utils/timeDistance.ts
2230
+ import calculateLength from "@turf/length";
2231
+ var WALKING_SPEED = 1.4;
2232
+ var calculatePathLength = (feature2) => calculateLength(feature2, { units: "kilometers" }) * 1e3;
2233
+ var calculateTravelingDuration = (distance5) => {
2234
+ const duration = distance5 / WALKING_SPEED;
2235
+ const minutes = Math.round(duration / 60);
2236
+ return minutes > 0 ? minutes : 1;
2237
+ };
2238
+ var calculateTotalDistance = (steps = []) => {
2239
+ return steps.reduce((acc, { path }) => acc + calculatePathLength(lineString(path)), 0);
2240
+ };
2241
+ var calculateRoundedDistance = (distance5) => {
2242
+ return Math.round(distance5 - distance5 % 25);
2243
+ };
2012
2244
 
2013
- // src/data/search/getSearchClient.ts
2014
- var getSearchClient = ({ occupants, amenities }) => {
2015
- const fuseAmenities = new Fuse(amenities, {
2016
- threshold: 0.2,
2017
- keys: [
2018
- { name: "properties.name", "weight": 1, getFn: (obj) => Object.values(obj.properties.name || {}) },
2019
- { name: "properties.category", "weight": 1 }
2020
- ]
2021
- });
2022
- const fuseOccupants = new Fuse(occupants, {
2023
- threshold: 0.25,
2024
- // 0.2 is too strict (can't find Mo-Mo Paradise with "momo" search string)
2025
- includeScore: true,
2026
- shouldSort: true,
2027
- keys: [
2028
- { name: "properties.name", "weight": 4, getFn: (obj) => Object.values(obj.properties.name || {}) },
2029
- { name: "properties.keywords", "weight": 0.5 },
2030
- { name: "properties.category", "weight": 0.25 },
2031
- { name: "properties.local_category_names", "weight": 0.25 },
2032
- { name: "properties.description", "weight": 0.25, getFn: (occ) => Object.values(occ.properties.description || {}) },
2033
- { name: "properties.unit_name", "weight": 0.25 },
2034
- { name: "properties.kiosk_name", "weight": 0.25 }
2035
- ]
2036
- });
2037
- const search2 = (value) => {
2038
- const sanitizedValue = sanitizeInput(value);
2039
- const matchedAmenities = fuseAmenities.search(sanitizedValue);
2040
- const matchedOccupants = fuseOccupants.search(sanitizedValue);
2041
- return [...matchedAmenities, ...matchedOccupants];
2245
+ // src/data/navigate/utils/createFindByIdSync.ts
2246
+ var createFindByIdSync = (data) => {
2247
+ const { amenities = [], anchors = [], fixtures = [], levels = [], kiosks = [], relationships = [], occupants = [], openings = [], units } = data;
2248
+ const featureById = /* @__PURE__ */ new Map();
2249
+ const entries = [
2250
+ ...amenities,
2251
+ ...anchors,
2252
+ ...fixtures,
2253
+ ...levels,
2254
+ ...kiosks,
2255
+ ...relationships,
2256
+ ...occupants,
2257
+ ...openings,
2258
+ ...units
2259
+ ];
2260
+ for (const f of entries) featureById.set(f.id, f);
2261
+ const findByIdSync = (id) => {
2262
+ return featureById.get(id);
2263
+ };
2264
+ return { findByIdSync };
2265
+ };
2266
+
2267
+ // src/data/navigate/getNavigateClient.ts
2268
+ var getNavigateClient = (options) => {
2269
+ const { data } = options;
2270
+ const { levels, units } = data;
2271
+ trace("nav", "\u2713 prepare");
2272
+ const t0 = performance.now();
2273
+ trace("nav", " \u251C\u2500 createGraph (dijkstra)");
2274
+ const { defaultGraph, accessibleGraph, addCoordinateOrdinalNode } = prepareGraph({ data });
2275
+ const t1 = performance.now();
2276
+ trace("nav", " \u2502 \u2514\u2500 Total ", t1 - t0);
2277
+ const t2 = performance.now();
2278
+ const { findByIdSync } = createFindByIdSync(data);
2279
+ const t3 = performance.now();
2280
+ trace("nav", " \u2514\u2500 findByIdSync", t3 - t2);
2281
+ const findCoordinateOrdinalUnit = (params) => {
2282
+ const [lat, lng, ordinal] = parseOrdinalCoordinate(params);
2283
+ const levelIdsWithOrdinal = levels.filter((level) => level.properties.ordinal === ordinal).map((level) => level.id);
2284
+ const unit = units.find((unit2) => levelIdsWithOrdinal.includes(unit2.properties.level_id) && booleanPointInPolygon4([lat, lng], unit2));
2285
+ return unit;
2286
+ };
2287
+ const stepUtils = createStepUtils({ ...options, findByIdSync });
2288
+ const findRoute = async (routeOriginParam, routeDestinationParam, options2) => {
2289
+ if (!routeOriginParam || !routeDestinationParam) return null;
2290
+ const graph = options2?.mode === "accessible" ? accessibleGraph : defaultGraph;
2291
+ if (isCoordinateOrdinalString(routeOriginParam)) {
2292
+ const walkwayUnit = findCoordinateOrdinalUnit(routeOriginParam);
2293
+ addCoordinateOrdinalNode(routeOriginParam, walkwayUnit);
2294
+ }
2295
+ if (isCoordinateOrdinalString(routeDestinationParam)) {
2296
+ const walkwayUnit = findCoordinateOrdinalUnit(routeDestinationParam);
2297
+ addCoordinateOrdinalNode(routeDestinationParam, walkwayUnit);
2298
+ }
2299
+ try {
2300
+ trace("nav", "\u2713 findRoute", 0);
2301
+ const t02 = performance.now();
2302
+ const path = graph.path(routeOriginParam, routeDestinationParam);
2303
+ const t12 = performance.now();
2304
+ trace("nav", " \u251C\u2500 path (dijkstra)", t12 - t02);
2305
+ const waypoints = stepUtils.toWaypoints(path);
2306
+ console.log({ waypoints });
2307
+ const t22 = performance.now();
2308
+ trace("nav", " \u251C\u2500 toWaypoints", t22 - t12);
2309
+ trace("nav", " \u251C\u2500 toSteps", 0);
2310
+ const steps = stepUtils.toSteps(waypoints);
2311
+ const t32 = performance.now();
2312
+ const totalDistance = calculateTotalDistance(steps);
2313
+ const roundedDistance = calculateRoundedDistance(totalDistance);
2314
+ const duration = calculateTravelingDuration(totalDistance);
2315
+ const t4 = performance.now();
2316
+ trace("nav", " \u2514\u2500 postProcess", t4 - t32);
2317
+ return {
2318
+ // origin: routeOrigin,
2319
+ // destination: routeDestination,
2320
+ description: null,
2321
+ distance: roundedDistance,
2322
+ duration,
2323
+ steps
2324
+ };
2325
+ } catch (error) {
2326
+ console.log(error);
2327
+ throw error;
2328
+ }
2042
2329
  };
2043
2330
  return {
2044
- search: search2
2331
+ findRoute,
2332
+ findByIdSync
2045
2333
  };
2046
2334
  };
2047
2335
 
2048
2336
  // src/data/getDataClient.ts
2049
2337
  var getDataClient = (options) => {
2050
2338
  let searchClient;
2339
+ let navigateClient;
2051
2340
  const observers = /* @__PURE__ */ new Map();
2052
2341
  const queryClient = options.queryClient ?? new QueryClient();
2053
2342
  const { mode = "delivery", projectId, apiKey, baseUrl, previewToken } = options;
@@ -2078,17 +2367,17 @@ var getDataClient = (options) => {
2078
2367
  const internalFindById = async (id) => {
2079
2368
  if (id === null || id === void 0) return null;
2080
2369
  const featureType = id.slice(0, id.lastIndexOf("-"));
2081
- const feature = await queryClient.ensureQueryData({
2370
+ const feature2 = await queryClient.ensureQueryData({
2082
2371
  queryKey: ["_deliveryapi", featureType, id],
2083
2372
  queryFn: async () => {
2084
2373
  const features = await internalFilterByType(featureType);
2085
- const feature2 = features.find(
2374
+ const feature3 = features.find(
2086
2375
  (f) => f.id === id
2087
2376
  );
2088
- return feature2 ?? null;
2377
+ return feature3 ?? null;
2089
2378
  }
2090
2379
  });
2091
- return feature;
2380
+ return feature2;
2092
2381
  };
2093
2382
  const populator = createPopulator({ internalFindById, internalFilterByType });
2094
2383
  const registerObserver = (featureType, refetchInterval) => {
@@ -2137,8 +2426,8 @@ var getDataClient = (options) => {
2137
2426
  const createFindByIdQueryOptions = (featureType, id, params = {}, options2 = {}) => ({
2138
2427
  queryKey: [featureType, "detail", id, params],
2139
2428
  queryFn: async () => {
2140
- const feature = await internalFindById(id);
2141
- return params.populate === true ? await populator[featureType](feature) : Promise.resolve(feature);
2429
+ const feature2 = await internalFindById(id);
2430
+ return params.populate === true ? await populator[featureType](feature2) : Promise.resolve(feature2);
2142
2431
  },
2143
2432
  ...options2 ?? {}
2144
2433
  });
@@ -2156,8 +2445,8 @@ var getDataClient = (options) => {
2156
2445
  id,
2157
2446
  params
2158
2447
  );
2159
- const feature = await queryClient.ensureQueryData(findQueryOptions);
2160
- return feature;
2448
+ const feature2 = await queryClient.ensureQueryData(findQueryOptions);
2449
+ return feature2;
2161
2450
  }
2162
2451
  const searchFn = async (txt) => {
2163
2452
  if (!searchClient) {
@@ -2170,6 +2459,24 @@ var getDataClient = (options) => {
2170
2459
  }
2171
2460
  return searchClient.search(txt);
2172
2461
  };
2462
+ const navigateFn = async (origin, destination) => {
2463
+ if (!navigateClient) {
2464
+ const [levels, occupants, openings, relationships, units, fixtures, kiosks, amenities, anchors] = await Promise.all([
2465
+ filterByType("level"),
2466
+ filterByType("occupant"),
2467
+ filterByType("opening"),
2468
+ filterByType("relationship"),
2469
+ filterByType("unit"),
2470
+ filterByType("fixture"),
2471
+ filterByType("kiosk"),
2472
+ filterByType("amenity"),
2473
+ filterByType("anchor")
2474
+ ]);
2475
+ const haystack = { levels, occupants, openings, relationships, units, fixtures, kiosks, amenities, anchors };
2476
+ navigateClient = getNavigateClient({ data: haystack });
2477
+ }
2478
+ return navigateClient.findRoute(origin, destination);
2479
+ };
2173
2480
  return {
2174
2481
  projectId,
2175
2482
  queryClient,
@@ -2181,7 +2488,8 @@ var getDataClient = (options) => {
2181
2488
  _internalFindById: internalFindById,
2182
2489
  filterByType,
2183
2490
  findById,
2184
- search: searchFn
2491
+ search: searchFn,
2492
+ navigate: navigateFn
2185
2493
  };
2186
2494
  };
2187
2495
  export {