diff --git a/configurations/default/env.yml.tmp b/configurations/default/env.yml.tmp index bd48088ad..efcad4a54 100644 --- a/configurations/default/env.yml.tmp +++ b/configurations/default/env.yml.tmp @@ -6,6 +6,7 @@ MAP_BASE_URL: optional-map-tile-url MAPBOX_ACCESS_TOKEN: your-mapbox-access-token MAPBOX_MAP_ID: mapbox/outdoors-v11 MAPBOX_ATTRIBUTION: © Mapbox © OpenStreetMap Improve this map +# MAP_BASE_URL: http://tile.openstreetmap.org/{z}/{x}/{y}.png # Uncomment it if maps are gray SLACK_CHANNEL: optional-slack-channel SLACK_WEBHOOK: optional-slack-webhook GRAPH_HOPPER_KEY: your-graph-hopper-key @@ -23,4 +24,4 @@ GRAPH_HOPPER_KEY: your-graph-hopper-key # - 83 GOOGLE_ANALYTICS_TRACKING_ID: optional-ga-key # GRAPH_HOPPER_POINT_LIMIT: 10 # Defaults to 30 -DISABLE_AUTH: true \ No newline at end of file +DISABLE_AUTH: true diff --git a/docs/dev/deployment.md b/docs/dev/deployment.md index 7a2376a16..7bbda2f57 100644 --- a/docs/dev/deployment.md +++ b/docs/dev/deployment.md @@ -244,6 +244,8 @@ Enables the GTFS Editor module. - `MAPBOX_ACCESS_TOKEN` - `R5_URL` (optional parameter for r5 routing in editor pattern drawing) +**Note:** If maps are gray, add the property `MAP_BASE_URL: http://tile.openstreetmap.org/{z}/{x}/{y}.png` into `env.yml`. + ### R5 network validation While the application handles basic validation even without the `r5_network` @@ -320,4 +322,4 @@ extensions: enabled: true api: http://api.transitfeeds.com/v1/getFeeds key: your-api-key -``` \ No newline at end of file +``` diff --git a/gtfs.yml b/gtfs.yml index bb59940f3..12c6afa8a 100644 --- a/gtfs.yml +++ b/gtfs.yml @@ -650,10 +650,20 @@ inputType: TIME columnWidth: 6 - name: "stop_id" - required: true + required: false inputType: GTFS_STOP columnWidth: 6 helpContent: "The stop_id field contains an ID that uniquely identifies a stop. Multiple routes may use the same stop. The stop_id is referenced from the stops.txt file. If location_type is used in stops.txt, all stops referenced in stop_times.txt must have location_type of 0." + - name: "location_id" + required: false + inputType: GTFS_ID + columnWidth: 6 + helpContent: "The location_id field contains an ID that uniquely identifies a location. Multiple routes may use the same stop. The stop_id is referenced from the stops.txt file. If location_type is used in stops.txt, all stops referenced in stop_times.txt must have location_type of 0." + - name: "location_group_id" + required: false + inputType: TEXT + columnWidth: 6 + helpContent: "The location_group_id field contains an ID that uniquely identifies a location group. Multiple routes may use the same stop. The stop_id is referenced from the stops.txt file. If location_type is used in stops.txt, all stops referenced in stop_times.txt must have location_type of 0." - name: "stop_sequence" required: true inputType: POSITIVE_INT @@ -1162,6 +1172,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 +1198,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" + diff --git a/i18n/polish.yml b/i18n/polish.yml index f3e9f8e05..e5b313f0e 100644 --- a/i18n/polish.yml +++ b/i18n/polish.yml @@ -381,6 +381,7 @@ components: autoPublish: Automatycznie publikuj dateFormat: MMM D, YYYY deployable: Rozmieszczany + flex: Flexible service edit: Edytować lastUpdatedDate: Last updated %date% noUpdates: No updates @@ -632,6 +633,10 @@ components: title: Real-time updaters walkSpeed: Walk Speed GeneralSettings: + flex: + title: Flex Service + checkbox: Feed includes flex service + description: Enable this feed source to include flex service. Checking this checkbox will enable flex tools in the editor. Flex service describes operations that follow a schedule, but also include flexible features such as dial-a-ride service, route deviation, continuous stops, or point to zone service. autoFetch: checkbox: Auto fetch feed source hint: Set this feed source to fetch automatically. (Feed source URL must be @@ -744,6 +749,8 @@ components: stop: label: Stops title: Edit stops + GtfsServiceSelect: + placeholder: Select existing service ID GtfsValidationExplorer: accessibilityValidation: Accessibility Explorer table: diff --git a/lib/editor/actions/active.js b/lib/editor/actions/active.js index 12c9925a3..ed2437653 100644 --- a/lib/editor/actions/active.js +++ b/lib/editor/actions/active.js @@ -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 diff --git a/lib/editor/actions/editor.js b/lib/editor/actions/editor.js index dd55830b8..dfede8f6c 100644 --- a/lib/editor/actions/editor.js +++ b/lib/editor/actions/editor.js @@ -77,8 +77,6 @@ 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})), - patternLocations: pattern.patternLocations.map(pl => ({...pl, patternId: newPatternId})), patternStops: pattern.patternStops.map(ps => ({...ps, patternId: newPatternId})) } case 'route': @@ -480,9 +478,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 diff --git a/lib/editor/actions/location.js b/lib/editor/actions/location.js index 9266005d9..de4abb8d8 100644 --- a/lib/editor/actions/location.js +++ b/lib/editor/actions/location.js @@ -8,6 +8,71 @@ import type {GtfsLocation} from '../../types' import { receivedNewEntity, savedGtfsEntity } from './active' +// FLEX TODO: The two big dispatch blocks are almost the same... Would it be possible to extract them (such as saveEntity('locationgroup', locationGroupUrl) and same for locationgroupstop)? +export function saveLocationGroup ( + feedId: ?string, + locationGroup: GtfsLocation, + 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({ + editor: true, + id: savedEntity.id, + namespace, + replaceNew: !notNew, + type: 'locationgroupstop' + })) + } 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({ + editor: true, + id: savedEntity.id, + namespace, + replaceNew: !notNew, + type: 'locationgroup' + })) + } else { + // Push new entity into store. + dispatch(receivedNewEntity({component: 'locationgroup', entity: savedEntity})) + Promise.resolve(savedEntity) + } + }) + } +} + export function saveLocation ( feedId: ?string, location: GtfsLocation, diff --git a/lib/editor/actions/map/stopStrategies.js b/lib/editor/actions/map/stopStrategies.js index 7958cd8ec..59178e0a4 100644 --- a/lib/editor/actions/map/stopStrategies.js +++ b/lib/editor/actions/map/stopStrategies.js @@ -8,6 +8,9 @@ import lineSliceAlong from '@turf/line-slice-along' import lineSlice from 'turf-line-slice' import lineString from 'turf-linestring' import point from 'turf-point' +import turfPolygon from 'turf-polygon' +// $FlowFixMe flow doesn't understand new packages +import centerOfMass from '@turf/center-of-mass' import {updateActiveGtfsEntity, saveActiveGtfsEntity} from '../active' import {updatePatternStops} from '../tripPattern' @@ -33,8 +36,6 @@ import {stopToGeoJSONPoint, } from '../../util/map' import type {ControlPoint, Coordinates, GtfsLocation, GtfsStop, LatLng, Pattern, PatternStop, StopControlPoint} from '../../../types' import type {dispatchFn, getStateFn} from '../../../types/reducers' -import { mergePatternHalts, mergePatternHaltsOfPattern } from '../../../gtfs/util' -import { patternHaltIsLocation, patternHaltIsLocationGroup, patternHaltIsStop } from '../../util/location' /** * Creates a new stop at click location (leaflet latlng) and extends the pattern @@ -207,8 +208,7 @@ export function addStopAtInterval (latlng: LatLng, activePattern: Pattern, contr const stopControlPoint = result.controlPoints[controlPoints.length + index] const patternStop = stopToPatternStop(s) // Set pattern stop's shape dist traveled. - if (patternHaltIsStop(patternStop)) { - // $FlowFixMe flow doesn't recognize the type check + if (patternStop.stopId !== null || patternStop.locationId !== null || patternStop.locationGroupId !== null) { patternStop.shapeDistTraveled = stopControlPoint.distance } patternStops.push(patternStop) @@ -231,50 +231,60 @@ export function addStopAtInterval (latlng: LatLng, activePattern: Pattern, contr } } -export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocation, index?: ?number) { +export function addStopToPattern (pattern: Pattern, origStop: GtfsStop | GtfsLocation, index?: ?number) { // eslint-disable-next-line complexity return async function (dispatch: dispatchFn, getState: getStateFn) { const {data, editSettings} = getState().editor const {avoidMotorways, followStreets} = editSettings.present const { - patternLocationGroups: currentPatternLocationGroups, - patternLocations: currentPatternLocations, patternStops: currentPatternStops, shapePoints } = pattern - const patternLocations = clone(currentPatternLocations) - const patternLocationGroups = clone(currentPatternLocationGroups) const patternStops = clone(currentPatternStops) const {controlPoints, patternSegments} = getControlPoints(getState()) const hasShapePoints = shapePoints && shapePoints.length > 1 - let patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroups) + + // $FlowFixMe this is a bit of a hack to get locations to work with methods built for stops. We do the work required to have all methods work. + const stop: GtfsStop = clone(origStop) + + if (stop.stop_id === undefined || stop.stop_id === null) { + // convert stop to turf polygon + const polygon = stop.location_shapes.map(ls => [ls.geometry_pt_lat, ls.geometry_pt_lon]) + + // find center to simulate stop + const center = centerOfMass(turfPolygon([polygon])) + const {coordinates} = center.geometry ? center.geometry : {coordinates: false} + if (coordinates) { + stop.stop_lon = coordinates[1] + stop.stop_lat = coordinates[0] + } + } + + const {stop_lon: lng, stop_lat: lat} = stop + const newStop = stopToPatternStop( stop, (typeof index === 'undefined' || index === null) - ? patternHalts.length + ? patternStops.length : index ) - if (patternHaltIsStop(newStop)) { - patternStops.push(patternHaltIsStop(newStop)) - pattern.shapeId = generateUID() + if (index === undefined) { + patternStops.push(newStop) } - if (patternHaltIsLocationGroup(newStop)) patternLocationGroups.push(patternHaltIsLocationGroup(newStop)) - if (patternHaltIsLocation(newStop)) patternLocations.push(patternHaltIsLocation(newStop)) + pattern.shapeId = generateUID() - 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) + // FLEX TODO: is this stuff all messed up by moving away from pattern halts? + if (typeof index === 'undefined' || index === null || index === patternStops.length) { + if (hasShapePoints) { + // Push pattern stop to cloned list. // console.log('extending pattern to new stop', stop) // If a pattern shape already exists, extend it from the current end // point to the new stop. const {shapePtLon, shapePtLat} = shapePoints[shapePoints.length - 1] const currentEndPoint = ll.toLeaflet([shapePtLon, shapePtLat]) - const {stop_lon: lng, stop_lat: lat} = stop + // Extend pattern to the new point. return dispatch(extendPatternToPoint(pattern, currentEndPoint, {lng, lat}, stop)) .then(result => { @@ -283,31 +293,26 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio // not cause issues when the pattern stops quantity does not match // the control points. TODO: add optional pattern stops to update pattern // geometry, so that these are more closely bound. - dispatch(updatePatternStops(pattern, patternHalts)) + dispatch(updatePatternStops(pattern, patternStops)) dispatch(updatePatternGeometry(result)) return dispatch(saveActiveGtfsEntity('trippattern')) }) } else { - // Push pattern location to cloned list. - patternHalts = mergePatternHalts(patternStops, patternLocations, patternLocationGroups) - - dispatch(updatePatternStops(pattern, patternHalts)) + dispatch(updatePatternStops(pattern, patternStops)) // Only a stop should be checked, not a location // Otherwise, check if a shape ought to be created. Then, save. - if (patternHalts.length === 2 && followStreets && !!stop.stop_lon && !!stop.stop_lat && - // FLEX TODO: How to do this for locations? - // This duplicated check doesn't appear to make much sense, but since there are two places - // this method is called from, each of which provides a different stop object. - (stop.hasOwnProperty('stop_id') || stop.hasOwnProperty('stopId')) - ) { + // FLEX TODO: Is this working? Why 2? + if (patternStops.length === 2 && followStreets) { // Create shape between stops the added stop is the second one and // followStreets is enabled. Otherwise, there is no need to create a // new shape because it would just be a straight line segment anyways. - const previousStopId = patternStops[patternStops.length - 2].stopId + const previousStopId = patternStops[patternStops.length - 2].stopId || patternStops[patternStops.length - 2].locationId || patternStops[patternStops.length - 2].locationGroupId const stops = getTableById(data.tables, 'stop') - const previousStop = stops.find(s => s.stop_id === previousStopId) + const locations = getTableById(data.tables, 'location') + const locationgroups = getTableById(data.tables, 'locationgroup') + const previousStop = stops.find(s => s.stop_id === previousStopId) || locations.find(s => s.location_id === previousStopId) || locationgroups.find(s => s.location_group_id === previousStopId) if (!previousStop) { - throw new Error(`Stop not found for stop_id ${previousStopId}.`) + throw new Error(`Stop not found for stop_id ${previousStopId || '(no previous stop id)'}.`) } const points = [previousStop, stop] .map((stop, index) => ({lng: stop.stop_lon, lat: stop.stop_lat})) @@ -321,10 +326,10 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio } } else if (index && index > 0) { // If adding stop in middle, splice the stop into the array. - patternHalts.splice(index, 0, newStop) - dispatch(updatePatternStops(pattern, patternHalts)) + patternStops.splice(index, 0, newStop) + dispatch(updatePatternStops(pattern, patternStops)) // Checking for stop_id ensures we are dealing with a stop and not a location - if (hasShapePoints && !!stop.stop_id) { + if (hasShapePoints) { // Update shape if it exists. No need to update anything besides pattern // stops (which already occurred above) if there is no shape. NOTE: the // behavior in this code block essentially replaces any @@ -372,6 +377,8 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio insertPoint, { stopId: stop.stop_id, + locationGroupId: stop.location_group_id, + locationId: stop.location_id, pointType: POINT_TYPE.STOP // 2 }) // Instead of splicing at the previousStopControlPoint, splice immediately after. @@ -413,14 +420,16 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop | GtfsLocatio return dispatch(saveActiveGtfsEntity('trippattern')) } else { // Handle adding stop to beginning of pattern. - patternHalts.splice(0, 0, newStop) - dispatch(updatePatternStops(pattern, patternHalts)) + patternStops.splice(0, 0, newStop) + dispatch(updatePatternStops(pattern, patternStops)) // Checking for stop_id ensures we don't process locations - if (hasShapePoints && !!stop.stop_id) { + if (hasShapePoints) { // Update shape if coordinates already exist. const clonedControlPoints = clone(controlPoints) const controlPoint = newControlPoint(0, stopToPoint(stop), { stopId: stop.stop_id, + locationId: stop.location_id, + locationGroupId: stop.location_group_id, pointType: POINT_TYPE.STOP // 2 }) clonedControlPoints.splice(index, 0, controlPoint) @@ -547,9 +556,9 @@ export function removeStopFromPattern (pattern: Pattern, stop: GtfsLocation | Gt const {controlPoints, patternSegments} = getControlPoints(getState()) const clonedControlPoints = clone(controlPoints) const clonedPatternSegments = clone(patternSegments) - const {shapePoints} = pattern - const patternHalts = mergePatternHaltsOfPattern(pattern) - const stopIsStop = stop.hasOwnProperty('stopId') + const {shapePoints, patternStops} = pattern + // $FlowFixMe Flow doesn't understand this property check + const stopIsStop = stop.hasOwnProperty('stopId') && stop.stopId !== null // Use control points to determine control point index for removed stop. // This is distinct from the index arg supplied because there may be more // control points than pattern stops, causing a mismatch in the indexes. @@ -586,9 +595,9 @@ export function removeStopFromPattern (pattern: Pattern, stop: GtfsLocation | Gt dispatch(setErrorMessage({message: `Could not remove stop from pattern:`})) return } - patternHalts.splice(index, 1) + patternStops.splice(index, 1) // Update the shape_dist_traveled values if we're removing the first stop (no control point will be left in this case) - if (index === 0) updateShapeDistTraveled(result.updatedControlPoints, result.coordinates, patternHalts) + if (index === 0) updateShapeDistTraveled(result.updatedControlPoints, result.coordinates, patternStops) // Update pattern geometry dispatch(updatePatternGeometry({ @@ -597,8 +606,8 @@ export function removeStopFromPattern (pattern: Pattern, stop: GtfsLocation | Gt })) } // Update pattern stops (whether or not geometry exists) - patternHalts.splice(index, 1) - dispatch(updatePatternStops(pattern, patternHalts)) + patternStops.splice(index, 1) + dispatch(updatePatternStops(pattern, patternStops)) dispatch(saveActiveGtfsEntity('trippattern')) } } diff --git a/lib/editor/actions/trip.js b/lib/editor/actions/trip.js index 11e556657..06dd9a2be 100644 --- a/lib/editor/actions/trip.js +++ b/lib/editor/actions/trip.js @@ -63,7 +63,7 @@ export const updateCellValue = createAction( (payload: { key: string, rowIndex: number, - value: ?(number | string | { stopId: string }) + value: ?(number | string | { stopId: ?string }) }) => payload ) @@ -120,6 +120,8 @@ export function fetchTripsForCalendar ( service_id stopTimes: stop_times (limit: -1) { stopId: stop_id + locationId: location_id + locationGroupId: location_group_id stopSequence: stop_sequence arrivalTime: arrival_time departureTime: departure_time @@ -133,10 +135,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 } } } diff --git a/lib/editor/actions/tripPattern.js b/lib/editor/actions/tripPattern.js index faa802f1e..3fa410b3a 100644 --- a/lib/editor/actions/tripPattern.js +++ b/lib/editor/actions/tripPattern.js @@ -12,10 +12,8 @@ import {fetchGTFSEntities, receiveGTFSEntities} from '../../manager/actions/vers import {getEditorNamespace} from '../util/gtfs' import {resequenceShapePoints, resequenceStops} from '../util/map' import {entityIsNew} from '../util/objects' -import type { ControlPoint, Pattern, PatternHalt } from '../../types' +import type { ControlPoint, Pattern, PatternStop } from '../../types' import type {dispatchFn, getStateFn} from '../../types/reducers' -import { mergePatternHaltsOfPattern } from '../../gtfs/util' -import { patternHaltIsLocation, patternHaltIsLocationGroup, patternHaltIsStop } from '../util/location' import {fetchTripCounts} from './trip' import {showEditorModal} from './editor' @@ -79,31 +77,16 @@ export function normalizeStopTimes (patternId: number, beginStopSequence: number */ export function updatePatternStops ( pattern: Pattern, - patternHalts: Array + patternStops: Array ) { return function (dispatch: dispatchFn, getState: getStateFn) { - const sortedPatternHalts = patternHalts.map(resequenceStops) - const {stops, locationGroups, locations} = sortedPatternHalts.reduce( - (acc, cur) => { - if (patternHaltIsLocation(cur)) { - acc.locations.push(cur) - } - if (patternHaltIsLocationGroup(cur)) { - acc.locationGroups.push(cur) - } - if (patternHaltIsStop(cur)) { - acc.stops.push(cur) - } - return acc - }, - { stops: [], locations: [], locationGroups: [] } - ) + const sortedPatternStops = patternStops && patternStops.map(resequenceStops) dispatch( updateActiveGtfsEntity( { component: 'trippattern', entity: pattern, - props: { patternStops: stops, patternLocations: locations, patternLocationGroups: locationGroups } + props: { patternStops: sortedPatternStops } } ) ) @@ -210,13 +193,7 @@ export function saveTripPattern (feedId: ?string, tripPattern: Pattern) { // so we prevent that here. // NOTE: This must be applied before snake case-ing (because // resequenceShapePoints updates the camelCase field shapePtSequence). - const patternHalts = mergePatternHaltsOfPattern(tripPattern) - .map(resequenceStops) - tripPattern.patternStops = patternHalts.filter(patternHaltIsStop) - // $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.patternStops = tripPattern.patternStops.map(resequenceStops) 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 diff --git a/lib/editor/components/EditorInput.js b/lib/editor/components/EditorInput.js index 63e4737f9..10467ea7d 100644 --- a/lib/editor/components/EditorInput.js +++ b/lib/editor/components/EditorInput.js @@ -67,13 +67,14 @@ export default class EditorInput extends React.Component { */ _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, @@ -282,6 +283,7 @@ export default class EditorInput extends React.Component { onChange={this._onHourMinuteInputChange} seconds={value && value * 60} standaloneInput + showSeconds={false} /> ) diff --git a/lib/editor/components/EntityDetails.js b/lib/editor/components/EntityDetails.js index 5c2bba806..57ea4901e 100644 --- a/lib/editor/components/EntityDetails.js +++ b/lib/editor/components/EntityDetails.js @@ -10,7 +10,7 @@ import * as mapActions from '../actions/map' import ActiveTripPatternList from '../containers/ActiveTripPatternList' import { getZones, getEditorTable, canApproveGtfs } from '../util' import { getTableById } from '../util/gtfs' -import type {Entity, Feed, GtfsSpecField, GtfsStop, Pattern, Project} from '../../types' +import type {Entity, Feed, GtfsSpecField, GtfsStop, GtfsLocation, Pattern, Project} from '../../types' import type {EditorTables, ManagerUserState, MapState} from '../../types/reducers' import type {EditorValidationIssue} from '../util/validation/common' @@ -25,6 +25,8 @@ type Props = { activeEntity: Entity, activeEntityId: number, activePattern: Pattern, + activePatternLocationGroups: Array, + activePatternLocations: Array, activePatternStops: Array, deleteEntity: typeof activeActions.deleteGtfsEntity, entities: Array, @@ -162,7 +164,7 @@ export default class EntityDetails extends Component {
{/* Editor Inputs */} - {renderDefault && currentTable.fields + {renderDefault && [...currentTable.fields, ...(currentTable.extraFields || [])] .map((field, i) => (
{
) + }
diff --git a/lib/editor/components/EntityDetailsHeader.js b/lib/editor/components/EntityDetailsHeader.js index 4a6bd2b49..d5fcd5663 100644 --- a/lib/editor/components/EntityDetailsHeader.js +++ b/lib/editor/components/EntityDetailsHeader.js @@ -145,7 +145,7 @@ class EntityDetailsHeader extends Component { : mapState.target === subEntity // prevent zooming to empty trip pattern const patterns = getTripPatterns(activeEntity) - const emptyTripPattern = patterns.some(pattern => pattern.patternLocations.length === 0 && pattern.patternStops.length === 0) + const emptyTripPattern = patterns.some(pattern => pattern.patternStops.length === 0 && pattern.patternStops.length === 0) // flex zoom available only when a trip pattern is actually rendered on the map const tripPatternRendered = activeEntity.tripPatterns && (this.props.params ? !this.props.params.subEntityId : true) diff --git a/lib/editor/components/ExceptionDate.js b/lib/editor/components/ExceptionDate.js index e40052ab6..571cca8ee 100644 --- a/lib/editor/components/ExceptionDate.js +++ b/lib/editor/components/ExceptionDate.js @@ -9,7 +9,7 @@ import {toast} from 'react-toastify' import {updateActiveGtfsEntity} from '../actions/active' import type {ScheduleException} from '../../types' -import type {EditorValidationIssue} from '../util/validation' +import type {EditorValidationIssue} from '../util/validation/common' import { getComponentMessages } from '../../common/util/config' import {sortDates} from './ExceptionDateRange' diff --git a/lib/editor/components/ExceptionDateRange.js b/lib/editor/components/ExceptionDateRange.js index f3c627426..3c25d76e3 100644 --- a/lib/editor/components/ExceptionDateRange.js +++ b/lib/editor/components/ExceptionDateRange.js @@ -9,7 +9,7 @@ import {updateActiveGtfsEntity} from '../actions/active' import { getComponentMessages } from '../../common/util/config' import {modifyRangeOfDates, updateDates} from '../../common/util/exceptions' import type {ExceptionDate, ScheduleException, ScheduleExceptionDateRange} from '../../types' -import type {EditorValidationIssue} from '../util/validation' +import type {EditorValidationIssue} from '../util/validation/common' import {inputStyleProps} from './ExceptionDate' import ExceptionValidationErrorsList from './ExceptionValidationErrorsList' diff --git a/lib/editor/components/ExceptionValidationErrorsList.js b/lib/editor/components/ExceptionValidationErrorsList.js index 97173a122..d125b94bc 100644 --- a/lib/editor/components/ExceptionValidationErrorsList.js +++ b/lib/editor/components/ExceptionValidationErrorsList.js @@ -3,7 +3,7 @@ import React from 'react' import { getComponentMessages } from '../../common/util/config' -import type { EditorValidationIssue } from '../util/validation' +import type { EditorValidationIssue } from '../util/validation/common' const VALIDATION_ERROR_LIMIT = 3 diff --git a/lib/editor/components/map/AddableLocationsLayer.js b/lib/editor/components/map/AddableLocationsLayer.js index 61e0f1ad9..eece40fcb 100644 --- a/lib/editor/components/map/AddableLocationsLayer.js +++ b/lib/editor/components/map/AddableLocationsLayer.js @@ -24,8 +24,8 @@ type Props = { */ const AddableLocationsLayer = ({activePattern, addStopToPattern, editSettings, locations, mapState}: Props) => { const { bounds, zoom } = mapState - const patternLocationIds = activePattern && activePattern.patternLocations - ? activePattern.patternLocations.map(pl => pl.locationId) + const patternLocationIds = activePattern && activePattern.patternStops + ? activePattern.patternStops.filter(ps => ps.locationId !== null).map(pl => pl.locationId) : [] const showAddableLocations = locations && activePattern && diff --git a/lib/editor/components/map/PatternLocationMarker.js b/lib/editor/components/map/PatternLocationMarker.js index ff7f50650..7534743ec 100644 --- a/lib/editor/components/map/PatternLocationMarker.js +++ b/lib/editor/components/map/PatternLocationMarker.js @@ -10,9 +10,8 @@ import * as stopStrategiesActions from '../../actions/map/stopStrategies' import * as tripPatternActions from '../../actions/tripPattern' import MinuteSecondInput from '../MinuteSecondInput' import PatternStopButtons from '../pattern/PatternStopButtons' -import type {ControlPoint, Feed, GtfsLocation, Pattern, PatternLocation, PatternLocationGroup} from '../../../types' -import { mergePatternHaltsOfPattern } from '../../../gtfs/util' -import { groupLocationShapePoints, patternHaltIsLocation, patternHaltIsStop } from '../../util/location' +import type { ControlPoint, Feed, GtfsLocation, Pattern, PatternStop } from '../../../types' +import { groupLocationShapePoints } from '../../util/location' import PolygonWithLabel from './PolygonWithLabel' @@ -25,7 +24,7 @@ type Props = { index: number, location: GtfsLocation, patternEdited: boolean, - patternLocation: PatternLocation | PatternLocationGroup, + patternStop: PatternStop, removeStopFromPattern: typeof stopStrategiesActions.removeStopFromPattern, saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity, setActiveEntity: typeof activeActions.setActiveEntity, @@ -51,32 +50,30 @@ export default class PatternLocationMarker extends Component { _onChangeTimeInLocation = (value: number) => { const {activePattern, index, updatePatternStops} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - if (patternHalts[index].hasOwnProperty('flexDefaultZoneTime')) { - // $FlowFixMe Flow is not able to understand this - patternHalts[index].flexDefaultZoneTime = value - updatePatternStops(activePattern, patternHalts) + const { patternStops } = activePattern + if (patternStops[index].hasOwnProperty('defaultDwellTime')) { + patternStops[index].defaultDwellTime = value + updatePatternStops(activePattern, patternStops) } else { - console.warn('Tried to update flexDefaultZoneTime on a stop!') + console.warn('Tried to update defaultDwellTime on a stop!') } } _onChangeTravelTime = (value: number) => { const {activePattern, index, updatePatternStops} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - if (patternHalts[index].hasOwnProperty('flexDefaultTravelTime')) { - // $FlowFixMe Flow is not able to understand this - patternHalts[index].flexDefaultTravelTime = value - updatePatternStops(activePattern, patternHalts) + const { patternStops } = activePattern + if (patternStops[index].hasOwnProperty('defaultTravelTime')) { + patternStops[index].defaultTravelTime = value + updatePatternStops(activePattern, patternStops) } else { - console.warn('Tried to update flexDefaultTravelTime on a stop!') + console.warn('Tried to update defaultTravelTime on a stop!') } } _onClick = () => { const {active, index, setActiveStop} = this.props - const {id} = this.props.patternLocation + const {id} = this.props.patternStop if (!active && id) { // The template string is used as string conversion in a way flow approves of setActiveStop({id: `${id}`, index}) @@ -86,7 +83,7 @@ export default class PatternLocationMarker extends Component { } render () { - const {active, index, location, patternLocation} = this.props + const {active, index, location, patternStop} = this.props // If no location is passed, don't attempt to render anything if (!location) return null @@ -103,7 +100,7 @@ export default class PatternLocationMarker extends Component { }) // Don't render a stop - if (!!patternLocation && patternHaltIsStop(patternLocation)) return null + if (!patternStop || patternStop.locationId === null) return null const groupedLocationShapePts = groupLocationShapePoints(location.location_shapes) @@ -115,27 +112,26 @@ export default class PatternLocationMarker extends Component { fillColor={active ? 'blue' : '#666'} fillOpacity={0.25} icon={patternStopIcon} - key={patternHaltIsLocation(patternLocation) ? patternLocation.id : location.id} + key={patternStop.locationId ? patternStop.id : location.id} onClick={this._onClick} opacity={0.6} positions={groupedLocationShapePts[key]} - // $FlowFixMe Flow doesn't understand patternHaltIsLocation - ref={`${patternHaltIsLocation(patternLocation) ? patternLocation.id || patternLocation.locationId : location.id}`} + ref={`${patternStop.locationId ? patternStop.id || patternStop.locationId : location.id}`} tooltip={`${index + 1}`} zIndexOffset={active ? 1000 : 0} >
- {!patternHaltIsLocation(patternLocation) &&

Location Group

} + {patternStop.locationGroupId &&

Location Group

}
{stopName}
{ // If we do not have a patternLocation, do not render patternLocation edit buttons - patternHaltIsLocation(patternLocation) && ( + patternStop.locationId !== null && ( + {...this.props} patternStop={patternStop} stop={location} /> @@ -144,7 +140,7 @@ export default class PatternLocationMarker extends Component { controlId='defaultTravelTime'> Travel time @@ -153,7 +149,7 @@ export default class PatternLocationMarker extends Component { controlId='defaultTimeInLocation'> Time in location diff --git a/lib/editor/components/map/PatternStopMarker.js b/lib/editor/components/map/PatternStopMarker.js index 0aa785da6..2a39dc346 100644 --- a/lib/editor/components/map/PatternStopMarker.js +++ b/lib/editor/components/map/PatternStopMarker.js @@ -10,9 +10,7 @@ import * as stopStrategiesActions from '../../actions/map/stopStrategies' import * as tripPatternActions from '../../actions/tripPattern' import MinuteSecondInput from '../MinuteSecondInput' import PatternStopButtons from '../pattern/PatternStopButtons' -import type {ControlPoint, Feed, GtfsStop, Pattern, PatternLocationGroup, PatternStop} from '../../../types' -import { mergePatternHaltsOfPattern } from '../../../gtfs/util' -import { patternHaltIsLocationGroup, patternHaltIsStop } from '../../util/location' +import type {ControlPoint, Feed, GtfsStop, Pattern, PatternStop} from '../../../types' type Props = { active: boolean, @@ -22,7 +20,7 @@ type Props = { feedSource: Feed, index: number, patternEdited: boolean, - patternStop: PatternStop | PatternLocationGroup, + patternStop: PatternStop, removeStopFromPattern: typeof stopStrategiesActions.removeStopFromPattern, saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity, setActiveEntity: typeof activeActions.setActiveEntity, @@ -45,11 +43,10 @@ export default class PatternStopMarker extends Component { _onChangeDwellTime = (value: number) => { const {activePattern, index, updatePatternStops} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - if (patternHalts[index].hasOwnProperty('defaultDwellTime')) { - // $FlowFixMe Flow is not able to understand this - patternHalts[index].defaultDwellTime = value - updatePatternStops(activePattern, patternHalts) + const patternStops = activePattern.patternStops + if (patternStops[index].hasOwnProperty('defaultDwellTime')) { + patternStops[index].defaultDwellTime = value + updatePatternStops(activePattern, patternStops) } else { console.warn('Tried to update defaultDwellTime on a location!') } @@ -57,11 +54,10 @@ export default class PatternStopMarker extends Component { _onChangeTravelTime = (value: number) => { const {activePattern, index, updatePatternStops} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - if (patternHalts[index].hasOwnProperty('defaultTravelTime')) { - // $FlowFixMe Flow is not able to understand this - patternHalts[index].defaultTravelTime = value - updatePatternStops(activePattern, patternHalts) + const patternStops = activePattern.patternStops + if (patternStops[index].hasOwnProperty('defaultTravelTime')) { + patternStops[index].defaultTravelTime = value + updatePatternStops(activePattern, patternStops) } else { console.warn('Tried to update defaultTravelTime on a location!') } @@ -82,7 +78,7 @@ export default class PatternStopMarker extends Component { const {active, stop, index, patternStop} = this.props // Don't render a location - if (!!patternStop && !patternHaltIsStop(patternStop) && !patternHaltIsLocationGroup(patternStop)) { + if (!!patternStop && patternStop.locationId !== null) { return null } @@ -99,7 +95,7 @@ export default class PatternStopMarker extends Component { return ( {
- {!patternHaltIsStop(patternStop) &&

Location Group

} + {!patternStop.stopId && !patternStop.locationId &&

Location Group

}
{stopName}
{ // If we do not have a patternStop, do not render patternStop edit buttons - patternHaltIsStop(patternStop) && ( + patternStop.stopId !== null && patternStop.locationId !== null && ( @@ -124,8 +120,7 @@ export default class PatternStopMarker extends Component { controlId='defaultTravelTime'> Travel time @@ -134,8 +129,7 @@ export default class PatternStopMarker extends Component { controlId='defaultDwellTime'> Dwell time diff --git a/lib/editor/components/map/PatternStopsLayer.js b/lib/editor/components/map/PatternStopsLayer.js index fbc3f7135..3385c1d43 100644 --- a/lib/editor/components/map/PatternStopsLayer.js +++ b/lib/editor/components/map/PatternStopsLayer.js @@ -10,8 +10,6 @@ import * as tripPatternActions from '../../actions/tripPattern' import {POINT_TYPE} from '../../constants' import type {ControlPoint, Feed, GtfsLocation, GtfsStop, Pattern, PatternStop} from '../../../types' import type {EditSettingsState} from '../../../types/reducers' -import { mergePatternHalts } from '../../../gtfs/util' -import { patternHaltIsLocation, patternHaltIsStop } from '../../util/location' import PatternStopMarker from './PatternStopMarker' import PatternLocationMarker from './PatternLocationMarker' @@ -33,8 +31,7 @@ type Props = { saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity, setActiveEntity: typeof activeActions.setActiveEntity, setActiveStop: typeof tripPatternActions.setActiveStop, - stops: Array, - updatePatternStops: typeof tripPatternActions.updatePatternStops, + updatePatternStops: typeof tripPatternActions.updatePatternStops } export default class PatternStopsLayer extends Component { @@ -47,28 +44,23 @@ export default class PatternStopsLayer extends Component { addStopToPattern, controlPoints, editSettings, - locations, patternSegment, removeStopFromPattern, - setActiveStop, - stops + setActiveStop } = this.props // FIXME: There is an issue here where the patternStop prop refers to the active // pattern stop, but in PatternStopMarker begins to refer to the PatternStop // type. The below destructuring is to satisfy Flow. const {patternStop: activePatternStop, ...otherProps} = this.props - if (!activePatternStops || !activePattern || !editSettings.showStops) { + if ((!activePatternStops && !activePatternLocations && !activePatternLocationGroups) || !activePattern || !editSettings.showStops) { return null } - const {patternLocations, patternLocationGroups, patternStops} = activePattern - + const {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 + patternStops.findIndex(ps => ps.id === activePatternStop.id) === -1 let cpIndex = 0 let psIndex = 0 - const patternStopsWithControlPointIndexes = [] + const patternStopsWithControlPointIndexes = patternStops.filter(ps => ps.locationId !== null || ps.locationGroupId !== null) // Associate pattern stops with control point indices. while (controlPoints[cpIndex]) { if (controlPoints[cpIndex].pointType === POINT_TYPE.STOP) { @@ -85,126 +77,73 @@ export default class PatternStopsLayer extends Component { if (cpIndex < patternStops.length) { console.warn(`Fewer control points (${controlPoints.length}) than pattern stops (${patternStops.length})!`, controlPoints, patternStops) } - const patternHalts = mergePatternHalts(patternStopsWithControlPointIndexes, patternLocations, patternLocationGroups) return (
- {activePatternLocationGroups.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) => { - const potentialStop = stops.find(s => s.stop_id === halt) - if (potentialStop) { - acc.stops.push(potentialStop) - } - - const potentialLocation = locations.find(l => l.location_id === halt) - if (potentialLocation) { - acc.locations.push(potentialLocation) - } - - return acc - }, {stops: [], locations: [], id: patternLocationGroups[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 - return - {activeHalts.stops.map(stop => { - return ( - - ) - })} - {activeHalts.locations.map(location => { - return ( - - ) - })} - - })} - {patternHalts.map((patternStop, index) => { - const ps = patternHaltIsStop(patternStop) - if (ps) { - const { cpIndex, stopId } = ps - const stop = activePatternStops.find((s) => s.stop_id === stopId) - if (!stop) { - console.warn( - `Could not find stop for stopId: ${stopId}`, - activePatternStops - ) - return - } - if ( - editSettings.hideInactiveSegments && - cpIndex && (cpIndex > patternSegment + 1 || cpIndex < patternSegment - 1) - ) { - // Do not render pattern stop if hiding inactive segments and - // pattern stop does not reference one of the adjacent control points. - return null - } + {patternStops.map((patternStop, index) => { + const matchedPatternStop = patternStopsWithControlPointIndexes.find(ps => ps.stopId === patternStop.stopId || ps.locationId === patternStop.locationId || ps.locationGroupId === patternStop.locationGroupId) + const {stopId, locationId, locationGroupId} = patternStop + const cpIndex = matchedPatternStop ? matchedPatternStop.cpIndex : null + const stop = activePatternStops.find(s => s && s.stop_id === stopId) + const location = activePatternLocations.find(l => l && l.location_id === locationId) + const locationGroup = activePatternLocationGroups.find(l => l && l.location_group_id === locationGroupId) + if (!stop && !location && !locationGroup) { + console.log(stop) + // $FlowFixMe + console.warn(`Could not find stop for stopId: ${stopId || locationId || locationGroupId}`, activePatternStops) + return + } + if ( + editSettings.hideInactiveSegments && + cpIndex && + (cpIndex > patternSegment + 1 || cpIndex < patternSegment - 1) + ) { + // Do not render pattern stop if hiding inactive segments and + // pattern stop does not reference one of the adjacent control points. + return null + } + if (stop) { return ( ) } - - const patternLocation = patternHaltIsLocation(patternStop) - if (patternLocation) { - const location = activePatternLocations.find( - (l) => l.location_id === patternLocation.locationId - ) - return ( - - ) + if (location) { + return + } + if (locationGroup) { + // FLEX TODO: support rendering location groups. will be tricky. need to + // grab location group stop data + return <> } })}
diff --git a/lib/editor/components/pattern/AddPatternStopDropdown.js b/lib/editor/components/pattern/AddPatternStopDropdown.js index 993b98e00..e6577e329 100644 --- a/lib/editor/components/pattern/AddPatternStopDropdown.js +++ b/lib/editor/components/pattern/AddPatternStopDropdown.js @@ -5,15 +5,14 @@ import React, {Component} from 'react' import { Button, Dropdown, MenuItem } from 'react-bootstrap' import * as stopStrategiesActions from '../../actions/map/stopStrategies' -import type {GtfsLocation, GtfsStop, Pattern, PatternHalt, Style} from '../../../types' -import { mergePatternHaltsOfPattern } from '../../../gtfs/util' +import type {GtfsLocation, GtfsStop, Pattern, Style, PatternStop} from '../../../types' type Props = { activePattern: Pattern, addStopToPattern: typeof stopStrategiesActions.addStopToPattern, index?: number, // current pattern stop index (if dropdown shown for current pattern stop) label?: string, - patternStop?: PatternHalt, // optional current pattern stop + patternStop?: PatternStop, // optional current pattern stop size?: string, stop: GtfsStop | GtfsLocation, style?: Style @@ -33,13 +32,11 @@ export default class AddPatternStopDropdown extends Component { _matchesStopAtIndex = (index: number): boolean => { const {activePattern, stop} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - const patternStopAtIndex = patternHalts[index] + const patternStopAtIndex = activePattern.patternStops[index] if (!stop || !stop.stop_id) { return false } - // $FlowFixMe Flow can't handle the strange type checking we're doing here return patternStopAtIndex && patternStopAtIndex.stopId === stop.stop_id } @@ -49,8 +46,8 @@ export default class AddPatternStopDropdown extends Component { render () { const {activePattern, index, label, size, stop, style} = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - const lastIndex = patternHalts.length - 1 + const {patternStops} = activePattern + const lastIndex = patternStops.length - 1 // Check that first/last stop is not already set to this stop. let addToEndDisabled = this._matchesStopAtIndex(lastIndex) let addToBeginningDisabled = this._matchesStopAtIndex(0) @@ -83,18 +80,18 @@ export default class AddPatternStopDropdown extends Component { + value={patternStops.length} + eventKey={patternStops.length}> Add to end (default) - {patternHalts && patternHalts.map((s, i) => { + {patternStops && patternStops.map((s, i) => { // addIndex is in "reverse" order. For example: // - 5 pattern stops // - rendering MenuItem 'Insert at stop #4' // - addIndex = 3 = 5 (stops) - 1 (i) - 1 // - nextIndex = 4 // - previousIndex = 2 - const addIndex = patternHalts.length - i - 1 + const addIndex = patternStops.length - i - 1 const nextIndex = addIndex + 1 const previousIndex = addIndex - 1 let disableAdjacent = false diff --git a/lib/editor/components/pattern/CalculateDefaultTimesForm.js b/lib/editor/components/pattern/CalculateDefaultTimesForm.js index a6a6950a6..54b0b113c 100644 --- a/lib/editor/components/pattern/CalculateDefaultTimesForm.js +++ b/lib/editor/components/pattern/CalculateDefaultTimesForm.js @@ -17,7 +17,6 @@ import * as tripPatternActions from '../../actions/tripPattern' import MinuteSecondInput from '../MinuteSecondInput' import {getDistanceScaleFactor, straightLineDistancesBetweenStopAnchors} from '../../util/map' import type {ControlPoint, Pattern} from '../../../types' -import { activePatternHasLocations } from '../../util/location' import NormalizeStopTimesTip from './NormalizeStopTimesTip' @@ -86,7 +85,7 @@ export default class CalculateDefaultTimesForm extends Component { } } } - updatePatternStops(activePattern, [...activePattern.patternLocations, ...activePattern.patternLocationGroups, ...patternStops]) + updatePatternStops(activePattern, [...patternStops]) saveActiveGtfsEntity('trippattern') } @@ -129,7 +128,7 @@ export default class CalculateDefaultTimesForm extends Component {
- {!activePatternHasLocations(activePattern) && + {activePattern.patternStops.filter(ps => ps.stopId === null).length > 0 && diff --git a/lib/editor/components/pattern/EditSchedulePanel.js b/lib/editor/components/pattern/EditSchedulePanel.js index 773450ef5..ccd51013e 100644 --- a/lib/editor/components/pattern/EditSchedulePanel.js +++ b/lib/editor/components/pattern/EditSchedulePanel.js @@ -9,7 +9,6 @@ import * as tripPatternActions from '../../actions/tripPattern' import OptionButton from '../../../common/components/OptionButton' import {ENTITY} from '../../constants' import type {Feed, GtfsRoute, Pattern} from '../../../types' -import { mergePatternHaltsOfPattern } from '../../../gtfs/util' type Props = { activeEntity: GtfsRoute, @@ -94,16 +93,16 @@ export default class EditSchedulePanel extends Component { const { directionId, name, - useFrequency + useFrequency, + patternStops } = activePattern const timetableOptions = [ Use timetables, Use frequencies ] - const patternHalts = mergePatternHaltsOfPattern(activePattern) const editSchedulesDisabled = activePatternId === ENTITY.NEW_ID || - patternHalts.length === 0 + patternStops.length === 0 return (
diff --git a/lib/editor/components/pattern/EditShapePanel.js b/lib/editor/components/pattern/EditShapePanel.js index 2bf24d87b..eb7dbe4bd 100644 --- a/lib/editor/components/pattern/EditShapePanel.js +++ b/lib/editor/components/pattern/EditShapePanel.js @@ -21,7 +21,7 @@ import { getPatternDistance, isValidStopControlPoint } from '../../util/map' -import type {ControlPoint, LatLng, Pattern, GtfsStop} from '../../../types' +import type {ControlPoint, LatLng, Pattern, GtfsStop, GtfsLocation} from '../../../types' import type {EditSettingsUndoState} from '../../../types/reducers' import EditSettings from './EditSettings' @@ -30,6 +30,8 @@ type Props = { activePattern: Pattern, controlPoints: Array, editSettings: EditSettingsUndoState, + locationGroups: Array, + locations: Array, patternSegment: number, resetActiveGtfsEntity: typeof activeActions.resetActiveGtfsEntity, saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity, @@ -98,7 +100,7 @@ export default class EditShapePanel extends Component { .map((s, index) => { const stop = stops.find(st => st.stop_id === s.stopId) if (!stop) { - console.warn(`Could not locate stop with stop_id=${s.stopId}`) + console.warn(`Could not locate stop with stop_id=${s.stopId || '(no stop id found)'}`) return {lng: 0, lat: 0} } return {lng: stop.stop_lon, lat: stop.stop_lat} @@ -122,6 +124,8 @@ export default class EditShapePanel extends Component { saveActiveGtfsEntity, showConfirmModal, stops, + locations, + locationGroups, updateActiveGtfsEntity, updatePatternGeometry } = this.props @@ -131,7 +135,7 @@ export default class EditShapePanel extends Component { body: `Are you sure you would like to delete this trip pattern shape (shape_id: ${shapeId})?`, onConfirm: () => { // FIXME: Do we need to update pattern geometry, too? - updatePatternGeometry(generateControlPointsFromPatternStops(activePattern.patternStops, stops)) + updatePatternGeometry(generateControlPointsFromPatternStops(activePattern.patternStops, stops, locations, locationGroups)) updateActiveGtfsEntity({ component: 'trippattern', entity: activePattern, diff --git a/lib/editor/components/pattern/NormalizeStopTimesModal.js b/lib/editor/components/pattern/NormalizeStopTimesModal.js index 28163daff..3c111e3ef 100644 --- a/lib/editor/components/pattern/NormalizeStopTimesModal.js +++ b/lib/editor/components/pattern/NormalizeStopTimesModal.js @@ -6,8 +6,6 @@ import { Alert, Button, Checkbox, ControlLabel, FormControl, Modal, OverlayTrigg import * as tripPatternActions from '../../actions/tripPattern' import type { GtfsLocation, GtfsStop, Pattern } from '../../../types' -import { mergePatternHaltsOfPattern } from '../../../gtfs/util' -import { patternHaltIsStop, patternHaltIsLocation } from '../../util/location' import { getComponentMessages } from '../../../common/util/config' type Props = { @@ -52,8 +50,8 @@ export default class NormalizeStopTimesModal extends Component { render () { const { Body, Footer, Header, Title } = Modal const { activePattern, locations, stops } = this.props - const patternHalts = mergePatternHaltsOfPattern(activePattern) - const timepoints = activePattern.patternStops.filter(ps => ps.timepoint === 1) + const { patternStops } = activePattern + const timepoints = patternStops.filter(ps => ps.timepoint === 1) const interpolationDisabled = timepoints.length < 2 return ( @@ -71,23 +69,21 @@ export default class NormalizeStopTimesModal extends Component { value={this.state.patternStopIndex} componentClass='select' onChange={this._onChangeStop}> - {patternHalts.map((patternHalt, index) => { + {patternStops.map((patternStop, index) => { let displayName // TODO: Add methods to return patternStop, patternLocation, patternStopArea separately and explicitly - if (patternHaltIsStop(patternHalt)) { - // $FlowFixMe: Flow doesn't recognize that patternHaltIsStop is a type check - const stop = stops.find(s => s.stop_id === patternHalt.stopId) + if (patternStop.stopId !== null) { + const stop = stops.find(s => s.stop_id === patternStop.stopId) if (!stop) return null displayName = stop.stop_name - } else if (patternHaltIsLocation(patternHalt)) { - // $FlowFixMe: Flow doesn't recognize that patternHaltIsLocation is a type check - const location = locations.find(l => l.location_id === patternHalt.locationId) + } else if (patternStop.locationId !== null || patternStop.locationGroupId !== null) { + const location = locations.find(l => l.location_id === patternStop.locationId) if (!location) return null displayName = location.stop_name || location.location_id } else { // TODO: when we have implemented areas for area naming, lookup the display name for the area. // $FlowFixMe: Flow doesn't recognize that above is a type check - displayName = patternHalt.areaId + displayName = patternStop.areaId } return (