Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
23f5fa3
add dropdown menu and todos
josh-willis-arcadis Mar 10, 2025
b809f44
use snap to option to calculate shape
josh-willis-arcadis Mar 10, 2025
356c46e
remove uses of followStreets
josh-willis-arcadis Mar 6, 2025
d69c421
update params sent to rail router
josh-willis-arcadis Mar 7, 2025
ddb4352
use util functions from dev branch
josh-willis-arcadis Mar 11, 2025
69187fb
use type from leaflet
josh-willis-arcadis Mar 11, 2025
d65f69a
use enum and remove magic string
josh-willis-arcadis Mar 19, 2025
b3888af
rename snapToOption to followOption
josh-willis-arcadis Mar 19, 2025
21be59b
disable 'avoidHighways' when street is not selected
josh-willis-arcadis Apr 15, 2025
18dc760
bring in changes from dev branch
josh-willis-arcadis Apr 15, 2025
d4d1ae6
dont delete followStreets logic
josh-willis-arcadis Apr 15, 2025
0e572c5
refactor(editor/util/map.js): bring back followStreets
josh-willis-arcadis Apr 15, 2025
fde3d92
restore use of followStreets
josh-willis-arcadis Apr 15, 2025
e693650
restore use of followStreet in valhalla.js
josh-willis-arcadis Apr 15, 2025
30306a3
restore usage of followStreets
josh-willis-arcadis Apr 16, 2025
c21bc38
add util function from dev
josh-willis-arcadis Apr 16, 2025
2a29438
add followRail to state and use in UI
josh-willis-arcadis Apr 17, 2025
0a1e546
rail routing works!
josh-willis-arcadis Apr 17, 2025
5f822a1
address pr feedback
josh-willis-arcadis Apr 22, 2025
852392d
remove text.js in favor of to-sentence-case.js
josh-willis-arcadis Apr 30, 2025
39722d9
extract followRail and use in polyline call
josh-willis-arcadis Apr 30, 2025
b8c38c1
create on change method for follow option
josh-willis-arcadis Apr 30, 2025
0042619
extract bounds var
josh-willis-arcadis Apr 30, 2025
a2d7022
use followRail when extending pattern
josh-willis-arcadis May 1, 2025
57c4816
call valhalla when followRail is true
josh-willis-arcadis May 2, 2025
0c2ab13
allow snap to rail option only when route_type is 0,1, or 2
josh-willis-arcadis May 5, 2025
9083e6f
sort props
josh-willis-arcadis May 6, 2025
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
6 changes: 4 additions & 2 deletions lib/editor/actions/map/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,15 @@ export function handleControlPointDrag (
patternCoordinates: any
) {
return function (dispatch: dispatchFn, getState: getStateFn) {
const {avoidMotorways, currentDragId, followStreets} = getState().editor.editSettings.present
const {avoidMotorways, currentDragId, followStreets, followRail} = getState().editor.editSettings.present
recalculateShape({
avoidMotorways,
controlPoints,
defaultToStraightLine: false,
dragId: currentDragId,
editType: 'update',
followStreets,
followRail,
index,
newPoint: latlng,
patternCoordinates
Expand Down Expand Up @@ -204,14 +205,15 @@ export function handleControlPointDragEnd (
dispatch(controlPointDragOrEnd())

// recalculate shape for final position
const {avoidMotorways, followStreets} = getState().editor.editSettings.present
const {avoidMotorways, followStreets, followRail} = getState().editor.editSettings.present
recalculateShape({
avoidMotorways,
controlPoints,
defaultToStraightLine: false,
editType: 'update',
index,
followStreets,
followRail,
newPoint: latlng,
patternCoordinates,
snapControlPointToNewSegment: true
Expand Down
14 changes: 8 additions & 6 deletions lib/editor/actions/map/stopStrategies.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export function addStopAtInterval (latlng: LatLng, activePattern: Pattern, contr
export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?number) {
return async function (dispatch: dispatchFn, getState: getStateFn) {
const {data, editSettings} = getState().editor
const {avoidMotorways, followStreets} = editSettings.present
const {avoidMotorways, followStreets, followRail} = editSettings.present
const {patternStops: currentPatternStops, shapePoints} = pattern
const patternStops = clone(currentPatternStops)
const {controlPoints, patternSegments} = getControlPoints(getState())
Expand Down Expand Up @@ -260,7 +260,7 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?num
} else {
dispatch(updatePatternStops(pattern, patternStops))
// Otherwise, check if a shape ought to be created. Then, save.
if (patternStops.length === 2 && followStreets) {
if (patternStops.length === 2 && (followStreets || followRail)) {
// 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.
Expand All @@ -272,7 +272,7 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?num
}
const points = [previousStop, stop]
.map((stop, index) => ({lng: stop.stop_lon, lat: stop.stop_lat}))
const patternSegments = await getPolyline(points, true, avoidMotorways)
const patternSegments = await getPolyline(points, true, avoidMotorways, followRail)
// Update pattern stops and geometry.
const controlPoints = controlPointsFromSegments(patternStops, patternSegments)
dispatch(updatePatternGeometry({controlPoints, patternSegments}))
Expand Down Expand Up @@ -336,6 +336,7 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?num
defaultToStraightLine: false,
editType: 'update',
followStreets,
followRail,
index: spliceIndex,
newPoint: {lng: stop.stop_lon, lat: stop.stop_lat},
snapControlPointToNewSegment: true,
Expand Down Expand Up @@ -377,6 +378,7 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?num
defaultToStraightLine: false,
editType: 'update',
followStreets,
followRail,
index,
newPoint: {lng: stop.stop_lon, lat: stop.stop_lat},
snapControlPointToNewSegment: true,
Expand Down Expand Up @@ -406,12 +408,12 @@ export function addStopToPattern (pattern: Pattern, stop: GtfsStop, index?: ?num
*/
function extendPatternToPoint (pattern, endPoint, newEndPoint, stop = null, splitInterval = 0) {
return async function (dispatch: dispatchFn, getState: getStateFn) {
const {avoidMotorways, followStreets} = getState().editor.editSettings.present
const {avoidMotorways, followStreets, followRail} = getState().editor.editSettings.present
const {controlPoints, patternSegments} = getControlPoints(getState())
const clonedControlPoints = clone(controlPoints)
let newShape
if (followStreets) {
newShape = await getPolyline([endPoint, newEndPoint], false, avoidMotorways)
if (followStreets || followRail) {
newShape = await getPolyline([endPoint, newEndPoint], false, avoidMotorways, followRail)
}
if (!newShape) {
// Get single coordinate for straight line if polyline fails or if not
Expand Down
49 changes: 45 additions & 4 deletions lib/editor/components/pattern/EditSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {Alert, Checkbox, Form, FormControl, ControlLabel} from 'react-bootstrap'
import Rcslider from 'rc-slider'

import {updateEditSetting} from '../../actions/active'
import {CLICK_OPTIONS} from '../../util'
import {CLICK_OPTIONS, SNAP_TO_OPTIONS} from '../../util'
import toSentenceCase from '../../../common/util/to-sentence-case'
import type {EditSettingsState} from '../../../types/reducers'
import type {GtfsRoute} from '../../../types'

type Props = {
activeEntity: GtfsRoute,
editSettings: EditSettingsState,
patternSegment: number,
updateEditSetting: typeof updateEditSetting
Expand Down Expand Up @@ -54,17 +56,27 @@ export default class EditSettings extends Component<Props, State> {
value
})

_onFollowOptionChange = (evt: SyntheticInputEvent<HTMLInputElement>) => {
// Update both settings based on selection
this.props.updateEditSetting({setting: 'followStreets', value: evt.target.value === SNAP_TO_OPTIONS.STREET})
this.props.updateEditSetting({setting: 'followRail', value: evt.target.value === SNAP_TO_OPTIONS.RAIL})
}

render () {
// Edit Settings passed in are present
const {editSettings, patternSegment, updateEditSetting} = this.props
const {activeEntity, editSettings, patternSegment, updateEditSetting} = this.props
const {
editGeometry,
followStreets,
followRail,
onMapClick,
stopInterval
} = editSettings

// Determine the current selection based on followStreets and followRail
const currentSelection = followStreets ? SNAP_TO_OPTIONS.STREET : (followRail ? SNAP_TO_OPTIONS.RAIL : SNAP_TO_OPTIONS.NONE)

const SETTINGS = [
{type: 'followStreets', label: 'Snap to streets'},
{type: 'avoidMotorways', label: 'Avoid highways in routing'},
{type: 'hideStopHandles', label: 'Hide stop handles'},
{type: 'hideInactiveSegments', label: 'Hide inactive segments'},
Expand All @@ -73,8 +85,37 @@ export default class EditSettings extends Component<Props, State> {
]
if (!editGeometry) return null
const noSegmentIsActive = !patternSegment && patternSegment !== 0

// If route_type is rail, add "snap to rail" option
const snapToOptions = Object.keys(SNAP_TO_OPTIONS).reduce((acc, key) => {
if (key === 'RAIL') {
const routeType = activeEntity && activeEntity.route_type
if ([0, 1, 2].includes(routeType)) {
acc[key] = SNAP_TO_OPTIONS[key]
}
} else {
acc[key] = SNAP_TO_OPTIONS[key]
}
return acc
}, {})

return (
<div>
<ControlLabel><small>Snap to</small></ControlLabel>
<FormControl
componentClass='select'
name='followOption'
onChange={this._onFollowOptionChange}
value={currentSelection}
>
{Object.keys(snapToOptions).map(key => (
<option
key={key}
value={snapToOptions[key]}>
{toSentenceCase(key)}
</option>
))}
</FormControl>
{SETTINGS.map((s, i) => (
<Checkbox
checked={editSettings[s.type]}
Expand All @@ -83,7 +124,7 @@ export default class EditSettings extends Component<Props, State> {
// this state would cause the entire shape to disappear).
disabled={
(s.type === 'hideInactiveSegments' && noSegmentIsActive) ||
(s.type === 'avoidMotorways' && !followStreets)
(s.type === 'avoidMotorways' && currentSelection !== SNAP_TO_OPTIONS.STREET)
}
name={s.type}
style={{margin: '3px 0'}}
Expand Down
7 changes: 5 additions & 2 deletions lib/editor/components/pattern/EditShapePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import {
getPatternDistance,
isValidStopControlPoint
} from '../../util/map'
import type {ControlPoint, LatLng, Pattern, GtfsStop} from '../../../types'
import type {ControlPoint, GtfsRoute, LatLng, Pattern, GtfsStop} from '../../../types'
import type {EditSettingsUndoState} from '../../../types/reducers'

import EditSettings from './EditSettings'

type Props = {
activeEntity: GtfsRoute,
activePattern: Pattern,
controlPoints: Array<ControlPoint>,
editSettings: EditSettingsUndoState,
Expand All @@ -41,7 +42,7 @@ type Props = {
undoActiveTripPatternEdits: typeof tripPatternActions.undoActiveTripPatternEdits,
updateActiveGtfsEntity: typeof activeActions.updateActiveGtfsEntity,
updateEditSetting: typeof activeActions.updateEditSetting,
updatePatternGeometry: typeof mapActions.updatePatternGeometry,
updatePatternGeometry: typeof mapActions.updatePatternGeometry
}

export default class EditShapePanel extends Component<Props> {
Expand Down Expand Up @@ -200,6 +201,7 @@ export default class EditShapePanel extends Component<Props> {

render () {
const {
activeEntity,
activePattern,
controlPoints, // FIXME use to describe which segment user is editing
patternSegment,
Expand Down Expand Up @@ -360,6 +362,7 @@ export default class EditShapePanel extends Component<Props> {
</Button>
</ButtonToolbar>
<EditSettings
activeEntity={activeEntity}
editSettings={editSettings}
patternSegment={patternSegment}
updateEditSetting={updateEditSetting} />
Expand Down
8 changes: 6 additions & 2 deletions lib/editor/reducers/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const defaultState = {
distanceFromIntersection: 5,
editGeometry: false,
followStreets: true,
followRail: false,
hideInactiveSegments: false,
intersectionStep: 2,
onMapClick: CLICK_OPTIONS[0],
Expand Down Expand Up @@ -49,7 +50,8 @@ export const reducers = {
...defaultState,
// Do not reset follow streets if exiting pattern editing.
// TODO: Are there other edit settings that should not be overridden?
followStreets: state.followStreets
followStreets: state.followStreets,
followRail: state.followRail
}
},
'SETTING_ACTIVE_GTFS_ENTITY' (
Expand All @@ -58,6 +60,7 @@ export const reducers = {
): EditSettingsState {
// Default for no route type is true (most routes are buses/follow streets)
let followStreets = true
const followRail = false
const {activeEntity, component} = action.payload
// Update value for follow streets when setting active route
if (activeEntity && component === 'route') {
Expand All @@ -70,7 +73,8 @@ export const reducers = {
}
return {
...defaultState,
followStreets
followStreets,
followRail
}
},
'UPDATE_TEMP_PATTERN_GEOMETRY' (
Expand Down
5 changes: 5 additions & 0 deletions lib/editor/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export const CLICK_OPTIONS: Array<string> = [
'ADD_STOPS_AT_INTERVAL',
'ADD_STOPS_AT_INTERSECTIONS'
]
export const SNAP_TO_OPTIONS = {
NONE: 'None',
STREET: 'Street',
RAIL: 'Rail'
}
export const YEAR_FORMAT: string = 'YYYY-MM-DD'
export const EXCEPTION_EXEMPLARS = {
MONDAY: 0,
Expand Down
22 changes: 15 additions & 7 deletions lib/editor/util/map.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import ll from '@conveyal/lonlat'
import {divIcon} from 'leaflet'
import {Bounds, divIcon} from 'leaflet'
import clone from 'lodash/cloneDeep'
import fetch from 'isomorphic-fetch'
import distance from '@turf/distance'
Expand Down Expand Up @@ -44,11 +44,16 @@ type R5Response = {
}>
}

export const stopIsOutOfBounds = (stop: GtfsStop, bounds: any) => {
return stop.stop_lat > bounds.getNorth() ||
stop.stop_lat < bounds.getSouth() ||
stop.stop_lon > bounds.getEast() ||
stop.stop_lon < bounds.getWest()
export const coordIsOutOfBounds = (coords: LatLng, bounds: Bounds) => {
if (!coords || !coords.lat || !coords.lng || !bounds) return true

return coords.lat > bounds.getNorth() ||
coords.lat < bounds.getSouth() ||
coords.lng > bounds.getEast() ||
coords.lng < bounds.getWest()
}
export const stopIsOutOfBounds = (stop: GtfsStop, bounds: Bounds) => {
return coordIsOutOfBounds({lat: stop.stop_lat, lng: stop.stop_lon}, bounds)
}

export const getStopIcon = (
Expand Down Expand Up @@ -264,6 +269,7 @@ export async function recalculateShape ({
dragId,
editType,
followStreets,
followRail = false,
index,
newPoint,
patternCoordinates,
Expand All @@ -274,6 +280,7 @@ export async function recalculateShape ({
defaultToStraightLine?: boolean,
dragId?: null | string,
editType: string,
followRail?: boolean,
followStreets: boolean,
index: number,
newPoint?: LatLng,
Expand Down Expand Up @@ -394,7 +401,8 @@ export async function recalculateShape ({
pointsToRoute,
followStreets,
defaultToStraightLine,
avoidMotorways
avoidMotorways,
followRail
)
if (!newSegment || !newSegment.coordinates) {
// If new segment calculation is unsuccessful, return null for coordinates and
Expand Down
Loading