Skip to content

Location group may 2024 #1015

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

Closed
wants to merge 5 commits into from
Closed
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
24 changes: 18 additions & 6 deletions gtfs.yml
Original file line number Diff line number Diff line change
@@ -1162,6 +1162,16 @@
helpContent: "The stop_url field contains the URL of a web page about a particular location. This should be different from the agency_url and the route_url fields."

# FIXME: helpContent is lifted from https://github.com/MobilityData/gtfs-flex/blob/master/spec/reference.md
- id: locationgroupstop
flex: true
name: location_group_stops.txt
fields:
- name: "location_group_id"
required: true
inputType: TEXT
- name: "stop_id"
required: true
inputType: GTFS_ID
- id: locationgroup
flex: true
name: location_groups.txt
@@ -1178,9 +1188,11 @@
inputType: TEXT
columnWidth: 12
helpContent: "Name of the location group. Must be defined either once, or exhaustively for a single location_group_id."
# TODO: enable validation to match spec (only appear when appropriate)
- name: "location_id"
required: false
inputType: GTFS_STOP_OR_LOCATION_LIST
columnWidth: 12
helpContent: "Identifies a stop or location belonging to the location group."
extraFields:
- name: "stop_id"
required: false
inputType: GTFS_STOP_OR_LOCATION_LIST
columnWidth: 12
helpContent: "Identifies a stop or location belonging to the location group."
activeComponentOverride: "locationgroupstop"

4 changes: 3 additions & 1 deletion lib/editor/actions/active.js
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ import {
removeEditorLock
} from './editor'
import {saveTripPattern} from './tripPattern'
import { saveLocation } from './location'
import { saveLocation, saveLocationGroup } from './location'

export const clearGtfsContent = createVoidPayloadAction('CLEAR_GTFSEDITOR_CONTENT')
export const receivedNewEntity = createAction(
@@ -373,6 +373,8 @@ export function saveActiveGtfsEntity (
return dispatch(saveTripPattern(feedId, (entity: any)))
case 'location':
return dispatch(saveLocation(feedId, (entity: any), refetch))
case 'locationgroup':
return dispatch(saveLocationGroup(feedId, (entity: any), refetch))
default:
// Default method for agencies, stops, routes, fares, calendars.
// Trip patterns and feed info are handled above. Trips are handled in
7 changes: 5 additions & 2 deletions lib/editor/actions/editor.js
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ function getCloneProps (entityId: number, component: string, state: AppState) {
patternId: newPatternId,
shapeId: newShapeId,
shapePoints: pattern.shapePoints.map(sp => ({...sp, shapeId: newShapeId})),
patternLocationGroups: pattern.patternLocationGroups && pattern.patternLocationGroups.map(plg => ({...plg, patternId: newPatternId})),
patternLocationGroupStops: pattern.patternLocationGroupStops && pattern.patternLocationGroupStops.map(plg => ({...plg, patternId: newPatternId})),
patternLocations: pattern.patternLocations.map(pl => ({...pl, patternId: newPatternId})),
patternStops: pattern.patternStops.map(ps => ({...ps, patternId: newPatternId}))
}
@@ -480,9 +480,12 @@ export function fetchBaseGtfs ({
location_groups {
id
location_group_id
location_id
location_group_name
}
location_group_stops {
location_group_id
stop_id
}
feed_info {
id
feed_id
66 changes: 65 additions & 1 deletion lib/editor/actions/location.js
Original file line number Diff line number Diff line change
@@ -4,10 +4,74 @@ import {getMapFromGtfsStrategy, entityIsNew} from '../util/objects'
import { getEditorNamespace } from '../util/gtfs'
import {fetchGTFSEntities} from '../../manager/actions/versions'
import type {dispatchFn, getStateFn} from '../../types/reducers'
import type {GtfsLocation} from '../../types'
import type {GtfsLocationGroup} from '../../types'

import { receivedNewEntity, savedGtfsEntity } from './active'

export function saveLocationGroup (
feedId: ?string,
locationGroup: GtfsLocationGroup,
refetch: ?boolean = true
) {
return function (dispatch: dispatchFn, getState: getStateFn) {
if (!feedId || !locationGroup) {
return
}
// dispatch(savingActiveLocation()) //Update this?

const notNew = !entityIsNew(locationGroup) // Checks if id is -2 or undefined
const method = notNew ? 'put' : 'post'
const idParam = notNew ? `/${locationGroup.id || ''}` : ''
const {sessionId} = getState().editor.data.lock

const mappingStrategy = getMapFromGtfsStrategy('locationGroup')
const data = mappingStrategy(locationGroup)

const locationGroupUrl = `/api/editor/secure/locationgroup${idParam}?feedId=${feedId}&sessionId=${sessionId || ''}`
const locationGroupStopsUrl = `/api/editor/secure/locationgroupstop${idParam}?feedId=${feedId}&sessionId=${sessionId || ''}`

dispatch(secureFetch(locationGroupStopsUrl, method, data))
.then(res => res.json())
.then(savedEntity => {
dispatch(savedGtfsEntity())
const namespace = getEditorNamespace(feedId, getState())
// Refetch entity and replace in store
if (refetch) {
dispatch(fetchGTFSEntities({
namespace,
id: savedEntity.id,
type: 'locationgroupstop',
editor: true,
replaceNew: !notNew
}))
} else {
// Push new entity into store.
dispatch(receivedNewEntity({component: 'locationgroupstop', entity: savedEntity}))
}
})
return dispatch(secureFetch(locationGroupUrl, method, data))
.then(res => res.json())
.then(savedEntity => {
dispatch(savedGtfsEntity())
const namespace = getEditorNamespace(feedId, getState())
// Refetch entity and replace in store
if (refetch) {
dispatch(fetchGTFSEntities({
namespace,
id: savedEntity.id,
type: 'locationgroup',
editor: true,
replaceNew: !notNew
}))
} else {
// Push new entity into store.
dispatch(receivedNewEntity({component: 'locationgroup', entity: savedEntity}))
Promise.resolve(savedEntity)
}
})
}
}

export function saveLocation (
feedId: ?string,
location: GtfsLocation,
12 changes: 6 additions & 6 deletions lib/editor/actions/map/stopStrategies.js
Original file line number Diff line number Diff line change
@@ -237,17 +237,17 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio
const {data, editSettings} = getState().editor
const {avoidMotorways, followStreets} = editSettings.present
const {
patternLocationGroups: currentPatternLocationGroups,
patternLocationGroupStops: currentpatternLocationGroupStops,
patternLocations: currentPatternLocations,
patternStops: currentPatternStops,
shapePoints
} = pattern
const patternLocations = clone(currentPatternLocations)
const patternLocationGroups = clone(currentPatternLocationGroups)
const patternLocationGroupStops = clone(currentpatternLocationGroupStops)
const patternStops = clone(currentPatternStops)
const {controlPoints, patternSegments} = getControlPoints(getState())
const hasShapePoints = shapePoints && shapePoints.length > 1
let patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroups)
let patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroupStops)
const newStop = stopToPatternStop(
stop,
(typeof index === 'undefined' || index === null)
@@ -259,15 +259,15 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio
patternStops.push(patternHaltIsStop(newStop))
pattern.shapeId = generateUID()
}
if (patternHaltIsLocationGroup(newStop)) patternLocationGroups.push(patternHaltIsLocationGroup(newStop))
if (patternHaltIsLocationGroup(newStop)) patternLocationGroupStops.push(patternHaltIsLocationGroup(newStop))
if (patternHaltIsLocation(newStop)) patternLocations.push(patternHaltIsLocation(newStop))

if (typeof index === 'undefined' || index === null || index === patternHalts.length) {
// Checking for stop_lat and stop_lon is how we check if we are dealing with
// a stop or a location
if (hasShapePoints && !!stop.stop_lat && !!stop.stop_lon) {
// Push pattern stop to cloned list.
patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroups)
patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroupStops)

// console.log('extending pattern to new stop', stop)
// If a pattern shape already exists, extend it from the current end
@@ -289,7 +289,7 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio
})
} else {
// Push pattern location to cloned list.
patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroups)
patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroupStops)

dispatch(updatePatternStops(pattern, patternHalts))
// Only a stop should be checked, not a location
4 changes: 0 additions & 4 deletions lib/editor/actions/trip.js
Original file line number Diff line number Diff line change
@@ -133,10 +133,6 @@ export function fetchTripsForCalendar (

pickupBookingRuleId: pickup_booking_rule_id
dropOffBookingRuleId: drop_off_booking_rule_id
meanDurationFactor: mean_duration_factor
meanDurationOffset: mean_duration_offset
safeDurationFactor: safe_duration_factor
safeDurationOffset: safe_duration_offset
}
}
}
4 changes: 2 additions & 2 deletions lib/editor/actions/tripPattern.js
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ export function updatePatternStops (
{
component: 'trippattern',
entity: pattern,
props: { patternStops: stops, patternLocations: locations, patternLocationGroups: locationGroups }
props: { patternStops: stops, patternLocations: locations, patternLocationGroupStops: locationGroups }
}
)
)
@@ -216,7 +216,7 @@ export function saveTripPattern (feedId: ?string, tripPattern: Pattern) {
// $FlowFixMe FLEX TODO: this type check looks good, but flow is complaining? why?
tripPattern.patternLocations = patternHalts.filter(patternHaltIsLocation)
// $FlowFixMe FLEX TODO: this type check looks good, but flow is complaining? why?
tripPattern.patternLocationGroups = patternHalts.filter(patternHaltIsLocationGroup)
tripPattern.patternLocationGroupStops = patternHalts.filter(patternHaltIsLocationGroup)
if (!tripPattern.shapeId && tripPattern.shapePoints && tripPattern.shapePoints.length > 0) {
// If trip pattern has no shape ID (e.g., if the pattern was imported
// without shapes) but it does have shape points, generate a new shape ID
3 changes: 2 additions & 1 deletion lib/editor/components/EditorInput.js
Original file line number Diff line number Diff line change
@@ -67,13 +67,14 @@ export default class EditorInput extends React.Component<Props> {
*/
_processFieldChange = (val: any) => {
const {
activeComponent,
activeEntity,
field,
onChange,
updateActiveGtfsEntity
} = this.props
onChange && onChange(val)
const activeComponent = field.activeComponentOverride || this.props.activeComponent

updateActiveGtfsEntity && activeEntity && updateActiveGtfsEntity({
component: activeComponent,
entity: activeEntity,
3 changes: 2 additions & 1 deletion lib/editor/components/EntityDetails.js
Original file line number Diff line number Diff line change
@@ -162,7 +162,7 @@ export default class EntityDetails extends Component<Props, State> {
<div>
<Form>
{/* Editor Inputs */}
{renderDefault && currentTable.fields
{renderDefault && [...currentTable.fields, ...(currentTable.extraFields || [])]
.map((field, i) => (
<div
key={`${activeComponent}-${activeEntity.id || ''}-${i}`}
@@ -180,6 +180,7 @@ export default class EntityDetails extends Component<Props, State> {
</Form>
</div>
)

}
</div>
</div>
2 changes: 1 addition & 1 deletion lib/editor/components/GtfsEditor.js
Original file line number Diff line number Diff line change
@@ -54,10 +54,10 @@ type Props = ContainerProps & {
activeEntity: Entity,
activeEntityId: number,
activePattern: Pattern,
activePatternLocationGroups: Array<GtfsLocation>,
activePatternLocations: Array<GtfsLocation>,
activePatternStops: Array<GtfsStop>,
activeSubSubEntity: string,
activepatternLocationGroupStops: Array<GtfsLocation>,
addStopAtIntersection: typeof stopStrategiesActions.addStopAtIntersection,
addStopAtInterval: typeof stopStrategiesActions.addStopAtIntersection,
addStopAtPoint: typeof stopStrategiesActions.addStopAtPoint,
4 changes: 2 additions & 2 deletions lib/editor/components/map/EditorMap.js
Original file line number Diff line number Diff line change
@@ -42,9 +42,9 @@ type Props = {
activeEntity: Entity,
activeEntityId: number,
activePattern: Pattern,
activePatternLocationGroups: Array<GtfsLocation>,
activePatternLocations: Array<GtfsLocation>,
activePatternStops: Array<GtfsStop>,
activepatternLocationGroupStops: Array<GtfsLocation>,
addStopAtIntersection: typeof stopStrategiesActions.addStopAtIntersection,
addStopAtInterval: typeof stopStrategiesActions.addStopAtInterval,
addStopAtPoint: typeof stopStrategiesActions.addStopAtPoint,
@@ -336,7 +336,7 @@ export default class EditorMap extends Component<Props, State> {
/>
<PatternStopsLayer
activePattern={activePattern}
activePatternLocationGroups={this.props.activePatternLocationGroups}
activepatternLocationGroupStops={this.props.activepatternLocationGroupStops}
activePatternLocations={this.props.activePatternLocations}
activePatternStops={this.props.activePatternStops}
addStopToPattern={addStopToPattern}
22 changes: 11 additions & 11 deletions lib/editor/components/map/PatternStopsLayer.js
Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@ import PatternLocationMarker from './PatternLocationMarker'

type Props = {
activePattern: Pattern,
activePatternLocationGroups: Array<GtfsLocation>,
activePatternLocations: Array<GtfsLocation>,
activePatternStops: Array<GtfsStop>,
activepatternLocationGroupStops: Array<GtfsLocation>,
addStopToPattern: typeof stopStrategiesActions.addStopToPattern,
controlPoints: Array<ControlPoint>,
editSettings: EditSettingsState,
@@ -41,7 +41,7 @@ export default class PatternStopsLayer extends Component<Props> {
render () {
const {
activePattern,
activePatternLocationGroups,
activepatternLocationGroupStops,
activePatternLocations,
activePatternStops,
addStopToPattern,
@@ -60,12 +60,12 @@ export default class PatternStopsLayer extends Component<Props> {
if (!activePatternStops || !activePattern || !editSettings.showStops) {
return null
}
const {patternLocations, patternLocationGroups, patternStops} = activePattern
const {patternLocations, patternLocationGroupStops, patternStops} = activePattern

const activeStopNotFound = activePatternStop &&
patternStops.findIndex(ps => ps.id === activePatternStop.id) === -1 &&
patternLocations.findIndex(pl => pl.id === activePatternStop.id) === -1 &&
patternLocationGroups.findIndex(plg => plg.id === activePatternStop.id) === -1
patternLocationGroupStops.findIndex(plg => plg.id === activePatternStop.id) === -1
let cpIndex = 0
let psIndex = 0
const patternStopsWithControlPointIndexes = []
@@ -85,10 +85,10 @@ export default class PatternStopsLayer extends Component<Props> {
if (cpIndex < patternStops.length) {
console.warn(`Fewer control points (${controlPoints.length}) than pattern stops (${patternStops.length})!`, controlPoints, patternStops)
}
const patternHalts = mergePatternHalts(patternStopsWithControlPointIndexes, patternLocations, patternLocationGroups)
const patternHalts = mergePatternHalts(patternStopsWithControlPointIndexes, patternLocations, patternLocationGroupStops)
return (
<div id='PatternStops'>
{activePatternLocationGroups.map((locationGroup, index) => {
{activepatternLocationGroupStops.map((locationGroup, index) => {
if (!locationGroup.location_id || locationGroup.location_id.length === 0) return null
const halts = typeof locationGroup.location_id === 'string' ? locationGroup.location_id.split(',') : locationGroup.location_id
const activeHalts = halts.reduce((acc, halt) => {
@@ -103,7 +103,7 @@ export default class PatternStopsLayer extends Component<Props> {
}

return acc
}, {stops: [], locations: [], id: patternLocationGroups[index].id})
}, {stops: [], locations: [], id: patternLocationGroupStops[index].id})
// Render stops and locations separately, but fix the index and patternStop
// to be a location group so that when you click it it opens the location group
// also, disable the buttons in the popup
@@ -114,9 +114,9 @@ export default class PatternStopsLayer extends Component<Props> {
{...otherProps}
active={activePatternStop.id === activeHalts.id || (activeStopNotFound && activePatternStop.index === index)}
addStopToPattern={addStopToPattern}
index={patternLocationGroups[index].stopSequence}
index={patternLocationGroupStops[index].stopSequence}
key={stop.id} // fallback to index if/when id changes
patternStop={patternLocationGroups[index]}
patternStop={patternLocationGroupStops[index]}
removeStopFromPattern={removeStopFromPattern}
setActiveStop={setActiveStop}
stop={stop}
@@ -129,10 +129,10 @@ export default class PatternStopsLayer extends Component<Props> {
{...otherProps}
active={activePatternStop.id === activeHalts.id || (activeStopNotFound && activePatternStop.index === index)}
addStopToPattern={addStopToPattern}
index={patternLocationGroups[index].stopSequence}
index={patternLocationGroupStops[index].stopSequence}
key={location.id} // fallback to index if/when id changes
location={location}
patternLocation={patternLocationGroups[index]}
patternLocation={patternLocationGroupStops[index]}
removeStopFromPattern={removeStopFromPattern}
setActiveStop={setActiveStop}
/>
2 changes: 1 addition & 1 deletion lib/editor/components/pattern/CalculateDefaultTimesForm.js
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ export default class CalculateDefaultTimesForm extends Component<Props, State> {
}
}
}
updatePatternStops(activePattern, [...activePattern.patternLocations, ...activePattern.patternLocationGroups, ...patternStops])
updatePatternStops(activePattern, [...activePattern.patternLocations, ...activePattern.patternLocationGroupStops, ...patternStops])
saveActiveGtfsEntity('trippattern')
}

Loading