@wemap/routers 6.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 (49) hide show
  1. package/assets/bureaux-wemap-montpellier-network.osm +162 -0
  2. package/assets/gare-de-lyon-extract.osm +174 -0
  3. package/assets/itinerary-deutsche-bahn-1.json +368 -0
  4. package/assets/itinerary-grenoble-otp-1.json +1536 -0
  5. package/assets/itinerary-grenoble-otp-2.json +1092 -0
  6. package/assets/itinerary-lehavre-cityway-1.json +6799 -0
  7. package/assets/itinerary-lehavre-cityway-2.json +2133 -0
  8. package/assets/itinerary-lehavre-cityway-3.json +12577 -0
  9. package/assets/itinerary-lehavre-cityway-4.json +1451 -0
  10. package/assets/itinerary-lehavre-cityway-5.json +5925 -0
  11. package/assets/itinerary-montpellier-osrm-3.json +1 -0
  12. package/assets/itinerary-montpellier-outdoor-without-steps.json +110 -0
  13. package/assets/itinerary-montpellier-outdoor.json +513 -0
  14. package/assets/itinerary-with-duplicate-nodes.json +110 -0
  15. package/assets/network-conveying-backward.osm +74 -0
  16. package/assets/network-elevator.osm +48 -0
  17. package/assets/network-simple.osm +27 -0
  18. package/assets/network-with-modifiers.osm +39 -0
  19. package/assets/one-way.osm +46 -0
  20. package/dist/wemap-osm.es.js +2012 -0
  21. package/dist/wemap-osm.es.js.map +1 -0
  22. package/index.js +24 -0
  23. package/package.json +35 -0
  24. package/src/ItineraryInfoManager.js +148 -0
  25. package/src/ItineraryInfoManager.spec.js +54 -0
  26. package/src/Utils.js +64 -0
  27. package/src/cityway/CitywayUtils.js +240 -0
  28. package/src/cityway/CitywayUtils.spec.js +109 -0
  29. package/src/deutsche-bahn/DeutscheBahnRouterUtils.js +91 -0
  30. package/src/deutsche-bahn/DeutscheBahnRouterUtils.spec.js +54 -0
  31. package/src/model/Itinerary.js +197 -0
  32. package/src/model/Itinerary.type.spec.js +105 -0
  33. package/src/model/ItineraryInfo.js +34 -0
  34. package/src/model/Leg.js +113 -0
  35. package/src/model/LevelChange.js +65 -0
  36. package/src/model/RouterResponse.js +19 -0
  37. package/src/model/RouterResponse.type.spec.js +24 -0
  38. package/src/model/Step.js +118 -0
  39. package/src/osrm/OsrmUtils.js +269 -0
  40. package/src/osrm/OsrmUtils.spec.js +457 -0
  41. package/src/otp/OtpUtils.js +150 -0
  42. package/src/otp/OtpUtils.spec.js +97 -0
  43. package/src/wemap/WemapNetworkUtils.js +195 -0
  44. package/src/wemap/WemapNetworkUtils.spec.js +109 -0
  45. package/src/wemap/WemapRouter.js +27 -0
  46. package/src/wemap/WemapRouter.spec.js +221 -0
  47. package/src/wemap/WemapRouterOptions.js +32 -0
  48. package/src/wemap/WemapRouterUtils.js +46 -0
  49. package/src/wemap/WemapStepsGeneration.js +104 -0
@@ -0,0 +1,97 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import chaiAlmost from 'chai-almost';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ import { Coordinates } from '@wemap/geo';
9
+
10
+ import { createRouterResponseFromJson } from './OtpUtils.js';
11
+
12
+ import { verifyRouterResponseData } from '../model/RouterResponse.type.spec.js';
13
+
14
+
15
+ const { expect } = chai;
16
+ chai.use(chaiAlmost(0.1));
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+
20
+ describe('OtpUtils - createRouterResponseFromJson', () => {
21
+
22
+ it('RouterResponse - 1', () => {
23
+
24
+ const filePath = path.resolve(__dirname, '../../assets/itinerary-grenoble-otp-1.json');
25
+ const fileString = fs.readFileSync(filePath, 'utf8');
26
+ const json = JSON.parse(fileString);
27
+
28
+ const routerResponse = createRouterResponseFromJson(json);
29
+ verifyRouterResponseData(routerResponse);
30
+
31
+ expect(routerResponse.routerName).equal('otp');
32
+ expect(routerResponse.itineraries.length).equal(2);
33
+ expect(routerResponse.from.equalsTo(new Coordinates(45.187291, 5.723455))).true;
34
+ expect(routerResponse.to.equalsTo(new Coordinates(45.163729, 5.74085))).true;
35
+
36
+ const itinerary1 = routerResponse.itineraries[0];
37
+ expect(itinerary1.coords.length).equal(162);
38
+ expect(itinerary1.duration).equal(1206);
39
+ expect(itinerary1.startTime).equal(1614607210000);
40
+ expect(itinerary1.endTime).equal(1614608416000);
41
+ expect(itinerary1.legs.length).equal(3);
42
+
43
+ const itinerary1leg1 = itinerary1.legs[0];
44
+ expect(itinerary1leg1.startTime).equal(1614607210000);
45
+ expect(itinerary1leg1.endTime).equal(1614607499000);
46
+ expect(itinerary1leg1.distance).almost.equal(260.14);
47
+ expect(itinerary1leg1.mode).equal('WALK');
48
+ expect(itinerary1leg1.transportInfo).is.null;
49
+ expect(itinerary1leg1.from.name).equal('Origin');
50
+ expect(itinerary1leg1.from.coords.equalsTo(new Coordinates(45.187291, 5.723455))).true;
51
+ expect(itinerary1leg1.to.name).equal('Grenoble, Victor Hugo');
52
+ expect(itinerary1leg1.to.coords.equalsTo(new Coordinates(45.1887, 5.72533))).true;
53
+
54
+ const itinerary1leg2 = itinerary1.legs[1];
55
+ expect(itinerary1leg2.mode).equal('BUS');
56
+ expect(itinerary1leg2.transportInfo).is.not.null;
57
+ expect(itinerary1leg2.transportInfo.name).equal('C4');
58
+ expect(itinerary1leg2.transportInfo.routeColor).equal('F3D000');
59
+ expect(itinerary1leg2.transportInfo.routeTextColor).equal('000000');
60
+ expect(itinerary1leg2.transportInfo.directionName).equal('Eybens, Le Verderet');
61
+
62
+ });
63
+
64
+
65
+ it('RouterResponse - 2', () => {
66
+
67
+ const filePath = path.resolve(__dirname, '../../assets/itinerary-grenoble-otp-2.json');
68
+ const fileString = fs.readFileSync(filePath, 'utf8');
69
+ const json = JSON.parse(fileString);
70
+
71
+ const routerResponse = createRouterResponseFromJson(json);
72
+ verifyRouterResponseData(routerResponse);
73
+
74
+ expect(routerResponse.itineraries.length).equal(2);
75
+ expect(routerResponse.from.equalsTo(new Coordinates(45.192514856662456, 5.731452541397871))).true;
76
+ expect(routerResponse.to.equalsTo(new Coordinates(45.18544, 5.73123))).true;
77
+
78
+ const itinerary1 = routerResponse.itineraries[0];
79
+ expect(itinerary1.legs.length).equal(5);
80
+
81
+ expect(itinerary1.legs[0].mode).equal('WALK');
82
+
83
+ expect(itinerary1.legs[1].mode).equal('TRAM');
84
+ expect(itinerary1.legs[1].transportInfo).is.not.null;
85
+
86
+ expect(itinerary1.legs[2].mode).equal('WALK');
87
+
88
+ expect(itinerary1.legs[3].mode).equal('TRAM');
89
+ expect(itinerary1.legs[3].transportInfo).is.not.null;
90
+
91
+ expect(itinerary1.legs[4].mode).equal('WALK');
92
+
93
+ });
94
+
95
+
96
+ });
97
+
@@ -0,0 +1,195 @@
1
+ import { Level, GraphEdge, GraphNode, Network } from '@wemap/geo';
2
+ import { OsmElement, OsmModel, OsmNode, OsmWay } from '@wemap/osm';
3
+
4
+
5
+ export const HIGHWAYS_PEDESTRIANS = ['footway', 'steps', 'pedestrian', 'living_street', 'path', 'track', 'sidewalk'];
6
+
7
+ export const DEFAULT_WAY_SELECTOR = way => {
8
+ return HIGHWAYS_PEDESTRIANS.includes(way.tags.highway)
9
+ || way.tags.footway === 'sidewalk'
10
+ || way.tags.public_transport === 'platform'
11
+ || way.tags.railway === 'platform';
12
+ };
13
+
14
+ /**
15
+ * @param {Network<OsmElement>} network
16
+ * @param {string} name
17
+ */
18
+ export function getNodeByName(network, name) {
19
+ return network.nodes.find(({ builtFrom }) => builtFrom.tags.name === name);
20
+ }
21
+
22
+ /**
23
+ * @param {Network<OsmElement>} network
24
+ * @param {string} name
25
+ */
26
+ export function getEdgeByName(network, name) {
27
+ return network.edges.find(({ builtFrom }) => builtFrom.tags.name === name);
28
+ }
29
+
30
+ /**
31
+ * @param {GraphEdge<OsmElement>} edge
32
+ * @param {OsmWay} way
33
+ * @returns {boolean}
34
+ */
35
+ function manageOneWay(edge, way) {
36
+
37
+ const { highway, oneway, conveying } = way.tags;
38
+
39
+ edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
40
+ || (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
41
+
42
+ if (edge.isOneway && conveying === 'backward') {
43
+ const tmpNode = edge.node1;
44
+ edge.node1 = edge.node2;
45
+ edge.node2 = tmpNode;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @param {Network} networkModel
51
+ * @param {GraphNode} node
52
+ */
53
+ function createNodesAndEdgesFromElevator(networkModel, node) {
54
+
55
+ /** @type {GraphNode[]} */
56
+ const createdNodes = [];
57
+ const getOrCreateLevelNode = (level, builtFrom) => {
58
+ let levelNode = createdNodes.find(({ coords }) => Level.equalsTo(level, coords.level));
59
+ if (!levelNode) {
60
+ levelNode = new GraphNode(node.coords.clone(), builtFrom);
61
+ levelNode.coords.level = level;
62
+ createdNodes.push(levelNode);
63
+ networkModel.nodes.push(levelNode);
64
+ }
65
+ return levelNode;
66
+ };
67
+
68
+ // Create nodes from node.edges
69
+ node.edges.forEach(edge => {
70
+ if (edge.level.isRange) {
71
+ throw new Error('Cannot handle this elevator edge due to ambiguity');
72
+ }
73
+
74
+ const levelNode = getOrCreateLevelNode(edge.level, node.builtFrom);
75
+ if (edge.node1 === node) {
76
+ edge.node1 = levelNode;
77
+ } else {
78
+ edge.node2 = levelNode;
79
+ }
80
+ levelNode.edges.push(edge);
81
+ });
82
+
83
+ // Create edges from createdNodes
84
+ for (let i = 0; i < createdNodes.length; i++) {
85
+ for (let j = i + 1; j < createdNodes.length; j++) {
86
+
87
+ const createdNode1 = createdNodes[i];
88
+ const createdNode2 = createdNodes[j];
89
+
90
+ const newEdge = new GraphEdge(
91
+ createdNode1,
92
+ createdNode2,
93
+ new Level(createdNode1.coords.level.val, createdNode2.coords.level.val),
94
+ node.builtFrom
95
+ );
96
+ networkModel.edges.push(newEdge);
97
+ }
98
+ }
99
+
100
+ // Remove the historical elevator node from the network
101
+ networkModel.nodes = networkModel.nodes.filter(_node => _node !== node);
102
+ }
103
+
104
+
105
+ /**
106
+ * @param {OsmModel} osmModel
107
+ * @param {function} _waySelectionFilter
108
+ * @returns {Network<OsmElement>}
109
+ */
110
+ export function createNetworkFromOsmModel(
111
+ osmModel,
112
+ waySelectionFilter = DEFAULT_WAY_SELECTOR
113
+ ) {
114
+
115
+ const networkModel = new Network();
116
+
117
+ const nodesCreated = {};
118
+ const elevatorNodes = [];
119
+
120
+ /**
121
+ * @param {OsmNode} osmNode
122
+ */
123
+ const getOrCreateNode = osmNode => {
124
+ let node = nodesCreated[osmNode.id];
125
+ if (!node) {
126
+ node = new GraphNode(osmNode.coords, osmNode);
127
+ nodesCreated[osmNode.id] = node;
128
+ networkModel.nodes.push(node);
129
+
130
+ if (osmNode.tags.highway === 'elevator') {
131
+ elevatorNodes.push(node);
132
+ }
133
+ }
134
+ return node;
135
+ };
136
+
137
+ osmModel.ways.forEach(way => {
138
+ if (!waySelectionFilter(way)) {
139
+ return;
140
+ }
141
+
142
+ let firstNode = getOrCreateNode(way.nodes[0]);
143
+ for (let i = 1; i < way.nodes.length; i++) {
144
+ const secondNode = getOrCreateNode(way.nodes[i]);
145
+
146
+ const edge = new GraphEdge(firstNode, secondNode, way.level, way);
147
+ manageOneWay(edge, way);
148
+ networkModel.edges.push(edge);
149
+ firstNode = secondNode;
150
+ }
151
+
152
+ });
153
+
154
+ elevatorNodes.forEach(node => {
155
+ // We have to clone this node for each connected edge
156
+ createNodesAndEdgesFromElevator(networkModel, node);
157
+ });
158
+
159
+ GraphNode.generateNodesLevels(networkModel.nodes);
160
+
161
+ return networkModel;
162
+ }
163
+
164
+
165
+ // /**
166
+ // * @param {GraphNode} node
167
+ // * @param {object} tags
168
+ // */
169
+ // static _applyNodePropertiesFromTags(node, tags) {
170
+ // node.name = tags.name || null;
171
+ // node.subwayEntrance = tags.railway === 'subway_entrance';
172
+ // if (node.subwayEntrance && tags.ref) {
173
+ // node.subwayEntranceRef = tags.ref;
174
+ // }
175
+ // }
176
+
177
+ // /**
178
+ // * @param {GraphEdge} edge
179
+ // * @param {object} tags
180
+ // */
181
+ // static _applyEdgePropertiesFromTags(edge, tags) {
182
+ // const { highway, oneway, conveying, name } = tags;
183
+ // edge.name = name || null;
184
+ // edge.isStairs = highway === 'steps';
185
+ // edge.isConveying = 'conveying' in tags;
186
+ // edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
187
+ // || (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
188
+
189
+ // if (conveying === 'backward') {
190
+ // const tmpNode = edge.node1;
191
+ // edge.node1 = edge.node2;
192
+ // edge.node2 = tmpNode;
193
+ // }
194
+
195
+ // }
@@ -0,0 +1,109 @@
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 { Network } from '@wemap/geo';
8
+ import { verifyCoherence } from '@wemap/geo/tests/CommonTest.js';
9
+
10
+ import { OsmModel, OsmParser } from '@wemap/osm';
11
+
12
+ import { createNetworkFromOsmModel, getNodeByName, getEdgeByName } from './WemapNetworkUtils.js';
13
+
14
+ const { expect } = chai;
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+
17
+
18
+ const loadFile = fileName => {
19
+ const filePath = path.resolve(__dirname, '../../assets/' + fileName);
20
+ return fs.readFileSync(filePath, 'utf8');
21
+ };
22
+
23
+ describe('OsmNetwork - simple', () => {
24
+
25
+ /** @type {OsmModel} */
26
+ let osmModel;
27
+
28
+ /** @type {Network} */
29
+ let network;
30
+ const osmXmlString = loadFile('network-simple.osm');
31
+
32
+ it('Network creation', () => {
33
+
34
+ osmModel = OsmParser.parseOsmXmlString(osmXmlString);
35
+ network = createNetworkFromOsmModel(osmModel);
36
+
37
+ verifyCoherence(network);
38
+ expect(network.nodes.length).equals(3);
39
+ expect(network.edges.length).equals(2);
40
+ });
41
+
42
+
43
+ it('Network creation - waySelectionFilter', () => {
44
+
45
+ const selectionNetwork = createNetworkFromOsmModel(osmModel, () => true);
46
+
47
+ verifyCoherence(selectionNetwork);
48
+ expect(selectionNetwork.nodes.length).equals(4);
49
+ expect(selectionNetwork.edges.length).equals(3);
50
+ });
51
+
52
+ it('Network creation - elevator', () => {
53
+
54
+ const elevatorModel = OsmParser.parseOsmXmlString(loadFile('network-elevator.osm'));
55
+ const elevatorNetwork = createNetworkFromOsmModel(elevatorModel);
56
+
57
+ verifyCoherence(elevatorNetwork);
58
+ expect(elevatorNetwork.nodes.length).equals(7);
59
+ expect(elevatorNetwork.edges.length).equals(7);
60
+
61
+
62
+ const osmModel2 = OsmParser.parseOsmXmlString(`
63
+ <osm>
64
+ <node id='1' lat='43' lon='4'>
65
+ <tag k='highway' v='elevator' />
66
+ <tag k='level' v='0;1' />
67
+ </node>
68
+ <node id='2' lat='43' lon='3' />
69
+ <way id='3'>
70
+ <nd ref='1' />
71
+ <nd ref='2' />
72
+ <tag k='highway' v='footway' />
73
+ <tag k='level' v='0;1' />
74
+ </way>
75
+ </osm>
76
+ `);
77
+ expect(() => createNetworkFromOsmModel(osmModel2)).throw(Error);
78
+ });
79
+
80
+ it('getNodeByName', () => {
81
+ let node = getNodeByName(network, 'p1');
82
+ expect(node).not.undefined;
83
+
84
+ node = getNodeByName(network, 'p5');
85
+ expect(node).is.undefined;
86
+ });
87
+ });
88
+
89
+
90
+ describe('OsmNetwork - with stairs', () => {
91
+
92
+ /** @type {OsmModel} */
93
+ let osmModel;
94
+
95
+ /** @type {Network} */
96
+ let network;
97
+ const osmXmlString = loadFile('bureaux-wemap-montpellier-network.osm');
98
+
99
+ it('Network creation', () => {
100
+
101
+ osmModel = OsmParser.parseOsmXmlString(osmXmlString);
102
+ network = createNetworkFromOsmModel(osmModel);
103
+
104
+ verifyCoherence(network);
105
+
106
+ expect(getEdgeByName(network, 'w6').builtFrom.tags.highway).equal('steps');
107
+ });
108
+
109
+ });
@@ -0,0 +1,27 @@
1
+ import { Coordinates, GraphItinerary, GraphRouter, Network } from '@wemap/geo';
2
+ import { OsmElement } from '@wemap/osm';
3
+
4
+ import WemapRouterOptions from './WemapRouterOptions.js';
5
+
6
+ class WemapRouter extends GraphRouter {
7
+
8
+ /**
9
+ * @param {!Network<OsmElement>} network
10
+ */
11
+ constructor(network) {
12
+ super(network);
13
+ }
14
+
15
+ /**
16
+ * @param {Coordinates} start
17
+ * @param {Coordinates} end
18
+ * @param {WemapRouterOptions} options
19
+ * @returns {GraphItinerary<OsmElement>}
20
+ */
21
+ getShortestPath(start, end, options = new WemapRouterOptions()) {
22
+ return super.getShortestPath(start, end, options);
23
+ }
24
+
25
+ }
26
+
27
+ export default WemapRouter;
@@ -0,0 +1,221 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import chaiAlmost from 'chai-almost';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ import {
9
+ Level, Coordinates, GraphNode, Network, GraphItinerary
10
+ } from '@wemap/geo';
11
+ import { OsmElement, OsmParser } from '@wemap/osm';
12
+
13
+
14
+ import WemapRouter from './WemapRouter.js';
15
+ import WemapRouterOptions from './WemapRouterOptions.js';
16
+ import WemapStepsGeneration from './WemapStepsGeneration.js';
17
+ import { createNetworkFromOsmModel, getNodeByName } from './WemapNetworkUtils.js';
18
+
19
+ import LevelChange from '../model/LevelChange.js';
20
+
21
+ const { expect } = chai;
22
+ chai.use(chaiAlmost(0.01));
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+
25
+
26
+ /**
27
+ * @param {GraphItinerary<OsmElement>} _itinerary
28
+ * @returns {string[]}
29
+ */
30
+ const getNodesNames = _itinerary => {
31
+ return _itinerary.nodes.map(({ builtFrom }) => builtFrom.tags.name || null);
32
+ };
33
+
34
+
35
+ const itineraryStart = new Coordinates(43.6092754, 3.8842306, null, new Level(2));
36
+ const itineraryEnd = new Coordinates(43.6092602, 3.8842669, null, new Level(1));
37
+
38
+
39
+ /**
40
+ * @param {string} fileName
41
+ * @returns {Network}
42
+ */
43
+ const loadNetwork = fileName => {
44
+ const filePath = path.resolve(__dirname, '../../assets/' + fileName);
45
+ const osmXmlString = fs.readFileSync(filePath, 'utf8');
46
+ const osmModel = OsmParser.parseOsmXmlString(osmXmlString);
47
+ return createNetworkFromOsmModel(osmModel);
48
+ };
49
+
50
+ const generateNodeNames = (start, end) =>
51
+ new Array(end - start + 1).fill().map((_, idx) => 'p' + (idx + start));
52
+
53
+ describe('WemapRouter - Multi-level itinerary', () => {
54
+
55
+ const networkModel = loadNetwork('bureaux-wemap-montpellier-network.osm');
56
+ const router = new WemapRouter(networkModel);
57
+
58
+ const itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
59
+
60
+ /** @type {GraphNode<OsmElement>[]} */
61
+ const p = [];
62
+
63
+ it('Search for nodes', () => {
64
+ for (let i = 1; i <= 16; i++) {
65
+ p[i] = getNodeByName(networkModel, 'p' + i);
66
+ expect(p[i]).instanceOf(GraphNode);
67
+ }
68
+ });
69
+
70
+ it('Router return shortest path', () => {
71
+ expect(getNodesNames(itinerary)).deep.equals(
72
+ ['w2', ...generateNodeNames(7, 16)]
73
+ );
74
+ });
75
+
76
+ it('Verify steps', () => {
77
+
78
+ const steps = WemapStepsGeneration.fromGraphItinerary(itinerary);
79
+ expect(steps.length).equal(8);
80
+
81
+ expect(steps[0].coords.equalsTo(itineraryStart.getSegmentProjection(p[6].coords, p[7].coords))).true;
82
+ expect(steps[0].name).equals('w2');
83
+ expect(steps[0].number).equals(1);
84
+ expect(steps[0].angle).almost.equals(-1.57);
85
+ expect(steps[0].previousBearing).almost.equals(1.778);
86
+ expect(steps[0].nextBearing).almost.equals(0.207);
87
+ expect(steps[0].distance).almost.equals(2.328);
88
+ expect(steps[0].duration).almost.equals(1.676);
89
+ expect(steps[0].firstStep).true;
90
+ expect(steps[0].lastStep).false;
91
+ expect(steps[0].levelChange).is.null;
92
+ expect(steps[0].extras).is.empty;
93
+
94
+ expect(steps[1].coords.equalsTo(p[8].coords)).true;
95
+ expect(steps[1].name).equals('w3');
96
+ expect(steps[1].number).equals(2);
97
+ expect(steps[1].distance).almost.equals(1.764);
98
+ expect(steps[1].firstStep).false;
99
+ expect(steps[1].lastStep).false;
100
+
101
+ expect(steps[2].coords.equalsTo(p[9].coords)).true;
102
+ expect(steps[2].name).equals('w3');
103
+
104
+ expect(steps[3].coords.equalsTo(p[10].coords)).true;
105
+ expect(steps[3].name).equals('w5');
106
+
107
+ expect(steps[4].coords.equalsTo(p[11].coords)).true;
108
+ expect(steps[4].name).equals('w6');
109
+ expect(steps[4].levelChange).instanceOf(LevelChange);
110
+ expect(steps[4].levelChange.difference).equals(-1);
111
+ expect(steps[4].levelChange.direction).equals('down');
112
+ expect(steps[4].levelChange.type).equals('stairs');
113
+
114
+ expect(steps[5].coords.equalsTo(p[14].coords)).true;
115
+ expect(steps[5].name).equals('w7');
116
+
117
+ expect(steps[6].coords.equalsTo(p[15].coords)).true;
118
+ expect(steps[6].name).equals('w9');
119
+
120
+ expect(steps[7].coords.equalsTo(p[16].coords)).true;
121
+ expect(steps[7].name).is.null;
122
+ expect(steps[7].lastStep).true;
123
+ });
124
+
125
+
126
+ it('router returns shortest path 2', () => {
127
+
128
+ const start = new Coordinates(43.609219, 3.8841743, null, new Level(2));
129
+ const end = new Coordinates(43.60917216742, 3.8842355275, null, new Level(2));
130
+ const itinerary2 = router.getShortestPath(start, end);
131
+
132
+ expect(getNodesNames(itinerary2)).deep.equals(['w1', 'p6', 'p5']);
133
+ });
134
+
135
+ it('do not use stairs', () => {
136
+
137
+ const itineraryWithoutStairs = router.getShortestPath(
138
+ itineraryStart, itineraryEnd,
139
+ WemapRouterOptions.WITHOUT_STAIRS
140
+ );
141
+ expect(itineraryWithoutStairs.nodes.length).equal(11);
142
+ itineraryWithoutStairs.edges.forEach((edge) => {
143
+ expect(edge.builtFrom.tags.highway).is.not.equal('steps');
144
+ });
145
+ });
146
+
147
+ // it('toCompressedJson / fromCompressedJson', () => {
148
+ // const json = itinerary.toCompressedJson();
149
+ // expect(() => JSON.stringify(json)).not.throw(Error);
150
+
151
+ // const itineraryBis = Itinerary.fromCompressedJson(json);
152
+ // verifyCoherence(itineraryBis);
153
+ // expect(itineraryBis.nodes.length).equal(11);
154
+ // verifyNodesOrder(itineraryBis.nodes, ['w2', ...generateNodeNames(7, 16)]);
155
+ // });
156
+
157
+ });
158
+
159
+ describe('WemapRouter - One Way itinerary', () => {
160
+
161
+ const networkModel = loadNetwork('one-way.osm');
162
+ const router = new WemapRouter(networkModel);
163
+
164
+ it('do not use oneway', () => {
165
+
166
+ const start = new Coordinates(43.6094542, 3.8842072);
167
+ const end = new Coordinates(43.6093792, 3.8841889);
168
+
169
+ const itinerary = router.getShortestPath(start, end);
170
+ expect(itinerary).is.not.undefined;
171
+ expect(getNodesNames(itinerary)).deep.equals(['p0', 'p1', 'p2', 'p3']);
172
+
173
+ const itineraryOtherWay = router.getShortestPath(end, start);
174
+ expect(itineraryOtherWay).is.not.undefined;
175
+ expect(getNodesNames(itineraryOtherWay)).deep.equals(['p3', 'p2', 'p5', 'p4', 'p1', 'p0']);
176
+ });
177
+ });
178
+
179
+
180
+ describe('WemapRouter - Conveying', () => {
181
+
182
+ const networkModel = loadNetwork('gare-de-lyon-extract.osm');
183
+ const router = new WemapRouter(networkModel);
184
+
185
+ it('do not use oneway conveying', () => {
186
+
187
+ const start = new Coordinates(48.8445715, 2.3718927, null, new Level(0));
188
+ const end = new Coordinates(48.84443728652394, 2.3721685669363524, null, new Level(-1));
189
+
190
+ const itinerary = router.getShortestPath(start, end);
191
+ expect(itinerary).is.not.undefined;
192
+ expect(getNodesNames(itinerary)).deep.equals(['p11', 'p12', 'p9', 'p8', 'p6', null]);
193
+
194
+ const itineraryOtherWay = router.getShortestPath(end, start);
195
+ expect(itineraryOtherWay).is.not.undefined;
196
+ expect(getNodesNames(itineraryOtherWay)).deep.equals([null, 'p6', 'p7', 'p10', 'p11']);
197
+ });
198
+ });
199
+
200
+
201
+ describe('WemapRouter - Conveying - backward', () => {
202
+
203
+ const networkModel = loadNetwork('network-conveying-backward.osm');
204
+ const router = new WemapRouter(networkModel);
205
+
206
+ it('do not use oneway conveying', () => {
207
+
208
+ const start = getNodeByName(networkModel, 'p6');
209
+ const end = getNodeByName(networkModel, 'p13');
210
+
211
+ const itinerary = router.getShortestPath(start, end);
212
+ expect(itinerary).is.not.undefined;
213
+ expect(getNodesNames(itinerary)).deep.equals(['p6', 'p7', 'p10', 'p11', 'p12', 'p13']);
214
+
215
+ const itineraryOtherWay = router.getShortestPath(end, start);
216
+ expect(itineraryOtherWay).is.not.undefined;
217
+ expect(getNodesNames(itineraryOtherWay)).deep.equals(['p13', 'p12', 'p9', 'p8', 'p6']);
218
+ });
219
+ });
220
+
221
+
@@ -0,0 +1,32 @@
1
+ import { GraphRouterOptions } from '@wemap/geo';
2
+ import { OsmElement } from '@wemap/osm';
3
+
4
+ class WemapRouterOptions extends GraphRouterOptions {
5
+
6
+ /** @type {WemapRouterOptions} */
7
+ static DEFAULT = new WemapRouterOptions();
8
+
9
+ /**
10
+ * @returns {WemapRouterOptions}
11
+ */
12
+ static get WITHOUT_STAIRS() {
13
+ const options = new WemapRouterOptions();
14
+ options.acceptEdgeFn = edge => edge.builtFrom.tags.highway !== 'steps';
15
+ return options;
16
+ }
17
+
18
+ /**
19
+ * Get route duration
20
+ * @param {Number} speed in km/h
21
+ */
22
+ static getDurationFromLength(length, speed = 5) {
23
+ return length / (speed * 1000 / 3600);
24
+ }
25
+
26
+ /** @type {function(GraphEdge<OsmElement>):boolean} */
27
+ weightEdgeFn = edge => edge.builtFrom.isElevator ? 30 : WemapRouterOptions.getDurationFromLength(edge.length);
28
+
29
+
30
+ }
31
+
32
+ export default WemapRouterOptions;