proximiio-js-library 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +719 -0
  2. package/assets/proximiio-js-library.css +520 -0
  3. package/assets/tbtnav.js +3 -0
  4. package/assets/wayfinding.d.ts +3 -0
  5. package/assets/wayfinding.js +1586 -0
  6. package/lib/common.d.ts +7 -0
  7. package/lib/common.js +49 -0
  8. package/lib/components/map/constants.d.ts +6 -0
  9. package/lib/components/map/constants.js +30 -0
  10. package/lib/components/map/custom-layers.d.ts +5 -0
  11. package/lib/components/map/custom-layers.js +74 -0
  12. package/lib/components/map/icons.d.ts +23 -0
  13. package/lib/components/map/icons.js +64 -0
  14. package/lib/components/map/layers/any_layer.d.ts +9 -0
  15. package/lib/components/map/layers/any_layer.js +2 -0
  16. package/lib/components/map/layers/background_layer.d.ts +16 -0
  17. package/lib/components/map/layers/background_layer.js +52 -0
  18. package/lib/components/map/layers/base_layer.d.ts +18 -0
  19. package/lib/components/map/layers/base_layer.js +127 -0
  20. package/lib/components/map/layers/circle_layer.d.ts +25 -0
  21. package/lib/components/map/layers/circle_layer.js +62 -0
  22. package/lib/components/map/layers/fill_extrusion_layer.d.ts +21 -0
  23. package/lib/components/map/layers/fill_extrusion_layer.js +57 -0
  24. package/lib/components/map/layers/fill_layer.d.ts +21 -0
  25. package/lib/components/map/layers/fill_layer.js +57 -0
  26. package/lib/components/map/layers/heatmap_layer.d.ts +18 -0
  27. package/lib/components/map/layers/heatmap_layer.js +54 -0
  28. package/lib/components/map/layers/hillshade_layer.d.ts +19 -0
  29. package/lib/components/map/layers/hillshade_layer.js +55 -0
  30. package/lib/components/map/layers/line_layer.d.ts +28 -0
  31. package/lib/components/map/layers/line_layer.js +64 -0
  32. package/lib/components/map/layers/raster_layer.d.ts +21 -0
  33. package/lib/components/map/layers/raster_layer.js +57 -0
  34. package/lib/components/map/layers/symbol_layer.d.ts +71 -0
  35. package/lib/components/map/layers/symbol_layer.js +153 -0
  36. package/lib/components/map/main.d.ts +625 -0
  37. package/lib/components/map/main.js +1765 -0
  38. package/lib/components/map/metadata.d.ts +134 -0
  39. package/lib/components/map/metadata.js +135 -0
  40. package/lib/components/map/routing.d.ts +12 -0
  41. package/lib/components/map/routing.js +74 -0
  42. package/lib/components/map/sources/base_source.d.ts +10 -0
  43. package/lib/components/map/sources/base_source.js +38 -0
  44. package/lib/components/map/sources/cluster_source.d.ts +7 -0
  45. package/lib/components/map/sources/cluster_source.js +30 -0
  46. package/lib/components/map/sources/data_source.d.ts +17 -0
  47. package/lib/components/map/sources/data_source.js +46 -0
  48. package/lib/components/map/sources/geojson_source.d.ts +16 -0
  49. package/lib/components/map/sources/geojson_source.js +77 -0
  50. package/lib/components/map/sources/image_source_manager.d.ts +16 -0
  51. package/lib/components/map/sources/image_source_manager.js +131 -0
  52. package/lib/components/map/sources/routing_source.d.ts +22 -0
  53. package/lib/components/map/sources/routing_source.js +131 -0
  54. package/lib/components/map/sources/synthetic_source.d.ts +7 -0
  55. package/lib/components/map/sources/synthetic_source.js +32 -0
  56. package/lib/components/map/sources/vector_source.d.ts +8 -0
  57. package/lib/components/map/sources/vector_source.js +31 -0
  58. package/lib/components/select/main.d.ts +66 -0
  59. package/lib/components/select/main.js +195 -0
  60. package/lib/controllers/auth.d.ts +48 -0
  61. package/lib/controllers/auth.js +156 -0
  62. package/lib/controllers/floors.d.ts +14 -0
  63. package/lib/controllers/floors.js +105 -0
  64. package/lib/controllers/geo.d.ts +10 -0
  65. package/lib/controllers/geo.js +131 -0
  66. package/lib/controllers/places.d.ts +14 -0
  67. package/lib/controllers/places.js +108 -0
  68. package/lib/controllers/repository.d.ts +24 -0
  69. package/lib/controllers/repository.js +68 -0
  70. package/lib/controllers/style.d.ts +4 -0
  71. package/lib/controllers/style.js +76 -0
  72. package/lib/eventable.d.ts +7 -0
  73. package/lib/eventable.js +29 -0
  74. package/lib/index.d.ts +26 -0
  75. package/lib/index.js +14 -0
  76. package/lib/models/amenity.d.ts +11 -0
  77. package/lib/models/amenity.js +41 -0
  78. package/lib/models/auth-data.d.ts +4 -0
  79. package/lib/models/auth-data.js +2 -0
  80. package/lib/models/base.d.ts +7 -0
  81. package/lib/models/base.js +18 -0
  82. package/lib/models/feature.d.ts +46 -0
  83. package/lib/models/feature.js +277 -0
  84. package/lib/models/floor.d.ts +30 -0
  85. package/lib/models/floor.js +51 -0
  86. package/lib/models/geopoint.d.ts +6 -0
  87. package/lib/models/geopoint.js +2 -0
  88. package/lib/models/mapbox-options.d.ts +171 -0
  89. package/lib/models/mapbox-options.js +2 -0
  90. package/lib/models/person.d.ts +8 -0
  91. package/lib/models/person.js +18 -0
  92. package/lib/models/place.d.ts +14 -0
  93. package/lib/models/place.js +40 -0
  94. package/lib/models/poi_type.d.ts +19 -0
  95. package/lib/models/poi_type.js +25 -0
  96. package/lib/models/style.d.ts +53 -0
  97. package/lib/models/style.js +424 -0
  98. package/lib/proximiio.js +2 -0
  99. package/lib/proximiio.js.LICENSE.txt +79 -0
  100. package/package.json +61 -0
@@ -0,0 +1,1586 @@
1
+ import * as turf from '@turf/turf';
2
+
3
+ export class Wayfinding {
4
+
5
+ /**
6
+ *
7
+ * @param featureCollection {FeatureCollection}
8
+ */
9
+ constructor(featureCollection) {
10
+ let featureList = featureCollection.features;
11
+ let minLevel = undefined;
12
+ let maxLevel = undefined;
13
+
14
+ featureList.forEach(feature => {
15
+ let level = feature.properties.level;
16
+ if (minLevel === undefined || level < minLevel) {
17
+ minLevel = level;
18
+ }
19
+ if (maxLevel === undefined || maxLevel < level) {
20
+ maxLevel = level;
21
+ }
22
+ if (feature.properties.levels !== undefined) {
23
+ feature.properties.levels.forEach(level => {
24
+ if (minLevel === undefined || level < minLevel) {
25
+ minLevel = level;
26
+ }
27
+ if (maxLevel === undefined || maxLevel < level) {
28
+ maxLevel = level;
29
+ }
30
+ });
31
+ }
32
+ });
33
+
34
+ if (minLevel === undefined) throw "No feature with level was supplied!";
35
+
36
+ let routableAreaFeatureList = featureList.filter(feature => feature.properties.routable && (feature.geometry.type === "MultiPolygon" || feature.geometry.type === "Polygon"));
37
+ let floorGeojsonMap = new Map();
38
+ for (let level = minLevel; level <= maxLevel; level++) {
39
+ let floorAreaFeature = routableAreaFeatureList.find(feature => feature.properties.level === level);
40
+ floorGeojsonMap.set(level, turf.featureCollection(floorAreaFeature !== undefined ? [floorAreaFeature] : []));
41
+ }
42
+
43
+ // Extract corridors, level changers, accessibility POIs
44
+ let corridorList = featureList.filter(feature => feature.properties.class === 'path');
45
+ let levelChangerList = featureList.filter(feature =>
46
+ feature.properties.type === 'elevator'
47
+ || feature.properties.type === 'escalator'
48
+ || feature.properties.type === 'staircase'
49
+ );
50
+ levelChangerList.forEach(levelChanger => {
51
+ if (levelChanger.id === undefined) levelChanger.id = levelChanger.properties.id;
52
+ });
53
+ let accesibilityPoiTypeList = ['door', 'ticket_gate'];
54
+ let accessibilityPoiList = featureList.filter(feature => accesibilityPoiTypeList.includes(feature.properties.type));
55
+
56
+ // LevelChangers: Create level array from legacy min/max values
57
+ levelChangerList.forEach(levelChanger => {
58
+ if (levelChanger.properties.levels === undefined) {
59
+ if (levelChanger.properties.level_min !== undefined && levelChanger.properties.level_max !== undefined) {
60
+ levelChanger.properties.levels = [];
61
+ for (let level = levelChanger.properties.level_min; level <= levelChanger.properties.level_max; level++) {
62
+ levelChanger.properties.levels.push(level);
63
+ }
64
+ }
65
+ }
66
+ });
67
+
68
+ // Use legacy constructor. TODO Replace this
69
+ this._legacyConstructor(floorGeojsonMap, levelChangerList, corridorList, accessibilityPoiList);
70
+
71
+ }
72
+
73
+
74
+ /**
75
+ * TODO: replace this
76
+ * @param floorGeojsonMap {Map}
77
+ * @param levelChangers {Feature<Point>[]}
78
+ * @param corridors {Feature<LineString>[]}
79
+ * @param accessibilityPoiList {Feature<Point>[]}
80
+ */
81
+ _legacyConstructor(floorGeojsonMap, levelChangers, corridors, accessibilityPoiList) {
82
+ this.wallOffsetDistance = 0.5; //m
83
+ this.floorList = floorGeojsonMap;
84
+ this.levelChangerList = levelChangers;
85
+ this.accessibilityPoi = accessibilityPoiList;
86
+ this.corridors = [
87
+ ...corridors.filter(it => it.geometry.type === 'LineString')
88
+ // ,
89
+ // ...corridors.filter(it => it.geometry.type === 'MultiLineString').map(it => turf.flatten(it).features).flat()
90
+ ];
91
+
92
+ this.rebuildData();
93
+ this.configuration = {
94
+ avoidElevators: false,
95
+ avoidEscalators: false,
96
+ avoidStaircases: false,
97
+ avoidRamps: false,
98
+ avoidNarrowPaths: false,
99
+ avoidRevolvingDoors: false,
100
+ avoidTicketGates: false,
101
+ avoidBarriers: false
102
+ };
103
+ this.POI_TYPE = {
104
+ ELEVATOR: 'elevator',
105
+ ESCALATOR: 'escalator',
106
+ STAIRCASE: 'staircase',
107
+ RAMP: 'ramp',
108
+ NARROW_PATH: 'narrow_path',
109
+ REVOLVING_DOOR: 'door',
110
+ TICKET_GATE: 'ticket_gate',
111
+ BARRIER: 'barrier'
112
+ };
113
+ this._pathFixDistance = 1.0;
114
+ }
115
+
116
+ /**
117
+ * @param configuration {Object}
118
+ * @param configuration.avoidElevators {Boolean}
119
+ * @param configuration.avoidEscalators {Boolean}
120
+ * @param configuration.avoidStaircases {Boolean}
121
+ * @param configuration.avoidRamps {Boolean}
122
+ * @param configuration.avoidNarrowPaths {Boolean}
123
+ * @param configuration.avoidRevolvingDoors {Boolean}
124
+ * @param configuration.avoidTicketGates {Boolean}
125
+ * @param configuration.avoidBarriers {Boolean}
126
+ * @param pathFixDistance {Number}
127
+ */
128
+ setConfiguration(configuration, pathFixDistance = 1.0) {
129
+ Object.keys(configuration).forEach(property => {
130
+ if (this.configuration.hasOwnProperty(property)) {
131
+ this.configuration[property] = configuration[property];
132
+ }
133
+ });
134
+ this._pathFixDistance = pathFixDistance;
135
+ }
136
+
137
+ rebuildData() {
138
+
139
+ let floorData = new Map();
140
+ this.floorList.forEach((floor, level) => {
141
+ let floorPoints = [];
142
+ let floorWalls = [];
143
+ let floorAreas = [];
144
+
145
+ // Floor features == "walkable areas"
146
+ floor.features.forEach((walkableArea, walkableAreaIndex) => {
147
+ let wallLineStringList = turf.flatten(turf.polygonToLine(walkableArea)).features.map(feature => {return feature.geometry; });
148
+ // Floor wall lines, we wish to split to individual walls
149
+ wallLineStringList.forEach(wallLineString => {
150
+ let firstPoint;
151
+ let nextPoint;
152
+
153
+ // Last point is the same as first, therefore limit index to exclude last point
154
+ for (let index = 0; index < wallLineString.coordinates.length - 1; index++) {
155
+ let point;
156
+ if (index === 0) {
157
+ firstPoint = turf.point(wallLineString.coordinates[index]);
158
+ firstPoint.properties.level = level;
159
+ firstPoint.properties.neighbours = [];
160
+ point = firstPoint;
161
+ } else {
162
+ point = nextPoint;
163
+ }
164
+ if (index === wallLineString.coordinates.length - 2) {
165
+ nextPoint = firstPoint
166
+ } else {
167
+ nextPoint = turf.point(wallLineString.coordinates[index + 1]);
168
+ nextPoint.properties.level = level;
169
+ nextPoint.properties.neighbours = [];
170
+ }
171
+ point.properties.neighbours.push(nextPoint);
172
+ nextPoint.properties.neighbours.push(point);
173
+ floorPoints.push(point);
174
+ floorWalls.push([point, nextPoint]);
175
+ }
176
+ });
177
+ floorAreas = floorAreas.concat(turf.flatten(walkableArea).features);
178
+ });
179
+
180
+ floorData.set(level, {
181
+ areas: floorAreas,
182
+ points: floorPoints,
183
+ walls: floorWalls,
184
+ wallFeatures: floorWalls.map(wall => turf.lineString([wall[0].geometry.coordinates, wall[1].geometry.coordinates]))
185
+ });
186
+
187
+ });
188
+
189
+ this.bearingCache = new Map();
190
+ this.floorData = floorData;
191
+
192
+ this.floorData.forEach((floorData, floorLevel) => {
193
+ // List of physical POIs on this level that are within area
194
+ let inAreaPoiList = this.accessibilityPoi
195
+ .filter(poi => floorLevel === poi.properties.level)
196
+ .filter(poi =>
197
+ floorData.areas.filter(area => turf.booleanContains(area, poi)) .length > 0)
198
+ ;
199
+ inAreaPoiList.forEach(poi => {
200
+ // Generate points around POI to allow going around, but only if they are "within area
201
+ let detourPointList = [
202
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, 0, {units: 'meters'}),
203
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, 60, {units: 'meters'}),
204
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, 120, {units: 'meters'}),
205
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, 180, {units: 'meters'}),
206
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, -120, {units: 'meters'}),
207
+ turf.destination(poi.geometry.coordinates, poi.properties.radius + this.wallOffsetDistance, -60, {units: 'meters'})
208
+ ].filter(poi => floorData.areas.filter(area => turf.booleanContains(area, poi)).length > 0);
209
+ detourPointList.forEach(point => {
210
+ point.properties.level = floorLevel;
211
+ point.properties.isDetourPoint = true;
212
+ });
213
+ floorData.points = floorData.points.concat(detourPointList);
214
+ this.detourPointList = detourPointList;
215
+ });
216
+ });
217
+
218
+ // Split lines into single line segments
219
+ let corridorLinePointPairs = [];
220
+ let corridorLineFeatures = [];
221
+ this.corridors.forEach((corridor, corridorIndex) => {
222
+ let coordinateList = corridor.geometry.coordinates;
223
+ let lastPoint = null;
224
+ for (let i = 0; i < coordinateList.length - 1; i++) {
225
+ let pointA;
226
+ if (lastPoint != null) {
227
+ pointA = lastPoint
228
+ } else {
229
+ pointA = turf.point(coordinateList[i]);
230
+ pointA.properties.neighbours = [];
231
+ pointA.properties.level = corridor.properties.level;
232
+ }
233
+ let pointB = turf.point(coordinateList[i + 1]);
234
+ pointB.properties.level = corridor.properties.level;
235
+ pointB.properties.neighbours = [];
236
+ if (corridor.properties.bidirectional != false || corridor.properties.swapDirection != false)
237
+ pointA.properties.neighbours.push(pointB);
238
+ if (corridor.properties.bidirectional != false || corridor.properties.swapDirection == true)
239
+ pointB.properties.neighbours.push(pointA);
240
+
241
+ let lineFeature = turf.lineString([pointA.geometry.coordinates, pointB.geometry.coordinates]);
242
+ lineFeature.properties.level = corridor.properties.level;
243
+
244
+ // Mark lineFeature accordingly
245
+ lineFeature.properties.bidirectional = corridor.properties.bidirectional;
246
+ lineFeature.properties.swapDirection = corridor.properties.swapDirection;
247
+
248
+ // Mark points as NarrowPath if corridor is NarrowPath
249
+ if (corridor.properties.narrowPath) {
250
+ pointA.properties.narrowPath = true;
251
+ pointB.properties.narrowPath = true;
252
+ lineFeature.properties.narrowPath = true;
253
+ }
254
+ // Mark points as Ramp if corridor is Ramp
255
+ if (corridor.properties.ramp) {
256
+ pointA.properties.ramp = true;
257
+ pointB.properties.ramp = true;
258
+ lineFeature.properties.ramp = true;
259
+ }
260
+
261
+ corridorLinePointPairs.push([pointA, pointB]);
262
+ corridorLineFeatures.push(lineFeature);
263
+ lastPoint = pointB;
264
+ }
265
+ });
266
+
267
+ let segmentIntersectionPointList = [];
268
+ let segmentIntersectionPointMap = new Map();
269
+ let i = 0;
270
+
271
+ // Split individual segments when intersecting
272
+ i = 0;
273
+ while (i < corridorLinePointPairs.length) {
274
+ // for (let i = 0; i < corridorLinePointPairs.length - 1; i++) {
275
+ let segment = corridorLinePointPairs[i];
276
+ let segmentLineString = corridorLineFeatures[i];
277
+
278
+ // let segmentIntersectionList = [];
279
+
280
+ if (!segmentIntersectionPointMap.has(i)) {
281
+ segmentIntersectionPointMap.set(i, []);
282
+ }
283
+ for (let j = i + 1; j < corridorLinePointPairs.length; j++) {
284
+ if (!segmentIntersectionPointMap.has(j)) {
285
+ segmentIntersectionPointMap.set(j, []);
286
+ }
287
+ let segmentToTest = corridorLinePointPairs[j];
288
+
289
+ if (segmentLineString.properties.level !== corridorLineFeatures[j].properties.level) {
290
+ continue
291
+ }
292
+
293
+ // Consecutive segments, should not cross (rather, they cross at the end point)
294
+ if (segmentToTest.includes(segment[0]) || segmentToTest.includes(segment[1])) {
295
+ continue;
296
+ }
297
+
298
+ let segmentLineStringToTest = corridorLineFeatures[j];
299
+ let intersections = turf.lineIntersect(segmentLineString, segmentLineStringToTest).features;
300
+ if (intersections.length > 0) {
301
+ let intersectingPoint = intersections[0];
302
+ intersectingPoint.properties.level = segment[0].properties.level;
303
+ intersectingPoint.properties.isCorridorPoint = true;
304
+ // Intersect point inherits filters from both intersecting lines
305
+ if (segmentLineString.properties.narrowPath || segmentLineStringToTest.properties.narrowPath) {
306
+ intersectingPoint.properties.narrowPath = true;
307
+ // console.log(intersectingPoint);
308
+ }
309
+ if (segmentLineString.properties.ramp || segmentLineStringToTest.properties.ramp) {
310
+ intersectingPoint.properties.ramp = true;
311
+ }
312
+ // this._setNeighbourhoodBasedOnCorridorDirectionality(corridorLineFeatures[i], segment[0], segment[1], intersectingPoint);
313
+
314
+ segmentIntersectionPointMap.get(i).push(intersectingPoint);
315
+ segmentIntersectionPointMap.get(j).push(intersectingPoint);
316
+ segmentIntersectionPointList.push(intersectingPoint);
317
+ // console.log();
318
+ // console.log(i + '/' +j + ' = ' + intersectingPoint.geometry.coordinates[0] + ',' + intersectingPoint.geometry.coordinates[1]);
319
+ }
320
+ }
321
+ i++;
322
+ }
323
+
324
+ i = 0;
325
+ while (i < corridorLinePointPairs.length) {
326
+ // for (let i = 0; i < corridorLinePointPairs.length; i++) {
327
+ let segment = corridorLinePointPairs[i];
328
+ let segmentFeature = corridorLineFeatures[i];
329
+ let pointA = segment[0];
330
+ let pointB = segment[1];
331
+ let segmentIntersectionList = segmentIntersectionPointMap.get(i);
332
+ segmentIntersectionList.sort((a, b) => this._comparePointsByDistanceFromReference(pointA, a, b));
333
+ if (segmentIntersectionList) {
334
+ segmentIntersectionList.forEach((intersection) => {
335
+ this._setNeighbourhoodBasedOnCorridorDirectionality(segmentFeature, pointA, pointB, intersection);
336
+ if (segmentFeature.properties.bidirectional != false) {
337
+ intersection.properties.neighbours = intersection.properties.neighbours.concat(segmentIntersectionList.filter(it => it !== intersection));
338
+ } else if (segmentFeature.properties.swapDirection != true) {
339
+ let pointsAfter = segmentIntersectionList.slice(segmentIntersectionList.indexOf(intersection));
340
+ intersection.properties.neighbours.push(...pointsAfter);
341
+ } else {
342
+ let pointsBefore = segmentIntersectionList.slice(0, segmentIntersectionList.indexOf(intersection));
343
+ intersection.properties.neighbours.push(...pointsBefore);
344
+ }
345
+ });
346
+ corridorLineFeatures[i].properties.intersectionPointList = segmentIntersectionList;
347
+ } else {
348
+ corridorLineFeatures[i].properties.intersectionPointList = []
349
+ }
350
+ i++;
351
+ }
352
+ let segmentToWallIntersectionPointList = [];
353
+
354
+ // Split corridor lines on interesections wilth walls
355
+ i = 0;
356
+ while (i < corridorLinePointPairs.length) {
357
+ let segment = corridorLinePointPairs[i];
358
+ let segmentFeature = corridorLineFeatures[i];
359
+
360
+ let segmentIntersections = [];
361
+ let walls = this.floorData.get(segment[0].properties.level).walls;
362
+ let wallFeatures = this.floorData.get(segment[0].properties.level).wallFeatures;
363
+
364
+ wallFeatures.forEach((wallFeature, wallIndex) => {
365
+ let intersections = turf.lineIntersect(segmentFeature, wallFeature).features;
366
+ if (intersections.length > 0) {
367
+ let intersectPoint = intersections[0];
368
+ intersectPoint.properties.level = segment[0].properties.level;
369
+ intersectPoint.properties.neighbours = [];
370
+ intersectPoint.properties.bordersArea = true;
371
+ // Intersect point inherits filters from both intersecting lines
372
+ if (segmentFeature.properties.narrowPath) {
373
+ intersectPoint.properties.narrowPath = true;
374
+ }
375
+ if (segmentFeature.properties.ramp) {
376
+ intersectPoint.properties.ramp = true;
377
+ }
378
+ let distance = this._distance(segment[0], intersectPoint);
379
+ segmentIntersections.push({
380
+ point: intersectPoint,
381
+ distance: distance,
382
+ wallIndex: wallIndex
383
+ });
384
+ }
385
+ });
386
+
387
+ if (segmentIntersections.length > 0) {
388
+
389
+ // Sort by distance from first point
390
+ segmentIntersections.sort((a, b) => a.distance - b.distance);
391
+
392
+ // Inject parts of segments split by intersections
393
+ let previousPoint = segment[0];
394
+
395
+ segmentIntersections.forEach((intersection, index) => {
396
+
397
+ // Set neighbourhood with points connected to intersection (next point will be added in next loop or with segment endpoint)
398
+ previousPoint.properties.neighbours.push(intersection.point);
399
+ walls[intersection.wallIndex][0].properties.neighbours.push(intersection.point);
400
+ walls[intersection.wallIndex][1].properties.neighbours.push(intersection.point);
401
+ intersection.point.properties.neighbours.push(previousPoint, walls[intersection.wallIndex][0], walls[intersection.wallIndex][1]);
402
+ // TODO check directionality?
403
+ intersection.point.properties.neighbours.push(...corridorLineFeatures[i].properties.intersectionPointList);
404
+ corridorLineFeatures[i].properties.intersectionPointList.forEach(point => point.properties.neighbours.push(intersection.point));
405
+
406
+ // Remember last point
407
+ previousPoint = intersection.point;
408
+ corridorLineFeatures[i].properties.intersectionPointList.push(intersection.point);
409
+ });
410
+
411
+ // Inject from last intersection to end of original segment
412
+ let newCorridor = turf.lineString([previousPoint.geometry.coordinates, segment[1].geometry.coordinates]);
413
+ newCorridor.properties.level = previousPoint.properties.level;
414
+
415
+ // connect last intersection point with end point
416
+ segment[1].properties.neighbours.push(previousPoint);
417
+ previousPoint.properties.neighbours.push(segment[1]);
418
+
419
+ segmentToWallIntersectionPointList.push(...segmentIntersections.map((intersection) => intersection.point));
420
+ }
421
+ i++;
422
+ }
423
+
424
+ segmentToWallIntersectionPointList.forEach((point) => {
425
+ this.floorData.get(point.properties.level).points.push(point);
426
+ });
427
+ // this.segmentToWallIntersectionPointList = segmentToWallIntersectionPointList.map(p => turf.point(p.geometry.coordinates));
428
+
429
+ segmentToWallIntersectionPointList.forEach((point) => {
430
+ let neighbours = this._findNeighbours(point, null, null, this.floorData.get(point.properties.level).points);
431
+ point.properties.neighbours.push(...neighbours);
432
+ });
433
+
434
+
435
+ // Store corridor data
436
+ this.corridorLinePointPairs = corridorLinePointPairs;
437
+ this.corridorLineFeatures = corridorLineFeatures;
438
+ this.corridorLinePoints = [];
439
+ this.corridorLinePointPairs.forEach(pair => {
440
+ pair[0].properties.isCorridorPoint = true;
441
+ pair[1].properties.isCorridorPoint = true;
442
+
443
+ if (!this.corridorLinePoints.includes(pair[0])) {
444
+ this.corridorLinePoints.push(pair[0]);
445
+ }
446
+ if (!this.corridorLinePoints.includes(pair[1])) {
447
+ this.corridorLinePoints.push(pair[1]);
448
+ }
449
+ });
450
+ this.corridorLinePoints = this.corridorLinePoints.concat(segmentIntersectionPointList);
451
+
452
+ let levelChangerGroupMap = new Map();
453
+
454
+ this.levelChangerList.forEach(levelChanger => {
455
+
456
+ // Create level changer groups
457
+ if (levelChanger.properties.group !== undefined) {
458
+ // Get group array, initiate if neccessary
459
+ let groupId = levelChanger.properties.group;
460
+ if (!levelChangerGroupMap.has(groupId)) levelChangerGroupMap.set(groupId, []);
461
+ let group = levelChangerGroupMap.get(groupId);
462
+ // Add lc to group map
463
+ group.push(levelChanger);
464
+ } else {
465
+ levelChangerGroupMap.set(levelChanger.id, [levelChanger]);
466
+ }
467
+
468
+ levelChanger.properties.fixedPointMap = new Map();
469
+ levelChanger.properties.levels.forEach(level => {
470
+ let point = this._copyPoint(levelChanger);
471
+ point.properties.level = level;
472
+ let fixedPoint = this._getFixPointInArea(point);
473
+ fixedPoint.id = levelChanger.id;
474
+ fixedPoint.properties.amenity = levelChanger.properties.amenity;
475
+ fixedPoint.properties.direction = levelChanger.properties.direction;
476
+ fixedPoint.properties.id = levelChanger.properties.id;
477
+ fixedPoint.properties.level = level;
478
+ fixedPoint.properties.type = levelChanger.properties.type;
479
+ if (fixedPoint.properties.neighbours === undefined) fixedPoint.properties.neighbours = [];
480
+
481
+ // Do not fix level changers that are further than 5 meters from any path or area
482
+ if (this._distance(point, fixedPoint) > 5) {
483
+ return;
484
+ }
485
+
486
+ // Store fixed point into the level changer
487
+ levelChanger.properties.fixedPointMap.set(level, fixedPoint);
488
+
489
+ // Add neighbourhood for corridor
490
+ if (fixedPoint.properties.onCorridor) {
491
+ // fixedPoint.properties.neighbours = [...this.corridorLinePointPairs[fixedPoint.properties.corridorIndex], ...segmentIntersectionPointMap.get(fixedPoint.properties.corridorIndex)];
492
+ if (fixedPoint.properties.neighboursLeadingTo !== undefined) {
493
+ fixedPoint.properties.neighboursLeadingTo.forEach(neighbour => {
494
+ if (neighbour.properties.neighbours === undefined) neighbour.properties.neighbours = [];
495
+ neighbour.properties.neighbours.push(fixedPoint);
496
+ });
497
+ this.corridorLineFeatures[fixedPoint.properties.corridorIndex].properties.intersectionPointList.push(fixedPoint);
498
+ }
499
+ }
500
+ });
501
+ });
502
+
503
+ levelChangerGroupMap.forEach( (lcList, groupId) => {
504
+
505
+ let direction = lcList.map(it => it.properties.direction).find(it => it !== undefined);
506
+ let fixedPointList = [];
507
+ lcList.forEach(lc => {
508
+ fixedPointList.push(...lc.properties.fixedPointMap.values());
509
+ });
510
+
511
+ fixedPointList.forEach(fixedPoint => {
512
+ // init neighbour
513
+ if (fixedPoint.properties.neighbours == undefined) fixedPoint.properties.neighbours = [];
514
+ // Set neighbourhood
515
+ if (direction === 'up') {
516
+ fixedPoint.properties.neighbours.push(...fixedPointList.filter(it => it.properties.level > fixedPoint.properties.level))
517
+ } else if (direction === 'down') {
518
+ fixedPoint.properties.neighbours.push(...fixedPointList.filter(it => it.properties.level < fixedPoint.properties.level))
519
+ } else {
520
+ fixedPoint.properties.neighbours.push(...fixedPointList.filter(it => it.properties.level !== fixedPoint.properties.level))
521
+ }
522
+ });
523
+ });
524
+ }
525
+
526
+ _removeItemFromList(list, item) {
527
+ let index = list.indexOf(item);
528
+ if (index >= 0) {
529
+ list.splice(index, 1);
530
+ }
531
+ }
532
+
533
+ load(neighboursMap, wallOffsets) {
534
+ this.neighbourMap = neighboursMap;
535
+ this.rebuildData();
536
+ this.wallOffsets = wallOffsets;
537
+ this.wallOffsetLineList = [];
538
+ this._getPointList().forEach((point, index) =>{
539
+ let offsetPoint = this.wallOffsets[index];
540
+ if (!offsetPoint) {
541
+ return;
542
+ }
543
+ // console.log(point);
544
+ let offsetLine = turf.lineString([point.geometry.coordinates, offsetPoint.geometry.coordinates]);
545
+ offsetLine.properties.level = point.properties.level;
546
+ this.wallOffsetLineList.push(offsetLine);
547
+ });
548
+ // drawLayer('offsetLayer', turf.featureCollection(this.wallOffsetLineList));
549
+ }
550
+
551
+ /**
552
+ * @param point {Feature<Point>}
553
+ * @param level {Number}
554
+ * @private true
555
+ */
556
+ _isPointOnLevel(point, level) {
557
+ if (point.properties.fixedPointMap !== undefined) {
558
+ return point.properties.fixedPointMap.has(level)
559
+ } else if (point.properties.level === level) {
560
+ return true;
561
+ } else {
562
+ return false;
563
+ }
564
+ }
565
+
566
+ _getPointList() {
567
+ let points = [];
568
+ this.floorData.forEach((data, level) => {
569
+ points = points.concat(data.points);
570
+ });
571
+ this.levelChangerList.forEach(lc => points.push(...lc.properties.fixedPointMap.values()));
572
+ points = points.concat();
573
+ points = points.concat(this.corridorLinePoints);
574
+ return points;
575
+ }
576
+
577
+ /**
578
+ *
579
+ * @returns {{neighbourhood: Object, wallOffsets: Object}}
580
+ */
581
+ preprocess() {
582
+ return {
583
+ neighbourhood: this._generateNeighbourhoodMap(),
584
+ wallOffsets: this._generateWallOffsets()
585
+ }
586
+ }
587
+
588
+ _generateNeighbourhoodMap() {
589
+
590
+ // this.nbLines = [];
591
+
592
+ let points = this._getPointList();
593
+ let neighboursMap = {};
594
+
595
+ // NeighbourMap for polygon points
596
+ this.floorData.forEach((levelFloorData, level) => {
597
+ let levelNeighboursMap = {};
598
+ let levelPoints = levelFloorData.points.concat(this.levelChangerList.filter(point => this._isPointOnLevel(point, level)).map(it => it.properties.fixedPointMap.get(level)));
599
+ levelPoints.forEach(point => {
600
+ let pointIndex = points.indexOf(point);
601
+ // Get unwrapped point if case the point is a level changer, so we can properly test neighbourhood
602
+ let unwrappedPoint = this._unwrapLevelChangerPoint(point, level);
603
+ // Simulate startPoint to force lowering number of intersections allowed.
604
+ // Unwrapped point is inside accessible area, thus there should be only one intersection, wall point itself.
605
+ let startPoint = unwrappedPoint === point ? null : unwrappedPoint;
606
+ let neighbours = this._findNeighbours(unwrappedPoint, startPoint, null, levelPoints);
607
+ // if (level === 1) {
608
+ // neighbours.forEach(neighbour => this.nbLines.push(turf.lineString([point.geometry.coordinates, neighbour.geometry.coordinates])));
609
+ // }
610
+ levelNeighboursMap[pointIndex] = neighbours.map(neighbour => points.indexOf(neighbour));
611
+ });
612
+ neighboursMap[level] = levelNeighboursMap;
613
+ });
614
+
615
+ // NeighbourMap for corridor points
616
+ this.corridorLinePoints.forEach(point => {
617
+
618
+ let pointIndex = points.indexOf(point);
619
+ let level = point.properties.level;
620
+ let neighbours;
621
+ let levelNeighboursMap = neighboursMap[level];
622
+
623
+ // Find neighbours in polygon only for points crossing polygon
624
+ if (point.properties.bordersArea) {
625
+ let potentialPoints = this.floorData.get(level).points
626
+ .concat(this.levelChangerList.filter(point => this._isPointOnLevel(point, level)))
627
+ .concat(this.corridorLinePoints.filter(point => point.properties.bordersArea && this._isPointOnLevel(point, level)));
628
+ neighbours = this._findNeighbours(point, point, null, potentialPoints);
629
+
630
+ // neighbours.forEach(neighbour => this.nbLines.push(turf.lineString([point.geometry.coordinates, neighbour.geometry.coordinates])));
631
+
632
+ // Add reverse relationship
633
+ neighbours.forEach(neighbour => {
634
+ let neighbourIndex = points.indexOf(neighbour);
635
+ if (levelNeighboursMap[neighbourIndex] === undefined) {
636
+ levelNeighboursMap[neighbourIndex] = [];
637
+ }
638
+ if (!levelNeighboursMap[neighbourIndex].includes(pointIndex)) {
639
+ levelNeighboursMap[neighbourIndex].push(pointIndex);
640
+ }
641
+ });
642
+ } else {
643
+ neighbours = point.properties.neighbours;
644
+
645
+ // neighbours.forEach(neighbour => this.nbLines.push(turf.lineString([point.geometry.coordinates, neighbour.geometry.coordinates])));
646
+
647
+ }
648
+
649
+ // Store relationship for corridor point
650
+ if (levelNeighboursMap[pointIndex] === undefined) {
651
+ levelNeighboursMap[pointIndex] = neighbours.map(neighbour => points.indexOf(neighbour));
652
+ } else {
653
+ neighbours.forEach(neighbour => levelNeighboursMap[pointIndex].push(points.indexOf(neighbour)));
654
+ }
655
+
656
+ });
657
+
658
+ // Export and store data
659
+ // console.log('neighbourMap:');
660
+ // console.log(JSON.stringify(neighboursMap));
661
+ this.neighbourMap = neighboursMap;
662
+ return neighboursMap;
663
+ }
664
+
665
+ _generateWallOffsets() {
666
+ this.wallOffsetLineList = [];
667
+ this.wallOffsets = {};
668
+ let pointList = this._getPointList();
669
+ pointList.forEach(point => {
670
+ // Do no process level changers
671
+ if (point.properties.level == null) {
672
+ return;
673
+ }
674
+
675
+ // a) Find walls where the point P is used and the other points in walls: A, B
676
+ let walls = this.floorData.get(point.properties.level).walls.filter(wall => (wall.includes(point)));
677
+
678
+ if (walls.length === 0) {
679
+ return;
680
+ }
681
+
682
+ let pointA = walls[0][0] === point ? walls[0][1] : walls[0][0];
683
+ let pointB = walls[1][0] === point ? walls[1][1] : walls[1][0];
684
+ let pointABearing = turf.bearing(point, pointA);
685
+ let pointBBearing = turf.bearing(point, pointB);
686
+
687
+ // b) Get average bearing to points A,B
688
+ let bearing = this._averageBearing(pointABearing, pointBBearing);
689
+ let oppositeBearing = bearing > 0 ? (bearing - 180) : (bearing + 180);
690
+ // this.wallOffsetLineList.push(turf.lineString([point.geometry.coordinates, offsetPoint.geometry.coordinates]));
691
+
692
+
693
+ // c) Generate two points M,N very close to point P
694
+ let pointM = turf.destination(point.geometry.coordinates, 0.01, bearing, {units: 'meters'});
695
+ let pointN = turf.destination(point.geometry.coordinates, 0.01, oppositeBearing, {units: 'meters'});
696
+
697
+ // d) Test which point is contained within accessible area
698
+ let containedPoint = null;
699
+ for (let areaIndex in this.floorData.get(point.properties.level).areas) {
700
+ let area = this.floorData.get(point.properties.level).areas[areaIndex];
701
+ if (turf.booleanContains(area, pointM)) {
702
+ containedPoint = pointM;
703
+ break;
704
+ } else if (turf.booleanContains(area, pointN)) {
705
+ containedPoint = pointN;
706
+ break;
707
+ }
708
+ }
709
+ // Stop if either of points is not contained...
710
+ if (containedPoint == null) {
711
+ return;
712
+ }
713
+
714
+ // e) Generate point F at double the distance of wall offset
715
+ let pointF = turf.destination(point.geometry.coordinates, this.wallOffsetDistance * 2, containedPoint === pointM ? bearing : oppositeBearing, {units: 'meters'});
716
+
717
+ // f) Test if PF intersects with any wall, update point F and PF to shortest available size
718
+ let linePF = turf.lineString([point.geometry.coordinates, pointF.geometry.coordinates]);
719
+ this.floorData.get(point.properties.level).walls.forEach((wall, index) => {
720
+ // Do not test walls containing point P, they will intersect of course
721
+ if (walls.includes(wall)) {
722
+ return;
723
+ }
724
+ let lineWall = this.floorData.get(point.properties.level).wallFeatures[index];
725
+ // Find intersection point, use it to produce new
726
+ let intersections = turf.lineIntersect(linePF, lineWall);
727
+ if (intersections.features.length > 0) {
728
+ pointF = turf.point(intersections.features[0].geometry.coordinates);
729
+ linePF = turf.lineString([point.geometry.coordinates, pointF.geometry.coordinates]);
730
+ }
731
+ });
732
+
733
+ // g) Create wall offset point as midpoint between points P,F
734
+ let offsetPoint = turf.midpoint(point.geometry.coordinates, pointF.geometry.coordinates);
735
+ offsetPoint.properties.level = point.properties.level;
736
+ this.wallOffsets[pointList.indexOf(point)] = offsetPoint;
737
+ let offsetLine = turf.lineString([point.geometry.coordinates, offsetPoint.geometry.coordinates]);
738
+ offsetLine.properties.level = point.properties.level;
739
+ this.wallOffsetLineList.push(offsetLine);
740
+ });
741
+
742
+ // console.log('wallOffsets:');
743
+ // console.log(JSON.stringify(this.wallOffsets));
744
+ return this.wallOffsets;
745
+ }
746
+
747
+ /**
748
+ * @param current {Feature<Point>}
749
+ * @return {[Feature<Point>]}
750
+ */
751
+ reconstructPath(current) {
752
+ let path = [];
753
+ let previous = current;
754
+ do {
755
+ let pointsToInject = this._calculateWallOffsetPointList(current, path.length > 0 ? path[path.length - 1] : previous);
756
+ pointsToInject.forEach((point) => {
757
+ if (previous === current || previous.geometry.coordinates[0] !== point.geometry.coordinates[0] || previous.geometry.coordinates[1] !== point.geometry.coordinates[1]) {
758
+ let newPoint = this._copyPoint(point);
759
+ newPoint.properties.level = current.properties.level;
760
+ path.push(newPoint);
761
+ previous = point;
762
+ }
763
+ });
764
+ current = current.properties.cameFrom;
765
+ } while (current != null);
766
+
767
+ path.reverse();
768
+ // let pathCoordinates = path.map(point => point.geometry.coordinates);
769
+ return path;
770
+ }
771
+
772
+ _calculateWallOffsetPointList(currentPoint, previousPoint) {
773
+ let pointList = this._getPointList();
774
+ let currentPointIndex = pointList.indexOf(currentPoint);
775
+ let previousPointIndex = pointList.indexOf(previousPoint);
776
+ let offsetPointList = [];
777
+ let currentOffsetPoint = currentPoint;
778
+ // a) offset current point
779
+ if (currentPointIndex >= 0 && this.wallOffsets[currentPointIndex]) {
780
+ currentOffsetPoint = this.wallOffsets[currentPointIndex];
781
+ }
782
+ offsetPointList.push(currentOffsetPoint);
783
+ let potentialOffsetPoints;
784
+ do {
785
+ let line = turf.lineString([previousPoint.geometry.coordinates, currentOffsetPoint.geometry.coordinates]);
786
+ potentialOffsetPoints = [];
787
+ this.wallOffsetLineList.forEach((wallOffsetLine, index) => {
788
+ // Do not process wall offsets from another floor
789
+ if (wallOffsetLine.properties.level !== currentPoint.properties.level) {
790
+ return;
791
+ }
792
+ // Do not process wall offsets with previous or current point
793
+ if (index === previousPointIndex || index === currentPointIndex) {
794
+ return;
795
+ }
796
+ let intersection = turf.lineIntersect(line, wallOffsetLine);
797
+ if (intersection.features.length > 0) {
798
+ let offsetPoint = this.wallOffsets[index];
799
+ // store distance to previousPoint
800
+ offsetPoint.properties.distance = this._distance(intersection.features[0], currentOffsetPoint);
801
+ offsetPoint.properties.offsetIndex = index;
802
+ potentialOffsetPoints.push(offsetPoint);
803
+ }
804
+ });
805
+ if (potentialOffsetPoints.length > 0) {
806
+ potentialOffsetPoints.sort((a, b) => (a.properties.distance - b.properties.distance));
807
+ currentOffsetPoint = potentialOffsetPoints[0];
808
+ currentPointIndex = currentOffsetPoint.properties.offsetIndex;
809
+ offsetPointList.push(currentOffsetPoint);
810
+ }
811
+ } while (potentialOffsetPoints.length > 0);
812
+
813
+ return offsetPointList.reverse();
814
+ }
815
+
816
+ _getIntersectingOffsetPoints(current, previous) {
817
+ if (current === previous || current.properties.level !== previous.properties.level) {
818
+ return [];
819
+ }
820
+ let line = turf.lineString([current.geometry.coordinates, previous.geometry.coordinates]);
821
+ let intersectingWallOffsetPoints = [];
822
+ this.wallOffsetLineList.forEach((wallOffsetLine, index) => {
823
+ if (wallOffsetLine.properties.level !== current.properties.level) {
824
+ return;
825
+ }
826
+ if (turf.lineIntersect(line, wallOffsetLine).features.length > 0) {
827
+ let point = this.wallOffsets[index];
828
+ point.properties.distance = turf.pointToLineDistance(current.geometry.coordinates, wallOffsetLine, {units: 'meters'});
829
+ intersectingWallOffsetPoints.push(point);
830
+ }
831
+ });
832
+ // let line
833
+
834
+ // console.log('//////////////');
835
+ // console.log(intersectingWallOffsetPoints);
836
+ intersectingWallOffsetPoints.sort((a,b) => (b.properties.distance - b.properties.distance));
837
+ // console.log(intersectingWallOffsetPoints);
838
+ return intersectingWallOffsetPoints;
839
+ }
840
+
841
+ clearData() {
842
+ this.floorData.forEach((data, level) => {
843
+ for (let index in data.points) {
844
+ let point = data.points[index];
845
+ delete point.properties.cameFrom;
846
+ delete point.properties.gscore;
847
+ delete point.properties.fscore;
848
+ }
849
+ });
850
+ this.levelChangerList.forEach(levelChanger => {
851
+ levelChanger.properties.fixedPointMap.forEach((it, level) => {
852
+ delete it.properties.cameFrom;
853
+ delete it.properties.gscore;
854
+ delete it.properties.fscore;
855
+ });
856
+ });
857
+ this.corridorLinePoints.forEach(point => {
858
+ delete point.properties.cameFrom;
859
+ delete point.properties.gscore;
860
+ delete point.properties.fscore;
861
+ });
862
+ }
863
+
864
+ /**
865
+ *
866
+ * @param startPoint {Feature<Point>}
867
+ * @param endPoint {Feature<Point>}
868
+ * @return {[Feature<Point>]}
869
+ * @private
870
+ */
871
+ runAStar(startPoint, endPoint) {
872
+ this.clearData();
873
+
874
+ this.nbLines = [];
875
+
876
+ this.bearingCache = new Map();
877
+
878
+ let fixedStartPoint = this._getFixPointInArea(startPoint);
879
+ let fixedEndPoint = this._getFixEndPoint(endPoint, startPoint.properties.level);
880
+
881
+ let openSet = [fixedStartPoint];
882
+ let closedSet = [];
883
+
884
+ fixedStartPoint.properties.gscore = 0;
885
+ fixedStartPoint.properties.fscore = this._heuristic(fixedStartPoint, fixedEndPoint);
886
+
887
+ while (openSet.length > 0) {
888
+ let current = this._getMinFScore(openSet);
889
+
890
+ // Unable to find best point to continue?
891
+ if (current === null) {
892
+ break;
893
+ }
894
+
895
+ if (current === fixedEndPoint) {
896
+ // console.log('found the route!');
897
+ let finalPath = this.reconstructPath(current);
898
+ if (fixedEndPoint !== endPoint && endPoint.properties.levels !== undefined && (!fixedEndPoint.properties.onCorridor || this._distance(fixedEndPoint, endPoint) > this._pathFixDistance)) {
899
+ endPoint.properties.fixed = true
900
+ finalPath.push(endPoint);
901
+ }
902
+ if (fixedStartPoint !== startPoint && (!fixedStartPoint.properties.onCorridor || this._distance(fixedStartPoint, startPoint) > this._pathFixDistance)) {
903
+ startPoint.properties.fixed = true
904
+ finalPath.unshift(startPoint);
905
+ }
906
+ finalPath[finalPath.length - 1].properties.gscore = current.properties.gscore;
907
+ return finalPath
908
+ }
909
+ closedSet.push(openSet.splice(openSet.indexOf(current),1));
910
+
911
+ let neighbours = this._getNeighbours(current, fixedStartPoint, fixedEndPoint);
912
+
913
+ neighbours.forEach(n => this.nbLines.push(turf.lineString([current.geometry.coordinates, n.geometry.coordinates])));
914
+
915
+ for (let nIndex in neighbours) {
916
+ let neighbour = neighbours[nIndex];
917
+ if (closedSet.indexOf(neighbour) > -1) {
918
+ continue;
919
+ }
920
+
921
+ let tentativeGScore = current.properties.gscore + this._distance(current, neighbour);
922
+ let gScoreNeighbour = neighbour.properties.gscore != null ? neighbour.properties.gscore : Infinity;
923
+ if (tentativeGScore < gScoreNeighbour) {
924
+ neighbour.properties.cameFrom = current;
925
+ neighbour.properties.gscore = tentativeGScore + 0.2;
926
+ neighbour.properties.fscore = tentativeGScore + this._heuristic(neighbour, fixedEndPoint);
927
+ if (openSet.indexOf(neighbour) < 0) {
928
+ openSet.push(neighbour);
929
+ }
930
+ }
931
+
932
+ }
933
+ }
934
+
935
+ // console.log('no path found?');
936
+ return undefined;
937
+
938
+ }
939
+
940
+ /**
941
+ *
942
+ * @param point {Feature<Point>}
943
+ * @param startPoint {Feature<Point>}
944
+ * @param endPoint {Feature<Point>}
945
+ * @return {Point[]}
946
+ * @private
947
+ */
948
+ _getNeighbours(point, startPoint, endPoint) {
949
+ let neighbours = [];
950
+ if (point === startPoint) {
951
+ let levelPoints = this._getPointList().filter(proposedPoint => this._isPointOnLevel(proposedPoint, point.properties.level));
952
+ neighbours = this._findNeighbours(point, startPoint, endPoint, levelPoints);
953
+ neighbours = neighbours.filter(neighbourPoint => {
954
+ // if (neighbourPoint === endPoint) {
955
+ // console.log('testing endPoint');
956
+ // }
957
+ let level = point.properties.level;
958
+ let revolvingDoorBlock = this.configuration.avoidRevolvingDoors && this._testAccessibilityPoiNeighbourhood(point, neighbourPoint, level, this.POI_TYPE.REVOLVING_DOOR);
959
+ let ticketGateBlock = this.configuration.avoidTicketGates && this._testAccessibilityPoiNeighbourhood(point, neighbourPoint, level, this.POI_TYPE.TICKET_GATE);
960
+ return !revolvingDoorBlock && !ticketGateBlock;
961
+ });
962
+ } else {
963
+ // Gather neighbours over all levels
964
+ let points = this._getPointList();
965
+ let pointIndex = points.indexOf(point);
966
+ if (pointIndex >= 0) {
967
+ this.floorData.forEach((_, level) => {
968
+ let levelNeighbourMap = this.neighbourMap[level];
969
+ if (levelNeighbourMap.hasOwnProperty(pointIndex)) {
970
+ levelNeighbourMap[pointIndex].forEach(neighbourIndex => {
971
+ let neighbourPoint = points[neighbourIndex];
972
+
973
+ let revolvingDoorBlock = this.configuration.avoidRevolvingDoors && this._testAccessibilityPoiNeighbourhood(point, neighbourPoint, level, this.POI_TYPE.REVOLVING_DOOR);
974
+ let ticketGateBlock = this.configuration.avoidTicketGates && this._testAccessibilityPoiNeighbourhood(point, neighbourPoint, level, this.POI_TYPE.TICKET_GATE);
975
+
976
+ if (!neighbours.includes(neighbourPoint) && !revolvingDoorBlock && !ticketGateBlock) {
977
+ neighbours.push(neighbourPoint);
978
+ }
979
+ });
980
+ }
981
+ });
982
+ }
983
+
984
+ // Test if endpoint is neighbour
985
+ if (
986
+ (point.properties.level !== undefined && point.properties.level === endPoint.properties.level)
987
+ || (point.properties.fixedPointMap != undefined && point.properties.fixedPointMap.has(endPoint.properties.level))
988
+ ) {
989
+
990
+ let revolvingDoorBlock = this.configuration.avoidRevolvingDoors && this._testAccessibilityPoiNeighbourhood(point, endPoint, endPoint.properties.level, this.POI_TYPE.REVOLVING_DOOR);
991
+ let ticketGateBlock = this.configuration.avoidTicketGates && this._testAccessibilityPoiNeighbourhood(point, endPoint, endPoint.properties.level, this.POI_TYPE.TICKET_GATE);
992
+
993
+ if (!revolvingDoorBlock && !ticketGateBlock) {
994
+
995
+ // Endpoint is fixed on corridor
996
+ if (endPoint.properties.onCorridor) {
997
+
998
+ if (endPoint.properties.neighbours.includes(point)) {
999
+ if (this.corridorLineFeatures[endPoint.properties.corridorIndex].properties.bidirectional != false) {
1000
+ neighbours.push(endPoint);
1001
+ }
1002
+ } else if (endPoint.properties.neighboursLeadingTo !== undefined && endPoint.properties.neighboursLeadingTo.includes(point)) {
1003
+ neighbours.push(endPoint);
1004
+ }
1005
+
1006
+ // End point is not on corridor, therefore should be in the area
1007
+ } else {
1008
+ let unwrapped = this._unwrapLevelChangerPoint(point, endPoint.properties.level);
1009
+ let allowedIntersections = 1;
1010
+ if (unwrapped.properties.isCorridorPoint || unwrapped.properties.onCorridor) {
1011
+ allowedIntersections--;
1012
+ }
1013
+ if (this._countIntersections(unwrapped, endPoint, allowedIntersections)) {
1014
+ neighbours.push(endPoint);
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ return neighbours.filter(neighbour => {
1021
+ if (this.configuration.avoidElevators && neighbour.properties.type === this.POI_TYPE.ELEVATOR) {
1022
+ return false;
1023
+ } else if (this.configuration.avoidEscalators && neighbour.properties.type === this.POI_TYPE.ESCALATOR) {
1024
+ return false;
1025
+ } else if (this.configuration.avoidStaircases && neighbour.properties.type === this.POI_TYPE.STAIRCASE) {
1026
+ return false;
1027
+ } else if (this.configuration.avoidNarrowPaths && neighbour.properties.narrowPath) {
1028
+ return false;
1029
+ } else if (this.configuration.avoidRamps && neighbour.properties.ramp) {
1030
+ return false;
1031
+ }
1032
+ return true;
1033
+ });
1034
+ }
1035
+
1036
+ /**
1037
+ *
1038
+ * @param pointA {Feature<Point>}
1039
+ * @param pointB {Feature<Point>}
1040
+ * @param level {Number}
1041
+ * @param accesibilityType {String}
1042
+ * @returns {boolean}
1043
+ * @private
1044
+ */
1045
+ _testAccessibilityPoiNeighbourhood(pointA, pointB, level, accesibilityType) {
1046
+ // console.log('testing accesibility for ' + accesibilityType);
1047
+ // Filter out lines that intersect revolving door POIs.
1048
+ if (this.configuration.avoidRevolvingDoors) {
1049
+ let line = turf.lineString([pointA.geometry.coordinates, pointB.geometry.coordinates]);
1050
+ let poiList = this.accessibilityPoi.filter(poi => (poi.properties.level === level && poi.properties.type === accesibilityType));
1051
+ for (let i = 0; i < poiList.length; i++) {
1052
+ let poi = poiList[i];
1053
+ let distance = turf.pointToLineDistance(poi.geometry.coordinates, line, {units: 'meters'});
1054
+ // console.log(distance);
1055
+ if (distance <= poi.properties.radius) {
1056
+ // console.log('accesibility for ' + accesibilityType + ' endend as true');
1057
+ return true;
1058
+ }
1059
+ }
1060
+ }
1061
+ // console.log('accesibility for ' + accesibilityType + ' endend as false');
1062
+ return false;
1063
+ }
1064
+
1065
+ /**
1066
+ * @param point {Feature<Point>}
1067
+ * @param startPoint {Feature<Point>}
1068
+ * @param endPoint {Feature<Point>}
1069
+ * @param proposedPointList {[Feature<Point>]}
1070
+ * @return {[Point]}
1071
+ */
1072
+ _findNeighbours(point, startPoint, endPoint, proposedPointList) {
1073
+ let neighbours = [];
1074
+ if (point.properties.neighbours != null) {
1075
+ neighbours = [...point.properties.neighbours];
1076
+ }
1077
+
1078
+ // Start point is on corridor line, use only preset neighbours on line
1079
+ if ((point === startPoint && point.properties.onCorridor) || (point.properties.isCorridorPoint && !point.properties.bordersArea)) {
1080
+ // End point is on the same corridor line == they are neighbours
1081
+ if (endPoint && endPoint.properties.onCorridor && startPoint.properties.corridorIndex === endPoint.properties.corridorIndex) {
1082
+ neighbours.push(endPoint);
1083
+ }
1084
+ return neighbours;
1085
+ }
1086
+
1087
+
1088
+ let allowedIntersections = 0;
1089
+ if (endPoint && this._isPointOnLevel(endPoint, point.properties.level)) {
1090
+ proposedPointList.push(endPoint);
1091
+ }
1092
+
1093
+ let fixedPoint = this._unwrapLevelChangerPoint(point, point.properties.level);
1094
+
1095
+ for (let index in proposedPointList) {
1096
+ let proposedPoint = proposedPointList[index];
1097
+
1098
+ // Same point is not neighbour with itself
1099
+ if (point === proposedPoint) {
1100
+ continue;
1101
+ }
1102
+
1103
+ // Already assigned
1104
+ if (neighbours.indexOf(proposedPoint) >= 0) {
1105
+ continue;
1106
+ }
1107
+
1108
+ let fixedProposedPoint = this._unwrapLevelChangerPoint(proposedPoint, point.properties.level);
1109
+
1110
+ allowedIntersections = 2;
1111
+ if (point === startPoint) {
1112
+ allowedIntersections--;
1113
+ }
1114
+ if (proposedPoint === startPoint) {
1115
+ allowedIntersections --;
1116
+ }
1117
+ if (point !== fixedPoint) {
1118
+ allowedIntersections--;
1119
+ }
1120
+ if (proposedPoint !== fixedProposedPoint) {
1121
+ allowedIntersections--;
1122
+ }
1123
+ if (proposedPoint.properties.isCorridorPoint || proposedPoint.properties.onCorridor) {
1124
+ allowedIntersections--;
1125
+ }
1126
+
1127
+ let intersects = this._countIntersections(point, fixedProposedPoint, allowedIntersections);
1128
+
1129
+ if (intersects) {
1130
+ // if (allowedIntersections >= 1) {
1131
+ let midpoint = turf.midpoint(point.geometry.coordinates, proposedPoint.geometry.coordinates);
1132
+ for (let polIndex in this.floorData.get(point.properties.level).areas) {
1133
+ let area = this.floorData.get(point.properties.level).areas[polIndex];
1134
+ if (turf.booleanContains(area, midpoint)) {
1135
+ neighbours.push(proposedPoint);
1136
+ break;
1137
+ }
1138
+ }
1139
+ // } else {
1140
+ // neighbours.push(proposedPoint);
1141
+ // }
1142
+ }
1143
+
1144
+
1145
+ //---
1146
+ // if (point === startPoint || proposedPoint === endPoint) {
1147
+ // if (point === startPoint && proposedPoint === endPoint) {
1148
+ // allowedIntersections = 0;
1149
+ // } else {
1150
+ // allowedIntersections = 1;
1151
+ // }
1152
+ // let intersections = this._countIntersections(point, proposedPoint, allowedIntersections);
1153
+ // if (intersections === true) {
1154
+ // neighbours.push(proposedPoint);
1155
+ // }
1156
+ // } else {
1157
+ // let intersections = this._countIntersections(point, proposedPoint, 2);
1158
+ // if (intersections === true) {
1159
+ // let midpoint = turf.midpoint(point.geometry.coordinates, proposedPoint.geometry.coordinates);
1160
+ // for (let polIndex in this.polygons) {
1161
+ // let polygon = this.polygons[polIndex];
1162
+ // if (turf.booleanContains(polygon, midpoint)) {
1163
+ // neighbours.push(proposedPoint);
1164
+ // break;
1165
+ // }
1166
+ // }
1167
+ // }
1168
+ // }
1169
+ }
1170
+
1171
+ return neighbours;
1172
+ }
1173
+
1174
+ /**
1175
+ * @param pointA {Feature<Point>}
1176
+ * @param pointB {Feature<Point>}
1177
+ * @private {Boolean} true if points are on the same level
1178
+ */
1179
+ _comparePointLevels(pointA, pointB) {
1180
+
1181
+ // If both points are NOT level changers
1182
+ if (pointA.properties.levels == null && pointB.properties.levels == null) {
1183
+ return pointA.properties.level === pointB.properties.level;
1184
+ }
1185
+
1186
+ // At least one of points is level changer
1187
+ let pointALevelList = pointA.properties.fixedPointMap !== undefined ? [...pointA.properties.fixedPointMap.keys()] : [pointA.properties.level];
1188
+ let pointBLevelList = pointB.properties.fixedPointMap !== undefined ? [...pointB.properties.fixedPointMap.keys()] : [pointB.properties.level];
1189
+ return pointALevelList.filter(feature => pointBLevelList.includes(feature)).length > 0;
1190
+
1191
+ }
1192
+
1193
+ /**
1194
+ * @param point {Feature<Point>}
1195
+ * @param level {Number}
1196
+ * @return {Feature<Point>}
1197
+ */
1198
+ _unwrapLevelChangerPoint(point, level) {
1199
+ let fixedPointMap = point.properties.fixedPointMap;
1200
+ if (fixedPointMap) {
1201
+ let fixedPoint = fixedPointMap.get(level);
1202
+ if (fixedPoint) {
1203
+ return fixedPoint;
1204
+ } else {
1205
+ return point;
1206
+ }
1207
+ } else {
1208
+ return point;
1209
+ }
1210
+ }
1211
+
1212
+ /**
1213
+ *
1214
+ * @param pointFrom {Point}
1215
+ * @param pointTo {Point}
1216
+ * @param maxIntersections {Number}
1217
+ * @return {Boolean}
1218
+ */
1219
+ _countIntersections(pointFrom, pointTo, maxIntersections) {
1220
+
1221
+ let fromCoordinates = pointFrom.geometry.coordinates;
1222
+ let toCoordinates = pointTo.geometry.coordinates;
1223
+ let bearing = this._bearing(fromCoordinates, toCoordinates);
1224
+ let intersections = 0;
1225
+ let intersectionPointList = [];
1226
+
1227
+ let floorWalls = this.floorData.get(pointFrom.properties.level).walls;
1228
+ let floorWallFeatures = this.floorData.get(pointFrom.properties.level).wallFeatures;
1229
+
1230
+ for (let index in floorWalls) {
1231
+ let wall = floorWalls[index];
1232
+
1233
+ let inRange = false;
1234
+ let pointIsInAWall = false;
1235
+ if (pointFrom == wall[0] || pointFrom == wall[1]) {
1236
+ inRange = true;
1237
+ pointIsInAWall = true;
1238
+ } else {
1239
+ let wBearingA = this._bearing(fromCoordinates, wall[0].geometry.coordinates);
1240
+ let wBearingB = this._bearing(fromCoordinates, wall[1].geometry.coordinates);
1241
+ if (wBearingA > wBearingB) {
1242
+ let temp = wBearingA;
1243
+ wBearingA = wBearingB;
1244
+ wBearingB = temp;
1245
+ }
1246
+ let bearingDiff = wBearingB - wBearingA;
1247
+ if (bearingDiff < Math.PI) {
1248
+ if (wBearingA <= bearing && bearing <= wBearingB) {
1249
+ inRange = true;
1250
+ }
1251
+ } else {
1252
+ if (wBearingA >= bearing || bearing >= wBearingB) {
1253
+ inRange = true;
1254
+ }
1255
+ }
1256
+ }
1257
+
1258
+ if (inRange) {
1259
+ if (pointIsInAWall) {
1260
+ if (!this._testIdenticalPointInList(pointFrom, intersectionPointList)) {
1261
+ intersectionPointList.push(pointFrom);
1262
+ intersections++;
1263
+ }
1264
+ } else {
1265
+ let intersectPoints = turf.lineIntersect(
1266
+ turf.lineString([fromCoordinates, toCoordinates]),
1267
+ floorWallFeatures[index]
1268
+ ).features;
1269
+ if (intersectPoints.length > 0) {
1270
+ if (!this._testIdenticalPointInList(intersectPoints[0], intersectionPointList)) {
1271
+ intersectionPointList.push(intersectPoints[0]);
1272
+ intersections++;
1273
+ }
1274
+ }
1275
+ }
1276
+ if (intersections > maxIntersections) {
1277
+ return false;
1278
+ }
1279
+ }
1280
+ }
1281
+ return true;
1282
+
1283
+ }
1284
+
1285
+ /**
1286
+ *
1287
+ * @private
1288
+ */
1289
+ _averageBearing(bearingA, bearingB) {
1290
+ if (bearingA > bearingB) {
1291
+ let temp = bearingA;
1292
+ bearingA = bearingB;
1293
+ bearingB = temp;
1294
+ }
1295
+ if (bearingB - bearingA > 180) {
1296
+ bearingB -= 360;
1297
+ }
1298
+ let finalBearing = (bearingB + bearingA) / 2;
1299
+ return finalBearing <= -180 ? 360 + finalBearing : finalBearing;
1300
+ }
1301
+
1302
+ // Converts from degrees to radians.
1303
+ _toRadians(degrees) {
1304
+ return degrees * Math.PI / 180;
1305
+ };
1306
+
1307
+ _bearing(start, end) {
1308
+ // let hasCache = false;
1309
+ let endCache = this.bearingCache.get(start);
1310
+ if (endCache) {
1311
+ let cache = endCache.get(end);
1312
+ if (cache != null) {
1313
+ return cache;
1314
+ }
1315
+ }
1316
+
1317
+ let startLng = this._toRadians(start[0]);
1318
+ let startLat = this._toRadians(start[1]);
1319
+ let destLng = this._toRadians(end[0]);
1320
+ let destLat = this._toRadians(end[1]);
1321
+ let cosDestLat = Math.cos(destLat);
1322
+
1323
+ let y = Math.sin(destLng - startLng) * cosDestLat;
1324
+ let x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * cosDestLat * Math.cos(destLng - startLng);
1325
+ let bearing = Math.atan2(y, x);
1326
+ this._storeBearingCache(start, end, bearing);
1327
+ return bearing;
1328
+ }
1329
+
1330
+ _storeBearingCache(start, end, bearing) {
1331
+ let cacheMap = this.bearingCache.get(start);
1332
+ if (!cacheMap) {
1333
+ cacheMap = new Map();
1334
+ this.bearingCache.set(start, cacheMap);
1335
+ }
1336
+ cacheMap.set(end, bearing);
1337
+
1338
+ cacheMap = this.bearingCache.get(end);
1339
+ if (!cacheMap) {
1340
+ cacheMap = new Map();
1341
+ this.bearingCache.set(end, cacheMap);
1342
+ }
1343
+ if (bearing <= 0) {
1344
+ bearing += Math.PI;
1345
+ } else {
1346
+ bearing -= Math.PI;
1347
+ }
1348
+ cacheMap.set(start, bearing);
1349
+ }
1350
+
1351
+ _testIdenticalPointInList(point, pointList) {
1352
+ let pointCoordinates = point.geometry.coordinates;
1353
+ for (let index in pointList) {
1354
+ let proposedPointCoordinates = pointList[index].geometry.coordinates;
1355
+ if (proposedPointCoordinates[0] === pointCoordinates[0] && proposedPointCoordinates[1] === pointCoordinates[1]) {
1356
+ return true;
1357
+ }
1358
+ }
1359
+ return false;
1360
+ }
1361
+
1362
+ /**
1363
+ *
1364
+ * @param pointSet {[Feature<Point>]}
1365
+ * @returns {Feature<Point>}
1366
+ */
1367
+ _getMinFScore(pointSet) {
1368
+ let bestPoint = null;
1369
+ let bestScore = Infinity;
1370
+ for (let index in pointSet) {
1371
+ let point = pointSet[index];
1372
+ if (point.properties.fscore < bestScore) {
1373
+ bestPoint = point;
1374
+ bestScore = point.properties.fscore;
1375
+ }
1376
+ }
1377
+ return bestPoint;
1378
+ }
1379
+
1380
+ /**
1381
+ *
1382
+ * @param pointA {Feature<Point>}
1383
+ * @param pointB {Feature<Point>}
1384
+ * @returns {*|number|undefined}
1385
+ */
1386
+ _heuristic(pointA, pointB) {
1387
+ if (this._comparePointLevels(pointA, pointB)) {
1388
+ let penalty = 0;
1389
+ if (pointA.properties.levels !== undefined || pointB.properties.levels !== undefined) {
1390
+ penalty = 20;
1391
+ }
1392
+ return this._distance(pointA, pointB) + penalty;
1393
+ } else {
1394
+ // Filter out direct level changers
1395
+ let directLevelChangerList = this.levelChangerList.filter(levelChanger => {
1396
+ return levelChanger !== pointA && levelChanger !== pointB
1397
+ && this._comparePointLevels(levelChanger, pointA)
1398
+ && this._comparePointLevels(levelChanger, pointB)
1399
+ });
1400
+
1401
+ // Calculate best estimation for direct level change
1402
+ let bestDistance = Infinity;
1403
+ directLevelChangerList.forEach(levelChanger => {
1404
+ let distance = this._distance(pointA, levelChanger) + this._distance(levelChanger, pointB) + 10;
1405
+ if (distance < bestDistance) {
1406
+ bestDistance = distance;
1407
+ }
1408
+ });
1409
+ // Return estimation if direct level change was found
1410
+ if (bestDistance < Infinity) {
1411
+ return bestDistance;
1412
+ }
1413
+ return 2000;
1414
+ }
1415
+ }
1416
+
1417
+ /**
1418
+ *
1419
+ * @param pointA {Feature}
1420
+ * @param pointB {Feature}
1421
+ * @returns {*|number|undefined}
1422
+ */
1423
+ _distance(pointA, pointB) {
1424
+ let levelChangePenalty = 0;
1425
+ if (pointB.properties.level !== pointA.properties.level) levelChangePenalty = 10;
1426
+ return turf.distance(pointA, pointB, {units:'meters'}) + levelChangePenalty;
1427
+ }
1428
+
1429
+ /**
1430
+ *
1431
+ * @private
1432
+ */
1433
+ _getFixEndPoint(endPoint, startPointLevel) {
1434
+ // LC
1435
+ if (endPoint.properties.fixedPointMap !== undefined) {
1436
+ let nearestLevel = undefined;
1437
+ endPoint.properties.fixedPointMap.keys().forEach(level => {
1438
+ if (nearestLevel === undefined || Math.abs(nearestLevel - startPointLevel) > Math.abs(level - startPointLevel)) {
1439
+ nearestLevel = level;
1440
+ }
1441
+ });
1442
+ endPoint = this._copyPoint(endPoint);
1443
+ endPoint.properties.level = nearestLevel;
1444
+ }
1445
+ return this._getFixPointInArea(endPoint);
1446
+ }
1447
+
1448
+ _getFixPointInArea(point) {
1449
+ let floorData = this.floorData.get(point.properties.level);
1450
+
1451
+ // If point is located without accessible area, do nothing
1452
+ let areaList = floorData.areas;
1453
+ for (let index in areaList) {
1454
+ let polygon = areaList[index];
1455
+ if (turf.booleanContains(polygon, point)) {
1456
+ return point;
1457
+ }
1458
+ }
1459
+
1460
+ // Find nearest wall to stick to
1461
+ let bestWall = null;
1462
+ let bestWallDistance = Infinity;
1463
+ floorData.wallFeatures.forEach(wall => {
1464
+ let distance = turf.pointToLineDistance(point.geometry.coordinates, wall, {units: 'meters'});
1465
+ if (distance < bestWallDistance) {
1466
+ bestWall = wall;
1467
+ bestWallDistance = distance;
1468
+ }
1469
+ });
1470
+
1471
+ let levelCorridorFeatures = this.corridorLineFeatures.filter(corridorLine => corridorLine.properties.level === point.properties.level);
1472
+ let bestCorridorIndex = null;
1473
+ let bestCorridorDistance = Infinity;
1474
+ levelCorridorFeatures.forEach(corridor => {
1475
+ let corridorIndex = this.corridorLineFeatures.indexOf(corridor);
1476
+ let corridorDistance = turf.pointToLineDistance(point.geometry.coordinates, corridor, {units: 'meters'});
1477
+ if (corridorDistance < bestCorridorDistance) {
1478
+ bestCorridorIndex = corridorIndex;
1479
+ bestCorridorDistance = corridorDistance;
1480
+ }
1481
+ });
1482
+
1483
+ // Test if area or corridor is closer, create appropriate fixed point
1484
+ if (bestWall === null && bestCorridorIndex === null) {
1485
+ // could not find neither close area or corridor
1486
+ return point;
1487
+ } else {
1488
+ let fixedPoint;
1489
+
1490
+ // Corridor is closer
1491
+ if (bestCorridorIndex !== undefined && bestCorridorDistance < bestWallDistance) {
1492
+
1493
+ // Create fixed point on line itself
1494
+ let line = this.corridorLineFeatures[bestCorridorIndex];
1495
+ fixedPoint = turf.nearestPointOnLine(line, point);
1496
+
1497
+ // Mark this fixed point is on corridor, preset neighbours
1498
+ fixedPoint.properties.onCorridor = true;
1499
+ fixedPoint.properties.corridorIndex = bestCorridorIndex;
1500
+ if (!fixedPoint.properties.neighbours) {
1501
+ fixedPoint.properties.neighbours = [];
1502
+ }
1503
+ if (this.corridorLineFeatures[bestCorridorIndex].properties.bidirectional != false) {
1504
+ fixedPoint.properties.neighbours.push(this.corridorLinePointPairs[bestCorridorIndex][0], this.corridorLinePointPairs[bestCorridorIndex][1]);
1505
+ fixedPoint.properties.neighbours.push(...line.properties.intersectionPointList);
1506
+ fixedPoint.properties.neighboursLeadingTo = [
1507
+ this.corridorLinePointPairs[bestCorridorIndex][0],
1508
+ this.corridorLinePointPairs[bestCorridorIndex][1],
1509
+ ...line.properties.intersectionPointList
1510
+ ];
1511
+ } else if (this.corridorLineFeatures[bestCorridorIndex].properties.swapDirection != true) {
1512
+ fixedPoint.properties.neighbours.push(this.corridorLinePointPairs[bestCorridorIndex][0]);
1513
+ // include only intersection points after this point
1514
+ let distance = this._distance(fixedPoint, this.corridorLinePointPairs[bestCorridorIndex][0]);
1515
+ let pointsBefore = line.properties.intersectionPointList.filter(point => this._distance(point, this.corridorLinePointPairs[bestCorridorIndex][0]) < distance);
1516
+ let pointsAfter = line.properties.intersectionPointList.filter(point => this._distance(point, this.corridorLinePointPairs[bestCorridorIndex][0]) >= distance);
1517
+ fixedPoint.properties.neighbours.push(...pointsAfter);
1518
+ fixedPoint.properties.neighboursLeadingTo = pointsBefore;
1519
+ } else {
1520
+ fixedPoint.properties.neighbours.push(this.corridorLinePointPairs[bestCorridorIndex][1]);
1521
+ // include only intersection points before this point
1522
+ let distance = this._distance(fixedPoint, this.corridorLinePointPairs[bestCorridorIndex][0]);
1523
+ let pointsBefore = line.properties.intersectionPointList.filter(point => this._distance(point, this.corridorLinePointPairs[bestCorridorIndex][0]) <= distance);
1524
+ let pointsAfter = line.properties.intersectionPointList.filter(point => this._distance(point, this.corridorLinePointPairs[bestCorridorIndex][0]) > distance);
1525
+ fixedPoint.properties.neighbours.push(...pointsBefore);
1526
+ fixedPoint.properties.neighboursLeadingTo = pointsAfter;
1527
+ }
1528
+
1529
+ // Wall is closer
1530
+ } else if (bestWall !== null) {
1531
+
1532
+ // Create fixed point inside area
1533
+ let nearestPoint = turf.nearestPointOnLine(bestWall, point);
1534
+ let bearing = turf.bearing(point, nearestPoint);
1535
+ fixedPoint = turf.destination(point.geometry.coordinates, bestWallDistance + 0.05, bearing, {units: 'meters'});
1536
+ }
1537
+
1538
+ // Mark level of fixed point
1539
+ fixedPoint.properties.level = point.properties.level;
1540
+
1541
+ // Return created point
1542
+ return fixedPoint;
1543
+
1544
+ }
1545
+ }
1546
+
1547
+ /**
1548
+ * @param point {Feature <Point>}
1549
+ * @return {Feature <Point>}
1550
+ */
1551
+ _copyPoint(pointFeature) {
1552
+ let point = turf.point([pointFeature.geometry.coordinates[0], pointFeature.geometry.coordinates[1]]);
1553
+ if (pointFeature.id !== undefined) point.id = pointFeature.id;
1554
+ if (pointFeature.properties.id !== undefined) point.properties.id = pointFeature.properties.id;
1555
+ if (pointFeature.properties.amenity !== undefined) point.properties.amenity = pointFeature.properties.amenity;
1556
+ if (pointFeature.properties.type !== undefined) point.properties.type = pointFeature.properties.type;
1557
+ return point;
1558
+ }
1559
+
1560
+ _setNeighbourhoodBasedOnCorridorDirectionality(segmentFeature, segmentPointA, segmentPointB, intersectionPoint) {
1561
+ if (intersectionPoint.properties.neighbours === undefined) {
1562
+ intersectionPoint.properties.neighbours = [];
1563
+ }
1564
+ if (segmentFeature.properties.bidirectional != false) {
1565
+ intersectionPoint.properties.neighbours.push(segmentPointA, segmentPointB);
1566
+ segmentPointA.properties.neighbours.push(intersectionPoint);
1567
+ segmentPointB.properties.neighbours.push(intersectionPoint);
1568
+ } else if (segmentFeature.properties.swapDirection === false) {
1569
+ segmentPointA.properties.neighbours.push(intersectionPoint);
1570
+ intersectionPoint.properties.neighbours.push(segmentPointB);
1571
+ } else {
1572
+ segmentPointB.properties.neighbours.push(intersectionPoint);
1573
+ intersectionPoint.properties.neighbours.push(segmentPointA);
1574
+ }
1575
+ }
1576
+
1577
+ _comparePointsByDistanceFromReference(reference, intersectionA, intersectionB) {
1578
+ let dA = turf.distance(reference, intersectionA);
1579
+ let dB = turf.distance(reference, intersectionB);
1580
+ if (dA > dB) return 1;
1581
+ if (dB > dA) return -1;
1582
+ return 0;
1583
+ }
1584
+ }
1585
+
1586
+ export default Wayfinding;