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.
- package/README.md +719 -0
- package/assets/proximiio-js-library.css +520 -0
- package/assets/tbtnav.js +3 -0
- package/assets/wayfinding.d.ts +3 -0
- package/assets/wayfinding.js +1586 -0
- package/lib/common.d.ts +7 -0
- package/lib/common.js +49 -0
- package/lib/components/map/constants.d.ts +6 -0
- package/lib/components/map/constants.js +30 -0
- package/lib/components/map/custom-layers.d.ts +5 -0
- package/lib/components/map/custom-layers.js +74 -0
- package/lib/components/map/icons.d.ts +23 -0
- package/lib/components/map/icons.js +64 -0
- package/lib/components/map/layers/any_layer.d.ts +9 -0
- package/lib/components/map/layers/any_layer.js +2 -0
- package/lib/components/map/layers/background_layer.d.ts +16 -0
- package/lib/components/map/layers/background_layer.js +52 -0
- package/lib/components/map/layers/base_layer.d.ts +18 -0
- package/lib/components/map/layers/base_layer.js +127 -0
- package/lib/components/map/layers/circle_layer.d.ts +25 -0
- package/lib/components/map/layers/circle_layer.js +62 -0
- package/lib/components/map/layers/fill_extrusion_layer.d.ts +21 -0
- package/lib/components/map/layers/fill_extrusion_layer.js +57 -0
- package/lib/components/map/layers/fill_layer.d.ts +21 -0
- package/lib/components/map/layers/fill_layer.js +57 -0
- package/lib/components/map/layers/heatmap_layer.d.ts +18 -0
- package/lib/components/map/layers/heatmap_layer.js +54 -0
- package/lib/components/map/layers/hillshade_layer.d.ts +19 -0
- package/lib/components/map/layers/hillshade_layer.js +55 -0
- package/lib/components/map/layers/line_layer.d.ts +28 -0
- package/lib/components/map/layers/line_layer.js +64 -0
- package/lib/components/map/layers/raster_layer.d.ts +21 -0
- package/lib/components/map/layers/raster_layer.js +57 -0
- package/lib/components/map/layers/symbol_layer.d.ts +71 -0
- package/lib/components/map/layers/symbol_layer.js +153 -0
- package/lib/components/map/main.d.ts +625 -0
- package/lib/components/map/main.js +1765 -0
- package/lib/components/map/metadata.d.ts +134 -0
- package/lib/components/map/metadata.js +135 -0
- package/lib/components/map/routing.d.ts +12 -0
- package/lib/components/map/routing.js +74 -0
- package/lib/components/map/sources/base_source.d.ts +10 -0
- package/lib/components/map/sources/base_source.js +38 -0
- package/lib/components/map/sources/cluster_source.d.ts +7 -0
- package/lib/components/map/sources/cluster_source.js +30 -0
- package/lib/components/map/sources/data_source.d.ts +17 -0
- package/lib/components/map/sources/data_source.js +46 -0
- package/lib/components/map/sources/geojson_source.d.ts +16 -0
- package/lib/components/map/sources/geojson_source.js +77 -0
- package/lib/components/map/sources/image_source_manager.d.ts +16 -0
- package/lib/components/map/sources/image_source_manager.js +131 -0
- package/lib/components/map/sources/routing_source.d.ts +22 -0
- package/lib/components/map/sources/routing_source.js +131 -0
- package/lib/components/map/sources/synthetic_source.d.ts +7 -0
- package/lib/components/map/sources/synthetic_source.js +32 -0
- package/lib/components/map/sources/vector_source.d.ts +8 -0
- package/lib/components/map/sources/vector_source.js +31 -0
- package/lib/components/select/main.d.ts +66 -0
- package/lib/components/select/main.js +195 -0
- package/lib/controllers/auth.d.ts +48 -0
- package/lib/controllers/auth.js +156 -0
- package/lib/controllers/floors.d.ts +14 -0
- package/lib/controllers/floors.js +105 -0
- package/lib/controllers/geo.d.ts +10 -0
- package/lib/controllers/geo.js +131 -0
- package/lib/controllers/places.d.ts +14 -0
- package/lib/controllers/places.js +108 -0
- package/lib/controllers/repository.d.ts +24 -0
- package/lib/controllers/repository.js +68 -0
- package/lib/controllers/style.d.ts +4 -0
- package/lib/controllers/style.js +76 -0
- package/lib/eventable.d.ts +7 -0
- package/lib/eventable.js +29 -0
- package/lib/index.d.ts +26 -0
- package/lib/index.js +14 -0
- package/lib/models/amenity.d.ts +11 -0
- package/lib/models/amenity.js +41 -0
- package/lib/models/auth-data.d.ts +4 -0
- package/lib/models/auth-data.js +2 -0
- package/lib/models/base.d.ts +7 -0
- package/lib/models/base.js +18 -0
- package/lib/models/feature.d.ts +46 -0
- package/lib/models/feature.js +277 -0
- package/lib/models/floor.d.ts +30 -0
- package/lib/models/floor.js +51 -0
- package/lib/models/geopoint.d.ts +6 -0
- package/lib/models/geopoint.js +2 -0
- package/lib/models/mapbox-options.d.ts +171 -0
- package/lib/models/mapbox-options.js +2 -0
- package/lib/models/person.d.ts +8 -0
- package/lib/models/person.js +18 -0
- package/lib/models/place.d.ts +14 -0
- package/lib/models/place.js +40 -0
- package/lib/models/poi_type.d.ts +19 -0
- package/lib/models/poi_type.js +25 -0
- package/lib/models/style.d.ts +53 -0
- package/lib/models/style.js +424 -0
- package/lib/proximiio.js +2 -0
- package/lib/proximiio.js.LICENSE.txt +79 -0
- 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;
|