venue-js 1.4.0-next.2 → 1.4.0-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/index.d.mts +4 -0
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.js +2572 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/index.mjs +2523 -0
- package/dist/data/index.mjs.map +1 -0
- package/dist/index-CrxL_5B7.d.mts +1025 -0
- package/dist/index-CrxL_5B7.d.ts +1025 -0
- package/dist/index.d.mts +10 -791
- package/dist/index.d.ts +10 -791
- package/dist/index.js +2181 -1060
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2168 -1050
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -1
|
@@ -0,0 +1,2523 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/data/index.ts
|
|
8
|
+
import { QueryObserver as QueryObserver2 } from "@tanstack/query-core";
|
|
9
|
+
|
|
10
|
+
// src/data/constant.ts
|
|
11
|
+
var DEFAULT_BASE_URL = "https://service.venue.in.th/api";
|
|
12
|
+
var IMDF_FEATURE_TYPES = [
|
|
13
|
+
"address",
|
|
14
|
+
"amenity",
|
|
15
|
+
"anchor",
|
|
16
|
+
"building",
|
|
17
|
+
"detail",
|
|
18
|
+
"fixture",
|
|
19
|
+
"footprint",
|
|
20
|
+
"geofence",
|
|
21
|
+
"kiosk",
|
|
22
|
+
"level",
|
|
23
|
+
"occupant",
|
|
24
|
+
"opening",
|
|
25
|
+
"relationship",
|
|
26
|
+
"section",
|
|
27
|
+
"unit",
|
|
28
|
+
"venue"
|
|
29
|
+
];
|
|
30
|
+
var IMDF_UNIT_CATEGORIES = [
|
|
31
|
+
"auditorium",
|
|
32
|
+
"brick",
|
|
33
|
+
"classroom",
|
|
34
|
+
"column",
|
|
35
|
+
"concrete",
|
|
36
|
+
"conferenceroom",
|
|
37
|
+
"drywall",
|
|
38
|
+
"elevator",
|
|
39
|
+
"escalator",
|
|
40
|
+
"fieldofplay",
|
|
41
|
+
"firstaid",
|
|
42
|
+
"fitnessroom",
|
|
43
|
+
"foodservice",
|
|
44
|
+
"footbridge",
|
|
45
|
+
"glass",
|
|
46
|
+
"huddleroom",
|
|
47
|
+
"kitchen",
|
|
48
|
+
"laboratory",
|
|
49
|
+
"library",
|
|
50
|
+
"lobby",
|
|
51
|
+
"lounge",
|
|
52
|
+
"mailroom",
|
|
53
|
+
"mothersroom",
|
|
54
|
+
"movietheater",
|
|
55
|
+
"movingwalkway",
|
|
56
|
+
"nonpublic",
|
|
57
|
+
"office",
|
|
58
|
+
"opentobelow",
|
|
59
|
+
"parking",
|
|
60
|
+
"phoneroom",
|
|
61
|
+
"platform",
|
|
62
|
+
"privatelounge",
|
|
63
|
+
"ramp",
|
|
64
|
+
"recreation",
|
|
65
|
+
"restroom",
|
|
66
|
+
"restroom.family",
|
|
67
|
+
"restroom.female",
|
|
68
|
+
"restroom.female.wheelchair",
|
|
69
|
+
"restroom.male",
|
|
70
|
+
"restroom.male.wheelchair",
|
|
71
|
+
"restroom.transgender",
|
|
72
|
+
"restroom.transgender.wheelchair",
|
|
73
|
+
"restroom.unisex",
|
|
74
|
+
"restroom.unisex.wheelchair",
|
|
75
|
+
"restroom.wheelchair",
|
|
76
|
+
"road",
|
|
77
|
+
"room",
|
|
78
|
+
"serverroom",
|
|
79
|
+
"shower",
|
|
80
|
+
"smokingarea",
|
|
81
|
+
"stairs",
|
|
82
|
+
"steps",
|
|
83
|
+
"storage",
|
|
84
|
+
"structure",
|
|
85
|
+
"terrace",
|
|
86
|
+
"theater",
|
|
87
|
+
"unenclosedarea",
|
|
88
|
+
"unspecified",
|
|
89
|
+
"vegetation",
|
|
90
|
+
"waitingroom",
|
|
91
|
+
"walkway",
|
|
92
|
+
"walkway.island",
|
|
93
|
+
"wood"
|
|
94
|
+
];
|
|
95
|
+
var NONIMDF_FEATURE_TYPES = [
|
|
96
|
+
"taxonomy",
|
|
97
|
+
"event",
|
|
98
|
+
"promotion",
|
|
99
|
+
"label",
|
|
100
|
+
"privilege"
|
|
101
|
+
];
|
|
102
|
+
var GEOJSON_FEATURE_TYPES = [
|
|
103
|
+
...IMDF_FEATURE_TYPES,
|
|
104
|
+
...NONIMDF_FEATURE_TYPES
|
|
105
|
+
];
|
|
106
|
+
var ALL_FEATURE_TYPES = [
|
|
107
|
+
...GEOJSON_FEATURE_TYPES,
|
|
108
|
+
"sponsored-content",
|
|
109
|
+
"element"
|
|
110
|
+
];
|
|
111
|
+
var defaultFeatureQueryOptionsMap = {
|
|
112
|
+
// IMDF
|
|
113
|
+
address: {},
|
|
114
|
+
amenity: {},
|
|
115
|
+
anchor: {},
|
|
116
|
+
building: {},
|
|
117
|
+
detail: { enabled: false },
|
|
118
|
+
fixture: {},
|
|
119
|
+
footprint: {},
|
|
120
|
+
geofence: { enabled: false },
|
|
121
|
+
kiosk: {},
|
|
122
|
+
level: {},
|
|
123
|
+
occupant: {
|
|
124
|
+
refetchInterval: 5 * 60 * 1e3,
|
|
125
|
+
// refresh every 5 min
|
|
126
|
+
staleTime: 5 * 60 * 1e3
|
|
127
|
+
},
|
|
128
|
+
opening: {},
|
|
129
|
+
relationship: {},
|
|
130
|
+
section: {},
|
|
131
|
+
unit: {},
|
|
132
|
+
venue: {},
|
|
133
|
+
// OTHERS GEOJSON
|
|
134
|
+
taxonomy: {},
|
|
135
|
+
privilege: {},
|
|
136
|
+
event: {},
|
|
137
|
+
promotion: {
|
|
138
|
+
refetchInterval: 0.5 * 60 * 1e3,
|
|
139
|
+
// refresh every 5 min
|
|
140
|
+
staleTime: 0.5 * 60 * 1e3
|
|
141
|
+
},
|
|
142
|
+
label: {},
|
|
143
|
+
// NON GEOJSON
|
|
144
|
+
"sponsored-content": {
|
|
145
|
+
refetchInterval: 1 * 60 * 1e3
|
|
146
|
+
// refresh every 5 min
|
|
147
|
+
},
|
|
148
|
+
element: {},
|
|
149
|
+
page: {},
|
|
150
|
+
model3d: {}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/data/utils/geometry-validator.ts
|
|
154
|
+
var isValidCoordinate = (point2) => {
|
|
155
|
+
return point2.length === 2 && point2.every((coord) => typeof coord === "number");
|
|
156
|
+
};
|
|
157
|
+
function isValidLinearRingCoordinates(ring) {
|
|
158
|
+
if (ring.length < 4) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return ring.every(isValidCoordinate) && ring[0][0] === ring[ring.length - 1][0] && ring[0][1] === ring[ring.length - 1][1];
|
|
162
|
+
}
|
|
163
|
+
var isValidPolygonCoordinates = (polygon2) => {
|
|
164
|
+
if (Array.isArray(polygon2[0]) && (polygon2[0].length === 0 || typeof polygon2[0][0] === "number")) {
|
|
165
|
+
return isValidLinearRingCoordinates(polygon2);
|
|
166
|
+
}
|
|
167
|
+
if (Array.isArray(polygon2) && polygon2.length > 0 && Array.isArray(polygon2[0])) {
|
|
168
|
+
if (!isValidLinearRingCoordinates(polygon2[0])) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
for (let i = 1; i < polygon2.length; i++) {
|
|
172
|
+
if (!isValidLinearRingCoordinates(polygon2[i])) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
};
|
|
180
|
+
var isValidMultiPolygonCoordinates = (multipolygon) => {
|
|
181
|
+
return multipolygon.every(isValidPolygonCoordinates);
|
|
182
|
+
};
|
|
183
|
+
var isValidLineStringCoordinates = (lineString2) => {
|
|
184
|
+
if (!Array.isArray(lineString2) || lineString2.length < 2) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const firstPoint = lineString2[0];
|
|
188
|
+
const lastPoint = lineString2[lineString2.length - 1];
|
|
189
|
+
if (firstPoint[0] === lastPoint[0] && firstPoint[1] === lastPoint[1]) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return lineString2.every(isValidCoordinate);
|
|
193
|
+
};
|
|
194
|
+
var isValidMultiPolygon = (geometry) => {
|
|
195
|
+
const { type, coordinates } = geometry;
|
|
196
|
+
return type === "MultiPolygon" && isValidMultiPolygonCoordinates(coordinates);
|
|
197
|
+
};
|
|
198
|
+
var isValidPolygon = (geometry) => {
|
|
199
|
+
const { type, coordinates } = geometry;
|
|
200
|
+
return type === "Polygon" && isValidPolygonCoordinates(coordinates);
|
|
201
|
+
};
|
|
202
|
+
var isValidLineString = (geometry) => {
|
|
203
|
+
const { type, coordinates } = geometry;
|
|
204
|
+
return type === "LineString" && isValidLineStringCoordinates(coordinates);
|
|
205
|
+
};
|
|
206
|
+
var isValidPoint = (geometry) => {
|
|
207
|
+
const { type, coordinates } = geometry;
|
|
208
|
+
return type === "Point" && isValidCoordinate(coordinates);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/data/utils/match-filters.ts
|
|
212
|
+
function isInFilter(filter) {
|
|
213
|
+
return typeof filter === "object" && filter !== null && "$in" in filter && Array.isArray(filter.$in);
|
|
214
|
+
}
|
|
215
|
+
var someIntersect = (a, b) => a.some((v) => b.includes(v));
|
|
216
|
+
function matchFilter(value, filter) {
|
|
217
|
+
if (Array.isArray(value)) {
|
|
218
|
+
if (isInFilter(filter)) return someIntersect(value, filter.$in);
|
|
219
|
+
return value.includes(filter);
|
|
220
|
+
} else {
|
|
221
|
+
if (isInFilter(filter)) return filter.$in.includes(value);
|
|
222
|
+
return value === filter;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function matchFilters(item, filters) {
|
|
226
|
+
return Object.entries(filters).every(([key, filter]) => {
|
|
227
|
+
return matchFilter(item.properties[key], filter);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/data/utils/occupant-helper.ts
|
|
232
|
+
var occupant_helper_exports = {};
|
|
233
|
+
__export(occupant_helper_exports, {
|
|
234
|
+
getOccupantCorrelatedLocations: () => getOccupantCorrelatedLocations,
|
|
235
|
+
getOccupantMainLocation: () => getOccupantMainLocation,
|
|
236
|
+
getOccupantMarkerLocations: () => getOccupantMarkerLocations
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// src/data/utils/lodash/compact.ts
|
|
240
|
+
var compact = (arr) => arr.filter((item) => Boolean(item));
|
|
241
|
+
|
|
242
|
+
// src/data/utils/occupant-helper.ts
|
|
243
|
+
var getOccupantMainLocation = (occupant) => {
|
|
244
|
+
return occupant.properties.kiosk || occupant.properties.unit;
|
|
245
|
+
};
|
|
246
|
+
var getOccupantCorrelatedLocations = (occupant) => {
|
|
247
|
+
const allCorrelatedLocations = [
|
|
248
|
+
...occupant.properties.units,
|
|
249
|
+
...occupant.properties.kiosks
|
|
250
|
+
];
|
|
251
|
+
return compact(allCorrelatedLocations);
|
|
252
|
+
};
|
|
253
|
+
var getOccupantMarkerLocations = (occupant, options) => {
|
|
254
|
+
const placementType = options?.type ? options.type : occupant.properties.show_name_on_all_units ? "ALL_LOCATIONS" : "ONCE_PER_LEVEL";
|
|
255
|
+
const mainLocation = getOccupantMainLocation(occupant);
|
|
256
|
+
const mainLocationLevel = mainLocation?.properties?.level_id;
|
|
257
|
+
const allCorrelatedLocations = getOccupantCorrelatedLocations(occupant);
|
|
258
|
+
if (placementType === "ALL_LOCATIONS") {
|
|
259
|
+
return compact([mainLocation, ...allCorrelatedLocations]);
|
|
260
|
+
}
|
|
261
|
+
const otherLevelLocations = allCorrelatedLocations.filter((f) => f.properties.level_id !== mainLocationLevel);
|
|
262
|
+
const onePerLevelLocations = [...new Map(otherLevelLocations.map((loc) => [loc.properties.level_id, loc])).values()];
|
|
263
|
+
return compact([mainLocation, ...onePerLevelLocations]);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/data/api/delivery-project.ts
|
|
267
|
+
async function fetchDeliveryApi(projectId, apiKey, featureType, baseUrl = DEFAULT_BASE_URL) {
|
|
268
|
+
switch (featureType) {
|
|
269
|
+
case "label":
|
|
270
|
+
case "element": {
|
|
271
|
+
const pluralFeatureType = `${featureType}s`;
|
|
272
|
+
const res = await fetch(
|
|
273
|
+
`${baseUrl}/delivery/projects/${projectId}/${pluralFeatureType}.geojson?api-key=${apiKey}`
|
|
274
|
+
);
|
|
275
|
+
if (res.status !== 200) return [];
|
|
276
|
+
const items = await res.json();
|
|
277
|
+
return items;
|
|
278
|
+
}
|
|
279
|
+
case "model3d": {
|
|
280
|
+
const res = await fetch(
|
|
281
|
+
`${baseUrl}/delivery/projects/${projectId}/${featureType}.geojson?api-key=${apiKey}`
|
|
282
|
+
);
|
|
283
|
+
if (res.status !== 200) return [];
|
|
284
|
+
const items = await res.json();
|
|
285
|
+
return items.features;
|
|
286
|
+
}
|
|
287
|
+
case "sponsored-content": {
|
|
288
|
+
const res = await fetch(
|
|
289
|
+
`${baseUrl}/delivery/projects/${projectId}/sponsored-content.json?api-key=${apiKey}`
|
|
290
|
+
);
|
|
291
|
+
if (res.status !== 200) return [];
|
|
292
|
+
const jsonRes = await res.json();
|
|
293
|
+
const items = jsonRes.data;
|
|
294
|
+
return items.map((item) => ({
|
|
295
|
+
id: item.id,
|
|
296
|
+
...item.attributes
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
default: {
|
|
300
|
+
const res = await fetch(
|
|
301
|
+
`${baseUrl}/delivery/projects/${projectId}/imdf/${featureType}.geojson?api-key=${apiKey}`
|
|
302
|
+
);
|
|
303
|
+
if (res.status !== 200) return [];
|
|
304
|
+
const collections = await res.json();
|
|
305
|
+
return collections.features;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async function fetchPreviewApi(projectId, previewToken, featureType, baseUrl = DEFAULT_BASE_URL) {
|
|
310
|
+
switch (featureType) {
|
|
311
|
+
case "label":
|
|
312
|
+
case "element": {
|
|
313
|
+
const pluralFeatureType = `${featureType}s`;
|
|
314
|
+
const res = await fetch(
|
|
315
|
+
`${baseUrl}/preview/projects/${projectId}/${pluralFeatureType}.geojson`,
|
|
316
|
+
{
|
|
317
|
+
headers: {
|
|
318
|
+
Authorization: `Bearer ${previewToken}`
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
if (res.status !== 200) return [];
|
|
323
|
+
const items = await res.json();
|
|
324
|
+
return items;
|
|
325
|
+
}
|
|
326
|
+
case "sponsored-content": {
|
|
327
|
+
const res = await fetch(
|
|
328
|
+
`${baseUrl}/preview/projects/${projectId}/sponsored-content.json`,
|
|
329
|
+
{
|
|
330
|
+
headers: {
|
|
331
|
+
Authorization: `Bearer ${previewToken}`
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
if (res.status !== 200) return [];
|
|
336
|
+
const jsonRes = await res.json();
|
|
337
|
+
const items = jsonRes.data;
|
|
338
|
+
return items.map((item) => ({
|
|
339
|
+
id: item.id,
|
|
340
|
+
...item.attributes
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
default: {
|
|
344
|
+
const res = await fetch(
|
|
345
|
+
`${baseUrl}/preview/projects/${projectId}/imdf/${featureType}.geojson`,
|
|
346
|
+
{
|
|
347
|
+
headers: {
|
|
348
|
+
Authorization: `Bearer ${previewToken}`
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
if (res.status !== 200) return [];
|
|
353
|
+
const collections = await res.json();
|
|
354
|
+
return collections.features;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
var safeFetchFeature = async (featureType, params) => {
|
|
359
|
+
const mode = params.mode ?? "delivery";
|
|
360
|
+
const projectId = params.projectId;
|
|
361
|
+
const apiKey = params.apiKey;
|
|
362
|
+
const previewToken = params.previewToken;
|
|
363
|
+
const baseUrl = params.baseUrl ?? DEFAULT_BASE_URL;
|
|
364
|
+
try {
|
|
365
|
+
let result = [];
|
|
366
|
+
if (mode === "delivery") {
|
|
367
|
+
result = await fetchDeliveryApi(
|
|
368
|
+
projectId,
|
|
369
|
+
apiKey,
|
|
370
|
+
featureType,
|
|
371
|
+
baseUrl
|
|
372
|
+
);
|
|
373
|
+
} else if (mode === "preview") {
|
|
374
|
+
result = await fetchPreviewApi(
|
|
375
|
+
projectId,
|
|
376
|
+
previewToken,
|
|
377
|
+
featureType,
|
|
378
|
+
baseUrl
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return result ?? [];
|
|
382
|
+
} catch (e) {
|
|
383
|
+
return Promise.resolve([]);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/data/getDataClient.ts
|
|
388
|
+
import {
|
|
389
|
+
QueryClient,
|
|
390
|
+
QueryObserver
|
|
391
|
+
} from "@tanstack/query-core";
|
|
392
|
+
|
|
393
|
+
// src/data/populator/index.ts
|
|
394
|
+
import { center as center2 } from "@turf/center";
|
|
395
|
+
import { booleanPointInPolygon as booleanPointInPolygon2 } from "@turf/boolean-point-in-polygon";
|
|
396
|
+
|
|
397
|
+
// src/data/utils/findContaining.ts
|
|
398
|
+
import { center } from "@turf/center";
|
|
399
|
+
import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon";
|
|
400
|
+
var findContainingUnit = (poi, units) => {
|
|
401
|
+
const unit = units.find(
|
|
402
|
+
(unit2) => {
|
|
403
|
+
try {
|
|
404
|
+
return unit2.properties.level_id === poi.properties.level_id && booleanPointInPolygon(center(poi), unit2);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
console.log(`Cannot find containing unit of (${poi.id}):`, e.message);
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
);
|
|
411
|
+
return unit;
|
|
412
|
+
};
|
|
413
|
+
var findContainingUnitAtPoint = (point2, levelId, units) => {
|
|
414
|
+
const unit = units.find(
|
|
415
|
+
(unit2) => {
|
|
416
|
+
try {
|
|
417
|
+
return unit2.properties.level_id === levelId && booleanPointInPolygon(point2, unit2);
|
|
418
|
+
} catch (e) {
|
|
419
|
+
console.log(`Cannot find containing unit of (point: ${point2}, levelId: ${levelId}):`, e.message);
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
return unit;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/data/populator/index.ts
|
|
428
|
+
var createPopulator = ({
|
|
429
|
+
internalFindById,
|
|
430
|
+
internalFilterByType
|
|
431
|
+
}) => {
|
|
432
|
+
const populateAddress = (address) => Promise.resolve(address);
|
|
433
|
+
const populateBuilding = (building) => Promise.resolve(building);
|
|
434
|
+
const populateDetail = (detail) => Promise.resolve(detail);
|
|
435
|
+
const populateFootprint = (footprint) => Promise.resolve(footprint);
|
|
436
|
+
const populateGeofence = (geofence) => Promise.resolve(geofence);
|
|
437
|
+
const populatePrivilege = (privilege) => Promise.resolve(privilege);
|
|
438
|
+
const populateEvent = (event) => Promise.resolve(event);
|
|
439
|
+
const populatePromotion = async (promotion) => {
|
|
440
|
+
const venue = await internalFindById(promotion.properties.venue_id);
|
|
441
|
+
return {
|
|
442
|
+
...promotion,
|
|
443
|
+
properties: {
|
|
444
|
+
...promotion.properties,
|
|
445
|
+
venue
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
const populateAmenity = async (amenity) => {
|
|
450
|
+
const units = await Promise.all(
|
|
451
|
+
amenity.properties.unit_ids.map(internalFindById)
|
|
452
|
+
);
|
|
453
|
+
const populatedUnits = await Promise.all(units.map(populateUnit));
|
|
454
|
+
const venue = await internalFindById(amenity.properties.venue_id);
|
|
455
|
+
const defaultLevel = populatedUnits[0].properties.level;
|
|
456
|
+
const kiosks = await internalFilterByType("kiosk");
|
|
457
|
+
const ordinalKiosks = kiosks.filter(
|
|
458
|
+
(kiosk2) => kiosk2.properties.level_id === defaultLevel.id
|
|
459
|
+
);
|
|
460
|
+
const kiosk = ordinalKiosks.find((kiosk2) => booleanPointInPolygon2(amenity, kiosk2));
|
|
461
|
+
return {
|
|
462
|
+
...amenity,
|
|
463
|
+
properties: {
|
|
464
|
+
...amenity.properties,
|
|
465
|
+
ordinal: defaultLevel.properties.ordinal,
|
|
466
|
+
level_name: defaultLevel.properties.name.en,
|
|
467
|
+
level: defaultLevel,
|
|
468
|
+
units: populatedUnits,
|
|
469
|
+
venue,
|
|
470
|
+
_experimental_kiosk: kiosk ? await populateKiosk(kiosk) : null
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
const populateAnchor = async (anchor) => {
|
|
475
|
+
const unit = await internalFindById(anchor.properties.unit_id);
|
|
476
|
+
const venue = await internalFindById(unit.properties.venue_id);
|
|
477
|
+
const level = await internalFindById(unit.properties.level_id);
|
|
478
|
+
const sections = await internalFilterByType("section");
|
|
479
|
+
const section = sections.find((section2) => booleanPointInPolygon2(anchor, section2));
|
|
480
|
+
return {
|
|
481
|
+
...anchor,
|
|
482
|
+
properties: {
|
|
483
|
+
...anchor.properties,
|
|
484
|
+
level: await populateLevel(level),
|
|
485
|
+
unit: await populateUnit(unit),
|
|
486
|
+
section: section ? await populateSection(section) : null,
|
|
487
|
+
venue: await populateVenue(venue),
|
|
488
|
+
ordinal: level.properties.ordinal
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
};
|
|
492
|
+
const populateFixture = async (fixture) => {
|
|
493
|
+
const level = await internalFindById(fixture.properties.level_id);
|
|
494
|
+
const venue = await internalFindById(fixture.properties.venue_id);
|
|
495
|
+
const anchor = await internalFindById(fixture.properties.anchor_id);
|
|
496
|
+
return {
|
|
497
|
+
...fixture,
|
|
498
|
+
properties: {
|
|
499
|
+
...fixture.properties,
|
|
500
|
+
anchor: anchor ? await populateAnchor(anchor) : null,
|
|
501
|
+
level: await populateLevel(level),
|
|
502
|
+
venue: await populateVenue(venue),
|
|
503
|
+
ordinal: level.properties.ordinal
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
const populateKiosk = async (kiosk) => {
|
|
508
|
+
const level = await internalFindById(kiosk.properties.level_id);
|
|
509
|
+
const venue = await internalFindById(kiosk.properties.venue_id);
|
|
510
|
+
const anchor = await internalFindById(kiosk.properties.anchor_id);
|
|
511
|
+
const units = await internalFilterByType("unit");
|
|
512
|
+
const unit = findContainingUnit(kiosk, units.filter((unit2) => unit2.properties.category === "walkway"));
|
|
513
|
+
let section = null;
|
|
514
|
+
if (anchor) {
|
|
515
|
+
const sections = await internalFilterByType("section");
|
|
516
|
+
section = sections.find((section2) => booleanPointInPolygon2(anchor, section2));
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
...kiosk,
|
|
520
|
+
properties: {
|
|
521
|
+
...kiosk.properties,
|
|
522
|
+
anchor: anchor ? await populateAnchor(anchor) : null,
|
|
523
|
+
level: await populateLevel(level),
|
|
524
|
+
venue: await populateVenue(venue),
|
|
525
|
+
ordinal: level.properties.ordinal,
|
|
526
|
+
unit: unit ? await populateUnit(unit) : null,
|
|
527
|
+
section: section ? await populateSection(section) : null
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
};
|
|
531
|
+
const populateLevel = async (level) => {
|
|
532
|
+
const venue = await internalFindById(level.properties.venue_id);
|
|
533
|
+
return {
|
|
534
|
+
...level,
|
|
535
|
+
properties: {
|
|
536
|
+
...level.properties,
|
|
537
|
+
venue
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
};
|
|
541
|
+
const populateOccupant = async (occupant) => {
|
|
542
|
+
const {
|
|
543
|
+
anchor_id,
|
|
544
|
+
venue_id,
|
|
545
|
+
local_category_ids,
|
|
546
|
+
promotion_ids = [],
|
|
547
|
+
privilege_ids = [],
|
|
548
|
+
kiosk_id,
|
|
549
|
+
unit_id,
|
|
550
|
+
kiosk_ids = [],
|
|
551
|
+
unit_ids = []
|
|
552
|
+
} = occupant.properties;
|
|
553
|
+
const anchor = await internalFindById(anchor_id);
|
|
554
|
+
const venue = await internalFindById(venue_id);
|
|
555
|
+
const localCategories = await Promise.all(
|
|
556
|
+
local_category_ids.map(internalFindById)
|
|
557
|
+
);
|
|
558
|
+
const promotions = await Promise.all(
|
|
559
|
+
promotion_ids.map(internalFindById)
|
|
560
|
+
);
|
|
561
|
+
const privileges = await Promise.all(
|
|
562
|
+
privilege_ids.map(internalFindById)
|
|
563
|
+
);
|
|
564
|
+
const kiosk = await internalFindById(kiosk_id);
|
|
565
|
+
const unit = await internalFindById(unit_id);
|
|
566
|
+
const kiosks = await Promise.all(kiosk_ids.map(internalFindById));
|
|
567
|
+
const units = await Promise.all(unit_ids.map(internalFindById));
|
|
568
|
+
return {
|
|
569
|
+
...occupant,
|
|
570
|
+
properties: {
|
|
571
|
+
...occupant.properties,
|
|
572
|
+
anchor: anchor ? await populateAnchor(anchor) : null,
|
|
573
|
+
local_categories: await Promise.all(
|
|
574
|
+
compact(localCategories).map(populateTaxonomy)
|
|
575
|
+
),
|
|
576
|
+
venue,
|
|
577
|
+
promotions,
|
|
578
|
+
privileges,
|
|
579
|
+
kiosk: kiosk ? await populateKiosk(kiosk) : null,
|
|
580
|
+
unit: unit ? await populateUnit(unit) : null,
|
|
581
|
+
kiosks: await Promise.all(kiosks.map(populateKiosk)),
|
|
582
|
+
units: await Promise.all(units.map(populateUnit))
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
const populateOpening = async (opening) => {
|
|
587
|
+
const venue = await internalFindById(opening.properties.venue_id);
|
|
588
|
+
const level = await internalFindById(opening.properties.level_id);
|
|
589
|
+
return {
|
|
590
|
+
...opening,
|
|
591
|
+
properties: {
|
|
592
|
+
venue,
|
|
593
|
+
level: await populateLevel(level),
|
|
594
|
+
ordinal: level.properties.ordinal
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
};
|
|
598
|
+
const populateSection = async (section) => {
|
|
599
|
+
const venue = await internalFindById(section.properties.venue_id);
|
|
600
|
+
const level = await internalFindById(section.properties.level_id);
|
|
601
|
+
return {
|
|
602
|
+
...section,
|
|
603
|
+
properties: {
|
|
604
|
+
...section.properties,
|
|
605
|
+
venue,
|
|
606
|
+
level: await populateLevel(level),
|
|
607
|
+
ordinal: level.properties.ordinal
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
};
|
|
611
|
+
const populateRelationship = async (relationship) => {
|
|
612
|
+
const originId = relationship.properties.origin?.id;
|
|
613
|
+
const destinationId = relationship.properties.destination?.id;
|
|
614
|
+
const origin = originId ? await internalFindById(originId) : null;
|
|
615
|
+
const destination = destinationId ? await internalFindById(destinationId) : null;
|
|
616
|
+
const intermediary_ids = (relationship.properties.intermediary || []).map(({ id }) => id);
|
|
617
|
+
const intermediary = await Promise.all(intermediary_ids.map(internalFindById));
|
|
618
|
+
return {
|
|
619
|
+
...relationship,
|
|
620
|
+
properties: {
|
|
621
|
+
...relationship.properties,
|
|
622
|
+
origin,
|
|
623
|
+
destination,
|
|
624
|
+
intermediary
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
};
|
|
628
|
+
const populateUnit = async (unit) => {
|
|
629
|
+
const venue = await internalFindById(unit.properties.venue_id);
|
|
630
|
+
const level = await internalFindById(unit.properties.level_id);
|
|
631
|
+
const sections = await internalFilterByType("section");
|
|
632
|
+
try {
|
|
633
|
+
const section = unit.geometry.type !== "MultiPolygon" ? sections.find((section2) => booleanPointInPolygon2(center2(unit), section2)) : null;
|
|
634
|
+
return {
|
|
635
|
+
...unit,
|
|
636
|
+
properties: {
|
|
637
|
+
...unit.properties,
|
|
638
|
+
venue,
|
|
639
|
+
ordinal: level.properties.ordinal,
|
|
640
|
+
level: await populateLevel(level),
|
|
641
|
+
section: section ? await populateSection(section) : null
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
} catch (err) {
|
|
645
|
+
console.log(`error finding section `, { unit, sections });
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const populateVenue = (venue) => {
|
|
649
|
+
return Promise.resolve(venue);
|
|
650
|
+
};
|
|
651
|
+
const populateTaxonomy = async (taxonomy) => {
|
|
652
|
+
const venue = await internalFindById(taxonomy.properties.venue_id);
|
|
653
|
+
return {
|
|
654
|
+
...taxonomy,
|
|
655
|
+
properties: {
|
|
656
|
+
...taxonomy.properties,
|
|
657
|
+
venue: venue ? await populateVenue(venue) : null
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
const populateModel3D = async (model3d) => {
|
|
662
|
+
const level = await internalFindById(model3d.properties.level_id);
|
|
663
|
+
try {
|
|
664
|
+
return {
|
|
665
|
+
...model3d,
|
|
666
|
+
properties: {
|
|
667
|
+
...model3d.properties,
|
|
668
|
+
level: await populateLevel(level)
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
} catch (err) {
|
|
672
|
+
console.log(`error finding level`, { model3d, level });
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
const populateFeature = (feature2) => Promise.resolve(feature2);
|
|
676
|
+
return {
|
|
677
|
+
address: populateAddress,
|
|
678
|
+
building: populateBuilding,
|
|
679
|
+
detail: populateDetail,
|
|
680
|
+
fixture: populateFixture,
|
|
681
|
+
footprint: populateFootprint,
|
|
682
|
+
geofence: populateGeofence,
|
|
683
|
+
opening: populateOpening,
|
|
684
|
+
relationship: populateRelationship,
|
|
685
|
+
privilege: populatePrivilege,
|
|
686
|
+
promotion: populatePromotion,
|
|
687
|
+
event: populateEvent,
|
|
688
|
+
label: populateFeature,
|
|
689
|
+
element: populateFeature,
|
|
690
|
+
page: populateFeature,
|
|
691
|
+
amenity: populateAmenity,
|
|
692
|
+
anchor: populateAnchor,
|
|
693
|
+
kiosk: populateKiosk,
|
|
694
|
+
level: populateLevel,
|
|
695
|
+
occupant: populateOccupant,
|
|
696
|
+
section: populateSection,
|
|
697
|
+
unit: populateUnit,
|
|
698
|
+
venue: populateVenue,
|
|
699
|
+
taxonomy: populateTaxonomy,
|
|
700
|
+
model3d: populateModel3D
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// src/data/search/getSearchClient.ts
|
|
705
|
+
import Fuse from "fuse.js";
|
|
706
|
+
|
|
707
|
+
// src/data/search/utils/sanitizeInput.ts
|
|
708
|
+
var sanitizeInput = (str) => str.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\-–—_./()]+/g, "").normalize("NFC").trim();
|
|
709
|
+
|
|
710
|
+
// src/data/search/getSearchClient.ts
|
|
711
|
+
var getSearchClient = ({ occupants, amenities }) => {
|
|
712
|
+
const fuseAmenities = new Fuse(amenities, {
|
|
713
|
+
threshold: 0.2,
|
|
714
|
+
keys: [
|
|
715
|
+
{ name: "properties.name", "weight": 1, getFn: (obj) => Object.values(obj.properties.name || {}) },
|
|
716
|
+
{ name: "properties.category", "weight": 1 }
|
|
717
|
+
]
|
|
718
|
+
});
|
|
719
|
+
const fuseOccupants = new Fuse(occupants, {
|
|
720
|
+
threshold: 0.25,
|
|
721
|
+
// 0.2 is too strict (can't find Mo-Mo Paradise with "momo" search string)
|
|
722
|
+
includeScore: true,
|
|
723
|
+
shouldSort: true,
|
|
724
|
+
keys: [
|
|
725
|
+
{ name: "properties.name", "weight": 4, getFn: (obj) => Object.values(obj.properties.name || {}) },
|
|
726
|
+
{ name: "properties.keywords", "weight": 0.5 },
|
|
727
|
+
{ name: "properties.category", "weight": 0.25 },
|
|
728
|
+
{ name: "properties.local_category_names", "weight": 0.25 },
|
|
729
|
+
{ name: "properties.description", "weight": 0.25, getFn: (occ) => Object.values(occ.properties.description || {}) },
|
|
730
|
+
{ name: "properties.unit_name", "weight": 0.25 },
|
|
731
|
+
{ name: "properties.kiosk_name", "weight": 0.25 }
|
|
732
|
+
]
|
|
733
|
+
});
|
|
734
|
+
const search = (value) => {
|
|
735
|
+
const sanitizedValue = sanitizeInput(value);
|
|
736
|
+
const matchedAmenities = fuseAmenities.search(sanitizedValue);
|
|
737
|
+
const matchedOccupants = fuseOccupants.search(sanitizedValue);
|
|
738
|
+
return [...matchedAmenities, ...matchedOccupants];
|
|
739
|
+
};
|
|
740
|
+
return {
|
|
741
|
+
search
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/data/navigate/getNavigateClient.ts
|
|
746
|
+
import { booleanPointInPolygon as booleanPointInPolygon4 } from "@turf/boolean-point-in-polygon";
|
|
747
|
+
|
|
748
|
+
// src/data/navigate/graph/prepare.ts
|
|
749
|
+
import _6 from "lodash";
|
|
750
|
+
import DijstraGraph from "node-dijkstra";
|
|
751
|
+
import { distance as distance2 } from "@turf/distance";
|
|
752
|
+
import { center as turfCenter3 } from "@turf/center";
|
|
753
|
+
|
|
754
|
+
// src/data/navigate/graph/nodemap/createTraversalNodeMap.ts
|
|
755
|
+
import { distance as turfDistance } from "@turf/distance";
|
|
756
|
+
import { center as turfCenter } from "@turf/center";
|
|
757
|
+
import _ from "lodash";
|
|
758
|
+
|
|
759
|
+
// src/data/navigate/graph/constants.ts
|
|
760
|
+
var ROOM_BASEDISTANCE = 1e3;
|
|
761
|
+
var TERRACE_BASEDISTANCE = 1e3;
|
|
762
|
+
var ESCALATOR_BASEDISTANCE = 200;
|
|
763
|
+
var RAMP_BASEDISTANCE = 200;
|
|
764
|
+
var ELEVATOR_BASEDISTANCE = 500;
|
|
765
|
+
var STAIR_BASEDISTANCE = 1e5;
|
|
766
|
+
var BASE_POI_BASEDISTANCE = 9999999;
|
|
767
|
+
var DEFAULT_UNIT_BASEDISTANCE_OPTIONS = {
|
|
768
|
+
default: { baseDistance: 0 },
|
|
769
|
+
byCategory: {
|
|
770
|
+
room: { baseDistance: ROOM_BASEDISTANCE },
|
|
771
|
+
terrace: { baseDistance: TERRACE_BASEDISTANCE },
|
|
772
|
+
escalator: { baseDistance: ESCALATOR_BASEDISTANCE, scaleDistanceByLevel: false },
|
|
773
|
+
ramp: { baseDistance: RAMP_BASEDISTANCE, scaleDistanceByLevel: false },
|
|
774
|
+
elevator: { baseDistance: ELEVATOR_BASEDISTANCE, scaleDistanceByLevel: false },
|
|
775
|
+
stairs: {
|
|
776
|
+
baseDistance: STAIR_BASEDISTANCE,
|
|
777
|
+
scaleDistanceByLevel: true
|
|
778
|
+
},
|
|
779
|
+
"stairs.emergencyexit": {
|
|
780
|
+
baseDistance: STAIR_BASEDISTANCE,
|
|
781
|
+
scaleDistanceByLevel: true
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/data/navigate/graph/utils/getDistanceOption.ts
|
|
787
|
+
var getDistanceOptions = (options, category) => {
|
|
788
|
+
if (!options) return DEFAULT_UNIT_BASEDISTANCE_OPTIONS.byCategory[category];
|
|
789
|
+
return (category && options.byCategory?.[category]) ?? DEFAULT_UNIT_BASEDISTANCE_OPTIONS.byCategory[category] ?? options?.default ?? DEFAULT_UNIT_BASEDISTANCE_OPTIONS.default;
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
// src/data/utils/trace.ts
|
|
793
|
+
var trace = (namespace, text, ms, color) => {
|
|
794
|
+
if (process.env.NODE_ENV !== "test") console.log(`[${namespace}] %c${text.padEnd(90)} ${ms !== void 0 ? `${ms.toFixed(1).padStart(6)} ms` : ``}`, color ? `color: ${color}` : void 0);
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
// src/data/navigate/graph/nodemap/createTraversalNodeMap.ts
|
|
798
|
+
var createTraversalNodeMap = (unitOpenings, options) => {
|
|
799
|
+
const { units } = options.data;
|
|
800
|
+
let counter = 0;
|
|
801
|
+
const calculateFeatureDistanceWithinUnit = (features, distanceOptions) => {
|
|
802
|
+
let relationshipGraph = {};
|
|
803
|
+
for (let currentIndex = 0; currentIndex < features.length; currentIndex++) {
|
|
804
|
+
const isLastItem = currentIndex + 1 === features.length;
|
|
805
|
+
if (isLastItem) break;
|
|
806
|
+
for (let j = currentIndex + 1; j < features.length; j++) {
|
|
807
|
+
const opening = features[currentIndex];
|
|
808
|
+
const feature2 = features[j];
|
|
809
|
+
try {
|
|
810
|
+
const distance5 = turfDistance(
|
|
811
|
+
turfCenter(opening.geometry),
|
|
812
|
+
turfCenter(feature2.geometry),
|
|
813
|
+
{ units: "meters" }
|
|
814
|
+
) + (distanceOptions?.baseDistance ?? 0);
|
|
815
|
+
if (opening.id === feature2.id) continue;
|
|
816
|
+
_.set(relationshipGraph, `${opening.id}.${feature2.id}`, distance5);
|
|
817
|
+
_.set(relationshipGraph, `${feature2.id}.${opening.id}`, distance5);
|
|
818
|
+
counter++;
|
|
819
|
+
} catch (error) {
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return relationshipGraph;
|
|
825
|
+
};
|
|
826
|
+
const t0 = performance.now();
|
|
827
|
+
const nodeMap = _.reduce(
|
|
828
|
+
unitOpenings,
|
|
829
|
+
(acc, openings, unitId) => {
|
|
830
|
+
const unit = units.find((unit2) => unit2.id === unitId);
|
|
831
|
+
const unitDistanceOption = getDistanceOptions(options.unitDistanceOptions, unit.properties.category);
|
|
832
|
+
return _.merge(
|
|
833
|
+
acc,
|
|
834
|
+
calculateFeatureDistanceWithinUnit(openings, unitDistanceOption)
|
|
835
|
+
);
|
|
836
|
+
},
|
|
837
|
+
{}
|
|
838
|
+
);
|
|
839
|
+
const t1 = performance.now();
|
|
840
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} traversal relationships`, t1 - t0);
|
|
841
|
+
return nodeMap;
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// src/data/navigate/graph/nodemap/createElevatorNodeMap.ts
|
|
845
|
+
import _2 from "lodash";
|
|
846
|
+
var createElevatorNodeMap = (elevatorLikeRelationships, unitOpenings, options) => {
|
|
847
|
+
const t0 = performance.now();
|
|
848
|
+
const { levels, units } = options.data;
|
|
849
|
+
const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "elevator");
|
|
850
|
+
const {
|
|
851
|
+
baseDistance = ELEVATOR_BASEDISTANCE,
|
|
852
|
+
scaleDistanceByLevel = false
|
|
853
|
+
} = distanceOptions;
|
|
854
|
+
let elevatorNodeMap = {};
|
|
855
|
+
let counter = 0;
|
|
856
|
+
for (const relationship of elevatorLikeRelationships) {
|
|
857
|
+
try {
|
|
858
|
+
const {
|
|
859
|
+
origin: originTypeAndId,
|
|
860
|
+
intermediary,
|
|
861
|
+
destination: destinationTypeAndId
|
|
862
|
+
} = relationship.properties;
|
|
863
|
+
const origin = units.find((unit) => unit.id === originTypeAndId.id);
|
|
864
|
+
if (!origin) return;
|
|
865
|
+
const originOpenings = compact(unitOpenings[origin.id]);
|
|
866
|
+
const originLevel = levels.find((level) => level.id === origin.properties.level_id);
|
|
867
|
+
const destination = units.find((unit) => unit.id === destinationTypeAndId.id);
|
|
868
|
+
const destinationOpenings = unitOpenings[destination.id];
|
|
869
|
+
const destinationOpeningAndLevels = destinationOpenings.map((opening) => {
|
|
870
|
+
const level = levels.find((level2) => level2.id === destination.properties.level_id);
|
|
871
|
+
return { opening, level };
|
|
872
|
+
});
|
|
873
|
+
const intermediaryOpeningAndLevels = intermediary.map((unitTypeAndId) => {
|
|
874
|
+
const openings = unitOpenings[unitTypeAndId.id];
|
|
875
|
+
const unit = units.find((unit2) => unit2.id === unitTypeAndId.id);
|
|
876
|
+
const level = levels.find((level2) => level2.id === unit.properties.level_id);
|
|
877
|
+
return openings.map((opening) => ({ opening, level }));
|
|
878
|
+
}).flat();
|
|
879
|
+
const connections = compact([...intermediaryOpeningAndLevels, ...destinationOpeningAndLevels]);
|
|
880
|
+
if (!originOpenings || originOpenings.length === 0) return;
|
|
881
|
+
for (const originOpening of originOpenings) {
|
|
882
|
+
for (const connection of connections) {
|
|
883
|
+
const { opening, level } = connection;
|
|
884
|
+
let distance5 = baseDistance;
|
|
885
|
+
if (scaleDistanceByLevel) {
|
|
886
|
+
const originOrdinal = originLevel.properties.ordinal;
|
|
887
|
+
const connectionOrdinal = level.properties.ordinal;
|
|
888
|
+
const levelDifference = Math.abs(originOrdinal - connectionOrdinal);
|
|
889
|
+
if (levelDifference > 0) distance5 *= levelDifference;
|
|
890
|
+
}
|
|
891
|
+
_2.set(elevatorNodeMap, `${originOpening.id}.${opening.id}`, distance5);
|
|
892
|
+
_2.set(elevatorNodeMap, `${opening.id}.${originOpening.id}`, distance5);
|
|
893
|
+
counter++;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
} catch (err) {
|
|
897
|
+
console.log(err);
|
|
898
|
+
console.log("cannot create elevatorNodeMap for ", { relationship });
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const t1 = performance.now();
|
|
902
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} escalator relationships`, t1 - t0);
|
|
903
|
+
return elevatorNodeMap;
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
// src/data/navigate/graph/nodemap/createEscalatorNodeMap.ts
|
|
907
|
+
import set from "lodash/set";
|
|
908
|
+
var createEscalatorNodeMap = (relationships, options) => {
|
|
909
|
+
const t0 = performance.now();
|
|
910
|
+
const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "escalator");
|
|
911
|
+
let counter = 0;
|
|
912
|
+
let nodeMap = {};
|
|
913
|
+
for (const relationship of relationships) {
|
|
914
|
+
const {
|
|
915
|
+
properties: { direction, origin, destination }
|
|
916
|
+
} = relationship;
|
|
917
|
+
set(nodeMap, `${origin.id}.${destination.id}`, distanceOptions.baseDistance);
|
|
918
|
+
if (direction === "undirected") {
|
|
919
|
+
set(nodeMap, `${destination.id}.${origin.id}`, distanceOptions.baseDistance);
|
|
920
|
+
}
|
|
921
|
+
counter++;
|
|
922
|
+
}
|
|
923
|
+
const t1 = performance.now();
|
|
924
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} escalator relationships`, t1 - t0);
|
|
925
|
+
return nodeMap;
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
// src/data/navigate/graph/nodemap/createRampNodeMap.ts
|
|
929
|
+
import set2 from "lodash/set";
|
|
930
|
+
var createRampNodeMap = (relationships, options) => {
|
|
931
|
+
const t0 = performance.now();
|
|
932
|
+
const distanceOptions = getDistanceOptions(options.unitDistanceOptions, "ramp");
|
|
933
|
+
let counter = 0;
|
|
934
|
+
let nodeMap = {};
|
|
935
|
+
relationships.forEach((relationship) => {
|
|
936
|
+
const {
|
|
937
|
+
properties: { origin, destination }
|
|
938
|
+
} = relationship;
|
|
939
|
+
set2(nodeMap, `${origin.id}.${destination.id}`, distanceOptions.baseDistance);
|
|
940
|
+
set2(nodeMap, `${destination.id}.${origin.id}`, distanceOptions.baseDistance);
|
|
941
|
+
counter++;
|
|
942
|
+
});
|
|
943
|
+
const t1 = performance.now();
|
|
944
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} ramp relationships`, t1 - t0);
|
|
945
|
+
return nodeMap;
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
// src/data/navigate/graph/nodemap/createStairNodeMap.ts
|
|
949
|
+
import _3 from "lodash";
|
|
950
|
+
var createStairNodeMap = (elevatorLikeRelationships, unitOpenings, options) => {
|
|
951
|
+
const t0 = performance.now();
|
|
952
|
+
const { levels = [], openings = [], units = [] } = options.data;
|
|
953
|
+
const { baseDistance, scaleDistanceByLevel } = getDistanceOptions(options.unitDistanceOptions, "stairs");
|
|
954
|
+
let elevatorNodeMap = {};
|
|
955
|
+
let counter = 0;
|
|
956
|
+
for (const relationship of elevatorLikeRelationships) {
|
|
957
|
+
try {
|
|
958
|
+
const {
|
|
959
|
+
origin: { id: originId },
|
|
960
|
+
intermediary,
|
|
961
|
+
destination: { id: destinationId }
|
|
962
|
+
} = relationship.properties;
|
|
963
|
+
const origin = openings.find((opening) => opening.id === originId);
|
|
964
|
+
if (!origin) return;
|
|
965
|
+
const originLevel = levels.find((level) => level.id === origin.properties.level_id);
|
|
966
|
+
const destination = openings.find((opening) => opening.id === destinationId);
|
|
967
|
+
const destinationOpeningAndLevel = {
|
|
968
|
+
opening: destination,
|
|
969
|
+
level: levels.find((level) => level.id === destination.properties.level_id)
|
|
970
|
+
};
|
|
971
|
+
const intermediaryOpeningAndLevels = intermediary.map((unitTypeAndId) => {
|
|
972
|
+
const openings2 = unitOpenings[unitTypeAndId.id];
|
|
973
|
+
const unit = units.find((unit2) => unit2.id === unitTypeAndId.id);
|
|
974
|
+
const level = levels.find((level2) => level2.id === unit.properties.level_id);
|
|
975
|
+
return openings2.map((opening) => ({ opening, level }));
|
|
976
|
+
}).flat();
|
|
977
|
+
const connections = [...intermediaryOpeningAndLevels, destinationOpeningAndLevel];
|
|
978
|
+
if (!origin) return;
|
|
979
|
+
for (const connection of connections) {
|
|
980
|
+
const { opening, level } = connection;
|
|
981
|
+
let distance5 = baseDistance;
|
|
982
|
+
if (scaleDistanceByLevel) {
|
|
983
|
+
const originOrdinal = originLevel.properties.ordinal;
|
|
984
|
+
const connectionOrdinal = level.properties.ordinal;
|
|
985
|
+
const levelDifference = Math.abs(originOrdinal - connectionOrdinal);
|
|
986
|
+
if (levelDifference > 0) distance5 *= levelDifference;
|
|
987
|
+
}
|
|
988
|
+
_3.set(elevatorNodeMap, `${origin.id}.${opening.id}`, distance5);
|
|
989
|
+
_3.set(elevatorNodeMap, `${opening.id}.${origin.id}`, distance5);
|
|
990
|
+
counter++;
|
|
991
|
+
}
|
|
992
|
+
} catch (err) {
|
|
993
|
+
console.warn(
|
|
994
|
+
"Failed to create stairNodeMap",
|
|
995
|
+
{
|
|
996
|
+
relationshipId: relationship.id,
|
|
997
|
+
featureType: relationship.feature_type,
|
|
998
|
+
error: err instanceof Error ? err.message : err,
|
|
999
|
+
stack: err instanceof Error ? err.stack : void 0
|
|
1000
|
+
}
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
const t1 = performance.now();
|
|
1005
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} stairs relationships`, t1 - t0);
|
|
1006
|
+
return elevatorNodeMap;
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
// src/data/navigate/graph/nodemap/createOccupantNodeMap.ts
|
|
1010
|
+
import _4 from "lodash";
|
|
1011
|
+
var createOccupantNodeMap = (occupants) => {
|
|
1012
|
+
const t0 = performance.now();
|
|
1013
|
+
let nodeMap = {};
|
|
1014
|
+
let counter = 0;
|
|
1015
|
+
occupants.forEach((occupant) => {
|
|
1016
|
+
const { unit_id, unit_ids = [], kiosk_id, kiosk_ids = [] } = occupant.properties;
|
|
1017
|
+
const occupantRoomIds = compact([unit_id, ...unit_ids]);
|
|
1018
|
+
const occupantKioskIds = compact([kiosk_id, ...kiosk_ids]);
|
|
1019
|
+
for (const roomId of occupantRoomIds) {
|
|
1020
|
+
_4.set(nodeMap, `${roomId}.${occupant.id}`, 1e-3);
|
|
1021
|
+
_4.set(nodeMap, `${occupant.id}.${roomId}`, 1e-3);
|
|
1022
|
+
counter++;
|
|
1023
|
+
}
|
|
1024
|
+
for (const kioskId of occupantKioskIds) {
|
|
1025
|
+
_4.set(nodeMap, `${kioskId}.${occupant.id}`, 1e-3);
|
|
1026
|
+
_4.set(nodeMap, `${occupant.id}.${kioskId}`, 1e-3);
|
|
1027
|
+
counter++;
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
const t1 = performance.now();
|
|
1031
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} occupants relationships`, t1 - t0);
|
|
1032
|
+
return nodeMap;
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
// src/data/navigate/graph/nodemap/createPOINodeMaps.ts
|
|
1036
|
+
import { center as turfCenter2 } from "@turf/center";
|
|
1037
|
+
import { distance } from "@turf/distance";
|
|
1038
|
+
import _5 from "lodash";
|
|
1039
|
+
var createPOINodeMap = (features, getFeatureUnit, unitOpenings) => {
|
|
1040
|
+
const t0 = performance.now();
|
|
1041
|
+
let nodeMap = {};
|
|
1042
|
+
let counter = 0;
|
|
1043
|
+
features.forEach((feat) => {
|
|
1044
|
+
try {
|
|
1045
|
+
const locatedOnUnitId = getFeatureUnit(feat);
|
|
1046
|
+
const openings = unitOpenings[locatedOnUnitId];
|
|
1047
|
+
const center7 = turfCenter2(feat);
|
|
1048
|
+
for (const opening of openings) {
|
|
1049
|
+
try {
|
|
1050
|
+
const openingCenter = turfCenter2(opening);
|
|
1051
|
+
const dis = distance(center7, openingCenter, { units: "meters" }) + BASE_POI_BASEDISTANCE;
|
|
1052
|
+
_5.set(nodeMap, `${opening.id}.${feat.id}`, dis);
|
|
1053
|
+
_5.set(nodeMap, `${feat.id}.${opening.id}`, dis);
|
|
1054
|
+
counter++;
|
|
1055
|
+
} catch (err) {
|
|
1056
|
+
console.log(err, opening);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
console.log(err);
|
|
1061
|
+
console.log(`cannot connect poi to openings`, err.message, { feat });
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
const type = features.length > 0 ? features[0].feature_type : "-";
|
|
1065
|
+
const t1 = performance.now();
|
|
1066
|
+
trace("nav", ` \u2502 \u251C\u2500 add ${counter} ${type} relationships`, t1 - t0);
|
|
1067
|
+
return nodeMap;
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
// src/data/navigate/graph/utils/mergeNodeMap.ts
|
|
1071
|
+
var mergeNodeMap = (nodeMaps) => {
|
|
1072
|
+
const out = {};
|
|
1073
|
+
for (const nodeMap of nodeMaps) {
|
|
1074
|
+
for (const from in nodeMap) {
|
|
1075
|
+
out[from] = {
|
|
1076
|
+
...out[from] ?? {},
|
|
1077
|
+
...nodeMap[from]
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return out;
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
// src/data/navigate/graph/utils/createUnitOpenings.ts
|
|
1085
|
+
import uniqBy from "lodash/uniqBy";
|
|
1086
|
+
var createUnitOpenings = (relationships, units, openings) => {
|
|
1087
|
+
const openingConnections = {};
|
|
1088
|
+
const relationshipMap = /* @__PURE__ */ new Map();
|
|
1089
|
+
relationships.forEach((relationship) => {
|
|
1090
|
+
const originId = relationship.properties.origin?.id || null;
|
|
1091
|
+
const destinationId = relationship.properties.destination?.id || null;
|
|
1092
|
+
if (!relationshipMap.has(originId)) {
|
|
1093
|
+
relationshipMap.set(originId, []);
|
|
1094
|
+
}
|
|
1095
|
+
if (!relationshipMap.has(destinationId)) {
|
|
1096
|
+
relationshipMap.set(destinationId, []);
|
|
1097
|
+
}
|
|
1098
|
+
relationshipMap.get(originId).push(relationship);
|
|
1099
|
+
relationshipMap.get(destinationId).push(relationship);
|
|
1100
|
+
});
|
|
1101
|
+
units.forEach((unit) => {
|
|
1102
|
+
const unitId = unit.id;
|
|
1103
|
+
const connectedRelationship = relationshipMap.get(unitId) || [];
|
|
1104
|
+
const relationshipIntermediaryTypeAndId = compact(connectedRelationship.map(
|
|
1105
|
+
(relationship) => relationship.properties.intermediary?.[0]
|
|
1106
|
+
// Assuming intermediary is always an array
|
|
1107
|
+
));
|
|
1108
|
+
const relationshipIntermediary = relationshipIntermediaryTypeAndId.map(({ id }) => {
|
|
1109
|
+
return openings.find((opening) => !!opening && opening.id === id);
|
|
1110
|
+
});
|
|
1111
|
+
openingConnections[unitId] = uniqBy(
|
|
1112
|
+
[...openingConnections[unitId] || [], ...relationshipIntermediary],
|
|
1113
|
+
"id"
|
|
1114
|
+
);
|
|
1115
|
+
});
|
|
1116
|
+
return openingConnections;
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// src/data/navigate/parsers.ts
|
|
1120
|
+
var parseOrdinalCoordinate = (id) => {
|
|
1121
|
+
return id.slice(0, -1).split(",").map(Number);
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// src/data/navigate/graph/prepare.ts
|
|
1125
|
+
var prepareGraph = (options) => {
|
|
1126
|
+
const {
|
|
1127
|
+
data: {
|
|
1128
|
+
amenities = [],
|
|
1129
|
+
anchors = [],
|
|
1130
|
+
occupants = [],
|
|
1131
|
+
relationships = [],
|
|
1132
|
+
openings = [],
|
|
1133
|
+
units = [],
|
|
1134
|
+
kiosks = []
|
|
1135
|
+
}
|
|
1136
|
+
} = options;
|
|
1137
|
+
const {
|
|
1138
|
+
traversal: traversalRelationships = [],
|
|
1139
|
+
escalator: escalatorRelationships = [],
|
|
1140
|
+
ramp: rampRelationships = [],
|
|
1141
|
+
elevator: elevatorRelationships = [],
|
|
1142
|
+
stairs: stairsRelationships = []
|
|
1143
|
+
} = _6.groupBy(relationships, "properties.category");
|
|
1144
|
+
const unitOpenings = createUnitOpenings(traversalRelationships, units, openings);
|
|
1145
|
+
const traversalNodeMap = createTraversalNodeMap(unitOpenings, options);
|
|
1146
|
+
const escalatorNodeMap = createEscalatorNodeMap(escalatorRelationships, options);
|
|
1147
|
+
const rampNodeMap = createRampNodeMap(rampRelationships, options);
|
|
1148
|
+
const elevatorNodeMap = createElevatorNodeMap(
|
|
1149
|
+
elevatorRelationships,
|
|
1150
|
+
unitOpenings,
|
|
1151
|
+
options
|
|
1152
|
+
);
|
|
1153
|
+
const stairNodeMap = createStairNodeMap(
|
|
1154
|
+
stairsRelationships,
|
|
1155
|
+
unitOpenings,
|
|
1156
|
+
options
|
|
1157
|
+
);
|
|
1158
|
+
const amenityNodeMap = createPOINodeMap(amenities, (amenity) => amenity.properties.unit_ids[0], unitOpenings);
|
|
1159
|
+
const anchorsNodeMap = createPOINodeMap(anchors, (anchor) => anchor.properties.unit_id, unitOpenings);
|
|
1160
|
+
const walkwayUnits = units.filter((unit) => unit.properties.category === "walkway");
|
|
1161
|
+
const kioskNodeMap = createPOINodeMap(kiosks, (kiosk) => findContainingUnit(kiosk, walkwayUnits)?.id, unitOpenings);
|
|
1162
|
+
const unitNodeMap = createPOINodeMap(units, (unit) => unit.id, unitOpenings);
|
|
1163
|
+
const occupantNodeMap = createOccupantNodeMap(occupants);
|
|
1164
|
+
const defaultGraph = new DijstraGraph(mergeNodeMap([
|
|
1165
|
+
traversalNodeMap,
|
|
1166
|
+
escalatorNodeMap,
|
|
1167
|
+
rampNodeMap,
|
|
1168
|
+
elevatorNodeMap,
|
|
1169
|
+
stairNodeMap,
|
|
1170
|
+
amenityNodeMap,
|
|
1171
|
+
anchorsNodeMap,
|
|
1172
|
+
kioskNodeMap,
|
|
1173
|
+
unitNodeMap,
|
|
1174
|
+
occupantNodeMap
|
|
1175
|
+
]));
|
|
1176
|
+
const accessibleGraph = new DijstraGraph(mergeNodeMap([
|
|
1177
|
+
traversalNodeMap,
|
|
1178
|
+
rampNodeMap,
|
|
1179
|
+
elevatorNodeMap,
|
|
1180
|
+
amenityNodeMap,
|
|
1181
|
+
anchorsNodeMap,
|
|
1182
|
+
kioskNodeMap,
|
|
1183
|
+
unitNodeMap,
|
|
1184
|
+
occupantNodeMap
|
|
1185
|
+
]));
|
|
1186
|
+
const addCoordinateOrdinalNode = (params, locatedOnUnit) => {
|
|
1187
|
+
const [lat, lng, ordinal] = parseOrdinalCoordinate(params);
|
|
1188
|
+
if (locatedOnUnit) {
|
|
1189
|
+
const openings2 = unitOpenings[locatedOnUnit.id];
|
|
1190
|
+
for (const opening of openings2) {
|
|
1191
|
+
const openingCenter = turfCenter3(opening);
|
|
1192
|
+
const dis = distance2([lat, lng], openingCenter, { units: "meters" });
|
|
1193
|
+
defaultGraph.addNode(params, { [opening.id]: dis }).addNode(opening.id, { [params]: dis });
|
|
1194
|
+
accessibleGraph.addNode(params, { [opening.id]: dis }).addNode(opening.id, { [params]: dis });
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
return { defaultGraph, accessibleGraph, unitOpenings, addCoordinateOrdinalNode };
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
// src/data/navigate/steps/createStepUtils.ts
|
|
1202
|
+
import { capitalize } from "lodash";
|
|
1203
|
+
import _intersectionBy from "lodash/intersectionBy";
|
|
1204
|
+
|
|
1205
|
+
// src/data/navigate/description/describe.ts
|
|
1206
|
+
var t = (template, locale, options) => {
|
|
1207
|
+
return template.replace(`{{intermediary}}`, options.intermediary ?? "").replace(`{{toward}}`, options.toward?.[locale] ?? "").replace(`{{landmark}}`, options.landmark?.[locale] ?? "");
|
|
1208
|
+
};
|
|
1209
|
+
var describeVerticalStep = (fromLevel, toLevel, intermediary) => {
|
|
1210
|
+
const dir = fromLevel.properties.ordinal < toLevel.properties.ordinal ? "up" : "down";
|
|
1211
|
+
const template = `Take the {{intermediary}} ${dir} to {{toward}}`;
|
|
1212
|
+
return {
|
|
1213
|
+
template,
|
|
1214
|
+
text: t(template, "en", { intermediary, toward: toLevel.properties.name })
|
|
1215
|
+
};
|
|
1216
|
+
};
|
|
1217
|
+
var describeHorizontalStep = (intermediary, toward, landmark) => {
|
|
1218
|
+
const template = `Follow the path ${intermediary === "walkway" ? `along the walkway` : `through {{intermediary}}`} ${toward ? `toward {{toward}}` : ``} ${landmark ? `near {{landmark}}` : ``}`.trim();
|
|
1219
|
+
return {
|
|
1220
|
+
text: t(template, "en", { intermediary, toward, landmark }),
|
|
1221
|
+
template
|
|
1222
|
+
};
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
// src/data/navigate/steps/path/index.ts
|
|
1226
|
+
import _7 from "lodash";
|
|
1227
|
+
|
|
1228
|
+
// src/data/navigate/constants.ts
|
|
1229
|
+
var OBSTACLE_FEATURE_TYPES = [
|
|
1230
|
+
"kiosk"
|
|
1231
|
+
/* , "fixture" */
|
|
1232
|
+
];
|
|
1233
|
+
var OBSTACLE_CATEGORIES = [
|
|
1234
|
+
"fixture.water",
|
|
1235
|
+
"fixture.stage",
|
|
1236
|
+
"nonpublic",
|
|
1237
|
+
"opentobelow",
|
|
1238
|
+
"elevator",
|
|
1239
|
+
"escalator",
|
|
1240
|
+
"stairs",
|
|
1241
|
+
"stairs.emergencyexit",
|
|
1242
|
+
"room",
|
|
1243
|
+
"unspecified",
|
|
1244
|
+
"structure",
|
|
1245
|
+
"brick",
|
|
1246
|
+
"concrete",
|
|
1247
|
+
"drywall",
|
|
1248
|
+
"glass",
|
|
1249
|
+
"wood",
|
|
1250
|
+
"column"
|
|
1251
|
+
];
|
|
1252
|
+
var WALKABLE_CATEGORY = [
|
|
1253
|
+
"walkway",
|
|
1254
|
+
"parking",
|
|
1255
|
+
"room",
|
|
1256
|
+
"terrace",
|
|
1257
|
+
"unenclosedarea",
|
|
1258
|
+
"vegetation",
|
|
1259
|
+
"unspecified"
|
|
1260
|
+
];
|
|
1261
|
+
|
|
1262
|
+
// node_modules/@turf/helpers/dist/esm/index.js
|
|
1263
|
+
var earthRadius = 63710088e-1;
|
|
1264
|
+
var factors = {
|
|
1265
|
+
centimeters: earthRadius * 100,
|
|
1266
|
+
centimetres: earthRadius * 100,
|
|
1267
|
+
degrees: 360 / (2 * Math.PI),
|
|
1268
|
+
feet: earthRadius * 3.28084,
|
|
1269
|
+
inches: earthRadius * 39.37,
|
|
1270
|
+
kilometers: earthRadius / 1e3,
|
|
1271
|
+
kilometres: earthRadius / 1e3,
|
|
1272
|
+
meters: earthRadius,
|
|
1273
|
+
metres: earthRadius,
|
|
1274
|
+
miles: earthRadius / 1609.344,
|
|
1275
|
+
millimeters: earthRadius * 1e3,
|
|
1276
|
+
millimetres: earthRadius * 1e3,
|
|
1277
|
+
nauticalmiles: earthRadius / 1852,
|
|
1278
|
+
radians: 1,
|
|
1279
|
+
yards: earthRadius * 1.0936
|
|
1280
|
+
};
|
|
1281
|
+
function feature(geom, properties, options = {}) {
|
|
1282
|
+
const feat = { type: "Feature" };
|
|
1283
|
+
if (options.id === 0 || options.id) {
|
|
1284
|
+
feat.id = options.id;
|
|
1285
|
+
}
|
|
1286
|
+
if (options.bbox) {
|
|
1287
|
+
feat.bbox = options.bbox;
|
|
1288
|
+
}
|
|
1289
|
+
feat.properties = properties || {};
|
|
1290
|
+
feat.geometry = geom;
|
|
1291
|
+
return feat;
|
|
1292
|
+
}
|
|
1293
|
+
function point(coordinates, properties, options = {}) {
|
|
1294
|
+
if (!coordinates) {
|
|
1295
|
+
throw new Error("coordinates is required");
|
|
1296
|
+
}
|
|
1297
|
+
if (!Array.isArray(coordinates)) {
|
|
1298
|
+
throw new Error("coordinates must be an Array");
|
|
1299
|
+
}
|
|
1300
|
+
if (coordinates.length < 2) {
|
|
1301
|
+
throw new Error("coordinates must be at least 2 numbers long");
|
|
1302
|
+
}
|
|
1303
|
+
if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {
|
|
1304
|
+
throw new Error("coordinates must contain numbers");
|
|
1305
|
+
}
|
|
1306
|
+
const geom = {
|
|
1307
|
+
type: "Point",
|
|
1308
|
+
coordinates
|
|
1309
|
+
};
|
|
1310
|
+
return feature(geom, properties, options);
|
|
1311
|
+
}
|
|
1312
|
+
function polygon(coordinates, properties, options = {}) {
|
|
1313
|
+
for (const ring of coordinates) {
|
|
1314
|
+
if (ring.length < 4) {
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
"Each LinearRing of a Polygon must have 4 or more Positions."
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
if (ring[ring.length - 1].length !== ring[0].length) {
|
|
1320
|
+
throw new Error("First and last Position are not equivalent.");
|
|
1321
|
+
}
|
|
1322
|
+
for (let j = 0; j < ring[ring.length - 1].length; j++) {
|
|
1323
|
+
if (ring[ring.length - 1][j] !== ring[0][j]) {
|
|
1324
|
+
throw new Error("First and last Position are not equivalent.");
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
const geom = {
|
|
1329
|
+
type: "Polygon",
|
|
1330
|
+
coordinates
|
|
1331
|
+
};
|
|
1332
|
+
return feature(geom, properties, options);
|
|
1333
|
+
}
|
|
1334
|
+
function lineString(coordinates, properties, options = {}) {
|
|
1335
|
+
if (coordinates.length < 2) {
|
|
1336
|
+
throw new Error("coordinates must be an array of two or more positions");
|
|
1337
|
+
}
|
|
1338
|
+
const geom = {
|
|
1339
|
+
type: "LineString",
|
|
1340
|
+
coordinates
|
|
1341
|
+
};
|
|
1342
|
+
return feature(geom, properties, options);
|
|
1343
|
+
}
|
|
1344
|
+
function featureCollection(features, options = {}) {
|
|
1345
|
+
const fc = { type: "FeatureCollection" };
|
|
1346
|
+
if (options.id) {
|
|
1347
|
+
fc.id = options.id;
|
|
1348
|
+
}
|
|
1349
|
+
if (options.bbox) {
|
|
1350
|
+
fc.bbox = options.bbox;
|
|
1351
|
+
}
|
|
1352
|
+
fc.features = features;
|
|
1353
|
+
return fc;
|
|
1354
|
+
}
|
|
1355
|
+
function isNumber(num) {
|
|
1356
|
+
return !isNaN(num) && num !== null && !Array.isArray(num);
|
|
1357
|
+
}
|
|
1358
|
+
function isObject(input) {
|
|
1359
|
+
return input !== null && typeof input === "object" && !Array.isArray(input);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// node_modules/@turf/invariant/dist/esm/index.js
|
|
1363
|
+
function getCoord(coord) {
|
|
1364
|
+
if (!coord) {
|
|
1365
|
+
throw new Error("coord is required");
|
|
1366
|
+
}
|
|
1367
|
+
if (!Array.isArray(coord)) {
|
|
1368
|
+
if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
|
|
1369
|
+
return [...coord.geometry.coordinates];
|
|
1370
|
+
}
|
|
1371
|
+
if (coord.type === "Point") {
|
|
1372
|
+
return [...coord.coordinates];
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
|
|
1376
|
+
return [...coord];
|
|
1377
|
+
}
|
|
1378
|
+
throw new Error("coord must be GeoJSON Point or an Array of numbers");
|
|
1379
|
+
}
|
|
1380
|
+
function getGeom(geojson) {
|
|
1381
|
+
if (geojson.type === "Feature") {
|
|
1382
|
+
return geojson.geometry;
|
|
1383
|
+
}
|
|
1384
|
+
return geojson;
|
|
1385
|
+
}
|
|
1386
|
+
function getType(geojson, _name) {
|
|
1387
|
+
if (geojson.type === "FeatureCollection") {
|
|
1388
|
+
return "FeatureCollection";
|
|
1389
|
+
}
|
|
1390
|
+
if (geojson.type === "GeometryCollection") {
|
|
1391
|
+
return "GeometryCollection";
|
|
1392
|
+
}
|
|
1393
|
+
if (geojson.type === "Feature" && geojson.geometry !== null) {
|
|
1394
|
+
return geojson.geometry.type;
|
|
1395
|
+
}
|
|
1396
|
+
return geojson.type;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/data/navigate/steps/path/index.ts
|
|
1400
|
+
import difference from "@turf/difference";
|
|
1401
|
+
import envelope from "@turf/envelope";
|
|
1402
|
+
import booleanOverlap from "@turf/boolean-overlap";
|
|
1403
|
+
import booleanIntersects from "@turf/boolean-intersects";
|
|
1404
|
+
|
|
1405
|
+
// ../../node_modules/@turf/meta/dist/esm/index.js
|
|
1406
|
+
function coordEach(geojson, callback, excludeWrapCoord) {
|
|
1407
|
+
if (geojson === null) return;
|
|
1408
|
+
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
|
|
1409
|
+
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
|
|
1410
|
+
geometryMaybeCollection = isFeatureCollection ? geojson.features[featureIndex].geometry : isFeature ? geojson.geometry : geojson;
|
|
1411
|
+
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
|
|
1412
|
+
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
|
|
1413
|
+
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
|
|
1414
|
+
var multiFeatureIndex = 0;
|
|
1415
|
+
var geometryIndex = 0;
|
|
1416
|
+
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
|
|
1417
|
+
if (geometry === null) continue;
|
|
1418
|
+
coords = geometry.coordinates;
|
|
1419
|
+
var geomType = geometry.type;
|
|
1420
|
+
wrapShrink = excludeWrapCoord && (geomType === "Polygon" || geomType === "MultiPolygon") ? 1 : 0;
|
|
1421
|
+
switch (geomType) {
|
|
1422
|
+
case null:
|
|
1423
|
+
break;
|
|
1424
|
+
case "Point":
|
|
1425
|
+
if (callback(
|
|
1426
|
+
coords,
|
|
1427
|
+
coordIndex,
|
|
1428
|
+
featureIndex,
|
|
1429
|
+
multiFeatureIndex,
|
|
1430
|
+
geometryIndex
|
|
1431
|
+
) === false)
|
|
1432
|
+
return false;
|
|
1433
|
+
coordIndex++;
|
|
1434
|
+
multiFeatureIndex++;
|
|
1435
|
+
break;
|
|
1436
|
+
case "LineString":
|
|
1437
|
+
case "MultiPoint":
|
|
1438
|
+
for (j = 0; j < coords.length; j++) {
|
|
1439
|
+
if (callback(
|
|
1440
|
+
coords[j],
|
|
1441
|
+
coordIndex,
|
|
1442
|
+
featureIndex,
|
|
1443
|
+
multiFeatureIndex,
|
|
1444
|
+
geometryIndex
|
|
1445
|
+
) === false)
|
|
1446
|
+
return false;
|
|
1447
|
+
coordIndex++;
|
|
1448
|
+
if (geomType === "MultiPoint") multiFeatureIndex++;
|
|
1449
|
+
}
|
|
1450
|
+
if (geomType === "LineString") multiFeatureIndex++;
|
|
1451
|
+
break;
|
|
1452
|
+
case "Polygon":
|
|
1453
|
+
case "MultiLineString":
|
|
1454
|
+
for (j = 0; j < coords.length; j++) {
|
|
1455
|
+
for (k = 0; k < coords[j].length - wrapShrink; k++) {
|
|
1456
|
+
if (callback(
|
|
1457
|
+
coords[j][k],
|
|
1458
|
+
coordIndex,
|
|
1459
|
+
featureIndex,
|
|
1460
|
+
multiFeatureIndex,
|
|
1461
|
+
geometryIndex
|
|
1462
|
+
) === false)
|
|
1463
|
+
return false;
|
|
1464
|
+
coordIndex++;
|
|
1465
|
+
}
|
|
1466
|
+
if (geomType === "MultiLineString") multiFeatureIndex++;
|
|
1467
|
+
if (geomType === "Polygon") geometryIndex++;
|
|
1468
|
+
}
|
|
1469
|
+
if (geomType === "Polygon") multiFeatureIndex++;
|
|
1470
|
+
break;
|
|
1471
|
+
case "MultiPolygon":
|
|
1472
|
+
for (j = 0; j < coords.length; j++) {
|
|
1473
|
+
geometryIndex = 0;
|
|
1474
|
+
for (k = 0; k < coords[j].length; k++) {
|
|
1475
|
+
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
|
|
1476
|
+
if (callback(
|
|
1477
|
+
coords[j][k][l],
|
|
1478
|
+
coordIndex,
|
|
1479
|
+
featureIndex,
|
|
1480
|
+
multiFeatureIndex,
|
|
1481
|
+
geometryIndex
|
|
1482
|
+
) === false)
|
|
1483
|
+
return false;
|
|
1484
|
+
coordIndex++;
|
|
1485
|
+
}
|
|
1486
|
+
geometryIndex++;
|
|
1487
|
+
}
|
|
1488
|
+
multiFeatureIndex++;
|
|
1489
|
+
}
|
|
1490
|
+
break;
|
|
1491
|
+
case "GeometryCollection":
|
|
1492
|
+
for (j = 0; j < geometry.geometries.length; j++)
|
|
1493
|
+
if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false)
|
|
1494
|
+
return false;
|
|
1495
|
+
break;
|
|
1496
|
+
default:
|
|
1497
|
+
throw new Error("Unknown Geometry Type");
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// ../../node_modules/@turf/bbox/dist/esm/index.js
|
|
1504
|
+
function bbox(geojson, options = {}) {
|
|
1505
|
+
if (geojson.bbox != null && true !== options.recompute) {
|
|
1506
|
+
return geojson.bbox;
|
|
1507
|
+
}
|
|
1508
|
+
const result = [Infinity, Infinity, -Infinity, -Infinity];
|
|
1509
|
+
coordEach(geojson, (coord) => {
|
|
1510
|
+
if (result[0] > coord[0]) {
|
|
1511
|
+
result[0] = coord[0];
|
|
1512
|
+
}
|
|
1513
|
+
if (result[1] > coord[1]) {
|
|
1514
|
+
result[1] = coord[1];
|
|
1515
|
+
}
|
|
1516
|
+
if (result[2] < coord[0]) {
|
|
1517
|
+
result[2] = coord[0];
|
|
1518
|
+
}
|
|
1519
|
+
if (result[3] < coord[1]) {
|
|
1520
|
+
result[3] = coord[1];
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
return result;
|
|
1524
|
+
}
|
|
1525
|
+
var index_default = bbox;
|
|
1526
|
+
|
|
1527
|
+
// src/data/navigate/steps/path/turf/shortestPath.ts
|
|
1528
|
+
import booleanPointInPolygon3 from "@turf/boolean-point-in-polygon";
|
|
1529
|
+
import distance3 from "@turf/distance";
|
|
1530
|
+
import scale from "@turf/transform-scale";
|
|
1531
|
+
import union from "@turf/union";
|
|
1532
|
+
import bboxPolygon from "@turf/bbox-polygon";
|
|
1533
|
+
import { cleanCoords } from "@turf/clean-coords";
|
|
1534
|
+
import PF from "pathfinding";
|
|
1535
|
+
import set3 from "lodash/set";
|
|
1536
|
+
|
|
1537
|
+
// src/data/navigate/steps/path/turf/stringPull.ts
|
|
1538
|
+
function stringPull(grid, path) {
|
|
1539
|
+
const isWalkable = (x, y) => grid.isInside(x, y) && grid.isWalkableAt(x, y);
|
|
1540
|
+
function hasLOS(a, b) {
|
|
1541
|
+
let x0 = a[0], y0 = a[1];
|
|
1542
|
+
const x1 = b[0], y1 = b[1];
|
|
1543
|
+
if (!isWalkable(x0, y0) || !isWalkable(x1, y1)) return false;
|
|
1544
|
+
const dx = Math.abs(x1 - x0);
|
|
1545
|
+
const dy = Math.abs(y1 - y0);
|
|
1546
|
+
const sx = x0 < x1 ? 1 : -1;
|
|
1547
|
+
const sy = y0 < y1 ? 1 : -1;
|
|
1548
|
+
let err = dx - dy;
|
|
1549
|
+
while (true) {
|
|
1550
|
+
if (!isWalkable(x0, y0)) return false;
|
|
1551
|
+
if (x0 === x1 && y0 === y1) break;
|
|
1552
|
+
const e2 = err * 2;
|
|
1553
|
+
let nx = x0;
|
|
1554
|
+
let ny = y0;
|
|
1555
|
+
let movedX = false;
|
|
1556
|
+
let movedY = false;
|
|
1557
|
+
if (e2 > -dy) {
|
|
1558
|
+
err -= dy;
|
|
1559
|
+
nx += sx;
|
|
1560
|
+
movedX = true;
|
|
1561
|
+
}
|
|
1562
|
+
if (e2 < dx) {
|
|
1563
|
+
err += dx;
|
|
1564
|
+
ny += sy;
|
|
1565
|
+
movedY = true;
|
|
1566
|
+
}
|
|
1567
|
+
if (movedX && movedY) {
|
|
1568
|
+
if (!isWalkable(nx, y0) || !isWalkable(x0, ny)) return false;
|
|
1569
|
+
}
|
|
1570
|
+
x0 = nx;
|
|
1571
|
+
y0 = ny;
|
|
1572
|
+
}
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
if (path.length <= 2) return path;
|
|
1576
|
+
const out = [path[0]];
|
|
1577
|
+
let i = 0;
|
|
1578
|
+
while (i < path.length - 1) {
|
|
1579
|
+
let best = i + 1;
|
|
1580
|
+
for (let j = i + 2; j < path.length; j++) {
|
|
1581
|
+
if (hasLOS(path[i], path[j])) best = j;
|
|
1582
|
+
else break;
|
|
1583
|
+
}
|
|
1584
|
+
out.push(path[best]);
|
|
1585
|
+
i = best;
|
|
1586
|
+
}
|
|
1587
|
+
return out;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/data/navigate/steps/path/turf/pruneSmallAngle.ts
|
|
1591
|
+
function pruneSmallAngles(path, minDeg = 10) {
|
|
1592
|
+
if (path.length <= 2) return path;
|
|
1593
|
+
const out = [path[0]];
|
|
1594
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
1595
|
+
const a = out.at(-1);
|
|
1596
|
+
const b = path[i];
|
|
1597
|
+
const c = path[i + 1];
|
|
1598
|
+
const abx = b[0] - a[0], aby = b[1] - a[1];
|
|
1599
|
+
const bcx = c[0] - b[0], bcy = c[1] - b[1];
|
|
1600
|
+
const dot = abx * bcx + aby * bcy;
|
|
1601
|
+
const ab = Math.hypot(abx, aby);
|
|
1602
|
+
const bc = Math.hypot(bcx, bcy);
|
|
1603
|
+
const angle = Math.acos(dot / (ab * bc)) * 180 / Math.PI;
|
|
1604
|
+
if (angle > minDeg) out.push(b);
|
|
1605
|
+
}
|
|
1606
|
+
out.push(path.at(-1));
|
|
1607
|
+
return out;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// src/data/navigate/steps/path/turf/pruneShortSegments.ts
|
|
1611
|
+
function pruneShortSegments(path, minLen = 5) {
|
|
1612
|
+
const out = [path[0]];
|
|
1613
|
+
for (let i = 1; i < path.length; i++) {
|
|
1614
|
+
const [x0, y0] = out.at(-1);
|
|
1615
|
+
const [x1, y1] = path[i];
|
|
1616
|
+
if (Math.hypot(x1 - x0, y1 - y0) >= minLen) {
|
|
1617
|
+
out.push(path[i]);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return out;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// src/data/navigate/steps/path/turf/clearance.ts
|
|
1624
|
+
function buildClearanceGrid(matrix) {
|
|
1625
|
+
const h = matrix.length;
|
|
1626
|
+
const w = matrix[0].length;
|
|
1627
|
+
const INF = 1e9;
|
|
1628
|
+
const dist = Array.from({ length: h }, () => Array(w).fill(INF));
|
|
1629
|
+
const q = [];
|
|
1630
|
+
for (let y = 0; y < h; y++) {
|
|
1631
|
+
for (let x = 0; x < w; x++) {
|
|
1632
|
+
if (matrix[y][x] === 1) {
|
|
1633
|
+
dist[y][x] = 0;
|
|
1634
|
+
q.push([x, y]);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const dirs = [
|
|
1639
|
+
[1, 0],
|
|
1640
|
+
[-1, 0],
|
|
1641
|
+
[0, 1],
|
|
1642
|
+
[0, -1],
|
|
1643
|
+
[1, 1],
|
|
1644
|
+
[1, -1],
|
|
1645
|
+
[-1, 1],
|
|
1646
|
+
[-1, -1]
|
|
1647
|
+
];
|
|
1648
|
+
let qi = 0;
|
|
1649
|
+
while (qi < q.length) {
|
|
1650
|
+
const [x, y] = q[qi++];
|
|
1651
|
+
const d0 = dist[y][x];
|
|
1652
|
+
for (const [dx, dy] of dirs) {
|
|
1653
|
+
const nx = x + dx, ny = y + dy;
|
|
1654
|
+
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
|
1655
|
+
const nd = d0 + 1;
|
|
1656
|
+
if (nd < dist[ny][nx]) {
|
|
1657
|
+
dist[ny][nx] = nd;
|
|
1658
|
+
q.push([nx, ny]);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
return dist;
|
|
1663
|
+
}
|
|
1664
|
+
function snapPointToClearancePeak(p, clearance, isWalkableCell, radius = 4) {
|
|
1665
|
+
const [px, py] = p;
|
|
1666
|
+
let best = p;
|
|
1667
|
+
let bestScore = clearance[py]?.[px] ?? -Infinity;
|
|
1668
|
+
for (let dy = -radius; dy <= radius; dy++) {
|
|
1669
|
+
for (let dx = -radius; dx <= radius; dx++) {
|
|
1670
|
+
const x = px + dx;
|
|
1671
|
+
const y = py + dy;
|
|
1672
|
+
if (!isWalkableCell(x, y)) continue;
|
|
1673
|
+
const score = clearance[y][x];
|
|
1674
|
+
const penalty = Math.hypot(dx, dy) * 5e-3;
|
|
1675
|
+
const finalScore = score - penalty;
|
|
1676
|
+
if (finalScore > bestScore) {
|
|
1677
|
+
bestScore = finalScore;
|
|
1678
|
+
best = [x, y];
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return best;
|
|
1683
|
+
}
|
|
1684
|
+
function centerlineSnapPath(path, clearance, isWalkableCell, radius = 4) {
|
|
1685
|
+
const snapped = path.map((p) => snapPointToClearancePeak(p, clearance, isWalkableCell, radius));
|
|
1686
|
+
return snapped;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// src/data/navigate/steps/path/turf/shortestPath.ts
|
|
1690
|
+
function shortestPath(start, end, options) {
|
|
1691
|
+
options = options || {};
|
|
1692
|
+
if (!isObject(options)) throw new Error("options is invalid");
|
|
1693
|
+
let resolution = options.resolution;
|
|
1694
|
+
const smoothenPath = options.smoothenPath;
|
|
1695
|
+
let obstacles = options.obstacles || featureCollection([]);
|
|
1696
|
+
if (!start) throw new Error("start is required");
|
|
1697
|
+
if (!end) throw new Error("end is required");
|
|
1698
|
+
if (resolution && !isNumber(resolution) || resolution <= 0)
|
|
1699
|
+
throw new Error("options.resolution must be a number, greater than 0");
|
|
1700
|
+
const startCoord = getCoord(start);
|
|
1701
|
+
const endCoord = getCoord(end);
|
|
1702
|
+
start = point(startCoord);
|
|
1703
|
+
end = point(endCoord);
|
|
1704
|
+
switch (getType(obstacles)) {
|
|
1705
|
+
case "FeatureCollection":
|
|
1706
|
+
if (obstacles.features.length === 0)
|
|
1707
|
+
return lineString([startCoord, endCoord]);
|
|
1708
|
+
break;
|
|
1709
|
+
case "Polygon":
|
|
1710
|
+
obstacles = featureCollection([feature(getGeom(obstacles))]);
|
|
1711
|
+
break;
|
|
1712
|
+
default:
|
|
1713
|
+
throw new Error("invalid obstacles");
|
|
1714
|
+
}
|
|
1715
|
+
const collection = obstacles;
|
|
1716
|
+
collection.features.push(start, end);
|
|
1717
|
+
const box = index_default(scale(bboxPolygon(index_default(collection)), 1.15));
|
|
1718
|
+
if (!resolution) {
|
|
1719
|
+
const width = distance3([box[0], box[1]], [box[2], box[1]], options);
|
|
1720
|
+
resolution = width / 100;
|
|
1721
|
+
}
|
|
1722
|
+
collection.features.pop();
|
|
1723
|
+
collection.features.pop();
|
|
1724
|
+
const [west, south, east, north] = box;
|
|
1725
|
+
const xFraction = resolution / distance3([west, south], [east, south], options);
|
|
1726
|
+
const cellWidth = xFraction * (east - west);
|
|
1727
|
+
const yFraction = resolution / distance3([west, south], [west, north], options);
|
|
1728
|
+
const cellHeight = yFraction * (north - south);
|
|
1729
|
+
const bboxHorizontalSide = east - west;
|
|
1730
|
+
const bboxVerticalSide = north - south;
|
|
1731
|
+
const columns = Math.floor(bboxHorizontalSide / cellWidth);
|
|
1732
|
+
const rows = Math.floor(bboxVerticalSide / cellHeight);
|
|
1733
|
+
const deltaX = (bboxHorizontalSide - columns * cellWidth) / 2;
|
|
1734
|
+
const deltaY = (bboxVerticalSide - rows * cellHeight) / 2;
|
|
1735
|
+
let closestToStart = null, closestToEnd = null, minDistStart = Infinity, minDistEnd = Infinity, currentY = north - deltaY, currentX = west + deltaX, row = 0, column = 0, distStart, distEnd, pt, isInsideObstacle;
|
|
1736
|
+
const roundLoopY = Math.ceil((currentY - south) / cellHeight);
|
|
1737
|
+
const roundLoopX = Math.ceil((east - currentX) / cellWidth);
|
|
1738
|
+
let totalRounds = roundLoopX * roundLoopY;
|
|
1739
|
+
const pointMatrix = [];
|
|
1740
|
+
const matrix = [];
|
|
1741
|
+
const obstacleTotal = collection.features.length;
|
|
1742
|
+
const obstacleFeatures = collection.features;
|
|
1743
|
+
let combinedObstacle = obstacleFeatures[0];
|
|
1744
|
+
let obstacleIndex = 0;
|
|
1745
|
+
for (obstacleIndex = 0; obstacleIndex < obstacleTotal; obstacleIndex++) {
|
|
1746
|
+
const nextObstacleFeature = obstacleFeatures[obstacleIndex + 1];
|
|
1747
|
+
if (!nextObstacleFeature) continue;
|
|
1748
|
+
try {
|
|
1749
|
+
combinedObstacle = union(
|
|
1750
|
+
featureCollection([combinedObstacle, nextObstacleFeature])
|
|
1751
|
+
);
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
console.log(e);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
while (totalRounds--) {
|
|
1757
|
+
pt = point([currentX, currentY]);
|
|
1758
|
+
isInsideObstacle = booleanPointInPolygon3(pt, combinedObstacle);
|
|
1759
|
+
set3(matrix, `[${row}][${column}]`, isInsideObstacle ? 1 : 0);
|
|
1760
|
+
set3(pointMatrix, `[${row}][${column}]`, `${currentX}|${currentY}`);
|
|
1761
|
+
distStart = distance3(pt, start);
|
|
1762
|
+
if (!isInsideObstacle && distStart < minDistStart) {
|
|
1763
|
+
minDistStart = distStart;
|
|
1764
|
+
closestToStart = { x: column, y: row };
|
|
1765
|
+
}
|
|
1766
|
+
distEnd = distance3(pt, end);
|
|
1767
|
+
if (!isInsideObstacle && distEnd < minDistEnd) {
|
|
1768
|
+
minDistEnd = distEnd;
|
|
1769
|
+
closestToEnd = { x: column, y: row };
|
|
1770
|
+
}
|
|
1771
|
+
if (column < roundLoopX) {
|
|
1772
|
+
currentX += cellWidth;
|
|
1773
|
+
column++;
|
|
1774
|
+
continue;
|
|
1775
|
+
}
|
|
1776
|
+
if (row < roundLoopY) {
|
|
1777
|
+
currentY -= cellHeight;
|
|
1778
|
+
currentX = west + deltaX;
|
|
1779
|
+
column = 0;
|
|
1780
|
+
row++;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
const finder = new PF.AStarFinder({
|
|
1784
|
+
allowDiagonal: true,
|
|
1785
|
+
dontCrossCorners: true,
|
|
1786
|
+
heuristic: PF.Heuristic.euclidean
|
|
1787
|
+
});
|
|
1788
|
+
const grid = new PF.Grid(matrix);
|
|
1789
|
+
const startOnMatrix = [closestToStart.x, closestToStart.y];
|
|
1790
|
+
const endOnMatrix = [closestToEnd.x, closestToEnd.y];
|
|
1791
|
+
let result = finder.findPath(...startOnMatrix, ...endOnMatrix, grid);
|
|
1792
|
+
if (result.length > 0) {
|
|
1793
|
+
result = stringPull(grid, result);
|
|
1794
|
+
const clearanceGrid = buildClearanceGrid(matrix);
|
|
1795
|
+
const isWalkable = (x, y) => grid.isInside(x, y) && grid.isWalkableAt(x, y);
|
|
1796
|
+
result = centerlineSnapPath(result, clearanceGrid, isWalkable);
|
|
1797
|
+
result = stringPull(grid, result);
|
|
1798
|
+
result = pruneSmallAngles(result);
|
|
1799
|
+
result = pruneShortSegments(result);
|
|
1800
|
+
}
|
|
1801
|
+
result.pop();
|
|
1802
|
+
result.shift();
|
|
1803
|
+
const path = [startCoord];
|
|
1804
|
+
result.forEach((coord) => {
|
|
1805
|
+
const coords = pointMatrix[coord[1]][coord[0]].split("|");
|
|
1806
|
+
path.push([+coords[0], +coords[1]]);
|
|
1807
|
+
});
|
|
1808
|
+
path.push(endCoord);
|
|
1809
|
+
return cleanCoords(lineString(path));
|
|
1810
|
+
}
|
|
1811
|
+
var shortestPath_default = shortestPath;
|
|
1812
|
+
|
|
1813
|
+
// src/data/navigate/steps/path/index.ts
|
|
1814
|
+
var createStepPathUtils = (options) => {
|
|
1815
|
+
const resolution = options.resolution ?? 88e-5;
|
|
1816
|
+
const { units = [], kiosks = [], fixtures = [] } = options.data;
|
|
1817
|
+
const possibleObstacleFeatures = [...units, ...kiosks, ...fixtures];
|
|
1818
|
+
const filterObstaclesByOrdinal = (levelId) => {
|
|
1819
|
+
return possibleObstacleFeatures.filter(({ feature_type, properties, geometry }) => {
|
|
1820
|
+
return properties.level_id === levelId && ["Polygon", "MultiPolygon"].includes(geometry.type) && (OBSTACLE_FEATURE_TYPES.includes(feature_type) || "category" in properties && OBSTACLE_CATEGORIES.includes(properties.category));
|
|
1821
|
+
});
|
|
1822
|
+
};
|
|
1823
|
+
const findObstaclesFromWalkway = (intermediaryUnit, exceptionIds = []) => {
|
|
1824
|
+
const result = featureCollection([]);
|
|
1825
|
+
if (!intermediaryUnit) return result;
|
|
1826
|
+
const walkwayLevelId = intermediaryUnit.properties.level_id;
|
|
1827
|
+
const obstacleOnLevel = filterObstaclesByOrdinal(walkwayLevelId).filter(
|
|
1828
|
+
(obstacle) => !exceptionIds.includes(obstacle.id)
|
|
1829
|
+
);
|
|
1830
|
+
const relatedObstacleWithIntermediary = obstacleOnLevel.reduce(
|
|
1831
|
+
(obstacles, feature2) => {
|
|
1832
|
+
if (
|
|
1833
|
+
// Prevent detecting itself as an obstacle
|
|
1834
|
+
// Ex. Unable to draw a line to amenity located with in a room as room is also consider as obstacle
|
|
1835
|
+
feature2.id !== intermediaryUnit.id && (booleanOverlap(intermediaryUnit, feature2) || booleanIntersects(intermediaryUnit, feature2))
|
|
1836
|
+
) {
|
|
1837
|
+
const polygons = getType(feature2) === "Polygon" ? [polygon(feature2.geometry.coordinates, { id: feature2.id })] : feature2.geometry.coordinates.map((ring) => polygon(ring, { id: feature2.id }));
|
|
1838
|
+
obstacles.push(...polygons);
|
|
1839
|
+
}
|
|
1840
|
+
return obstacles;
|
|
1841
|
+
},
|
|
1842
|
+
[]
|
|
1843
|
+
);
|
|
1844
|
+
const intermediaryExtends = envelope(intermediaryUnit);
|
|
1845
|
+
const walkwayPerimeter = difference(
|
|
1846
|
+
featureCollection([intermediaryExtends, intermediaryUnit])
|
|
1847
|
+
);
|
|
1848
|
+
result.features.push(...relatedObstacleWithIntermediary, walkwayPerimeter);
|
|
1849
|
+
return result;
|
|
1850
|
+
};
|
|
1851
|
+
const findPathOnArea = (originPoint, destinationPoint, options2) => {
|
|
1852
|
+
const { obstacles = featureCollection([]), resolution: resolution2, properties } = options2 || {};
|
|
1853
|
+
const stepPath = shortestPath_default(originPoint, destinationPoint, {
|
|
1854
|
+
obstacles,
|
|
1855
|
+
smoothenPath: false,
|
|
1856
|
+
resolution: resolution2
|
|
1857
|
+
});
|
|
1858
|
+
stepPath.properties = properties;
|
|
1859
|
+
return stepPath;
|
|
1860
|
+
};
|
|
1861
|
+
const findStepPath = (from, to, intermediaries) => {
|
|
1862
|
+
const t0 = performance.now();
|
|
1863
|
+
const relatedWalkablePlatform = intermediaries.find(
|
|
1864
|
+
(feature2) => WALKABLE_CATEGORY.includes(feature2.properties.category)
|
|
1865
|
+
);
|
|
1866
|
+
const exceptionFeatureIds = [];
|
|
1867
|
+
const obstacles = findObstaclesFromWalkway(
|
|
1868
|
+
relatedWalkablePlatform,
|
|
1869
|
+
_7.compact(exceptionFeatureIds)
|
|
1870
|
+
);
|
|
1871
|
+
const line = findPathOnArea(from, to, {
|
|
1872
|
+
resolution,
|
|
1873
|
+
obstacles
|
|
1874
|
+
});
|
|
1875
|
+
return line.geometry.coordinates;
|
|
1876
|
+
};
|
|
1877
|
+
return {
|
|
1878
|
+
findStepPath
|
|
1879
|
+
};
|
|
1880
|
+
};
|
|
1881
|
+
|
|
1882
|
+
// src/data/navigate/steps/utils/combineWalkwaySteps.ts
|
|
1883
|
+
import uniq from "lodash/uniq";
|
|
1884
|
+
var combineWalkwaySteps = (steps) => {
|
|
1885
|
+
let result = [];
|
|
1886
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1887
|
+
const thisStep = steps[i];
|
|
1888
|
+
if (i === steps.length - 1) {
|
|
1889
|
+
result.push(thisStep);
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
const nextStep = steps[i + 1];
|
|
1893
|
+
if (thisStep.intermediaryCategory === "walkway" && nextStep.intermediaryCategory === "walkway" && thisStep.from.source.type === "opening" && nextStep.from.source.type === "opening") {
|
|
1894
|
+
result.push({
|
|
1895
|
+
from: thisStep.from,
|
|
1896
|
+
to: nextStep.to,
|
|
1897
|
+
levelIds: uniq([...thisStep.levelIds, ...nextStep.levelIds]),
|
|
1898
|
+
ordinals: uniq([...thisStep.ordinals, ...nextStep.ordinals]),
|
|
1899
|
+
intermediaryCategory: "walkway",
|
|
1900
|
+
description: nextStep.description,
|
|
1901
|
+
path: [
|
|
1902
|
+
...thisStep.path,
|
|
1903
|
+
...nextStep.path
|
|
1904
|
+
]
|
|
1905
|
+
});
|
|
1906
|
+
i++;
|
|
1907
|
+
} else {
|
|
1908
|
+
result.push(thisStep);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
return result;
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
// src/data/navigate/steps/createStepUtils.ts
|
|
1915
|
+
var createStepUtils = (options) => {
|
|
1916
|
+
const { data: { units, relationships }, findByIdSync } = options;
|
|
1917
|
+
const stepPathUtils = createStepPathUtils({ ...options, resolution: 88e-5 });
|
|
1918
|
+
const findUnitBetweenOpenings = (originId, destinationId) => {
|
|
1919
|
+
const origin = findByIdSync(originId);
|
|
1920
|
+
const destination = findByIdSync(destinationId);
|
|
1921
|
+
const matchedOne = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(origin.id));
|
|
1922
|
+
const matchOneUnits = matchedOne ? [matchedOne.properties.origin, matchedOne.properties.destination] : [];
|
|
1923
|
+
const matchedTwo = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(destination.id));
|
|
1924
|
+
const matchTwoUnits = matchedTwo ? [matchedTwo.properties.origin, matchedTwo.properties.destination] : [];
|
|
1925
|
+
const unitIds = _intersectionBy(matchOneUnits, matchTwoUnits, "id");
|
|
1926
|
+
return unitIds.map(({ id }) => findByIdSync(id));
|
|
1927
|
+
};
|
|
1928
|
+
const findHorizontalIntermediary = (from, to) => {
|
|
1929
|
+
if (from.source.type !== "opening") {
|
|
1930
|
+
const unit = findContainingUnitAtPoint(from.point, from.levelId, units);
|
|
1931
|
+
return unit ? [unit] : [];
|
|
1932
|
+
}
|
|
1933
|
+
if (to.source.type !== "opening") {
|
|
1934
|
+
const unit = findContainingUnitAtPoint(to.point, to.levelId, units);
|
|
1935
|
+
return unit ? [unit] : [];
|
|
1936
|
+
}
|
|
1937
|
+
return findUnitBetweenOpenings(from.source.id, to.source.id);
|
|
1938
|
+
};
|
|
1939
|
+
const findVerticalIntermediary = (from, to) => {
|
|
1940
|
+
const firstOpeningId = from.source.id;
|
|
1941
|
+
const secondOpeningId = to.source.id;
|
|
1942
|
+
const relationship = relationships.find((rel) => {
|
|
1943
|
+
return rel.properties.origin?.id === firstOpeningId && rel.properties.destination?.id === secondOpeningId || rel.properties.origin?.id === secondOpeningId && rel.properties.destination?.id === firstOpeningId;
|
|
1944
|
+
});
|
|
1945
|
+
const intermediaryTypeAndId = relationship.properties.intermediary;
|
|
1946
|
+
return intermediaryTypeAndId.map(({ id }) => findByIdSync(id));
|
|
1947
|
+
};
|
|
1948
|
+
const formatCategoryLabel = (category) => {
|
|
1949
|
+
return capitalize(category);
|
|
1950
|
+
};
|
|
1951
|
+
const getNextStepIntermediary = (from, to, intermediary) => {
|
|
1952
|
+
if (to.type === "end") return to.name;
|
|
1953
|
+
const intermediaryIds = intermediary.map((int) => int.id);
|
|
1954
|
+
const relationship = relationships.find((rel) => rel.properties.intermediary.map((int) => int.id).includes(to.source.id));
|
|
1955
|
+
if (!relationship) return to.name;
|
|
1956
|
+
const candidates = [relationship.properties.origin.id, relationship.properties.destination.id];
|
|
1957
|
+
const nextUnitId = candidates.filter((id) => !intermediaryIds.includes(id))[0];
|
|
1958
|
+
if (!nextUnitId) return to.name;
|
|
1959
|
+
const nextUnit = findByIdSync(nextUnitId);
|
|
1960
|
+
return { en: formatCategoryLabel(`${nextUnit.properties.category}`) };
|
|
1961
|
+
};
|
|
1962
|
+
const createHorizontalStep = (from, to) => {
|
|
1963
|
+
const intermediary = findHorizontalIntermediary(from, to);
|
|
1964
|
+
const intermediaryCategories = intermediary.map((unit) => unit.properties.category);
|
|
1965
|
+
const intermediaryCategory = intermediaryCategories.length > 0 ? intermediaryCategories[0] : "unspecified";
|
|
1966
|
+
const toward = getNextStepIntermediary(from, to, intermediary);
|
|
1967
|
+
const landmark = to.hint?.kind === "landmark" ? to.hint.name : null;
|
|
1968
|
+
const path = stepPathUtils.findStepPath(from.point, to.point, intermediary);
|
|
1969
|
+
const step = {
|
|
1970
|
+
from,
|
|
1971
|
+
to,
|
|
1972
|
+
levelIds: [from.levelId],
|
|
1973
|
+
ordinals: [from.ordinal],
|
|
1974
|
+
intermediaryCategory,
|
|
1975
|
+
description: describeHorizontalStep(intermediaryCategory, toward, landmark),
|
|
1976
|
+
path: path.map((coord) => [...coord, from.ordinal * 9])
|
|
1977
|
+
};
|
|
1978
|
+
return step;
|
|
1979
|
+
};
|
|
1980
|
+
const createVerticalStep = (from, to) => {
|
|
1981
|
+
const intermediary = findVerticalIntermediary(from, to);
|
|
1982
|
+
const intermediaryCategories = intermediary.map((unit) => unit.properties.category);
|
|
1983
|
+
const intermediaryCategory = intermediaryCategories.length > 0 ? intermediaryCategories[0] : "unspecified";
|
|
1984
|
+
const fromLevel = findByIdSync(from.levelId);
|
|
1985
|
+
const toLevel = findByIdSync(to.levelId);
|
|
1986
|
+
return {
|
|
1987
|
+
from,
|
|
1988
|
+
to,
|
|
1989
|
+
levelIds: [from.levelId, to.levelId],
|
|
1990
|
+
ordinals: [from.ordinal, to.ordinal],
|
|
1991
|
+
intermediaryCategory,
|
|
1992
|
+
description: describeVerticalStep(fromLevel, toLevel, intermediaryCategory),
|
|
1993
|
+
path: [
|
|
1994
|
+
[...from.point, from.ordinal * 9],
|
|
1995
|
+
[...to.point, to.ordinal * 9]
|
|
1996
|
+
]
|
|
1997
|
+
};
|
|
1998
|
+
};
|
|
1999
|
+
const isVertical = (from, to) => {
|
|
2000
|
+
const fromLevel = findByIdSync(from.levelId);
|
|
2001
|
+
const toLevel = findByIdSync(to.levelId);
|
|
2002
|
+
return !!fromLevel && !!toLevel && fromLevel.properties.ordinal !== toLevel.properties.ordinal;
|
|
2003
|
+
};
|
|
2004
|
+
const toSteps = (waypoints) => {
|
|
2005
|
+
let steps = [];
|
|
2006
|
+
const t0_allSteps = performance.now();
|
|
2007
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
2008
|
+
const from = waypoints[i];
|
|
2009
|
+
const to = waypoints[i + 1];
|
|
2010
|
+
const t0 = performance.now();
|
|
2011
|
+
const step = isVertical(from, to) ? createVerticalStep(from, to) : createHorizontalStep(from, to);
|
|
2012
|
+
steps.push(step);
|
|
2013
|
+
const t1 = performance.now();
|
|
2014
|
+
trace("nav", ` \u2502 \u251C\u2500 #${i} ${from.id.padEnd(25)} \u2192 ${`(${step.intermediaryCategory})`.padEnd(12)} \u2192 ${to.id}`, t1 - t0);
|
|
2015
|
+
trace("nav", ` \u2502 \u2502 ${step.description.text}`, void 0, "#bada55");
|
|
2016
|
+
}
|
|
2017
|
+
const simplifySteps = combineWalkwaySteps(steps);
|
|
2018
|
+
const t1_allSteps = performance.now();
|
|
2019
|
+
trace("nav", " \u2502 \u2514\u2500 Total ", t1_allSteps - t0_allSteps);
|
|
2020
|
+
return simplifySteps;
|
|
2021
|
+
};
|
|
2022
|
+
return {
|
|
2023
|
+
toSteps
|
|
2024
|
+
};
|
|
2025
|
+
};
|
|
2026
|
+
|
|
2027
|
+
// src/data/navigate/utils/timeDistance.ts
|
|
2028
|
+
import calculateLength from "@turf/length";
|
|
2029
|
+
var WALKING_SPEED = 1.4;
|
|
2030
|
+
var calculatePathLength = (feature2) => calculateLength(feature2, { units: "kilometers" }) * 1e3;
|
|
2031
|
+
var calculateTravelingDuration = (distance5) => {
|
|
2032
|
+
const duration = distance5 / WALKING_SPEED;
|
|
2033
|
+
const minutes = Math.round(duration / 60);
|
|
2034
|
+
return minutes > 0 ? minutes : 1;
|
|
2035
|
+
};
|
|
2036
|
+
var calculateTotalDistance = (steps = []) => {
|
|
2037
|
+
return steps.reduce((acc, { path }) => acc + calculatePathLength(lineString(path)), 0);
|
|
2038
|
+
};
|
|
2039
|
+
var calculateRoundedDistance = (distance5) => {
|
|
2040
|
+
return Math.round(distance5 - distance5 % 25);
|
|
2041
|
+
};
|
|
2042
|
+
|
|
2043
|
+
// src/data/navigate/type-guard.ts
|
|
2044
|
+
function isCoordinateOrdinalString(id) {
|
|
2045
|
+
return /^-?\d+(\.\d+)?,-?\d+(\.\d+)?,-?\d+(\.\d+)?o$/.test(id);
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// src/data/navigate/utils/createFindByIdSync.ts
|
|
2049
|
+
var createFindByIdSync = (data) => {
|
|
2050
|
+
const { amenities = [], anchors = [], fixtures = [], levels = [], kiosks = [], relationships = [], occupants = [], openings = [], units } = data;
|
|
2051
|
+
const featureById = /* @__PURE__ */ new Map();
|
|
2052
|
+
const entries = [
|
|
2053
|
+
...amenities,
|
|
2054
|
+
...anchors,
|
|
2055
|
+
...fixtures,
|
|
2056
|
+
...levels,
|
|
2057
|
+
...kiosks,
|
|
2058
|
+
...relationships,
|
|
2059
|
+
...occupants,
|
|
2060
|
+
...openings,
|
|
2061
|
+
...units
|
|
2062
|
+
];
|
|
2063
|
+
for (const f of entries) featureById.set(f.id, f);
|
|
2064
|
+
const findByIdSync = (id) => {
|
|
2065
|
+
return featureById.get(id);
|
|
2066
|
+
};
|
|
2067
|
+
return { findByIdSync };
|
|
2068
|
+
};
|
|
2069
|
+
|
|
2070
|
+
// src/data/navigate/waypoint/createWaypointUtils.ts
|
|
2071
|
+
import { center as center6 } from "@turf/center";
|
|
2072
|
+
|
|
2073
|
+
// src/data/navigate/waypoint/extractEndWaypoint.ts
|
|
2074
|
+
import { center as center3 } from "@turf/center";
|
|
2075
|
+
|
|
2076
|
+
// src/data/navigate/waypoint/featureIdGuard.ts
|
|
2077
|
+
var isOccupant = (id) => !!id && id.startsWith("occupant-");
|
|
2078
|
+
var isUnit = (id) => !!id && id.startsWith("unit-");
|
|
2079
|
+
var isKiosk = (id) => !!id && id.startsWith("kiosk-");
|
|
2080
|
+
var isOpening = (id) => !!id && id.startsWith("opening-");
|
|
2081
|
+
|
|
2082
|
+
// src/data/navigate/waypoint/extractEndWaypoint.ts
|
|
2083
|
+
var extractEndPoint = (path, options) => {
|
|
2084
|
+
const { findByIdSync } = options;
|
|
2085
|
+
const [c, b, a] = path.slice(-3);
|
|
2086
|
+
if (isOccupant(a) && isUnit(b) && isOpening(c)) {
|
|
2087
|
+
const occ = findByIdSync(a);
|
|
2088
|
+
const opening = findByIdSync(c);
|
|
2089
|
+
const level = findByIdSync(opening.properties.level_id);
|
|
2090
|
+
return [
|
|
2091
|
+
{
|
|
2092
|
+
id: occ.id,
|
|
2093
|
+
type: "end",
|
|
2094
|
+
name: occ.properties.name,
|
|
2095
|
+
point: center3(opening).geometry.coordinates,
|
|
2096
|
+
levelId: opening.properties.level_id,
|
|
2097
|
+
ordinal: level.properties.ordinal,
|
|
2098
|
+
source: { type: "opening", id: opening.id }
|
|
2099
|
+
},
|
|
2100
|
+
path.slice(0, -3)
|
|
2101
|
+
];
|
|
2102
|
+
}
|
|
2103
|
+
if (isOccupant(a) && isKiosk(b)) {
|
|
2104
|
+
const occ = findByIdSync(a);
|
|
2105
|
+
const kiosk = findByIdSync(b);
|
|
2106
|
+
const level = findByIdSync(kiosk.properties.level_id);
|
|
2107
|
+
return [
|
|
2108
|
+
{
|
|
2109
|
+
id: occ.id,
|
|
2110
|
+
type: "end",
|
|
2111
|
+
name: occ.properties.name,
|
|
2112
|
+
point: center3(kiosk).geometry.coordinates,
|
|
2113
|
+
levelId: kiosk.properties.level_id,
|
|
2114
|
+
ordinal: level.properties.ordinal,
|
|
2115
|
+
source: { type: "kiosk", id: kiosk.id }
|
|
2116
|
+
},
|
|
2117
|
+
path.slice(0, -2)
|
|
2118
|
+
];
|
|
2119
|
+
}
|
|
2120
|
+
if (isCoordinateOrdinalString(a) && isOpening(b)) {
|
|
2121
|
+
const [lat, lng, ordinal] = parseOrdinalCoordinate(a);
|
|
2122
|
+
const opening = findByIdSync(b);
|
|
2123
|
+
return [
|
|
2124
|
+
{
|
|
2125
|
+
id: a,
|
|
2126
|
+
type: "end",
|
|
2127
|
+
name: { en: a },
|
|
2128
|
+
point: [lng, lat],
|
|
2129
|
+
levelId: opening.properties.level_id,
|
|
2130
|
+
ordinal,
|
|
2131
|
+
source: { type: "coordinate", raw: a }
|
|
2132
|
+
},
|
|
2133
|
+
path.slice(0, -1)
|
|
2134
|
+
];
|
|
2135
|
+
}
|
|
2136
|
+
return [null, path];
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
// src/data/navigate/waypoint/extractStartWaypoint.ts
|
|
2140
|
+
import { center as center4 } from "@turf/center";
|
|
2141
|
+
var extractStartPoint = (path, options) => {
|
|
2142
|
+
const { findByIdSync } = options;
|
|
2143
|
+
const [a, b, c] = path;
|
|
2144
|
+
if (isOccupant(a) && isUnit(b) && isOpening(c)) {
|
|
2145
|
+
const occ = findByIdSync(a);
|
|
2146
|
+
const opening = findByIdSync(c);
|
|
2147
|
+
const level = findByIdSync(opening.properties.level_id);
|
|
2148
|
+
return [
|
|
2149
|
+
{
|
|
2150
|
+
id: occ.id,
|
|
2151
|
+
type: "start",
|
|
2152
|
+
name: occ.properties.name,
|
|
2153
|
+
point: center4(opening).geometry.coordinates,
|
|
2154
|
+
levelId: opening.properties.level_id,
|
|
2155
|
+
ordinal: level.properties.ordinal,
|
|
2156
|
+
source: { type: "opening", id: opening.id }
|
|
2157
|
+
},
|
|
2158
|
+
path.slice(3)
|
|
2159
|
+
];
|
|
2160
|
+
}
|
|
2161
|
+
if (isOccupant(a) && isKiosk(b)) {
|
|
2162
|
+
const occ = findByIdSync(a);
|
|
2163
|
+
const kiosk = findByIdSync(b);
|
|
2164
|
+
const level = findByIdSync(kiosk.properties.level_id);
|
|
2165
|
+
return [
|
|
2166
|
+
{
|
|
2167
|
+
id: occ.id,
|
|
2168
|
+
type: "start",
|
|
2169
|
+
name: occ.properties.name,
|
|
2170
|
+
point: center4(kiosk).geometry.coordinates,
|
|
2171
|
+
levelId: kiosk.properties.level_id,
|
|
2172
|
+
ordinal: level.properties.ordinal,
|
|
2173
|
+
source: { type: "kiosk", id: kiosk.id }
|
|
2174
|
+
},
|
|
2175
|
+
path.slice(2)
|
|
2176
|
+
];
|
|
2177
|
+
}
|
|
2178
|
+
if (isCoordinateOrdinalString(a) && isOpening(b)) {
|
|
2179
|
+
const [lat, lng, ordinal] = parseOrdinalCoordinate(a);
|
|
2180
|
+
const opening = findByIdSync(b);
|
|
2181
|
+
return [
|
|
2182
|
+
{
|
|
2183
|
+
id: a,
|
|
2184
|
+
type: "start",
|
|
2185
|
+
name: { en: a },
|
|
2186
|
+
point: [lng, lat],
|
|
2187
|
+
levelId: opening.properties.level_id,
|
|
2188
|
+
ordinal,
|
|
2189
|
+
source: { type: "coordinate", raw: a }
|
|
2190
|
+
},
|
|
2191
|
+
path.slice(1)
|
|
2192
|
+
];
|
|
2193
|
+
}
|
|
2194
|
+
return [null, path];
|
|
2195
|
+
};
|
|
2196
|
+
|
|
2197
|
+
// src/data/navigate/landmark/createLandmarkUtils.ts
|
|
2198
|
+
import { center as center5 } from "@turf/center";
|
|
2199
|
+
import distance4 from "@turf/distance";
|
|
2200
|
+
var NEARBY_DISTANCE = 30;
|
|
2201
|
+
var createLandmarkUtils = (options) => {
|
|
2202
|
+
const { data, findByIdSync } = options;
|
|
2203
|
+
const { occupants = [] } = data;
|
|
2204
|
+
const occupantToLandmark = (occupant) => {
|
|
2205
|
+
const locationType = occupant.properties.unit_id ? "unit" : "kiosk";
|
|
2206
|
+
const locationId = locationType === "unit" ? occupant.properties.unit_id : occupant.properties.kiosk_id;
|
|
2207
|
+
const location = locationType === "unit" ? findByIdSync(locationId) : findByIdSync(locationId);
|
|
2208
|
+
const level = findByIdSync(location.properties.level_id);
|
|
2209
|
+
return {
|
|
2210
|
+
name: occupant.properties.name,
|
|
2211
|
+
point: center5(location.geometry).geometry.coordinates,
|
|
2212
|
+
level_id: location.properties.level_id,
|
|
2213
|
+
is_priority: occupant.properties.is_landmark,
|
|
2214
|
+
ordinal: level.properties.ordinal
|
|
2215
|
+
};
|
|
2216
|
+
};
|
|
2217
|
+
const landmarks = [
|
|
2218
|
+
...occupants.map(occupantToLandmark)
|
|
2219
|
+
];
|
|
2220
|
+
const findNearbyLandmarks = (point2, levelId) => {
|
|
2221
|
+
if (point2 === null || levelId === null) return [];
|
|
2222
|
+
return landmarks.map((landmark) => {
|
|
2223
|
+
const landmarkAndDistance = {
|
|
2224
|
+
landmark,
|
|
2225
|
+
d: distance4(point2, landmark.point, { units: "meters" })
|
|
2226
|
+
};
|
|
2227
|
+
return landmarkAndDistance;
|
|
2228
|
+
}).filter(({ landmark, d }) => d <= NEARBY_DISTANCE && landmark.level_id === levelId).sort((a, b) => {
|
|
2229
|
+
const aPriority = a.landmark.is_priority ? 0 : 1;
|
|
2230
|
+
const bPriority = b.landmark.is_priority ? 0 : 1;
|
|
2231
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
2232
|
+
return a.d - b.d;
|
|
2233
|
+
}).map(({ landmark }) => landmark);
|
|
2234
|
+
};
|
|
2235
|
+
const findNearestLandmark = (point2, levelId) => {
|
|
2236
|
+
const nearbyLandmarks = findNearbyLandmarks(point2, levelId);
|
|
2237
|
+
const nearestLandmark = nearbyLandmarks.length > 0 ? nearbyLandmarks[0] : null;
|
|
2238
|
+
return nearestLandmark;
|
|
2239
|
+
};
|
|
2240
|
+
return { findNearbyLandmarks, findNearestLandmark };
|
|
2241
|
+
};
|
|
2242
|
+
|
|
2243
|
+
// src/data/navigate/waypoint/createWaypointUtils.ts
|
|
2244
|
+
var createWaypointUtils = (options) => {
|
|
2245
|
+
const { findByIdSync } = options;
|
|
2246
|
+
const landmarkUtils = createLandmarkUtils(options);
|
|
2247
|
+
const toWaypoints = (path) => {
|
|
2248
|
+
const [startPoint, middleAndEndPoints] = extractStartPoint(path, options);
|
|
2249
|
+
const [endPoint, middlePoints] = extractEndPoint(middleAndEndPoints, options);
|
|
2250
|
+
const waypoints = middlePoints.map((openingId) => {
|
|
2251
|
+
const opening = findByIdSync(openingId);
|
|
2252
|
+
const level = findByIdSync(opening.properties.level_id);
|
|
2253
|
+
const coordinates = center6(opening).geometry.coordinates;
|
|
2254
|
+
const landmark = landmarkUtils.findNearestLandmark(coordinates, opening.properties.level_id);
|
|
2255
|
+
return {
|
|
2256
|
+
id: `${opening.properties.level_id}:${openingId}`,
|
|
2257
|
+
type: "between",
|
|
2258
|
+
point: coordinates,
|
|
2259
|
+
name: null,
|
|
2260
|
+
levelId: opening.properties.level_id,
|
|
2261
|
+
ordinal: level.properties.ordinal,
|
|
2262
|
+
hint: landmark ? { kind: "landmark", name: landmark.name } : void 0,
|
|
2263
|
+
source: { type: "opening", id: opening.id }
|
|
2264
|
+
};
|
|
2265
|
+
});
|
|
2266
|
+
return [startPoint, ...waypoints, endPoint];
|
|
2267
|
+
};
|
|
2268
|
+
return { toWaypoints };
|
|
2269
|
+
};
|
|
2270
|
+
|
|
2271
|
+
// src/data/navigate/getNavigateClient.ts
|
|
2272
|
+
var getNavigateClient = (options) => {
|
|
2273
|
+
const { data } = options;
|
|
2274
|
+
const { levels, units } = data;
|
|
2275
|
+
trace("nav", "\u2713 prepare");
|
|
2276
|
+
const t0 = performance.now();
|
|
2277
|
+
trace("nav", " \u251C\u2500 createGraph (dijkstra)");
|
|
2278
|
+
const { defaultGraph, accessibleGraph, addCoordinateOrdinalNode } = prepareGraph({ data });
|
|
2279
|
+
const t1 = performance.now();
|
|
2280
|
+
trace("nav", " \u2502 \u2514\u2500 Total ", t1 - t0);
|
|
2281
|
+
const t2 = performance.now();
|
|
2282
|
+
const { findByIdSync } = createFindByIdSync(data);
|
|
2283
|
+
const t3 = performance.now();
|
|
2284
|
+
trace("nav", " \u2514\u2500 findByIdSync", t3 - t2);
|
|
2285
|
+
const findCoordinateOrdinalUnit = (params) => {
|
|
2286
|
+
const [lat, lng, ordinal] = parseOrdinalCoordinate(params);
|
|
2287
|
+
const levelIdsWithOrdinal = levels.filter((level) => level.properties.ordinal === ordinal).map((level) => level.id);
|
|
2288
|
+
const unit = units.find((unit2) => levelIdsWithOrdinal.includes(unit2.properties.level_id) && booleanPointInPolygon4([lat, lng], unit2));
|
|
2289
|
+
return unit;
|
|
2290
|
+
};
|
|
2291
|
+
const stepUtils = createStepUtils({ ...options, findByIdSync });
|
|
2292
|
+
const waypointUtils = createWaypointUtils({ ...options, findByIdSync });
|
|
2293
|
+
const findRoute = async (routeOriginParam, routeDestinationParam, options2) => {
|
|
2294
|
+
if (!routeOriginParam || !routeDestinationParam) return null;
|
|
2295
|
+
const graph = options2?.mode === "accessible" ? accessibleGraph : defaultGraph;
|
|
2296
|
+
if (isCoordinateOrdinalString(routeOriginParam)) {
|
|
2297
|
+
const walkwayUnit = findCoordinateOrdinalUnit(routeOriginParam);
|
|
2298
|
+
addCoordinateOrdinalNode(routeOriginParam, walkwayUnit);
|
|
2299
|
+
}
|
|
2300
|
+
if (isCoordinateOrdinalString(routeDestinationParam)) {
|
|
2301
|
+
const walkwayUnit = findCoordinateOrdinalUnit(routeDestinationParam);
|
|
2302
|
+
addCoordinateOrdinalNode(routeDestinationParam, walkwayUnit);
|
|
2303
|
+
}
|
|
2304
|
+
try {
|
|
2305
|
+
trace("nav", "\u2713 findRoute", 0);
|
|
2306
|
+
const t02 = performance.now();
|
|
2307
|
+
const path = graph.path(routeOriginParam, routeDestinationParam);
|
|
2308
|
+
const t12 = performance.now();
|
|
2309
|
+
trace("nav", " \u251C\u2500 path (dijkstra)", t12 - t02);
|
|
2310
|
+
const waypoints = waypointUtils.toWaypoints(path);
|
|
2311
|
+
const t22 = performance.now();
|
|
2312
|
+
trace("nav", " \u251C\u2500 toWaypoints", t22 - t12);
|
|
2313
|
+
trace("nav", " \u251C\u2500 toSteps", 0);
|
|
2314
|
+
const steps = stepUtils.toSteps(waypoints);
|
|
2315
|
+
const t32 = performance.now();
|
|
2316
|
+
const totalDistance = calculateTotalDistance(steps);
|
|
2317
|
+
const roundedDistance = calculateRoundedDistance(totalDistance);
|
|
2318
|
+
const duration = calculateTravelingDuration(totalDistance);
|
|
2319
|
+
const t4 = performance.now();
|
|
2320
|
+
trace("nav", " \u2514\u2500 postProcess", t4 - t32);
|
|
2321
|
+
return {
|
|
2322
|
+
distance: roundedDistance,
|
|
2323
|
+
duration,
|
|
2324
|
+
steps
|
|
2325
|
+
};
|
|
2326
|
+
} catch (error) {
|
|
2327
|
+
console.log(error);
|
|
2328
|
+
throw error;
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
return {
|
|
2332
|
+
findRoute,
|
|
2333
|
+
findByIdSync
|
|
2334
|
+
};
|
|
2335
|
+
};
|
|
2336
|
+
|
|
2337
|
+
// src/data/getDataClient.ts
|
|
2338
|
+
var getDataClient = (options) => {
|
|
2339
|
+
let searchClient;
|
|
2340
|
+
let navigateClient;
|
|
2341
|
+
const observers = /* @__PURE__ */ new Map();
|
|
2342
|
+
const queryClient = options.queryClient ?? new QueryClient();
|
|
2343
|
+
const { mode = "delivery", projectId, apiKey, baseUrl, previewToken } = options;
|
|
2344
|
+
if (!projectId)
|
|
2345
|
+
throw new Error(
|
|
2346
|
+
"Cannot create VenueDataClient. Reason: `projectId` is missing"
|
|
2347
|
+
);
|
|
2348
|
+
if (mode === "delivery" && !apiKey)
|
|
2349
|
+
throw new Error(
|
|
2350
|
+
"Cannot create VenueDataClient. Reason: `apiKey` is missing"
|
|
2351
|
+
);
|
|
2352
|
+
if (mode === "preview" && !previewToken)
|
|
2353
|
+
throw new Error(
|
|
2354
|
+
"Cannot create VenueDataClient. Reason: `previewToken` is missing"
|
|
2355
|
+
);
|
|
2356
|
+
const createDeliveryApiQueryOptions = (featureType) => ({
|
|
2357
|
+
queryKey: ["_deliveryapi", featureType],
|
|
2358
|
+
queryFn: () => safeFetchFeature(featureType, { mode, projectId, apiKey, previewToken, baseUrl })
|
|
2359
|
+
});
|
|
2360
|
+
const internalFilterByType = async (featureType) => {
|
|
2361
|
+
try {
|
|
2362
|
+
const features = await queryClient.ensureQueryData(createDeliveryApiQueryOptions(featureType));
|
|
2363
|
+
return features;
|
|
2364
|
+
} catch (error) {
|
|
2365
|
+
throw error;
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
const internalFindById = async (id) => {
|
|
2369
|
+
if (id === null || id === void 0) return null;
|
|
2370
|
+
const featureType = id.slice(0, id.lastIndexOf("-"));
|
|
2371
|
+
const feature2 = await queryClient.ensureQueryData({
|
|
2372
|
+
queryKey: ["_deliveryapi", featureType, id],
|
|
2373
|
+
queryFn: async () => {
|
|
2374
|
+
const features = await internalFilterByType(featureType);
|
|
2375
|
+
const feature3 = features.find(
|
|
2376
|
+
(f) => f.id === id
|
|
2377
|
+
);
|
|
2378
|
+
return feature3 ?? null;
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
return feature2;
|
|
2382
|
+
};
|
|
2383
|
+
const populator = createPopulator({ internalFindById, internalFilterByType });
|
|
2384
|
+
const registerObserver = (featureType, refetchInterval) => {
|
|
2385
|
+
if (observers.has(featureType)) {
|
|
2386
|
+
console.warn(`Observer for ${featureType} already exists`);
|
|
2387
|
+
const record = observers.get(featureType);
|
|
2388
|
+
return record.observer;
|
|
2389
|
+
}
|
|
2390
|
+
const options2 = createDeliveryApiQueryOptions(featureType);
|
|
2391
|
+
const observer = new QueryObserver(queryClient, {
|
|
2392
|
+
...options2,
|
|
2393
|
+
refetchInterval
|
|
2394
|
+
});
|
|
2395
|
+
const unsubscribe = observer.subscribe(() => {
|
|
2396
|
+
console.log(`[venue-js] Listening to ${featureType} changes (interval = ${refetchInterval}ms)`);
|
|
2397
|
+
});
|
|
2398
|
+
observers.set(featureType, { observer, unsubscribe });
|
|
2399
|
+
return observer;
|
|
2400
|
+
};
|
|
2401
|
+
const destroyObserver = (featureType) => {
|
|
2402
|
+
const record = observers.get(featureType);
|
|
2403
|
+
if (!record) return;
|
|
2404
|
+
record.unsubscribe();
|
|
2405
|
+
observers.delete(featureType);
|
|
2406
|
+
};
|
|
2407
|
+
const destroyObservers = () => {
|
|
2408
|
+
observers.forEach(({ observer, unsubscribe }) => {
|
|
2409
|
+
unsubscribe();
|
|
2410
|
+
observer.destroy();
|
|
2411
|
+
});
|
|
2412
|
+
observers.clear();
|
|
2413
|
+
};
|
|
2414
|
+
const createFilterByTypeQueryOptions = (featureType, params = {}, options2 = {}) => ({
|
|
2415
|
+
queryKey: [featureType, "list", params],
|
|
2416
|
+
queryFn: async () => {
|
|
2417
|
+
const features = await internalFilterByType(featureType);
|
|
2418
|
+
const filters = params.filters ?? {};
|
|
2419
|
+
let result = features;
|
|
2420
|
+
if (params.filters) {
|
|
2421
|
+
result = features.filter((f) => matchFilters(f, filters));
|
|
2422
|
+
}
|
|
2423
|
+
return params.populate === true ? await Promise.all(result.map((f) => populator[featureType](f))) : result;
|
|
2424
|
+
},
|
|
2425
|
+
...options2 ?? {}
|
|
2426
|
+
});
|
|
2427
|
+
const createFindByIdQueryOptions = (featureType, id, params = {}, options2 = {}) => ({
|
|
2428
|
+
queryKey: [featureType, "detail", id, params],
|
|
2429
|
+
queryFn: async () => {
|
|
2430
|
+
const feature2 = await internalFindById(id);
|
|
2431
|
+
return params.populate === true ? await populator[featureType](feature2) : Promise.resolve(feature2);
|
|
2432
|
+
},
|
|
2433
|
+
...options2 ?? {}
|
|
2434
|
+
});
|
|
2435
|
+
async function filterByType(featureType, params) {
|
|
2436
|
+
const filterQueryOptions = createFilterByTypeQueryOptions(
|
|
2437
|
+
featureType,
|
|
2438
|
+
params
|
|
2439
|
+
);
|
|
2440
|
+
const features = await queryClient.ensureQueryData(filterQueryOptions);
|
|
2441
|
+
return params?.populate === true ? features : features;
|
|
2442
|
+
}
|
|
2443
|
+
async function findById(featureType, id, params) {
|
|
2444
|
+
const findQueryOptions = createFindByIdQueryOptions(
|
|
2445
|
+
featureType,
|
|
2446
|
+
id,
|
|
2447
|
+
params
|
|
2448
|
+
);
|
|
2449
|
+
const feature2 = await queryClient.ensureQueryData(findQueryOptions);
|
|
2450
|
+
return feature2;
|
|
2451
|
+
}
|
|
2452
|
+
const searchFn = async (txt) => {
|
|
2453
|
+
if (!searchClient) {
|
|
2454
|
+
const [occupants, amenities] = await Promise.all([
|
|
2455
|
+
filterByType("occupant"),
|
|
2456
|
+
filterByType("amenity")
|
|
2457
|
+
]);
|
|
2458
|
+
const haystack = { occupants, amenities };
|
|
2459
|
+
searchClient = getSearchClient(haystack);
|
|
2460
|
+
}
|
|
2461
|
+
return searchClient.search(txt);
|
|
2462
|
+
};
|
|
2463
|
+
const navigateFn = async (origin, destination) => {
|
|
2464
|
+
if (!navigateClient) {
|
|
2465
|
+
const [levels, occupants, openings, relationships, units, fixtures, kiosks, amenities, anchors] = await Promise.all([
|
|
2466
|
+
filterByType("level"),
|
|
2467
|
+
filterByType("occupant"),
|
|
2468
|
+
filterByType("opening"),
|
|
2469
|
+
filterByType("relationship"),
|
|
2470
|
+
filterByType("unit"),
|
|
2471
|
+
filterByType("fixture"),
|
|
2472
|
+
filterByType("kiosk"),
|
|
2473
|
+
filterByType("amenity"),
|
|
2474
|
+
filterByType("anchor")
|
|
2475
|
+
]);
|
|
2476
|
+
const haystack = { levels, occupants, openings, relationships, units, fixtures, kiosks, amenities, anchors };
|
|
2477
|
+
navigateClient = getNavigateClient({ data: haystack });
|
|
2478
|
+
}
|
|
2479
|
+
return navigateClient.findRoute(origin, destination);
|
|
2480
|
+
};
|
|
2481
|
+
return {
|
|
2482
|
+
projectId,
|
|
2483
|
+
queryClient,
|
|
2484
|
+
registerObserver,
|
|
2485
|
+
destroyObserver,
|
|
2486
|
+
destroyObservers,
|
|
2487
|
+
createFilterByTypeQueryOptions,
|
|
2488
|
+
createFindByIdQueryOptions,
|
|
2489
|
+
_internalFindById: internalFindById,
|
|
2490
|
+
filterByType,
|
|
2491
|
+
findById,
|
|
2492
|
+
search: searchFn,
|
|
2493
|
+
navigate: navigateFn
|
|
2494
|
+
};
|
|
2495
|
+
};
|
|
2496
|
+
export {
|
|
2497
|
+
ALL_FEATURE_TYPES,
|
|
2498
|
+
DEFAULT_BASE_URL,
|
|
2499
|
+
GEOJSON_FEATURE_TYPES,
|
|
2500
|
+
IMDF_FEATURE_TYPES,
|
|
2501
|
+
IMDF_UNIT_CATEGORIES,
|
|
2502
|
+
NONIMDF_FEATURE_TYPES,
|
|
2503
|
+
occupant_helper_exports as OccupantHelpers,
|
|
2504
|
+
QueryObserver2 as QueryObserver,
|
|
2505
|
+
defaultFeatureQueryOptionsMap,
|
|
2506
|
+
fetchDeliveryApi,
|
|
2507
|
+
fetchPreviewApi,
|
|
2508
|
+
getDataClient,
|
|
2509
|
+
getNavigateClient,
|
|
2510
|
+
getSearchClient,
|
|
2511
|
+
isValidCoordinate,
|
|
2512
|
+
isValidLineString,
|
|
2513
|
+
isValidLineStringCoordinates,
|
|
2514
|
+
isValidMultiPolygon,
|
|
2515
|
+
isValidMultiPolygonCoordinates,
|
|
2516
|
+
isValidPoint,
|
|
2517
|
+
isValidPolygon,
|
|
2518
|
+
isValidPolygonCoordinates,
|
|
2519
|
+
matchFilter,
|
|
2520
|
+
matchFilters,
|
|
2521
|
+
safeFetchFeature
|
|
2522
|
+
};
|
|
2523
|
+
//# sourceMappingURL=index.mjs.map
|