@wemap/osm 4.0.14 → 5.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 (32) hide show
  1. package/assets/itinerary-grenoble-otp-1.json +1536 -0
  2. package/assets/itinerary-grenoble-otp-2.json +1092 -0
  3. package/assets/itinerary-montpellier-outdoor-without-steps.json +110 -1
  4. package/assets/itinerary-montpellier-outdoor.json +513 -1
  5. package/index.js +21 -2
  6. package/package.json +6 -5
  7. package/src/routers/Itinerary.js +185 -0
  8. package/src/routers/Itinerary.type.spec.js +104 -0
  9. package/src/routers/Leg.js +101 -0
  10. package/src/routers/LevelChange.js +65 -0
  11. package/src/routers/RouterResponse.js +19 -0
  12. package/src/routers/RouterResponse.type.spec.js +24 -0
  13. package/src/routers/Step.js +113 -0
  14. package/src/routers/Utils.js +22 -0
  15. package/src/routers/custom/OsmNetworkUtils.js +194 -0
  16. package/src/{network → routers/custom}/OsmNetworkUtils.spec.js +13 -34
  17. package/src/routers/custom/OsmRouter.js +27 -0
  18. package/src/routers/custom/OsmRouter.spec.js +222 -0
  19. package/src/routers/custom/OsmRouterOptions.js +33 -0
  20. package/src/routers/custom/OsmRouterUtils.js +46 -0
  21. package/src/routers/custom/StepsGeneration.js +104 -0
  22. package/src/routers/info/ItineraryInfo.js +34 -0
  23. package/src/routers/info/ItineraryInfoManager.js +142 -0
  24. package/src/routers/info/ItineraryInfoManager.spec.js +54 -0
  25. package/src/routers/osrm/OsrmUtils.js +273 -0
  26. package/src/routers/osrm/OsrmUtils.spec.js +431 -0
  27. package/src/routers/otp/OtpUtils.js +172 -0
  28. package/src/routers/otp/OtpUtils.spec.js +97 -0
  29. package/src/network/OsmNetworkUtils.js +0 -163
  30. package/src/network/OsmRouter.spec.js +0 -174
  31. package/src/osrm/OsrmUtils.js +0 -284
  32. package/src/osrm/OsrmUtils.spec.js +0 -384
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/osm"
12
12
  },
13
13
  "name": "@wemap/osm",
14
- "version": "4.0.14",
14
+ "version": "5.0.0",
15
15
  "bugs": {
16
16
  "url": "https://github.com/wemap/wemap-modules-js/issues"
17
17
  },
@@ -25,10 +25,11 @@
25
25
  ],
26
26
  "license": "ISC",
27
27
  "dependencies": {
28
- "@wemap/geo": "^4.0.14",
29
- "@wemap/logger": "^4.0.0",
30
- "@wemap/maths": "^4.0.3",
28
+ "@mapbox/polyline": "^1.1.1",
29
+ "@wemap/geo": "^5.0.0",
30
+ "@wemap/logger": "^5.0.0",
31
+ "@wemap/maths": "^5.0.0",
31
32
  "sax": "^1.2.4"
32
33
  },
33
- "gitHead": "0b7188383438fcb1dd498dd117799076115dfee6"
34
+ "gitHead": "73607e294f29d3f9a41673bfc25ad9489a1da7f4"
34
35
  }
@@ -0,0 +1,185 @@
1
+ /* eslint-disable max-statements */
2
+ import { Coordinates, Network } from '@wemap/geo';
3
+ import Leg from './Leg.js';
4
+
5
+ /**
6
+ * Main attributes are:
7
+ * nodes: the ordered list of Node
8
+ * edges: the ordered list of Edge
9
+ * start: the start point (Coordinates)
10
+ * end: the end point (Coordinates)
11
+ * length: the route length
12
+ */
13
+ class Itinerary {
14
+
15
+ /** @type {!Coordinates} */
16
+ from;
17
+
18
+ /** @type {!Coordinates} */
19
+ to;
20
+
21
+ /** @type {!number} */
22
+ distance;
23
+
24
+ /** @type {!number} */
25
+ duration;
26
+
27
+ /** @type {?number} */
28
+ startTime = null;
29
+
30
+ /** @type {?number} */
31
+ endTime = null;
32
+
33
+ /** @type {!(Leg[])} */
34
+ legs = [];
35
+
36
+ /** @type {?Coordinates[]} */
37
+ _coords = null;
38
+
39
+ set coords(_) {
40
+ throw new Error('Itinerary.coords cannot be set. They are calculated from Itinerary.legs.');
41
+ }
42
+
43
+ /** @type {!(Coordinates[])} */
44
+ get coords() {
45
+ if (!this._coords) {
46
+ // Returns the coordinates contained in all legs and remove duplicates between array
47
+ this._coords = this.legs.reduce((acc, val) => {
48
+ const isDuplicate = acc.length && val.coords.length && acc[acc.length - 1].equalsTo(val.coords[0]);
49
+ acc.push(...val.coords.slice(isDuplicate ? 1 : 0));
50
+ return acc;
51
+ }, []);
52
+ }
53
+ return this._coords;
54
+ }
55
+
56
+ /**
57
+ * @returns {Network}
58
+ */
59
+ toNetwork() {
60
+ return Network.fromCoordinates([this.coords]);
61
+ }
62
+
63
+ /**
64
+ * @param {Itinerary[]} itineraries
65
+ * @returns {Itinerary}
66
+ */
67
+ static fromItineraries(...itineraries) {
68
+ const itinerary = new Itinerary();
69
+ itinerary.from = itineraries[0].from;
70
+ itinerary.to = itineraries[itineraries.length - 1].to;
71
+ itinerary.distance = 0;
72
+ itinerary.duration = 0;
73
+ itinerary.legs = [];
74
+
75
+ itineraries.forEach(_itinerary => {
76
+ itinerary.distance += _itinerary.distance;
77
+ itinerary.duration += _itinerary.duration;
78
+ itinerary.legs.push(..._itinerary.legs);
79
+ itinerary.legs.forEach(leg => {
80
+ leg.steps[0].firstStep = false;
81
+ leg.steps[leg.steps.length - 1].lastStep = false;
82
+ });
83
+ });
84
+
85
+ itinerary.legs[0].steps[0].firstStep = true;
86
+ const lastLeg = itinerary.legs[itinerary.legs.length - 1];
87
+ lastLeg.steps[lastLeg.steps.length - 1].lastStep = true;
88
+
89
+ return itinerary;
90
+ }
91
+
92
+ /**
93
+ * Convert lat/lng/level points to Itinerary
94
+ * @param {number[][]} points 2D points array of lat/lng/level (level is optional)
95
+ * @param {Coordinates} from
96
+ * @param {Coordinates} to
97
+ * @param {string} mode
98
+ * @returns {Itinerary}
99
+ */
100
+ static fromOrderedPointsArray(points, start, end) {
101
+
102
+ const pointToCoordinates = point => new Coordinates(point[0], point[1], null, point[2]);
103
+
104
+ return this.fromOrderedCoordinates(
105
+ points.map(pointToCoordinates),
106
+ pointToCoordinates(start),
107
+ pointToCoordinates(end)
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Convert ordered Coordinates to Itinerary
113
+ * @param {Coordinates[]} points
114
+ * @param {Coordinates} from
115
+ * @param {Coordinates} to
116
+ * @param {string} mode
117
+ * @returns {Itinerary}
118
+ */
119
+ static fromOrderedCoordinates(points, from, to, mode = 'WALK') {
120
+
121
+ const itinerary = new Itinerary();
122
+ itinerary.from = from;
123
+ itinerary.to = to;
124
+
125
+ const leg = new Leg();
126
+ leg.mode = mode;
127
+ leg.from = from;
128
+ leg.to = to;
129
+
130
+ leg.coords = points;
131
+ leg.distance = points.reduce((acc, coords, idx, arr) => {
132
+ if (idx !== 0) {
133
+ return acc + arr[idx - 1].distanceTo(coords);
134
+ }
135
+ return acc;
136
+ }, 0);
137
+ leg.duration = leg.distance;
138
+
139
+ itinerary.distance = leg.distance;
140
+ itinerary.duration = leg.duration;
141
+
142
+ return itinerary;
143
+ }
144
+
145
+ /**
146
+ * @returns {object}
147
+ */
148
+ toJson() {
149
+ const output = {
150
+ from: this.from.toCompressedJson(),
151
+ to: this.to.toCompressedJson(),
152
+ distance: this.distance,
153
+ duration: this.duration,
154
+ legs: this.legs.map(leg => leg.toJson())
155
+ };
156
+ if (this.startTime !== null) {
157
+ output.startTime = this.startTime;
158
+ }
159
+ if (this.endTime !== null) {
160
+ output.endTime = this.endTime;
161
+ }
162
+ return output;
163
+ }
164
+
165
+ /**
166
+ * @param {object} json
167
+ * @returns {Itinerary}
168
+ */
169
+ static fromJson(json) {
170
+ const itinerary = new Itinerary();
171
+ itinerary.from = Coordinates.fromCompressedJson(json.from);
172
+ itinerary.to = Coordinates.fromCompressedJson(json.to);
173
+ itinerary.distance = json.distance;
174
+ itinerary.duration = json.duration;
175
+ itinerary.legs = json.legs.map(Leg.fromJson);
176
+ if (json.startTime) {
177
+ itinerary.startTime = json.startTime;
178
+ }
179
+ if (json.endTime) {
180
+ itinerary.endTime = json.endTime;
181
+ }
182
+ return itinerary;
183
+ }
184
+ }
185
+ export default Itinerary;
@@ -0,0 +1,104 @@
1
+ import chai from 'chai';
2
+
3
+ import { Coordinates } from '@wemap/geo';
4
+
5
+ import LevelChange from './LevelChange.js';
6
+ import Leg from './Leg.js';
7
+ import Itinerary from './Itinerary.js';
8
+ import Step from './Step.js';
9
+
10
+ const { expect } = chai;
11
+
12
+
13
+ const isNullOrNumber = val => val === null || typeof val === 'number';
14
+ const isNullOrString = val => val === null || typeof val === 'string';
15
+ const isNullOrObject = val => val === null || typeof val === 'object';
16
+ const isNullOrArray = val => val === null || Array.isArray(val);
17
+ const isNullOrLevelChange = val => val === null || val instanceof LevelChange;
18
+
19
+ const isUndefinedOrBoolean = val => typeof val === 'undefined' || typeof val === 'boolean';
20
+ const isUndefinedOrString = val => typeof val === 'undefined' || typeof val === 'string';
21
+
22
+ const stepExtraProperties = ['subwayEntrance', 'subwayEntranceRef'];
23
+
24
+ /**
25
+ * @param {Step} step
26
+ */
27
+ export function verifyStepData(step) {
28
+
29
+ expect(step).instanceOf(Step);
30
+ expect(step.firstStep).be.a('boolean');
31
+ expect(step.lastStep).be.a('boolean');
32
+ expect(step.number).be.a('number');
33
+ expect(step.coords).instanceOf(Coordinates);
34
+ expect(step.angle).be.a('number');
35
+ expect(step.previousBearing).be.a('number');
36
+ expect(step.nextBearing).be.a('number');
37
+ expect(step.distance).be.a('number');
38
+ expect(step.duration).satisfies(isNullOrNumber);
39
+ expect(step.name).satisfies(isNullOrString);
40
+ expect(step.levelChange).satisfies(isNullOrLevelChange);
41
+
42
+ expect(step.extras).satisfies(isNullOrObject);
43
+ if (step.extras !== null) {
44
+ for (const key of Object.keys(step.extras)) {
45
+ expect(stepExtraProperties).includes(key);
46
+ }
47
+ expect(step.extras.subwayEntrance).satisfies(isUndefinedOrBoolean);
48
+ expect(step.extras.subwayEntranceRef).satisfies(isUndefinedOrString);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @param {Leg} routerResponse
54
+ */
55
+ export function verifyLegData(leg) {
56
+
57
+ expect(leg).instanceOf(Leg);
58
+ expect(leg.mode).be.a('string');
59
+ expect(leg.distance).be.a('number');
60
+ expect(leg.duration).be.a('number');
61
+ expect(leg.startTime).satisfies(isNullOrNumber);
62
+ expect(leg.endTime).satisfies(isNullOrNumber);
63
+ expect(leg.from).be.an('object');
64
+ expect(leg.from.name).satisfies(isNullOrString);
65
+ expect(leg.from.coords).instanceOf(Coordinates);
66
+ expect(leg.to).be.an('object');
67
+ expect(leg.to.name).satisfies(isNullOrString);
68
+ expect(leg.to.coords).instanceOf(Coordinates);
69
+ expect(leg.coords).be.an('array');
70
+ leg.coords.forEach(coords => expect(coords).instanceOf(Coordinates));
71
+
72
+ expect(leg.transportInfo).satisfies(isNullOrObject);
73
+ if (leg.transportInfo !== null) {
74
+ expect(leg.transportInfo.name).be.a('string');
75
+ expect(leg.transportInfo.routeColor).satisfies(isNullOrString);
76
+ expect(leg.transportInfo.routeTextColor).satisfies(isNullOrString);
77
+ expect(leg.transportInfo.directionName).satisfies(isNullOrString);
78
+ }
79
+ expect(leg.steps).satisfies(isNullOrArray);
80
+ if (leg.steps !== null) {
81
+ leg.steps.forEach(verifyStepData);
82
+ }
83
+ }
84
+
85
+
86
+ /**
87
+ * @param {Itinerary} itinerary
88
+ */
89
+ export function verifyItineraryData(itinerary) {
90
+
91
+ expect(itinerary).instanceOf(Itinerary);
92
+ expect(itinerary.from).instanceOf(Coordinates);
93
+ expect(itinerary.to).instanceOf(Coordinates);
94
+ expect(itinerary.coords).be.an('array');
95
+ for (const coords of itinerary.coords) {
96
+ expect(coords).instanceOf(Coordinates);
97
+ }
98
+ expect(itinerary.distance).be.a('number');
99
+ expect(itinerary.duration).be.a('number');
100
+ expect(itinerary.startTime).satisfies(isNullOrNumber);
101
+ expect(itinerary.endTime).satisfies(isNullOrNumber);
102
+ expect(itinerary.legs).be.an('array');
103
+ itinerary.legs.forEach(verifyLegData);
104
+ }
@@ -0,0 +1,101 @@
1
+ import { Coordinates, Network } from '@wemap/geo';
2
+
3
+ import Step from './Step.js';
4
+
5
+ class Leg {
6
+
7
+ /** @type {!string} can be WALK, BIKE, BUS, TRAM, CAR */
8
+ mode;
9
+
10
+ /** @type {!number} */
11
+ distance;
12
+
13
+ /** @type {!number} */
14
+ duration;
15
+
16
+ /** @type {?number} */
17
+ startTime = null;
18
+
19
+ /** @type {?number} */
20
+ endTime = null;
21
+
22
+ /** @type {!{name: ?string, coords: !Coordinates}} */
23
+ from;
24
+
25
+ /** @type {!{name: ?string, coords: !Coordinates}} */
26
+ to;
27
+
28
+ /** @type {!Coordinates[]} */
29
+ coords;
30
+
31
+ /** @type {?{name: !string, routeColor: ?string, routeTextColor: ?string, directionName: ?string}} */
32
+ transportInfo = null;
33
+
34
+ /** @type {?(Step[])} */
35
+ steps = null;
36
+
37
+ /**
38
+ * @returns {Network}
39
+ */
40
+ toNetwork() {
41
+ return Network.fromCoordinates([this.coords]);
42
+ }
43
+
44
+ /**
45
+ * @returns {object}
46
+ */
47
+ toJson() {
48
+ const output = {
49
+ mode: this.mode,
50
+ from: this.from.toCompressedJson(),
51
+ to: this.to.toCompressedJson(),
52
+ distance: this.distance,
53
+ duration: this.duration,
54
+ coords: this.coords.map(coords => coords.toCompressedJson())
55
+ };
56
+ if (this.startTime !== null) {
57
+ output.startTime = this.startTime;
58
+ }
59
+ if (this.endTime !== null) {
60
+ output.endTime = this.endTime;
61
+ }
62
+ if (this.transportInfo !== null) {
63
+ output.transportInfo = this.transportInfo;
64
+ }
65
+ if (this.steps !== null && this.steps.length > 0) {
66
+ output.steps = this.steps.map(step => step.toJson());
67
+ }
68
+ return output;
69
+ }
70
+
71
+
72
+ /**
73
+ * @param {object} json
74
+ * @returns {Leg}
75
+ */
76
+ static fromJson(json) {
77
+ const leg = new Leg();
78
+ leg.mode = json.mode;
79
+ leg.from = Coordinates.fromCompressedJson(json.from);
80
+ leg.to = Coordinates.fromCompressedJson(json.to);
81
+ leg.distance = json.distance;
82
+ leg.duration = json.duration;
83
+ leg.coords = json.coords.map(Coordinates.fromCompressedJson);
84
+ if (json.startTime) {
85
+ leg.startTime = json.startTime;
86
+ }
87
+ if (json.endTime) {
88
+ leg.endTime = json.endTime;
89
+ }
90
+ if (json.transportInfo) {
91
+ leg.transportInfo = json.transportInfo;
92
+ }
93
+ if (json.steps) {
94
+ leg.steps = json.steps.map(Step.fromJson);
95
+ }
96
+ return leg;
97
+ }
98
+
99
+ }
100
+
101
+ export default Leg;
@@ -0,0 +1,65 @@
1
+ import { Level, GraphNode, GraphUtils } from '@wemap/geo';
2
+
3
+ import OsmElement from '../model/OsmElement.js';
4
+
5
+ class LevelChange {
6
+
7
+ /** @type {!string} [up|down] */
8
+ direction;
9
+
10
+ /** @type {!number} [-2, -1, 1, ...] */
11
+ difference;
12
+
13
+ /** @type {?string} [elevator|conveyor|stairs] */
14
+ type = null;
15
+
16
+ /**
17
+ * @param {GraphNode<OsmElement>} firstNode
18
+ * @param {GraphNode<OsmElement>} secondNode
19
+ * @returns {LevelChange}
20
+ */
21
+ static fromTwoNodes(firstNode, secondNode) {
22
+
23
+ const levelChange = new LevelChange();
24
+
25
+ const edge = GraphUtils.getEdgeByNodes(firstNode.edges, firstNode, secondNode);
26
+
27
+ if (edge.builtFrom.isElevator) {
28
+ levelChange.type = 'elevator';
29
+ } else if (edge.builtFrom.isConveying) {
30
+ levelChange.type = 'conveyor';
31
+ } else if (edge.builtFrom.areStairs) {
32
+ levelChange.type = 'stairs';
33
+ }
34
+
35
+ levelChange.difference = Level.diff(firstNode.coords.level, secondNode.coords.level);
36
+ levelChange.direction = levelChange.difference > 0 ? 'up' : 'down';
37
+
38
+ return levelChange;
39
+ }
40
+
41
+ /**
42
+ * @returns {object}
43
+ */
44
+ toJson() {
45
+ return {
46
+ direction: this.direction,
47
+ difference: this.difference,
48
+ type: this.type
49
+ };
50
+ }
51
+
52
+ /**
53
+ * @param {object} json
54
+ * @returns {LevelChange}
55
+ */
56
+ static fromJson(json) {
57
+ const levelChange = new LevelChange();
58
+ levelChange.direction = json.direction;
59
+ levelChange.difference = json.difference;
60
+ levelChange.type = json.type;
61
+ return levelChange;
62
+ }
63
+ }
64
+
65
+ export default LevelChange;
@@ -0,0 +1,19 @@
1
+ import { Coordinates } from '@wemap/geo';
2
+
3
+ import Itinerary from './Itinerary.js';
4
+ class RouterResponse {
5
+
6
+ /** @type {!string} */
7
+ routerName;
8
+
9
+ /** @type {!Coordinates} */
10
+ from;
11
+
12
+ /** @type {!Coordinates} */
13
+ to;
14
+
15
+ /** @type {!(Itinerary[])} */
16
+ itineraries = [];
17
+ }
18
+
19
+ export default RouterResponse;
@@ -0,0 +1,24 @@
1
+ import chai from 'chai';
2
+
3
+ import { Coordinates } from '@wemap/geo';
4
+
5
+ import RouterResponse from './RouterResponse.js';
6
+
7
+ import { verifyItineraryData } from './Itinerary.type.spec.js';
8
+
9
+ const { expect } = chai;
10
+
11
+ /**
12
+ * @param {RouterResponse} routerResponse
13
+ */
14
+ export function verifyRouterResponseData(routerResponse) {
15
+
16
+ expect(routerResponse).instanceOf(RouterResponse);
17
+ expect(routerResponse.routerName).be.a('string');
18
+ expect(routerResponse.from).instanceOf(Coordinates);
19
+ expect(routerResponse.to).instanceOf(Coordinates);
20
+ expect(routerResponse.itineraries).be.an('array');
21
+ routerResponse.itineraries.forEach(verifyItineraryData);
22
+
23
+ }
24
+
@@ -0,0 +1,113 @@
1
+ import { Coordinates } from '@wemap/geo';
2
+
3
+ import LevelChange from './LevelChange.js';
4
+
5
+ class Step {
6
+
7
+ /** @type {!boolean} */
8
+ firstStep = false;
9
+
10
+ /** @type {!boolean} */
11
+ lastStep = false;
12
+
13
+ /** @type {!number} */
14
+ number;
15
+
16
+ /** @type {!Coordinates} */
17
+ coords = [];
18
+
19
+
20
+ /** @type {!number} */
21
+ angle;
22
+
23
+ /** @type {!number} */
24
+ previousBearing;
25
+
26
+ /** @type {!number} */
27
+ nextBearing;
28
+
29
+
30
+ /** @type {!number} */
31
+ distance;
32
+
33
+ /** @type {?number} */
34
+ duration = null;
35
+
36
+ /** @type {?string} */
37
+ name = null;
38
+
39
+
40
+ /** @type {?LevelChange} */
41
+ levelChange = null;
42
+
43
+ /** @type {?{?subwayEntrance: boolean, ?subwayEntranceRef: string}} */
44
+ extras = {};
45
+
46
+ /**
47
+ * @returns {object}
48
+ */
49
+ toJson() {
50
+ const output = {
51
+ number: this.number,
52
+ coords: this.coords.toCompressedJson(),
53
+ angle: this.angle,
54
+ previousBearing: this.previousBearing,
55
+ nextBearing: this.nextBearing,
56
+ distance: this.distance
57
+ };
58
+ if (this.firstStep) {
59
+ output.firstStep = true;
60
+ }
61
+ if (this.lastStep) {
62
+ output.lastStep = true;
63
+ }
64
+ if (this.duration !== null) {
65
+ output.duration = this.duration;
66
+ }
67
+ if (this.name !== null) {
68
+ output.name = this.name;
69
+ }
70
+ if (this.levelChange !== null) {
71
+ output.levelChange = this.levelChange.toJson();
72
+ }
73
+ if (this.extras !== null) {
74
+ output.extras = this.extras;
75
+ }
76
+ return output;
77
+ }
78
+
79
+ /**
80
+ * @param {object} json
81
+ * @returns {Step}
82
+ */
83
+ static fromJson(json) {
84
+ const step = new Step();
85
+ step.number = json.number;
86
+ step.coords = Coordinates.fromCompressedJson(json.coords);
87
+ step.angle = json.angle;
88
+ step.previousBearing = json.previousBearing;
89
+ step.nextBearing = json.nextBearing;
90
+ step.distance = json.distance;
91
+ if (json.firstStep) {
92
+ step.firstStep = json.firstStep;
93
+ }
94
+ if (json.lastStep) {
95
+ step.lastStep = json.lastStep;
96
+ }
97
+ if (json.duration) {
98
+ step.duration = json.duration;
99
+ }
100
+ if (json.name) {
101
+ step.name = json.name;
102
+ }
103
+ if (json.levelChange) {
104
+ step.levelChange = LevelChange.fromJson(json.levelChange);
105
+ }
106
+ if (json.extras) {
107
+ step.extras = json.extras;
108
+ }
109
+ return step;
110
+ }
111
+ }
112
+
113
+ export default Step;
@@ -0,0 +1,22 @@
1
+ import Itinerary from './Itinerary.js';
2
+
3
+ /**
4
+ * @param {Itinerary} itinerary
5
+ */
6
+ export function generateStepsNumbers(itinerary) {
7
+ let counter = 1;
8
+ itinerary.legs.forEach((leg, legId) => {
9
+ leg.steps.forEach((step, stepId) => {
10
+
11
+ if (counter === 1) {
12
+ step.firstStep = true;
13
+ }
14
+ if (legId === itinerary.legs.length - 1
15
+ && stepId === leg.steps.length - 1) {
16
+ step.lastStep = true;
17
+ }
18
+
19
+ step.number = counter++;
20
+ });
21
+ });
22
+ }