@opentripplanner/core-utils 4.8.0 → 4.9.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/src/itinerary.js CHANGED
@@ -1,6 +1,28 @@
1
1
  import polyline from "@mapbox/polyline";
2
2
  import turfAlong from "@turf/along";
3
3
 
4
+ import {
5
+ calculateFares,
6
+ getLegModeLabel,
7
+ getModeForPlace,
8
+ getPlaceName,
9
+ getStepDirection,
10
+ getStepInstructions,
11
+ getStepStreetName,
12
+ getTransitFare
13
+ } from "./deprecated";
14
+
15
+ export {
16
+ calculateFares,
17
+ getLegModeLabel,
18
+ getModeForPlace,
19
+ getPlaceName,
20
+ getStepDirection,
21
+ getStepInstructions,
22
+ getStepStreetName,
23
+ getTransitFare
24
+ };
25
+
4
26
  // All OTP transit modes
5
27
  export const transitModes = [
6
28
  "TRAM",
@@ -159,51 +181,6 @@ export function getMapColor(mode) {
159
181
  return "#aaa";
160
182
  }
161
183
 
162
- // TODO: temporary code; handle via migrated OTP i18n language table
163
- export function getStepDirection(step) {
164
- switch (step.relativeDirection) {
165
- case "DEPART":
166
- return `Head ${step.absoluteDirection.toLowerCase()}`;
167
- case "LEFT":
168
- return "Left";
169
- case "HARD_LEFT":
170
- return "Hard left";
171
- case "SLIGHTLY_LEFT":
172
- return "Slight left";
173
- case "CONTINUE":
174
- return "Continue";
175
- case "SLIGHTLY_RIGHT":
176
- return "Slight right";
177
- case "RIGHT":
178
- return "Right";
179
- case "HARD_RIGHT":
180
- return "Hard right";
181
- case "CIRCLE_CLOCKWISE":
182
- return "Follow circle clockwise";
183
- case "CIRCLE_COUNTERCLOCKWISE":
184
- return "Follow circle counterclockwise";
185
- case "ELEVATOR":
186
- return "Take elevator";
187
- case "UTURN_LEFT":
188
- return "Left U-turn";
189
- case "UTURN_RIGHT":
190
- return "Right U-turn";
191
- default:
192
- return step.relativeDirection;
193
- }
194
- }
195
-
196
- export function getStepInstructions(step) {
197
- const conjunction = step.relativeDirection === "ELEVATOR" ? "to" : "on";
198
- return `${getStepDirection(step)} ${conjunction} ${step.streetName}`;
199
- }
200
-
201
- export function getStepStreetName(step) {
202
- if (step.streetName === "road") return "Unnamed Road";
203
- if (step.streetName === "path") return "Unnamed Path";
204
- return step.streetName;
205
- }
206
-
207
184
  export function toSentenceCase(str) {
208
185
  if (str == null) {
209
186
  return "";
@@ -233,25 +210,6 @@ export function getCompanyFromLeg(leg) {
233
210
  return null;
234
211
  }
235
212
 
236
- export function getLegModeLabel(leg) {
237
- switch (leg.mode) {
238
- case "BICYCLE_RENT":
239
- return "Biketown";
240
- case "CAR":
241
- return leg.hailedCar ? "Ride" : "Drive";
242
- case "GONDOLA":
243
- return "Aerial Tram";
244
- case "TRAM":
245
- if (leg.routeLongName.toLowerCase().indexOf("streetcar") !== -1)
246
- return "Streetcar";
247
- return "Light Rail";
248
- case "MICROMOBILITY":
249
- return "Ride";
250
- default:
251
- return toSentenceCase(leg.mode);
252
- }
253
- }
254
-
255
213
  export function getItineraryBounds(itinerary) {
256
214
  let coords = [];
257
215
  itinerary.legs.forEach(leg => {
@@ -400,7 +358,7 @@ export function getTextWidth(text, font = "22px Arial") {
400
358
  * Get the configured company object for the given network string if the company
401
359
  * has been defined in the provided companies array config.
402
360
  */
403
- function getCompanyForNetwork(networkString, companies = []) {
361
+ export function getCompanyForNetwork(networkString, companies = []) {
404
362
  const company = companies.find(co => co.id === networkString);
405
363
  if (!company) {
406
364
  console.warn(
@@ -426,47 +384,6 @@ export function getCompaniesLabelFromNetworks(networks, companies = []) {
426
384
  .join("/");
427
385
  }
428
386
 
429
- /**
430
- * Returns mode name by checking the vertex type (VertexType class in OTP) for
431
- * the provided place. NOTE: this is currently only intended for vehicles at
432
- * the moment (not transit or walking).
433
- *
434
- * TODO: I18N
435
- * @param {string} place place from itinerary leg
436
- */
437
- export function getModeForPlace(place) {
438
- switch (place.vertexType) {
439
- case "CARSHARE":
440
- return "car";
441
- case "VEHICLERENTAL":
442
- return "E-scooter";
443
- // TODO: Should the type change depending on bike vertex type?
444
- case "BIKESHARE":
445
- case "BIKEPARK":
446
- return "bike";
447
- // If company offers more than one mode, default to `vehicle` string.
448
- default:
449
- return "vehicle";
450
- }
451
- }
452
-
453
- export function getPlaceName(place, companies) {
454
- // If address is provided (i.e. for carshare station, use it)
455
- if (place.address) return place.address.split(",")[0];
456
- if (place.networks && place.vertexType === "VEHICLERENTAL") {
457
- // For vehicle rental pick up, do not use the place name. Rather, use
458
- // company name + vehicle type (e.g., SPIN E-scooter). Place name is often just
459
- // a UUID that has no relevance to the actual vehicle. For bikeshare, however,
460
- // there are often hubs or bikes that have relevant names to the user.
461
- const company = getCompanyForNetwork(place.networks[0], companies);
462
- if (company) {
463
- return `${company.label} ${getModeForPlace(place)}`;
464
- }
465
- }
466
- // Default to place name
467
- return place.name;
468
- }
469
-
470
387
  export function getTNCLocation(leg, type) {
471
388
  const location = leg[type];
472
389
  return `${location.lat.toFixed(5)},${location.lon.toFixed(5)}`;
@@ -488,121 +405,6 @@ export function calculatePhysicalActivity(itinerary) {
488
405
  };
489
406
  }
490
407
 
491
- /**
492
- * For a given fare component (either total fare or component parts), returns
493
- * an object with string formatters and the fare value (in cents).
494
- */
495
- export function getTransitFare(fareComponent) {
496
- // Default values (if fare component is not valid).
497
- let digits = 2;
498
- let transitFare = 0;
499
- let symbol = "$";
500
- let currencyCode = "USD";
501
- if (fareComponent) {
502
- // Assign values without declaration. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#assignment_without_declaration
503
- ({
504
- currencyCode,
505
- defaultFractionDigits: digits,
506
- symbol
507
- } = fareComponent.currency);
508
- transitFare = fareComponent.cents;
509
- }
510
- // For cents to string conversion, use digits from fare component.
511
- const centsToString = cents => {
512
- const dollars = (cents / 10 ** digits).toFixed(digits);
513
- return `${symbol}${dollars}`;
514
- };
515
- // For dollars to string conversion, assume we're rounding to two digits.
516
- const dollarsToString = dollars => `${symbol}${dollars.toFixed(2)}`;
517
- return {
518
- centsToString,
519
- currencyCode,
520
- dollarsToString,
521
- transitFare
522
- };
523
- }
524
-
525
- /**
526
- * For an itinerary, calculates the transit/TNC fares and returns an object with
527
- * these values, currency info, as well as string formatters.
528
- * It is assumed that the same currency is used for transit and TNC legs.
529
- *
530
- * multiple being set to true will change the output behavior:
531
- * - dollarsToString and centsToString will be returned as part of each fare
532
- * - currencyCode will be returned separately for each fare
533
- * - tnc currency code will be returned separately
534
- * - each fare type will be returned separately within a new transitFares property
535
- *
536
- * FIXME: a new approach to fare calculation must be found:
537
- * the current approach is not sustainable, as centsToString and DollarsToString
538
- * must be replaced by i18n anyway.
539
- *
540
- * However, the current behavior should ideally be kept to avoid a breaking change.
541
- * The "multiple" mode is helpful, but only prevents tnc fare calculation from being duplicated.
542
- * This method could be split out into a new one, along with tnc fare calculation.
543
- * If this is done, the individual fare calculation should also be modified to support
544
- * a default fare not being called "regular". However, this again would be a breaking change.
545
- * This breaking change is avoided by adding the "multiple" parameter.
546
- *
547
- * When centsToString and dollarsToString are removed, this method should be split into
548
- * individual fare calculation on a variable fare key, fare calculation of an entire leg,
549
- * which will get fares for every fare key in the leg, and a method to calculate the fare of
550
- * a tnc ride within the leg. This will make typescripting easier, as the types will be cleaner.
551
- */
552
- export function calculateFares(itinerary, multiple = false) {
553
- // Process any TNC fares
554
- let minTNCFare = 0;
555
- let maxTNCFare = 0;
556
- let tncCurrencyCode;
557
- itinerary.legs.forEach(leg => {
558
- if (leg.mode === "CAR" && leg.hailedCar && leg.tncData) {
559
- const { currency, maxCost, minCost } = leg.tncData;
560
- // TODO: Support non-USD
561
- minTNCFare += minCost;
562
- maxTNCFare += maxCost;
563
- tncCurrencyCode = currency;
564
- }
565
- });
566
-
567
- if (multiple) {
568
- // Return object of fares
569
- const transitFares = {};
570
- if (itinerary && itinerary.fare && itinerary.fare.fare) {
571
- Object.keys(itinerary.fare.fare).forEach(fareKey => {
572
- const fareComponent = itinerary.fare.fare[fareKey];
573
- transitFares[fareKey] = getTransitFare(fareComponent);
574
- });
575
- }
576
-
577
- return {
578
- maxTNCFare,
579
- minTNCFare,
580
- tncCurrencyCode,
581
- transitFares
582
- };
583
- }
584
-
585
- // Extract fare total from itinerary fares.
586
- const fareComponent =
587
- itinerary.fare && itinerary.fare.fare && itinerary.fare.fare.regular;
588
- // Get string formatters and itinerary fare.
589
- const {
590
- centsToString,
591
- currencyCode: transitCurrencyCode,
592
- dollarsToString,
593
- transitFare
594
- } = getTransitFare(fareComponent);
595
-
596
- return {
597
- centsToString,
598
- currencyCode: transitCurrencyCode || tncCurrencyCode,
599
- dollarsToString,
600
- maxTNCFare,
601
- minTNCFare,
602
- transitFare
603
- };
604
- }
605
-
606
408
  export function getTimeZoneOffset(itinerary) {
607
409
  if (!itinerary.legs || !itinerary.legs.length) return 0;
608
410
 
@@ -616,3 +418,13 @@ export function getTimeZoneOffset(itinerary) {
616
418
  (new Date().getTimezoneOffset() + dstOffset) * 60000
617
419
  );
618
420
  }
421
+
422
+ export function calculateTncFares(itinerary) {
423
+ // TODO: don't rely on deprecated methods!
424
+ // At the moment this is safe as none of these exported variables contain strings
425
+ const { maxTNCFare, minTNCFare, tncCurrencyCode } = calculateFares(
426
+ itinerary,
427
+ true
428
+ );
429
+ return { maxTNCFare, minTNCFare, tncCurrencyCode };
430
+ }
package/src/map.js CHANGED
@@ -1,17 +1,13 @@
1
- import moment from "moment";
2
-
3
1
  import { getPlaceName, isTransit, isFlex, toSentenceCase } from "./itinerary";
4
2
 
5
- export function latlngToString(latlng) {
6
- return (
7
- latlng &&
8
- `${latlng.lat.toFixed(5)}, ${(latlng.lng || latlng.lon).toFixed(5)}`
9
- );
10
- }
3
+ import {
4
+ coordsToString,
5
+ getDetailText,
6
+ latlngToString,
7
+ logDeprecationWarning
8
+ } from "./deprecated";
11
9
 
12
- export function coordsToString(coords) {
13
- return coords.length && coords.map(c => (+c).toFixed(5)).join(", ");
14
- }
10
+ export { coordsToString, getDetailText, latlngToString };
15
11
 
16
12
  export function currentPositionToLocation(currentPosition) {
17
13
  if (currentPosition.error || !currentPosition.coords) {
@@ -23,7 +19,6 @@ export function currentPositionToLocation(currentPosition) {
23
19
  return {
24
20
  lat: currentPosition.coords.latitude,
25
21
  lon: currentPosition.coords.longitude,
26
- name: "(Current Location)",
27
22
  category: "CURRENT_LOCATION"
28
23
  };
29
24
  }
@@ -34,26 +29,16 @@ export function stringToCoords(str) {
34
29
 
35
30
  export function constructLocation(latlng) {
36
31
  return {
37
- name: latlngToString(latlng),
38
32
  lat: latlng.lat,
39
33
  lon: latlng.lng
40
34
  };
41
35
  }
42
36
 
43
- export function getDetailText(location) {
44
- let detailText;
45
- if (location.type === "home" || location.type === "work") {
46
- detailText = location.name;
47
- }
48
- if (location.type === "stop") {
49
- detailText = location.id;
50
- } else if (location.type === "recent" && location.timestamp) {
51
- detailText = moment(location.timestamp).fromNow();
37
+ export function formatStoredPlaceName(location, withDetails = true) {
38
+ if (withDetails) {
39
+ logDeprecationWarning("the formatStoredPlaceName withDetails parameter");
52
40
  }
53
- return detailText;
54
- }
55
41
 
56
- export function formatStoredPlaceName(location, withDetails = true) {
57
42
  let displayName =
58
43
  location.type === "home" || location.type === "work"
59
44
  ? toSentenceCase(location.type)
@@ -99,6 +84,7 @@ export function itineraryToTransitive(
99
84
 
100
85
  const journey = {
101
86
  journey_id: "itin",
87
+ // This string is not shown in the UI
102
88
  journey_name: "Iterarary-derived Journey",
103
89
  segments: []
104
90
  };
@@ -180,6 +166,7 @@ export function itineraryToTransitive(
180
166
  });
181
167
  tdata.places.push({
182
168
  place_id: toPlaceId,
169
+ // This string is not shown in the UI
183
170
  place_name: getPlaceName(leg.to, companies),
184
171
  place_lat: leg.to.lat,
185
172
  place_lon: leg.to.lon
@@ -205,6 +192,7 @@ export function itineraryToTransitive(
205
192
  const ptnId = `ptn_${patternId}`;
206
193
  const pattern = {
207
194
  pattern_id: ptnId,
195
+ // This string is not shown in the UI
208
196
  pattern_name: `Pattern ${patternId}`,
209
197
  route_id: leg.routeId,
210
198
  stops: []
package/src/messages.js CHANGED
@@ -1,3 +1,5 @@
1
+ // This entire file can be removed once deprecation goes forward
2
+ import { logDeprecationWarning } from "./deprecated";
1
3
  /**
2
4
  * Takes component's default props messages and its instance props messages and
3
5
  * returns the merged messages. The returned object will ensure that the default
@@ -7,6 +9,8 @@
7
9
  */
8
10
  /* eslint-disable import/prefer-default-export */
9
11
  export function mergeMessages(defaultPropsMessages, propsMessages) {
12
+ logDeprecationWarning("mergeMessages");
13
+
10
14
  const defaultMessages = defaultPropsMessages || {};
11
15
  const instanceMessages = propsMessages || {};
12
16
  return {
package/src/profile.js CHANGED
@@ -27,8 +27,8 @@ export function filterProfileOptions(response) {
27
27
  return response;
28
28
  }
29
29
 
30
- function locationString(str, defaultStr) {
31
- return str ? str.split(",")[0] : defaultStr;
30
+ function locationString(str) {
31
+ return str ? str.split(",")[0] : null;
32
32
  }
33
33
 
34
34
  function accessToLeg(access, origin, destination) {
@@ -37,10 +37,10 @@ function accessToLeg(access, origin, destination) {
37
37
  duration: access.time,
38
38
  transitLeg: false,
39
39
  from: {
40
- name: locationString(origin, "Origin")
40
+ name: locationString(origin)
41
41
  },
42
42
  to: {
43
- name: locationString(destination, "Destination")
43
+ name: locationString(destination)
44
44
  }
45
45
  };
46
46
  }
@@ -98,7 +98,7 @@ function optionToItinerary(option, query) {
98
98
  duration: walkOnTime,
99
99
  transitLeg: false,
100
100
  from: {
101
- name: locationString(query && query.from.name, "Destination")
101
+ name: locationString(query && query.from.name)
102
102
  },
103
103
  to: {
104
104
  name: onStationName
@@ -127,7 +127,7 @@ function optionToItinerary(option, query) {
127
127
  name: offStationName
128
128
  },
129
129
  to: {
130
- name: locationString(query && query.to.name, "Destination")
130
+ name: locationString(query && query.to.name)
131
131
  }
132
132
  });
133
133
  } else {
@@ -177,16 +177,6 @@ function optionToItinerary(option, query) {
177
177
  itin.walkTime += option.egress[0].time;
178
178
  }
179
179
 
180
- // construct summary
181
- if (option.transit) {
182
- itin.summary = "Transit";
183
- } else if (option.modes.length === 1 && option.modes[0] === "bicycle")
184
- itin.summary = "Bicycle";
185
- else if (option.modes.length === 1 && option.modes[0] === "walk")
186
- itin.summary = "Walk";
187
- else if (option.modes.indexOf("bicycle_rent") !== -1)
188
- itin.summary = "Bikeshare";
189
-
190
180
  return itin;
191
181
  }
192
182
 
@@ -1,3 +1,7 @@
1
+ // TODO: Remove this entire file, as it is deprecated in favor of i18n-queryParams within
2
+ // the SettingsSelector package
3
+
4
+ // This is only used within stories
1
5
  import cloneDeep from "lodash.clonedeep";
2
6
  import React from "react";
3
7
  import { Wheelchair } from "@styled-icons/foundation/Wheelchair";
@@ -54,10 +58,14 @@ import { getCurrentDate, getCurrentTime } from "./time";
54
58
  /**
55
59
  * Format location object as string for use in fromPlace or toPlace query param.
56
60
  */
57
- export function formatPlace(location, alternateName = "Place") {
61
+ export function formatPlace(location, alternateName) {
58
62
  if (!location) return null;
59
63
  const name =
60
- location.name || `${alternateName} (${location.lat},${location.lon})`;
64
+ location.name ||
65
+ `${alternateName ? `${alternateName} ` : ""}(${location.lat},${
66
+ location.lon
67
+ })`;
68
+ // This string is not language-specific
61
69
  return `${name}::${location.lat},${location.lon}`;
62
70
  }
63
71
 
@@ -70,7 +78,7 @@ const queryParams = [
70
78
  name: "from",
71
79
  routingTypes: ["ITINERARY", "PROFILE"],
72
80
  default: null,
73
- itineraryRewrite: value => ({ fromPlace: formatPlace(value, "Origin") }),
81
+ itineraryRewrite: value => ({ fromPlace: formatPlace(value) }),
74
82
  profileRewrite: value => ({ from: { lat: value.lat, lon: value.lon } })
75
83
  // FIXME: Use for parsing URL values?
76
84
  // fromURL: stringToLocation
@@ -81,7 +89,7 @@ const queryParams = [
81
89
  name: "to",
82
90
  routingTypes: ["ITINERARY", "PROFILE"],
83
91
  default: null,
84
- itineraryRewrite: value => ({ toPlace: formatPlace(value, "Destination") }),
92
+ itineraryRewrite: value => ({ toPlace: formatPlace(value) }),
85
93
  profileRewrite: value => ({ to: { lat: value.lat, lon: value.lon } })
86
94
  // FIXME: Use for parsing URL values?
87
95
  // fromURL: stringToLocation
package/src/query.js CHANGED
@@ -2,14 +2,8 @@ import moment from "moment";
2
2
  import getGeocoder from "@opentripplanner/geocoder/lib";
3
3
  import qs from "qs";
4
4
 
5
- import {
6
- getTransitModes,
7
- hasCar,
8
- hasTransit,
9
- isAccessMode,
10
- toSentenceCase
11
- } from "./itinerary";
12
- import { coordsToString, matchLatLon, stringToCoords } from "./map";
5
+ import { getTransitModes, hasCar, isAccessMode } from "./itinerary";
6
+ import { stringToCoords } from "./map";
13
7
  import queryParams from "./query-params";
14
8
  import {
15
9
  getCurrentTime,
@@ -18,6 +12,10 @@ import {
18
12
  OTP_API_TIME_FORMAT
19
13
  } from "./time";
20
14
 
15
+ import { coordsToString, summarizeQuery } from "./deprecated";
16
+
17
+ export { summarizeQuery };
18
+
21
19
  /* The list of default parameters considered in the settings panel */
22
20
 
23
21
  export const defaultParams = [
@@ -84,24 +82,6 @@ export function getOtpUrlParams() {
84
82
  return Object.keys(getUrlParams()).filter(key => !key.startsWith("ui_"));
85
83
  }
86
84
 
87
- function findLocationType(
88
- location,
89
- locations = [],
90
- types = ["home", "work", "suggested"]
91
- ) {
92
- const match = locations.find(l => matchLatLon(l, location));
93
- return match && types.indexOf(match.type) !== -1 ? match.type : null;
94
- }
95
-
96
- export function summarizeQuery(query, locations = []) {
97
- const from =
98
- findLocationType(query.from, locations) || query.from.name.split(",")[0];
99
- const to =
100
- findLocationType(query.to, locations) || query.to.name.split(",")[0];
101
- const mode = hasTransit(query.mode) ? "Transit" : toSentenceCase(query.mode);
102
- return `${mode} from ${from} to ${to}`;
103
- }
104
-
105
85
  export function getTripOptionsFromQuery(query, keepPlace = false) {
106
86
  const options = { ...query };
107
87
  // Delete time/date options and from/to
package/src/time.js CHANGED
@@ -23,7 +23,11 @@ export const OTP_API_TIME_FORMAT = "HH:mm";
23
23
  * Otherwise, uses date-fns default
24
24
  * @returns Formatted duration
25
25
  */
26
- function formatDurationLikeMoment(seconds, showSeconds, localize = true) {
26
+ function formatDurationLikeMoment(
27
+ seconds,
28
+ showSeconds,
29
+ localize = { enabled: true, code: "en-US" }
30
+ ) {
27
31
  // date-fns doesn't do this automatically
28
32
  if ((!showSeconds && seconds < 60) || seconds === 0) {
29
33
  return "0 min";
@@ -41,7 +45,8 @@ function formatDurationLikeMoment(seconds, showSeconds, localize = true) {
41
45
  };
42
46
  const locale = localize
43
47
  ? {
44
- code: "en-US",
48
+ // Maintain backwards compatibility when called with localize=true
49
+ code: localize?.code || "en-US",
45
50
  formatDistance: (spec, val) => {
46
51
  return `${val} ${specLookup[spec]}`;
47
52
  }
@@ -92,8 +97,8 @@ export function formatDuration(seconds) {
92
97
  * @param {number} seconds duration in seconds
93
98
  * @returns {string} formatted text representation
94
99
  */
95
- export function formatDurationWithSeconds(seconds) {
96
- return formatDurationLikeMoment(seconds, true);
100
+ export function formatDurationWithSeconds(seconds, region) {
101
+ return formatDurationLikeMoment(seconds, { enabled: true, code: region });
97
102
  }
98
103
  /**
99
104
  * Formats a time value for display in narrative