Skip to content

Commit 47af44f

Browse files
authored
Merge pull request #207 from opentripplanner/realtime-time-update
Display transit leg real-time status
2 parents 10cac0f + 83c2012 commit 47af44f

File tree

4 files changed

+147
-6
lines changed

4 files changed

+147
-6
lines changed

Diff for: lib/components/narrative/line-itin/connected-itinerary-body.js

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import styled from 'styled-components'
1212
import { showLegDiagram } from '../../../actions/map'
1313
import { setViewedTrip } from '../../../actions/ui'
1414
import TransitLegSubheader from './connected-transit-leg-subheader'
15+
import RealtimeTimeColumn from './realtime-time-column'
1516
import TripDetails from '../connected-trip-details'
1617
import TripTools from '../trip-tools'
1718

@@ -67,6 +68,7 @@ class ConnectedItineraryBody extends Component {
6768
toRouteAbbreviation={noop}
6869
TransitLegSubheader={TransitLegSubheader}
6970
TransitLegSummary={TransitLegSummary}
71+
TimeColumnContent={RealtimeTimeColumn}
7072
/>
7173
<TripDetails itinerary={itinerary} />
7274
<TripTools itinerary={itinerary} />
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { isTransit } from '@opentripplanner/core-utils/lib/itinerary'
2+
import {
3+
legType,
4+
timeOptionsType
5+
} from '@opentripplanner/core-utils/lib/types'
6+
import { formatTime } from '@opentripplanner/core-utils/lib/time'
7+
import PropTypes from 'prop-types'
8+
import React from 'react'
9+
import styled from 'styled-components'
10+
11+
const TimeText = styled.div``
12+
13+
const TimeStruck = styled.div`
14+
text-decoration: line-through;
15+
`
16+
17+
const TimeBlock = styled.div`
18+
line-height: 1em;
19+
margin-bottom: 4px;
20+
`
21+
22+
const TimeColumnBase = styled.div``
23+
24+
const StatusText = styled.div`
25+
color: #bbb;
26+
font-size: 80%;
27+
line-height: 1em;
28+
`
29+
30+
const DelayText = styled.span`
31+
display: block;
32+
white-space: nowrap;
33+
`
34+
35+
// Reusing stop viewer colors.
36+
const TimeColumnOnTime = styled(TimeColumnBase)`
37+
${TimeText}, ${StatusText} {
38+
color: #5cb85c;
39+
}
40+
`
41+
const TimeColumnEarly = styled(TimeColumnBase)`
42+
${TimeText}, ${StatusText} {
43+
color: #337ab7;
44+
}
45+
`
46+
const TimeColumnLate = styled(TimeColumnBase)`
47+
${TimeText}, ${StatusText} {
48+
color: #d9534f;
49+
}
50+
`
51+
52+
/**
53+
* This component displays the scheduled departure/arrival time for a leg,
54+
* and, for transit legs, displays any delays or earliness where applicable.
55+
*/
56+
export default function RealtimeTimeColumn ({
57+
isDestination,
58+
leg,
59+
timeOptions
60+
}) {
61+
const time = isDestination ? leg.endTime : leg.startTime
62+
const formattedTime = time && formatTime(time, timeOptions)
63+
const isTransitLeg = isTransit(leg.mode)
64+
65+
// For non-real-time legs, show only the scheduled time,
66+
// except for transit legs where we add the "scheduled" text underneath.
67+
if (!leg.realTime) {
68+
return (
69+
<>
70+
<TimeText>{formattedTime}</TimeText>
71+
{isTransitLeg && <StatusText>scheduled</StatusText>}
72+
</>
73+
)
74+
}
75+
76+
// Delay in seconds.
77+
const delay = isDestination ? leg.arrivalDelay : leg.departureDelay
78+
// Time is in milliseconds.
79+
const originalTime = time - delay * 1000
80+
const originalFormattedTime =
81+
originalTime && formatTime(originalTime, timeOptions)
82+
83+
// TODO: refine on-time thresholds.
84+
// const isOnTime = delay >= -60 && delay <= 120;
85+
const isOnTime = delay === 0
86+
87+
let statusText
88+
let TimeColumn = TimeColumnBase
89+
if (isOnTime) {
90+
statusText = 'on time'
91+
TimeColumn = TimeColumnOnTime
92+
} else if (delay < 0) {
93+
statusText = 'early'
94+
TimeColumn = TimeColumnEarly
95+
} else if (delay > 0) {
96+
statusText = 'late'
97+
TimeColumn = TimeColumnLate
98+
}
99+
100+
// Absolute delay in rounded minutes, for display purposes.
101+
const delayInMinutes = Math.abs(
102+
Math.round((isDestination ? leg.arrivalDelay : leg.departureDelay) / 60)
103+
)
104+
105+
let renderedTime
106+
if (!isOnTime) {
107+
// If the transit vehicle is not on time, strike the original scheduled time
108+
// and display the updated time underneath.
109+
renderedTime = (
110+
<TimeBlock>
111+
{!isOnTime && <TimeStruck>{originalFormattedTime}</TimeStruck>}
112+
<TimeText>{formattedTime}</TimeText>
113+
</TimeBlock>
114+
)
115+
} else {
116+
renderedTime = <TimeText>{formattedTime}</TimeText>
117+
}
118+
119+
return (
120+
<TimeColumn>
121+
{renderedTime}
122+
<StatusText>
123+
{/* Keep the '5 min' string on the same line. */}
124+
{!isOnTime && <DelayText>{delayInMinutes} min</DelayText>}
125+
{statusText}
126+
</StatusText>
127+
</TimeColumn>
128+
)
129+
}
130+
131+
RealtimeTimeColumn.propTypes = {
132+
isDestination: PropTypes.bool.isRequired,
133+
leg: legType.isRequired,
134+
timeOptions: timeOptionsType
135+
}
136+
137+
RealtimeTimeColumn.defaultProps = {
138+
timeOptions: null
139+
}

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"@opentripplanner/geocoder": "^1.0.2",
3535
"@opentripplanner/humanize-distance": "^0.0.22",
3636
"@opentripplanner/icons": "^1.0.1",
37-
"@opentripplanner/itinerary-body": "^1.0.2",
37+
"@opentripplanner/itinerary-body": "^1.1.0",
3838
"@opentripplanner/location-field": "^1.0.2",
3939
"@opentripplanner/location-icon": "^1.0.0",
4040
"@opentripplanner/park-and-ride-overlay": "^1.0.1",

Diff for: yarn.lock

+5-5
Original file line numberDiff line numberDiff line change
@@ -1331,12 +1331,12 @@
13311331
"@opentripplanner/core-utils" "^1.2.0"
13321332
prop-types "^15.7.2"
13331333

1334-
"@opentripplanner/itinerary-body@^1.0.2":
1335-
version "1.0.2"
1336-
resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-1.0.2.tgz#f280932a13723f49bac92c79feb83a088d14329b"
1337-
integrity sha512-3x8UvtkL3WmUeNTeWsWTJJtR2FqcWU42xw4833+6pMMzPoS2U4bYzSbw7ucQT64vqcoRQ0ykUjE7as32W+VgGg==
1334+
"@opentripplanner/itinerary-body@^1.1.0":
1335+
version "1.1.0"
1336+
resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-1.1.0.tgz#6fabc389ad25f6f14db5a5861603a127530e0d9e"
1337+
integrity sha512-svu2A0z+CnL5vrkDXuaMjJaDoSC4d52utzdGfgkXdfMaK9prO1D/6SHWiVILXmaDJExdBAoF3mxSuA7xPItTTA==
13381338
dependencies:
1339-
"@opentripplanner/core-utils" "^1.2.0"
1339+
"@opentripplanner/core-utils" "^2.1.0"
13401340
"@opentripplanner/humanize-distance" "^0.0.22"
13411341
"@opentripplanner/icons" "^1.0.0"
13421342
"@opentripplanner/location-icon" "^1.0.0"

0 commit comments

Comments
 (0)