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