@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.
- package/README.md +0 -4
- package/esm/itinerary.js +74 -32
- package/esm/itinerary.js.map +1 -1
- package/esm/otpSchema.json +7321 -5821
- package/esm/planQuery.graphql +12 -0
- package/esm/profile.js +1 -1
- package/esm/profile.js.map +1 -1
- package/esm/query-gen.js +2 -2
- package/esm/query-gen.js.map +1 -1
- package/esm/query.js +2 -5
- package/esm/query.js.map +1 -1
- package/esm/storage.js +2 -6
- package/esm/storage.js.map +1 -1
- package/esm/time.js +1 -2
- package/esm/time.js.map +1 -1
- package/lib/index.js +50 -35
- package/lib/index.js.map +1 -1
- package/lib/itinerary.d.ts +25 -8
- package/lib/itinerary.d.ts.map +1 -1
- package/lib/itinerary.js +524 -491
- package/lib/itinerary.js.map +1 -1
- package/lib/map.js +40 -39
- package/lib/map.js.map +1 -1
- package/lib/otpSchema.json +7321 -5821
- package/lib/planQuery.graphql +12 -0
- package/lib/profile.js +1 -1
- package/lib/profile.js.map +1 -1
- package/lib/query-gen.js +134 -138
- package/lib/query-gen.js.map +1 -1
- package/lib/query.js +3 -6
- package/lib/query.js.map +1 -1
- package/lib/route.js +230 -248
- package/lib/route.js.map +1 -1
- package/lib/storage.d.ts.map +1 -1
- package/lib/storage.js +22 -28
- package/lib/storage.js.map +1 -1
- package/lib/suspense.js +28 -16
- package/lib/suspense.js.map +1 -1
- package/lib/time.js +36 -49
- package/lib/time.js.map +1 -1
- package/lib/ui.js +33 -36
- package/lib/ui.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/__snapshots__/query-params.ts.snap +201 -201
- package/src/__tests__/__snapshots__/query.js.snap +28 -28
- package/src/__tests__/__snapshots__/route.js.snap +76 -76
- package/src/__tests__/__snapshots__/time.js.snap +2 -2
- package/src/__tests__/itinerary.ts +108 -23
- package/src/__tests__/query.js +1 -1
- package/src/__tests__/route.js +1 -1
- package/src/core-utils.story.tsx +34 -23
- package/src/itinerary.ts +88 -37
- package/src/otpSchema.json +7321 -5821
- package/src/planQuery.graphql +12 -0
- package/src/profile.js +1 -1
- package/src/query-gen.ts +1 -1
- package/src/query.js +2 -5
- package/src/storage.ts +5 -9
- package/tsconfig.tsbuildinfo +1 -1
package/lib/itinerary.js
CHANGED
|
@@ -1,378 +1,385 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
31
|
+
return leg.transitLeg;
|
|
70
32
|
}
|
|
33
|
+
exports.isTransitLeg = isTransitLeg;
|
|
71
34
|
function isTransit(mode) {
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
63
|
+
return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.to);
|
|
97
64
|
}
|
|
65
|
+
exports.endsWithGeometry = endsWithGeometry;
|
|
98
66
|
function startsWithGeometry(leg) {
|
|
99
|
-
|
|
67
|
+
return containsGeometry(leg === null || leg === void 0 ? void 0 : leg.from);
|
|
100
68
|
}
|
|
69
|
+
exports.startsWithGeometry = startsWithGeometry;
|
|
101
70
|
function legContainsGeometry(leg) {
|
|
102
|
-
|
|
71
|
+
return endsWithGeometry(leg) || startsWithGeometry(leg);
|
|
103
72
|
}
|
|
73
|
+
exports.legContainsGeometry = legContainsGeometry;
|
|
104
74
|
function isAdvanceBookingRequired(info) {
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
127
|
-
|
|
101
|
+
if (!mode)
|
|
102
|
+
return false;
|
|
103
|
+
return mode === "WALK";
|
|
128
104
|
}
|
|
105
|
+
exports.isWalk = isWalk;
|
|
129
106
|
function isBicycle(mode) {
|
|
130
|
-
|
|
131
|
-
|
|
107
|
+
if (!mode)
|
|
108
|
+
return false;
|
|
109
|
+
return mode === "BICYCLE";
|
|
132
110
|
}
|
|
111
|
+
exports.isBicycle = isBicycle;
|
|
133
112
|
function isBicycleRent(mode) {
|
|
134
|
-
|
|
135
|
-
|
|
113
|
+
if (!mode)
|
|
114
|
+
return false;
|
|
115
|
+
return mode === "BICYCLE_RENT";
|
|
136
116
|
}
|
|
117
|
+
exports.isBicycleRent = isBicycleRent;
|
|
137
118
|
function isCar(mode) {
|
|
138
|
-
|
|
139
|
-
|
|
119
|
+
if (!mode)
|
|
120
|
+
return false;
|
|
121
|
+
return mode.startsWith("CAR");
|
|
140
122
|
}
|
|
123
|
+
exports.isCar = isCar;
|
|
141
124
|
function isMicromobility(mode) {
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
185
|
+
return modesStr.split(",").some(mode => mode.indexOf("_RENT") > -1);
|
|
195
186
|
}
|
|
187
|
+
exports.hasRental = hasRental;
|
|
196
188
|
function getMapColor(mode) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
mode,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
rideHailingEstimate
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
coords.
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
bikeDuration
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
|
551
|
-
* @param
|
|
552
|
-
* @param
|
|
553
|
-
* @
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
-
|
|
669
|
-
|
|
670
|
-
route,
|
|
671
|
-
routeShortName
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
route,
|
|
682
|
-
routeLongName
|
|
683
|
-
|
|
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
|
-
|
|
693
|
-
|
|
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
|