From f044955a986c5f27cdde00965ffa6d9c62f68a9b Mon Sep 17 00:00:00 2001 From: "philip.cline" Date: Sun, 5 Nov 2023 14:29:54 -0500 Subject: [PATCH 1/6] feat(NormalizeStopTimesModal): add stop time normalization --- lib/editor/actions/tripPattern.js | 12 +++++----- .../pattern/NormalizeStopTimesModal.js | 23 ++++++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/editor/actions/tripPattern.js b/lib/editor/actions/tripPattern.js index 3234fd522..fbafa8b6d 100644 --- a/lib/editor/actions/tripPattern.js +++ b/lib/editor/actions/tripPattern.js @@ -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( @@ -51,12 +51,12 @@ export type EditorTripPatternActions = ActionType | * 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()}` return dispatch(secureFetch(url, 'put')) .then(res => res.json()) .then(json => toast.info(`ⓘ ${json.updateResult}`, { diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index 6847fb9f6..bb0cf7989 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -2,7 +2,7 @@ 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 } from 'react-bootstrap' import * as tripPatternActions from '../../actions/tripPattern' import type { GtfsStop, Pattern } from '../../../types' @@ -15,17 +15,18 @@ type Props = { stops: Array } -type State = { patternStopIndex: number, show: boolean } + type State = { interpolateStopTimes: boolean, patternStopIndex: number, show: boolean } export default class NormalizeStopTimesModal extends Component { 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) } _onChangeStop = (evt: SyntheticInputEvent) => { @@ -37,6 +38,10 @@ export default class NormalizeStopTimesModal extends Component { this.props.onClose() } + _onChangeInterpolation = () => { + this.setState({interpolateStopTimes: !this.state.interpolateStopTimes}) + } + render () { const { Body, Footer, Header, Title } = Modal const { activePattern, stops } = this.props @@ -69,6 +74,12 @@ export default class NormalizeStopTimesModal extends Component { } )} + + Interpolate stop times between timepoints? +
{this.state.patternStopIndex === 0 @@ -96,6 +107,12 @@ export default class NormalizeStopTimesModal extends Component { you can normalize the stop times to bring them into alignment with the updated travel times reflected in the pattern stops.
+ 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. +
Note: this does not account for any variation in travel time between stops for trips throughout the day (e.g., due to slower travel speeds during the AM peak). It overwrites ALL From 62216cacba02f4596e040fa10b3334b2de985a52 Mon Sep 17 00:00:00 2001 From: "philip.cline" Date: Wed, 8 Nov 2023 08:47:12 -0500 Subject: [PATCH 2/6] refactor(NormalizeStopTimesModal): i18n --- i18n/english.yml | 19 ++++++++++++ i18n/german.yml | 19 ++++++++++++ i18n/polish.yml | 19 ++++++++++++ .../pattern/NormalizeStopTimesModal.js | 29 +++++++------------ 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/i18n/english.yml b/i18n/english.yml index 80e42408f..077e780ae 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -818,6 +818,25 @@ 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:" + 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 diff --git a/i18n/german.yml b/i18n/german.yml index 8a3bf9ef6..22107e85b 100644 --- a/i18n/german.yml +++ b/i18n/german.yml @@ -830,6 +830,25 @@ 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:" + 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 diff --git a/i18n/polish.yml b/i18n/polish.yml index 613369242..ae0c16bd3 100644 --- a/i18n/polish.yml +++ b/i18n/polish.yml @@ -821,6 +821,25 @@ 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:" + 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.' diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index bb0cf7989..3f41cb4fc 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -6,6 +6,7 @@ import { Alert, Button, Checkbox, ControlLabel, FormControl, Modal } from 'react import * as tripPatternActions from '../../actions/tripPattern' import type { GtfsStop, Pattern } from '../../../types' +import { getComponentMessages } from '../../../common/util/config' type Props = { activePattern: Pattern, @@ -18,6 +19,8 @@ type Props = { type State = { interpolateStopTimes: boolean, patternStopIndex: number, show: boolean } export default class NormalizeStopTimesModal extends Component { + messages = getComponentMessages('NormalizeStopTimesModal') + state = { interpolateStopTimes: false, patternStopIndex: 0, // default to zeroth pattern stop @@ -48,7 +51,7 @@ export default class NormalizeStopTimesModal extends Component { return (
- Normalize stop times? + {this.messages('normalizeStopTimesQuestion')}

@@ -56,7 +59,7 @@ export default class NormalizeStopTimesModal extends Component { times for all trips on this pattern to conform to the default travel and dwell times defined for the pattern stops.

- Select beginning pattern stop: + {this.messages('selectBeginningPatternStop')} { onChange={this._onChangeInterpolation} value={this.state.interpolateStopTimes} > - Interpolate stop times between timepoints? + {this.messages('interpolateStopTimes')}
@@ -97,21 +100,11 @@ export default class NormalizeStopTimesModal extends Component { } -
Usage notes
+
{this.messages('usageNotes')}
- 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')}
- 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. + {this.messages('usageExplanationTwo')}
Note: this does not account for any variation in travel time between stops for trips throughout the day (e.g., @@ -125,11 +118,11 @@ export default class NormalizeStopTimesModal extends Component { bsStyle='primary' onClick={this._onClickNormalize} > - Normalize stop times + {this.messages('normalizeStopTimes')}
From 954f0a2507dfe9a9e7526a614834378201b96485 Mon Sep 17 00:00:00 2001 From: "philip.cline" Date: Wed, 8 Nov 2023 15:57:17 -0500 Subject: [PATCH 3/6] refactor(PatternStopsPanel): disable normalize if no stops --- lib/editor/components/pattern/PatternStopsPanel.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/editor/components/pattern/PatternStopsPanel.js b/lib/editor/components/pattern/PatternStopsPanel.js index f9d9ca52f..e85ecbf2a 100644 --- a/lib/editor/components/pattern/PatternStopsPanel.js +++ b/lib/editor/components/pattern/PatternStopsPanel.js @@ -122,11 +122,14 @@ export default class PatternStopsPanel extends Component {
From 64e15cc1e93b270ca776209001df8722ea045302 Mon Sep 17 00:00:00 2001 From: "philip.cline" Date: Fri, 17 Nov 2023 13:36:58 -0500 Subject: [PATCH 4/6] refactor(NormalizeStopTimesModal): disable if too few timepoints --- i18n/english.yml | 1 + i18n/german.yml | 1 + i18n/polish.yml | 1 + .../pattern/NormalizeStopTimesModal.js | 24 +++++++++++++------ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/i18n/english.yml b/i18n/english.yml index 077e780ae..c8c0bcd83 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -824,6 +824,7 @@ components: 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 diff --git a/i18n/german.yml b/i18n/german.yml index 22107e85b..c80aeca22 100644 --- a/i18n/german.yml +++ b/i18n/german.yml @@ -836,6 +836,7 @@ components: 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 diff --git a/i18n/polish.yml b/i18n/polish.yml index ae0c16bd3..4db630110 100644 --- a/i18n/polish.yml +++ b/i18n/polish.yml @@ -827,6 +827,7 @@ components: 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 diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index 3f41cb4fc..5fdc5fd34 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -2,7 +2,7 @@ import Icon from '@conveyal/woonerf/components/icon' import React, { Component } from 'react' -import { Alert, Button, Checkbox, 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' @@ -48,6 +48,8 @@ export default class NormalizeStopTimesModal extends Component { 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 (
@@ -77,12 +79,20 @@ export default class NormalizeStopTimesModal extends Component { } )} - - {this.messages('interpolateStopTimes')} - +
+ {this.messages('tooFewTimepoints')}} + placement='bottom' + > + + + {/* Separate label so that tooltip appears over checkbox. Hack: Padding to align center with checkbox */} + {this.messages('interpolateStopTimes')} +

{this.state.patternStopIndex === 0 From e7973ecd479b958a30ff2e9cf865060fbfaf9c93 Mon Sep 17 00:00:00 2001 From: "philip.cline" Date: Fri, 17 Nov 2023 13:47:44 -0500 Subject: [PATCH 5/6] refactor(NormalizeStopTimesModal): Conditionally render tooltip --- lib/editor/components/pattern/NormalizeStopTimesModal.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index 5fdc5fd34..8ed82ff1d 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -83,6 +83,8 @@ export default class NormalizeStopTimesModal extends Component { {this.messages('tooFewTimepoints')}} placement='bottom' + // Semi-hack: Use the trigger prop to conditionally render the tooltip text only when checkbox is disabled. + trigger={interpolationDisabled ? ['hover'] : []} > Date: Mon, 20 Nov 2023 15:15:44 -0500 Subject: [PATCH 6/6] refactor(NormalizeStopTimesModal): avoid state bug --- lib/editor/components/pattern/NormalizeStopTimesModal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index 8ed82ff1d..42fdf88f7 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -30,6 +30,7 @@ export default class NormalizeStopTimesModal extends Component { _onClickNormalize = () => { const { activePattern, normalizeStopTimes } = this.props normalizeStopTimes(activePattern.id, this.state.patternStopIndex, this.state.interpolateStopTimes) + this.setState({interpolateStopTimes: false}) } _onChangeStop = (evt: SyntheticInputEvent) => { @@ -37,7 +38,7 @@ export default class NormalizeStopTimesModal extends Component { } _onClose = () => { - this.setState({ show: false }) + this.setState({ show: false, interpolateStopTimes: false }) this.props.onClose() }