itinerary getTransitFare should return defaults with missing fare 1`] = `0`;
-
-exports[`util > itinerary getTransitFare should work with valid fare component 1`] = `575`;
diff --git a/packages/core-utils/src/__tests__/itinerary.js b/packages/core-utils/src/__tests__/itinerary.js
index d8a5cc302..5663e4d77 100644
--- a/packages/core-utils/src/__tests__/itinerary.js
+++ b/packages/core-utils/src/__tests__/itinerary.js
@@ -2,12 +2,14 @@ import {
calculateTncFares,
getCompanyFromLeg,
getDisplayedStopId,
- getTransitFare,
+ getItineraryCost,
+ getLegCost,
isTransit
} from "../itinerary";
const bikeRentalItinerary = require("./__mocks__/bike-rental-itinerary.json");
const tncItinerary = require("./__mocks__/tnc-itinerary.json");
+const fareProductItinerary = require("./__mocks__/fare-products-itinerary.json");
const basePlace = {
lat: 0,
@@ -35,30 +37,6 @@ describe("util > itinerary", () => {
});
});
- describe("getTransitFare", () => {
- it("should return defaults with missing fare", () => {
- const { transitFare } = getTransitFare(null);
- // transit fare value should be zero
- expect(transitFare).toMatchSnapshot();
- });
-
- it("should work with valid fare component", () => {
- const fareComponent = {
- currency: {
- currency: "USD",
- defaultFractionDigits: 2,
- currencyCode: "USD",
- symbol: "$"
- },
- cents: 575
- };
- const { currencyCode, transitFare } = getTransitFare(fareComponent);
- expect(currencyCode).toEqual(fareComponent.currency.currencyCode);
- // Snapshot tests
- expect(transitFare).toMatchSnapshot();
- });
- });
-
describe("calculateTncFares", () => {
it("should return the correct amounts and currency for an itinerary with TNC", () => {
const fareResult = calculateTncFares(tncItinerary, true);
@@ -111,4 +89,72 @@ describe("util > itinerary", () => {
expect(getDisplayedStopId(basePlace)).toBeFalsy();
});
});
+
+ describe("getLegCost", () => {
+ const leg = {
+ fareProducts: [
+ {
+ id: "testId",
+ product: {
+ medium: { id: "cash" },
+ name: "rideCost",
+ price: { amount: 200, currency: "USD" },
+ riderCategory: { id: "regular" }
+ }
+ },
+ {
+ id: "testId",
+ product: {
+ medium: { id: "cash" },
+ name: "transfer",
+ price: { amount: 50, currency: "USD" },
+ riderCategory: { id: "regular" }
+ }
+ }
+ ]
+ };
+ it("should return the total cost for a leg", () => {
+ const result = getLegCost(leg, "cash", "regular");
+ expect(result.price).toEqual({ amount: 200, currency: "USD" });
+ });
+
+ it("should return the transfer discount amount if a transfer was used", () => {
+ const result = getLegCost(leg, "cash", "regular");
+ expect(result.price).toEqual({ amount: 200, currency: "USD" });
+ expect(result.transferAmount).toEqual({ amount: 50, currency: "USD" });
+ });
+
+ it("should return undefined if no fare products exist on the leg", () => {
+ const emptyleg = {};
+ const result = getLegCost(emptyleg, "cash", "regular");
+ expect(result.price).toBeUndefined();
+ });
+ it("should return undefined if the keys are invalid", () => {
+ const result = getLegCost(leg, "invalidkey", "invalidkey");
+ expect(result.price).toBeUndefined();
+ });
+ });
+
+ describe("getItineraryCost", () => {
+ it("should calculate the total cost of an itinerary", () => {
+ const result = getItineraryCost(
+ fareProductItinerary.legs,
+ "orca:cash",
+ "orca:regular"
+ );
+ expect(result.amount).toEqual(5.75);
+ expect(result.currency).toEqual({
+ code: "USD",
+ digits: 2
+ });
+ });
+ it("should return undefined when the keys are invalid", () => {
+ const result = getItineraryCost(
+ fareProductItinerary.legs,
+ "invalidkey",
+ "invalidkey"
+ );
+ expect(result).toBeUndefined();
+ });
+ });
});
diff --git a/packages/core-utils/src/itinerary.ts b/packages/core-utils/src/itinerary.ts
index 7bd263dcc..8fb47c78f 100644
--- a/packages/core-utils/src/itinerary.ts
+++ b/packages/core-utils/src/itinerary.ts
@@ -4,7 +4,6 @@ import {
Config,
ElevationProfile,
FlexBookingInfo,
- Itinerary,
ItineraryOnlyLegsRequired,
LatLngArray,
Leg,
@@ -474,27 +473,6 @@ export function calculateTncFares(
);
}
-/**
- * For a given fare component (either total fare or component parts), returns
- * an object with the fare value (in cents).
- */
-export function getTransitFare(
- fareComponent: Money
-): {
- currencyCode: string;
- transitFare: number;
-} {
- return fareComponent
- ? {
- currencyCode: fareComponent.currency.currencyCode,
- transitFare: fareComponent.cents
- }
- : {
- currencyCode: "USD",
- transitFare: 0
- };
-}
-
/**
* Sources:
* - https://www.itf-oecd.org/sites/default/files/docs/environmental-performance-new-mobility.pdf
@@ -577,20 +555,6 @@ export function getDisplayedStopId(placeOrStop: Place | Stop): string {
return stopCode || stopId?.split(":")[1] || stopId;
}
-/**
- * Adds the fare product info to each leg in an itinerary, from the itinerary's fare property
- * @param itinerary Itinerary with legProducts in fare object
- * @returns Itinerary with legs that have fare products attached to them
- */
-export function getLegsWithFares(itinerary: Itinerary): Leg[] {
- return itinerary.legs.map((leg, i) => ({
- ...leg,
- fareProducts: itinerary.fare?.legProducts
- ?.filter(lp => lp?.legIndices?.includes(i))
- .flatMap(lp => lp.products)
- }));
-}
-
/**
* Extracts useful data from the fare products on a leg, such as the leg cost and transfer info.
* @param leg Leg with fare products (must have used getLegsWithFares)
@@ -600,23 +564,26 @@ export function getLegsWithFares(itinerary: Itinerary): Leg[] {
*/
export function getLegCost(
leg: Leg,
- category: string,
- container: string
-): { price?: Money; transferAmount?: number } {
+ mediumId: string,
+ riderCategoryId: string
+): { price?: Money; transferAmount?: Money | undefined } {
if (!leg.fareProducts) return { price: undefined };
-
- const relevantFareProducts = leg.fareProducts.filter(
- fp => fp.category.name === category && fp.container.name === container
- );
- const totalCost = relevantFareProducts.find(fp => fp.name === "rideCost")
- ?.amount;
+ const relevantFareProducts = leg.fareProducts.filter(({ product }) => {
+ return (
+ product.riderCategory.id === riderCategoryId &&
+ product.medium.id === mediumId
+ );
+ });
+ const totalCost = relevantFareProducts.find(
+ fp => fp.product.name === "rideCost"
+ )?.product?.price;
const transferFareProduct = relevantFareProducts.find(
- fp => fp.name === "transfer"
+ fp => fp.product.name === "transfer"
);
return {
price: totalCost,
- transferAmount: transferFareProduct?.amount?.cents
+ transferAmount: transferFareProduct?.product.price
};
}
@@ -629,17 +596,64 @@ export function getLegCost(
*/
export function getItineraryCost(
legs: Leg[],
- category: string,
- container: string
-): Money {
- return legs
- .filter(leg => !!leg.fareProducts)
- .map(leg => getLegCost(leg, category, container).price)
- .reduce
(
- (prev, cur) => ({
- cents: prev.cents + cur?.cents || 0,
- currency: prev.currency ?? cur?.currency
- }),
- { cents: 0, currency: null }
- );
+ mediumId: string,
+ riderCategoryId: string
+): Money | undefined {
+ const legCosts = legs
+ .filter(leg => leg.fareProducts?.length > 0)
+ .map(leg => getLegCost(leg, mediumId, riderCategoryId).price)
+ .filter(cost => cost !== undefined);
+ if (legCosts.length === 0) return undefined;
+ return legCosts.reduce(
+ (prev, cur) => ({
+ amount: prev.amount + cur?.amount || 0,
+ currency: prev.currency ?? cur?.currency
+ }),
+ { amount: 0, currency: null }
+ );
}
+
+const pickupDropoffTypeToOtp1 = otp2Type => {
+ switch (otp2Type) {
+ case "COORDINATE_WITH_DRIVER":
+ return "coordinateWithDriver";
+ case "CALL_AGENCY":
+ return "mustPhone";
+ case "SCHEDULED":
+ return "scheduled";
+ case "NONE":
+ return "none";
+ default:
+ return null;
+ }
+};
+
+export const convertGraphQLResponseToLegacy = (leg: any): any => ({
+ ...leg,
+ agencyBrandingUrl: leg.agency?.url,
+ agencyName: leg.agency?.name,
+ agencyUrl: leg.agency?.url,
+ alightRule: pickupDropoffTypeToOtp1(leg.dropoffType),
+ boardRule: pickupDropoffTypeToOtp1(leg.pickupType),
+ dropOffBookingInfo: {
+ latestBookingTime: leg.dropOffBookingInfo
+ },
+ from: {
+ ...leg.from,
+ stopCode: leg.from.stop?.code,
+ stopId: leg.from.stop?.gtfsId
+ },
+ route: leg.route?.shortName,
+ routeColor: leg.route?.color,
+ routeId: leg.route?.id,
+ routeLongName: leg.route?.longName,
+ routeShortName: leg.route?.shortName,
+ routeTextColor: leg.route?.textColor,
+ to: {
+ ...leg.to,
+ stopCode: leg.to.stop?.code,
+ stopId: leg.to.stop?.gtfsId
+ },
+ tripHeadsign: leg.trip?.tripHeadsign,
+ tripId: leg.trip?.gtfsId
+});
diff --git a/packages/core-utils/src/planQuery.graphql b/packages/core-utils/src/planQuery.graphql
index b1ee05731..cc4d99f1f 100644
--- a/packages/core-utils/src/planQuery.graphql
+++ b/packages/core-utils/src/planQuery.graphql
@@ -1,6 +1,6 @@
query Plan(
- $fromPlace: String!
- $toPlace: String!
+ $fromPlace: String!
+ $toPlace: String!
$modes: [TransportMode]
$time: String
$date: String
@@ -57,6 +57,28 @@ query Plan(
dropoffType
duration
endTime
+ fareProducts {
+ id
+ product {
+ id
+ medium {
+ id
+ name
+ }
+ name
+ price {
+ amount
+ currency {
+ code
+ digits
+ }
+ }
+ riderCategory {
+ id
+ name
+ }
+ }
+ }
from {
lat
lon
@@ -211,4 +233,4 @@ query Plan(
inputField
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/itinerary-body/src/ItineraryBody/index.tsx b/packages/itinerary-body/src/ItineraryBody/index.tsx
index 73f40333e..f6362f431 100755
--- a/packages/itinerary-body/src/ItineraryBody/index.tsx
+++ b/packages/itinerary-body/src/ItineraryBody/index.tsx
@@ -18,6 +18,7 @@ const ItineraryBody = ({
alwaysCollapseAlerts = false,
className,
config,
+ defaultFareSelector,
diagramVisible,
frameLeg = noop,
itinerary,
@@ -35,7 +36,6 @@ const ItineraryBody = ({
showElevationProfile,
showLegIcon,
showMapButtonColumn = true,
- showRouteFares,
showViewTripButton,
TimeColumnContent,
toRouteAbbreviation = defaultRouteAbbr,
@@ -50,7 +50,6 @@ const ItineraryBody = ({
const rows = [];
let followsTransit = false;
let lastLeg;
- const { fare } = itinerary;
itinerary.legs.forEach((leg, i) => {
function createPlaceRow(isDestination) {
// Create a row containing this leg's start place and leg traversal details
@@ -63,11 +62,8 @@ const ItineraryBody = ({
// eslint-disable-next-line react/no-array-index-key
key={i + (isDestination ? 1 : 0)}
config={config}
+ defaultFareSelector={defaultFareSelector}
diagramVisible={diagramVisible}
- // Itinerary fare is only passed as prop if showRouteFares is enabled.
- // The fare details will be processed in the TransitLeg component and
- // shown for all legs.
- fare={showRouteFares ? fare : null}
followsTransit={followsTransit}
frameLeg={frameLeg}
isDestination={isDestination}
diff --git a/packages/itinerary-body/src/ItineraryBody/place-row.tsx b/packages/itinerary-body/src/ItineraryBody/place-row.tsx
index bb6e84ad9..0d6d38bd4 100755
--- a/packages/itinerary-body/src/ItineraryBody/place-row.tsx
+++ b/packages/itinerary-body/src/ItineraryBody/place-row.tsx
@@ -21,8 +21,8 @@ export default function PlaceRow({
AlertToggleIcon,
alwaysCollapseAlerts,
config,
+ defaultFareSelector,
diagramVisible,
- fare,
followsTransit,
frameLeg,
isDestination,
@@ -135,11 +135,11 @@ export default function PlaceRow({
AlertBodyIcon={AlertBodyIcon}
AlertToggleIcon={AlertToggleIcon}
alwaysCollapseAlerts={alwaysCollapseAlerts}
- fare={fare}
+ defaultFareSelector={defaultFareSelector}
leg={leg}
+ legDestination={formattedPlace(leg.to)}
LegIcon={LegIcon}
legIndex={legIndex}
- legDestination={formattedPlace(leg.to)}
RouteDescription={RouteDescription}
setActiveLeg={setActiveLeg}
setViewedTrip={setViewedTrip}
diff --git a/packages/itinerary-body/src/TransitLegBody/index.tsx b/packages/itinerary-body/src/TransitLegBody/index.tsx
index ec6072acb..c408afce4 100644
--- a/packages/itinerary-body/src/TransitLegBody/index.tsx
+++ b/packages/itinerary-body/src/TransitLegBody/index.tsx
@@ -1,6 +1,6 @@
import coreUtils from "@opentripplanner/core-utils";
import {
- Fare,
+ FareProductSelector,
FlexBookingInfo,
Leg,
LegIconComponent,
@@ -14,6 +14,7 @@ import {
injectIntl,
IntlShape
} from "react-intl";
+
import { Duration } from "../defaults";
import * as S from "../styled";
@@ -34,12 +35,12 @@ interface Props {
AlertBodyIcon?: FunctionComponent;
AlertToggleIcon?: FunctionComponent;
alwaysCollapseAlerts: boolean;
- fare?: Fare;
+ defaultFareSelector?: FareProductSelector;
intl: IntlShape;
leg: Leg;
+ legDestination: string;
LegIcon: LegIconComponent;
legIndex: number;
- legDestination: string;
RouteDescription: FunctionComponent;
setActiveLeg: SetActiveLegFunction;
setViewedTrip: SetViewedTripFunction;
@@ -108,16 +109,6 @@ class TransitLegBody extends Component {
};
}
- getFareForLeg = (leg: Leg, fare: Fare) => {
- let fareForLeg;
- fare?.details?.regular?.forEach(fareComponent => {
- if (fareComponent.routes?.includes(leg.routeId)) {
- fareForLeg = coreUtils.itinerary.getTransitFare(fareComponent.price);
- }
- });
- return fareForLeg;
- };
-
onToggleStopsClick = () => {
const { stopsExpanded } = this.state;
this.setState({ stopsExpanded: !stopsExpanded });
@@ -135,14 +126,14 @@ class TransitLegBody extends Component {
render(): ReactElement {
const {
- AlertToggleIcon = S.DefaultAlertToggleIcon,
AlertBodyIcon,
+ AlertToggleIcon = S.DefaultAlertToggleIcon,
alwaysCollapseAlerts,
- fare,
+ defaultFareSelector,
intl,
leg,
- LegIcon,
legDestination,
+ LegIcon,
RouteDescription,
setViewedTrip,
showAgencyInfo,
@@ -177,7 +168,14 @@ class TransitLegBody extends Component {
const shouldOnlyShowAlertsExpanded =
!(shouldCollapseDueToAlertCount || alwaysCollapseAlerts) || !leg.alerts;
const expandAlerts = alertsExpanded || shouldOnlyShowAlertsExpanded;
- const fareForLeg = this.getFareForLeg(leg, fare);
+
+ const legCost =
+ defaultFareSelector &&
+ coreUtils.itinerary.getLegCost(
+ leg,
+ defaultFareSelector.mediumId,
+ defaultFareSelector.riderCategoryId
+ );
return (
<>
@@ -343,7 +341,7 @@ class TransitLegBody extends Component {
>
- {fareForLeg && (
+ {legCost?.price && (
{
values={{
fare: (
)
}}
@@ -368,7 +366,6 @@ class TransitLegBody extends Component {
)}
-
{/* Average wait details, if present */}
{leg.averageWait && (
diff --git a/packages/itinerary-body/src/__mocks__/itineraries/leg-fare-products.json b/packages/itinerary-body/src/__mocks__/itineraries/leg-fare-products.json
new file mode 100644
index 000000000..0387113e8
--- /dev/null
+++ b/packages/itinerary-body/src/__mocks__/itineraries/leg-fare-products.json
@@ -0,0 +1,1283 @@
+{
+ "duration": 6952,
+ "endTime": 1685146739000,
+ "startTime": 1685139787000,
+ "waitingTime": 184,
+ "walkTime": 2436,
+ "accessibilityScore": null,
+ "legs": [
+ {
+ "fareProducts": [],
+ "pickupType": "SCHEDULED",
+ "dropoffType": "SCHEDULED",
+ "pickupBookingInfo": null,
+ "rentedBike": false,
+ "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "arrivalDelay": 0,
+ "distance": 825.81,
+ "duration": 641,
+ "endTime": 1685140428000,
+ "mode": "WALK",
+ "realTime": false,
+ "realtimeState": null,
+ "startTime": 1685139787000,
+ "transitLeg": false,
+ "accessibilityScore": null,
+ "trip": null,
+ "agency": null,
+ "legGeometry": {
+ "length": 16,
+ "points": "}zrbHnykiV@GHk@Dg@dC@j@BhDh@AlD?tCCzKGfV?T?NO?a@???"
+ },
+ "intermediateStops": null,
+ "route": null,
+ "from": {
+ "lat": 47.7791977,
+ "lon": -122.2903181,
+ "name": "47.7792, -122.29032",
+ "vertexType": "NORMAL",
+ "rentalVehicle": null,
+ "stop": null
+ },
+ "to": {
+ "lat": 47.7776833,
+ "lon": -122.297684,
+ "name": "48th Ave W & 244th St SW",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDprY206ODI0MDU",
+ "code": "82405",
+ "gtfsId": "kcm:82405",
+ "alerts": []
+ }
+ },
+ "steps": [
+ {
+ "distance": 36.61,
+ "lat": 47.7791959,
+ "lon": -122.290319,
+ "relativeDirection": "DEPART",
+ "absoluteDirection": "EAST",
+ "stayOn": false,
+ "streetName": "242nd Street Southwest",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 194.04,
+ "lat": 47.7791005,
+ "lon": -122.2898502,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "SOUTH",
+ "stayOn": false,
+ "streetName": "Cedar Way",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 567.32,
+ "lat": 47.7773669,
+ "lon": -122.2900921,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "WEST",
+ "stayOn": false,
+ "streetName": "Northeast 205th Street - 244th Street Southwest",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 27.83,
+ "lat": 47.7774331,
+ "lon": -122.2976837,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "NORTH",
+ "stayOn": false,
+ "streetName": "path",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ }
+ ]
+ },
+ {
+ "fareProducts": [
+ {
+ "id": "3ff3d12d-6747-3b47-842a-92c7bf167015",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 2.75
+ },
+ "riderCategory": {
+ "id": "orca:regular",
+ "name": "regular"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "77a8ba05-8081-301f-9fe4-777dcd10ae02",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 2.75
+ },
+ "riderCategory": {
+ "id": "orca:regular",
+ "name": "regular"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "0740913d-0e0a-34ab-9518-014f2025b1a8",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1
+ },
+ "riderCategory": {
+ "id": "orca:senior",
+ "name": "senior"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "16ac6750-91b2-347c-b991-e63f8c483de9",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:youth",
+ "name": "youth"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "cf501333-167f-31b1-b99a-0e9b17d4a16c",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1.5
+ },
+ "riderCategory": {
+ "id": "orca:special",
+ "name": "special"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "1b2d9477-8ce5-3eec-a63d-7d2534fa3bef",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:youth",
+ "name": "youth"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "105e04d3-a575-3f65-96c1-e33d5199f339",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1
+ },
+ "riderCategory": {
+ "id": "orca:senior",
+ "name": "senior"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ }
+ ],
+ "pickupType": "SCHEDULED",
+ "dropoffType": "SCHEDULED",
+ "pickupBookingInfo": null,
+ "rentedBike": null,
+ "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "arrivalDelay": 0,
+ "distance": 12180.55,
+ "duration": 2112,
+ "endTime": 1685142540000,
+ "mode": "BUS",
+ "realTime": false,
+ "realtimeState": null,
+ "startTime": 1685140428000,
+ "transitLeg": true,
+ "accessibilityScore": null,
+ "trip": {
+ "id": "VHJpcDprY206NTQxNDgwODUz",
+ "gtfsId": "kcm:541480853",
+ "tripHeadsign": "Northgate Station Ridgecrest"
+ },
+ "agency": {
+ "name": "Metro Transit",
+ "id": "QWdlbmN5OmtjbTox",
+ "timezone": "America/Los_Angeles",
+ "url": "https://kingcounty.gov/en/dept/metro",
+ "alerts": []
+ },
+ "legGeometry": {
+ "length": 262,
+ "points": "oqrbH~fmiVp@??fEC|DCjE?fN?h@??AfEAvCAxA@n@AdCAhD?tAAtCA~@???hDAfF?bBAn@???ZA`A?`@Av@Ax@AhB?R@NBNFLLPVXN@T?R?l@A??fCGnCEzBE??hBErAApIM??nGKX?R?L?N@XFLBFBJ@LFLHPHPJTNXV^ZTPRRrAl@F@??d@Jb@DT?TCxCIVCl@Ab@CTEHCTGNGTKb@Sp@c@TINIPC??FCPEVAVAV?b@?V@^A\\IZEZKf@UrAq@|@a@d@W`@_@f@_@NM??x@m@b@Cz@M\\C^?z@Px@XZPZXn@r@r@f@ZLb@Rb@FZFb@?z@???tA?rF@|I?AjD??CbJA|FA|C??AtBEpOEtOxCC??Z?|BAzEGdECf@???dIEhJG`DE??jAAfFEzCE??J?zA?xACdDE??N?pDCpDE`BCp@A??pFG~CC^A^???fBAbEEvCE\\?X???bCA|@F`E?f@?@mG???C@yGFwO@qG@gF???]DyOtAC??tGAt@???vHEn@???~HE|HA??n@?|G?tC?xA?~D@Z???lI?p@???rHBB?pAC??vGIp@???xHI|@C??|CCt@?ZBRJRNRR`@h@LTpFhI??~@xAbD~E`A|A|AzB\\Z^Vf@Rx@R~CArAC?fB??CvLA|OnA???hDEbFGx@???|DErDAvDA?jB??AfCA`HA~G?pB?P|A?lA?"
+ },
+ "intermediateStops": [
+ {
+ "lat": 47.7775421,
+ "lon": -122.303215,
+ "name": "NE 205th St & 52nd Ave W",
+ "stopCode": "82410",
+ "stopId": "U3RvcDprY206ODI0MTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7775955,
+ "lon": -122.308685,
+ "name": "NE 205th St & 56th Ave W",
+ "stopCode": "82430",
+ "stopId": "U3RvcDprY206ODI0MzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7776146,
+ "lon": -122.311432,
+ "name": "NE 205th St & 58th Pl W",
+ "stopCode": "82440",
+ "stopId": "U3RvcDprY206ODI0NDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7768135,
+ "lon": -122.31382,
+ "name": "15th Ave NE & Ballinger Way NE",
+ "stopCode": "77400",
+ "stopId": "U3RvcDprY206Nzc0MDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7747955,
+ "lon": -122.313721,
+ "name": "15th Ave NE & Forest Park Dr NE",
+ "stopCode": "77410",
+ "stopId": "U3RvcDprY206Nzc0MTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7721519,
+ "lon": -122.313614,
+ "name": "15th Ave NE & NE 200th Ct",
+ "stopCode": "77418",
+ "stopId": "U3RvcDprY206Nzc0MTg",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.768734,
+ "lon": -122.314636,
+ "name": "15th Ave NE & NE 192nd St",
+ "stopCode": "77940",
+ "stopId": "U3RvcDprY206Nzc5NDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7656479,
+ "lon": -122.313995,
+ "name": "15th Ave NE & NE Perkins Way",
+ "stopCode": "77440",
+ "stopId": "U3RvcDprY206Nzc0NDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7626801,
+ "lon": -122.312752,
+ "name": "15th Ave NE & 24th Ave NE",
+ "stopCode": "77450",
+ "stopId": "U3RvcDprY206Nzc0NTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7591515,
+ "lon": -122.31356,
+ "name": "15th Ave NE & NE 180th St",
+ "stopCode": "77460",
+ "stopId": "U3RvcDprY206Nzc0NjA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7558212,
+ "lon": -122.314339,
+ "name": "NE 175th St & 15th Ave NE",
+ "stopCode": "77738",
+ "stopId": "U3RvcDprY206Nzc3Mzg",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7558632,
+ "lon": -122.318176,
+ "name": "NE 175th St & 10th Ave NE",
+ "stopCode": "77737",
+ "stopId": "U3RvcDprY206Nzc3Mzc",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7550964,
+ "lon": -122.324158,
+ "name": "5th Ave NE & NE 174th St",
+ "stopCode": "81246",
+ "stopId": "U3RvcDprY206ODEyNDY",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.752037,
+ "lon": -122.324104,
+ "name": "5th Ave NE & NE 170th St",
+ "stopCode": "81248",
+ "stopId": "U3RvcDprY206ODEyNDg",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7477875,
+ "lon": -122.324013,
+ "name": "5th Ave NE & NE 163rd St",
+ "stopCode": "77560",
+ "stopId": "U3RvcDprY206Nzc1NjA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7454681,
+ "lon": -122.323944,
+ "name": "5th Ave NE & NE 161st St",
+ "stopCode": "77570",
+ "stopId": "U3RvcDprY206Nzc1NzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7436676,
+ "lon": -122.323883,
+ "name": "5th Ave NE & NE 158th St",
+ "stopCode": "77580",
+ "stopId": "U3RvcDprY206Nzc1ODA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.741066,
+ "lon": -122.323792,
+ "name": "5th Ave NE & NE 155th St",
+ "stopCode": "81252",
+ "stopId": "U3RvcDprY206ODEyNTI",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7387314,
+ "lon": -122.323723,
+ "name": "5th Ave NE & NE 152nd St",
+ "stopCode": "81254",
+ "stopId": "U3RvcDprY206ODEyNTQ",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7361908,
+ "lon": -122.323654,
+ "name": "5th Ave NE & NE 148th St",
+ "stopCode": "81256",
+ "stopId": "U3RvcDprY206ODEyNTY",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7339783,
+ "lon": -122.322235,
+ "name": "NE 145th St & 6th Ave NE",
+ "stopCode": "82200",
+ "stopId": "U3RvcDprY206ODIyMDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7339096,
+ "lon": -122.315598,
+ "name": "NE 145th St & 12th Ave NE",
+ "stopCode": "82220",
+ "stopId": "U3RvcDprY206ODIyMjA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7335167,
+ "lon": -122.312851,
+ "name": "15th Ave NE & NE 145th St",
+ "stopCode": "38830",
+ "stopId": "U3RvcDprY206Mzg4MzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7318573,
+ "lon": -122.31282,
+ "name": "15th Ave NE & NE 143rd St",
+ "stopCode": "38840",
+ "stopId": "U3RvcDprY206Mzg4NDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7300568,
+ "lon": -122.312798,
+ "name": "15th Ave NE & NE 140th St",
+ "stopCode": "38850",
+ "stopId": "U3RvcDprY206Mzg4NTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7268677,
+ "lon": -122.312752,
+ "name": "15th Ave NE & NE 135th St",
+ "stopCode": "38870",
+ "stopId": "U3RvcDprY206Mzg4NzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7228928,
+ "lon": -122.312767,
+ "name": "15th Ave NE & NE 130th St",
+ "stopCode": "38890",
+ "stopId": "U3RvcDprY206Mzg4OTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7209663,
+ "lon": -122.312782,
+ "name": "15th Ave NE & NE 127th St",
+ "stopCode": "38900",
+ "stopId": "U3RvcDprY206Mzg5MDA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7190018,
+ "lon": -122.312767,
+ "name": "15th Ave NE & NE 125th St",
+ "stopCode": "38910",
+ "stopId": "U3RvcDprY206Mzg5MTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.71735,
+ "lon": -122.312714,
+ "name": "15th Ave NE & NE 123rd St",
+ "stopCode": "38920",
+ "stopId": "U3RvcDprY206Mzg5MjA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7154694,
+ "lon": -122.312645,
+ "name": "15th Ave NE & NE 120th St",
+ "stopCode": "38930",
+ "stopId": "U3RvcDprY206Mzg5MzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7125664,
+ "lon": -122.314827,
+ "name": "Pinehurst Way NE & NE 115th St",
+ "stopCode": "38962",
+ "stopId": "U3RvcDprY206Mzg5NjI",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7086334,
+ "lon": -122.318367,
+ "name": "NE Northgate Way & Roosevelt Way NE",
+ "stopCode": "82198",
+ "stopId": "U3RvcDprY206ODIxOTg",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7081947,
+ "lon": -122.323364,
+ "name": "5th Ave NE & NE Northgate Way",
+ "stopCode": "23230",
+ "stopId": "U3RvcDprY206MjMyMzA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7059097,
+ "lon": -122.323296,
+ "name": "5th Ave NE & Northgate Mall",
+ "stopCode": "23250",
+ "stopId": "U3RvcDprY206MjMyNTA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.7032166,
+ "lon": -122.323692,
+ "name": "NE 103rd St & 5th Ave NE",
+ "stopCode": "35290",
+ "stopId": "U3RvcDprY206MzUyOTA",
+ "locationType": "STOP"
+ }
+ ],
+ "route": {
+ "shortName": "347",
+ "longName": null,
+ "color": null,
+ "textColor": null,
+ "id": "Um91dGU6a2NtOjEwMDIwNA",
+ "type": 3,
+ "alerts": []
+ },
+ "from": {
+ "lat": 47.7776833,
+ "lon": -122.297684,
+ "name": "48th Ave W & 244th St SW",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDprY206ODI0MDU",
+ "code": "82405",
+ "gtfsId": "kcm:82405",
+ "alerts": []
+ }
+ },
+ "to": {
+ "lat": 47.7023087,
+ "lon": -122.328026,
+ "name": "Northgate Station - Bay 4",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDprY206MzUzMTg",
+ "code": "35318",
+ "gtfsId": "kcm:35318",
+ "alerts": []
+ }
+ },
+ "steps": []
+ },
+ {
+ "fareProducts": [],
+ "pickupType": "SCHEDULED",
+ "dropoffType": "SCHEDULED",
+ "pickupBookingInfo": null,
+ "rentedBike": false,
+ "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "arrivalDelay": 0,
+ "distance": 60.87,
+ "duration": 56,
+ "endTime": 1685142596000,
+ "mode": "WALK",
+ "realTime": false,
+ "realtimeState": null,
+ "startTime": 1685142540000,
+ "transitLeg": false,
+ "accessibilityScore": null,
+ "trip": null,
+ "agency": null,
+ "legGeometry": {
+ "length": 9,
+ "points": "kzcbHdesiV?Ce@?Q??L?V?RO??B"
+ },
+ "intermediateStops": null,
+ "route": null,
+ "from": {
+ "lat": 47.7023087,
+ "lon": -122.328026,
+ "name": "Northgate Station - Bay 4",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDprY206MzUzMTg",
+ "code": "35318",
+ "gtfsId": "kcm:35318",
+ "alerts": []
+ }
+ },
+ "to": {
+ "lat": 47.702662,
+ "lon": -122.32832,
+ "name": "Northgate Station",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDo0MDo5OTAwMDU",
+ "code": "N11-T1",
+ "gtfsId": "40:990005",
+ "alerts": []
+ }
+ },
+ "steps": [
+ {
+ "distance": 60.87,
+ "lat": 47.7023088,
+ "lon": -122.3280018,
+ "relativeDirection": "DEPART",
+ "absoluteDirection": "NORTH",
+ "stayOn": false,
+ "streetName": "path",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ }
+ ]
+ },
+ {
+ "fareProducts": [
+ {
+ "id": "0f76bb4a-7491-34b9-95ce-5d6f4e6b4d53",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 3
+ },
+ "riderCategory": {
+ "id": "orca:regular",
+ "name": "regular"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "ab8213b7-eddd-3d20-9b2d-a5ed8d8573a4",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0.25
+ },
+ "riderCategory": {
+ "id": "orca:regular",
+ "name": "regular"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "75fef4dc-142b-3589-ac62-d484f5b41143",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 2.75
+ },
+ "riderCategory": {
+ "id": "orca:regular",
+ "name": "regular"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "69bafcef-e2ee-3a6c-a355-0883f3d5561f",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1
+ },
+ "riderCategory": {
+ "id": "orca:senior",
+ "name": "senior"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "b0026496-8eea-31c8-8468-a67b00abc2b5",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:youth",
+ "name": "youth"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "8e77f7c7-7eda-32a8-ad44-23372b079942",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:special",
+ "name": "special"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "e8e69341-c5f3-3833-9a0a-de3118460842",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1.5
+ },
+ "riderCategory": {
+ "id": "orca:special",
+ "name": "special"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "7ab667f5-3bb1-3973-b065-46ac5bffe3a4",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:youth",
+ "name": "youth"
+ },
+ "medium": {
+ "id": "orca:cash",
+ "name": "cash"
+ }
+ }
+ },
+ {
+ "id": "b3ff8f6e-f55f-3098-bb23-2bc49441d81f",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 0
+ },
+ "riderCategory": {
+ "id": "orca:senior",
+ "name": "senior"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ },
+ {
+ "id": "2fe47d57-d05f-3f51-9d4b-4d060a68abd9",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ },
+ "amount": 1
+ },
+ "riderCategory": {
+ "id": "orca:senior",
+ "name": "senior"
+ },
+ "medium": {
+ "id": "orca:electronic",
+ "name": "electronic"
+ }
+ }
+ }
+ ],
+ "pickupType": "SCHEDULED",
+ "dropoffType": "SCHEDULED",
+ "pickupBookingInfo": null,
+ "rentedBike": null,
+ "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "arrivalDelay": 0,
+ "distance": 23087.2,
+ "duration": 2220,
+ "endTime": 1685145000000,
+ "mode": "TRAM",
+ "realTime": false,
+ "realtimeState": null,
+ "startTime": 1685142780000,
+ "transitLeg": true,
+ "accessibilityScore": null,
+ "trip": {
+ "id": "VHJpcDo0MDpMTFJfMjAyMy0wMy0xOC0yMDIzLTA5LTA0X1dlZWtkYXlfMjA4Ng",
+ "gtfsId": "40:LLR_2023-03-18-2023-09-04_Weekday_2086",
+ "tripHeadsign": "Angle Lake"
+ },
+ "agency": {
+ "name": "Sound Transit",
+ "id": "QWdlbmN5OjQwOjQw",
+ "timezone": "America/Los_Angeles",
+ "url": "https://www.soundtransit.org",
+ "alerts": []
+ },
+ "legGeometry": {
+ "length": 509,
+ "points": "s|cbHhgsiVJ?dA?pAApA@nAHnANnALnANnALnALpAFnABpACpAGnAKr@G^CnAKpAEnA?pA@pA?nA@pA@pA@nA@pA@pA?nACpAKlASlAWjA]fAc@fAi@`Ak@`Aq@z@u@z@w@v@{@r@{@r@_Ar@}@r@}@r@_Ar@}@r@}@r@_Ar@}@r@_Ar@}@r@}@r@_Ar@}@r@}@r@_Ar@}@r@}@r@_Ar@}@r@}@r@_Ar@}@r@}@r@_Ar@}@r@}@r@}@v@{@x@w@~@s@bAk@fAe@hA_@nAWnAMnACpAApAAnA?V???x@ApACnAGnAKpAMnAMnAKnAMnAMnAMnAMpAKnAMnAMnAMnAKnAMnAMpAMnAKnAMnAMnAMnAMnAKpAMnAKnAIpAInAEpAGnACpAApAApA?nA?pA@pA@nABpA@h@???f@@nAAnAQfAc@~@q@v@{@j@cAd@gAZkARmANoAJqAJoAHoAJqAJoAJoALoALoARoATmAZkA^kA`@iAf@eAj@cAp@aAt@{@x@w@~@q@dAk@hAa@jAYnAOpAEnAApAApAAnAApAAbDAb@Cb@???H?p@Ar@?|FGx@?hA?rA@pADzAHtAHrALrARrAPzAV`BZxA^`Bd@rBn@|Aj@lBx@lBz@|Ax@fB`AhBlAbBlAfE`DrB~Avh@da@bHlFbDdCjEfDdK~HrB~A~@r@n@d@bAh@|@d@jAd@jA^tAVpAR~@HfADtA?n@A????vFC~A?x@?|@HbAPbAZbA`@x@b@dAz@v@r@~@jAr@dAp@rAd@fA`@jAd@bBZxAVbBRfBH~AFtA@|AAdBGpAIzAOdBWzASxAKx@Ix@?f@?n@@l@Dr@Jx@Hf@Jh@JZjAnDb@tAhBzFXvAhB|FnBlG??@?FTj@fABBRh@PVNRLHLDR@N?VGXMNInJoIlC_Cp@o@RQ????t@s@lCaC|GcGnCcClCaC\\[nBgBlBgB@???VUVURWVc@Xe@PYpBeDx@sAx@mAV_@TQ\\Sp@MzCA|FF@@??N@RFNFN@LB~BCF?D?fCAF?N?NCN?NALAHCJAJAHCNEPC^KxDeAxCy@NGJAJCJALAL?h@CrC???nHBrB?V?X@R@VBn@HVDP@T@z@?b@?z@?vp@B??@?zG?L?N?JCF?JAHEDABCF?@CDABCDCBCBEDCBEBCBGBEBC@EBGBEBG@E@E@GBE@G@E?CBM?IBI@K?I?SDeAFeBBqAJ_D@q@@o@?y@?s@?qAAgI@y@@o@Ds@JiBXkE@s@Bm@?{@Aw@Ey@Gy@Gg@Gi@Ie@Ke@UaAoDiNa@aBc@kBkAwF???AoAcGKg@Ii@Ge@Gg@Ee@Ek@Am@Ak@Ak@@k@Bg@Bk@@e@Dk@Fc@Hk@Js@Rw@zDsPNs@Lm@RqAPqAHgAf@kHLiBDc@BWFWDUFUHUJUJULUJONSNKPQtCmB??~BcBb@Ud@STEVGVCVA^?pHDvOFjLCx@?pDH`@Bb@Cb@?^Ib@G^I\\M\\O\\Qhg@oX~@e@????|L{GjDgB|@c@f@QtE_BpAe@lO}FbJkDdQwGrFyBt[{LnSqI?A"
+ },
+ "intermediateStops": [
+ {
+ "lat": 47.676107,
+ "lon": -122.316041,
+ "name": "Roosevelt Station",
+ "stopCode": "N09-T1",
+ "stopId": "U3RvcDo0MDo5OTAwMDM",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.659875,
+ "lon": -122.314194,
+ "name": "U District Station",
+ "stopCode": "N07-T1",
+ "stopId": "U3RvcDo0MDo5OTAwMDE",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.649349,
+ "lon": -122.303795,
+ "name": "Univ of Washington Station",
+ "stopCode": "N05-T1",
+ "stopId": "U3RvcDo0MDo5OTYwNA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.61956,
+ "lon": -122.320389,
+ "name": "Capitol Hill Station",
+ "stopCode": "N03-T1",
+ "stopId": "U3RvcDo0MDo5OTYxMA",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.61145,
+ "lon": -122.337532,
+ "name": "Westlake Station",
+ "stopCode": "C03-T1",
+ "stopId": "U3RvcDo0MDoxMTA4",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.607246,
+ "lon": -122.335754,
+ "name": "University St Station",
+ "stopCode": "C05-T1",
+ "stopId": "U3RvcDo0MDo0NTU",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.602139,
+ "lon": -122.331055,
+ "name": "Pioneer Square Station",
+ "stopCode": "C07-T1",
+ "stopId": "U3RvcDo0MDo1MDE",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.59766,
+ "lon": -122.328217,
+ "name": "Int'l Dist/Chinatown Station",
+ "stopCode": "C09-T1",
+ "stopId": "U3RvcDo0MDo2MjM",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.591824,
+ "lon": -122.327354,
+ "name": "Stadium Station",
+ "stopCode": "C13-T1",
+ "stopId": "U3RvcDo0MDo5OTEwMQ",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.579952,
+ "lon": -122.327522,
+ "name": "SODO Station",
+ "stopCode": "C15-T1",
+ "stopId": "U3RvcDo0MDo5OTExMQ",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.579124,
+ "lon": -122.311279,
+ "name": "Beacon Hill Station",
+ "stopCode": "C19-T1",
+ "stopId": "U3RvcDo0MDo5OTEyMQ",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.576439,
+ "lon": -122.297737,
+ "name": "Mount Baker Station",
+ "stopCode": "C23-T1",
+ "stopId": "U3RvcDo0MDo1NTk0OQ",
+ "locationType": "STOP"
+ },
+ {
+ "lat": 47.559025,
+ "lon": -122.292389,
+ "name": "Columbia City Station",
+ "stopCode": "C25-T1",
+ "stopId": "U3RvcDo0MDo1NjAzOQ",
+ "locationType": "STOP"
+ }
+ ],
+ "route": {
+ "shortName": "1-Line",
+ "longName": "Northgate - Angle Lake",
+ "color": "28813F",
+ "textColor": "FFFFFF",
+ "id": "Um91dGU6NDA6MTAwNDc5",
+ "type": 0,
+ "alerts": []
+ },
+ "from": {
+ "lat": 47.702662,
+ "lon": -122.32832,
+ "name": "Northgate Station",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDo0MDo5OTAwMDU",
+ "code": "N11-T1",
+ "gtfsId": "40:990005",
+ "alerts": []
+ }
+ },
+ "to": {
+ "lat": 47.537529,
+ "lon": -122.281471,
+ "name": "Othello Station",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDo0MDo1NjE1OQ",
+ "code": "C27-T1",
+ "gtfsId": "40:56159",
+ "alerts": []
+ }
+ },
+ "steps": []
+ },
+ {
+ "fareProducts": [],
+ "pickupType": "SCHEDULED",
+ "dropoffType": "SCHEDULED",
+ "pickupBookingInfo": null,
+ "rentedBike": false,
+ "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "arrivalDelay": 0,
+ "distance": 2167.47,
+ "duration": 1739,
+ "endTime": 1685146739000,
+ "mode": "WALK",
+ "realTime": false,
+ "realtimeState": null,
+ "startTime": 1685145000000,
+ "transitLeg": false,
+ "accessibilityScore": null,
+ "trip": null,
+ "agency": null,
+ "legGeometry": {
+ "length": 135,
+ "points": "otcaHfbjiV?BXMF\\\\??fC?NJ?@|@BbC?R?TAB?|A?t@@f@?L@B?V?TA@@D?D?vA?h@?X?TAXCXANCPCPKd@ERCLCH@@HFLJ@@@?@@@@B@F?F?D?DAENDAHCFCFNPTDBD?RIl@Wb@Qn@c@h@[ZONGPCD?LEFAL@Ad@@vF?pAApA?nH@\\@J@DBPJLEFIFI^ENQf@EBCFHJP^BL@J@PAhFA|A?bAArB?hA@JBFDDDHl@b@RHTFXD\\@P@PCjB[hB[h@M`A]lAk@vBoAlAu@h@YLCPAJ@J@LFLHHJHJFNHTv@dGFb@@NK@KCOCM@{Ad@e@F_@LM@"
+ },
+ "intermediateStops": null,
+ "route": null,
+ "from": {
+ "lat": 47.537529,
+ "lon": -122.281471,
+ "name": "Othello Station",
+ "vertexType": "TRANSIT",
+ "rentalVehicle": null,
+ "stop": {
+ "id": "U3RvcDo0MDo1NjE1OQ",
+ "code": "C27-T1",
+ "gtfsId": "40:56159",
+ "alerts": []
+ }
+ },
+ "to": {
+ "lat": 47.5312889,
+ "lon": -122.2958113,
+ "name": "New Light Christian Church, Seattle, WA, USA",
+ "vertexType": "NORMAL",
+ "rentalVehicle": null,
+ "stop": null
+ },
+ "steps": [
+ {
+ "distance": 14.92,
+ "lat": 47.5375246,
+ "lon": -122.2814907,
+ "relativeDirection": "DEPART",
+ "absoluteDirection": "SOUTH",
+ "stayOn": false,
+ "streetName": "Martin Luther King Junior Way South",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 12.32,
+ "lat": 47.5373972,
+ "lon": -122.2814286,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "SOUTHWEST",
+ "stayOn": false,
+ "streetName": "service road",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 79.38,
+ "lat": 47.537352,
+ "lon": -122.2815784,
+ "relativeDirection": "LEFT",
+ "absoluteDirection": "SOUTH",
+ "stayOn": true,
+ "streetName": "parking aisle",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 186.19,
+ "lat": 47.5371499,
+ "lon": -122.2823392,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "WEST",
+ "stayOn": true,
+ "streetName": "sidewalk",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 185.66,
+ "lat": 47.5371051,
+ "lon": -122.2847952,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "NORTHWEST",
+ "stayOn": true,
+ "streetName": "sidewalk",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 223.22,
+ "lat": 47.5369894,
+ "lon": -122.2868292,
+ "relativeDirection": "HARD_RIGHT",
+ "absoluteDirection": "NORTHWEST",
+ "stayOn": true,
+ "streetName": "path",
+ "area": false,
+ "alerts": [null],
+ "elevationProfile": []
+ },
+ {
+ "distance": 316.7,
+ "lat": 47.535227,
+ "lon": -122.2863141,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "WEST",
+ "stayOn": false,
+ "streetName": "South Webster Street",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 55.15,
+ "lat": 47.5351293,
+ "lon": -122.2904874,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "NORTHWEST",
+ "stayOn": false,
+ "streetName": "path",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 259.82,
+ "lat": 47.5354239,
+ "lon": -122.2910651,
+ "relativeDirection": "LEFT",
+ "absoluteDirection": "SOUTHWEST",
+ "stayOn": false,
+ "streetName": "South Webster Street",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 57.96,
+ "lat": 47.5352763,
+ "lon": -122.2944348,
+ "relativeDirection": "SLIGHTLY_LEFT",
+ "absoluteDirection": "SOUTHWEST",
+ "stayOn": false,
+ "streetName": "29th Avenue South",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 644.09,
+ "lat": 47.5348545,
+ "lon": -122.2948441,
+ "relativeDirection": "CONTINUE",
+ "absoluteDirection": "SOUTH",
+ "stayOn": false,
+ "streetName": "Military Road South",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ },
+ {
+ "distance": 132.1,
+ "lat": 47.5301402,
+ "lon": -122.2953592,
+ "relativeDirection": "RIGHT",
+ "absoluteDirection": "NORTH",
+ "stayOn": false,
+ "streetName": "service road",
+ "area": false,
+ "alerts": [],
+ "elevationProfile": []
+ }
+ ]
+ }
+ ]
+}
diff --git a/packages/itinerary-body/src/__mocks__/itineraries/otp2-with-fareproducts.json b/packages/itinerary-body/src/__mocks__/itineraries/otp2-with-fareproducts.json
index 177302f1b..5fb61c392 100644
--- a/packages/itinerary-body/src/__mocks__/itineraries/otp2-with-fareproducts.json
+++ b/packages/itinerary-body/src/__mocks__/itineraries/otp2-with-fareproducts.json
@@ -1,1130 +1,1492 @@
{
- "duration": 3583,
- "startTime": 1674505485000,
- "endTime": 1674509068000,
- "walkTime": 930,
- "transitTime": 2340,
- "waitingTime": 313,
- "walkDistance": 1157.33,
- "walkLimitExceeded": false,
- "generalizedCost": 5616,
- "elevationLost": 0,
- "elevationGained": 0,
- "transfers": 1,
- "fare": {
- "fare": {
- "senior": {
- "cents": 225,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
- }
+ "accessibilityScore": null,
+ "duration": 5710,
+ "endTime": 1686626346000,
+ "legs": [
+ {
+ "accessibilityScore": null,
+ "fareProducts": [],
+ "agency": null,
+ "arrivalDelay": 0,
+ "departureDelay": 0,
+ "distance": 1563.5,
+ "dropoffType": "SCHEDULED",
+ "duration": 1264,
+ "endTime": 1686621900000,
+ "from": {
+ "lat": 47.8308509,
+ "lon": -122.3177839,
+ "name": "47.83085, -122.31778",
+ "rentalVehicle": null,
+ "stop": null,
+ "vertexType": "NORMAL"
},
- "electronicSpecial": {
- "cents": 150,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
- }
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": null,
+ "legGeometry": {
+ "length": 49,
+ "points": "e~|bHdeqiV@xJpA?~A?H?|A?n@@jC@N?L?xAAdDCrB@bA?pDAL?rBA~@AhD?xBCV?|@?t@?d@@X?Z?V?H@V?T?J?N??U@sA?{@?c@@Q?}@@qBF?DkF?WBmC@IDI@CL@\\V@A"
},
- "electronicSenior": {
- "cents": 125,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "mode": "WALK",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": false,
+ "route": null,
+ "startTime": 1686620636000,
+ "steps": [
+ {
+ "absoluteDirection": "WEST",
+ "alerts": [],
+ "area": false,
+ "distance": 141.18,
+ "elevationProfile": [],
+ "lat": 47.8309108,
+ "lon": -122.3177846,
+ "relativeDirection": "DEPART",
+ "stayOn": false,
+ "streetName": "185th Place Southwest"
+ },
+ {
+ "absoluteDirection": "SOUTH",
+ "alerts": [],
+ "area": false,
+ "distance": 1080.61,
+ "elevationProfile": [],
+ "lat": 47.830901,
+ "lon": -122.3196758,
+ "relativeDirection": "LEFT",
+ "stayOn": false,
+ "streetName": "64th Avenue West"
+ },
+ {
+ "absoluteDirection": "EAST",
+ "alerts": [],
+ "area": false,
+ "distance": 148.37,
+ "elevationProfile": [],
+ "lat": 47.8211832,
+ "lon": -122.3196493,
+ "relativeDirection": "LEFT",
+ "stayOn": false,
+ "streetName": "196th Street Southwest"
+ },
+ {
+ "absoluteDirection": "SOUTH",
+ "alerts": [],
+ "area": false,
+ "distance": 5.26,
+ "elevationProfile": [],
+ "lat": 47.8211587,
+ "lon": -122.3176623,
+ "relativeDirection": "RIGHT",
+ "stayOn": false,
+ "streetName": "service road"
+ },
+ {
+ "absoluteDirection": "EAST",
+ "alerts": [],
+ "area": false,
+ "distance": 188.06,
+ "elevationProfile": [],
+ "lat": 47.8211115,
+ "lon": -122.3176665,
+ "relativeDirection": "LEFT",
+ "stayOn": true,
+ "streetName": "sidewalk"
}
+ ],
+ "to": {
+ "lat": 47.820787,
+ "lon": -122.315656,
+ "name": "Crossroads SB Station at 196th St SW",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "2764",
+ "gtfsId": "CommTrans:2764",
+ "id": "U3RvcDpDb21tVHJhbnM6Mjc2NA"
+ },
+ "vertexType": "TRANSIT"
},
- "electronicYouth": {
- "cents": 0,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "transitLeg": false,
+ "trip": null
+ },
+ {
+ "accessibilityScore": null,
+ "fareProducts": [
+ {
+ "id": "ea135a2b-9d68-3a39-904c-9e60a8688e9c",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 1.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "5134f5c4-6055-3250-977c-62c1c7611a44",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
+ },
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
+ }
+ }
+ },
+ {
+ "id": "c3cd5fea-fc52-3368-85e5-97ac250cdba3",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 1.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "special",
+ "id": "orca:special"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "6a93fabf-1515-3f43-af14-f8803568504c",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 2.5,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
+ },
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
+ }
+ }
+ },
+ {
+ "id": "095bed9e-c459-361e-bc5a-b944196874b6",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "2505a198-c406-3ebd-aa77-ac2b8094bcc8",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 2.5,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "8b25f594-46f2-3d5e-ae92-ac7f28d8b043",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 1.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
+ },
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
+ }
+ }
}
+ ],
+ "agency": {
+ "alerts": [],
+ "id": "QWdlbmN5OkNvbW1UcmFuczoyOQ",
+ "name": "Community Transit",
+ "timezone": "America/Los_Angeles",
+ "url": "http://www.communitytransit.org/"
},
- "electronicRegular": {
- "cents": 250,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
- }
+ "arrivalDelay": 0,
+ "departureDelay": 0,
+ "distance": 6117.26,
+ "dropoffType": "SCHEDULED",
+ "duration": 1140,
+ "endTime": 1686623040000,
+ "from": {
+ "lat": 47.820787,
+ "lon": -122.315656,
+ "name": "Crossroads SB Station at 196th St SW",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "2764",
+ "gtfsId": "CommTrans:2764",
+ "id": "U3RvcDpDb21tVHJhbnM6Mjc2NA"
+ },
+ "vertexType": "TRANSIT"
},
- "youth": {
- "cents": 0,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": [
+ {
+ "lat": 47.813736,
+ "locationType": "STOP",
+ "lon": -122.321744,
+ "name": "College SB Station at 204th St SW",
+ "stopCode": "2760",
+ "stopId": "U3RvcDpDb21tVHJhbnM6Mjc2MA"
+ },
+ {
+ "lat": 47.802879,
+ "locationType": "STOP",
+ "lon": -122.329612,
+ "name": "216th St SW SB Station",
+ "stopCode": "2754",
+ "stopId": "U3RvcDpDb21tVHJhbnM6Mjc1NA"
+ },
+ {
+ "lat": 47.782854,
+ "locationType": "STOP",
+ "lon": -122.344036,
+ "name": "238th St SW SB Station",
+ "stopCode": "2748",
+ "stopId": "U3RvcDpDb21tVHJhbnM6Mjc0OA"
}
+ ],
+ "legGeometry": {
+ "length": 272,
+ "points": "o~zbH|vpiVv@n@LJh@b@dA|@TPHFXT|@t@ZVPNl@f@f@`@LJ`@Zl@h@n@h@t@l@\\VlB~Ab@^f@`@VTNLNJnAbArAhATR\\XHF\\XLJf@`@LJTRzAnAf@^RNHF??DBNJ`@ZLHh@`@JHRLf@\\LHdAp@PLf@Zr@d@PJv@f@HFb@XXRrAz@`@Vv@f@\\TXRLHLHLHJFn@b@LHJHb@Xp@b@NJfAp@HF\\TRLl@^\\TTN\\Td@XXPv@f@xA`Aj@^PJn@b@p@b@TN|BxADBLHb@ZTNHFl@^HFVNXP\\T^Th@\\\\TfAt@FD??p@b@HF`@VNHTNZRj@^d@ZjAv@bAn@bAn@FDxBvAt@d@bAn@JFNJNHvA~@hAt@t@d@NJ^T`@VTNXPn@b@r@f@LHHF|@j@ZTv@f@vA|@NJVNZRd@Zb@XTNXPf@\\TLb@XdAr@zA`ARLj@`@PLh@\\VNf@\\hAt@RLTLTNTNLHLHRLPLTNdAp@h@\\xA~@b@XLHZR~@l@^V`BdANJd@Z`An@VNZR^V\\TZRjBjAfAr@d@Z`GxD^VXPNJh@Zt@d@z@j@b@XJHlAv@`Al@VPFD??LHZRTNPLHFJHNJNLPLLJ\\VLJt@l@b@\\RP`@Z\\VXRXPd@VPJTJRHPFZJVHXHVFPDRD`@FXB\\@X@^?T?hAEz@GdAILA|@Eb@ANAjACr@Cr@AX?R?P?VAX?l@?T?f@?T?|AAH?BqBAg@?{A?m@?c@?iB?u@?yCe@?Eu@Fw@?sF"
},
- "regular": {
- "cents": 250,
- "currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
- }
+ "mode": "BUS",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": null,
+ "route": {
+ "alerts": [],
+ "color": "006aff",
+ "id": "Um91dGU6Q29tbVRyYW5zOjcwMQ",
+ "longName": "Everett - Aurora Village",
+ "shortName": "Swift Blue",
+ "textColor": "ffffff",
+ "type": 3
+ },
+ "startTime": 1686621900000,
+ "steps": [],
+ "to": {
+ "lat": 47.774297,
+ "lon": -122.341069,
+ "name": "Aurora Village Station",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "2742",
+ "gtfsId": "CommTrans:2742",
+ "id": "U3RvcDpDb21tVHJhbnM6Mjc0Mg"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "transitLeg": true,
+ "trip": {
+ "gtfsId": "CommTrans:11084840__MCOB-DO:123:0:Weekday:1:23MAR:67020:12345",
+ "id": "VHJpcDpDb21tVHJhbnM6MTEwODQ4NDBfX01DT0ItRE86MTIzOjA6V2Vla2RheToxOjIzTUFSOjY3MDIwOjEyMzQ1",
+ "tripHeadsign": "Aurora Village"
}
},
- "details": {
- "senior": [],
- "electronicSpecial": [],
- "electronicSenior": [],
- "electronicYouth": [],
- "electronicRegular": [],
- "youth": [],
- "regular": []
+ {
+ "accessibilityScore": null,
+ "fareProducts": [],
+ "agency": null,
+ "arrivalDelay": 0,
+ "departureDelay": 0,
+ "distance": 138.12,
+ "dropoffType": "SCHEDULED",
+ "duration": 131,
+ "endTime": 1686623171000,
+ "from": {
+ "lat": 47.774297,
+ "lon": -122.341069,
+ "name": "Aurora Village Station",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "2742",
+ "gtfsId": "CommTrans:2742",
+ "id": "U3RvcDpDb21tVHJhbnM6Mjc0Mg"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": null,
+ "legGeometry": {
+ "length": 15,
+ "points": "i|qbHtvuiVE??t@?zBJ??tB@L@C?A@C?C?ACI?O@?"
+ },
+ "mode": "WALK",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": false,
+ "route": null,
+ "startTime": 1686623040000,
+ "steps": [
+ {
+ "absoluteDirection": "WEST",
+ "alerts": [],
+ "area": false,
+ "distance": 65.97,
+ "elevationProfile": [],
+ "lat": 47.7743217,
+ "lon": -122.3410691,
+ "relativeDirection": "DEPART",
+ "stayOn": false,
+ "streetName": "open area"
+ },
+ {
+ "absoluteDirection": "SOUTH",
+ "alerts": [],
+ "area": false,
+ "distance": 6.04,
+ "elevationProfile": [],
+ "lat": 47.774323,
+ "lon": -122.341952,
+ "relativeDirection": "LEFT",
+ "stayOn": true,
+ "streetName": "path"
+ },
+ {
+ "absoluteDirection": "WEST",
+ "alerts": [],
+ "area": false,
+ "distance": 49.38,
+ "elevationProfile": [],
+ "lat": 47.7742687,
+ "lon": -122.3419519,
+ "relativeDirection": "RIGHT",
+ "stayOn": true,
+ "streetName": "sidewalk"
+ },
+ {
+ "absoluteDirection": "SOUTHEAST",
+ "alerts": [],
+ "area": true,
+ "distance": 16.73,
+ "elevationProfile": [],
+ "lat": 47.7742565,
+ "lon": -122.3426111,
+ "relativeDirection": "HARD_LEFT",
+ "stayOn": true,
+ "streetName": "open area"
+ }
+ ],
+ "to": {
+ "lat": 47.7742424,
+ "lon": -122.342407,
+ "name": "Aurora Village Transit Center - Bay 10",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "16100",
+ "gtfsId": "kcm:16100",
+ "id": "U3RvcDprY206MTYxMDA"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "transitLeg": false,
+ "trip": null
},
- "legProducts": [
- {
- "legIndices": [1],
- "products": [
- {
- "id": "orcaFares:farePayment",
+ {
+ "accessibilityScore": null,
+ "fareProducts": [
+ {
+ "id": "ca24dfed-196e-38f8-941e-88de142005df",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "e4c53703-37ce-3cc2-851f-3db7f40882d8",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 1.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "2f8986c1-2552-345a-a020-57506f70838f",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
+ },
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
+ }
+ }
+ },
+ {
+ "id": "2d3b06e8-6521-338e-b674-f8ce3c09d1ed",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "special",
+ "id": "orca:special"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "6b5bae4b-d495-3133-9924-f199ef1f1a72",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 1.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "special",
+ "id": "orca:special"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "f87203f1-9dba-3dd0-90c0-18cc2ed361a2",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 2.75,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "youth"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "c58a11c1-00d2-3093-bada-28543482a861",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 125,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
},
- "category": {
- "id": "orcaFares",
- "name": "senior"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "65becef9-10d7-3e6c-8a6b-f8037ae57aec",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 0.25,
+ "currency": {
+ "code": "USD",
+ "digits": 2
+ }
+ },
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
+ },
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
+ }
+ }
+ },
+ {
+ "id": "6e098f71-365a-3540-af1a-398a083028de",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 2.5,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "youth"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "5ba0d2b6-2539-36ad-b808-85ba1957e260",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 125,
+ "price": {
+ "amount": 1,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
},
- "category": {
- "id": "orcaFares",
- "name": "special"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ }
+ ],
+ "agency": {
+ "alerts": [],
+ "id": "QWdlbmN5OmtjbTox",
+ "name": "Metro Transit",
+ "timezone": "America/Los_Angeles",
+ "url": "https://kingcounty.gov/en/dept/metro"
+ },
+ "arrivalDelay": 0,
+ "departureDelay": 0,
+ "distance": 9663.15,
+ "dropoffType": "SCHEDULED",
+ "duration": 1260,
+ "endTime": 1686624720000,
+ "from": {
+ "lat": 47.7742424,
+ "lon": -122.342407,
+ "name": "Aurora Village Transit Center - Bay 10",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "16100",
+ "gtfsId": "kcm:16100",
+ "id": "U3RvcDprY206MTYxMDA"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": [
+ {
+ "lat": 47.7737427,
+ "locationType": "STOP",
+ "lon": -122.346207,
+ "name": "Aurora Ave N & N 200th St",
+ "stopCode": "75700",
+ "stopId": "U3RvcDprY206NzU3MDA"
+ },
+ {
+ "lat": 47.7672043,
+ "locationType": "STOP",
+ "lon": -122.3461,
+ "name": "Aurora Ave N & N 192nd St",
+ "stopCode": "75730",
+ "stopId": "U3RvcDprY206NzU3MzA"
+ },
+ {
+ "lat": 47.7628479,
+ "locationType": "STOP",
+ "lon": -122.346184,
+ "name": "Aurora Ave N & N 185th St",
+ "stopCode": "75740",
+ "stopId": "U3RvcDprY206NzU3NDA"
+ },
+ {
+ "lat": 47.7594452,
+ "locationType": "STOP",
+ "lon": -122.346237,
+ "name": "Aurora Ave N & N 180th St",
+ "stopCode": "75750",
+ "stopId": "U3RvcDprY206NzU3NTA"
+ },
+ {
+ "lat": 47.7552986,
+ "locationType": "STOP",
+ "lon": -122.345856,
+ "name": "Aurora Ave N & N 175th St",
+ "stopCode": "75760",
+ "stopId": "U3RvcDprY206NzU3NjA"
+ },
+ {
+ "lat": 47.7520294,
+ "locationType": "STOP",
+ "lon": -122.345741,
+ "name": "Aurora Ave N & N 170th St",
+ "stopCode": "75770",
+ "stopId": "U3RvcDprY206NzU3NzA"
+ },
+ {
+ "lat": 47.7483559,
+ "locationType": "STOP",
+ "lon": -122.345688,
+ "name": "Aurora Ave N & N 165th St",
+ "stopCode": "75780",
+ "stopId": "U3RvcDprY206NzU3ODA"
+ },
+ {
+ "lat": 47.7446899,
+ "locationType": "STOP",
+ "lon": -122.34565,
+ "name": "Aurora Ave N & N 160th St",
+ "stopCode": "75790",
+ "stopId": "U3RvcDprY206NzU3OTA"
+ },
+ {
+ "lat": 47.7408867,
+ "locationType": "STOP",
+ "lon": -122.345497,
+ "name": "Aurora Ave N & N 155th St",
+ "stopCode": "75800",
+ "stopId": "U3RvcDprY206NzU4MDA"
+ },
+ {
+ "lat": 47.7386818,
+ "locationType": "STOP",
+ "lon": -122.345428,
+ "name": "Aurora Ave N & N 152nd St",
+ "stopCode": "75810",
+ "stopId": "U3RvcDprY206NzU4MTA"
+ },
+ {
+ "lat": 47.7335701,
+ "locationType": "STOP",
+ "lon": -122.345268,
+ "name": "Aurora Ave N & N 145th St",
+ "stopCode": "6950",
+ "stopId": "U3RvcDprY206Njk1MA"
+ },
+ {
+ "lat": 47.726429,
+ "locationType": "STOP",
+ "lon": -122.3451,
+ "name": "Aurora Ave N & N 135th St",
+ "stopCode": "6990",
+ "stopId": "U3RvcDprY206Njk5MA"
+ },
+ {
+ "lat": 47.7229614,
+ "locationType": "STOP",
+ "lon": -122.345078,
+ "name": "Aurora Ave N & N 130th St",
+ "stopCode": "7000",
+ "stopId": "U3RvcDprY206NzAwMA"
+ },
+ {
+ "lat": 47.719902,
+ "locationType": "STOP",
+ "lon": -122.345047,
+ "name": "Aurora Ave N & N 125th St",
+ "stopCode": "7010",
+ "stopId": "U3RvcDprY206NzAxMA"
+ },
+ {
+ "lat": 47.7119751,
+ "locationType": "STOP",
+ "lon": -122.34491,
+ "name": "Aurora Ave N & N 115th St",
+ "stopCode": "7040",
+ "stopId": "U3RvcDprY206NzA0MA"
+ },
+ {
+ "lat": 47.7046356,
+ "locationType": "STOP",
+ "lon": -122.344856,
+ "name": "Aurora Ave N & N 105th St",
+ "stopCode": "7080",
+ "stopId": "U3RvcDprY206NzA4MA"
+ },
+ {
+ "lat": 47.701088,
+ "locationType": "STOP",
+ "lon": -122.34481,
+ "name": "Aurora Ave N & N 100th St",
+ "stopCode": "7100",
+ "stopId": "U3RvcDprY206NzEwMA"
+ },
+ {
+ "lat": 47.6974945,
+ "locationType": "STOP",
+ "lon": -122.344704,
+ "name": "Aurora Ave N & N 95th St",
+ "stopCode": "7120",
+ "stopId": "U3RvcDprY206NzEyMA"
+ },
+ {
+ "lat": 47.6939621,
+ "locationType": "STOP",
+ "lon": -122.344734,
+ "name": "Aurora Ave N & N 90th St",
+ "stopCode": "7140",
+ "stopId": "U3RvcDprY206NzE0MA"
+ }
+ ],
+ "legGeometry": {
+ "length": 168,
+ "points": "q{qbH`_viV?`AB~R?b@\\Cr@???dB?rBE~@A`HA|IElGArBC??f@?`DBpA?fB?pA@r@BhDBdB@lA???~@BbDDr@?|BBxAB|@?v@?PCb@?HA??v@AtAIjBInBGn@AvAGhES~BM??TA`AE`AA`B?hACbDCfB?|@C??v@AxDCnAAhA?bB?pA?xAA`AA??l@?n@?r@?|@CxAAj@?pB@vA?vB?dAA??|CEv@?fAE`CIhA?~CEnBC??~JIt@A??vBEz@AZCrAApAEfBEvFCvDCrBA??d@?RM~DA|FAlB?hJCfJGxA???pB?~EAlHCt@???dCArD?THxBAJKlCA??bA?r]OJL`HELMxCCbA???jPIbFAlFA|@CjBALLpDCrAA??|@?nCAnC?pCCnC?dAA??nB?`DCRItDC\\?rC?~@???pACpC?pCArA?NHh@?pCCj@???bB?jA?JKx@?fAAhA?~A?LH`@?pCAfC?"
+ },
+ "mode": "BUS",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": null,
+ "route": {
+ "alerts": [],
+ "color": null,
+ "id": "Um91dGU6a2NtOjEwMjYxNQ",
+ "longName": null,
+ "shortName": "E Line",
+ "textColor": null,
+ "type": 3
+ },
+ "startTime": 1686623460000,
+ "steps": [],
+ "to": {
+ "lat": 47.6898766,
+ "lon": -122.344681,
+ "name": "Aurora Ave N & N 85th St",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "7160",
+ "gtfsId": "kcm:7160",
+ "id": "U3RvcDprY206NzE2MA"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "transitLeg": true,
+ "trip": {
+ "gtfsId": "kcm:585679864",
+ "id": "VHJpcDprY206NTg1Njc5ODY0",
+ "tripHeadsign": "Downtown Seattle"
+ }
+ },
+ {
+ "accessibilityScore": null,
+ "fareProducts": [],
+ "agency": null,
+ "arrivalDelay": 0,
+ "departureDelay": 0,
+ "distance": 69.62,
+ "dropoffType": "SCHEDULED",
+ "duration": 62,
+ "endTime": 1686624782000,
+ "from": {
+ "lat": 47.6898766,
+ "lon": -122.344681,
+ "name": "Aurora Ave N & N 85th St",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "7160",
+ "gtfsId": "kcm:7160",
+ "id": "U3RvcDprY206NzE2MA"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": null,
+ "legGeometry": {
+ "length": 19,
+ "points": "ulabHhmviV?BE?AAS?A@W?AAM?G?E?I?M?C?A@ABACE??D"
+ },
+ "mode": "WALK",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": false,
+ "route": null,
+ "startTime": 1686624720000,
+ "steps": [
+ {
+ "absoluteDirection": "NORTH",
+ "alerts": [],
+ "area": false,
+ "distance": 69.62,
+ "elevationProfile": [],
+ "lat": 47.6898767,
+ "lon": -122.3447008,
+ "relativeDirection": "DEPART",
+ "stayOn": false,
+ "streetName": "sidewalk"
+ }
+ ],
+ "to": {
+ "lat": 47.6904907,
+ "lon": -122.344734,
+ "name": "N 85th St & Aurora Ave N",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "5370",
+ "gtfsId": "kcm:5370",
+ "id": "U3RvcDprY206NTM3MA"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "transitLeg": false,
+ "trip": null
+ },
+ {
+ "accessibilityScore": null,
+ "fareProducts": [
+ {
+ "id": "2db1b8be-49cd-3e32-8e21-6a2731f7e006",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 125,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
},
- "category": {
- "id": "orcaFares",
- "name": "senior"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
- "name": "rideCost",
- "amount": {
- "cents": 250,
+ }
+ },
+ {
+ "id": "7cf53db2-09ff-3b98-b9ef-b3278dc3ab04",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 1.25,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
},
- "category": {
- "id": "orcaFares",
- "name": "regular"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "710d3a35-1450-3130-b93d-c67a8be4b527",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 250,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
},
- "category": {
- "id": "orcaFares",
- "name": "regular"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
}
- ]
- },
- {
- "legIndices": [3],
- "products": [
- {
- "id": "orcaFares:farePayment",
+ },
+ {
+ "id": "f5b38df6-10a0-35c1-92d6-679eddfce6ff",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "special",
+ "id": "orca:special"
},
- "category": {
- "id": "orcaFares",
- "name": "youth"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
- "name": "rideCost",
- "amount": {
- "cents": 100,
+ }
+ },
+ {
+ "id": "d74b0553-a50d-3f52-9572-74aae1033a6a",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 1.5,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "special",
+ "id": "orca:special"
},
- "category": {
- "id": "orcaFares",
- "name": "senior"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "5dcb16dc-64f5-3ab3-99b1-29cf0d74c4d5",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "youth"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
- },
- {
- "id": "orcaFares:farePayment",
- "name": "rideCost",
- "amount": {
- "cents": 25,
+ }
+ },
+ {
+ "id": "e38356c5-9559-3a39-a691-4fce5ef857d9",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 2.75,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "special"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
- },
- {
- "id": "orcaFares:farePayment",
- "name": "transfer",
- "amount": {
- "cents": 125,
+ }
+ },
+ {
+ "id": "462c8831-4aa6-3ab8-92d3-e5610bdc9815",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "rideCost",
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "youth",
+ "id": "orca:youth"
},
- "category": {
- "id": "orcaFares",
- "name": "special"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "36578739-9027-38f3-9891-76fcb1d0218e",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "senior"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "f6abeea9-9870-31f8-a19f-2a2b7f7afda5",
+ "product": {
+ "id": "orca:farePayment",
"name": "transfer",
- "amount": {
- "cents": 125,
+ "price": {
+ "amount": 2.75,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "regular",
+ "id": "orca:regular"
},
- "category": {
- "id": "orcaFares",
- "name": "senior"
+ "medium": {
+ "name": "electronic",
+ "id": "orca:electronic"
}
- },
- {
- "id": "orcaFares:farePayment",
+ }
+ },
+ {
+ "id": "3bd1b287-bfcc-3ef8-8736-eeeca140dbff",
+ "product": {
+ "id": "orca:farePayment",
"name": "rideCost",
- "amount": {
- "cents": 0,
+ "price": {
+ "amount": 0,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "electronic"
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
},
- "category": {
- "id": "orcaFares",
- "name": "regular"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
- },
- {
- "id": "orcaFares:farePayment",
- "name": "rideCost",
- "amount": {
- "cents": 0,
+ }
+ },
+ {
+ "id": "5fab13fb-3029-3033-bde8-c2c2e29fa1de",
+ "product": {
+ "id": "orca:farePayment",
+ "name": "transfer",
+ "price": {
+ "amount": 1,
"currency": {
- "currency": "USD",
- "defaultFractionDigits": 2,
- "currencyCode": "USD",
- "symbol": "$"
+ "code": "USD",
+ "digits": 2
}
},
- "container": {
- "id": "orcaFares",
- "name": "cash"
+ "riderCategory": {
+ "name": "senior",
+ "id": "orca:senior"
},
- "category": {
- "id": "orcaFares",
- "name": "regular"
+ "medium": {
+ "name": "cash",
+ "id": "orca:cash"
}
}
- ]
- }
- ]
- },
- "legs": [
- {
- "startTime": 1674505485000,
- "endTime": 1674506100000,
- "departureDelay": 0,
- "arrivalDelay": 0,
- "realTime": false,
- "distance": 802.9,
- "generalizedCost": 1204,
- "pathway": false,
- "mode": "WALK",
- "transitLeg": false,
- "route": "",
- "agencyTimeZoneOffset": -28800000,
- "interlineWithPreviousLeg": false,
- "from": {
- "name": "47.83549, -122.31293",
- "lon": -122.3129313,
- "lat": 47.8354853,
- "departure": 1674505485000,
- "vertexType": "NORMAL"
- },
- "to": {
- "name": "Hwy 99 & 180th St SW",
- "stopId": "CommTrans:1521",
- "stopCode": "1521",
- "lon": -122.303382,
- "lat": 47.834973,
- "arrival": 1674506100000,
- "departure": 1674506100000,
- "vertexType": "TRANSIT"
- },
- "legGeometry": {
- "points": "u{}bHxfpiV?q@@eE?wG?_D?Y@o@?kCBqF?{D@qI?qA?c@?qA?i@?IZWB?d@ZBAPa@b@^EL",
- "length": 23
- },
- "steps": [
- {
- "distance": 717.88,
- "relativeDirection": "DEPART",
- "streetName": "180th Street Southwest",
- "absoluteDirection": "EAST",
- "stayOn": false,
- "area": false,
- "bogusName": false,
- "lon": -122.3129292,
- "lat": 47.8356351,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 62.21,
- "relativeDirection": "RIGHT",
- "streetName": "service road",
- "absoluteDirection": "SOUTHEAST",
- "stayOn": false,
- "area": false,
- "bogusName": true,
- "lon": -122.3033119,
- "lat": 47.8355803,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 22.81,
- "relativeDirection": "RIGHT",
- "streetName": "Highway 99",
- "absoluteDirection": "SOUTHWEST",
- "stayOn": false,
- "area": false,
- "bogusName": false,
- "lon": -122.3031554,
- "lat": 47.8351213,
- "elevation": "",
- "walkingBike": false
- }
- ],
- "rentedBike": false,
- "walkingBike": false,
- "duration": 615
- },
- {
- "startTime": 1674506100000,
- "endTime": 1674507660000,
- "departureDelay": 0,
- "arrivalDelay": 0,
- "realTime": false,
- "distance": 7813.43,
- "generalizedCost": 2160,
- "pathway": false,
- "mode": "BUS",
- "transitLeg": true,
- "route": "Mariner P&R - Aurora Village",
- "agencyName": "Community Transit",
- "agencyUrl": "http://www.communitytransit.org/",
- "agencyTimeZoneOffset": -28800000,
- "routeColor": "0070c0",
- "routeType": 3,
- "routeId": "CommTrans:101",
- "routeTextColor": "ffffff",
- "interlineWithPreviousLeg": false,
- "tripBlockId": "MCOB-DO:123:0:Weekday:1:22SEP:41028:12345",
- "headsign": "Aurora Village",
- "agencyId": "CommTrans:29",
- "tripId": "CommTrans:10499096__MCOB-DO:123:0:Weekday:1:22SEP:41028:12345",
- "serviceDate": "2023-01-23",
- "from": {
- "name": "Hwy 99 & 180th St SW",
- "stopId": "CommTrans:1521",
- "stopCode": "1521",
- "lon": -122.303382,
- "lat": 47.834973,
- "arrival": 1674506100000,
- "departure": 1674506100000,
- "stopIndex": 16,
- "stopSequence": 17,
- "vertexType": "TRANSIT"
- },
- "to": {
- "name": "Aurora Village Transit Center",
- "stopId": "CommTrans:2877",
- "stopCode": "2877",
- "lon": -122.34277,
- "lat": 47.77435,
- "arrival": 1674507660000,
- "departure": 1674507660000,
- "stopIndex": 30,
- "stopSequence": 31,
- "vertexType": "TRANSIT"
- },
- "intermediateStops": [
- {
- "name": "Hwy 99 & 188th St SW",
- "stopId": "CommTrans:1502",
- "stopCode": "1502",
- "lon": -122.309711,
- "lat": 47.827669,
- "arrival": 1674506220000,
- "departure": 1674506220000,
- "stopIndex": 17,
- "stopSequence": 18,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 60th Ave W",
- "stopId": "CommTrans:1495",
- "stopCode": "1495",
- "lon": -122.312185,
- "lat": 47.824801,
- "arrival": 1674506280000,
- "departure": 1674506280000,
- "stopIndex": 18,
- "stopSequence": 19,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 196th St SW",
- "stopId": "CommTrans:1503",
- "stopCode": "1503",
- "lon": -122.31592,
- "lat": 47.820482,
- "arrival": 1674506340000,
- "departure": 1674506340000,
- "stopIndex": 19,
- "stopSequence": 20,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 200th St SW",
- "stopId": "CommTrans:1504",
- "stopCode": "1504",
- "lon": -122.318702,
- "lat": 47.81725,
- "arrival": 1674506340000,
- "departure": 1674506340000,
- "stopIndex": 20,
- "stopSequence": 21,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 202nd St SW",
- "stopId": "CommTrans:1505",
- "stopCode": "1505",
- "lon": -122.320426,
- "lat": 47.815264,
- "arrival": 1674506400000,
- "departure": 1674506400000,
- "stopIndex": 21,
- "stopSequence": 22,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 208th St SW",
- "stopId": "CommTrans:1506",
- "stopCode": "1506",
- "lon": -122.324693,
- "lat": 47.809703,
- "arrival": 1674506520000,
- "departure": 1674506520000,
- "stopIndex": 22,
- "stopSequence": 23,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 212th St SW",
- "stopId": "CommTrans:1507",
- "stopCode": "1507",
- "lon": -122.327809,
- "lat": 47.805379,
- "arrival": 1674506580000,
- "departure": 1674506580000,
- "stopIndex": 23,
- "stopSequence": 24,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 216th St SW",
- "stopId": "CommTrans:1508",
- "stopCode": "1508",
- "lon": -122.329866,
- "lat": 47.802523,
- "arrival": 1674506640000,
- "departure": 1674506640000,
- "stopIndex": 24,
- "stopSequence": 25,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 220th St SW",
- "stopId": "CommTrans:1509",
- "stopCode": "1509",
- "lon": -122.332468,
- "lat": 47.798918,
- "arrival": 1674506700000,
- "departure": 1674506700000,
- "stopIndex": 25,
- "stopSequence": 26,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 224th St SW",
- "stopId": "CommTrans:1510",
- "stopCode": "1510",
- "lon": -122.334893,
- "lat": 47.795549,
- "arrival": 1674506820000,
- "departure": 1674506820000,
- "stopIndex": 26,
- "stopSequence": 27,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 230th St SW",
- "stopId": "CommTrans:1511",
- "stopCode": "1511",
- "lon": -122.338743,
- "lat": 47.790207,
- "arrival": 1674507000000,
- "departure": 1674507000000,
- "stopIndex": 27,
- "stopSequence": 28,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & 238th St SW",
- "stopId": "CommTrans:1517",
- "stopCode": "1517",
- "lon": -122.343432,
- "lat": 47.783697,
- "arrival": 1674507240000,
- "departure": 1674507240000,
- "stopIndex": 28,
- "stopSequence": 29,
- "vertexType": "TRANSIT"
- },
- {
- "name": "Hwy 99 & N 205th St",
- "stopId": "CommTrans:2089",
- "stopCode": "2089",
- "lon": -122.346379,
- "lat": 47.777264,
- "arrival": 1674507420000,
- "departure": 1674507420000,
- "stopIndex": 29,
- "stopSequence": 30,
- "vertexType": "TRANSIT"
}
],
- "legGeometry": {
- "points": "ew}bHfjniVj@d@JHLJPNTRb@^h@b@XThA~@n@h@j@d@l@f@VTd@^v@n@RPZVNLVRZVLJrAfAd@`@z@r@^Zf@`@LJbAz@dCrBj@d@ZV~ApA`BtATP\\X??NLNJ^ZPNb@\\HFRPl@f@NL`@^NLn@f@`@\\RPf@`@jA`Ad@^t@l@@@??t@n@JHRP^ZZVHFNLNLb@\\PNJHTRPLd@^TPd@^VRPNVTXTd@^n@h@^Zb@^fA|@bAz@pAdABB??HFh@b@dA|@TPHFXT|@t@ZVPNl@f@f@`@LJ`@Zl@h@n@h@t@l@\\Vn@h@??|@t@b@^f@`@VTNLNJnAbArAhATRNL??LJHF\\XLJf@`@LJTRzAnAf@^RNNJNJ`@ZLHh@`@JHRLf@\\LHdAp@PLf@Zr@d@PJv@f@HFb@XXRrAz@`@Vv@f@\\TXRLHLHLHFD??B@n@b@LHJHb@Xp@b@NJfAp@HF\\TRLl@^\\TTN\\Td@XXPv@f@xA`Aj@^PJn@b@p@b@TN`@V??zA`ADBLHb@ZTNHFl@^HFVNXP\\T^Th@\\\\TfAt@x@h@HFHF??VNNHTNZRj@^d@ZjAv@bAn@bAn@FDxBvAt@d@bAn@JFNJNHh@^??l@^hAt@t@d@NJ^T`@VTNXPn@b@r@f@LHHF|@j@ZTv@f@vA|@JF??BBVNZRd@Zb@XTNXPf@\\TLb@XdAr@zA`ARLj@`@PLh@\\VNf@\\hAt@RLTLTNTNLHLHRLPLTNdAp@h@\\pAx@??FDb@XLHZR~@l@^V`BdANJd@Z`An@VNZR^V\\TZRjBjAfAr@d@Z`GxD^VXPNJh@Zt@d@z@j@b@XJHDB??fAr@`Al@VPTNZRTNPLHFJHNJNLPLLJ\\VLJt@l@b@\\RP`@Z\\VXRXPd@VPJTJRHPFZJVHXHVFPDRD`@FXB\\@X@^?T?hAEz@GdAILATA??f@Cb@ANAjACr@Cr@AX?R?P?VAX?l@?T?f@?T?|AAH?BqBAg@?{A?m@?c@?iB?u@?yCe@??K",
- "length": 368
+ "agency": {
+ "alerts": [],
+ "id": "QWdlbmN5OmtjbTox",
+ "name": "Metro Transit",
+ "timezone": "America/Los_Angeles",
+ "url": "https://kingcounty.gov/en/dept/metro"
},
- "steps": [],
- "routeLongName": "Mariner P&R - Aurora Village",
- "duration": 1560
- },
- {
- "startTime": 1674507660000,
- "endTime": 1674507707000,
- "departureDelay": 0,
"arrivalDelay": 0,
- "realTime": false,
- "distance": 38.11,
- "generalizedCost": 67,
- "pathway": false,
- "mode": "WALK",
- "transitLeg": false,
- "route": "",
- "agencyTimeZoneOffset": -28800000,
- "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "distance": 4454.61,
+ "dropoffType": "SCHEDULED",
+ "duration": 967,
+ "endTime": 1686625941000,
"from": {
- "name": "Aurora Village Transit Center",
- "stopId": "CommTrans:2877",
- "stopCode": "2877",
- "lon": -122.34277,
- "lat": 47.77435,
- "arrival": 1674507660000,
- "departure": 1674507660000,
- "vertexType": "TRANSIT"
- },
- "to": {
- "name": "Aurora Village Transit Center - Bay 10",
- "stopId": "kcm:16100",
- "stopCode": "16100",
- "lon": -122.342407,
- "lat": 47.7742424,
- "arrival": 1674507707000,
- "departure": 1674508020000,
- "zoneId": "18",
+ "lat": 47.6904907,
+ "lon": -122.344734,
+ "name": "N 85th St & Aurora Ave N",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "5370",
+ "gtfsId": "kcm:5370",
+ "id": "U3RvcDprY206NTM3MA"
+ },
"vertexType": "TRANSIT"
},
- "legGeometry": {
- "points": "u|qbHhaviV??R??]@C?A@C?C?ACI?O@?",
- "length": 12
- },
- "steps": [
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": [
{
- "distance": 10.22,
- "relativeDirection": "DEPART",
- "streetName": "service road",
- "absoluteDirection": "SOUTH",
- "stayOn": false,
- "area": false,
- "bogusName": true,
- "lon": -122.3427609,
- "lat": 47.77435,
- "elevation": "",
- "walkingBike": false
+ "lat": 47.6904716,
+ "locationType": "STOP",
+ "lon": -122.341507,
+ "name": "N 85th St & Stone Ave N",
+ "stopCode": "5380",
+ "stopId": "U3RvcDprY206NTM4MA"
},
{
- "distance": 11.17,
- "relativeDirection": "LEFT",
- "streetName": "path",
- "absoluteDirection": "EAST",
- "stayOn": true,
- "area": false,
- "bogusName": true,
- "lon": -122.3427605,
- "lat": 47.7742581,
- "elevation": "",
- "walkingBike": false
+ "lat": 47.6904449,
+ "locationType": "STOP",
+ "lon": -122.336601,
+ "name": "N 85th St & Wallingford Ave N",
+ "stopCode": "5400",
+ "stopId": "U3RvcDprY206NTQwMA"
},
{
- "distance": 16.73,
- "relativeDirection": "RIGHT",
- "streetName": "open area",
- "absoluteDirection": "SOUTHEAST",
- "stayOn": true,
- "area": true,
- "bogusName": true,
- "lon": -122.3426111,
- "lat": 47.7742565,
- "elevation": "",
- "walkingBike": false
- }
- ],
- "rentedBike": false,
- "walkingBike": false,
- "duration": 47
- },
- {
- "startTime": 1674508020000,
- "endTime": 1674508800000,
- "departureDelay": 0,
- "arrivalDelay": 0,
- "realTime": false,
- "distance": 4792.95,
- "generalizedCost": 1693,
- "pathway": false,
- "mode": "BUS",
- "transitLeg": true,
- "agencyName": "Metro Transit",
- "agencyUrl": "http://metro.kingcounty.gov",
- "agencyTimeZoneOffset": -28800000,
- "routeType": 3,
- "routeId": "kcm:102615",
- "interlineWithPreviousLeg": false,
- "tripShortName": "LOCAL",
- "tripBlockId": "6649263",
- "headsign": "Downtown Seattle",
- "agencyId": "kcm:1",
- "tripId": "kcm:585679422",
- "serviceDate": "2023-01-23",
- "from": {
- "name": "Aurora Village Transit Center - Bay 10",
- "stopId": "kcm:16100",
- "stopCode": "16100",
- "lon": -122.342407,
- "lat": 47.7742424,
- "arrival": 1674507707000,
- "departure": 1674508020000,
- "zoneId": "18",
- "stopIndex": 0,
- "stopSequence": 1,
- "vertexType": "TRANSIT"
- },
- "to": {
- "name": "Aurora Ave N & N 145th St",
- "stopId": "kcm:6950",
- "stopCode": "6950",
- "lon": -122.345268,
- "lat": 47.7335701,
- "arrival": 1674508800000,
- "departure": 1674508800000,
- "zoneId": "20",
- "stopIndex": 11,
- "stopSequence": 84,
- "vertexType": "TRANSIT"
- },
- "intermediateStops": [
+ "lat": 47.68853,
+ "locationType": "STOP",
+ "lon": -122.33651,
+ "name": "Wallingford Ave N & N 82nd St",
+ "stopCode": "17070",
+ "stopId": "U3RvcDprY206MTcwNzA"
+ },
{
- "name": "Aurora Ave N & N 200th St",
- "stopId": "kcm:75700",
- "stopCode": "75700",
- "lon": -122.346207,
- "lat": 47.7737427,
- "arrival": 1674508073000,
- "departure": 1674508073000,
- "zoneId": "18",
- "stopIndex": 1,
- "stopSequence": 6,
- "vertexType": "TRANSIT"
+ "lat": 47.6870499,
+ "locationType": "STOP",
+ "lon": -122.336494,
+ "name": "Wallingford Ave N & N 80th St",
+ "stopCode": "17080",
+ "stopId": "U3RvcDprY206MTcwODA"
},
{
- "name": "Aurora Ave N & N 192nd St",
- "stopId": "kcm:75730",
- "stopCode": "75730",
- "lon": -122.3461,
- "lat": 47.7672043,
- "arrival": 1674508191000,
- "departure": 1674508191000,
- "zoneId": "18",
- "stopIndex": 2,
- "stopSequence": 13,
- "vertexType": "TRANSIT"
+ "lat": 47.6857948,
+ "locationType": "STOP",
+ "lon": -122.336861,
+ "name": "Wallingford Ave N & East Green Lake Dr N",
+ "stopCode": "17081",
+ "stopId": "U3RvcDprY206MTcwODE"
},
{
- "name": "Aurora Ave N & N 185th St",
- "stopId": "kcm:75740",
- "stopCode": "75740",
- "lon": -122.346184,
- "lat": 47.7628479,
- "arrival": 1674508270000,
- "departure": 1674508270000,
- "zoneId": "18",
- "stopIndex": 3,
- "stopSequence": 22,
- "vertexType": "TRANSIT"
+ "lat": 47.682682,
+ "locationType": "STOP",
+ "lon": -122.332932,
+ "name": "East Green Lake Dr N & Meridian Ave N",
+ "stopCode": "35691",
+ "stopId": "U3RvcDprY206MzU2OTE"
},
{
- "name": "Aurora Ave N & N 180th St",
- "stopId": "kcm:75750",
- "stopCode": "75750",
- "lon": -122.346237,
- "lat": 47.7594452,
- "arrival": 1674508331000,
- "departure": 1674508331000,
- "zoneId": "18",
- "stopIndex": 4,
- "stopSequence": 32,
- "vertexType": "TRANSIT"
+ "lat": 47.6812057,
+ "locationType": "STOP",
+ "lon": -122.326973,
+ "name": "East Green Lake Dr N & 4th Ave NE",
+ "stopCode": "35721",
+ "stopId": "U3RvcDprY206MzU3MjE"
},
{
- "name": "Aurora Ave N & N 175th St",
- "stopId": "kcm:75760",
- "stopCode": "75760",
- "lon": -122.345856,
- "lat": 47.7552986,
- "arrival": 1674508407000,
- "departure": 1674508407000,
- "zoneId": "18",
- "stopIndex": 5,
- "stopSequence": 40,
- "vertexType": "TRANSIT"
+ "lat": 47.6784592,
+ "locationType": "STOP",
+ "lon": -122.32486,
+ "name": "NE Ravenna Blvd & Woodlawn Ave NE",
+ "stopCode": "16390",
+ "stopId": "U3RvcDprY206MTYzOTA"
},
{
- "name": "Aurora Ave N & N 170th St",
- "stopId": "kcm:75770",
- "stopCode": "75770",
- "lon": -122.345741,
- "lat": 47.7520294,
- "arrival": 1674508466000,
- "departure": 1674508466000,
- "zoneId": "18",
- "stopIndex": 6,
- "stopSequence": 48,
- "vertexType": "TRANSIT"
+ "lat": 47.6773109,
+ "locationType": "STOP",
+ "lon": -122.323807,
+ "name": "NE Ravenna Blvd & NE 68th St",
+ "stopCode": "16400",
+ "stopId": "U3RvcDprY206MTY0MDA"
},
{
- "name": "Aurora Ave N & N 165th St",
- "stopId": "kcm:75780",
- "stopCode": "75780",
- "lon": -122.345688,
- "lat": 47.7483559,
- "arrival": 1674508532000,
- "departure": 1674508532000,
- "zoneId": "18",
- "stopIndex": 7,
- "stopSequence": 56,
- "vertexType": "TRANSIT"
+ "lat": 47.6757851,
+ "locationType": "STOP",
+ "lon": -122.320129,
+ "name": "NE 65th St & 8th Ave NE",
+ "stopCode": "16416",
+ "stopId": "U3RvcDprY206MTY0MTY"
},
{
- "name": "Aurora Ave N & N 160th St",
- "stopId": "kcm:75790",
- "stopCode": "75790",
- "lon": -122.34565,
- "lat": 47.7446899,
- "arrival": 1674508599000,
- "departure": 1674508599000,
- "zoneId": "18",
- "stopIndex": 8,
- "stopSequence": 66,
- "vertexType": "TRANSIT"
+ "lat": 47.6757507,
+ "locationType": "STOP",
+ "lon": -122.316673,
+ "name": "Roosevelt Station - Bay 1",
+ "stopCode": "16430",
+ "stopId": "U3RvcDprY206MTY0MzA"
},
{
- "name": "Aurora Ave N & N 155th St",
- "stopId": "kcm:75800",
- "stopCode": "75800",
- "lon": -122.345497,
- "lat": 47.7408867,
- "arrival": 1674508668000,
- "departure": 1674508668000,
- "zoneId": "18",
- "stopIndex": 9,
- "stopSequence": 73,
- "vertexType": "TRANSIT"
+ "lat": 47.6757202,
+ "locationType": "STOP",
+ "lon": -122.312683,
+ "name": "NE 65th St & 14th Ave NE",
+ "stopCode": "37359",
+ "stopId": "U3RvcDprY206MzczNTk"
},
{
- "name": "Aurora Ave N & N 152nd St",
- "stopId": "kcm:75810",
- "stopCode": "75810",
- "lon": -122.345428,
- "lat": 47.7386818,
- "arrival": 1674508707000,
- "departure": 1674508707000,
- "zoneId": "18",
- "stopIndex": 10,
- "stopSequence": 75,
- "vertexType": "TRANSIT"
+ "lat": 47.6706772,
+ "locationType": "STOP",
+ "lon": -122.313133,
+ "name": "University Way NE & NE Ravenna Blvd",
+ "stopCode": "38022",
+ "stopId": "U3RvcDprY206MzgwMjI"
}
],
"legGeometry": {
- "points": "q{qbH`_viV?`AB~R?b@\\Cr@???dB?rBE~@A`HA|IElGArBC??f@?`DBpA?fB?pA@r@BhDBdB@lA???~@BbDDr@?|BBxAB|@?v@?PCb@?HA??v@AtAIjBInBGn@AvAGhES~BM??TA`AE`AA`B?hACbDCfB?|@C??v@AxDCnAAhA?bB?pA?xAA`AA??l@?n@?r@?|@CxAAj@?pB@vA?vB?dAA??|CEv@?fAE`CIhA?~CEnBC??~JIt@A??vBEz@AZCrAApAEfBEvFCvDCrBA",
- "length": 94
+ "length": 151,
+ "points": "_qabHnmviV@YAY@iE@sE?mB?aA???g@BcD?s@?yE@uE?U?_@?}B?oB???_@hJE`@???jCA|C???F?H@FDHDJJRXRL`DD??b@?Ny@To@HQHQV]Z]LMzB}BfCeCRWVUVWV[R]R]Nc@Re@Ja@Ha@??BCFe@Ho@Fs@@e@@i@Ae@Ae@Ca@Ce@Cq@C]?WAW@m@B_@Bg@Bi@Ha@Fe@Je@Jg@L_@JUL[Na@R[Tg@t@sAjAwB??Xi@Ta@NUNUPSLKPQTOROTMNENGRETAV?XQTIVOhCeBp@m@??bEmDb@c@??dHsG@}A?mB@wB?s@?_A???K?gCBgC?gC?cC@iC??@iE@uC?oB?{B@wB@yA???]?{A?[rB?tACrA?hA?vJAx@hA~A|BNNHDNDZ?|@???vFCpD?D?h@?"
},
+ "mode": "BUS",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": null,
+ "route": {
+ "alerts": [],
+ "color": null,
+ "id": "Um91dGU6a2NtOjEwMDIyNQ",
+ "longName": null,
+ "shortName": "45",
+ "textColor": null,
+ "type": 3
+ },
+ "startTime": 1686624974000,
"steps": [],
- "routeShortName": "E Line",
- "duration": 780
+ "to": {
+ "lat": 47.6683159,
+ "lon": -122.313126,
+ "name": "University Way NE & NE 55th St",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "38024",
+ "gtfsId": "kcm:38024",
+ "id": "U3RvcDprY206MzgwMjQ"
+ },
+ "vertexType": "TRANSIT"
+ },
+ "transitLeg": true,
+ "trip": {
+ "gtfsId": "kcm:583679024",
+ "id": "VHJpcDprY206NTgzNjc5MDI0",
+ "tripHeadsign": "University District Roosevelt Station"
+ }
},
{
- "startTime": 1674508800000,
- "endTime": 1674509068000,
- "departureDelay": 0,
+ "accessibilityScore": null,
+ "fareProducts": [],
+ "agency": null,
"arrivalDelay": 0,
- "realTime": false,
- "distance": 316.32,
- "generalizedCost": 490,
- "pathway": false,
- "mode": "WALK",
- "transitLeg": false,
- "route": "",
- "agencyTimeZoneOffset": -28800000,
- "interlineWithPreviousLeg": false,
+ "departureDelay": 0,
+ "distance": 492.88,
+ "dropoffType": "SCHEDULED",
+ "duration": 405,
+ "endTime": 1686626346000,
"from": {
- "name": "Aurora Ave N & N 145th St",
- "stopId": "kcm:6950",
- "stopCode": "6950",
- "lon": -122.345268,
- "lat": 47.7335701,
- "arrival": 1674508800000,
- "departure": 1674508800000,
- "zoneId": "20",
+ "lat": 47.6683159,
+ "lon": -122.313126,
+ "name": "University Way NE & NE 55th St",
+ "rentalVehicle": null,
+ "stop": {
+ "alerts": [],
+ "code": "38024",
+ "gtfsId": "kcm:38024",
+ "id": "U3RvcDprY206MzgwMjQ"
+ },
"vertexType": "TRANSIT"
},
- "to": {
- "name": "1318 North 145th Street, Seattle, WA, USA",
- "lon": -122.3420287,
- "lat": 47.7342206,
- "arrival": 1674509068000,
- "vertexType": "NORMAL"
- },
+ "interlineWithPreviousLeg": false,
+ "intermediateStops": null,
"legGeometry": {
- "points": "y}ibH|pviV@Be@?I??Q]?O?Q??c@?W?qB?aA?kA@e@AiEQ??Q@K?o@",
- "length": 19
+ "length": 41,
+ "points": "}e}aH`hpiV?F[??C?AAQ?Y@CAC@qA@{A?C?O?OAE?_B?}A?G?I?G?G?aB?aB?E?G?e@?G?I?eB?{A?G?K?G?G?cBA{A?E?K?I?E?u@"
},
+ "mode": "WALK",
+ "pickupBookingInfo": null,
+ "pickupType": "SCHEDULED",
+ "realTime": false,
+ "realtimeState": null,
+ "rentedBike": false,
+ "route": null,
+ "startTime": 1686625941000,
"steps": [
{
- "distance": 26.16,
- "relativeDirection": "DEPART",
- "streetName": "sidewalk",
- "absoluteDirection": "NORTH",
- "stayOn": false,
- "area": false,
- "bogusName": true,
- "lon": -122.3452855,
- "lat": 47.73357,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 7.06,
- "relativeDirection": "RIGHT",
- "streetName": "service road",
- "absoluteDirection": "EAST",
- "stayOn": true,
- "area": false,
- "bogusName": true,
- "lon": -122.3452885,
- "lat": 47.7338053,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 35.59,
- "relativeDirection": "LEFT",
- "streetName": "Aurora Avenue North",
- "absoluteDirection": "NORTH",
- "stayOn": false,
- "area": false,
- "bogusName": false,
- "lon": -122.3451941,
- "lat": 47.7338066,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 207.88,
- "relativeDirection": "RIGHT",
- "streetName": "North 145th Street",
- "absoluteDirection": "EAST",
- "stayOn": false,
- "area": false,
- "bogusName": false,
- "lon": -122.3451971,
- "lat": 47.7341266,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 10.3,
- "relativeDirection": "LEFT",
- "streetName": "Stone Avenue North",
"absoluteDirection": "NORTH",
- "stayOn": false,
+ "alerts": [],
"area": false,
- "bogusName": false,
- "lon": -122.3424174,
- "lat": 47.7341208,
- "elevation": "",
- "walkingBike": false
- },
- {
- "distance": 29.32,
- "relativeDirection": "RIGHT",
- "streetName": "path",
- "absoluteDirection": "EAST",
+ "distance": 492.91,
+ "elevationProfile": [],
+ "lat": 47.6683162,
+ "lon": -122.3131688,
+ "relativeDirection": "DEPART",
"stayOn": false,
- "area": false,
- "bogusName": true,
- "lon": -122.3424186,
- "lat": 47.7342134,
- "elevation": "",
- "walkingBike": false
+ "streetName": "sidewalk"
}
],
- "rentedBike": false,
- "walkingBike": false,
- "duration": 268
+ "to": {
+ "lat": 47.6683393,
+ "lon": -122.3067976,
+ "name": "47.66834, -122.3068",
+ "rentalVehicle": null,
+ "stop": null,
+ "vertexType": "NORMAL"
+ },
+ "transitLeg": false,
+ "trip": null
}
],
- "tooSloped": false,
- "arrivedAtDestinationWithRentedBicycle": false
+ "startTime": 1686620636000,
+ "waitingTime": 481,
+ "walkTime": 1862
}
diff --git a/packages/itinerary-body/src/stories/OtpRrItineraryBody.story.tsx b/packages/itinerary-body/src/stories/OtpRrItineraryBody.story.tsx
index 7f545819f..020e731e1 100644
--- a/packages/itinerary-body/src/stories/OtpRrItineraryBody.story.tsx
+++ b/packages/itinerary-body/src/stories/OtpRrItineraryBody.story.tsx
@@ -1,4 +1,4 @@
-import { Itinerary } from "@opentripplanner/types";
+import { FareProductSelector, Itinerary } from "@opentripplanner/types";
import React, { ReactElement } from "react";
import ItineraryBody from "..";
@@ -22,7 +22,7 @@ const bikeRentalTransitBikeRentalItinerary = require("../__mocks__/itineraries/b
const bikeTransitBikeItinerary = require("../__mocks__/itineraries/bike-transit-bike.json");
const eScooterRentalItinerary = require("../__mocks__/itineraries/e-scooter-rental.json");
const eScooterRentalTransiteScooterRentalItinerary = require("../__mocks__/itineraries/e-scooter-transit-e-scooter.json");
-const fareComponentsItinerary = require("../__mocks__/itineraries/fare-components.json");
+const fareProductsItinerary = require("../__mocks__/itineraries/leg-fare-products.json");
const parkAndRideItinerary = require("../__mocks__/itineraries/park-and-ride.json");
const tncTransitTncItinerary = require("../__mocks__/itineraries/tnc-transit-tnc.json");
const walkInterlinedTransitItinerary = require("../__mocks__/itineraries/walk-interlined-transit-walk.json");
@@ -46,21 +46,22 @@ if (!isRunningJest()) {
}
interface StoryWrapperProps {
+ alwaysCollapseAlerts?: boolean;
+ defaultFareSelector?: FareProductSelector;
itinerary: Itinerary;
- showRouteFares: boolean;
- TimeColumnContent: FunctionComponent;
- alwaysCollapseAlerts: boolean;
+ TimeColumnContent?: FunctionComponent;
}
function OtpRRItineraryBodyWrapper({
+ alwaysCollapseAlerts,
+ defaultFareSelector,
itinerary,
- showRouteFares,
- TimeColumnContent,
- alwaysCollapseAlerts
+ TimeColumnContent
}: StoryWrapperProps): ReactElement {
return (
(
export const IndividualLegFareComponents = (): ReactElement => (
);
diff --git a/packages/itinerary-body/src/stories/OtpUiItineraryBody.story.tsx b/packages/itinerary-body/src/stories/OtpUiItineraryBody.story.tsx
index b7350d5b1..a4eaba97c 100644
--- a/packages/itinerary-body/src/stories/OtpUiItineraryBody.story.tsx
+++ b/packages/itinerary-body/src/stories/OtpUiItineraryBody.story.tsx
@@ -24,7 +24,7 @@ const walkInterlinedTransitItinerary = require("../__mocks__/itineraries/walk-in
const walkOnlyItinerary = require("../__mocks__/itineraries/walk-only.json");
const walkTransitWalkItinerary = require("../__mocks__/itineraries/walk-transit-walk.json");
const walkTransitWalkTransitWalkItinerary = require("../__mocks__/itineraries/walk-transit-walk-transit-walk.json");
-const fareComponentsItinerary = require("../__mocks__/itineraries/fare-components.json");
+const fareProductsItinerary = require("../__mocks__/itineraries/leg-fare-products.json");
const walkTransitWalkTransitWalkA11yItinerary = require("../__mocks__/itineraries/walk-transit-walk-transit-walk-with-accessibility-scores.json");
const otp2ScooterItinerary = require("../__mocks__/itineraries/otp2-scooter.json");
const flexItinerary = require("../__mocks__/itineraries/flex-itinerary.json");
@@ -147,10 +147,7 @@ export const OTP2FlexItinerary = (): ReactElement => (
);
export const IndividualLegFareComponents = (): ReactElement => (
-
+
);
export const CustomAlertIconsItinerary = (): ReactElement => (
diff --git a/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.tsx b/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.tsx
index dbc559ed4..92aabdbac 100644
--- a/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.tsx
+++ b/packages/itinerary-body/src/stories/itinerary-body-defaults-wrapper.tsx
@@ -42,6 +42,7 @@ export default class ItineraryBodyDefaultsWrapper extends Component<
render(): ReactElement {
const {
alwaysCollapseAlerts,
+ defaultFareSelector,
itinerary,
LegIcon = TriMetLegIcon,
LineColumnContent,
@@ -50,7 +51,6 @@ export default class ItineraryBodyDefaultsWrapper extends Component<
showAgencyInfo,
showLegIcon,
showMapButtonColumn = true,
- showRouteFares,
showViewTripButton,
styledItinerary,
TimeColumnContent,
@@ -78,6 +78,7 @@ export default class ItineraryBodyDefaultsWrapper extends Component<
AlertToggleIcon={AlertToggleIcon}
alwaysCollapseAlerts={alwaysCollapseAlerts}
config={config}
+ defaultFareSelector={defaultFareSelector}
diagramVisible={diagramVisible}
frameLeg={action("frameLeg")}
itinerary={itinerary}
@@ -94,7 +95,6 @@ export default class ItineraryBodyDefaultsWrapper extends Component<
showElevationProfile
showLegIcon={showLegIcon}
showMapButtonColumn={showMapButtonColumn}
- showRouteFares={showRouteFares}
showViewTripButton={showViewTripButton}
TimeColumnContent={TimeColumnContent}
toRouteAbbreviation={toRouteAbbreviation}
diff --git a/packages/itinerary-body/src/types.ts b/packages/itinerary-body/src/types.ts
index 681b31a0d..208ef1496 100644
--- a/packages/itinerary-body/src/types.ts
+++ b/packages/itinerary-body/src/types.ts
@@ -2,7 +2,7 @@ import { FunctionComponent } from "react";
import {
Config,
- Fare,
+ FareProductSelector,
GradationMap,
Itinerary,
Leg,
@@ -109,6 +109,12 @@ interface ItineraryBodySharedProps {
className?: string;
/** Contains OTP configuration details. */
config: Config;
+ /**
+ * Allows selection of a fare product type for display in the itinerary body.
+ * When fare leg information is available, it will be shown per-leg.
+ * Example: regular, cash or regular, electronic.
+ */
+ defaultFareSelector?: FareProductSelector;
/**
* Should be either null or a legType. Indicates that a particular leg diagram
* has been selected and is active.
@@ -184,8 +190,6 @@ interface ItineraryBodySharedProps {
showLegIcon?: boolean;
/** If true, will show the right column with the map button */
showMapButtonColumn?: boolean;
- /** If true, will show fare information in transit leg bodies */
- showRouteFares?: boolean;
/** If true, shows the view trip button in transit leg bodies */
showViewTripButton?: boolean;
/**
@@ -216,8 +220,6 @@ interface ItineraryBodySharedProps {
export interface PlaceRowProps
extends ItineraryBodySharedProps,
LegSharedProps {
- /** The fare information to be displayed for the corresponding leg. */
- fare: Fare;
/** Indicates whether this leg directly follows a transit leg */
followsTransit?: boolean;
}
diff --git a/packages/trip-details/__mocks__/custom-english-messages.yml b/packages/trip-details/__mocks__/custom-english-messages.yml
index 8cbbf1040..afa5fe070 100644
--- a/packages/trip-details/__mocks__/custom-english-messages.yml
+++ b/packages/trip-details/__mocks__/custom-english-messages.yml
@@ -1,14 +1,14 @@
otpUi:
TripDetails:
- departure: >-
- Leave at {departureDate, time, short} on
- {departureDate, date, long}
- fareDetailsHeaders:
+ FareTable:
cash: Cash
electronic: ORCA
regular: Regular
senior: Senior
special: LIFT
youth: Youth
+ departure: >-
+ Leave at {departureDate, time, short} on
+ {departureDate, date, long}
title: Custom Trip Details Title
tncFare: Pay {minTNCFare}-{maxTNCFare} to {companies}
diff --git a/packages/trip-details/__mocks__/custom-french-messages.yml b/packages/trip-details/__mocks__/custom-french-messages.yml
index fae693f24..6be58a368 100644
--- a/packages/trip-details/__mocks__/custom-french-messages.yml
+++ b/packages/trip-details/__mocks__/custom-french-messages.yml
@@ -1,10 +1,9 @@
-# Example used to overwrite a subset of the messages from /i18n/en-US.yml.
otpUi:
TripDetails:
- fareDetailsHeaders:
- regular: "Ordinaire"
- youth: "Jeune"
- senior: "Senior"
- cash: "Espèces"
- electronic: "ORCA"
- special: "LIFT"
+ FareTable:
+ cash: Espèces
+ electronic: ORCA
+ regular: Plein tarif
+ senior: Senior
+ special: LIFT
+ youth: Jeune
diff --git a/packages/trip-details/i18n/en-US.yml b/packages/trip-details/i18n/en-US.yml
index c4b11ef5c..aaf2a54f9 100644
--- a/packages/trip-details/i18n/en-US.yml
+++ b/packages/trip-details/i18n/en-US.yml
@@ -1,5 +1,12 @@
otpUi:
TripDetails:
+ FareTable:
+ cash: Cash
+ electronic: Electronic
+ regular: Adult
+ senior: Senior
+ special: Special
+ youth: Youth
co2: "CO₂ Emitted: {co2} "
co2description: >-
CO₂ intensity is calculated by multiplying the distance of each leg of a
@@ -8,13 +15,6 @@ otpUi:
departure: >-
Depart {departureDate, date, long} at
{departureDate, time, short}
- fareDetailsHeaders:
- cash: Cash
- electronic: Electronic
- regular: Adult
- senior: Senior
- special: Special
- youth: Youth
hideDetail: Hide details
minutesActive: >-
Time Spent Active: {minutes, plural, one {# minute} other {#
@@ -29,5 +29,4 @@ otpUi:
transferDiscountExplanation: Transfer discount of {transferAmount} is applied
transitFare: Transit Fare
transitFareEntry: "{name}: {value} "
- transitFareUnknown: No fare info
tripIncludesFlex: This trip includes flexible routes. {extraMessage}
diff --git a/packages/trip-details/i18n/es.yml b/packages/trip-details/i18n/es.yml
index d2cf21155..b7247783e 100644
--- a/packages/trip-details/i18n/es.yml
+++ b/packages/trip-details/i18n/es.yml
@@ -1,5 +1,12 @@
otpUi:
TripDetails:
+ FareTable:
+ cash: Efectivo
+ electronic: Electrónico
+ regular: Adulto
+ senior: Major edad
+ special: Especial
+ youth: Juventud
co2: "CO₂ emitido: {co2} "
co2description: >-
La intensidad de CO₂ se calcula multiplicando la distancia de cada tramo
@@ -8,24 +15,18 @@ otpUi:
departure: >-
Salida {departureDate, date, long} a las
{departureDate, time, short}
- fareDetailsHeaders:
- cash: Efectivo
- electronic: Electrónico
- regular: Adulto
- senior: Major edad
- special: Especial
- youth: Juventud
hideDetail: Ocultar detalles
+ minutesActive: >-
+ Tiempo de actividad: {minutes, plural, one {# minuto} other {#
+ minutos}}
showDetail: Mostrar detalles
+ timeActiveDescription: >
+ Al hacer este viaje, gastarás {walkMinutes, plural, one {# minuto}
+ other {# minutos}} caminando y {bikeMinutes, plural, one
+ {# minuto} other {# minutos}} en bicicleta.
title: Detalles del viaje
tncFare: "{companies} Tarifa: {minTNCFare} - {maxTNCFare} "
transferDiscountExplanation: Se aplica un descuento de transferencia de {transferAmount}
transitFare: Tarifa de tránsito
transitFareEntry: "{name} : {value} "
- transitFareUnknown: No hay información sobre las tarifas
tripIncludesFlex: Este viaje incluye rutas flexibles. {extraMessage}
- minutesActive: 'Tiempo de actividad: {minutes, plural, one {# minuto}
- other {# minutos}} '
- timeActiveDescription: "Al hacer este viaje, gastarás {walkMinutes, plural,
- one {# minuto} other {# minutos}} caminando y {bikeMinutes,
- plural, one {# minuto} other {# minutos}} en bicicleta.\n"
diff --git a/packages/trip-details/i18n/fr.yml b/packages/trip-details/i18n/fr.yml
index 82d2520b0..d26732fef 100644
--- a/packages/trip-details/i18n/fr.yml
+++ b/packages/trip-details/i18n/fr.yml
@@ -1,5 +1,12 @@
otpUi:
TripDetails:
+ FareTable:
+ cash: Espèces
+ electronic: Électronique
+ regular: Plein tarif
+ senior: Séniors
+ special: Spécial
+ youth: Jeunes
co2: "CO₂ émis : {co2} "
co2description: >-
L'intensité carbone est calculée en multipliant la distance de chaque
@@ -8,16 +15,8 @@ otpUi:
departure: >-
Départ le {departureDate, date, long} à
{departureDate, time, short}
- fareDetailsHeaders:
- cash: Espèces
- electronic: Électronique
- regular: Normal
- senior: Séniors
- special: Spécial
- youth: Jeunes
hideDetail: Masquer les détails
- minutesActive: "Activité physique : {minutes, plural, one {# minute} other
- {# minutes}} "
+ minutesActive: "Activité physique\_: {minutes, plural, one {# minute} other {# minutes}} "
showDetail: Afficher les détails
timeActiveDescription: >
Sur ce trajet, vous effectuerez {walkMinutes, plural, one {#
@@ -25,9 +24,7 @@ otpUi:
plural, one {# minute} other {# minutes}} à vélo.
title: Détails du trajet
tncFare: "Tarif {companies} : {minTNCFare} - {maxTNCFare} "
- transferDiscountExplanation: Cette correspondance vous donne une réduction de
- {transferAmount}
+ transferDiscountExplanation: Cette correspondance vous donne une réduction de {transferAmount}
transitFare: Tarif en transports
transitFareEntry: "{name} : {value} "
- transitFareUnknown: Tarif inconnu
tripIncludesFlex: Ce trajet comprend un service à la demande (Flex). {extraMessage}
diff --git a/packages/trip-details/i18n/i18n-exceptions.json b/packages/trip-details/i18n/i18n-exceptions.json
new file mode 100644
index 000000000..755ff7466
--- /dev/null
+++ b/packages/trip-details/i18n/i18n-exceptions.json
@@ -0,0 +1,12 @@
+{
+ "groups": {
+ "otpUi.TripDetails.FareTable.*": [
+ "cash",
+ "electronic",
+ "regular",
+ "special",
+ "senior",
+ "youth"
+ ]
+ }
+}
diff --git a/packages/trip-details/i18n/ko.yml b/packages/trip-details/i18n/ko.yml
index 25ca80bcc..407a29a24 100644
--- a/packages/trip-details/i18n/ko.yml
+++ b/packages/trip-details/i18n/ko.yml
@@ -1,5 +1,12 @@
otpUi:
TripDetails:
+ FareTable:
+ cash: 현금
+ electronic: 전자
+ regular: 성인
+ senior: 경로
+ special: 특별
+ youth: 청소년
co2: "CO₂ 배출: {co2} "
co2description: >-
CO₂ 강도는 각 모드의 CO₂ 강도를 트립에서 각 다리가 걸은 거리를 곱하여 계산됩니다. 각 모드의 CO₂ 강도는 이
@@ -7,13 +14,6 @@ otpUi:
departure: >-
{departureDate, time, short} 에 {departureDate,
date, long} 출발
- fareDetailsHeaders:
- cash: 현금
- electronic: 전자
- regular: 성인
- senior: 경로
- special: 특별
- youth: 청소년
hideDetail: 세부정보 숨기기
showDetail: 세부정보 표시
title: 트립 상세정보
@@ -21,5 +21,4 @@ otpUi:
transferDiscountExplanation: 환승 할인 {transferAmount} 적용
transitFare: 대중 교통 운임
transitFareEntry: "{name} : {value} "
- transitFareUnknown: 운임 정보 없음
tripIncludesFlex: 이 트립에는 가변 경로가 포함되어 있습니다. {extraMessage}
diff --git a/packages/trip-details/i18n/nb_NO.yml b/packages/trip-details/i18n/nb_NO.yml
index 10f433bb6..e4b9c3594 100644
--- a/packages/trip-details/i18n/nb_NO.yml
+++ b/packages/trip-details/i18n/nb_NO.yml
@@ -1,4 +1,4 @@
otpUi:
TripDetails:
- fareDetailsHeaders:
+ FareTable:
cash: Kontanter
diff --git a/packages/trip-details/i18n/vi.yml b/packages/trip-details/i18n/vi.yml
index 5a532dce6..dc5e82735 100644
--- a/packages/trip-details/i18n/vi.yml
+++ b/packages/trip-details/i18n/vi.yml
@@ -1,5 +1,12 @@
otpUi:
TripDetails:
+ FareTable:
+ cash: Tiền mặt
+ electronic: Điện tử
+ regular: Người lớn
+ senior: Người lớn tuổi
+ special: Đặc biệt
+ youth: Thiếu niên
co2: "CO₂ thải ra: {co2} "
co2description: >-
Nồng độ CO₂ được tính bằng cách nhân khoảng cách của mỗi chặng của một
@@ -8,13 +15,6 @@ otpUi:
departure: >-
Khởi hành {departureDate, date, long} lúc
{departureDate, time, short}
- fareDetailsHeaders:
- cash: Tiền mặt
- electronic: Điện tử
- regular: Người lớn
- senior: Người lớn tuổi
- special: Đặc biệt
- youth: Thiếu niên
hideDetail: Ẩn chi tiết
showDetail: Hiển thị chi tiết
title: Chi tiết chuyến đi
@@ -22,5 +22,4 @@ otpUi:
transferDiscountExplanation: Giảm giá chuyển khoản {transferAmount} được áp dụng
transitFare: Giá vé xe công cộng
transitFareEntry: "{name}: {value} "
- transitFareUnknown: Không có thông tin giá vé
tripIncludesFlex: Chuyến đi này bao gồm các tuyến đường linh hoạt. {extraMessage}
diff --git a/packages/trip-details/i18n/zh_Hans.yml b/packages/trip-details/i18n/zh_Hans.yml
index 304d4124d..cbe393a91 100644
--- a/packages/trip-details/i18n/zh_Hans.yml
+++ b/packages/trip-details/i18n/zh_Hans.yml
@@ -1,17 +1,17 @@
otpUi:
TripDetails:
- co2: "排放的二氧化碳: {co2} "
- co2description: CO₂ 强度的计算方法是将行程的每条腿的距离乘以每种模式的 CO₂ 强度。 每种模式的二氧化碳强度来自 此电子表格。
- departure: >-
- {departureDate, date, long} {departureDate, time,
- short} 出发
- fareDetailsHeaders:
+ FareTable:
cash: 现金
electronic: 电子的
regular: 成人
senior: 长者
special: 特别的
youth: 青年
+ co2: "排放的二氧化碳: {co2} "
+ co2description: CO₂ 强度的计算方法是将行程的每条腿的距离乘以每种模式的 CO₂ 强度。 每种模式的二氧化碳强度来自 此电子表格。
+ departure: >-
+ {departureDate, date, long} {departureDate, time,
+ short} 出发
hideDetail: 隐藏细节
showDetail: 显示详细资料
title: 行程详情
@@ -19,5 +19,4 @@ otpUi:
transferDiscountExplanation: 应用了 {transferAmount} 的转让折扣
transitFare: 公共交通费
transitFareEntry: "{name}: {value} "
- transitFareUnknown: 没有票价信息
tripIncludesFlex: 这个行程包括灵活的路线. {extraMessage}
diff --git a/packages/trip-details/package.json b/packages/trip-details/package.json
index 2f5ce2757..dbf680e4f 100644
--- a/packages/trip-details/package.json
+++ b/packages/trip-details/package.json
@@ -11,13 +11,13 @@
"license": "MIT",
"private": false,
"dependencies": {
- "@opentripplanner/core-utils": "^8.2.1",
+ "@opentripplanner/core-utils": "^9.0.0-alpha.36",
"@styled-icons/fa-solid": "^10.34.0",
"flat": "^5.0.2",
"react-animate-height": "^3.0.4"
},
"devDependencies": {
- "@opentripplanner/types": "^5.0.0",
+ "@opentripplanner/types": "^6.0.0-alpha.9",
"@types/flat": "^5.0.2"
},
"peerDependencies": {
diff --git a/packages/trip-details/src/TripDetails.story.tsx b/packages/trip-details/src/TripDetails.story.tsx
index b615acf8c..09b23af53 100644
--- a/packages/trip-details/src/TripDetails.story.tsx
+++ b/packages/trip-details/src/TripDetails.story.tsx
@@ -9,6 +9,7 @@ import {
} from "@storybook/react";
import styled from "styled-components";
// The below eslint-disable is due to https://github.com/storybookjs/storybook/issues/13408
+import { convertGraphQLResponseToLegacy } from "@opentripplanner/core-utils/lib/itinerary";
// eslint-disable-next-line import/no-named-as-default
import TripDetails, { FareLegTable } from ".";
import * as TripDetailsClasses from "./styled";
@@ -17,7 +18,6 @@ import {
DepartureDetailsProps,
FareDetailsProps,
FareTableLayout,
- FareTableText,
TripDetailsProps
} from "./types";
@@ -26,21 +26,13 @@ import customFrenchMessages from "../__mocks__/custom-french-messages.yml";
// import mock itinaries. These are all trip plan outputs from OTP.
const bikeOnlyItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/bike-only.json");
-const bikeRentalItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/bike-rental.json");
-const bikeRentalTransitBikeRentalItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/bike-rental-transit-bike-rental.json");
-const bikeTransitBikeItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/bike-transit-bike.json");
-const eScooterRentalItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/e-scooter-rental.json");
-const eScooterRentalTransiteScooterRentalItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/e-scooter-transit-e-scooter.json");
-const parkAndRideItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/park-and-ride.json");
const tncTransitTncItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/tnc-transit-tnc.json");
-const walkInterlinedTransitItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/walk-interlined-transit-walk.json");
const walkOnlyItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/walk-only.json");
const walkTransitWalkItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/walk-transit-walk.json");
const walkTransitWalkTransitWalkItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/walk-transit-walk-transit-walk.json");
-const fareComponentsItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/fare-components.json");
const flexItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/flex-itinerary.json");
const otp2ScooterItinerary = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/otp2-scooter.json");
-const otp2FareProducts = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/otp2-with-fareproducts.json");
+const otp2FareProducts = require("@opentripplanner/itinerary-body/src/__mocks__/itineraries/leg-fare-products.json");
const flattenedEnglishMessages = flatten(customEnglishMessages);
const flattenedFrenchMessages = flatten(customFrenchMessages);
@@ -57,104 +49,70 @@ const StyledTripDetails = styled(TripDetails)`
}
`;
-const otp2FareByLegLayout: FareTableLayout[] = [
+const orcaFareByLegLayout: FareTableLayout[] = [
{
- header: "regular" as FareTableText,
cols: [
{
- header: "cash" as FareTableText,
- riderCategory: "regular",
- fareContainer: "cash"
+ columnHeaderKey: "cash",
+ mediumId: "orca:cash",
+ riderCategoryId: "orca:regular"
},
{
- header: "electronic" as FareTableText,
- riderCategory: "regular",
- fareContainer: "electronic"
+ columnHeaderKey: "electronic",
+ mediumId: "orca:electronic",
+ riderCategoryId: "orca:regular"
},
{
- header: "special" as FareTableText,
- riderCategory: "special",
- fareContainer: "electronic"
+ columnHeaderKey: "special",
+ mediumId: "orca:electronic",
+ riderCategoryId: "orca:special"
}
- ]
- },
- {
- header: "youth" as FareTableText,
- cols: [
- {
- header: "cash" as FareTableText,
- riderCategory: "youth",
- fareContainer: "cash"
- },
- {
- header: "electronic" as FareTableText,
- riderCategory: "youth",
- fareContainer: "electronic"
- }
- ]
+ ],
+ headerKey: "regular"
},
{
- header: "senior" as FareTableText,
- cols: [
- {
- header: "cash" as FareTableText,
- riderCategory: "senior",
- fareContainer: "cash"
- },
- {
- header: "electronic" as FareTableText,
- riderCategory: "senior",
- fareContainer: "electronic"
- }
- ]
- }
-];
-const fareByLegLayout: FareTableLayout[] = [
- {
- header: "regular" as FareTableText,
cols: [
{
- header: "cash" as FareTableText,
- key: "regular"
- },
- {
- header: "electronic" as FareTableText,
- key: "electronicRegular"
+ columnHeaderKey: "cash",
+ mediumId: "orca:cash",
+ riderCategoryId: "orca:youth"
},
{
- header: "special" as FareTableText,
- key: "electronicSpecial"
- }
- ]
- },
- {
- header: "youth" as FareTableText,
- cols: [
- {
- header: "cash" as FareTableText,
- key: "youth"
+ columnHeaderKey: "electronic",
+ mediumId: "orca:electronic",
+ riderCategoryId: "orca:youth"
},
{
- header: "electronic" as FareTableText,
- key: "electronicYouth"
+ columnHeaderKey: "test",
+ mediumId: "invalidkey",
+ riderCategoryId: "invalidkey"
}
- ]
+ ],
+ headerKey: "youth"
},
{
- header: "senior" as FareTableText,
+ headerKey: "senior",
cols: [
{
- header: "cash" as FareTableText,
- key: "cash"
+ columnHeaderKey: "cash",
+ mediumId: "orca:cash",
+ riderCategoryId: "orca:senior"
},
{
- header: "electronic" as FareTableText,
- key: "electronic"
+ columnHeaderKey: "electronic",
+ mediumId: "orca:electronic",
+ riderCategoryId: "orca:senior"
}
]
}
];
+const orcaDefaultFareType = {
+ headerKey: "cash-regular",
+ mediumId: "orca:cash",
+ riderCategoryId: "orca:regular"
+};
+
const englishFareKeyMap = {
regular: "Transit Fare",
student: "Student Fare",
@@ -227,10 +185,11 @@ function createTripDetailsTemplate(
): ComponentStory {
const TripDetailsTemplate = (
{
- TimeActiveDetails,
+ defaultFareType,
DepartureDetails,
FareDetails,
fareDetailsLayout,
+ TimeActiveDetails,
itinerary
}: TripDetailsProps,
{ globals, parameters }: StoryContext
@@ -251,6 +210,7 @@ function createTripDetailsTemplate(
fareKeyNameMap={fareKeyNameMap}
itinerary={itinerary}
co2Config={defaultCo2Config}
+ defaultFareType={defaultFareType}
/>
);
};
@@ -299,7 +259,7 @@ export const WalkTransitWalkItinerary = makeStory({
itinerary: walkTransitWalkItinerary
});
-export const StyledWalkTransitWalkItinerary = makeStory(
+export const StyledItinerary = makeStory(
{
itinerary: walkTransitWalkItinerary
},
@@ -307,51 +267,10 @@ export const StyledWalkTransitWalkItinerary = makeStory(
StyledTripDetails
);
-export const BikeTransitBikeItinerary = makeStory({
- itinerary: bikeTransitBikeItinerary
-});
-
-export const WalkInterlinedTransitItinerary = makeStory(
- {
- defaultFareKey: "electronicRegular",
- fareDetailsLayout: fareByLegLayout,
- itinerary: walkInterlinedTransitItinerary
- },
- {
- useCustomFareKeyMap: true,
- // For illustration purposes,
- // override a subset of localized strings with custom messages.
- reactIntl: {
- messages: {
- "en-US": flattenedEnglishMessages,
- fr: flattenedFrenchMessages
- }
- }
- }
-);
-
-export const WalkTransitTransferItinerary = makeStory({
- itinerary: walkTransitWalkTransitWalkItinerary
-});
-
-export const BikeRentalItinerary = makeStory({
- itinerary: bikeRentalItinerary
-});
-
-export const EScooterRentalItinerary = makeStory({
- itinerary: eScooterRentalItinerary
-});
-
-export const ParkAndRideItinerary = makeStory({
- itinerary: parkAndRideItinerary
-});
-
-export const BikeRentalTransitItinerary = makeStory({
- itinerary: bikeRentalTransitBikeRentalItinerary
-});
-
-export const EScooterRentalTransitItinerary = makeStory({
- itinerary: eScooterRentalTransiteScooterRentalItinerary
+export const LegFareProductsItinerary = makeStory({
+ defaultFareType: orcaDefaultFareType,
+ fareDetailsLayout: orcaFareByLegLayout,
+ itinerary: otp2FareProducts
});
// The render of this itinerary is uninteresting, but the test
@@ -372,10 +291,14 @@ export const TncTransitItinerary = makeStory(
export const TncTransitItineraryWithCustomMessages = makeStory(
{
- TimeActiveDetails: CustomTimeActiveDetails,
- defaultFareKey: "electronicRegular",
+ defaultFareType: {
+ headerKey: "electronicRegular",
+ mediumId: "orca:electronic",
+ riderCategoryId: "orca:regular"
+ },
DepartureDetails: CustomDepartureDetails,
- itinerary: tncTransitTncItinerary
+ itinerary: tncTransitTncItinerary,
+ TimeActiveDetails: CustomTimeActiveDetails
},
{
// For illustration purposes,
@@ -391,23 +314,9 @@ export const TncTransitItineraryWithCustomMessages = makeStory(
StyledTripDetails
);
-export const FareComponentsItinerary = makeStory({
- itinerary: fareComponentsItinerary
-});
-
export const OTP2FlexItinerary = makeStory({ itinerary: flexItinerary });
-export const FareLegTableStory = (): ReactElement => {
- return (
-
- );
-};
-
export const FareLegTableStoryLegProducts = (): ReactElement => {
- return (
-
- );
+ const otp1legs = otp2FareProducts.legs.map(convertGraphQLResponseToLegacy);
+ return ;
};
diff --git a/packages/trip-details/src/fare-table.tsx b/packages/trip-details/src/fare-table.tsx
index 289bcaa4f..d516be8a4 100644
--- a/packages/trip-details/src/fare-table.tsx
+++ b/packages/trip-details/src/fare-table.tsx
@@ -1,16 +1,15 @@
-import { Leg, Money } from "@opentripplanner/types";
+import { Leg } from "@opentripplanner/types";
import React from "react";
import styled from "styled-components";
import { Transfer } from "@styled-icons/boxicons-regular/Transfer";
import {
getItineraryCost,
- getLegCost,
- getLegsWithFares
+ getLegCost
} from "@opentripplanner/core-utils/lib/itinerary";
import { useIntl } from "react-intl";
import { flatten } from "flat";
-import { boldText, getFormattedTextForConfigKey, renderFare } from "./utils";
+import { boldText, renderFare } from "./utils";
import { FareLegTableProps, FareTableLayout } from "./types";
@@ -23,14 +22,8 @@ import defaultEnglishMessages from "../i18n/en-US.yml";
// - the yaml loader for jest returns messages with flattened ids.
const defaultMessages: Record = flatten(defaultEnglishMessages);
-type LegAndFare = Leg & {
- fares: Record;
-};
-
interface FareTypeTableProps extends FareTableLayout {
- fareTotals: Record;
- legs: LegAndFare[];
- hasLegProducts?: boolean;
+ legs: Leg[];
}
const TableHeader = styled.thead`
@@ -73,63 +66,70 @@ const TransferIcon = styled(Transfer)`
padding-left: 4px;
`;
+const useGetHeaderString = (headerKey: string): string => {
+ const intl = useIntl();
+ return intl.formatMessage({
+ id: `otpUi.TripDetails.FareTable.${headerKey}`,
+ description: `Fare leg table header for key ${headerKey}`,
+ defaultMessage:
+ defaultEnglishMessages[`otpUi.TripDetails.FareTable.${headerKey}`]
+ });
+};
+
const FareTypeTable = ({
cols,
- fareTotals,
- header,
- legs,
- hasLegProducts
+ headerKey,
+ legs
}: FareTypeTableProps): JSX.Element => {
- const colsToRender = cols.filter(col =>
- hasLegProducts
- ? getItineraryCost(legs, col.riderCategory, col.fareContainer)
- : fareTotals[col.key]
- );
-
const intl = useIntl();
+ // FIXME: Is there a nicer way to do this?
+ const colsToRender = cols
+ .map(col => ({
+ ...col,
+ total: getItineraryCost(legs, col.mediumId, col.riderCategoryId)
+ }))
+ .filter(col => col.total);
+ const headerString = useGetHeaderString(headerKey);
+
+ const filteredLegs = legs.filter(leg => leg.fareProducts?.length > 0);
if (colsToRender.length) {
return (
- {boldText(getFormattedTextForConfigKey(header))}
+ {boldText(headerString)}
{colsToRender.map(col => {
- const fare = hasLegProducts
- ? getItineraryCost(legs, col.riderCategory, col.fareContainer)
- : fareTotals[col.key];
+ const fare = col.total;
return (
- {boldText(getFormattedTextForConfigKey(col.header))}
+ {boldText(useGetHeaderString(col.columnHeaderKey))}
- {renderFare(
- fare?.currency?.currencyCode,
- (fare?.cents || 0) / 100
- )}
+ {renderFare(fare?.currency?.code, fare?.amount || 0)}
);
})}
- {legs.map((leg, index) => (
+ {filteredLegs.map((leg, index) => (
- {leg.routeShortName || leg.route || leg.routeLongName}
+ {leg.routeShortName || leg.routeLongName}
{colsToRender.map(col => {
- const fare = hasLegProducts
- ? getLegCost(leg, col.riderCategory, col.fareContainer)
- : leg.fares[col.key];
-
+ const fare = getLegCost(leg, col.mediumId, col.riderCategoryId);
return (
0 &&
intl.formatMessage(
{
defaultMessage:
@@ -142,9 +142,9 @@ const FareTypeTable = ({
},
{
transferAmount: intl.formatNumber(
- fare.transferAmount / 100,
+ fare?.transferAmount?.amount,
{
- currency: fare?.price?.currency?.currencyCode,
+ currency: fare?.price?.currency?.code,
currencyDisplay: "narrowSymbol",
style: "currency"
}
@@ -153,15 +153,15 @@ const FareTypeTable = ({
)
}
>
- {(("isTransfer" in fare && fare?.isTransfer) ||
- ("transferAmount" in fare && fare?.transferAmount)) && (
- <>
- {" "}
- >
- )}
+ {"transferAmount" in fare &&
+ fare?.transferAmount?.amount > 0 && (
+ <>
+ {" "}
+ >
+ )}
{renderFare(
- fare?.price?.currency?.currencyCode,
- (fare?.price?.cents || 0) / 100
+ fare?.price?.currency?.code,
+ fare?.price?.amount || 0
)}
);
@@ -174,53 +174,20 @@ const FareTypeTable = ({
return null;
};
-const FareLegDetails = ({
- layout,
- itinerary
-}: FareLegTableProps): JSX.Element => {
- const hasLegProducts = !!itinerary?.fare?.legProducts;
- const fareKeys = Object.keys(itinerary?.fare?.details);
-
- let legsWithFares;
-
- if (!hasLegProducts) {
- // OTP1 Logic
- legsWithFares = itinerary.legs
- .map((leg, index) => {
- const fares = fareKeys.reduce((prev, key) => {
- const fareForKey = itinerary.fare.details[key]?.find(
- detail => detail.legIndex === index
- );
- if (fareForKey) {
- prev[key] = fareForKey;
- }
- return prev;
- }, {});
- return {
- ...leg,
- fares
- };
- })
- .filter(leg => leg.transitLeg);
- } else {
- // OTP2 Logic using core-utils function
- legsWithFares = getLegsWithFares(itinerary).filter(leg => leg.transitLeg);
- }
-
+const FareLegTable = ({ layout, legs }: FareLegTableProps): JSX.Element => {
+ // the layout argument contains an object for every table to be displayed
return (
{layout.map(config => (
))}
);
};
-export default FareLegDetails;
+export default FareLegTable;
diff --git a/packages/trip-details/src/index.tsx b/packages/trip-details/src/index.tsx
index 297ae2119..aa8f1eabb 100644
--- a/packages/trip-details/src/index.tsx
+++ b/packages/trip-details/src/index.tsx
@@ -1,5 +1,5 @@
-import flatten from "flat";
import coreUtils from "@opentripplanner/core-utils";
+import { FareProductSelector } from "@opentripplanner/types";
import React, { ReactElement } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { CalendarAlt } from "@styled-icons/fa-solid/CalendarAlt";
@@ -7,17 +7,13 @@ import { Heartbeat } from "@styled-icons/fa-solid/Heartbeat";
import { MoneyBillAlt } from "@styled-icons/fa-solid/MoneyBillAlt";
import { Leaf } from "@styled-icons/fa-solid/Leaf";
import { Route } from "@styled-icons/fa-solid/Route";
-
+import { flatten } from "flat";
import * as S from "./styled";
import TripDetail from "./trip-detail";
import FareLegTable from "./fare-table";
import { boldText, renderFare } from "./utils";
-import {
- TimeActiveDetailsProps,
- TransitFareProps,
- TripDetailsProps
-} from "./types";
+import { TimeActiveDetailsProps, TripDetailsProps } from "./types";
// Load the default messages.
import defaultEnglishMessages from "../i18n/en-US.yml";
@@ -63,54 +59,13 @@ function DefaultTimeActiveDetails({
);
}
-/**
- * Helper component that renders a transit fare entry.
- */
-const TransitFare = ({
- fareKey,
- fareNameFallback,
- fareKeyNameMap,
- transitFares
-}: TransitFareProps): ReactElement => {
- const currentFare = transitFares[fareKey];
-
- // TODO: Is this needed? Every implementation of TransitFare does a check for currentFare's existence, although not the cents field
- if (typeof currentFare?.cents !== "number") {
- return (
-
- );
- }
-
- return (
-
-
-
- );
-};
-
/**
* Renders trip details such as departure instructions, fare amount, and minutes active.
*/
export function TripDetails({
className = "",
co2Config,
- defaultFareKey = "regular",
+ defaultFareType,
DepartureDetails = null,
displayTimeActive = true,
FareDetails = null,
@@ -122,63 +77,105 @@ export function TripDetails({
// process the transit fare
const fareResult = coreUtils.itinerary.calculateTncFares(itinerary);
const { currencyCode, maxTNCFare, minTNCFare } = fareResult;
- const transitFares = itinerary?.fare?.fare;
-
- let companies = "";
- itinerary.legs.forEach(leg => {
- if (leg.rideHailingEstimate) {
- companies = leg.rideHailingEstimate.provider.id;
- }
- });
- let fare;
- const fareKeys = transitFares && Object.keys(transitFares).sort();
+ const { companies, fareTypes } = itinerary.legs.reduce<{
+ companies: string;
+ fareTypes: FareProductSelector[];
+ }>(
+ (prev, leg) => {
+ if (leg.rideHailingEstimate) {
+ prev.companies = leg.rideHailingEstimate.provider.id;
+ }
- if (transitFares && fareKeys.length > 0) {
- let defaultFare = defaultFareKey;
- if (!transitFares[defaultFareKey]) {
- defaultFare = "regular";
- }
+ if (leg.fareProducts) {
+ leg.fareProducts.forEach(fp => {
+ const mediumId = fp.product.medium?.id;
+ const riderCategoryId = fp.product.riderCategory?.id;
+ if (
+ !prev.fareTypes.find(
+ ft =>
+ ft.mediumId === mediumId &&
+ ft.riderCategoryId === riderCategoryId
+ )
+ ) {
+ prev.fareTypes.push({ mediumId, riderCategoryId });
+ }
+ });
+ }
+ return prev;
+ },
+ { companies: "", fareTypes: [] }
+ );
+ let fare;
+ if (fareTypes.length > 0 && defaultFareType) {
+ const defaultFareTotal = coreUtils.itinerary.getItineraryCost(
+ itinerary.legs,
+ defaultFareType.mediumId,
+ defaultFareType.riderCategoryId
+ );
// Depending on if there are additional fares to display either render a or a
const TransitFareWrapper =
- transitFares && fareKeys.length > 1 ? S.TransitFare : S.TransitFareSingle;
+ fareTypes.length > 1 ? S.TransitFare : S.TransitFareSingle;
- fare = transitFares?.[defaultFare] && (
+ const fareNameFallback = (
+
+ );
+
+ fare = defaultFareTotal?.amount && (
- 1 ? "list-item" : "" }}>
-
+ 1 ? "list-item" : "" }}>
+
{fareDetailsLayout ? (
// Show full ƒare details by leg
-
+
) : (
// Just show the fares for each payment type
- fareKeys.map(fareKey => {
+ fareTypes.map(fareType => {
// Don't show the default fare twice!
- if (fareKey === defaultFare) {
+ if (fareType) {
return null;
}
return (
-
);
})
@@ -280,15 +277,15 @@ export function TripDetails({
}
/>
- {fare && (
+ {!!fare && (
)
}
@@ -302,9 +299,9 @@ export function TripDetails({
description={
FareDetails && (
)
}
diff --git a/packages/trip-details/src/types.ts b/packages/trip-details/src/types.ts
index 7a180ca2c..1feba9d6c 100644
--- a/packages/trip-details/src/types.ts
+++ b/packages/trip-details/src/types.ts
@@ -1,7 +1,12 @@
// Prettier does not recognize the import type syntax.
// eslint-disable-next-line prettier/prettier
-import type { MassUnitOption, Fare, Itinerary, Money } from "@opentripplanner/types";
-import type { ReactElement } from "react";
+import type {
+ FareProductSelector,
+ Itinerary,
+ Leg,
+ MassUnitOption,
+ Money
+ } from "@opentripplanner/types";
export interface TimeActiveDetailsProps {
bikeMinutes: number;
@@ -19,47 +24,34 @@ export interface DepartureDetailsProps {
departureDate: Date;
}
-export enum FareTableText {
- regular = "regular",
- youth = "youth",
- senior = "senior",
- special = "special",
- cash = "cash",
- electronic = "electronic"
-}
-
+/**
+ * This is the interface used to define the layout for a particular fare table.
+ * The table with be rendered with the columns defined here,
+ * with each row being an individual transit leg from the itinerary.
+ */
export interface FareTableLayout {
- cols: {
- header: FareTableText;
- key?: string;
- riderCategory?: string;
- fareContainer?: string;
- }[];
- header: FareTableText;
+ cols: (FareProductSelector & {
+ columnHeaderKey: string;
+ })[]
+ headerKey: string;
}
-export interface TransitFareData {
- [key: string]: Money
+/**
+ * Interface containing the lgs and the layout of the fare table.
+ */
+export interface FareLegTableProps {
+ layout?: FareTableLayout[];
+ legs: Leg[];
}
+// Total fare amount corresponding to a fare key
+export type FareTotals = (FareProductSelector & { price: Money })[]
+
export interface FareDetailsProps {
+ legs: Leg[];
maxTNCFare: number;
minTNCFare: number;
- transitFares: TransitFareData;
-}
-
-export interface FareLegTableProps {
- layout?: FareTableLayout[];
- itinerary: Itinerary;
}
-export interface TransitFareProps {
- fareKey: string;
- fareNameFallback?: ReactElement;
- fareKeyNameMap: {
- [key: string]: string;
- };
- transitFares: Fare;
-}
export interface TripDetailsProps {
/**
@@ -73,7 +65,7 @@ export interface TripDetailsProps {
/**
* Determines which transit fare should be displayed by default, should there be multiple transit fare types.
*/
- defaultFareKey?: string;
+ defaultFareType?: { headerKey: string } & FareProductSelector;
/**
* Slot for a custom component to render the expandable section for departure.
*/
diff --git a/packages/trip-details/src/utils.tsx b/packages/trip-details/src/utils.tsx
index 809c596c1..36cfd05f6 100644
--- a/packages/trip-details/src/utils.tsx
+++ b/packages/trip-details/src/utils.tsx
@@ -1,21 +1,10 @@
-import React, { ReactElement } from "react";
-import { FormattedMessage, FormattedNumber } from "react-intl";
-import { flatten } from "flat";
-import { FareTableText } from "./types";
-
-// Load the default messages.
-import defaultEnglishMessages from "../i18n/en-US.yml";
-
-// HACK: We should flatten the messages loaded above because
-// the YAML loaders behave differently between webpack and our version of jest:
-// - the yaml loader for webpack returns a nested object,
-// - the yaml loader for jest returns messages with flattened ids.
-const defaultMessages: Record = flatten(defaultEnglishMessages);
+import React, { ReactElement, ReactNode } from "react";
+import { FormattedNumber } from "react-intl";
/**
* Format text bold (used with FormattedMessage).
*/
-export function boldText(contents: ReactElement): ReactElement {
+export function boldText(contents: ReactNode): ReactElement {
return {contents} ;
}
@@ -39,63 +28,3 @@ export function renderFare(currencyCode: string, fare: number): ReactElement {
/>
);
}
-
-export const getFormattedTextForConfigKey = (textKey: FareTableText) => {
- switch (textKey) {
- case FareTableText.cash:
- return (
-
- );
- case FareTableText.electronic:
- return (
-
- );
- case FareTableText.youth:
- return (
-
- );
- case FareTableText.senior:
- return (
-
- );
- case FareTableText.special:
- return (
-
- );
- case FareTableText.regular:
- default:
- return (
-
- );
- }
-};
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 3731bb292..d9f2e97ea 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -187,11 +187,6 @@ export type Config = {
transitOperators?: TransitOperator[];
};
-type FeedScopedId = {
- agencyId?: string;
- id?: string;
-};
-
export type EncodedPolyline = {
length: number;
points: string;
@@ -298,6 +293,7 @@ export type Leg = {
dropOffBookingInfo?: FlexDropOffBookingInfo;
duration: number;
endTime: number;
+ fareProducts?: { id: string; product: FareProduct }[];
from: Place;
headsign?: string;
interlineWithPreviousLeg: boolean;
@@ -342,11 +338,6 @@ export type Leg = {
tripBlockId?: string;
tripId?: string;
walkingBike?: boolean;
- /**
- * Below this are extra properties added in OTP-RR
- * They are not returned in the API response
- */
- fareProducts?: Array;
};
type TripStopTime = {
@@ -368,44 +359,13 @@ type TemporaryTNCPriceType = {
* Describes the cost of an itinerary leg.
*/
export type Money = {
- cents: number;
+ amount: number;
currency: {
- defaultFractionDigits: number;
- currencyCode: string;
- symbol: string;
- currency: string;
+ code: string;
+ digits: number;
};
};
-/**
- * Describes a fare id or route to which a fare applies.
- */
-type ApplicableId = string | FeedScopedId;
-
-export type FareDetail = {
- fareId?: ApplicableId;
- isTransfer?: boolean;
- legIndex?: number;
- price: Money;
- routes?: ApplicableId[];
-};
-
-export type FareDetails = Record;
-
-/**
- * Represents the fare component of an itinerary of an OTP plan response. See
- * detailed documentation in OTP webservice documentation here:
- * http://otp-docs.ibi-transit.com/api/json_Fare.html
- *
- * NOTE: so far the fare includes ONLY a fare encountered on public transit and
- * not any bike rental or TNC rental fees.
- */
-export type Fare = {
- details?: FareDetails;
- fare?: Record;
- legProducts?: Array;
-};
-
/**
* Represents an itinerary of an OTP plan response. See detailed documentation
* in OTP webservice documentation here:
@@ -418,7 +378,6 @@ export type Itinerary = {
elevationGained: number;
elevationLost: number;
endTime: number;
- fare?: Fare;
legs: Leg[];
startTime: number;
tooSloped?: boolean;
@@ -791,23 +750,26 @@ export type ModeButtonDefinition = {
modeSettings?: ModeSetting[]; // From OTP definitions + config
};
+/**
+ * Definition for a fare product used to pay the fare for a leg in a transit journey
+ */
export type FareProduct = {
- amount: Money;
id: string;
- name: string;
- category: {
+ medium?: {
id: string;
name: string;
};
- container: {
+ name: string;
+ price: Money;
+ riderCategory?: {
id: string;
name: string;
};
};
-export type LegProduct = {
- legIndices: Array;
- products: Array;
+export type FareProductSelector = {
+ mediumId: string;
+ riderCategoryId: string;
};
/**
diff --git a/yarn.lock b/yarn.lock
index 292fe6180..89314e45f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3241,6 +3241,25 @@
resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca"
integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==
+"@opentripplanner/core-utils@^9.0.0-alpha.36":
+ version "9.0.0-alpha.36"
+ resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-9.0.0-alpha.36.tgz#d28a8f602eea198e23c017cea2dce87fbb41ff1b"
+ integrity sha512-/Jo4x810D41Htr1eJXekU2MBgqxw2tO7ZTejt2jBuwMwRgL68qPCt45MKcLfXGrJLXv8J/0lVwmkqbcqK95D+A==
+ dependencies:
+ "@conveyal/lonlat" "^1.4.1"
+ "@mapbox/polyline" "^1.1.0"
+ "@opentripplanner/geocoder" "^1.4.0"
+ "@styled-icons/foundation" "^10.34.0"
+ "@turf/along" "^6.0.1"
+ bowser "^2.7.0"
+ chroma-js "^2.4.2"
+ date-fns "^2.28.0"
+ date-fns-tz "^1.2.2"
+ graphql "^16.6.0"
+ lodash.clonedeep "^4.5.0"
+ lodash.isequal "^4.5.0"
+ qs "^6.9.1"
+
"@opentripplanner/geocoder@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-1.4.0.tgz#728afec6585cf3748a1931493b3a3566421f445d"
@@ -3256,6 +3275,11 @@
resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.0.0-alpha.4.tgz#dee3c06675e30c596159d182ed707e7710cd937c"
integrity sha512-FlYsHm/tk+x0ldrEG1X9Ph1R1k3way1oEdKX7XSnhu9lNTzL4lO0hfT1PLzNXMfBRar7TzK94PHNDibJsod8Kw==
+"@opentripplanner/types@^6.0.0-alpha.9":
+ version "6.0.0-alpha.10"
+ resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.0.0-alpha.10.tgz#1489c55f7741ef46df6882c682580ea96ca23d62"
+ integrity sha512-s0xBgA3WLqvda5itUYzv7PGyMV/dqITfnA2zpdwsbyzuNQeOYTk1tSQLWr+QH7mbUYJXAs5i8qzC/VNvDSZQqg==
+
"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0":
version "2.3.3"
resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777"