Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Stop Time Interpolation #998

Merged
merged 6 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions i18n/english.yml
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,26 @@ components:
fieldUndefined: Field to normalize must be defined.
substitutionUndefined: Substitution patterns must be defined.
substitutionInvalid: Some substitution patterns are invalid.
NormalizeStopTimesModal:
close: Close
interpolateStopTimes: Interpolate stop times between timepoints?
normalizeStopTimes: Normalize stop times
normalizeStopTimesQuestion: Normalize stop times?
selectBeginningPatternStop: "Select beginning pattern stop:"
tooFewTimepoints: "You must have more than 1 timepoint to interpolate times"
usageExplanationOne: This feature is useful when the travel times for one or more
pattern stops change. Take for example a pattern
that has been re-routed along to travel a longer distance, has had a
stop added (or removed), or has had a layover introduced mid-trip.
Once you have adjusted the travel times to account for these changes,
you can normalize the stop times to bring them into alignment with the
updated travel times reflected in the pattern stops.
usageExplanationTwo: Interpolating stop times calculates the implicit speed between timepoints
based on the shape distance and the default travel times. This speed is
then applied to the shape distance traveled for each intermediate non-timepoint
stop to provide interpolated travel times. The default travel time for non-timepoint
stops will not be modified.
usageNotes: " Usage notes"
NormalizeStopTimesTip:
info: "Tip: when changing travel times, consider
using the 'Normalize stop times' button above to automatically update
Expand Down
20 changes: 20 additions & 0 deletions i18n/german.yml
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,26 @@ components:
fieldUndefined: Zu normalisierendes Feld muss angegeben werden.
substitutionInvalid: Substitutions-Muster ungültig.
substitutionUndefined: Substitution-Muster müssen angegeben werden.
NormalizeStopTimesModal:
close: Close
interpolateStopTimes: Interpolate stop times between timepoints?
normalizeStopTimes: Normalize stop times
normalizeStopTimesQuestion: Normalize stop times?
selectBeginningPatternStop: "Select beginning pattern stop:"
tooFewTimepoints: "You must have more than 1 timepoint to interpolate times"
usageExplanationOne: This feature is useful when the travel times for one or more
pattern stops change. Take for example a pattern
that has been re-routed along to travel a longer distance, has had a
stop added (or removed), or has had a layover introduced mid-trip.
Once you have adjusted the travel times to account for these changes,
you can normalize the stop times to bring them into alignment with the
updated travel times reflected in the pattern stops.
usageExplanationTwo: Interpolating stop times calculates the implicit speed between timepoints
based on the shape distance and the default travel times. This speed is
then applied to the shape distance traveled for each intermediate non-timepoint
stop to provide interpolated travel times. The default travel time for non-timepoint
stops will not be modified.
usageNotes: " Usage notes"
NormalizeStopTimesTip:
info: 'Tipp: Wenn Sie Reisezeiten ändern, erwägen Sie die Benutzung des "Stop
times normalisieren"-Buttons oberhalb, um automatisch alle Stop Times auf die
Expand Down
20 changes: 20 additions & 0 deletions i18n/polish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,26 @@ components:
fieldUndefined: Field to normalize must be defined.
substitutionInvalid: Some substitution patterns are invalid.
substitutionUndefined: Substitution patterns must be defined.
NormalizeStopTimesModal:
close: Close
interpolateStopTimes: Interpolate stop times between timepoints?
normalizeStopTimes: Normalize stop times
normalizeStopTimesQuestion: Normalize stop times?
selectBeginningPatternStop: "Select beginning pattern stop:"
tooFewTimepoints: "You must have more than 1 timepoint to interpolate times"
usageExplanationOne: This feature is useful when the travel times for one or more
pattern stops change. Take for example a pattern
that has been re-routed along to travel a longer distance, has had a
stop added (or removed), or has had a layover introduced mid-trip.
Once you have adjusted the travel times to account for these changes,
you can normalize the stop times to bring them into alignment with the
updated travel times reflected in the pattern stops.
usageExplanationTwo: Interpolating stop times calculates the implicit speed between timepoints
based on the shape distance and the default travel times. This speed is
then applied to the shape distance traveled for each intermediate non-timepoint
stop to provide interpolated travel times. The default travel time for non-timepoint
stops will not be modified.
usageNotes: " Usage notes"
NormalizeStopTimesTip:
info: 'Tip: when changing travel times, consider using the "Normalize stop times"
button above to automatically update all stop times to the updated travel time.'
Expand Down
12 changes: 6 additions & 6 deletions lib/editor/actions/tripPattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import {createAction, type ActionType} from 'redux-actions'
import {ActionCreators} from 'redux-undo'
import {toast} from 'react-toastify'

import {resetActiveGtfsEntity, savedGtfsEntity, updateActiveGtfsEntity, updateEditSetting} from './active'
import {createVoidPayloadAction, fetchGraphQL, secureFetch} from '../../common/actions'
import {snakeCaseKeys} from '../../common/util/map-keys'
import {generateUID} from '../../common/util/util'
import {showEditorModal} from './editor'
import {shapes} from '../../gtfs/util/graphql'
import {fetchGTFSEntities, receiveGTFSEntities} from '../../manager/actions/versions'
import {fetchTripCounts} from './trip'
import {getEditorNamespace} from '../util/gtfs'
import {resequenceStops, resequenceShapePoints} from '../util/map'
import {entityIsNew} from '../util/objects'

import type {ControlPoint, Pattern, PatternStop} from '../../types'
import type {dispatchFn, getStateFn} from '../../types/reducers'

import {fetchTripCounts} from './trip'
import {showEditorModal} from './editor'
import {resetActiveGtfsEntity, savedGtfsEntity, updateActiveGtfsEntity, updateEditSetting} from './active'

const fetchingTripPatterns = createVoidPayloadAction('FETCHING_TRIP_PATTERNS')
const savedTripPattern = createVoidPayloadAction('SAVED_TRIP_PATTERN')
export const setActivePatternSegment = createAction(
Expand Down Expand Up @@ -51,12 +51,12 @@ export type EditorTripPatternActions = ActionType<typeof normalizeStopTimes> |
* provides a way to bulk update existing trips when pattern stops are modified
* (e.g., a pattern stop is inserted, removed, or its travel times modified).
*/
export function normalizeStopTimes (patternId: number, beginStopSequence: number) {
export function normalizeStopTimes (patternId: number, beginStopSequence: number, interpolateStopTimes: boolean) {
return function (dispatch: dispatchFn, getState: getStateFn) {
const {data} = getState().editor
const {feedSourceId} = data.active
const sessionId = data.lock.sessionId || ''
const url = `/api/editor/secure/pattern/${patternId}/stop_times?feedId=${feedSourceId || ''}&sessionId=${sessionId}&stopSequence=${beginStopSequence}`
const url = `/api/editor/secure/pattern/${patternId}/stop_times?feedId=${feedSourceId || ''}&sessionId=${sessionId}&stopSequence=${beginStopSequence}&interpolateStopTimes=${interpolateStopTimes.toString()}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a crazy hack but glad it works

return dispatch(secureFetch(url, 'put'))
.then(res => res.json())
.then(json => toast.info(`ⓘ ${json.updateResult}`, {
Expand Down
55 changes: 39 additions & 16 deletions lib/editor/components/pattern/NormalizeStopTimesModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import Icon from '@conveyal/woonerf/components/icon'
import React, { Component } from 'react'
import { Alert, Button, ControlLabel, FormControl, Modal } from 'react-bootstrap'
import { Alert, Button, Checkbox, ControlLabel, FormControl, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap'

import * as tripPatternActions from '../../actions/tripPattern'
import type { GtfsStop, Pattern } from '../../../types'
import { getComponentMessages } from '../../../common/util/config'

type Props = {
activePattern: Pattern,
Expand All @@ -15,43 +16,53 @@ type Props = {
stops: Array<GtfsStop>
}

type State = { patternStopIndex: number, show: boolean }
type State = { interpolateStopTimes: boolean, patternStopIndex: number, show: boolean }

export default class NormalizeStopTimesModal extends Component<Props, State> {
messages = getComponentMessages('NormalizeStopTimesModal')

state = {
interpolateStopTimes: false,
patternStopIndex: 0, // default to zeroth pattern stop
show: false
}

_onClickNormalize = () => {
const { activePattern, normalizeStopTimes } = this.props
normalizeStopTimes(activePattern.id, this.state.patternStopIndex)
normalizeStopTimes(activePattern.id, this.state.patternStopIndex, this.state.interpolateStopTimes)
this.setState({interpolateStopTimes: false})
}

_onChangeStop = (evt: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({patternStopIndex: +evt.target.value})
}

_onClose = () => {
this.setState({ show: false })
this.setState({ show: false, interpolateStopTimes: false })
this.props.onClose()
}

_onChangeInterpolation = () => {
this.setState({interpolateStopTimes: !this.state.interpolateStopTimes})
}

render () {
const { Body, Footer, Header, Title } = Modal
const { activePattern, stops } = this.props
const timepoints = activePattern.patternStops.filter(ps => ps.timepoint === 1)
const interpolationDisabled = timepoints.length < 2
return (
<Modal show={this.props.show || this.state.show} onHide={this._onClose}>
<Header>
<Title>Normalize stop times?</Title>
<Title>{this.messages('normalizeStopTimesQuestion')}</Title>
</Header>
<Body>
<p>
Normalizing stop times will overwrite the arrival and departure
times for <strong>all trips</strong> on this pattern to conform
to the default travel and dwell times defined for the pattern stops.
</p>
<ControlLabel>Select beginning pattern stop:</ControlLabel>
<ControlLabel>{this.messages('selectBeginningPatternStop')}</ControlLabel>
<FormControl
value={this.state.patternStopIndex}
componentClass='select'
Expand All @@ -69,6 +80,22 @@ export default class NormalizeStopTimesModal extends Component<Props, State> {
}
)}
</FormControl>
<div style={{alignContent: 'center', alignItems: 'center', display: 'flex'}}>
<OverlayTrigger
overlay={<Tooltip>{this.messages('tooFewTimepoints')}</Tooltip>}
placement='bottom'
// Semi-hack: Use the trigger prop to conditionally render the tooltip text only when checkbox is disabled.
trigger={interpolationDisabled ? ['hover'] : []}
>
<Checkbox
disabled={interpolationDisabled}
onChange={this._onChangeInterpolation}
value={this.state.interpolateStopTimes}
/>
</OverlayTrigger>
{/* Separate label so that tooltip appears over checkbox. Hack: Padding to align center with checkbox */}
<span style={{paddingBottom: '2px'}}>{this.messages('interpolateStopTimes')}</span>
</div>
<br />
<Alert bsStyle='warning'>
{this.state.patternStopIndex === 0
Expand All @@ -86,15 +113,11 @@ export default class NormalizeStopTimesModal extends Component<Props, State> {
}
</Alert>
<Alert bsStyle='info'>
<h5><Icon type='info-circle' /> Usage notes</h5>
<h5><Icon type='info-circle' />{this.messages('usageNotes')}</h5>
<small>
This feature is useful when the travel times for one or more
pattern stops change. Take for example a pattern
that has been re-routed along to travel a longer distance, has had a
stop added (or removed), or has had a layover introduced mid-trip.
Once you have adjusted the travel times to account for these changes,
you can normalize the stop times to bring them into alignment with the
updated travel times reflected in the pattern stops.
{this.messages('usageExplanationOne')}
<hr />
{this.messages('usageExplanationTwo')}
<hr />
<strong>Note:</strong> this does not account for any variation
in travel time between stops for trips throughout the day (e.g.,
Expand All @@ -108,11 +131,11 @@ export default class NormalizeStopTimesModal extends Component<Props, State> {
bsStyle='primary'
onClick={this._onClickNormalize}
>
Normalize stop times
{this.messages('normalizeStopTimes')}
</Button>
<Button
onClick={this._onClose}>
Close
{this.messages('close')}
</Button>
</Footer>
</Modal>
Expand Down
5 changes: 4 additions & 1 deletion lib/editor/components/pattern/PatternStopsPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,14 @@ export default class PatternStopsPanel extends Component<Props, State> {
</h4>
<div style={{width: '100%'}}>
<Button
disabled={!patternHasStops}
onClick={this._clickNormalizeStopTimes}
style={{marginBottom: '5px'}}
className='pull-right'
block
bsSize='small'>
bsSize='small'
title={!patternHasStops ? 'Must add stops to pattern before normalizing' : ''}
>
<Icon type='clock-o' />{' '}
Normalize stop times
</Button>
Expand Down
Loading