Skip to content

Commit 4c07e81

Browse files
committed
fix(ControlPoint): fix draggable behavior for leaflet 1.0.x
1 parent 6b2492f commit 4c07e81

File tree

13 files changed

+175
-91
lines changed

13 files changed

+175
-91
lines changed

lib/editor/actions/map/index.js

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import shp from 'shpjs'
22
import lineSlice from 'turf-line-slice'
33
import point from 'turf-point'
44
import lineDistance from 'turf-line-distance'
5-
import ll from 'lonlng'
5+
import ll from '@conveyal/lonlat'
66

77
import { updateActiveGtfsEntity, saveActiveGtfsEntity } from '../active'
8-
import { handlePatternEdit } from '../../util/map'
8+
import { handlePatternEdit, getControlPointSnap } from '../../util/map'
99
import {polyline as getPolyline} from '../../../scenario-editor/utils/valhalla'
1010

1111
export function updateMapSetting (props) {
@@ -23,6 +23,50 @@ export function addControlPoint (controlPoint, index) {
2323
}
2424
}
2525

26+
export function dragControlPointEnded (index, controlPoint, evt, pattern) {
27+
return {
28+
type: 'HANDLE_CONTROL_POINT_DRAG_END',
29+
index,
30+
controlPoint,
31+
evt,
32+
pattern
33+
}
34+
}
35+
36+
export function handleControlPointDragEnd (index, controlPoint, evt, pattern) {
37+
return function (dispatch, getState) {
38+
dispatch(dragControlPointEnded(index, controlPoint, evt, pattern))
39+
const coordinates = getState().editor.editSettings.patternCoordinates
40+
const { snap, distTraveled } = getControlPointSnap(evt, coordinates)
41+
dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates}}))
42+
dispatch(updateControlPoint(index, snap, distTraveled))
43+
}
44+
}
45+
46+
export function dragControlPoint (latlng, previous, next, pattern) {
47+
return {
48+
type: 'HANDLE_CONTROL_POINT_DRAG',
49+
latlng,
50+
previous,
51+
next,
52+
pattern
53+
}
54+
}
55+
56+
export function handleControlPointDrag (latlng, previous, next, pattern) {
57+
return function (dispatch, getState) {
58+
// dispatch(dragControlPoint(latlng, previous, next, pattern))
59+
const { followStreets, patternCoordinates } = getState().editor.editSettings
60+
const defaultToStraightLine = false
61+
handlePatternEdit(latlng, previous, next, pattern, followStreets, patternCoordinates, defaultToStraightLine)
62+
.then(coords => {
63+
if (coords) {
64+
dispatch(updatePatternCoordinates(coords))
65+
}
66+
})
67+
}
68+
}
69+
2670
export function removingControlPoint (pattern, index, begin, end) {
2771
return {
2872
type: 'REMOVE_CONTROL_POINT',
@@ -37,7 +81,8 @@ export function constructControlPoint (pattern, latlng, controlPoints) {
3781
return function (dispatch, getState) {
3882
// slice line
3983
const beginPoint = point(pattern.shape.coordinates[0])
40-
const clickPoint = point(ll.toCoordinates(latlng))
84+
const coord = ll.toCoordinates(latlng)
85+
const clickPoint = point(coord)
4186
const lineSegment = lineSlice(beginPoint, clickPoint, pattern.shape)
4287

4388
// measure line segment
@@ -70,7 +115,7 @@ export function removeControlPoint (pattern, index, begin, end) {
70115
const { followStreets, patternCoordinates } = getState().editor.editSettings
71116
const coordinates = await handlePatternEdit(null, begin, end, pattern, followStreets, patternCoordinates)
72117
// update pattern
73-
dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: coordinates}}))
118+
dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates}}))
74119
// remove controlPoint
75120
dispatch(removingControlPoint(index))
76121
}

lib/editor/actions/map/stopStrategies.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import along from 'turf-along'
2-
import ll from 'lonlng'
2+
import ll from '@conveyal/lonlat'
33
import lineDistance from 'turf-line-distance'
44
import lineSlice from 'turf-line-slice'
55

@@ -82,7 +82,7 @@ export function addStopAtIntersection (latlng, activePattern) {
8282
: constructPoint(feature.geometry.coordinates[feature.geometry.coordinates.length - 1])
8383
const lineFromPoint = lineSlice(start, end, {type: 'Feature', geometry: shape})
8484
const stopLocation = along(lineFromPoint, distanceFromIntersection / 1000, 'kilometers')
85-
const latlng = ll.toLatlng(stopLocation.geometry.coordinates)
85+
const latlng = ll.toLeaflet(stopLocation.geometry.coordinates)
8686
// const {afterIntersection, intersectionStep, distanceFromIntersection} = getState().editor.editSettings
8787
return dispatch(addStopAtPoint(latlng, false, patternStops.length, activePattern))
8888
}))
@@ -128,7 +128,7 @@ export function addStopAtInterval (latlng, activePattern) {
128128

129129
// add stops along shape at interval (stopInterval)
130130
const position = along(updatedShape, stopDistance, 'meters')
131-
const stopLatlng = ll.toLatlng(position.geometry.coordinates)
131+
const stopLatlng = ll.toLeaflet(position.geometry.coordinates)
132132
latlngList.push(stopLatlng)
133133
}
134134
return Promise.all(latlngList.map(latlng => {
@@ -166,7 +166,7 @@ export function addStopToPattern (pattern, stop, index) {
166166
if (typeof index === 'undefined' || index === null) {
167167
// if shape coordinates already exist, just extend them
168168
if (coordinates) {
169-
const endPoint = ll.toLatlng(coordinates[coordinates.length - 1])
169+
const endPoint = ll.toLeaflet(coordinates[coordinates.length - 1])
170170
patternStops.push(newStop)
171171
dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {patternStops: patternStops}))
172172
// saveActiveGtfsEntity('trippattern')

lib/editor/components/map/ControlPoint.js

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,71 @@
1+
import {Draggable} from 'leaflet'
2+
import throttle from 'lodash.throttle'
13
import React, { Component, PropTypes } from 'react'
24
import { Marker } from 'react-leaflet'
3-
4-
import { handlePatternEdit, handleControlPointDragEnd } from '../../util/map'
5+
import { shallowEqual } from 'react-pure-render'
56

67
export default class ControlPoint extends Component {
78
static propTypes = {
8-
position: PropTypes.array
9+
activePattern: PropTypes.object,
10+
icon: PropTypes.object,
11+
index: PropTypes.number,
12+
handleControlPointDrag: PropTypes.func,
13+
handleControlPointDragEnd: PropTypes.func,
14+
next: PropTypes.object,
15+
permanent: PropTypes.bool,
16+
position: PropTypes.array,
17+
previous: PropTypes.object,
18+
removeControlPoint: PropTypes.func,
19+
updateActiveEntity: PropTypes.func,
20+
updateControlPoint: PropTypes.func,
21+
updatePatternCoordinates: PropTypes.func
922
}
1023
constructor (props) {
1124
super(props)
1225
this.state = {
13-
timer: null,
1426
latlng: null
1527
}
1628
}
29+
shouldComponentUpdate (nextProps) {
30+
// TODO: fix this hack that keeps unknown position change (perhaps react-leaflet is updating it) from triggering
31+
// a component update, which funks with the position of the marker
32+
return !shallowEqual(nextProps.controlPoint.snap, this.props.controlPoint.snap)
33+
}
34+
_onClick = (e) => {
35+
console.log('control point clicked', e)
36+
// only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop)
37+
if (!this.props.permanent) {
38+
this.props.removeControlPoint(this.props.activePattern, this.props.index, this.props.previous, this.props.next)
39+
}
40+
}
41+
_onDrag = () => {
42+
const throttled = throttle(this.handleDrag, 750)
43+
throttled()
44+
}
45+
handleDrag = () => {
46+
const latlng = this.refs.marker.leafletElement.getLatLng()
47+
this.setState({latlng})
48+
this.props.handleControlPointDrag(latlng, this.props.previous, this.props.next, this.props.activePattern)
49+
}
50+
_onDragEnd = (e) => {
51+
this.setState({latlng: null})
52+
this.props.handleControlPointDragEnd(this.props.index, this.props.controlPoint, e, this.props.activePattern)
53+
}
1754
render () {
18-
const { position, icon, previous, next, activePattern, index, permanent, removeControlPoint, updateActiveEntity, updateControlPoint, editSettings } = this.props
19-
const { patternCoordinates, followStreets } = editSettings
55+
if (this.props.index === 1) console.log(this.props)
56+
const { position, icon } = this.props
57+
const {latlng} = this.state
58+
const markerPosition = latlng ? [latlng.lat, latlng.lng] : position
2059
return (
2160
<Marker
22-
position={this.state.latlng || position}
61+
position={markerPosition}
2362
icon={icon}
2463
ref='marker'
2564
zIndexOffset={1000}
2665
draggable
27-
onDragStart={(e) => {
28-
const timerFunction = () => {
29-
const latlng = this.refs.marker.leafletElement.getLatLng()
30-
// console.log(latlng)
31-
handlePatternEdit(latlng, this.props.previous, this.props.next, this.props.activePattern, followStreets, patternCoordinates)
32-
.then(coords => {
33-
this.setState({latlng})
34-
this.props.updatePatternCoordinates(coords)
35-
})
36-
}
37-
timerFunction()
38-
const timer = setInterval(timerFunction, 500)
39-
this.setState({timer})
40-
}}
41-
onDragEnd={(e) => {
42-
// console.log('drag end')
43-
// clear timer
44-
if (this.state.timer) clearInterval(this.state.timer)
45-
const { snap, distTraveled } = handleControlPointDragEnd(e, patternCoordinates)
46-
updateActiveEntity(activePattern, 'trippattern', {shape: {type: 'LineString', coordinates: patternCoordinates}})
47-
updateControlPoint(index, snap, distTraveled)
48-
this.setState({latlng: null})
49-
}}
50-
onClick={(e) => {
51-
console.log('control point clicked', e)
52-
// only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop)
53-
if (!permanent) {
54-
removeControlPoint(activePattern, index, previous, next)
55-
}
56-
}}
66+
onDrag={throttle(this.handleDrag, 500)}
67+
onDragEnd={this._onDragEnd}
68+
onClick={this._onClick}
5769
color='black'
5870
/>
5971
)

lib/editor/components/map/ControlPointsLayer.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,21 @@ export default class ControlPointsLayer extends Component {
3232
const {
3333
stops,
3434
activePattern,
35-
editSettings,
36-
controlPoints
35+
controlPoints,
36+
snapToStops,
37+
handleControlPointDrag,
38+
handleControlPointDragEnd,
39+
removeControlPoint,
40+
updateActiveEntity,
41+
updateControlPoint,
42+
updatePatternCoordinates
3743
} = this.props
3844
return (
39-
<FeatureGroup ref='controlPoints' key='controlPoints'>
40-
{stops && activePattern && activePattern.shape && editSettings.editGeometry && controlPoints
45+
<FeatureGroup ref='controlPoints'>
46+
{stops && activePattern && activePattern.shape && controlPoints
4147
? controlPoints.map((cp, index) => {
4248
// don't include controlPoint on end of segment (for now) or hidden controlPoints
43-
if (cp.stopId && editSettings.snapToStops) {
49+
if (cp.stopId && snapToStops) {
4450
return null
4551
}
4652
const position = cp.point
@@ -57,12 +63,19 @@ export default class ControlPointsLayer extends Component {
5763
<ControlPoint
5864
position={[position.geometry.coordinates[1], position.geometry.coordinates[0]]}
5965
icon={icon}
66+
controlPoint={cp}
6067
key={`controlPoint-${index}`}
6168
index={index}
6269
permanent={cp.permanent}
6370
previous={this.getPrevious(index, controlPoints, activePattern)}
6471
next={this.getNext(index, controlPoints)}
65-
{...this.props}
72+
activePattern={activePattern}
73+
handleControlPointDrag={handleControlPointDrag}
74+
handleControlPointDragEnd={handleControlPointDragEnd}
75+
removeControlPoint={removeControlPoint}
76+
updateActiveEntity={updateActiveEntity}
77+
updateControlPoint={updateControlPoint}
78+
updatePatternCoordinates={updatePatternCoordinates}
6679
/>
6780
)
6881
})

lib/editor/components/map/EditorMap.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,21 @@ export default class EditorMap extends Component {
171171
<DirectionIconsLayer
172172
patternCoordinates={this.props.editSettings.patternCoordinates}
173173
mapState={mapState} />
174-
<ControlPointsLayer
175-
stops={stops}
176-
activePattern={activePattern}
177-
editSettings={editSettings}
178-
handlePatternEdit={this.props.handlePatternEdit}
179-
updateControlPoint={this.props.updateControlPoint}
180-
removeControlPoint={this.props.removeControlPoint}
181-
updateActiveEntity={this.props.updateActiveEntity}
182-
handleControlPointDragEnd={this.props.handleControlPointDragEnd}
183-
updatePatternCoordinates={this.props.updatePatternCoordinates}
184-
controlPoints={this.props.controlPoints}
185-
polyline={activePattern && this.refs[activePattern.id]} />
174+
{editSettings.editGeometry &&
175+
<ControlPointsLayer
176+
stops={stops}
177+
activePattern={activePattern}
178+
snapToStops={editSettings.snapToStops}
179+
handlePatternEdit={this.props.handlePatternEdit}
180+
handleControlPointDrag={this.props.handleControlPointDrag}
181+
handleControlPointDragEnd={this.props.handleControlPointDragEnd}
182+
updateControlPoint={this.props.updateControlPoint}
183+
removeControlPoint={this.props.removeControlPoint}
184+
updateActiveEntity={this.props.updateActiveEntity}
185+
updatePatternCoordinates={this.props.updatePatternCoordinates}
186+
controlPoints={this.props.controlPoints}
187+
polyline={activePattern && this.refs[activePattern.id]} />
188+
}
186189
<PatternStopsLayer
187190
stops={stops}
188191
activePattern={activePattern}

lib/editor/components/map/EditorMapLayersControl.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export default class EditorMapLayersControl extends Component {
3232
}
3333
]
3434
const activeMapLayerIndex = MAP_LAYERS.findIndex(l => l.id === getUserMetadataProperty(user.profile, 'editor.map_id'))
35-
console.log(process.env)
3635
const MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN
3736
const MAPBOX_ATTRIBUTION = process.env.MAPBOX_ATTRIBUTION
3837
return (

lib/editor/containers/ActiveGtfsEditor.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
import {
2222
updateControlPoint,
2323
addControlPoint,
24+
handleControlPointDrag,
25+
handleControlPointDragEnd,
2426
removeControlPoint,
2527
updateMapSetting,
2628
constructControlPoint,
@@ -239,6 +241,8 @@ const mapDispatchToProps = (dispatch, ownProps) => {
239241
removeControlPoint: (index, begin, end, pattern, polyline) => { dispatch(removeControlPoint(index, begin, end, pattern, polyline)) },
240242
updateControlPoint: (index, point, distance) => { dispatch(updateControlPoint(index, point, distance)) },
241243
constructControlPoint: (pattern, latlng, controlPoints) => { dispatch(constructControlPoint(pattern, latlng, controlPoints)) },
244+
handleControlPointDragEnd: (index, controlPoint, evt, pattern) => { dispatch(handleControlPointDragEnd(index, controlPoint, evt, pattern)) },
245+
handleControlPointDrag: (latlng, previous, next, pattern) => { dispatch(handleControlPointDrag(latlng, previous, next, pattern)) },
242246

243247
// EDITOR UI
244248
setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) }

lib/editor/reducers/data.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import update from 'react-addons-update'
22
import clone from 'lodash.clonedeep'
3-
import ll from 'lonlng'
3+
import ll from '@conveyal/lonlat'
44

55
import { stopToGtfs, routeToGtfs, agencyToGtfs, calendarToGtfs, fareToGtfs, gtfsSort } from '../util/gtfs'
66
import { getStopsForPattern } from '../util'

lib/editor/reducers/settings.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ const editSettings = (state = defaultState, action) => {
9292
actions: {$splice: [[action.lastActionIndex, 1]]}
9393
}
9494
switch (action.lastActionType) {
95-
case 'ADD_CONTROL_POINT':
95+
case 'ADD_CONTROL_POINT': // THIS IS AN UNDO ACTION
9696
stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]}
9797
break
98-
case 'UPDATE_CONTROL_POINT':
98+
case 'UPDATE_CONTROL_POINT': // THIS IS AN UNDO ACTION
9999
stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]}
100100
stateUpdate.coordinatesHistory = {$splice: [[action.lastCoordinatesIndex, 1]]}
101101
stateUpdate.patternCoordinates = {$set: action.lastCoordinates}
102102
break
103-
case 'REMOVE_CONTROL_POINT':
103+
case 'REMOVE_CONTROL_POINT': // THIS IS AN UNDO ACTION
104104
stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]}
105105
stateUpdate.coordinatesHistory = {$splice: [[action.lastCoordinatesIndex, 1]]}
106106
stateUpdate.patternCoordinates = {$set: action.lastCoordinates}
@@ -138,7 +138,7 @@ const editSettings = (state = defaultState, action) => {
138138
for (var i = 0; i < controlPoints.length; i++) {
139139
newControlPoints.push(Object.assign({}, controlPoints[i]))
140140
}
141-
const newest = update(newControlPoints, {[action.index]: {point: {$set: action.point}, distance: {$set: action.distance}}})
141+
const newest = update(newControlPoints, {[action.index]: {point: {$set: action.point}, distance: {$set: action.distance}, snap: {$set: Math.random()}}})
142142
return update(state, {
143143
controlPoints: {$push: [newest]},
144144
actions: {$push: [action.type]}

0 commit comments

Comments
 (0)