@opentripplanner/core-utils 4.11.6-alpha.1 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/esm/deprecated-with-types.js +47 -0
  2. package/esm/deprecated-with-types.js.map +1 -0
  3. package/esm/index.js +0 -4
  4. package/esm/index.js.map +1 -1
  5. package/esm/itinerary.js +1 -1
  6. package/esm/itinerary.js.map +1 -1
  7. package/esm/map.js +16 -8
  8. package/esm/map.js.map +1 -1
  9. package/esm/route.js +5 -3
  10. package/esm/route.js.map +1 -1
  11. package/esm/storage.js +1 -0
  12. package/esm/storage.js.map +1 -1
  13. package/esm/time.js +6 -36
  14. package/esm/time.js.map +1 -1
  15. package/esm/ui.js +1 -1
  16. package/esm/ui.js.map +1 -1
  17. package/lib/deprecated-with-types.d.ts +23 -0
  18. package/lib/deprecated-with-types.d.ts.map +1 -0
  19. package/lib/deprecated-with-types.js +61 -0
  20. package/lib/deprecated-with-types.js.map +1 -0
  21. package/lib/index.d.ts +19 -0
  22. package/lib/index.d.ts.map +1 -0
  23. package/lib/index.js +0 -6
  24. package/lib/index.js.map +1 -1
  25. package/lib/itinerary.d.ts +113 -0
  26. package/lib/itinerary.d.ts.map +1 -0
  27. package/lib/itinerary.js +2 -1
  28. package/lib/itinerary.js.map +1 -1
  29. package/lib/map.d.ts +30 -0
  30. package/lib/map.d.ts.map +1 -0
  31. package/lib/map.js +15 -7
  32. package/lib/map.js.map +1 -1
  33. package/lib/route.d.ts +98 -0
  34. package/lib/route.d.ts.map +1 -0
  35. package/lib/route.js +5 -3
  36. package/lib/route.js.map +1 -1
  37. package/lib/storage.d.ts +19 -0
  38. package/lib/storage.d.ts.map +1 -0
  39. package/lib/storage.js +2 -0
  40. package/lib/storage.js.map +1 -1
  41. package/lib/time.d.ts +65 -0
  42. package/lib/time.d.ts.map +1 -0
  43. package/lib/time.js +22 -39
  44. package/lib/time.js.map +1 -1
  45. package/lib/ui.d.ts +13 -0
  46. package/lib/ui.d.ts.map +1 -0
  47. package/lib/ui.js +1 -1
  48. package/lib/ui.js.map +1 -1
  49. package/package.json +4 -1
  50. package/src/__tests__/__snapshots__/route.js.snap +30 -30
  51. package/src/deprecated-with-types.ts +62 -0
  52. package/src/{index.js → index.ts} +0 -4
  53. package/src/{itinerary.js → itinerary.ts} +70 -36
  54. package/src/{map.js → map.ts} +51 -28
  55. package/src/{route.js → route.ts} +52 -28
  56. package/src/{storage.js → storage.ts} +6 -5
  57. package/src/{time.js → time.ts} +28 -46
  58. package/src/{ui.js → ui.ts} +8 -8
  59. package/tsconfig.json +15 -0
  60. package/tsconfig.tsbuildinfo +4921 -0
  61. package/esm/messages.js +0 -25
  62. package/esm/messages.js.map +0 -1
  63. package/esm/types.js +0 -560
  64. package/esm/types.js.map +0 -1
  65. package/lib/messages.js +0 -29
  66. package/lib/messages.js.map +0 -1
  67. package/lib/types.js +0 -661
  68. package/lib/types.js.map +0 -1
  69. package/src/messages.js +0 -20
  70. package/src/types.js +0 -605
@@ -1,4 +1,14 @@
1
1
  import polyline from "@mapbox/polyline";
2
+ import {
3
+ Company,
4
+ Config,
5
+ ElevationProfile,
6
+ FlexBookingInfo,
7
+ Itinerary,
8
+ LatLngArray,
9
+ Leg,
10
+ Step
11
+ } from "@opentripplanner/types";
2
12
  import turfAlong from "@turf/along";
3
13
 
4
14
  import {
@@ -40,13 +50,16 @@ export const transitModes = [
40
50
  * @return {Array} List of all transit modes defined in config; otherwise default mode list
41
51
  */
42
52
 
43
- export function getTransitModes(config) {
53
+ export function getTransitModes(config: Config): string[] {
44
54
  if (!config || !config.modes || !config.modes.transitModes)
45
55
  return transitModes;
46
- return config.modes.transitModes.map(tm => tm.mode);
56
+
57
+ return config.modes.transitModes.map(tm =>
58
+ typeof tm !== "string" ? tm.mode : tm
59
+ );
47
60
  }
48
61
 
49
- export function isTransit(mode) {
62
+ export function isTransit(mode: string): boolean {
50
63
  return transitModes.includes(mode) || mode === "TRANSIT";
51
64
  }
52
65
 
@@ -55,7 +68,7 @@ export function isTransit(mode) {
55
68
  * calling ahead for the service to run. "mustPhone" is the only
56
69
  * property of boardRule which encodes this info.
57
70
  */
58
- export function isReservationRequired(leg) {
71
+ export function isReservationRequired(leg: Leg): boolean {
59
72
  return leg.boardRule === "mustPhone";
60
73
  }
61
74
  /**
@@ -63,53 +76,53 @@ export function isReservationRequired(leg) {
63
76
  * asking the driver to let the user off. "coordinateWithDriver" is the only
64
77
  * property of alightRule which encodes this info.
65
78
  */
66
- export function isContinuousDropoff(leg) {
79
+ export function isContinuousDropoff(leg: Leg): boolean {
67
80
  return leg.alightRule === "coordinateWithDriver";
68
81
  }
69
82
  /**
70
83
  * The two rules checked by the above two functions are the only values
71
84
  * returned by OTP when a leg is a flex leg.
72
85
  */
73
- export function isFlex(leg) {
86
+ export function isFlex(leg: Leg): boolean {
74
87
  return isReservationRequired(leg) || isContinuousDropoff(leg);
75
88
  }
76
89
 
77
- export function isAdvanceBookingRequired(info) {
90
+ export function isAdvanceBookingRequired(info: FlexBookingInfo): boolean {
78
91
  return info?.latestBookingTime?.daysPrior > 0;
79
92
  }
80
- export function legDropoffRequiresAdvanceBooking(leg) {
93
+ export function legDropoffRequiresAdvanceBooking(leg: Leg): boolean {
81
94
  return isAdvanceBookingRequired(leg.dropOffBookingInfo);
82
95
  }
83
96
 
84
- export function isWalk(mode) {
97
+ export function isWalk(mode: string): boolean {
85
98
  if (!mode) return false;
86
99
 
87
100
  return mode === "WALK";
88
101
  }
89
102
 
90
- export function isBicycle(mode) {
103
+ export function isBicycle(mode: string): boolean {
91
104
  if (!mode) return false;
92
105
 
93
106
  return mode === "BICYCLE";
94
107
  }
95
108
 
96
- export function isBicycleRent(mode) {
109
+ export function isBicycleRent(mode: string): boolean {
97
110
  if (!mode) return false;
98
111
 
99
112
  return mode === "BICYCLE_RENT";
100
113
  }
101
114
 
102
- export function isCar(mode) {
115
+ export function isCar(mode: string): boolean {
103
116
  if (!mode) return false;
104
117
  return mode.startsWith("CAR");
105
118
  }
106
119
 
107
- export function isMicromobility(mode) {
120
+ export function isMicromobility(mode: string): boolean {
108
121
  if (!mode) return false;
109
122
  return mode.startsWith("MICROMOBILITY") || mode.startsWith("SCOOTER");
110
123
  }
111
124
 
112
- export function isAccessMode(mode) {
125
+ export function isAccessMode(mode: string): boolean {
113
126
  return (
114
127
  isWalk(mode) ||
115
128
  isBicycle(mode) ||
@@ -123,7 +136,7 @@ export function isAccessMode(mode) {
123
136
  * @param {string} modesStr a comma-separated list of OTP modes
124
137
  * @return {boolean} whether any of the modes are transit modes
125
138
  */
126
- export function hasTransit(modesStr) {
139
+ export function hasTransit(modesStr: string): boolean {
127
140
  return modesStr.split(",").some(mode => isTransit(mode));
128
141
  }
129
142
 
@@ -131,7 +144,7 @@ export function hasTransit(modesStr) {
131
144
  * @param {string} modesStr a comma-separated list of OTP modes
132
145
  * @return {boolean} whether any of the modes are car-based modes
133
146
  */
134
- export function hasCar(modesStr) {
147
+ export function hasCar(modesStr: string): boolean {
135
148
  return modesStr.split(",").some(mode => isCar(mode));
136
149
  }
137
150
 
@@ -139,7 +152,7 @@ export function hasCar(modesStr) {
139
152
  * @param {string} modesStr a comma-separated list of OTP modes
140
153
  * @return {boolean} whether any of the modes are bicycle-based modes
141
154
  */
142
- export function hasBike(modesStr) {
155
+ export function hasBike(modesStr: string): boolean {
143
156
  return modesStr
144
157
  .split(",")
145
158
  .some(mode => isBicycle(mode) || isBicycleRent(mode));
@@ -149,7 +162,7 @@ export function hasBike(modesStr) {
149
162
  * @param {string} modesStr a comma-separated list of OTP modes
150
163
  * @return {boolean} whether any of the modes are micromobility-based modes
151
164
  */
152
- export function hasMicromobility(modesStr) {
165
+ export function hasMicromobility(modesStr: string): boolean {
153
166
  return modesStr.split(",").some(mode => isMicromobility(mode));
154
167
  }
155
168
 
@@ -157,7 +170,7 @@ export function hasMicromobility(modesStr) {
157
170
  * @param {string} modesStr a comma-separated list of OTP modes
158
171
  * @return {boolean} whether any of the modes is a hailing mode
159
172
  */
160
- export function hasHail(modesStr) {
173
+ export function hasHail(modesStr: string): boolean {
161
174
  return modesStr.split(",").some(mode => mode.indexOf("_HAIL") > -1);
162
175
  }
163
176
 
@@ -165,11 +178,11 @@ export function hasHail(modesStr) {
165
178
  * @param {string} modesStr a comma-separated list of OTP modes
166
179
  * @return {boolean} whether any of the modes is a rental mode
167
180
  */
168
- export function hasRental(modesStr) {
181
+ export function hasRental(modesStr: string): boolean {
169
182
  return modesStr.split(",").some(mode => mode.indexOf("_RENT") > -1);
170
183
  }
171
184
 
172
- export function getMapColor(mode) {
185
+ export function getMapColor(mode: string): string {
173
186
  mode = mode || this.get("mode");
174
187
  if (mode === "WALK") return "#444";
175
188
  if (mode === "BICYCLE") return "#0073e5";
@@ -183,7 +196,7 @@ export function getMapColor(mode) {
183
196
  return "#aaa";
184
197
  }
185
198
 
186
- export function toSentenceCase(str) {
199
+ export function toSentenceCase(str: string): string {
187
200
  if (str == null) {
188
201
  return "";
189
202
  }
@@ -194,7 +207,7 @@ export function toSentenceCase(str) {
194
207
  /**
195
208
  * Derive the company string based on mode and network associated with leg.
196
209
  */
197
- export function getCompanyFromLeg(leg) {
210
+ export function getCompanyFromLeg(leg: Leg): string {
198
211
  if (!leg) return null;
199
212
  const { from, mode, rentedBike, rentedCar, rentedVehicle, tncData } = leg;
200
213
  if (mode === "CAR" && rentedCar) {
@@ -216,12 +229,12 @@ export function getCompanyFromLeg(leg) {
216
229
  return null;
217
230
  }
218
231
 
219
- export function getItineraryBounds(itinerary) {
232
+ export function getItineraryBounds(itinerary: Itinerary): LatLngArray[] {
220
233
  let coords = [];
221
234
  itinerary.legs.forEach(leg => {
222
235
  const legCoords = polyline
223
236
  .toGeoJSON(leg.legGeometry.points)
224
- .coordinates.map(c => [c[1], c[0]]);
237
+ .coordinates.map((c: number[]) => [c[1], c[0]]);
225
238
  coords = [...coords, ...legCoords];
226
239
  });
227
240
  return coords;
@@ -230,7 +243,7 @@ export function getItineraryBounds(itinerary) {
230
243
  /**
231
244
  * Return a coords object that encloses the given leg's geometry.
232
245
  */
233
- export function getLegBounds(leg) {
246
+ export function getLegBounds(leg: Leg): number[] {
234
247
  const coords = polyline
235
248
  .toGeoJSON(leg.legGeometry.points)
236
249
  .coordinates.map(c => [c[1], c[0]]);
@@ -246,7 +259,7 @@ export function getLegBounds(leg) {
246
259
 
247
260
  /* Returns an interpolated lat-lon at a specified distance along a leg */
248
261
 
249
- export function legLocationAtDistance(leg, distance) {
262
+ export function legLocationAtDistance(leg: Leg, distance: number): number[] {
250
263
  if (!leg.legGeometry) return null;
251
264
 
252
265
  try {
@@ -264,7 +277,10 @@ export function legLocationAtDistance(leg, distance) {
264
277
 
265
278
  /* Returns an interpolated elevation at a specified distance along a leg */
266
279
 
267
- export function legElevationAtDistance(points, distance) {
280
+ export function legElevationAtDistance(
281
+ points: number[][],
282
+ distance: number
283
+ ): number {
268
284
  // Iterate through the combined elevation profile
269
285
  let traversed = 0;
270
286
  // If first point distance is not zero, insert starting point at zero with
@@ -302,7 +318,10 @@ export function legElevationAtDistance(points, distance) {
302
318
 
303
319
  // Iterate through the steps, building the array of elevation points and
304
320
  // keeping track of the minimum and maximum elevations reached
305
- export function getElevationProfile(steps, unitConversion = 1) {
321
+ export function getElevationProfile(
322
+ steps: Step[],
323
+ unitConversion = 1
324
+ ): ElevationProfile {
306
325
  let minElev = 100000;
307
326
  let maxElev = -100000;
308
327
  let traversed = 0;
@@ -349,11 +368,14 @@ export function getElevationProfile(steps, unitConversion = 1) {
349
368
  *
350
369
  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
351
370
  */
352
- export function getTextWidth(text, font = "22px Arial") {
371
+ export function getTextWidth(text: string, font = "22px Arial"): number {
372
+ // Create custom type for function including re-used canvas object
373
+ type GetTextWidth = typeof getTextWidth & { canvas: HTMLCanvasElement };
374
+
353
375
  // re-use canvas object for better performance
354
376
  const canvas =
355
- getTextWidth.canvas ||
356
- (getTextWidth.canvas = document.createElement("canvas"));
377
+ (getTextWidth as GetTextWidth).canvas ||
378
+ ((getTextWidth as GetTextWidth).canvas = document.createElement("canvas"));
357
379
  const context = canvas.getContext("2d");
358
380
  context.font = font;
359
381
  const metrics = context.measureText(text);
@@ -364,7 +386,10 @@ export function getTextWidth(text, font = "22px Arial") {
364
386
  * Get the configured company object for the given network string if the company
365
387
  * has been defined in the provided companies array config.
366
388
  */
367
- export function getCompanyForNetwork(networkString, companies = []) {
389
+ export function getCompanyForNetwork(
390
+ networkString: string,
391
+ companies: Company[] = []
392
+ ): Company {
368
393
  const company = companies.find(co => co.id === networkString);
369
394
  if (!company) {
370
395
  console.warn(
@@ -382,7 +407,10 @@ export function getCompanyForNetwork(networkString, companies = []) {
382
407
  * @param {Array<object>} [companies=[]] An optional list of the companies config.
383
408
  * @return {string} A label for use in presentation on a website.
384
409
  */
385
- export function getCompaniesLabelFromNetworks(networks, companies = []) {
410
+ export function getCompaniesLabelFromNetworks(
411
+ networks: string[],
412
+ companies: Company[] = []
413
+ ): string {
386
414
  return networks
387
415
  .map(network => getCompanyForNetwork(network, companies))
388
416
  .filter(co => !!co)
@@ -390,12 +418,18 @@ export function getCompaniesLabelFromNetworks(networks, companies = []) {
390
418
  .join("/");
391
419
  }
392
420
 
393
- export function getTNCLocation(leg, type) {
421
+ export function getTNCLocation(leg: Leg, type: string): string {
394
422
  const location = leg[type];
395
423
  return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}`;
396
424
  }
397
425
 
398
- export function calculatePhysicalActivity(itinerary) {
426
+ export function calculatePhysicalActivity(
427
+ itinerary: Itinerary
428
+ ): {
429
+ bikeDuration: number;
430
+ caloriesBurned: number;
431
+ walkDuration: number;
432
+ } {
399
433
  let walkDuration = 0;
400
434
  let bikeDuration = 0;
401
435
  itinerary.legs.forEach(leg => {
@@ -1,9 +1,18 @@
1
+ import {
2
+ Company,
3
+ Itinerary,
4
+ LatLngArray,
5
+ Leg,
6
+ Location,
7
+ TransitiveData,
8
+ UserPosition
9
+ } from "@opentripplanner/types";
1
10
  import {
2
11
  getPlaceName,
3
- isTransit,
12
+ isAccessMode,
4
13
  isFlex,
5
- toSentenceCase,
6
- isAccessMode
14
+ isTransit,
15
+ toSentenceCase
7
16
  } from "./itinerary";
8
17
 
9
18
  import {
@@ -15,7 +24,9 @@ import {
15
24
 
16
25
  export { coordsToString, getDetailText, latlngToString };
17
26
 
18
- export function currentPositionToLocation(currentPosition) {
27
+ export function currentPositionToLocation(
28
+ currentPosition: UserPosition
29
+ ): Location {
19
30
  if (currentPosition.error || !currentPosition.coords) {
20
31
  console.warn(
21
32
  "Cannot construct location from current position due to geolocation error or missing coordinates."
@@ -29,18 +40,24 @@ export function currentPositionToLocation(currentPosition) {
29
40
  };
30
41
  }
31
42
 
32
- export function stringToCoords(str) {
43
+ export function stringToCoords(str: string): number[] {
33
44
  return (str && str.split(",").map(c => +c)) || [];
34
45
  }
35
46
 
36
- export function constructLocation(latlng) {
47
+ export function constructLocation(latlng: {
48
+ lat: number;
49
+ lng: number;
50
+ }): Location {
37
51
  return {
38
52
  lat: latlng.lat,
39
53
  lon: latlng.lng
40
54
  };
41
55
  }
42
56
 
43
- export function formatStoredPlaceName(location, withDetails = true) {
57
+ export function formatStoredPlaceName(
58
+ location: Location,
59
+ withDetails = true
60
+ ): string {
44
61
  if (withDetails) {
45
62
  logDeprecationWarning("the formatStoredPlaceName withDetails parameter");
46
63
  }
@@ -56,7 +73,7 @@ export function formatStoredPlaceName(location, withDetails = true) {
56
73
  return displayName;
57
74
  }
58
75
 
59
- export function matchLatLon(location1, location2) {
76
+ export function matchLatLon(location1: Location, location2: Location): boolean {
60
77
  if (!location1 || !location2) return location1 === location2;
61
78
  return location1.lat === location2.lat && location1.lon === location2.lon;
62
79
  }
@@ -70,11 +87,11 @@ export function matchLatLon(location1, location2) {
70
87
  * @returns An itinerary in the transitive.js format.
71
88
  */
72
89
  export function itineraryToTransitive(
73
- itin,
74
- companies,
75
- getRouteLabel,
76
- disableFlexArc
77
- ) {
90
+ itin: Itinerary,
91
+ companies: Company[],
92
+ getRouteLabel: (leg: Leg) => string,
93
+ disableFlexArc: boolean
94
+ ): TransitiveData {
78
95
  const tdata = {
79
96
  journeys: [],
80
97
  streetEdges: [],
@@ -109,20 +126,17 @@ export function itineraryToTransitive(
109
126
 
110
127
  itin.legs.forEach((leg, idx) => {
111
128
  if (isAccessMode(leg.mode)) {
112
- let fromPlaceId;
113
- if (
114
- leg.from.bikeShareId &&
115
- (leg.mode === "BICYCLE" || leg.mode === "BICYCLE_RENT")
116
- ) {
129
+ let fromPlaceId: string;
130
+ if (leg.from.bikeShareId) {
117
131
  fromPlaceId = `bicycle_rent_station_${leg.from.bikeShareId}`;
118
132
  if (
119
133
  // OTP2 Scooter case
120
134
  leg.mode === "SCOOTER"
121
135
  ) {
122
- fromPlaceId = `escooter_rent_station_${leg.from.name}`;
136
+ fromPlaceId = `escooter_rent_station_${leg.from.bikeShareId}`;
123
137
  }
124
- // OTP1 Scooter case
125
138
  } else if (leg.from.vertexType === "VEHICLERENTAL") {
139
+ // OTP1 Scooter case
126
140
  fromPlaceId = `escooter_rent_station_${leg.from.name}`;
127
141
  } else if (
128
142
  leg.mode === "CAR" &&
@@ -131,13 +145,18 @@ export function itineraryToTransitive(
131
145
  ) {
132
146
  // create a special place ID for car legs preceded by walking legs
133
147
  fromPlaceId = `itin_car_${streetEdgeId}_from`;
134
- } else {
148
+ } else if (!fromPlaceId) {
135
149
  fromPlaceId = `itin_street_${streetEdgeId}_from`;
136
150
  }
137
151
 
138
152
  let toPlaceId;
139
153
  if (leg.to.bikeShareId) {
140
154
  toPlaceId = `bicycle_rent_station_${leg.to.bikeShareId}`;
155
+ // OTP2 scooter case
156
+ // Need to check next leg since this is a "to" place "
157
+ if (leg.mode === "SCOOTER" || itin.legs?.[idx + 1].mode === "SCOOTER") {
158
+ toPlaceId = `escooter_rent_station_${leg.to.bikeShareId}`;
159
+ }
141
160
  } else if (leg.to.vertexType === "VEHICLERENTAL") {
142
161
  toPlaceId = `escooter_rent_station_${leg.to.name}`;
143
162
  } else if (
@@ -147,11 +166,12 @@ export function itineraryToTransitive(
147
166
  ) {
148
167
  // create a special place ID for car legs followed by walking legs
149
168
  toPlaceId = `itin_car_${streetEdgeId}_to`;
150
- } else {
169
+ } else if (!toPlaceId) {
151
170
  toPlaceId = `itin_street_${streetEdgeId}_to`;
152
171
  }
153
172
 
154
173
  const segment = {
174
+ arc: false,
155
175
  type: leg.mode,
156
176
  streetEdges: [streetEdgeId],
157
177
  from: { type: "PLACE", place_id: fromPlaceId },
@@ -307,27 +327,30 @@ export function itineraryToTransitive(
307
327
  return tdata;
308
328
  }
309
329
 
310
- export function isBikeshareStation(place) {
330
+ type TransitivePlaceRaw = {
331
+ place_id: string;
332
+ };
333
+ export function isBikeshareStation(place: TransitivePlaceRaw): boolean {
311
334
  return place.place_id.lastIndexOf("bicycle_rent_station") !== -1;
312
335
  }
313
336
 
314
- export function isEScooterStation(place) {
337
+ export function isEScooterStation(place: TransitivePlaceRaw): boolean {
315
338
  return place.place_id.lastIndexOf("escooter_rent_station") !== -1;
316
339
  }
317
340
 
318
- export function isCarWalkTransition(place) {
341
+ export function isCarWalkTransition(place: TransitivePlaceRaw): boolean {
319
342
  return place.place_id.lastIndexOf("itin_car_") !== -1;
320
343
  }
321
344
 
322
- export function isValidLat(lat) {
345
+ export function isValidLat(lat: number): boolean {
323
346
  return Number.isFinite(lat) && lat >= -90 && lat <= 90;
324
347
  }
325
348
 
326
- export function isValidLng(lng) {
349
+ export function isValidLng(lng: number): boolean {
327
350
  return Number.isFinite(lng) && lng >= -180 && lng <= 180;
328
351
  }
329
352
 
330
- export function isValidLatLng(arr) {
353
+ export function isValidLatLng(arr: LatLngArray): boolean {
331
354
  return (
332
355
  Array.isArray(arr) &&
333
356
  arr.length === 2 &&
@@ -1,3 +1,4 @@
1
+ import { Leg, Route, TransitOperator } from "@opentripplanner/types";
1
2
  /**
2
3
  * Returns the transit operator (if an exact match is found) from the transit
3
4
  * operators config value. It is critical to use both the feedId and agencyId in
@@ -11,10 +12,10 @@
11
12
  * was found
12
13
  */
13
14
  export function getTransitOperatorFromFeedIdAndAgencyId(
14
- feedId,
15
- agencyId,
16
- transitOperators
17
- ) {
15
+ feedId: string,
16
+ agencyId: string | number,
17
+ transitOperators: TransitOperator[]
18
+ ): TransitOperator {
18
19
  return (
19
20
  transitOperators.find(
20
21
  transitOperator =>
@@ -32,7 +33,10 @@ export function getTransitOperatorFromFeedIdAndAgencyId(
32
33
  * @param {object} transitOperators transitOperators from config.
33
34
  * @return {object} the operator if one was found or null if no match was found
34
35
  */
35
- export function getTransitOperatorFromLeg(leg, transitOperators) {
36
+ export function getTransitOperatorFromLeg(
37
+ leg: Leg,
38
+ transitOperators: TransitOperator[]
39
+ ): TransitOperator {
36
40
  if (!leg.routeId || !leg.agencyId) return null;
37
41
  const feedId = leg.routeId.split(":")[0];
38
42
  return getTransitOperatorFromFeedIdAndAgencyId(
@@ -52,10 +56,13 @@ export function getTransitOperatorFromLeg(leg, transitOperators) {
52
56
  * @param {array} transitOperators transitOperators from config
53
57
  * @return {object} the operator if one was found or null if no match was found
54
58
  */
55
- export function getTransitOperatorFromOtpRoute(route, transitOperators) {
59
+ export function getTransitOperatorFromOtpRoute(
60
+ route: Route,
61
+ transitOperators: TransitOperator[]
62
+ ): TransitOperator {
56
63
  if (!route.id) return null;
57
64
  const feedId = route.id.split(":")[0];
58
- let agencyId;
65
+ let agencyId: string | number;
59
66
  if (route.agency) {
60
67
  // This is returned in the OTP Route model
61
68
  agencyId = route.agency.id;
@@ -97,7 +104,10 @@ const END_OF_LIST_COMPARATOR_VALUE = 999999999999;
97
104
  * the transitOperators value is not defined. Otherwise an integer will be
98
105
  * returned.
99
106
  */
100
- function getTransitOperatorComparatorValue(route, transitOperators) {
107
+ function getTransitOperatorComparatorValue(
108
+ route: Route,
109
+ transitOperators: TransitOperator[]
110
+ ): number | string {
101
111
  // if the transitOperators is undefined or has zero length, use the route's
102
112
  // agency name as the comparator value
103
113
  if (!transitOperators || transitOperators.length === 0) {
@@ -129,8 +139,8 @@ function getTransitOperatorComparatorValue(route, transitOperators) {
129
139
  * Calculates the sort comparator value given two routes based off of the
130
140
  * route's agency and provided transitOperators config data.
131
141
  */
132
- function makeTransitOperatorComparator(transitOperators) {
133
- return (a, b) => {
142
+ function makeTransitOperatorComparator(transitOperators: TransitOperator[]) {
143
+ return (a: Route, b: Route) => {
134
144
  const aVal = getTransitOperatorComparatorValue(a, transitOperators);
135
145
  const bVal = getTransitOperatorComparatorValue(b, transitOperators);
136
146
  if (typeof aVal === "string") {
@@ -140,8 +150,7 @@ function makeTransitOperatorComparator(transitOperators) {
140
150
  if (aVal > bVal) return 1;
141
151
  return 0;
142
152
  }
143
-
144
- // transitOperators are defined and therefore a numeric value is guaranteed
153
+ // @ts-expect-error transitOperators are defined and therefore a numeric value is guaranteed
145
154
  // to be returned
146
155
  return aVal - bVal;
147
156
  };
@@ -151,9 +160,13 @@ function makeTransitOperatorComparator(transitOperators) {
151
160
  * Gets the desired sort values according to an optional getter function. If the
152
161
  * getter function is not defined, the original sort values are returned.
153
162
  */
154
- function getSortValues(getterFn, a, b) {
155
- let aVal;
156
- let bVal;
163
+ function getSortValues(
164
+ getterFn: (item: unknown) => unknown,
165
+ a: unknown,
166
+ b: unknown
167
+ ) {
168
+ let aVal: unknown;
169
+ let bVal: unknown;
157
170
  if (typeof getterFn === "function") {
158
171
  aVal = getterFn(a);
159
172
  bVal = getterFn(b);
@@ -199,11 +212,11 @@ const routeTypeComparatorValue = {
199
212
  // Gets a comparator value for a given route's type (OTP mode).
200
213
  // Note: JSDoc format not used to avoid bug in documentationjs.
201
214
  // ttps://github.com/documentationjs/documentation/issues/372
202
- function getRouteTypeComparatorValue(route) {
215
+ function getRouteTypeComparatorValue(route: Route): number {
203
216
  // For some strange reason, the short route response in OTP returns the
204
217
  // string-based modes, but the long route response returns the
205
218
  // integer route type. This attempts to account for both of those cases.
206
- if (!route) throw new Error("Route is undefined.", route);
219
+ if (!route) throw new Error(`Route is undefined. ${route}`);
207
220
  if (typeof modeComparatorValue[route.mode] !== "undefined") {
208
221
  return modeComparatorValue[route.mode];
209
222
  }
@@ -212,6 +225,7 @@ function getRouteTypeComparatorValue(route) {
212
225
  }
213
226
  // Default the comparator value to a large number (placing the route at the
214
227
  // end of the list).
228
+ // eslint-disable-next-line no-console
215
229
  console.warn("no mode/route type found for route", route);
216
230
  return END_OF_LIST_COMPARATOR_VALUE;
217
231
  }
@@ -220,7 +234,7 @@ function getRouteTypeComparatorValue(route) {
220
234
  * Calculates the sort comparator value given two routes based off of route type
221
235
  * (OTP mode).
222
236
  */
223
- function routeTypeComparator(a, b) {
237
+ function routeTypeComparator(a: Route, b: Route): number {
224
238
  return getRouteTypeComparatorValue(a) - getRouteTypeComparatorValue(b);
225
239
  }
226
240
 
@@ -228,7 +242,7 @@ function routeTypeComparator(a, b) {
228
242
  * Determines whether a value is a string that starts with an alphabetic
229
243
  * ascii character.
230
244
  */
231
- function startsWithAlphabeticCharacter(val) {
245
+ function startsWithAlphabeticCharacter(val: unknown): boolean {
232
246
  if (typeof val === "string" && val.length > 0) {
233
247
  const firstCharCode = val.charCodeAt(0);
234
248
  return (
@@ -244,7 +258,7 @@ function startsWithAlphabeticCharacter(val) {
244
258
  * character. Routes with shortn that do start with an alphabetic character will
245
259
  * be prioritized over those that don't.
246
260
  */
247
- function alphabeticShortNameComparator(a, b) {
261
+ function alphabeticShortNameComparator(a: Route, b: Route): number {
248
262
  const aStartsWithAlphabeticCharacter = startsWithAlphabeticCharacter(
249
263
  a.shortName
250
264
  );
@@ -284,11 +298,15 @@ function alphabeticShortNameComparator(a, b) {
284
298
  * @param {function} [objGetterFn] An optional function to obtain the
285
299
  * comparison value from the comparator function arguments
286
300
  */
287
- export function makeNumericValueComparator(objGetterFn) {
301
+ export function makeNumericValueComparator(
302
+ objGetterFn?: (item: Route) => number
303
+ ) {
288
304
  /* Note: Using the global version of isNaN (the Number version behaves differently. */
289
305
  /* eslint-disable no-restricted-globals */
290
- return (a, b) => {
306
+ return (a: number, b: number): number => {
291
307
  const { aVal, bVal } = getSortValues(objGetterFn, a, b);
308
+ if (typeof aVal !== "number" || typeof bVal !== "number") return 0;
309
+
292
310
  // if both values aren't valid numbers, use the next sort criteria
293
311
  if (isNaN(aVal) && isNaN(bVal)) return 0;
294
312
  // b is a valid number, b gets priority
@@ -310,8 +328,10 @@ export function makeNumericValueComparator(objGetterFn) {
310
328
  * @param {function} [objGetterFn] An optional function to obtain the
311
329
  * comparison value from the comparator function arguments
312
330
  */
313
- export function makeStringValueComparator(objGetterFn) {
314
- return (a, b) => {
331
+ export function makeStringValueComparator(
332
+ objGetterFn?: (item: Route) => string
333
+ ) {
334
+ return (a: string, b: string): number => {
315
335
  const { aVal, bVal } = getSortValues(objGetterFn, a, b);
316
336
  // both a and b are uncomparable strings, return equivalent value
317
337
  if (!aVal && !bVal) return 0;
@@ -334,7 +354,7 @@ export function makeStringValueComparator(objGetterFn) {
334
354
  * See https://github.com/opentripplanner/OpenTripPlanner/issues/2938
335
355
  * Also see https://github.com/opentripplanner/otp-react-redux/issues/122
336
356
  */
337
- function getRouteSortOrderValue(val) {
357
+ function getRouteSortOrderValue(val: number): number {
338
358
  return val === -999 ? undefined : val;
339
359
  }
340
360
 
@@ -345,8 +365,10 @@ function getRouteSortOrderValue(val) {
345
365
  * returned. If all comparison functions return equivalence, then the values
346
366
  * are assumed to be equivalent.
347
367
  */
348
- function makeMultiCriteriaSort(...criteria) {
349
- return (a, b) => {
368
+ function makeMultiCriteriaSort(
369
+ ...criteria: ((a: unknown, b: unknown) => number)[]
370
+ ) {
371
+ return (a: number, b: number): number => {
350
372
  for (let i = 0; i < criteria.length; i++) {
351
373
  const curCriteriaComparatorValue = criteria[i](a, b);
352
374
  // if the comparison objects are not equivalent, return the value obtained
@@ -390,7 +412,9 @@ function makeMultiCriteriaSort(...criteria) {
390
412
  * those with shortNames.
391
413
  * 7. longName as string.
392
414
  */
393
- export function makeRouteComparator(transitOperators) {
415
+ export function makeRouteComparator(
416
+ transitOperators: TransitOperator[]
417
+ ): (a: number, b: number) => number {
394
418
  return makeMultiCriteriaSort(
395
419
  makeTransitOperatorComparator(transitOperators),
396
420
  makeNumericValueComparator(obj => getRouteSortOrderValue(obj.sortOrder)),