@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,57 @@
1
+ /* eslint-disable max-statements */
2
+
3
+ import { Coordinates } from '@wemap/geo';
4
+
5
+ import RouterResponse from '../../model/RouterResponse.js';
6
+ import RemoteRouter from '../RemoteRouter.js';
7
+ import RemoteRouterServerUnreachable from '../RemoteRouterServerUnreachable.js';
8
+ import WemapMetaRemoteRouterOptions from './WemapMetaRemoteRouterOptions.js';
9
+ import WemapMetaRemoteRouterPayload from './WemapMetaRemoteRouterPayload.js';
10
+
11
+
12
+ /**
13
+ * Singleton.
14
+ */
15
+ class WemapMetaRemoteRouter extends RemoteRouter {
16
+
17
+ /**
18
+ * @override
19
+ */
20
+ get rname() {
21
+ return 'wemap-meta';
22
+ }
23
+
24
+ /**
25
+ * @param {!string} endpointUrl
26
+ * @param {!string} mode see Constants.ROUTING_MODE
27
+ * @param {Array<Coordinates>} waypoints
28
+ * @param {?WemapMetaRemoteRouterOptions} options
29
+ * @returns {!RouterResponse}
30
+ * @throws {RemoteRouterServerUnreachable}
31
+ */
32
+ async getItineraries(endpointUrl, mode, waypoints, options) {
33
+
34
+ const payload = new WemapMetaRemoteRouterPayload();
35
+ payload.waypoints = waypoints;
36
+ payload.mode = mode;
37
+ payload.options = options;
38
+
39
+ const res = await fetch(endpointUrl, {
40
+ method: 'POST',
41
+ headers: {
42
+ 'Accept': 'application/json',
43
+ 'Content-Type': 'application/json'
44
+ },
45
+ body: JSON.stringify(payload.toJson())
46
+ });
47
+
48
+ if (res.status !== 200) {
49
+ throw new RemoteRouterServerUnreachable(this.rname, endpointUrl);
50
+ }
51
+ const response = await res.json();
52
+ return RouterResponse.fromJson(response);
53
+ }
54
+
55
+ }
56
+
57
+ export default new WemapMetaRemoteRouter();
@@ -0,0 +1,22 @@
1
+ /* eslint-disable max-statements */
2
+ // import chai from 'chai';
3
+ // import fs from 'fs';
4
+ // import path from 'path';
5
+ // import { fileURLToPath } from 'url';
6
+
7
+ // import { Coordinates } from '@wemap/geo';
8
+
9
+ // import CitywayUtils from './CitywayUtils.js';
10
+
11
+ // import { verifyRouterResponseData } from '../model/RouterResponse.type.spec.js';
12
+
13
+
14
+ // const { expect } = chai;
15
+
16
+ // const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+
18
+ // describe('CitywayUtils - createRouterResponseFromJson', () => {
19
+
20
+
21
+ // });
22
+
@@ -0,0 +1,36 @@
1
+ import RemoteRouterOptions from '../RemoteRouterOptions.js';
2
+
3
+ class WemapMetaRemoteRouterOptions extends RemoteRouterOptions {
4
+
5
+ /** @type {!{name: string, endpointUrl: string}[]} */
6
+ remoteRouters = [];
7
+
8
+ /** @type {?(string[])} */
9
+ targetMaps = null;
10
+
11
+ /**
12
+ * @returns {object}
13
+ */
14
+ toJson() {
15
+ const json = super.toJson();
16
+ json.remoteRouters = this.remoteRouters;
17
+ if (this.targetMaps) {
18
+ json.targetMaps = this.targetMaps;
19
+ }
20
+ return json;
21
+ }
22
+
23
+ /**
24
+ * @param {object}
25
+ * @returns {WemapMetaRemoteRouterOptions}
26
+ */
27
+ static fromJson(json) {
28
+ const obj = new WemapMetaRemoteRouterOptions();
29
+ obj.useStairs = json.useStairs;
30
+ obj.remoteRouters = json.remoteRouters;
31
+ obj.targetMaps = json.targetMaps ? json.targetMaps : null;
32
+ return obj;
33
+ }
34
+ }
35
+
36
+ export default WemapMetaRemoteRouterOptions;
@@ -0,0 +1,44 @@
1
+ import { Coordinates } from '@wemap/geo';
2
+ import WemapMetaRemoteRouterOptions from './WemapMetaRemoteRouterOptions.js';
3
+
4
+ class WemapMetaRemoteRouterPayload {
5
+
6
+ /** @type {!(Coordinates[])} */
7
+ waypoints;
8
+
9
+ /** @type {!string} */
10
+ mode;
11
+
12
+ /** @type {!WemapMetaRemoteRouterOptions} */
13
+ options = null;
14
+
15
+ /**
16
+ * @returns {object}
17
+ */
18
+ toJson() {
19
+ const json = {
20
+ waypoints: this.waypoints.map(coords => coords.toCompressedJson()),
21
+ mode: this.mode
22
+ };
23
+ if (this.options) {
24
+ json.options = this.options.toJson();
25
+ }
26
+ return json;
27
+ }
28
+
29
+ /**
30
+ * @param {object}
31
+ * @returns {WemapMetaRemoteRouterPayload}
32
+ */
33
+ static fromJson(json) {
34
+ const obj = new WemapMetaRemoteRouterPayload();
35
+ obj.waypoints = json.waypoints.map(coords => Coordinates.fromCompressedJson(coords));
36
+ obj.mode = json.mode;
37
+ if (json.options) {
38
+ obj.options = WemapMetaRemoteRouterOptions.fromJson(json.options);
39
+ }
40
+ return obj;
41
+ }
42
+ }
43
+
44
+ export default WemapMetaRemoteRouterPayload;
@@ -12,6 +12,12 @@ class WemapRouter extends GraphRouter {
12
12
  super(network);
13
13
  }
14
14
 
15
+
16
+ /** @type {string} */
17
+ static get rname() {
18
+ return 'wemap';
19
+ }
20
+
15
21
  /**
16
22
  * @param {Coordinates} start
17
23
  * @param {Coordinates} end
@@ -1,6 +1,7 @@
1
1
  import { GraphItinerary } from '@wemap/geo';
2
2
  import { OsmElement } from '@wemap/osm';
3
3
 
4
+ import Constants from '../Constants.js';
4
5
  import Itinerary from '../model/Itinerary.js';
5
6
  import Leg from '../model/Leg.js';
6
7
  import WemapStepsGeneration from './WemapStepsGeneration.js';
@@ -10,17 +11,21 @@ import WemapStepsGeneration from './WemapStepsGeneration.js';
10
11
  * @param {string} mode
11
12
  * @returns {Leg}
12
13
  */
13
- export function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
14
+ export function createLegFromGraphItinerary(graphItinerary,
15
+ mode = Constants.ROUTING_MODE.WALK) {
14
16
 
15
17
  const leg = new Leg();
16
18
 
17
- leg.from = { coords: graphItinerary.start };
18
- leg.to = { coords: graphItinerary.end };
19
+ leg.from = { coords: graphItinerary.start, name: null };
20
+ leg.to = { coords: graphItinerary.end, name: null };
19
21
  leg.coords = graphItinerary.nodes.map(node => node.coords);
20
22
  leg.distance = graphItinerary.edges.reduce((acc, edge) => acc + edge.length, 0);
21
23
  leg.duration = graphItinerary.edgesWeights.reduce((acc, weight) => acc + weight, 0);
22
24
  leg.mode = mode;
23
25
  leg.steps = WemapStepsGeneration.fromGraphItinerary(graphItinerary);
26
+ leg.steps.forEach(step => {
27
+ step._idCoordsInLeg = leg.coords.findIndex(coords => coords === step.coords);
28
+ });
24
29
 
25
30
  return leg;
26
31
  }
@@ -30,7 +35,8 @@ export function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
30
35
  * @param {string} mode
31
36
  * @returns {Itinerary}
32
37
  */
33
- export function createItineraryFromGraphItinerary(graphItinerary, mode = 'WALK') {
38
+ export function createItineraryFromGraphItinerary(graphItinerary,
39
+ mode = Constants.ROUTING_MODE.WALK) {
34
40
 
35
41
  const leg = createLegFromGraphItinerary(graphItinerary, mode);
36
42
 
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable complexity */
2
2
  /* eslint-disable max-statements */
3
- import { Coordinates, GraphItinerary } from '@wemap/geo';
3
+ import { Coordinates, Level, GraphItinerary, GraphNode, GraphUtils } from '@wemap/geo';
4
4
  import { diffAngle, deg2rad } from '@wemap/maths';
5
5
  import { OsmElement } from '@wemap/osm';
6
6
 
@@ -69,7 +69,7 @@ class WemapStepsGeneration {
69
69
  }
70
70
 
71
71
  if (splitByLevel) {
72
- currentStep.levelChange = LevelChange.fromTwoNodes(node, nextNode);
72
+ currentStep.levelChange = WemapStepsGeneration.levelChangefromTwoNodes(node, nextNode);
73
73
  }
74
74
 
75
75
  steps.push(currentStep);
@@ -85,20 +85,47 @@ class WemapStepsGeneration {
85
85
  }
86
86
 
87
87
  const lastNode = nodes[nodes.length - 1];
88
- const lastStep = new Step();
89
- lastStep.coords = lastNode.coords;
90
- lastStep.number = steps.length + 1;
91
- lastStep.previousBearing = previousBearing;
92
- lastStep.distance = lastNode.coords.distanceTo(end);
88
+
89
+ // Create a last step if end is not on the network
93
90
  if (!Coordinates.equalsTo(lastNode.coords, end)) {
91
+ const lastStep = new Step();
92
+ lastStep.coords = lastNode.coords;
93
+ lastStep.number = steps.length + 1;
94
+ lastStep.previousBearing = previousBearing;
95
+ lastStep.distance = lastNode.coords.distanceTo(end);
94
96
  lastStep.nextBearing = lastNode.coords.bearingTo(end);
95
97
  lastStep.angle = diffAngle(lastStep.previousBearing, lastStep.nextBearing + Math.PI);
98
+ steps.push(lastStep);
96
99
  }
97
- lastStep.lastStep = true;
98
- steps.push(lastStep);
100
+
101
+ steps[steps.length - 1].lastStep = true;
99
102
 
100
103
  return steps;
101
104
  }
102
105
 
106
+ /**
107
+ * @param {GraphNode<OsmElement>} firstNode
108
+ * @param {GraphNode<OsmElement>} secondNode
109
+ * @returns {LevelChange}
110
+ */
111
+ static levelChangefromTwoNodes(firstNode, secondNode) {
112
+
113
+ const levelChange = new LevelChange();
114
+
115
+ const edge = GraphUtils.getEdgeByNodes(firstNode.edges, firstNode, secondNode);
116
+
117
+ if (edge.builtFrom.isElevator) {
118
+ levelChange.type = 'elevator';
119
+ } else if (edge.builtFrom.isConveying) {
120
+ levelChange.type = 'conveyor';
121
+ } else if (edge.builtFrom.areStairs) {
122
+ levelChange.type = 'stairs';
123
+ }
124
+
125
+ levelChange.difference = Level.diff(firstNode.coords.level, secondNode.coords.level);
126
+ levelChange.direction = levelChange.difference > 0 ? 'up' : 'down';
127
+
128
+ return levelChange;
129
+ }
103
130
  }
104
131
  export default WemapStepsGeneration;
@@ -0,0 +1,191 @@
1
+ import pointInPolygon from '@turf/boolean-point-in-polygon';
2
+ import convexHullFn from '@turf/convex';
3
+ import { polygon as turfPolygon } from '@turf/helpers';
4
+
5
+ import { Coordinates, GraphNode, GraphRouter, Network, NoRouteFoundError } from '@wemap/geo';
6
+ import { OsmParser } from '@wemap/osm';
7
+ import { Itinerary, WemapNetworkUtils, WemapRouterOptions, WemapRouterUtils } from '@wemap/routers';
8
+
9
+ import Constants from '../Constants.js';
10
+
11
+ class IOMap {
12
+
13
+ /** @type {?string} */
14
+ name;
15
+
16
+ /** @type {!GraphRouter} */
17
+ router;
18
+
19
+ /** @type {!([number, number][])} */
20
+ bounds;
21
+
22
+
23
+ /** @type {!(GraphNode[])} */
24
+ entryPoints;
25
+
26
+ /**
27
+ * @param {Network} network The network of the map
28
+ * @param {GraphNode[]} entryPoints The map vertex that can be used to go inside / outside a IOMap
29
+ * @param {Coordinates[]} bounds The bounds
30
+ * @param {string} name The name of the map
31
+ * @returns {IOMap}
32
+ */
33
+ constructor(network, entryPoints, bounds = null, name = null) {
34
+
35
+ this.name = name;
36
+ this.router = new GraphRouter(network);
37
+
38
+ // Entry points
39
+ entryPoints.forEach(entryPoint => {
40
+ if (!network.nodes.includes(entryPoint)) {
41
+ throw new Error(`Cannot find entry point ${entryPoint.coords.toString()} in network "${name}"`);
42
+ }
43
+ });
44
+ this.entryPoints = entryPoints;
45
+
46
+ // Bounds
47
+ if (bounds) {
48
+ this.bounds = turfPolygon([bounds.map(coords => [coords.lng, coords.lat])]);
49
+ } else {
50
+ const polygon = [network.nodes.map(node => [node.coords.lng, node.coords.lat])];
51
+ const convexHull = convexHullFn(polygon);
52
+ if (!convexHull) {
53
+ throw new Error(`Cannot calculate convexHull of network "${name}"`);
54
+ }
55
+ this.bounds = convexHull;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * @param {string} osmXmlString
61
+ * @param {string} name
62
+ * @returns {IOMap}
63
+ */
64
+ static fromOsmXml(osmXmlString, name = null) {
65
+
66
+ const osmModel = OsmParser.parseOsmXmlString(osmXmlString);
67
+ const network = WemapNetworkUtils.createNetworkFromOsmModel(osmModel);
68
+
69
+ const entryPoints = osmModel.nodes
70
+ .filter(({ tags }) => tags && tags['wemap:routing-io'])
71
+ .map(osmNode => network.getNodeByCoords(osmNode.coords));
72
+ if (entryPoints.some(el => el === null)
73
+ || new Set(entryPoints).size !== entryPoints.length
74
+ ) {
75
+ throw new Error('Cannot parse wemap:routing-io correctly');
76
+ }
77
+
78
+ const wayBounds = osmModel.ways.find(({ tags }) => tags['wemap:routing-bounds']);
79
+ if (!wayBounds) {
80
+ throw new Error('Search bounds is undefined. Please use OSM tag : "wemap:routing-bounds=yes"');
81
+ }
82
+ const bounds = wayBounds.nodes.map(node => node.coords);
83
+ return new IOMap(network, entryPoints, bounds, name);
84
+ }
85
+
86
+ /**
87
+ * @param {Coordinates} coordinates
88
+ * @returns {boolean}
89
+ */
90
+ isPointInside(coordinates) {
91
+ return pointInPolygon([coordinates.lng, coordinates.lat], this.bounds);
92
+ }
93
+
94
+ /**
95
+ * Get the list of entry points sorted by the lowest distance between:
96
+ * start -> entry point -> end
97
+ * (as the crow flies)
98
+ *
99
+ * @param {Coordinates} start
100
+ * @param {Coordinates} end
101
+ * @returns {GraphNode[]}
102
+ */
103
+ getOrderedEntryPointsSortedByDistance(start, end) {
104
+ const entryPointsCopy = [...this.entryPoints];
105
+ return entryPointsCopy.sort((ep1, ep2) =>
106
+ + ep1.coords.distanceTo(start) + ep1.coords.distanceTo(end)
107
+ - ep2.coords.distanceTo(start) - ep2.coords.distanceTo(end)
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Get the best itinerary from any entry point to an end coordinates.
113
+ *
114
+ * The algorithm works as following:
115
+ * 1 - Entry points are sorted using distance (as the crow flies) between start - entry point - end
116
+ * 2 - Try to calculate an itinerary from the first entry point to the end coordinates.
117
+ * 3 - If an itinerary is found, it is returned. Otherwise it tries from the next entry point.
118
+ *
119
+ * /!\ start is only used to sort the entry points (step 1).
120
+ *
121
+ * @param {Coordinates} start The start coordinates (which is outside the network)
122
+ * @param {Coordinates} end Then end coordinates (which is inside the network)
123
+ * @param {WemapRouterOptions} options
124
+ * @returns {Itinerary}
125
+ */
126
+ getBestItineraryFromEntryPointsToEnd(start, end, options) {
127
+
128
+ const sortedEntryPoints = this.getOrderedEntryPointsSortedByDistance(start, end);
129
+ for (const entryPoint of sortedEntryPoints) {
130
+ const itinerary = this.getItineraryInsideMap(entryPoint, end, options);
131
+ if (itinerary) {
132
+ return itinerary;
133
+ }
134
+ // no route found
135
+ }
136
+ throw new NoRouteFoundError(start, end,
137
+ `No route found from entry points to ${end.toString()} in map: ${this.name}`
138
+ );
139
+ }
140
+
141
+
142
+ /**
143
+ * Get the best itinerary from start coordinates to any entry point.
144
+ *
145
+ * The algorithm works as following:
146
+ * 1 - Entry points are sorted using distance (as the crow flies) between start - entry point - end
147
+ * 2 - Try to calculate an itinerary from the start coordinates to the first entry point.
148
+ * 3 - If an itinerary is found, it is returned. Otherwise it tries to the next entry point.
149
+ *
150
+ * /!\ end is only used to sort the entry points (step 1).
151
+ *
152
+ * @param {Coordinates} start The start coordinates (which is inside the network)
153
+ * @param {Coordinates} end Then end coordinates (which is outside the network)
154
+ * @param {WemapRouterOptions} options
155
+ * @returns {Itinerary}
156
+ */
157
+ getBestItineraryFromStartToEntryPoints(start, end, options) {
158
+
159
+ const sortedEntryPoints = this.getOrderedEntryPointsSortedByDistance(start, end);
160
+ for (const entryPoint of sortedEntryPoints) {
161
+ const itinerary = this.getItineraryInsideMap(start, entryPoint, options);
162
+ if (itinerary) {
163
+ return itinerary;
164
+ }
165
+ // no route found
166
+ }
167
+ throw new NoRouteFoundError(start, end,
168
+ `No route found from ${start.toString()} to entry points in map: ${this.name}`
169
+ );
170
+ }
171
+
172
+ /**
173
+ * @param {Coordinates} start the start coordinates
174
+ * @param {Coordinates} end the end coordinates
175
+ * @param {WemapRouterOptions} options
176
+ * @returns {?Itinerary}
177
+ */
178
+ getItineraryInsideMap(start, end, options) {
179
+
180
+ // Call the Wemap router to get the shortest path
181
+ const graphItinerary = this.router.getShortestPath(start, end, options);
182
+ if (graphItinerary === null) {
183
+ return null;
184
+ }
185
+
186
+ // Transform a network itinerary (nodes, edges...) to a router itinerary (legs, steps...)
187
+ return WemapRouterUtils.createItineraryFromGraphItinerary(graphItinerary, Constants.ROUTING_MODE.WALK);
188
+ }
189
+ }
190
+
191
+ export default IOMap;