@wemap/routers 6.2.2 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/assets/biocbon-bergere-rdc-network.osm +163 -0
  2. package/assets/gare-de-lest-network-pp-bounds.osm +1615 -0
  3. package/dist/wemap-routers.es.js +1811 -695
  4. package/dist/wemap-routers.es.js.map +1 -1
  5. package/index.js +13 -5
  6. package/package.json +9 -6
  7. package/src/Constants.js +4 -2
  8. package/src/ItineraryInfoManager.spec.js +2 -2
  9. package/src/Utils.js +0 -77
  10. package/src/model/Itinerary.js +41 -5
  11. package/src/model/Itinerary.spec.js +91 -0
  12. package/src/model/Itinerary.type.spec.js +3 -78
  13. package/src/model/Leg.js +89 -19
  14. package/src/model/Leg.spec.js +110 -0
  15. package/src/model/Leg.type.spec.js +48 -0
  16. package/src/model/LevelChange.js +14 -24
  17. package/src/model/LevelChange.spec.js +78 -0
  18. package/src/model/LevelChange.type.spec.js +26 -0
  19. package/src/model/RouterResponse.js +70 -1
  20. package/src/model/RouterResponse.spec.js +85 -0
  21. package/src/model/RouterResponse.type.spec.js +7 -4
  22. package/src/model/Step.js +45 -6
  23. package/src/model/Step.spec.js +100 -0
  24. package/src/model/Step.type.spec.js +52 -0
  25. package/src/remote/RemoteRouter.js +31 -0
  26. package/src/remote/RemoteRouterManager.js +84 -0
  27. package/src/remote/RemoteRouterOptions.js +25 -0
  28. package/src/remote/RemoteRouterServerUnreachable.js +10 -0
  29. package/src/remote/RemoteRouterUtils.js +78 -0
  30. package/src/remote/RoutingModeCorrespondanceNotFound.js +18 -0
  31. package/src/remote/cityway/CitywayRemoteRouter.js +386 -0
  32. package/src/{cityway/CitywayUtils.spec.js → remote/cityway/CitywayRemoteRouter.spec.js} +19 -18
  33. package/src/remote/deutsche-bahn/DeutscheBahnRemoteRouter.js +143 -0
  34. package/src/{deutsche-bahn/DeutscheBahnRouterUtils.spec.js → remote/deutsche-bahn/DeutscheBahnRemoteRouter.spec.js} +7 -6
  35. package/src/remote/idfm/IdfmRemoteRouter.js +432 -0
  36. package/src/{idfm/IdfmUtils.spec.js → remote/idfm/IdfmRemoteRouter.spec.js} +7 -6
  37. package/src/remote/idfm/IdfmRemoteRouterTokenError.js +6 -0
  38. package/src/remote/osrm/OsrmRemoteRouter.js +331 -0
  39. package/src/{osrm/OsrmUtils.spec.js → remote/osrm/OsrmRemoteRouter.spec.js} +9 -15
  40. package/src/remote/otp/OtpRemoteRouter.js +222 -0
  41. package/src/{otp/OtpUtils.spec.js → remote/otp/OtpRemoteRouter.spec.js} +10 -9
  42. package/src/remote/wemap-meta/WemapMetaRemoteRouter.js +57 -0
  43. package/src/remote/wemap-meta/WemapMetaRemoteRouter.spec.js +22 -0
  44. package/src/remote/wemap-meta/WemapMetaRemoteRouterOptions.js +36 -0
  45. package/src/remote/wemap-meta/WemapMetaRemoteRouterPayload.js +44 -0
  46. package/src/wemap/WemapRouter.js +6 -0
  47. package/src/wemap/WemapRouterUtils.js +10 -4
  48. package/src/wemap/WemapStepsGeneration.js +36 -9
  49. package/src/wemap-meta/IOMap.js +191 -0
  50. package/src/wemap-meta/WemapMetaRouter.js +314 -0
  51. package/src/wemap-meta/WemapMetaRouter.spec.js +119 -0
  52. package/src/wemap-meta/WemapMetaRouterOptions.js +20 -0
  53. package/src/cityway/CitywayUtils.js +0 -252
  54. package/src/deutsche-bahn/DeutscheBahnRouterUtils.js +0 -91
  55. package/src/idfm/IdfmUtils.js +0 -247
  56. package/src/osrm/OsrmUtils.js +0 -269
  57. package/src/otp/OtpUtils.js +0 -150
@@ -0,0 +1,314 @@
1
+ /* eslint-disable complexity */
2
+ /* eslint-disable max-statements */
3
+
4
+ import { Coordinates } from '@wemap/geo';
5
+ import Logger from '@wemap/logger';
6
+
7
+ import IOMap from './IOMap.js';
8
+ import WemapMetaRouterOptions from './WemapMetaRouterOptions.js';
9
+ import RemoteRouterManager from '../remote/RemoteRouterManager.js';
10
+ import WemapMetaRemoteRouterOptions from '../remote/wemap-meta/WemapMetaRemoteRouterOptions.js';
11
+ import WemapRouterOptions from '../wemap/WemapRouterOptions.js';
12
+ import RouterResponse from '../model/RouterResponse.js';
13
+ import WemapRouter from '../wemap/WemapRouter.js';
14
+ import Itinerary from '../model/Itinerary.js';
15
+
16
+
17
+ class WemapMetaRouter {
18
+
19
+ /** @type {!(IOMap[])} */
20
+ maps = [];
21
+
22
+
23
+ /** @type {string} */
24
+ get rname() {
25
+ return 'wemap-meta';
26
+ }
27
+
28
+ /**
29
+ * @param {!IOMap} ioMap
30
+ */
31
+ addIOMap(ioMap) {
32
+ this.maps.push(ioMap);
33
+ }
34
+
35
+ /**
36
+ * @param {!IOMap} ioMap
37
+ */
38
+ removeIOMap(ioMap) {
39
+ this.maps = this.maps.filter(map => map !== ioMap);
40
+ }
41
+
42
+ /**
43
+ * @param {string} mode see Constants.ROUTING_MODE
44
+ * @param {Coordinates[]} waypoints
45
+ * @param {WemapMetaRouterOptions} options
46
+ * @returns {RouterResponse}
47
+ */
48
+ async getItineraries(mode, waypoints, options) {
49
+
50
+ /*
51
+ * Here, we try to get the shortest path using io maps networks and a remote router server.
52
+ */
53
+
54
+ /**
55
+ * ----- 1 -----
56
+ * Parse function params
57
+ * -------------
58
+ */
59
+
60
+ if (waypoints.length > 2) {
61
+ Logger.warn(`WemapMetaRouter uses only the first 2 waypoints (asked ${waypoints.length})`);
62
+ }
63
+ const start = waypoints[0];
64
+ const end = waypoints[1];
65
+
66
+ // Avoid cycles on remoteRouters
67
+ const remoteRouters = options.remoteRouters.filter(
68
+ ({ name }) => name !== WemapMetaRemoteRouterOptions.rname
69
+ );
70
+
71
+ /*
72
+ * ----- 2 -----
73
+ * Retrieve the IO maps to consider for this itinerary
74
+ * -------------
75
+ *
76
+ * By default, all maps in this.maps are considered
77
+ * If options.targetMaps is defined, only use this subset
78
+ */
79
+ let ioMapsToTest = this.maps;
80
+
81
+ const { targetMaps } = options;
82
+ if (targetMaps) {
83
+
84
+ ioMapsToTest = this.maps.filter(map => targetMaps.includes(map));
85
+
86
+ // Send a warning if one of the request ioMap (from targetMaps) is not present in this.maps
87
+ if (ioMapsToTest.length !== targetMaps.length) {
88
+ targetMaps.forEach(map => {
89
+ if (!ioMapsToTest.includes(map)) {
90
+ Logger.warn(`IOMap "${map.name}" not found in WemapMetaRouter`);
91
+ }
92
+ });
93
+ }
94
+ }
95
+
96
+ /*
97
+ * If there is no local map to test, use remote router directly.
98
+ * This should happen:
99
+ * 1 - this.maps is empty
100
+ * 2 - options.targetMaps is defined but empty
101
+ * 3 - intersection of this.maps and options.targetMaps is empty
102
+ */
103
+ if (!ioMapsToTest.length) {
104
+ return RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints);
105
+ }
106
+
107
+
108
+ /**
109
+ * ----- 3 -----
110
+ * Run the IO Maps - Remote Routers logic
111
+ * -------------
112
+ *
113
+ * For this purpose we have to consider 5 use cases
114
+ *
115
+ */
116
+
117
+ const routerResponse = new RouterResponse();
118
+ routerResponse.from = start;
119
+ routerResponse.to = end;
120
+
121
+
122
+ // Find the first map where the "start" is inside.
123
+ const mapWithStart = ioMapsToTest.find(map => map.isPointInside(start));
124
+
125
+ // Create WemapRouterOptions from WemapMetaRouterOptions
126
+ const wemapRouterOptions = options.useStairs
127
+ ? WemapRouterOptions.DEFAULT
128
+ : WemapRouterOptions.WITHOUT_STAIRS;
129
+
130
+ /*
131
+ * Case 1
132
+ *
133
+ * If "start" and "end" are in the same map, use the local router.
134
+ */
135
+ if (mapWithStart && mapWithStart.isPointInside(end)) {
136
+
137
+ const itinerary = mapWithStart.getItineraryInsideMap(start, end, wemapRouterOptions);
138
+
139
+ routerResponse.itineraries.push(itinerary);
140
+ routerResponse.routerName = [this.rname, WemapRouter.rname];
141
+
142
+ return routerResponse;
143
+ }
144
+
145
+ // Find the first map where the "end" is inside.
146
+ // Note: At this step, mapWithEnd is necessarily different from mapWithStart
147
+ const mapWithEnd = ioMapsToTest.find(map => map.isPointInside(end));
148
+
149
+ /*
150
+ * Case 2
151
+ *
152
+ * If no io map have been found for "start" and "end", therefore use remote router.
153
+ */
154
+ if (!mapWithStart && !mapWithEnd) {
155
+ return RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints);
156
+ }
157
+
158
+ /** @type {RouterResponse} */
159
+ let remoteRouterResponse;
160
+
161
+ /** @type {Itinerary} */
162
+ let ioMapItinerary;
163
+
164
+ /**
165
+ * Case 3
166
+ *
167
+ * If a map has been found for the "start" but not for the "end", so:
168
+ * - A first itinerary (firstRoute) is calculated from "start" to an "entrypoint"
169
+ * of the IO map network using the wemap router.
170
+ * - A second itinerary (secondRoute) is calculated from an "entrypoint" to the
171
+ * "end" using remote routers.
172
+ * Itinerary returned is the concatenation of the both itineraries.
173
+ *
174
+ * Note: Check the mapWithEnd.getBestItineraryFromEntryPointsToEnd to understand
175
+ * which "entrypoint" is chosen by the algorithm
176
+ */
177
+ if (mapWithStart && !mapWithEnd) {
178
+
179
+ if (!mapWithStart.entryPoints.length) {
180
+ routerResponse.error = `A map including the "start" but the "end" has been
181
+ found (${mapWithStart.name}), however, no "entrypoints" have been found to go out`;
182
+ return routerResponse;
183
+ }
184
+
185
+ ioMapItinerary = mapWithStart.getBestItineraryFromStartToEntryPoints(start, end, wemapRouterOptions);
186
+ remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
187
+ remoteRouters, mode, [ioMapItinerary.to, end]
188
+ );
189
+
190
+
191
+ if (!remoteRouterResponse.itineraries.length) {
192
+ routerResponse.error = `Tried to calculate an itinerary from "start"
193
+ to "entrypoints" using wemap router on local map "${mapWithStart.name}" and
194
+ an itinerary from "entrypoints" to "end" using remote routers
195
+ (${remoteRouters.map(r => r.name).join(', ')}), but failed.`;
196
+ return routerResponse;
197
+ }
198
+
199
+ // Concat the the IO map itinerary with the remote router response (for each itinerary)
200
+ routerResponse.itineraries = remoteRouterResponse.itineraries.map(
201
+ remoteRouterItinerary => Itinerary.fromItineraries(ioMapItinerary, remoteRouterItinerary)
202
+ );
203
+ routerResponse.routerName = [this.rname, remoteRouterResponse.routerName, WemapRouter.rname];
204
+ return routerResponse;
205
+ }
206
+
207
+ /*
208
+ * Case 4
209
+ *
210
+ * If a map has been found for the "end" but not for the "start", so:
211
+ * - A first itinerary (remoteRouterResponse) is calculated from "start" to an "entrypoint"
212
+ * of the IO map network using remote routers.
213
+ * - A second itinerary (ioMapItinerary) is calculated from an "entrypoint" to the
214
+ * "end" using the wemap router.
215
+ * Itinerary returned is the concatenation of the both itineraries.
216
+ *
217
+ * Note: Check the mapWithEnd.getBestItineraryFromEntryPointsToEnd to understand
218
+ * which "entrypoint" is chosen by the algorithm
219
+ */
220
+ if (!mapWithStart && mapWithEnd) {
221
+
222
+ if (!mapWithEnd.entryPoints.length) {
223
+ routerResponse.error = `A map including the "end" but the "start" has been
224
+ found (${mapWithEnd.name}), however, no "entrypoints" have been found to go in`;
225
+ return routerResponse;
226
+ }
227
+
228
+ /*
229
+ * ioMapItinerary is computed before the remoteRouterResponse because it is less expensive to
230
+ * calculate all the routes to entrypoints using local router than all the routes with the
231
+ * remote router.
232
+ */
233
+ ioMapItinerary = mapWithEnd.getBestItineraryFromEntryPointsToEnd(start, end, wemapRouterOptions);
234
+ remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
235
+ remoteRouters, mode, [start, ioMapItinerary.from]
236
+ );
237
+
238
+ if (!remoteRouterResponse.itineraries.length) {
239
+ routerResponse.error = `Tried to calculate an itinerary from "start" to "entrypoints"
240
+ using remote routers (${remoteRouters.map(r => r.name).join(', ')}) and an
241
+ itinerary from "entrypoints" to "end" using wemap router on local map
242
+ "${mapWithEnd.name}", but failed.`;
243
+ return routerResponse;
244
+ }
245
+
246
+ // Concat the remote router response (for each itinerary) with the IO map itinerary
247
+ routerResponse.itineraries = remoteRouterResponse.itineraries.map(
248
+ remoteRouterItinerary => Itinerary.fromItineraries(remoteRouterItinerary, ioMapItinerary)
249
+ );
250
+ routerResponse.routerName = [this.rname, remoteRouterResponse.routerName, WemapRouter.rname];
251
+ return routerResponse;
252
+ }
253
+
254
+ /**
255
+ * Case 5
256
+ *
257
+ * If maps have been found for the "start" and the "end" but they are different, so:
258
+ * - A first itinerary (ioMapItinerary1) is calculated from "start" to an "entrypoint" of
259
+ * the mapWithStart using the wemap router.
260
+ * - A second itinerary (remoteRouterResponse) is calculated from an "entrypoint" of the
261
+ * mapWithStart to an "entrypoint" of the endWithMap using remote routers.
262
+ * - A third itinerary (ioMapItinerary2) is calculated from an "entrypoint" of the mapWithEnd
263
+ * to the "end" using the wemap router.
264
+ * Itinerary returned is the concatenation of the three itineraries.
265
+ */
266
+ // if (startMap && endMap) {
267
+
268
+ if (!mapWithStart.entryPoints.length) {
269
+ routerResponse.error = `One map including the "start" (${mapWithStart.name}) and another
270
+ including the "end" (${mapWithEnd.name}) has been found, however, no "entrypoints" have
271
+ been found to go out of the start map`;
272
+ return routerResponse;
273
+ }
274
+
275
+ if (!mapWithEnd.entryPoints.length) {
276
+ routerResponse.error = `One map including the "start" (${mapWithStart.name}) and another
277
+ including the "end" (${mapWithEnd.name}) has been found, however, no "entrypoints" have
278
+ been found to go in the second map`;
279
+ return routerResponse;
280
+ }
281
+
282
+ const ioMapItinerary1 = mapWithStart.getBestItineraryFromStartToEntryPoints(start, end, wemapRouterOptions);
283
+ const ioMapItinerary2 = mapWithEnd.getBestItineraryFromEntryPointsToEnd(start, end, wemapRouterOptions);
284
+ remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
285
+ remoteRouters, mode, [ioMapItinerary1.to, ioMapItinerary2.from]
286
+ );
287
+
288
+ if (!remoteRouterResponse.itineraries.length) {
289
+ routerResponse.error = `Tried to calculate an itinerary from "start" to "entrypoints1"
290
+ using wemap router on local map "${mapWithStart.name}", an itinerary from
291
+ "entrypoints1" to "entrypoints2" using remote routers
292
+ (${remoteRouters.map(r => r.name).join(', ')}) and an itinerary from "entrypoints2"
293
+ to "end" using wemap router on local map "${mapWithEnd.name}", but failed.`;
294
+ return routerResponse;
295
+ }
296
+
297
+ // Concat the IO map itinerary 2 with the remote router response (for each itinerary)
298
+ // and the IO map itinerary 2
299
+ routerResponse.itineraries = remoteRouterResponse.itineraries.map(
300
+ remoteRouterItinerary => Itinerary.fromItineraries(
301
+ ioMapItinerary1,
302
+ remoteRouterItinerary,
303
+ ioMapItinerary2
304
+ )
305
+ );
306
+ routerResponse.routerName = [this.rname, remoteRouterResponse.routerName, WemapRouter.rname];
307
+ return routerResponse;
308
+
309
+ // }
310
+ }
311
+
312
+ }
313
+
314
+ export default WemapMetaRouter;
@@ -0,0 +1,119 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import fetch, { Headers } from 'node-fetch';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ import { Level, Coordinates } from '@wemap/geo';
9
+ import Logger from '@wemap/logger';
10
+
11
+ import IOMap from './IOMap.js';
12
+ import WemapMetaRouter from './WemapMetaRouter.js';
13
+ import Constants from '../Constants.js';
14
+ import WemapMetaRouterOptions from './WemapMetaRouterOptions.js';
15
+ import { OsrmRemoteRouter } from '../../index.js';
16
+ import checkRouterResponseType from '../model/RouterResponse.type.spec.js';
17
+
18
+ const { expect } = chai;
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+
21
+ // TODO: Remove with node 18
22
+ global.fetch = fetch;
23
+ global.Headers = Headers;
24
+
25
+ function createIOMapFromAsset(assetName) {
26
+ const filePath = path.resolve(__dirname, '../../assets/' + assetName);
27
+ const osmXmlString = fs.readFileSync(filePath, 'utf8');
28
+ const mapName = path.parse(assetName).name;
29
+ return IOMap.fromOsmXml(osmXmlString, mapName);
30
+ }
31
+
32
+ describe('WemapMetaRouter', () => {
33
+
34
+ const router = new WemapMetaRouter();
35
+
36
+ const biocbonMap = createIOMapFromAsset('biocbon-bergere-rdc-network.osm');
37
+ router.addIOMap(biocbonMap);
38
+ const garedelestMap = createIOMapFromAsset('gare-de-lest-network-pp-bounds.osm');
39
+ router.addIOMap(garedelestMap);
40
+
41
+ const mode = Constants.ROUTING_MODE.WALK;
42
+ const options = new WemapMetaRouterOptions();
43
+ options.targetMaps = [biocbonMap];
44
+ options.remoteRouters.push({
45
+ name: OsrmRemoteRouter.rname,
46
+ endpointUrl: 'https://routing.getwemap.com'
47
+ });
48
+
49
+ before(async () => {
50
+ Logger.ENABLED = false;
51
+ });
52
+
53
+ after(() => {
54
+ Logger.ENABLED = true;
55
+ });
56
+
57
+
58
+ it('indoor to indoor', async () => {
59
+ const start = new Coordinates(48.8725992, 2.343431);
60
+ const end = new Coordinates(48.8725694, 2.3433);
61
+
62
+ const routerResponse = await router.getItineraries(mode, [start, end], options);
63
+ checkRouterResponseType(routerResponse);
64
+
65
+ const osrmResponse = OsrmRemoteRouter.itineraryToOsrmJson(routerResponse.itineraries[0]);
66
+ expect(osrmResponse).is.not.null;
67
+ const routes = osrmResponse.routes;
68
+ expect(routes.length).equals(1);
69
+ expect(routes[0].geometry.coordinates.length).equals(6);
70
+ expect(routes[0].legs).not.empty;
71
+ });
72
+
73
+ it('outdoor to outdoor', async () => {
74
+ const start = new Coordinates(48.8726513, 2.343449);
75
+ const end = new Coordinates(48.8726397, 2.3431657);
76
+
77
+ const routerResponse = await router.getItineraries(mode, [start, end], options);
78
+ checkRouterResponseType(routerResponse);
79
+
80
+ const osrmResponse = OsrmRemoteRouter.itineraryToOsrmJson(routerResponse.itineraries[0]);
81
+ expect(osrmResponse.routes[0].geometry.coordinates.length).equals(4);
82
+ });
83
+
84
+ it('outdoor to indoor', async () => {
85
+ const start = new Coordinates(48.8726085, 2.3434289);
86
+ const end = new Coordinates(48.8725694, 2.3433);
87
+
88
+ const routerResponse = await router.getItineraries(mode, [start, end], options);
89
+ checkRouterResponseType(routerResponse);
90
+
91
+ const osrmResponse = OsrmRemoteRouter.itineraryToOsrmJson(routerResponse.itineraries[0]);
92
+ expect(osrmResponse.routes[0].geometry.coordinates.length).equals(6);
93
+ });
94
+
95
+ it('indoor to outdoor', async () => {
96
+ const start = new Coordinates(48.8725992, 2.343431);
97
+ const end = new Coordinates(48.8726513, 2.343449);
98
+
99
+ const routerResponse = await router.getItineraries(mode, [start, end], options);
100
+ checkRouterResponseType(routerResponse);
101
+
102
+ const osrmResponse = OsrmRemoteRouter.itineraryToOsrmJson(routerResponse.itineraries[0]);
103
+ expect(osrmResponse.routes[0].geometry.coordinates.length).equals(6);
104
+ });
105
+
106
+ it('indoor to outdoor to indoor', async () => {
107
+
108
+ options.targetMaps.push(garedelestMap);
109
+ const start = new Coordinates(48.8725992, 2.343431);
110
+ const end = new Coordinates(48.8772962, 2.3584458, null, new Level(0));
111
+
112
+ const routerResponse = await router.getItineraries(mode, [start, end], options);
113
+ checkRouterResponseType(routerResponse);
114
+
115
+ const osrmResponse = OsrmRemoteRouter.itineraryToOsrmJson(routerResponse.itineraries[0]);
116
+ expect(osrmResponse.routes[0].geometry.coordinates.length).equals(77);
117
+ });
118
+
119
+ });
@@ -0,0 +1,20 @@
1
+ import IOMap from './IOMap.js';
2
+
3
+ class WemapMetaRouterOptions {
4
+
5
+ /** @type {!boolean} */
6
+ useStairs = true;
7
+
8
+ /** @type {?(IOMap[])} */
9
+ targetMaps = null;
10
+
11
+ /**
12
+ * List of ordered remote routers (first of the list, first tested)
13
+ * Algorithm will try the first router and if it fails continue
14
+ * with the second, etc...
15
+ * @type {!({name: string, endpointUrl: string}[])}
16
+ */
17
+ remoteRouters = [];
18
+ }
19
+
20
+ export default WemapMetaRouterOptions;