@wemap/geo 0.2.1 → 0.3.1

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.
@@ -0,0 +1,72 @@
1
+ import { expect } from 'chai';
2
+
3
+ import GraphRouter from './GraphRouter';
4
+
5
+ import {
6
+ network, nodes, itineraryStart, itineraryEnd
7
+ } from '../../tests/CommonTest';
8
+
9
+ describe('GraphRouter', () => {
10
+
11
+
12
+ const router = new GraphRouter(network);
13
+ let itinerary;
14
+
15
+
16
+ it('network did not change', () => {
17
+ const nodesBefore = network.nodes.slice(0);
18
+ const edgesBefore = network.edges.slice(0);
19
+
20
+ itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
21
+
22
+ const isEdgeInArray = (edgeArray, edge) => edgeArray.find(_edge =>
23
+ edge.node1 === _edge.node1
24
+ && edge.node2 === _edge.node2
25
+ && edge.level === _edge.level
26
+ && edge.data === _edge.data
27
+ );
28
+ const areEdgeArrayEquals = (edgeArray1, edgeArray2) => {
29
+ if (edgeArray1.length !== edgeArray2.length) {
30
+ return false;
31
+ }
32
+ for (let j = 0; j < edgeArray1.length; j++) {
33
+ if (!isEdgeInArray(edgeArray2, edgeArray1[j])) {
34
+ return false;
35
+ }
36
+ }
37
+ for (let j = 0; j < edgeArray2.length; j++) {
38
+ if (!isEdgeInArray(edgeArray1, edgeArray2[j])) {
39
+ return false;
40
+ }
41
+ }
42
+ return true;
43
+ };
44
+
45
+ expect(network.nodes.length).equals(nodesBefore.length);
46
+ for (let i = 0; i < nodesBefore.length; i++) {
47
+ const node = network.nodes[i];
48
+ const nodeBefore = nodesBefore[i];
49
+ expect(nodeBefore).equals(node);
50
+ expect(areEdgeArrayEquals(node.edges, nodeBefore.edges)).true;
51
+ }
52
+
53
+ expect(areEdgeArrayEquals(network.edges, edgesBefore)).true;
54
+ });
55
+
56
+ it('router returns shortest path', () => {
57
+
58
+ expect(itinerary.nodes.length).equal(11);
59
+
60
+ for (let i = 1; i <= 10; i++) {
61
+ expect(itinerary.nodes[i].data).equal(nodes[i + 6].data);
62
+ }
63
+
64
+ });
65
+
66
+ it('no route found', () => {
67
+ const itineraryWithoutStairs = router.getShortestPath(itineraryStart, itineraryEnd,
68
+ edge => !edge.data || !edge.data.hasOwnProperty('stairs') || !edge.data.stairs
69
+ );
70
+ expect(itineraryWithoutStairs).is.undefined;
71
+ });
72
+ });
@@ -1,9 +1,16 @@
1
-
1
+ /* eslint-disable max-statements */
2
2
  import { WGS84 } from '@wemap/geo';
3
3
 
4
4
  import Network from './Network';
5
5
  import Edge from './Edge';
6
6
  import Node from './Node';
7
+ import Step from './Step';
8
+ import {
9
+ diffAngle, deg2rad
10
+ } from '@wemap/maths';
11
+ import MapMatching from './MapMatching';
12
+
13
+ const SKIP_STEP_ANGLE_MAX = deg2rad(20);
7
14
 
8
15
  /**
9
16
  * Main attributes are:
@@ -19,15 +26,63 @@ class Itinerary extends Network {
19
26
  super();
20
27
  this.start = null;
21
28
  this.end = null;
22
- this.length = -1;
29
+ this._steps = null;
30
+ this._length = 0;
31
+
32
+ // This array is for computational time gain only
33
+ // It helps to know if this.edges is in the same direction than the current itinerary
34
+ // true = opposite direction
35
+ // false = same direction
36
+ // if this.edgeDirectionReversed[i] is true, this.edges[i].node2 is before
37
+ // this.edges[i].node1 following the current itinerary
38
+ this._edgesDirectionReversed = [];
39
+
40
+ // This array is for computational time gain only
41
+ // It helps to find next and previous steps by storing next steps indexes for each node
42
+ // The size of this array is the same than this.nodes
43
+ this._nextStepsIndexes = null;
44
+
45
+ this.mapMatching = new MapMatching(this);
46
+ }
47
+
48
+ /**
49
+ * Get route length
50
+ */
51
+ get length() {
52
+ if (!this._length) {
53
+ this._length = this.edges.reduce((acc, edge) => acc + edge.length, 0);
54
+ }
55
+ return this._length;
56
+ }
57
+
58
+ /**
59
+ * Get route duration with default speed
60
+ */
61
+ get duration() {
62
+ return this.getDuration();
23
63
  }
24
64
 
25
65
  /**
26
66
  * Get route duration
27
67
  * @param {Number} speed in km/h
28
68
  */
29
- getDuration(speed = 4) {
30
- return (speed * 1000 / 3600) * this.length;
69
+ getDuration(speed) {
70
+ return Itinerary.getDurationFromLength(this.length, speed);
71
+ }
72
+
73
+ /**
74
+ * Get route duration
75
+ * @param {Number} speed in km/h
76
+ */
77
+ static getDurationFromLength(length, speed = 4) {
78
+ return length / (speed * 1000 / 3600);
79
+ }
80
+
81
+ get steps() {
82
+ if (!this._steps) {
83
+ this.generateSteps();
84
+ }
85
+ return this._steps;
31
86
  }
32
87
 
33
88
  /**
@@ -51,6 +106,147 @@ class Itinerary extends Network {
51
106
  return null;
52
107
  }
53
108
 
109
+ getNextNode(graphElement) {
110
+ if (graphElement instanceof Edge) {
111
+ const indexOfEdge = this.edges.indexOf(graphElement);
112
+ if (indexOfEdge !== -1) {
113
+ return this.nodes[indexOfEdge + 1];
114
+ }
115
+ }
116
+
117
+ if (graphElement instanceof Node) {
118
+ const indexOfNode = this.nodes.indexOf(graphElement);
119
+ if (indexOfNode !== -1 && indexOfNode !== this.nodes.length - 1) {
120
+ return this.nodes[indexOfNode + 1];
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+
126
+ getNextEdge(graphElement) {
127
+ if (graphElement instanceof Edge) {
128
+ const indexOfEdge = this.edges.indexOf(graphElement);
129
+ if (indexOfEdge !== -1 && indexOfEdge !== this.edges.length - 1) {
130
+ return this.edges[indexOfEdge + 1];
131
+ }
132
+ }
133
+
134
+ if (graphElement instanceof Node) {
135
+ const indexOfNode = this.nodes.indexOf(graphElement);
136
+ if (indexOfNode !== -1 && indexOfNode !== this.edges.length) {
137
+ return this.edges[indexOfNode];
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ getNextStep(graphElement) {
144
+
145
+ if (graphElement instanceof Edge) {
146
+ const indexOfEdge = this.edges.indexOf(graphElement);
147
+ if (indexOfEdge !== -1) {
148
+ return this.getNextStepFromEdgeId(indexOfEdge);
149
+ }
150
+ } else if (graphElement instanceof Node) {
151
+ const indexOfNode = this.nodes.indexOf(graphElement);
152
+ if (indexOfNode !== -1) {
153
+ return this.getNextStepFromNodeId(indexOfNode);
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+
159
+ getNextStepFromEdgeId(edgeId) {
160
+ return this.steps[this._nextStepsIndexes[edgeId + 1]];
161
+ }
162
+
163
+ getNextStepFromNodeId(nodeId) {
164
+ return this.steps[this._nextStepsIndexes[nodeId]];
165
+ }
166
+
167
+ getPreviousStep(graphElement) {
168
+
169
+ if (graphElement instanceof Edge) {
170
+ const indexOfEdge = this.edges.indexOf(graphElement);
171
+ if (indexOfEdge !== -1) {
172
+ return this.getPreviousStepFromEdgeId(indexOfEdge);
173
+ }
174
+ } else if (graphElement instanceof Node) {
175
+ const indexOfNode = this.nodes.indexOf(graphElement);
176
+ if (indexOfNode !== -1) {
177
+ return this.getPreviousStepFromNodeId(indexOfNode);
178
+ }
179
+ }
180
+ return null;
181
+ }
182
+
183
+ getPreviousStepFromEdgeId(edgeId) {
184
+ const previousStepId = this._nextStepsIndexes[edgeId + 1] - 1;
185
+ if (previousStepId !== -1) {
186
+ return this.steps[previousStepId];
187
+ }
188
+ return null;
189
+ }
190
+
191
+ getPreviousStepFromNodeId(nodeId) {
192
+ const previousStepId = this._nextStepsIndexes[nodeId] - 1;
193
+ if (previousStepId !== -1) {
194
+ return this.steps[previousStepId];
195
+ }
196
+ return null;
197
+ }
198
+
199
+ getInfo(position) {
200
+
201
+ if (!(position) instanceof WGS84) {
202
+ return null;
203
+ }
204
+
205
+ const projection = this.mapMatching.getProjection(position);
206
+ if (!projection) {
207
+ return null;
208
+ }
209
+
210
+ const totalDistance = this.length;
211
+ let traveledDistance = 0;
212
+ let nextStep, previousStep;
213
+
214
+ if (projection.nearestElement instanceof Node) {
215
+
216
+ let currentNodeIndex = 0;
217
+ while (this.nodes[currentNodeIndex] !== projection.nearestElement) {
218
+ traveledDistance += this.edges[currentNodeIndex].length;
219
+ currentNodeIndex++;
220
+ }
221
+ nextStep = this.getNextStepFromNodeId(currentNodeIndex);
222
+ previousStep = this.getPreviousStepFromNodeId(currentNodeIndex);
223
+
224
+ } else if (projection.nearestElement instanceof Edge) {
225
+
226
+ let currentEdgeIndex = 0;
227
+ while (this.edges[currentEdgeIndex] !== projection.nearestElement) {
228
+ traveledDistance += this.edges[currentEdgeIndex].length;
229
+ currentEdgeIndex++;
230
+ }
231
+ traveledDistance += this.nodes[currentEdgeIndex].coords
232
+ .distanceTo(projection.projection);
233
+ nextStep = this.getNextStepFromEdgeId(currentEdgeIndex);
234
+ previousStep = this.getPreviousStepFromEdgeId(currentEdgeIndex);
235
+
236
+ } else {
237
+ throw new Error('No projection found');
238
+ }
239
+
240
+ return {
241
+ nextStep,
242
+ previousStep,
243
+ projection,
244
+ traveledDistance,
245
+ traveledPercentage: traveledDistance / totalDistance,
246
+ remainingDistance: totalDistance - traveledDistance,
247
+ remainingPercentage: 1 - traveledDistance / totalDistance
248
+ };
249
+ }
54
250
 
55
251
  static fromNetworkNodes(nodes, start, end) {
56
252
 
@@ -59,32 +255,26 @@ class Itinerary extends Network {
59
255
  */
60
256
 
61
257
  const itinerary = new Itinerary();
62
- itinerary.length = 0;
258
+ itinerary._length = 0;
63
259
  itinerary.start = start;
64
260
  itinerary.end = end;
65
261
 
66
- let nextItineraryNode;
67
-
68
262
  for (let i = 0; i < nodes.length; i++) {
69
263
 
70
264
  const networkNode = nodes[i];
71
- const itineraryNode = nextItineraryNode ? nextItineraryNode : networkNode.clone();
72
265
 
73
- itinerary.nodes.push(itineraryNode);
266
+ itinerary.nodes.push(networkNode);
74
267
 
75
268
  if (i === nodes.length - 1) {
76
269
  break;
77
270
  }
78
- const nextNetworkNode = nodes[i + 1];
79
- nextItineraryNode = nextNetworkNode.clone();
80
271
 
272
+ const nextNetworkNode = nodes[i + 1];
81
273
  const networkEdge = Network.getEdgeByNodes(networkNode.edges, networkNode, nextNetworkNode);
82
- const itineraryEdge = networkEdge.clone(itineraryNode, nextItineraryNode);
83
- itineraryNode.edges.push(itineraryEdge);
84
- nextItineraryNode.edges.push(itineraryEdge);
274
+ itinerary._edgesDirectionReversed.push(networkNode !== networkEdge.node1);
85
275
 
86
- itinerary.edges.push(itineraryEdge);
87
- itinerary.length += itineraryEdge.length;
276
+ itinerary.edges.push(networkEdge);
277
+ itinerary._length += networkEdge.length;
88
278
  }
89
279
 
90
280
  return itinerary;
@@ -97,7 +287,7 @@ class Itinerary extends Network {
97
287
  */
98
288
  static fromOrderedPointsArray(points, start, end, lngLatFormat) {
99
289
  const route = new Itinerary();
100
- route.length = 0;
290
+ route._length = 0;
101
291
  route.start = new WGS84(start[lngLatFormat ? 1 : 0],
102
292
  start[lngLatFormat ? 0 : 1]);
103
293
  route.end = new WGS84(end[lngLatFormat ? 1 : 0],
@@ -114,12 +304,9 @@ class Itinerary extends Network {
114
304
 
115
305
  if (lastPoint) {
116
306
  const edge = new Edge(lastPoint, currentPoint);
117
-
307
+ route._edgesDirectionReversed.push(false);
118
308
  route.edges.push(edge);
119
- route.length += edge.length;
120
-
121
- currentPoint.edges.push(edge);
122
- lastPoint.edges.push(edge);
309
+ route._length += edge.length;
123
310
  }
124
311
 
125
312
  route.nodes.push(currentPoint);
@@ -129,5 +316,90 @@ class Itinerary extends Network {
129
316
  return route;
130
317
  }
131
318
 
319
+
320
+ generateSteps() {
321
+
322
+ this._steps = [];
323
+ this._nextStepsIndexes = [];
324
+
325
+ let currentStep, previousStep;
326
+ let previousBearing = this.start.bearingTo(this.nodes[0].coords);
327
+
328
+ for (let i = 0; i < this.nodes.length - 1; i++) {
329
+
330
+ const node = this.nodes[i];
331
+ const nextNode = this.nodes[i + 1];
332
+ const edge = this.edges[i];
333
+
334
+ const { length } = edge;
335
+ const bearing = edge.bearing + (edge.node1 !== node ? Math.PI : 0);
336
+ const angle = diffAngle(previousBearing, bearing + Math.PI);
337
+
338
+ // If it is not the first iteration
339
+ if (currentStep) {
340
+ currentStep.nodes.push(node);
341
+ }
342
+
343
+ if (!currentStep || Math.abs(diffAngle(Math.PI, angle)) >= SKIP_STEP_ANGLE_MAX) {
344
+
345
+ previousStep = currentStep;
346
+
347
+ currentStep = new Step();
348
+ currentStep.number = this._steps.length + 1;
349
+ currentStep.angle = angle;
350
+ currentStep.previousBearing = previousBearing;
351
+ currentStep.nextBearing = bearing;
352
+ currentStep.nodes.push(node);
353
+ this._steps.push(currentStep);
354
+
355
+ if (!previousStep) {
356
+ currentStep.firstStep = true;
357
+ }
358
+
359
+ const indexOfCurrentStep = this._steps.length - 1;
360
+ const numberOfNodesToFill = previousStep ? previousStep.nodes.length - 1 : 1;
361
+ for (let j = 0; j < numberOfNodesToFill; j++) {
362
+ this._nextStepsIndexes.push(indexOfCurrentStep);
363
+ }
364
+ }
365
+
366
+ currentStep.edges.push(edge);
367
+ currentStep._length += length;
368
+
369
+ if (i === this.nodes.length - 2) {
370
+ currentStep.nodes.push(nextNode);
371
+ }
372
+
373
+ previousBearing = bearing;
374
+ }
375
+
376
+ const lastNode = this.nodes[this.nodes.length - 1];
377
+ if (lastNode.coords !== this.end) {
378
+
379
+ const lastStep = new Step();
380
+ lastStep.number = this._steps.length + 1;
381
+ lastStep._length = 0;
382
+ lastStep.nextBearing = lastNode.coords.bearingTo(this.end);
383
+ lastStep.previousBearing = previousBearing;
384
+ lastStep.lastStep = true;
385
+ lastStep.angle = diffAngle(lastStep.previousBearing, lastStep.nextBearing + Math.PI);
386
+ lastStep.nodes.push(lastNode);
387
+ this._steps.push(lastStep);
388
+
389
+ if (!currentStep) {
390
+ lastStep.firstStep = true;
391
+ }
392
+
393
+ const indexOfCurrentStep = this._steps.length - 1;
394
+ const numberOfNodesToFill = currentStep ? currentStep.nodes.length - 1 : 1;
395
+ for (let j = 0; j < numberOfNodesToFill; j++) {
396
+ this._nextStepsIndexes.push(indexOfCurrentStep);
397
+ }
398
+ } else {
399
+ currentStep.lastStep = true;
400
+ }
401
+
402
+ }
403
+
132
404
  }
133
405
  export default Itinerary;
@@ -0,0 +1,168 @@
1
+ /* eslint-disable max-statements */
2
+ import { expect } from 'chai';
3
+
4
+ import GraphRouter from './GraphRouter';
5
+
6
+ import {
7
+ network, nodes, itineraryStart, itineraryEnd, edges
8
+ } from '../../tests/CommonTest';
9
+ import Level from '../coordinates/Level';
10
+ import WGS84 from '../coordinates/WGS84';
11
+
12
+ describe('Itinerary', () => {
13
+
14
+ const router = new GraphRouter(network);
15
+ const itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
16
+
17
+ it('is readable', () => {
18
+
19
+ for (let i = 0; i < itinerary.nodes.length; i++) {
20
+ const node = itinerary.nodes[i];
21
+
22
+ if (i !== itinerary.nodes.length - 1) {
23
+
24
+ const reversed = itinerary._edgesDirectionReversed[i];
25
+ const firstNode = reversed ? itinerary.edges[i].node2 : itinerary.edges[i].node1;
26
+ const secondNode = reversed ? itinerary.edges[i].node1 : itinerary.edges[i].node2;
27
+
28
+ expect(firstNode).equal(node);
29
+ expect(secondNode).equal(itinerary.nodes[i + 1]);
30
+ }
31
+
32
+ }
33
+ });
34
+
35
+ it('verify getNextEdge() and getNextNode()', () => {
36
+
37
+ let currentNode, currentEdge;
38
+
39
+ // Iterate on nodes
40
+ currentNode = itinerary.nodes[0];
41
+ for (let i = 0; i < itinerary.nodes.length - 1; i++) {
42
+ currentEdge = itinerary.getNextEdge(currentNode);
43
+ expect(itinerary.edges.indexOf(currentEdge)).equal(i);
44
+
45
+ currentNode = itinerary.getNextNode(currentNode);
46
+ expect(itinerary.nodes.indexOf(currentNode)).equal(i + 1);
47
+ }
48
+ expect(itinerary.getNextNode(currentNode)).is.null;
49
+ expect(itinerary.getNextEdge(currentNode)).is.null;
50
+
51
+ // Iterate on edges
52
+ currentEdge = itinerary.edges[0];
53
+ for (let i = 0; i < itinerary.edges.length - 1; i++) {
54
+ currentNode = itinerary.getNextNode(currentEdge);
55
+ expect(itinerary.nodes.indexOf(currentNode)).equal(i + 1);
56
+
57
+ currentEdge = itinerary.getNextEdge(currentEdge);
58
+ expect(itinerary.edges.indexOf(currentEdge)).equal(i + 1);
59
+ }
60
+ const lastNode = itinerary.getNextNode(currentEdge);
61
+ expect(lastNode).is.not.null;
62
+ expect(itinerary.getNextNode(lastNode)).is.null;
63
+ expect(itinerary.getNextEdge(currentEdge)).is.null;
64
+ });
65
+
66
+ it('verify steps', () => {
67
+
68
+ const steps = itinerary.steps;
69
+ expect(steps.length).equal(10);
70
+
71
+ expect(steps[0].nodes[1]).equal(nodes[7]);
72
+ expect(steps[0].nodes[2]).equal(nodes[8]);
73
+
74
+ for (let i = 2; i <= 9; i++) {
75
+ expect(steps[i - 1].nodes[0]).equal(nodes[i + 6]);
76
+ expect(steps[i - 1].nodes[1]).equal(nodes[i + 7]);
77
+ }
78
+
79
+ expect(steps[9].nodes[0]).equal(nodes[16]);
80
+ });
81
+
82
+
83
+ it('verify next and previous steps', () => {
84
+
85
+ const {
86
+ steps, _nextStepsIndexes
87
+ } = itinerary;
88
+ const itineraryNodes = itinerary.nodes;
89
+ const itineraryEdges = itinerary.edges;
90
+
91
+ expect(_nextStepsIndexes.length).equal(itinerary.nodes.length);
92
+
93
+ expect(_nextStepsIndexes[0]).equal(0);
94
+ expect(_nextStepsIndexes[1]).equal(1);
95
+ for (let i = 2; i <= 10; i++) {
96
+ expect(_nextStepsIndexes[i]).equal(i - 1);
97
+ }
98
+
99
+ expect(itinerary.getNextStep(itineraryNodes[0])).equal(steps[0]);
100
+ expect(itinerary.getNextStep(itineraryNodes[1])).equal(steps[1]);
101
+ for (let i = 2; i <= 10; i++) {
102
+ expect(itinerary.getNextStep(itineraryNodes[i])).equal(steps[i - 1]);
103
+ }
104
+
105
+ expect(itinerary.getNextStep(itineraryEdges[0])).equal(steps[1]);
106
+ for (let i = 1; i <= 9; i++) {
107
+ expect(itinerary.getNextStep(itineraryEdges[i])).equal(steps[i]);
108
+ }
109
+
110
+
111
+ expect(itinerary.getPreviousStep(itineraryNodes[0])).is.null;
112
+ expect(itinerary.getPreviousStep(itineraryNodes[1])).equal(steps[0]);
113
+ for (let i = 3; i <= 10; i++) {
114
+ expect(itinerary.getPreviousStep(itineraryNodes[i])).equal(steps[i - 2]);
115
+ }
116
+
117
+ expect(itinerary.getPreviousStep(itineraryEdges[0])).equal(steps[0]);
118
+ for (let i = 2; i <= 9; i++) {
119
+ expect(itinerary.getPreviousStep(itineraryEdges[i])).equal(steps[i - 1]);
120
+ }
121
+
122
+ });
123
+
124
+ it('info', () => {
125
+ let itineraryInfo;
126
+ let currentPosition;
127
+
128
+ currentPosition = new WGS84(43.6092811, 3.8842406, null, new Level(2));
129
+ itineraryInfo = itinerary.getInfo(currentPosition);
130
+ expect(itineraryInfo.projection).is.not.null;
131
+ expect(itineraryInfo.projection).is.not.undefined;
132
+ expect(itineraryInfo.projection.projection).instanceOf(WGS84);
133
+ expect(itineraryInfo.projection.nearestElement).equal(edges[7]);
134
+ expect(itineraryInfo.remainingDistance).be.closeTo(30.35, 0.01);
135
+ expect(itineraryInfo.traveledDistance).be.closeTo(0.79, 0.01);
136
+ expect(itineraryInfo.remainingPercentage).be.closeTo(0.97, 0.01);
137
+ expect(itineraryInfo.traveledPercentage).be.closeTo(0.03, 0.01);
138
+ expect(itineraryInfo.previousStep).equal(itinerary.steps[0]);
139
+ expect(itineraryInfo.nextStep).equal(itinerary.steps[1]);
140
+
141
+ currentPosition = new WGS84(43.6092811, 3.8842406, null, null);
142
+ itineraryInfo = itinerary.getInfo(currentPosition);
143
+ expect(itineraryInfo).is.null;
144
+
145
+
146
+ currentPosition = new WGS84(43.6093476, 3.8841978, null, new Level(2));
147
+ itineraryInfo = itinerary.getInfo(currentPosition);
148
+ expect(itineraryInfo.projection.nearestElement).equal(edges[11]);
149
+ expect(itineraryInfo.remainingDistance).be.closeTo(17.37, 0.01);
150
+ expect(itineraryInfo.traveledDistance).be.closeTo(13.76, 0.01);
151
+ expect(itineraryInfo.remainingPercentage).be.closeTo(0.56, 0.01);
152
+ expect(itineraryInfo.traveledPercentage).be.closeTo(0.44, 0.01);
153
+ expect(itineraryInfo.previousStep).equal(itinerary.steps[4]);
154
+ expect(itineraryInfo.nextStep).equal(itinerary.steps[5]);
155
+
156
+
157
+ currentPosition = new WGS84(43.6093182, 3.8842847, null, new Level(1));
158
+ itineraryInfo = itinerary.getInfo(currentPosition);
159
+ expect(itineraryInfo.projection.nearestElement).equal(nodes[15]);
160
+ expect(itineraryInfo.remainingDistance).be.closeTo(5.02, 0.01);
161
+ expect(itineraryInfo.traveledDistance).be.closeTo(26.11, 0.01);
162
+ expect(itineraryInfo.remainingPercentage).be.closeTo(0.16, 0.01);
163
+ expect(itineraryInfo.traveledPercentage).be.closeTo(0.84, 0.01);
164
+ expect(itineraryInfo.previousStep).equal(itinerary.steps[7]);
165
+ expect(itineraryInfo.nextStep).equal(itinerary.steps[8]);
166
+ });
167
+
168
+ });
@@ -35,7 +35,7 @@ class MapMatching {
35
35
  this.network = network;
36
36
  }
37
37
 
38
- getProjection(location, useDistance = true, useBearing = true, useMultiLevelSegments = true, acceptEdgeFn = () => true) {
38
+ getProjection(location, useDistance = false, useBearing = false, useMultiLevelSegments = true, acceptEdgeFn = () => true) {
39
39
 
40
40
  if (!this.network) {
41
41
  return null;
@@ -49,6 +49,12 @@ class MapMatching {
49
49
  return null;
50
50
  }
51
51
 
52
+ const updateProjectionLevelFromEdge = (_edge, _projection) => {
53
+ if (_edge.level) {
54
+ _projection.level = _edge.level.clone();
55
+ }
56
+ };
57
+
52
58
  const projection = {};
53
59
  projection.origin = location;
54
60
  projection.distanceFromNearestElement = Number.MAX_VALUE;
@@ -83,6 +89,7 @@ class MapMatching {
83
89
  projection.distanceFromNearestElement = distNode1;
84
90
  projection.nearestElement = edge.node1;
85
91
  projection.projection = edge.node1.coords.clone();
92
+ updateProjectionLevelFromEdge(edge, projection.projection);
86
93
  }
87
94
 
88
95
  // Check node 2
@@ -92,12 +99,14 @@ class MapMatching {
92
99
  projection.distanceFromNearestElement = distNode2;
93
100
  projection.nearestElement = edge.node2;
94
101
  projection.projection = edge.node2.coords.clone();
102
+ updateProjectionLevelFromEdge(edge, projection.projection);
95
103
  }
96
104
  }
97
105
 
98
106
  // Check edge
99
107
  const segmentProjection = location.getProjection(edge);
100
108
  if (segmentProjection) {
109
+ updateProjectionLevelFromEdge(edge, segmentProjection);
101
110
  const distEdge = location.distanceTo(segmentProjection);
102
111
  if (distEdge < projection.distanceFromNearestElement
103
112
  && (!useDistance || distEdge <= this._maxDistance)) {