@opentripplanner/core-utils 14.3.0 → 14.3.2

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.
package/lib/itinerary.js CHANGED
@@ -1,385 +1,387 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getLegRouteLongName = exports.getLegRouteShortName = exports.convertGraphQLResponseToLegacy = exports.getItineraryCost = exports.getLegCost = exports.zeroDollars = exports.descope = exports.getDisplayedStopId = exports.calculateEmissions = exports.calculateTncFares = exports.calculatePhysicalActivity = exports.getTNCLocation = exports.getCompaniesLabelFromNetworks = exports.getCompanyForNetwork = exports.getTextWidth = exports.getElevationProfile = exports.mapOldElevationComponentToNew = exports.legElevationAtDistance = exports.legLocationAtDistance = exports.getLegBounds = exports.getItineraryBounds = exports.getCompanyFromLeg = exports.toSentenceCase = exports.getMapColor = exports.hasRental = exports.hasHail = exports.hasMicromobility = exports.hasBike = exports.hasCar = exports.hasTransit = exports.isAccessMode = exports.isMicromobility = exports.isCar = exports.isBicycleRent = exports.isBicycle = exports.isWalk = exports.isRideshareLeg = exports.isFlex = exports.legDropoffRequiresAdvanceBooking = exports.isAdvanceBookingRequired = exports.legContainsGeometry = exports.startsWithGeometry = exports.endsWithGeometry = exports.containsGeometry = exports.isCoordinationRequired = exports.isReservationRequired = exports.isTransit = exports.isTransitLeg = exports.getTransitModes = exports.transitModes = void 0;
7
- exports.getLegRouteName = void 0;
8
- const polyline_1 = __importDefault(require("@mapbox/polyline"));
9
- const along_1 = __importDefault(require("@turf/along"));
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.calculateEmissions = calculateEmissions;
8
+ exports.calculatePhysicalActivity = calculatePhysicalActivity;
9
+ exports.calculateTncFares = calculateTncFares;
10
+ exports.containsGeometry = containsGeometry;
11
+ exports.descope = exports.convertGraphQLResponseToLegacy = void 0;
12
+ exports.endsWithGeometry = endsWithGeometry;
13
+ exports.getCompaniesLabelFromNetworks = getCompaniesLabelFromNetworks;
14
+ exports.getCompanyForNetwork = getCompanyForNetwork;
15
+ exports.getCompanyFromLeg = getCompanyFromLeg;
16
+ exports.getDisplayedStopId = getDisplayedStopId;
17
+ exports.getElevationProfile = getElevationProfile;
18
+ exports.getItineraryBounds = getItineraryBounds;
19
+ exports.getItineraryCost = getItineraryCost;
20
+ exports.getLegBounds = getLegBounds;
21
+ exports.getLegCost = getLegCost;
22
+ exports.getLegRouteShortName = exports.getLegRouteName = exports.getLegRouteLongName = void 0;
23
+ exports.getMapColor = getMapColor;
24
+ exports.getTNCLocation = getTNCLocation;
25
+ exports.getTextWidth = getTextWidth;
26
+ exports.getTransitModes = getTransitModes;
27
+ exports.hasBike = hasBike;
28
+ exports.hasCar = hasCar;
29
+ exports.hasHail = hasHail;
30
+ exports.hasMicromobility = hasMicromobility;
31
+ exports.hasRental = hasRental;
32
+ exports.hasTransit = hasTransit;
33
+ exports.isAccessMode = isAccessMode;
34
+ exports.isAdvanceBookingRequired = isAdvanceBookingRequired;
35
+ exports.isBicycle = isBicycle;
36
+ exports.isBicycleRent = isBicycleRent;
37
+ exports.isCar = isCar;
38
+ exports.isCoordinationRequired = isCoordinationRequired;
39
+ exports.isFlex = isFlex;
40
+ exports.isMicromobility = isMicromobility;
41
+ exports.isReservationRequired = isReservationRequired;
42
+ exports.isRideshareLeg = isRideshareLeg;
43
+ exports.isTransit = isTransit;
44
+ exports.isTransitLeg = isTransitLeg;
45
+ exports.isWalk = isWalk;
46
+ exports.legContainsGeometry = legContainsGeometry;
47
+ exports.legDropoffRequiresAdvanceBooking = legDropoffRequiresAdvanceBooking;
48
+ exports.legElevationAtDistance = legElevationAtDistance;
49
+ exports.legLocationAtDistance = legLocationAtDistance;
50
+ exports.mapOldElevationComponentToNew = mapOldElevationComponentToNew;
51
+ exports.startsWithGeometry = startsWithGeometry;
52
+ exports.toSentenceCase = toSentenceCase;
53
+ exports.zeroDollars = exports.transitModes = void 0;
54
+ var _polyline = _interopRequireDefault(require("@mapbox/polyline"));
55
+ var _along = _interopRequireDefault(require("@turf/along"));
10
56
  // All OTP transit modes
11
- exports.transitModes = [
12
- "TRAM",
13
- "TROLLEYBUS",
14
- "BUS",
15
- "SUBWAY",
16
- "FERRY",
17
- "RAIL",
18
- "GONDOLA"
19
- ];
57
+ const transitModes = exports.transitModes = ["TRAM", "TROLLEYBUS", "BUS", "SUBWAY", "FERRY", "RAIL", "GONDOLA"];
58
+
20
59
  /**
21
60
  * @param {config} config OTP-RR configuration object
22
61
  * @return {Array} List of all transit modes defined in config; otherwise default mode list
23
62
  */
63
+
24
64
  function getTransitModes(config) {
25
- if (!config || !config.modes || !config.modes.transitModes)
26
- return exports.transitModes;
27
- return config.modes.transitModes.map(tm => typeof tm !== "string" ? tm.mode : tm);
65
+ if (!config || !config.modes || !config.modes.transitModes) return transitModes;
66
+ return config.modes.transitModes.map(tm => typeof tm !== "string" ? tm.mode : tm);
28
67
  }
29
- exports.getTransitModes = getTransitModes;
30
68
  function isTransitLeg(leg) {
31
- return leg.transitLeg;
69
+ return leg.transitLeg;
32
70
  }
33
- exports.isTransitLeg = isTransitLeg;
34
71
  function isTransit(mode) {
35
- return exports.transitModes.includes(mode) || mode === "TRANSIT";
72
+ return transitModes.includes(mode) || mode === "TRANSIT";
36
73
  }
37
- exports.isTransit = isTransit;
74
+
38
75
  /**
39
76
  * Returns true if the leg pickup rules enabled which require
40
77
  * calling ahead for the service to run. "mustPhone" is the only
41
78
  * property which encodes this info.
42
79
  */
43
80
  function isReservationRequired(leg) {
44
- return (leg === null || leg === void 0 ? void 0 : leg.boardRule) === "mustPhone" || (leg === null || leg === void 0 ? void 0 : leg.alightRule) === "mustPhone";
81
+ return (leg === null || leg === void 0 ? void 0 : leg.boardRule) === "mustPhone" || (leg === null || leg === void 0 ? void 0 : leg.alightRule) === "mustPhone";
45
82
  }
46
- exports.isReservationRequired = isReservationRequired;
47
83
  /**
48
84
  * Returns true if a user must ask the driver to let the user off
49
85
  * or if the user must flag the driver down for pickup.
50
86
  * "coordinateWithDriver" in board/alight rule encodes this info.
51
87
  */
52
88
  function isCoordinationRequired(leg) {
53
- return ((leg === null || leg === void 0 ? void 0 : leg.boardRule) === "coordinateWithDriver" ||
54
- (leg === null || leg === void 0 ? void 0 : leg.alightRule) === "coordinateWithDriver");
89
+ return (leg === null || leg === void 0 ? void 0 : leg.boardRule) === "coordinateWithDriver" || (leg === null || leg === void 0 ? void 0 : leg.alightRule) === "coordinateWithDriver";
55
90
  }
56
- exports.isCoordinationRequired = isCoordinationRequired;
57
91
  function containsGeometry(place) {
58
- var _a, _b;
59
- return (((_a = place === null || place === void 0 ? void 0 : place.stop) === null || _a === void 0 ? void 0 : _a.geometries) !== null && ((_b = place === null || place === void 0 ? void 0 : place.stop) === null || _b === void 0 ? void 0 : _b.geometries) !== undefined);
92
+ var _place$stop, _place$stop2;
93
+ return (place === null || place === void 0 || (_place$stop = place.stop) === null || _place$stop === void 0 ? void 0 : _place$stop.geometries) !== null && (place === null || place === void 0 || (_place$stop2 = place.stop) === null || _place$stop2 === void 0 ? void 0 : _place$stop2.geometries) !== undefined;
60
94
  }
61
- exports.containsGeometry = containsGeometry;
62
95
  function endsWithGeometry(leg) {
63
- return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.to);
96
+ return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.to);
64
97
  }
65
- exports.endsWithGeometry = endsWithGeometry;
66
98
  function startsWithGeometry(leg) {
67
- return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.from);
99
+ return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.from);
68
100
  }
69
- exports.startsWithGeometry = startsWithGeometry;
70
101
  function legContainsGeometry(leg) {
71
- return endsWithGeometry(leg) || startsWithGeometry(leg);
102
+ return endsWithGeometry(leg) || startsWithGeometry(leg);
72
103
  }
73
- exports.legContainsGeometry = legContainsGeometry;
74
104
  function isAdvanceBookingRequired(info) {
75
- var _a;
76
- return ((_a = info === null || info === void 0 ? void 0 : info.latestBookingTime) === null || _a === void 0 ? void 0 : _a.daysPrior) > 0;
105
+ var _info$latestBookingTi;
106
+ return (info === null || info === void 0 || (_info$latestBookingTi = info.latestBookingTime) === null || _info$latestBookingTi === void 0 ? void 0 : _info$latestBookingTi.daysPrior) > 0;
77
107
  }
78
- exports.isAdvanceBookingRequired = isAdvanceBookingRequired;
79
108
  function legDropoffRequiresAdvanceBooking(leg) {
80
- return isAdvanceBookingRequired(leg === null || leg === void 0 ? void 0 : leg.dropOffBookingInfo);
109
+ return isAdvanceBookingRequired(leg === null || leg === void 0 ? void 0 : leg.dropOffBookingInfo);
81
110
  }
82
- exports.legDropoffRequiresAdvanceBooking = legDropoffRequiresAdvanceBooking;
111
+
83
112
  /**
84
113
  * The two rules checked by the above two functions are the only values
85
114
  * returned by OTP when a leg is a flex leg.
86
115
  */
87
116
  function isFlex(leg) {
88
- var _a;
89
- return (((_a = leg === null || leg === void 0 ? void 0 : leg.stopCalls) === null || _a === void 0 ? void 0 : _a.some(call => { var _a;
90
- // Flex calls are "Location" or "LocationGroup"
91
- // eslint-disable-next-line no-underscore-dangle
92
- return (_a = call === null || call === void 0 ? void 0 : call.stopLocation) === null || _a === void 0 ? void 0 : _a.__typename.startsWith("Location"); })) || false);
117
+ var _leg$stopCalls;
118
+ return (leg === null || leg === void 0 || (_leg$stopCalls = leg.stopCalls) === null || _leg$stopCalls === void 0 ? void 0 : _leg$stopCalls.some(call => {
119
+ var _call$stopLocation;
120
+ return (// Flex calls are "Location" or "LocationGroup"
121
+ // eslint-disable-next-line no-underscore-dangle
122
+ call === null || call === void 0 || (_call$stopLocation = call.stopLocation) === null || _call$stopLocation === void 0 ? void 0 : _call$stopLocation.__typename.startsWith("Location")
123
+ );
124
+ })) || false;
93
125
  }
94
- exports.isFlex = isFlex;
95
126
  function isRideshareLeg(leg) {
96
- var _a, _b;
97
- return !!((_b = (_a = leg.rideHailingEstimate) === null || _a === void 0 ? void 0 : _a.provider) === null || _b === void 0 ? void 0 : _b.id);
127
+ var _leg$rideHailingEstim;
128
+ return !!((_leg$rideHailingEstim = leg.rideHailingEstimate) !== null && _leg$rideHailingEstim !== void 0 && (_leg$rideHailingEstim = _leg$rideHailingEstim.provider) !== null && _leg$rideHailingEstim !== void 0 && _leg$rideHailingEstim.id);
98
129
  }
99
- exports.isRideshareLeg = isRideshareLeg;
100
130
  function isWalk(mode) {
101
- if (!mode)
102
- return false;
103
- return mode === "WALK";
131
+ if (!mode) return false;
132
+ return mode === "WALK";
104
133
  }
105
- exports.isWalk = isWalk;
106
134
  function isBicycle(mode) {
107
- if (!mode)
108
- return false;
109
- return mode === "BICYCLE";
135
+ if (!mode) return false;
136
+ return mode === "BICYCLE";
110
137
  }
111
- exports.isBicycle = isBicycle;
112
138
  function isBicycleRent(mode) {
113
- if (!mode)
114
- return false;
115
- return mode === "BICYCLE_RENT";
139
+ if (!mode) return false;
140
+ return mode === "BICYCLE_RENT";
116
141
  }
117
- exports.isBicycleRent = isBicycleRent;
118
142
  function isCar(mode) {
119
- if (!mode)
120
- return false;
121
- return mode.startsWith("CAR");
143
+ if (!mode) return false;
144
+ return mode.startsWith("CAR");
122
145
  }
123
- exports.isCar = isCar;
124
146
  function isMicromobility(mode) {
125
- if (!mode)
126
- return false;
127
- return mode.startsWith("MICROMOBILITY") || mode.startsWith("SCOOTER");
147
+ if (!mode) return false;
148
+ return mode.startsWith("MICROMOBILITY") || mode.startsWith("SCOOTER");
128
149
  }
129
- exports.isMicromobility = isMicromobility;
130
150
  function isAccessMode(mode) {
131
- return (isWalk(mode) ||
132
- isBicycle(mode) ||
133
- isBicycleRent(mode) ||
134
- isCar(mode) ||
135
- isMicromobility(mode));
151
+ return isWalk(mode) || isBicycle(mode) || isBicycleRent(mode) || isCar(mode) || isMicromobility(mode);
136
152
  }
137
- exports.isAccessMode = isAccessMode;
153
+
138
154
  /**
139
155
  * @param {string} modesStr a comma-separated list of OTP modes
140
156
  * @return {boolean} whether any of the modes are transit modes
141
157
  */
142
158
  function hasTransit(modesStr) {
143
- return modesStr.split(",").some(mode => isTransit(mode));
159
+ return modesStr.split(",").some(mode => isTransit(mode));
144
160
  }
145
- exports.hasTransit = hasTransit;
161
+
146
162
  /**
147
163
  * @param {string} modesStr a comma-separated list of OTP modes
148
164
  * @return {boolean} whether any of the modes are car-based modes
149
165
  */
150
166
  function hasCar(modesStr) {
151
- return modesStr.split(",").some(mode => isCar(mode));
167
+ return modesStr.split(",").some(mode => isCar(mode));
152
168
  }
153
- exports.hasCar = hasCar;
169
+
154
170
  /**
155
171
  * @param {string} modesStr a comma-separated list of OTP modes
156
172
  * @return {boolean} whether any of the modes are bicycle-based modes
157
173
  */
158
174
  function hasBike(modesStr) {
159
- return modesStr
160
- .split(",")
161
- .some(mode => isBicycle(mode) || isBicycleRent(mode));
175
+ return modesStr.split(",").some(mode => isBicycle(mode) || isBicycleRent(mode));
162
176
  }
163
- exports.hasBike = hasBike;
177
+
164
178
  /**
165
179
  * @param {string} modesStr a comma-separated list of OTP modes
166
180
  * @return {boolean} whether any of the modes are micromobility-based modes
167
181
  */
168
182
  function hasMicromobility(modesStr) {
169
- return modesStr.split(",").some(mode => isMicromobility(mode));
183
+ return modesStr.split(",").some(mode => isMicromobility(mode));
170
184
  }
171
- exports.hasMicromobility = hasMicromobility;
185
+
172
186
  /**
173
187
  * @param {string} modesStr a comma-separated list of OTP modes
174
188
  * @return {boolean} whether any of the modes is a hailing mode
175
189
  */
176
190
  function hasHail(modesStr) {
177
- return modesStr.split(",").some(mode => mode.indexOf("_HAIL") > -1);
191
+ return modesStr.split(",").some(mode => mode.indexOf("_HAIL") > -1);
178
192
  }
179
- exports.hasHail = hasHail;
193
+
180
194
  /**
181
195
  * @param {string} modesStr a comma-separated list of OTP modes
182
196
  * @return {boolean} whether any of the modes is a rental mode
183
197
  */
184
198
  function hasRental(modesStr) {
185
- return modesStr.split(",").some(mode => mode.indexOf("_RENT") > -1);
199
+ return modesStr.split(",").some(mode => mode.indexOf("_RENT") > -1);
186
200
  }
187
- exports.hasRental = hasRental;
188
201
  function getMapColor(mode) {
189
- mode = mode || this.get("mode");
190
- if (mode === "WALK")
191
- return "#444";
192
- if (mode === "BICYCLE")
193
- return "#0073e5";
194
- if (mode === "SUBWAY")
195
- return "#e60000";
196
- if (mode === "RAIL")
197
- return "#b00";
198
- if (mode === "BUS")
199
- return "#080";
200
- if (mode === "TROLLEYBUS")
201
- return "#080";
202
- if (mode === "TRAM")
203
- return "#800";
204
- if (mode === "FERRY")
205
- return "#008";
206
- if (mode === "CAR")
207
- return "#444";
208
- if (mode === "MICROMOBILITY" || mode === "SCOOTER")
209
- return "#f5a729";
210
- return "#aaa";
202
+ mode = mode || this.get("mode");
203
+ if (mode === "WALK") return "#444";
204
+ if (mode === "BICYCLE") return "#0073e5";
205
+ if (mode === "SUBWAY") return "#e60000";
206
+ if (mode === "RAIL") return "#b00";
207
+ if (mode === "BUS") return "#080";
208
+ if (mode === "TROLLEYBUS") return "#080";
209
+ if (mode === "TRAM") return "#800";
210
+ if (mode === "FERRY") return "#008";
211
+ if (mode === "CAR") return "#444";
212
+ if (mode === "MICROMOBILITY" || mode === "SCOOTER") return "#f5a729";
213
+ return "#aaa";
211
214
  }
212
- exports.getMapColor = getMapColor;
213
215
  function toSentenceCase(str) {
214
- if (str == null) {
215
- return "";
216
- }
217
- str = String(str);
218
- return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
216
+ if (str == null) {
217
+ return "";
218
+ }
219
+ str = String(str);
220
+ return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
219
221
  }
220
- exports.toSentenceCase = toSentenceCase;
222
+
221
223
  /**
222
224
  * Derive the company string based on mode and network associated with leg.
223
225
  */
224
226
  function getCompanyFromLeg(leg) {
225
- var _a;
226
- if (!leg)
227
- return null;
228
- const { from, mode, rentedBike, rentedCar, rentedVehicle, rideHailingEstimate } = leg;
229
- if (mode === "CAR" && rentedCar) {
230
- return from.networks[0];
231
- }
232
- if (mode === "CAR" && rideHailingEstimate) {
233
- return rideHailingEstimate.provider.id;
234
- }
235
- if (mode === "BICYCLE" && rentedBike && from.networks) {
236
- return from.networks[0];
237
- }
238
- if (from.rentalVehicle) {
239
- return from.rentalVehicle.network;
240
- }
241
- if ((_a = from.vehicleRentalStation) === null || _a === void 0 ? void 0 : _a.rentalNetwork) {
242
- return from.vehicleRentalStation.rentalNetwork.networkId;
243
- }
244
- if ((mode === "MICROMOBILITY" || mode === "SCOOTER") &&
245
- rentedVehicle &&
246
- from.networks) {
247
- return from.networks[0];
248
- }
249
- return null;
227
+ var _from$vehicleRentalSt;
228
+ if (!leg) return null;
229
+ const {
230
+ from,
231
+ mode,
232
+ rentedBike,
233
+ rentedCar,
234
+ rentedVehicle,
235
+ rideHailingEstimate
236
+ } = leg;
237
+ if (mode === "CAR" && rentedCar) {
238
+ return from.networks[0];
239
+ }
240
+ if (mode === "CAR" && rideHailingEstimate) {
241
+ return rideHailingEstimate.provider.id;
242
+ }
243
+ if (mode === "BICYCLE" && rentedBike && from.networks) {
244
+ return from.networks[0];
245
+ }
246
+ if (from.rentalVehicle) {
247
+ return from.rentalVehicle.network;
248
+ }
249
+ if ((_from$vehicleRentalSt = from.vehicleRentalStation) !== null && _from$vehicleRentalSt !== void 0 && _from$vehicleRentalSt.rentalNetwork) {
250
+ return from.vehicleRentalStation.rentalNetwork.networkId;
251
+ }
252
+ if ((mode === "MICROMOBILITY" || mode === "SCOOTER") && rentedVehicle && from.networks) {
253
+ return from.networks[0];
254
+ }
255
+ return null;
250
256
  }
251
- exports.getCompanyFromLeg = getCompanyFromLeg;
252
257
  function getItineraryBounds(itinerary) {
253
- let coords = [];
254
- itinerary.legs.forEach(leg => {
255
- const legCoords = polyline_1.default
256
- .toGeoJSON(leg.legGeometry.points)
257
- .coordinates.map((c) => [c[1], c[0]]);
258
- coords = [...coords, ...legCoords];
259
- });
260
- return coords;
261
- }
262
- exports.getItineraryBounds = getItineraryBounds;
258
+ let coords = [];
259
+ itinerary.legs.forEach(leg => {
260
+ const legCoords = _polyline.default.toGeoJSON(leg.legGeometry.points).coordinates.map(c => [c[1], c[0]]);
261
+ coords = [...coords, ...legCoords];
262
+ });
263
+ return coords;
264
+ }
265
+
263
266
  /**
264
267
  * Return a coords object that encloses the given leg's geometry.
265
268
  */
266
269
  function getLegBounds(leg) {
267
- const coords = polyline_1.default
268
- .toGeoJSON(leg.legGeometry.points)
269
- .coordinates.map(c => [c[1], c[0]]);
270
- // in certain cases, there might be zero-length coordinates in the leg
271
- // geometry. In these cases, build us an array of coordinates using the from
272
- // and to data of the leg.
273
- if (coords.length === 0) {
274
- coords.push([leg.from.lat, leg.from.lon], [leg.to.lat, leg.to.lon]);
275
- }
276
- return coords;
277
- }
278
- exports.getLegBounds = getLegBounds;
270
+ const coords = _polyline.default.toGeoJSON(leg.legGeometry.points).coordinates.map(c => [c[1], c[0]]);
271
+
272
+ // in certain cases, there might be zero-length coordinates in the leg
273
+ // geometry. In these cases, build us an array of coordinates using the from
274
+ // and to data of the leg.
275
+ if (coords.length === 0) {
276
+ coords.push([leg.from.lat, leg.from.lon], [leg.to.lat, leg.to.lon]);
277
+ }
278
+ return coords;
279
+ }
280
+
279
281
  /* Returns an interpolated lat-lon at a specified distance along a leg */
282
+
280
283
  function legLocationAtDistance(leg, distance) {
281
- if (!leg.legGeometry)
282
- return null;
283
- try {
284
- const line = polyline_1.default.toGeoJSON(leg.legGeometry.points);
285
- const pt = (0, along_1.default)(line, distance, { units: "meters" });
286
- if (pt && pt.geometry && pt.geometry.coordinates) {
287
- return [pt.geometry.coordinates[1], pt.geometry.coordinates[0]];
288
- }
289
- }
290
- catch (e) {
291
- // FIXME handle error!
284
+ if (!leg.legGeometry) return null;
285
+ try {
286
+ const line = _polyline.default.toGeoJSON(leg.legGeometry.points);
287
+ const pt = (0, _along.default)(line, distance, {
288
+ units: "meters"
289
+ });
290
+ if (pt && pt.geometry && pt.geometry.coordinates) {
291
+ return [pt.geometry.coordinates[1], pt.geometry.coordinates[0]];
292
292
  }
293
- return null;
293
+ } catch (e) {
294
+ // FIXME handle error!
295
+ }
296
+ return null;
294
297
  }
295
- exports.legLocationAtDistance = legLocationAtDistance;
298
+
296
299
  /* Returns an interpolated elevation at a specified distance along a leg */
300
+
297
301
  function legElevationAtDistance(points, distance) {
298
- // Iterate through the combined elevation profile
299
- let traversed = 0;
300
- // If first point distance is not zero, insert starting point at zero with
301
- // null elevation. Encountering this value should trigger the warning below.
302
- if (points[0][0] > 0) {
303
- points.unshift([0, null]);
304
- }
305
- for (let i = 1; i < points.length; i++) {
306
- const start = points[i - 1];
307
- const elevDistanceSpan = points[i][0] - start[0];
308
- if (distance >= traversed && distance <= traversed + elevDistanceSpan) {
309
- // Distance falls within this point and the previous one;
310
- // compute & return interpolated elevation value
311
- if (start[1] === null) {
312
- console.warn("Elevation value does not exist for distance.", distance, traversed);
313
- return null;
314
- }
315
- const pct = (distance - traversed) / elevDistanceSpan;
316
- const elevSpan = points[i][1] - start[1];
317
- return start[1] + elevSpan * pct;
318
- }
319
- traversed += elevDistanceSpan;
302
+ // Iterate through the combined elevation profile
303
+ let traversed = 0;
304
+ // If first point distance is not zero, insert starting point at zero with
305
+ // null elevation. Encountering this value should trigger the warning below.
306
+ if (points[0][0] > 0) {
307
+ points.unshift([0, null]);
308
+ }
309
+ for (let i = 1; i < points.length; i++) {
310
+ const start = points[i - 1];
311
+ const elevDistanceSpan = points[i][0] - start[0];
312
+ if (distance >= traversed && distance <= traversed + elevDistanceSpan) {
313
+ // Distance falls within this point and the previous one;
314
+ // compute & return interpolated elevation value
315
+ if (start[1] === null) {
316
+ console.warn("Elevation value does not exist for distance.", distance, traversed);
317
+ return null;
318
+ }
319
+ const pct = (distance - traversed) / elevDistanceSpan;
320
+ const elevSpan = points[i][1] - start[1];
321
+ return start[1] + elevSpan * pct;
320
322
  }
321
- console.warn("Elevation value does not exist for distance.", distance, traversed);
322
- return null;
323
+ traversed += elevDistanceSpan;
324
+ }
325
+ console.warn("Elevation value does not exist for distance.", distance, traversed);
326
+ return null;
323
327
  }
324
- exports.legElevationAtDistance = legElevationAtDistance;
325
328
  function mapOldElevationComponentToNew(oldElev) {
326
- return {
327
- distance: oldElev.first,
328
- elevation: oldElev.second
329
- };
329
+ return {
330
+ distance: oldElev.first,
331
+ elevation: oldElev.second
332
+ };
330
333
  }
331
- exports.mapOldElevationComponentToNew = mapOldElevationComponentToNew;
334
+
332
335
  // Iterate through the steps, building the array of elevation points and
333
336
  // keeping track of the minimum and maximum elevations reached
334
337
  function getElevationProfile(steps, unitConversion = 1) {
335
- let minElev = 100000;
336
- let maxElev = -100000;
337
- let traversed = 0;
338
- let gain = 0;
339
- let loss = 0;
340
- let previous = null;
341
- const points = [];
342
- steps.forEach(step => {
343
- var _a;
344
- // Support for old REST response data (in step.elevation)
345
- const stepElevationProfile = step.elevationProfile ||
346
- (Array.isArray(step.elevation) &&
347
- ((_a = step.elevation) === null || _a === void 0 ? void 0 : _a.map(mapOldElevationComponentToNew)));
348
- if (!stepElevationProfile || stepElevationProfile.length === 0) {
349
- traversed += step.distance;
350
- return;
351
- }
352
- for (let i = 0; i < stepElevationProfile.length; i++) {
353
- const elev = stepElevationProfile[i];
354
- if (previous) {
355
- const diff = (elev.elevation - previous.elevation) * unitConversion;
356
- if (diff > 0)
357
- gain += diff;
358
- else
359
- loss += diff;
360
- }
361
- if (i === 0 && elev.distance !== 0) {
362
- // console.warn(`No elevation data available for step ${stepIndex}-${i} at beginning of segment`, elev)
363
- }
364
- const convertedElevation = elev.elevation * unitConversion;
365
- if (convertedElevation < minElev)
366
- minElev = convertedElevation;
367
- if (convertedElevation > maxElev)
368
- maxElev = convertedElevation;
369
- points.push([traversed + elev.distance, elev.elevation]);
370
- // Insert "filler" point if the last point in elevation profile does not
371
- // reach the full distance of the step.
372
- if (i === stepElevationProfile.length - 1 &&
373
- elev.distance !== step.distance) {
374
- // points.push([traversed + step.distance, elev.second])
375
- }
376
- previous = elev;
377
- }
378
- traversed += step.distance;
379
- });
380
- return { maxElev, minElev, points, traversed, gain, loss };
381
- }
382
- exports.getElevationProfile = getElevationProfile;
338
+ let minElev = 100000;
339
+ let maxElev = -100000;
340
+ let traversed = 0;
341
+ let gain = 0;
342
+ let loss = 0;
343
+ let previous = null;
344
+ const points = [];
345
+ steps.forEach(step => {
346
+ var _step$elevation;
347
+ // Support for old REST response data (in step.elevation)
348
+ const stepElevationProfile = step.elevationProfile || Array.isArray(step.elevation) && ((_step$elevation = step.elevation) === null || _step$elevation === void 0 ? void 0 : _step$elevation.map(mapOldElevationComponentToNew));
349
+ if (!stepElevationProfile || stepElevationProfile.length === 0) {
350
+ traversed += step.distance;
351
+ return;
352
+ }
353
+ for (let i = 0; i < stepElevationProfile.length; i++) {
354
+ const elev = stepElevationProfile[i];
355
+ if (previous) {
356
+ const diff = (elev.elevation - previous.elevation) * unitConversion;
357
+ if (diff > 0) gain += diff;else loss += diff;
358
+ }
359
+ if (i === 0 && elev.distance !== 0) {
360
+ // console.warn(`No elevation data available for step ${stepIndex}-${i} at beginning of segment`, elev)
361
+ }
362
+ const convertedElevation = elev.elevation * unitConversion;
363
+ if (convertedElevation < minElev) minElev = convertedElevation;
364
+ if (convertedElevation > maxElev) maxElev = convertedElevation;
365
+ points.push([traversed + elev.distance, elev.elevation]);
366
+ // Insert "filler" point if the last point in elevation profile does not
367
+ // reach the full distance of the step.
368
+ if (i === stepElevationProfile.length - 1 && elev.distance !== step.distance) {
369
+ // points.push([traversed + step.distance, elev.second])
370
+ }
371
+ previous = elev;
372
+ }
373
+ traversed += step.distance;
374
+ });
375
+ return {
376
+ maxElev,
377
+ minElev,
378
+ points,
379
+ traversed,
380
+ gain,
381
+ loss
382
+ };
383
+ }
384
+
383
385
  /**
384
386
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
385
387
  *
@@ -389,27 +391,28 @@ exports.getElevationProfile = getElevationProfile;
389
391
  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
390
392
  */
391
393
  function getTextWidth(text, font = "22px Arial") {
392
- // reuse canvas object for better performance
393
- const canvas = getTextWidth.canvas ||
394
- (getTextWidth.canvas = document.createElement("canvas"));
395
- const context = canvas.getContext("2d");
396
- context.font = font;
397
- const metrics = context.measureText(text);
398
- return metrics.width;
399
- }
400
- exports.getTextWidth = getTextWidth;
394
+ // Create custom type for function including reused canvas object
395
+
396
+ // reuse canvas object for better performance
397
+ const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
398
+ const context = canvas.getContext("2d");
399
+ context.font = font;
400
+ const metrics = context.measureText(text);
401
+ return metrics.width;
402
+ }
403
+
401
404
  /**
402
405
  * Get the configured company object for the given network string if the company
403
406
  * has been defined in the provided companies array config.
404
407
  */
405
408
  function getCompanyForNetwork(networkString, companies = []) {
406
- const company = companies.find(co => co.id === networkString);
407
- if (!company) {
408
- console.warn(`No company found in config.yml that matches rented vehicle network: ${networkString}`, companies);
409
- }
410
- return company;
409
+ const company = companies.find(co => co.id === networkString);
410
+ if (!company) {
411
+ console.warn(`No company found in config.yml that matches rented vehicle network: ${networkString}`, companies);
412
+ }
413
+ return company;
411
414
  }
412
- exports.getCompanyForNetwork = getCompanyForNetwork;
415
+
413
416
  /**
414
417
  * Get a string label to display from a list of vehicle rental networks. Returns
415
418
  * empty string if no networks provided.
@@ -419,60 +422,57 @@ exports.getCompanyForNetwork = getCompanyForNetwork;
419
422
  * @return {string} A label for use in presentation on a website.
420
423
  */
421
424
  function getCompaniesLabelFromNetworks(networks, companies = []) {
422
- if (!networks)
423
- return "";
424
- return (Array.isArray(networks) ? networks : [networks])
425
- .map(network => getCompanyForNetwork(network, companies))
426
- .filter(co => !!co)
427
- .map(co => co.label)
428
- .join("/");
425
+ if (!networks) return "";
426
+ return (Array.isArray(networks) ? networks : [networks]).map(network => getCompanyForNetwork(network, companies)).filter(co => !!co).map(co => co.label).join("/");
429
427
  }
430
- exports.getCompaniesLabelFromNetworks = getCompaniesLabelFromNetworks;
431
428
  function getTNCLocation(leg, type) {
432
- const location = leg[type];
433
- return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}`;
429
+ const location = leg[type];
430
+ return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}`;
434
431
  }
435
- exports.getTNCLocation = getTNCLocation;
436
432
  function calculatePhysicalActivity(itinerary) {
437
- let walkDuration = 0;
438
- let bikeDuration = 0;
439
- itinerary.legs.forEach(leg => {
440
- if (leg.mode.startsWith("WALK"))
441
- walkDuration += leg.duration;
442
- if (leg.mode.startsWith("BICYCLE"))
443
- bikeDuration += leg.duration;
444
- });
445
- const caloriesBurned = (walkDuration / 3600) * 280 + (bikeDuration / 3600) * 290;
446
- return {
447
- bikeDuration,
448
- caloriesBurned,
449
- walkDuration
450
- };
451
- }
452
- exports.calculatePhysicalActivity = calculatePhysicalActivity;
433
+ let walkDuration = 0;
434
+ let bikeDuration = 0;
435
+ itinerary.legs.forEach(leg => {
436
+ if (leg.mode.startsWith("WALK")) walkDuration += leg.duration;
437
+ if (leg.mode.startsWith("BICYCLE")) bikeDuration += leg.duration;
438
+ });
439
+ const caloriesBurned = walkDuration / 3600 * 280 + bikeDuration / 3600 * 290;
440
+ return {
441
+ bikeDuration,
442
+ caloriesBurned,
443
+ walkDuration
444
+ };
445
+ }
446
+
453
447
  /**
454
448
  * For an itinerary, calculates the TNC fares and returns an object with
455
449
  * these values and currency info.
456
450
  * It is assumed that the same currency is used for all TNC legs.
457
451
  */
458
452
  function calculateTncFares(itinerary) {
459
- return itinerary.legs
460
- .filter(leg => leg.mode === "CAR" && leg.rideHailingEstimate)
461
- .reduce(({ maxTNCFare, minTNCFare }, { rideHailingEstimate }) => {
462
- const { minPrice, maxPrice } = rideHailingEstimate;
463
- return {
464
- // Assumes a single currency for entire itinerary.
465
- currencyCode: minPrice.currency.code,
466
- maxTNCFare: maxTNCFare + maxPrice.amount,
467
- minTNCFare: minTNCFare + minPrice.amount
468
- };
469
- }, {
470
- currencyCode: null,
471
- maxTNCFare: 0,
472
- minTNCFare: 0
473
- });
453
+ return itinerary.legs.filter(leg => leg.mode === "CAR" && leg.rideHailingEstimate).reduce(({
454
+ maxTNCFare,
455
+ minTNCFare
456
+ }, {
457
+ rideHailingEstimate
458
+ }) => {
459
+ const {
460
+ minPrice,
461
+ maxPrice
462
+ } = rideHailingEstimate;
463
+ return {
464
+ // Assumes a single currency for entire itinerary.
465
+ currencyCode: minPrice.currency.code,
466
+ maxTNCFare: maxTNCFare + maxPrice.amount,
467
+ minTNCFare: minTNCFare + minPrice.amount
468
+ };
469
+ }, {
470
+ currencyCode: null,
471
+ maxTNCFare: 0,
472
+ minTNCFare: 0
473
+ });
474
474
  }
475
- exports.calculateTncFares = calculateTncFares;
475
+
476
476
  /**
477
477
  * Sources:
478
478
  * - https://www.itf-oecd.org/sites/default/files/docs/environmental-performance-new-mobility.pdf
@@ -481,23 +481,24 @@ exports.calculateTncFares = calculateTncFares;
481
481
  * Other values extrapolated.
482
482
  */
483
483
  const CARBON_INTENSITY_DEFAULTS = {
484
- walk: 0.026,
485
- bicycle: 0.017,
486
- car: 0.162,
487
- tram: 0.066,
488
- trolleybus: 0.066,
489
- subway: 0.066,
490
- rail: 0.066,
491
- bus: 0.09,
492
- ferry: 0.082,
493
- cable_car: 0.021,
494
- gondola: 0.021,
495
- funicular: 0.066,
496
- transit: 0.066,
497
- leg_switch: 0,
498
- airplane: 0.382,
499
- micromobility: 0.095
484
+ walk: 0.026,
485
+ bicycle: 0.017,
486
+ car: 0.162,
487
+ tram: 0.066,
488
+ trolleybus: 0.066,
489
+ subway: 0.066,
490
+ rail: 0.066,
491
+ bus: 0.09,
492
+ ferry: 0.082,
493
+ cable_car: 0.021,
494
+ gondola: 0.021,
495
+ funicular: 0.066,
496
+ transit: 0.066,
497
+ leg_switch: 0,
498
+ airplane: 0.382,
499
+ micromobility: 0.095
500
500
  };
501
+
501
502
  /**
502
503
  * @param {itinerary} itinerary OTP trip itinierary, only legs is required.
503
504
  * @param {carbonIntensity} carbonIntensity carbon intensity by mode in grams/meter
@@ -507,30 +508,30 @@ const CARBON_INTENSITY_DEFAULTS = {
507
508
  function calculateEmissions(
508
509
  // This type makes all the properties from Itinerary optional except legs.
509
510
  itinerary, carbonIntensity = {}, units) {
510
- var _a;
511
- // Apply defaults for any values that we don't have.
512
- const carbonIntensityWithDefaults = {
513
- ...CARBON_INTENSITY_DEFAULTS,
514
- ...carbonIntensity
515
- };
516
- // Distance is in meters, totalCarbon is in grams
517
- const totalCarbon = ((_a = itinerary === null || itinerary === void 0 ? void 0 : itinerary.legs) === null || _a === void 0 ? void 0 : _a.reduce((total, leg) => {
518
- return ((leg.distance * carbonIntensityWithDefaults[leg.mode.toLowerCase()] ||
519
- 0) + total);
520
- }, 0)) || 0;
521
- switch (units) {
522
- case "ounce":
523
- return totalCarbon / 28.35;
524
- case "kilogram":
525
- return totalCarbon / 1000;
526
- case "pound":
527
- return totalCarbon / 454;
528
- case "gram":
529
- default:
530
- return totalCarbon;
531
- }
532
- }
533
- exports.calculateEmissions = calculateEmissions;
511
+ var _itinerary$legs;
512
+ // Apply defaults for any values that we don't have.
513
+ const carbonIntensityWithDefaults = {
514
+ ...CARBON_INTENSITY_DEFAULTS,
515
+ ...carbonIntensity
516
+ };
517
+
518
+ // Distance is in meters, totalCarbon is in grams
519
+ const totalCarbon = (itinerary === null || itinerary === void 0 || (_itinerary$legs = itinerary.legs) === null || _itinerary$legs === void 0 ? void 0 : _itinerary$legs.reduce((total, leg) => {
520
+ return (leg.distance * carbonIntensityWithDefaults[leg.mode.toLowerCase()] || 0) + total;
521
+ }, 0)) || 0;
522
+ switch (units) {
523
+ case "ounce":
524
+ return totalCarbon / 28.35;
525
+ case "kilogram":
526
+ return totalCarbon / 1000;
527
+ case "pound":
528
+ return totalCarbon / 454;
529
+ case "gram":
530
+ default:
531
+ return totalCarbon;
532
+ }
533
+ }
534
+
534
535
  /**
535
536
  * Returns the user-facing stop id to display for a stop or place, using the following priority:
536
537
  * 1. stop code,
@@ -538,29 +539,38 @@ exports.calculateEmissions = calculateEmissions;
538
539
  * 3. stop id, whether null or not (this is the fallback case).
539
540
  */
540
541
  function getDisplayedStopId(placeOrStop) {
541
- let stopId;
542
- let stopCode;
543
- if ("stopId" in placeOrStop) {
544
- ({ stopCode, stopId } = placeOrStop);
545
- }
546
- else if ("id" in placeOrStop) {
547
- ({ code: stopCode, id: stopId } = placeOrStop);
548
- }
549
- return stopCode || (stopId === null || stopId === void 0 ? void 0 : stopId.split(":")[1]) || stopId;
550
- }
551
- exports.getDisplayedStopId = getDisplayedStopId;
542
+ var _stopId;
543
+ let stopId;
544
+ let stopCode;
545
+ if ("stopId" in placeOrStop) {
546
+ ({
547
+ stopCode,
548
+ stopId
549
+ } = placeOrStop);
550
+ } else if ("id" in placeOrStop) {
551
+ ({
552
+ code: stopCode,
553
+ id: stopId
554
+ } = placeOrStop);
555
+ }
556
+ return stopCode || ((_stopId = stopId) === null || _stopId === void 0 ? void 0 : _stopId.split(":")[1]) || stopId;
557
+ }
558
+
552
559
  /**
553
560
  * Removes the first part of the OTP standard scope (":"), if it is present
554
561
  * @param item String that is potentially scoped with `:` character
555
562
  * @returns descoped string
556
563
  */
557
- const descope = (item) => { var _a; return (_a = item === null || item === void 0 ? void 0 : item.split(":")) === null || _a === void 0 ? void 0 : _a[1]; };
564
+ const descope = item => {
565
+ var _item$split;
566
+ return item === null || item === void 0 || (_item$split = item.split(":")) === null || _item$split === void 0 ? void 0 : _item$split[1];
567
+ };
558
568
  exports.descope = descope;
559
- const zeroDollars = (currency) => ({
560
- amount: 0,
561
- currency
569
+ const zeroDollars = currency => ({
570
+ amount: 0,
571
+ currency
562
572
  });
563
- exports.zeroDollars = zeroDollars;
573
+
564
574
  /**
565
575
  * Extracts useful data from the fare products on a leg, such as the leg cost and transfer info.
566
576
  * @param leg Leg with Fares v2 information
@@ -572,53 +582,56 @@ exports.zeroDollars = zeroDollars;
572
582
  * all the information needed, but the other fields are kept to
573
583
  * make the transition to Fares V2 less jarring.
574
584
  */
585
+ exports.zeroDollars = zeroDollars;
575
586
  function getLegCost(leg, mediumId, riderCategoryId, seenFareIds) {
576
- if (!leg.fareProducts)
577
- return { price: undefined };
578
- const relevantFareProducts = leg.fareProducts
579
- .filter(({ product }) => {
580
- // riderCategory and medium can be specifically defined as null to handle
581
- // generic GTFS based fares from OTP when there is no fare model
582
- var _a, _b, _c, _d;
583
- // Remove (optional) agency scoping
584
- const productRiderCategoryId = (0, exports.descope)((_a = product === null || product === void 0 ? void 0 : product.riderCategory) === null || _a === void 0 ? void 0 : _a.id) ||
585
- ((_b = product === null || product === void 0 ? void 0 : product.riderCategory) === null || _b === void 0 ? void 0 : _b.id) ||
586
- null;
587
- const productMediaId = (0, exports.descope)((_c = product === null || product === void 0 ? void 0 : product.medium) === null || _c === void 0 ? void 0 : _c.id) || ((_d = product === null || product === void 0 ? void 0 : product.medium) === null || _d === void 0 ? void 0 : _d.id) || null;
588
- return (productRiderCategoryId === riderCategoryId &&
589
- productMediaId === mediumId &&
590
- (
591
- // Make sure there's a price
592
- // Some fare products don't have a price at all.
593
- product === null || product === void 0 ? void 0 : product.price));
594
- })
595
- .map(fare => {
596
- const alreadySeen = (seenFareIds === null || seenFareIds === void 0 ? void 0 : seenFareIds.indexOf(fare.id)) > -1;
597
- const { currency } = fare.product.price;
598
- return {
599
- id: fare.id,
600
- product: {
601
- ...fare.product,
602
- legPrice: alreadySeen ? (0, exports.zeroDollars)(currency) : fare.product.price
603
- }
604
- };
605
- })
606
- .sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = a.product) === null || _a === void 0 ? void 0 : _a.legPrice) === null || _b === void 0 ? void 0 : _b.amount) - ((_d = (_c = b.product) === null || _c === void 0 ? void 0 : _c.legPrice) === null || _d === void 0 ? void 0 : _d.amount); });
607
- // Return the cheapest, but include other matches as well
608
- const cheapestRelevantFareProduct = relevantFareProducts[0];
609
- // TODO: return one object here instead of dumbing it down?
587
+ if (!leg.fareProducts) return {
588
+ price: undefined
589
+ };
590
+ const relevantFareProducts = leg.fareProducts.filter(({
591
+ product
592
+ }) => {
593
+ var _product$riderCategor, _product$riderCategor2, _product$medium, _product$medium2;
594
+ // riderCategory and medium can be specifically defined as null to handle
595
+ // generic GTFS based fares from OTP when there is no fare model
596
+
597
+ // Remove (optional) agency scoping
598
+ const productRiderCategoryId = descope(product === null || product === void 0 || (_product$riderCategor = product.riderCategory) === null || _product$riderCategor === void 0 ? void 0 : _product$riderCategor.id) || (product === null || product === void 0 || (_product$riderCategor2 = product.riderCategory) === null || _product$riderCategor2 === void 0 ? void 0 : _product$riderCategor2.id) || null;
599
+ const productMediaId = descope(product === null || product === void 0 || (_product$medium = product.medium) === null || _product$medium === void 0 ? void 0 : _product$medium.id) || (product === null || product === void 0 || (_product$medium2 = product.medium) === null || _product$medium2 === void 0 ? void 0 : _product$medium2.id) || null;
600
+ return productRiderCategoryId === riderCategoryId && productMediaId === mediumId && (// Make sure there's a price
601
+ // Some fare products don't have a price at all.
602
+ product === null || product === void 0 ? void 0 : product.price);
603
+ }).map(fare => {
604
+ const alreadySeen = (seenFareIds === null || seenFareIds === void 0 ? void 0 : seenFareIds.indexOf(fare.id)) > -1;
605
+ const {
606
+ currency
607
+ } = fare.product.price;
610
608
  return {
611
- alternateFareProducts: relevantFareProducts.splice(1).map(fp => fp.product),
612
- appliedFareProduct: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product,
613
- isDependent:
614
- // eslint-disable-next-line no-underscore-dangle
615
- (cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product.__typename) ===
616
- "DependentFareProduct",
617
- price: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product.legPrice,
618
- productUseId: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.id
609
+ id: fare.id,
610
+ product: {
611
+ ...fare.product,
612
+ legPrice: alreadySeen ? zeroDollars(currency) : fare.product.price
613
+ }
619
614
  };
615
+ }).sort((a, b) => {
616
+ var _a$product, _b$product;
617
+ return ((_a$product = a.product) === null || _a$product === void 0 || (_a$product = _a$product.legPrice) === null || _a$product === void 0 ? void 0 : _a$product.amount) - ((_b$product = b.product) === null || _b$product === void 0 || (_b$product = _b$product.legPrice) === null || _b$product === void 0 ? void 0 : _b$product.amount);
618
+ });
619
+
620
+ // Return the cheapest, but include other matches as well
621
+ const cheapestRelevantFareProduct = relevantFareProducts[0];
622
+
623
+ // TODO: return one object here instead of dumbing it down?
624
+ return {
625
+ alternateFareProducts: relevantFareProducts.splice(1).map(fp => fp.product),
626
+ appliedFareProduct: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product,
627
+ isDependent:
628
+ // eslint-disable-next-line no-underscore-dangle
629
+ (cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product.__typename) === "DependentFareProduct",
630
+ price: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.product.legPrice,
631
+ productUseId: cheapestRelevantFareProduct === null || cheapestRelevantFareProduct === void 0 ? void 0 : cheapestRelevantFareProduct.id
632
+ };
620
633
  }
621
- exports.getLegCost = getLegCost;
634
+
622
635
  /**
623
636
  * Returns the total itinerary cost for a given set of legs.
624
637
  * @param legs Itinerary legs with fare products (must have used getLegsWithFares)
@@ -628,133 +641,148 @@ exports.getLegCost = getLegCost;
628
641
  * @returns Money object for the total itinerary cost.
629
642
  */
630
643
  function getItineraryCost(legs, mediumId, riderCategoryId) {
631
- var _a;
632
- // TODO: Better input type handling
633
- if (Array.isArray(mediumId) || Array.isArray(riderCategoryId)) {
634
- if ((mediumId === null || mediumId === void 0 ? void 0 : mediumId.length) !== riderCategoryId.length) {
635
- console.warn("Invalid input types, only using first item. medium id list and rider category list must have same number of items");
636
- return getItineraryCost(legs, mediumId[0], riderCategoryId[0]);
637
- }
638
- let total = { amount: 0, currency: null };
639
- for (let i = 0; i < mediumId.length; i++) {
640
- const newCost = getItineraryCost(legs, mediumId[i], riderCategoryId[i]);
641
- if (newCost) {
642
- total = {
643
- amount: (total === null || total === void 0 ? void 0 : total.amount) + ((newCost === null || newCost === void 0 ? void 0 : newCost.amount) || 0),
644
- currency: (_a = total.currency) !== null && _a !== void 0 ? _a : newCost === null || newCost === void 0 ? void 0 : newCost.currency
645
- };
646
- }
647
- }
648
- if (total.currency === null)
649
- return undefined;
650
- return total;
644
+ // TODO: Better input type handling
645
+ if (Array.isArray(mediumId) || Array.isArray(riderCategoryId)) {
646
+ if ((mediumId === null || mediumId === void 0 ? void 0 : mediumId.length) !== riderCategoryId.length) {
647
+ console.warn("Invalid input types, only using first item. medium id list and rider category list must have same number of items");
648
+ return getItineraryCost(legs, mediumId[0], riderCategoryId[0]);
649
+ }
650
+ let total = {
651
+ amount: 0,
652
+ currency: null
653
+ };
654
+ for (let i = 0; i < mediumId.length; i++) {
655
+ const newCost = getItineraryCost(legs, mediumId[i], riderCategoryId[i]);
656
+ if (newCost) {
657
+ var _total, _total$currency;
658
+ total = {
659
+ amount: ((_total = total) === null || _total === void 0 ? void 0 : _total.amount) + ((newCost === null || newCost === void 0 ? void 0 : newCost.amount) || 0),
660
+ currency: (_total$currency = total.currency) !== null && _total$currency !== void 0 ? _total$currency : newCost === null || newCost === void 0 ? void 0 : newCost.currency
661
+ };
662
+ }
651
663
  }
652
- const legCostsObj = legs
653
- // Only legs with fares (no walking legs)
654
- .filter(leg => { var _a; return ((_a = leg.fareProducts) === null || _a === void 0 ? void 0 : _a.length) > 0; })
655
- // Get the leg cost object of each leg
656
- .map((leg, index, arr) => getLegCost(leg, mediumId, riderCategoryId,
657
- // We need to include the seen fare ids by gathering all previous leg fare product ids
658
- arr.splice(0, index).flatMap(l => l === null || l === void 0 ? void 0 : l.fareProducts.map(fp => fp === null || fp === void 0 ? void 0 : fp.id))))
659
- .filter(cost => { var _a; return ((_a = cost.appliedFareProduct) === null || _a === void 0 ? void 0 : _a.legPrice) !== undefined; })
660
- // Filter out duplicate use IDs
661
- // One fare product can be used on multiple legs,
662
- // and we don't want to count it more than once.
663
- // Use an object keyed by productUseId to deduplicate, then extract prices
664
- .reduce((acc, cur) => {
665
- var _a;
666
- if (cur.productUseId && acc[cur.productUseId] === undefined) {
667
- acc[cur.productUseId] = (_a = cur.appliedFareProduct) === null || _a === void 0 ? void 0 : _a.legPrice;
668
- }
669
- return acc;
670
- }, {});
671
- const legCosts = Object.values(legCostsObj);
672
- if (legCosts.length === 0)
673
- return undefined;
674
- // Calculate the total
675
- return legCosts.reduce((prev, cur) => {
676
- var _a;
677
- return ({
678
- amount: prev.amount + (cur === null || cur === void 0 ? void 0 : cur.amount) || 0,
679
- currency: (_a = prev.currency) !== null && _a !== void 0 ? _a : cur === null || cur === void 0 ? void 0 : cur.currency
680
- });
681
- }, { amount: 0, currency: null });
664
+ if (total.currency === null) return undefined;
665
+ return total;
666
+ }
667
+ const legCosts = legs
668
+ // Only legs with fares (no walking legs)
669
+ .filter(leg => {
670
+ var _leg$fareProducts;
671
+ return ((_leg$fareProducts = leg.fareProducts) === null || _leg$fareProducts === void 0 ? void 0 : _leg$fareProducts.length) > 0;
672
+ })
673
+ // Get the leg cost object of each leg
674
+ .reduce((acc, leg) => {
675
+ // getLegCost handles filtering out duplicate use IDs
676
+ // One fare product can be used on multiple legs,
677
+ // and we don't want to count it more than once.
678
+ // Use an object keyed by productUseId to deduplicate, then extract prices
679
+ const {
680
+ appliedFareProduct,
681
+ productUseId
682
+ } = getLegCost(leg, mediumId, riderCategoryId, acc.seenIds);
683
+ if (!appliedFareProduct) return acc;
684
+ return {
685
+ legCosts: [...acc.legCosts, appliedFareProduct],
686
+ seenIds: [...acc.seenIds, productUseId]
687
+ };
688
+ }, {
689
+ seenIds: [],
690
+ legCosts: []
691
+ }).legCosts.map(lc => lc.legPrice);
692
+ if (legCosts.length === 0) return undefined;
693
+ // Calculate the total
694
+ return legCosts.reduce((prev, cur) => {
695
+ var _prev$currency;
696
+ return {
697
+ amount: prev.amount + (cur === null || cur === void 0 ? void 0 : cur.amount) || 0,
698
+ currency: (_prev$currency = prev.currency) !== null && _prev$currency !== void 0 ? _prev$currency : cur === null || cur === void 0 ? void 0 : cur.currency
699
+ };
700
+ }, {
701
+ amount: 0,
702
+ currency: null
703
+ });
682
704
  }
683
- exports.getItineraryCost = getItineraryCost;
684
705
  const pickupDropoffTypeToOtp1 = otp2Type => {
685
- switch (otp2Type) {
686
- case "COORDINATE_WITH_DRIVER":
687
- return "coordinateWithDriver";
688
- case "CALL_AGENCY":
689
- return "mustPhone";
690
- case "SCHEDULED":
691
- return "scheduled";
692
- case "NONE":
693
- return "none";
694
- default:
695
- return null;
696
- }
706
+ switch (otp2Type) {
707
+ case "COORDINATE_WITH_DRIVER":
708
+ return "coordinateWithDriver";
709
+ case "CALL_AGENCY":
710
+ return "mustPhone";
711
+ case "SCHEDULED":
712
+ return "scheduled";
713
+ case "NONE":
714
+ return "none";
715
+ default:
716
+ return null;
717
+ }
697
718
  };
698
- const convertGraphQLResponseToLegacy = (leg) => {
699
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
700
- return ({
701
- ...leg,
702
- agencyBrandingUrl: (_a = leg.agency) === null || _a === void 0 ? void 0 : _a.url,
703
- agencyId: (_b = leg.agency) === null || _b === void 0 ? void 0 : _b.id,
704
- agencyName: (_c = leg.agency) === null || _c === void 0 ? void 0 : _c.name,
705
- agencyUrl: (_d = leg.agency) === null || _d === void 0 ? void 0 : _d.url,
706
- alightRule: pickupDropoffTypeToOtp1(leg.dropoffType),
707
- boardRule: pickupDropoffTypeToOtp1(leg.pickupType),
708
- bookingRuleInfo: {
709
- dropOff: (leg === null || leg === void 0 ? void 0 : leg.dropOffBookingInfo) || {},
710
- pickUp: (leg === null || leg === void 0 ? void 0 : leg.pickUpBookingInfo) || {}
711
- },
712
- dropOffBookingInfo: {
713
- latestBookingTime: leg.dropOffBookingInfo
714
- },
715
- from: {
716
- ...leg.from,
717
- stopCode: (_e = leg.from.stop) === null || _e === void 0 ? void 0 : _e.code,
718
- stopId: (_f = leg.from.stop) === null || _f === void 0 ? void 0 : _f.gtfsId
719
- },
720
- route: (_g = leg.route) === null || _g === void 0 ? void 0 : _g.shortName,
721
- routeColor: (_h = leg.route) === null || _h === void 0 ? void 0 : _h.color,
722
- routeId: (_j = leg.route) === null || _j === void 0 ? void 0 : _j.gtfsId,
723
- routeLongName: (_k = leg.route) === null || _k === void 0 ? void 0 : _k.longName,
724
- routeShortName: (_l = leg.route) === null || _l === void 0 ? void 0 : _l.shortName,
725
- routeTextColor: (_m = leg.route) === null || _m === void 0 ? void 0 : _m.textColor,
726
- to: {
727
- ...leg.to,
728
- stopCode: (_o = leg.to.stop) === null || _o === void 0 ? void 0 : _o.code,
729
- stopId: (_p = leg.to.stop) === null || _p === void 0 ? void 0 : _p.gtfsId
730
- },
731
- tripHeadsign: (_q = leg.trip) === null || _q === void 0 ? void 0 : _q.tripHeadsign,
732
- tripId: (_r = leg.trip) === null || _r === void 0 ? void 0 : _r.gtfsId
733
- });
719
+ const convertGraphQLResponseToLegacy = leg => {
720
+ var _leg$agency, _leg$agency2, _leg$agency3, _leg$agency4, _leg$from$stop, _leg$from$stop2, _leg$route, _leg$route2, _leg$route3, _leg$route4, _leg$route5, _leg$route6, _leg$to$stop, _leg$to$stop2, _leg$trip, _leg$trip2;
721
+ return {
722
+ ...leg,
723
+ agencyBrandingUrl: (_leg$agency = leg.agency) === null || _leg$agency === void 0 ? void 0 : _leg$agency.url,
724
+ agencyId: (_leg$agency2 = leg.agency) === null || _leg$agency2 === void 0 ? void 0 : _leg$agency2.id,
725
+ agencyName: (_leg$agency3 = leg.agency) === null || _leg$agency3 === void 0 ? void 0 : _leg$agency3.name,
726
+ agencyUrl: (_leg$agency4 = leg.agency) === null || _leg$agency4 === void 0 ? void 0 : _leg$agency4.url,
727
+ alightRule: pickupDropoffTypeToOtp1(leg.dropoffType),
728
+ boardRule: pickupDropoffTypeToOtp1(leg.pickupType),
729
+ bookingRuleInfo: {
730
+ dropOff: (leg === null || leg === void 0 ? void 0 : leg.dropOffBookingInfo) || {},
731
+ pickUp: (leg === null || leg === void 0 ? void 0 : leg.pickUpBookingInfo) || {}
732
+ },
733
+ dropOffBookingInfo: {
734
+ latestBookingTime: leg.dropOffBookingInfo
735
+ },
736
+ from: {
737
+ ...leg.from,
738
+ stopCode: (_leg$from$stop = leg.from.stop) === null || _leg$from$stop === void 0 ? void 0 : _leg$from$stop.code,
739
+ stopId: (_leg$from$stop2 = leg.from.stop) === null || _leg$from$stop2 === void 0 ? void 0 : _leg$from$stop2.gtfsId
740
+ },
741
+ route: (_leg$route = leg.route) === null || _leg$route === void 0 ? void 0 : _leg$route.shortName,
742
+ routeColor: (_leg$route2 = leg.route) === null || _leg$route2 === void 0 ? void 0 : _leg$route2.color,
743
+ routeId: (_leg$route3 = leg.route) === null || _leg$route3 === void 0 ? void 0 : _leg$route3.gtfsId,
744
+ routeLongName: (_leg$route4 = leg.route) === null || _leg$route4 === void 0 ? void 0 : _leg$route4.longName,
745
+ routeShortName: (_leg$route5 = leg.route) === null || _leg$route5 === void 0 ? void 0 : _leg$route5.shortName,
746
+ routeTextColor: (_leg$route6 = leg.route) === null || _leg$route6 === void 0 ? void 0 : _leg$route6.textColor,
747
+ to: {
748
+ ...leg.to,
749
+ stopCode: (_leg$to$stop = leg.to.stop) === null || _leg$to$stop === void 0 ? void 0 : _leg$to$stop.code,
750
+ stopId: (_leg$to$stop2 = leg.to.stop) === null || _leg$to$stop2 === void 0 ? void 0 : _leg$to$stop2.gtfsId
751
+ },
752
+ tripHeadsign: (_leg$trip = leg.trip) === null || _leg$trip === void 0 ? void 0 : _leg$trip.tripHeadsign,
753
+ tripId: (_leg$trip2 = leg.trip) === null || _leg$trip2 === void 0 ? void 0 : _leg$trip2.gtfsId
754
+ };
734
755
  };
735
- exports.convertGraphQLResponseToLegacy = convertGraphQLResponseToLegacy;
756
+
736
757
  /** Extracts the route number for a leg returned from OTP1 or OTP2. */
737
- const getLegRouteShortName = (leg) => {
738
- const { route, routeShortName } = leg;
739
- // typeof route === "object" denotes newer OTP2 responses. routeShortName and route as string is OTP1.
740
- return typeof route === "object"
741
- ? route === null || route === void 0 ? void 0 : route.shortName
742
- : routeShortName || route;
758
+ exports.convertGraphQLResponseToLegacy = convertGraphQLResponseToLegacy;
759
+ const getLegRouteShortName = leg => {
760
+ const {
761
+ route,
762
+ routeShortName
763
+ } = leg;
764
+ // typeof route === "object" denotes newer OTP2 responses. routeShortName and route as string is OTP1.
765
+ return typeof route === "object" ? route === null || route === void 0 ? void 0 : route.shortName : routeShortName || route;
743
766
  };
744
- exports.getLegRouteShortName = getLegRouteShortName;
767
+
745
768
  /** Extract the route long name for a leg returned from OTP1 or OTP2. */
746
- const getLegRouteLongName = (leg) => {
747
- const { route, routeLongName } = leg;
748
- // typeof route === "object" denotes newer OTP2 responses. routeLongName is OTP1.
749
- return typeof route === "object" ? route === null || route === void 0 ? void 0 : route.longName : routeLongName;
769
+ exports.getLegRouteShortName = getLegRouteShortName;
770
+ const getLegRouteLongName = leg => {
771
+ const {
772
+ route,
773
+ routeLongName
774
+ } = leg;
775
+ // typeof route === "object" denotes newer OTP2 responses. routeLongName is OTP1.
776
+ return typeof route === "object" ? route === null || route === void 0 ? void 0 : route.longName : routeLongName;
750
777
  };
751
- exports.getLegRouteLongName = getLegRouteLongName;
778
+
752
779
  /**
753
780
  * Returns the route short name, or the route long name if no short name is provided.
754
781
  * This is happens with Seattle area streetcars and ferries.
755
782
  */
756
- const getLegRouteName = (leg) => {
757
- return (0, exports.getLegRouteShortName)(leg) || (0, exports.getLegRouteLongName)(leg);
783
+ exports.getLegRouteLongName = getLegRouteLongName;
784
+ const getLegRouteName = leg => {
785
+ return getLegRouteShortName(leg) || getLegRouteLongName(leg);
758
786
  };
759
787
  exports.getLegRouteName = getLegRouteName;
760
788
  //# sourceMappingURL=itinerary.js.map