Skip to content

Commit df8a5bb

Browse files
committed
fix(editor): fix shape point snapping
1 parent 3f33195 commit df8a5bb

File tree

6 files changed

+65
-46
lines changed

6 files changed

+65
-46
lines changed

lib/editor/components/map/ControlPoint.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export default class ControlPoint extends Component {
7070
} = this.props
7171
const latlng = e.latlng
7272
this.setState({latlng})
73+
console.log(`drag to`, latlng)
7374
handleControlPointDrag(controlPoints, index, latlng, activePattern, patternCoordinates)
7475
}, 500)
7576

@@ -94,13 +95,15 @@ export default class ControlPoint extends Component {
9495

9596
render () {
9697
// console.log(this.state)
97-
const {editSettings, icon, position, isActive} = this.props
98+
const {controlPoint, editSettings, icon, position, isActive} = this.props
9899
// keep track of position in state because we need this to be cleared once
99100
// the user has stopped dragging the marker, at which point
100101
// this.state.latlng will be null and the marker will "snap" back to the
101102
// polyline
102103
const {latlng} = this.state
103-
const tooltipMessage = isActive
104+
const tooltipMessage = isActive && controlPoint.stopId
105+
? 'Drag handle to change stop snap point'
106+
: isActive
104107
? 'Drag handle to change shape (click to remove)'
105108
: 'Click to begin editing segment'
106109
const markerPosition = latlng

lib/editor/components/map/PatternsLayer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class PatternSegment extends Component {
166166
patternIsActive,
167167
pattern
168168
} = this.props
169-
// isEditing && segmentIsActive && console.log('rendering pattern', pattern)
169+
// isEditing && segmentIsActive && console.log(`rendering active segment #${index}`, pattern, coordinates)
170170
if (!coordinates || !coordinates.length) {
171171
console.warn(`Could not render segment #${index} of pattern ID ${pattern.id}`, coordinates)
172172
return null

lib/editor/components/pattern/EditShapePanel.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export default class EditShapePanel extends Component {
193193
value={patternSegment - 1}
194194
disabled={!patternSegment || patternSegment < 1}
195195
bsSize='xsmall'>
196-
Previous
196+
<Icon type='square' style={{color: 'blue'}} /> Prev.
197197
</OptionButton>
198198
<small>
199199
{!patternSegment && patternSegment !== 0
@@ -209,7 +209,7 @@ export default class EditShapePanel extends Component {
209209
}
210210
disabled={patternSegment >= controlPoints.length - 1}
211211
bsSize='xsmall'>
212-
Next
212+
<Icon type='square' style={{color: 'yellow'}} /> Next
213213
</OptionButton>
214214
</div>
215215
<ButtonGroup justified>

lib/editor/components/pattern/PatternStopContainer.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import clone from 'lodash.clonedeep'
21
import React, { Component, PropTypes } from 'react'
32
import update from 'react/lib/update'
43
import { shallowEqual } from 'react-pure-render'
54
import { DropTarget, DragDropContext } from 'react-dnd'
65
import HTML5Backend from 'react-dnd-html5-backend'
76

8-
import {POINT_TYPE} from '../../constants'
97
import PatternStopCard from './PatternStopCard'
108

119
/* This component and its child components (Card.js) are based on the react-dnd sortable

lib/editor/selectors/index.js

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import clone from 'lodash.clonedeep'
2-
import oneLine from 'common-tags/lib/oneLine'
2+
// import oneLine from 'common-tags/lib/oneLine'
33
import {List} from 'immutable'
44
import randomColor from 'randomcolor'
55
import SortDirection from 'react-virtualized/dist/commonjs/Table/SortDirection'
@@ -107,7 +107,7 @@ export const getControlPoints = createSelector(
107107
return {patternSegments, controlPoints}
108108
}
109109
if (previousControlPoints && previousControlPoints.length && previousPatternSegments && previousPatternSegments.length) {
110-
console.log('defaulting to state\'s control points/patternSegments', previousControlPoints, previousPatternSegments)
110+
// console.log('defaulting to state\'s control points/patternSegments', previousControlPoints, previousPatternSegments)
111111
return {
112112
controlPoints: previousControlPoints,
113113
patternSegments: previousPatternSegments
@@ -124,7 +124,7 @@ export const getControlPoints = createSelector(
124124
// stop, rather than assuming they're all missing if the first one is.
125125
const projectStops = patternStops[0].shapeDistTraveled === null &&
126126
shapePoints.filter(sp => sp.pointType === POINT_TYPE.STOP) < patternStops.length
127-
console.log('projecting stops, shape dist traveled:', patternStops[0].shapeDistTraveled)
127+
// console.log('projecting stops, shape dist traveled:', patternStops[0].shapeDistTraveled)
128128
// if distance values are null, calculate distance values.
129129
// let newShapePoints = shapePoints
130130
// if (shapePoints.filter(sp => sp.pointType === POINT_TYPE.STOP) < patternStops.length) {
@@ -143,7 +143,7 @@ export const getControlPoints = createSelector(
143143
}
144144
patternSegments = result.patternSegments
145145
controlPoints = getControlPointsFromShapePoints(result.shapePoints)
146-
console.log(controlPoints, patternSegments)
146+
// console.log(controlPoints, patternSegments)
147147
if (controlPoints.length - 1 > patternSegments.length) {
148148
// There are non-stop control points that need to be added in.
149149
for (let i = 0; i < controlPoints.length; i++) {
@@ -152,20 +152,21 @@ export const getControlPoints = createSelector(
152152
// If the following control point is a user-defined anchor, slice
153153
// current line at the following control point and splice into
154154
// array of segments.
155-
console.log(`control point ${i}; pattern segment ${i - 1}`, nextControlPoint, patternSegments[i])
155+
// console.log(`control point ${i}; pattern segment ${i - 1}`, nextControlPoint, patternSegments[i])
156156
const {beforeSlice, afterSlice} = getLineSlices(lineString(patternSegments[i]), nextControlPoint.point)
157157
patternSegments.splice(i, 1, beforeSlice.geometry.coordinates, afterSlice.geometry.coordinates)
158158
}
159159
}
160160
}
161-
console.log(featurecollection([
161+
featurecollection([
162162
...patternSegments.map(ps => {
163163
const lineSegment = lineString(ps)
164164
lineSegment.properties.stroke = randomColor()
165165
return lineSegment
166166
}),
167167
...controlPoints.map(cp => cp.point)
168-
]))
168+
])
169+
// console.log()
169170
} else {
170171
// No pattern stops, return empty array (below).
171172
// FIXME: Return control points from shape points instead?
@@ -208,7 +209,7 @@ export const getControlPoints = createSelector(
208209
}
209210
// console.log('control points', controlPoints)
210211
const sortedControlPoints = controlPoints.sort((a, b) => a.distance - b.distance)
211-
console.log('sorted control points', sortedControlPoints)
212+
// console.log('sorted control points', sortedControlPoints)
212213
return {
213214
controlPoints: sortedControlPoints,
214215
patternSegments
@@ -226,12 +227,12 @@ export const getPatternCoordinates = createSelector(
226227
return null
227228
}
228229
if (patternSegments) {
229-
console.log('using coordinates from history', patternSegments)
230+
// console.log('using coordinates from history', patternSegments)
230231
return patternSegments
231232
}
232-
console.log('using shapepoints', shapePoints)
233+
// console.log('using shapepoints', shapePoints)
233234
// if (shapePoints.filter(sp => sp.pointType).length === 0) {
234-
// console.log('shape points do not contain control points. Merging.')
235+
// // console.log('shape points do not contain control points. Merging.')
235236
// controlPoints.forEach((cp, index) => {
236237
// const itemsToReplace = index < 0 && index < controlPoints.length - 1
237238
// ? 0
@@ -268,13 +269,14 @@ export const getPatternCoordinates = createSelector(
268269
}
269270
splitAndAddSegment(shapePoints, segments, fromIndex, toIndex)
270271
}
271-
let sum = 0
272-
segments.forEach(e => { sum += e.length })
273-
console.log(oneLine`
274-
points: ${sum}; shapepoints: ${shapePoints.length};
275-
cp: ${controlPoints.length}; seg: ${segments.length}
276-
${controlPoints.length === segments.length - 1 ? '✅' : '❌'}
277-
`, featurecollection(segments.map(seg => lineString(seg))))
272+
// let sum = 0
273+
// segments.forEach(e => { sum += e.length })
274+
// const qcString = oneLine`
275+
// points: ${sum}; shapepoints: ${shapePoints.length};
276+
// cp: ${controlPoints.length}; seg: ${segments.length}
277+
// ${controlPoints.length === segments.length - 1 ? '✅' : '❌'}
278+
// `
279+
// console.log(qcString, featurecollection(segments.map(seg => lineString(seg)))
278280
return segments
279281
}
280282
)
@@ -285,7 +287,7 @@ function splitAndAddSegment (shapePoints, segments, fromIndex, toIndex) {
285287
} else if ((toIndex - fromIndex < 2) || (fromIndex === shapePoints.length - 1)) {
286288
console.warn(`Should not slice shapepoints for segment with less than two coordinates (from ${fromIndex} to ${toIndex}).`)
287289
} else {
288-
console.log(`slicing ${shapePoints.length} items from ${fromIndex} to ${toIndex}`)
290+
// console.log(`slicing ${shapePoints.length} items from ${fromIndex} to ${toIndex}`)
289291
}
290292
const nextSegment = shapePoints
291293
.slice(fromIndex, toIndex)
@@ -334,17 +336,17 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
334336
const patternSegments = []
335337
const computedShapePoints = []
336338
// Keep only those shape points that are associated with stops
337-
// (this is an empty array if pattern shape is unedited).
339+
// (this is an empty array if pattern shape has never been edited).
338340
const stopControlPoints = shapePointsCopy.filter(sp => sp.pointType === POINT_TYPE.STOP)
339341
const patternLine = lineString(coordinatesFromShapePoints(shapePointsCopy))
340342
const patternLength = lineDistance(patternLine, 'meters')
341343
const beginPoint = point(patternLine.geometry.coordinates[0])
342344
const endPointIndex = patternLine.geometry.coordinates.length - 1
343345
const endPoint = point(patternLine.geometry.coordinates[endPointIndex])
344346
if (projectStops) {
345-
console.log('Projecting pattern stops onto shape (distances are null)', shapePointsCopy, stopControlPoints)
347+
// console.log('Projecting pattern stops onto shape (distances are null)', shapePointsCopy, stopControlPoints)
346348
} else {
347-
console.log('Generating control points from pattern stops (using pre-existing stop distances)', shapePointsCopy, stopControlPoints)
349+
// console.log('Generating control points from pattern stops (using pre-existing stop distances)', shapePointsCopy, stopControlPoints)
348350
}
349351
let previousDistance = 0
350352
let remainingLine = patternLine
@@ -363,14 +365,14 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
363365
if (i === 0) {
364366
// First stop must always be placed at first latlng coordinate
365367
insertPoint = beginPoint
366-
console.log('first stop placed at begin point')
368+
// console.log('first stop placed at begin point')
367369
insertIndex = 0
368370
distance = 0
369371
itemsToDelete = 1
370372
} else if (i === patternStops.length - 1) {
371373
// Last stop must always be placed at last latlng coordinate
372374
insertPoint = endPoint
373-
console.log('last stop placed at end point')
375+
// console.log('last stop placed at end point')
374376
insertIndex = endPointIndex
375377
distance = patternLength
376378
itemsToDelete = 1
@@ -393,13 +395,13 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
393395
if (i < stopControlPoints.length && stopControlPoints[i] && stopControlPoints[i].shapeDistTraveled !== null) {
394396
// Check shape points for existence of control points. If index is less
395397
// than number of stop control points, skip.
396-
console.log(`skipping index ${i} (control point should exist for stop)`)
398+
// console.log(`skipping index ${i} (control point should exist for stop)`)
397399
stopControlPoints[i].stopId = stopId
398400
distance = stopControlPoints[i].shapeDistTraveled
399401
if (i > 0) {
400402
const sliceDistance = (distance - previousDistance) / 1000
401403
let lineSegment
402-
console.log(remainingLine, sliceDistance, stopControlPoints[i])
404+
// console.log(remainingLine, sliceDistance, stopControlPoints[i])
403405
if (sliceDistance <= 0) {
404406
// If there is no more line segment left, extend line to stop control
405407
// point and TODO signal warning to user?
@@ -417,7 +419,7 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
417419
}
418420
continue
419421
}
420-
console.log(`generating shape point for stop #${i} at distance: ${patternStop.shapeDistTraveled}`, patternStop, remainingLine)
422+
// console.log(`generating shape point for stop #${i} at distance: ${patternStop.shapeDistTraveled}`, patternStop, remainingLine)
421423
if (i === 0 && patternStop.shapeDistTraveled !== 0) {
422424
console.warn(`Distance for first stop is not zero. Coercing to zero.`)
423425
distance = 0
@@ -436,28 +438,32 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
436438
// FIXME: what if this happens not on the first pattern stop
437439
if (i !== 0) console.warn(`shape dist traveled for pattern stop ${i} equals zero. this should not be the case!`)
438440
insertPoint = pointAtFirstCoordinate(remainingLine)
441+
insertIndex = 0
439442
distance = 0
440-
itemsToDelete = 0
443+
itemsToDelete = 1
441444
} else {
442445
// This should cover all cases except the first stop.
443446
const remainingLineDistance = lineDistance(remainingLine, 'meters')
444447
// FIXME this is breaking stuff when the remaining line has only one coordinate!!!
445-
console.log(`remaining line dist = ${remainingLineDistance}`, remainingLine, patternStop.shapeDistTraveled / 1000)
448+
// console.log(`remaining line dist = ${remainingLineDistance}`, remainingLine, patternStop.shapeDistTraveled / 1000)
446449
if (patternStop.shapeDistTraveled === remainingLineDistance) {
447450
// This should only be the case with the last stop
448-
console.log(`pattern stop ${i}/${patternStops.length} distance is equal to remaining lint distance, setting to end of shape`)
451+
// console.log(`pattern stop ${i}/${patternStops.length} distance is equal to remaining lint distance, setting to end of shape`)
449452
// FIXME: what if this happens not on the last pattern stop
450453
insertPoint = pointAtLastCoordinate(remainingLine)
454+
insertIndex = shapePointsCopy.length - 1
451455
distance = remainingLineDistance
452-
itemsToDelete = 0
456+
itemsToDelete = 1
453457
} else {
454458
// This should be the case for all but first and last stops
455-
const lineSegment = lineSliceAlong(remainingLine, 0, patternStop.shapeDistTraveled / 1000)
456-
console.log(`pattern stop ${i}/${patternStops.length} remaining segment and previous dist`, lineSegment, previousDistance)
457-
distance = previousDistance + lineDistance(lineSegment, 'meters')
459+
distance = patternStop.shapeDistTraveled
460+
// Use the pattern line to find the lat/lng to use for the generated
461+
// shape point.
462+
const lineSegment = lineSliceAlong(patternLine, 0, patternStop.shapeDistTraveled / 1000)
458463
insertPoint = pointAtLastCoordinate(lineSegment)
459-
// FIXME: How to determine insert index?
460-
// insertIndex = result.index
464+
// Insert generated shape point in the correct location according to
465+
// its distance along the line.
466+
insertIndex = shapePointsCopy.findIndex(sp => sp.shapeDistTraveled > distance)
461467
itemsToDelete = 0
462468
}
463469
}
@@ -474,22 +480,33 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
474480
// every shape point below in the re-mapping.
475481
}
476482
// Insert closest point into pattern shape
483+
// console.log(`inserting shape point at ${insertIndex}`, newShapePoint, shapePointsCopy)
477484
shapePointsCopy.splice(insertIndex, itemsToDelete, newShapePoint)
478485
if (i > 0) {
479486
// Add pattern segment to sliced segments
480487
let sliceDistance = (distance - previousDistance) / 1000
481488
if (sliceDistance <= 0) {
489+
console.warn(`Slice distance is ${sliceDistance}, setting to 1 meter`)
482490
sliceDistance = 1 / 1000 // set slice distance to 1 meter if negative or zero
483491
}
484-
console.log(`slicing line at ${sliceDistance} km`, remainingLine)
492+
// console.log(`slicing line at ${sliceDistance} km`, remainingLine)
485493
const lineSegment = lineSliceAlong(remainingLine, 0, sliceDistance)
486-
console.log(`slice #${i}/${patternStops.length - 1}`, lineSegment)
494+
// console.log(`slice #${i}/${patternStops.length - 1}`, lineSegment)
487495
remainingLine = lineSliceAlong(remainingLine, sliceDistance, lineDistance(remainingLine))
488-
console.log('remainingLine', remainingLine)
496+
// console.log('remainingLine', remainingLine)
497+
if (remainingLine.geometry.coordinates.length < 2) {
498+
throw new Error(`Remaining line after pattern stop #${i}/${patternStops.length - 1} has fewer than two coordinates`)
499+
}
489500
patternSegments.push(lineSegment.geometry.coordinates)
490501
previousDistance = distance
491502
}
492503
computedShapePoints.push(newShapePoint)
504+
// const shapePointFeatures = featurecollection(computedShapePoints.map(sp => {
505+
// const p = point([sp.shapePtLon, sp.shapePtLat])
506+
// p.properties = {stopId: sp.stopId}
507+
// return p
508+
// }))
509+
// console.log(shapePointFeatures)
493510
}
494511
let previousShapePoint
495512
for (let i = 0; i < computedShapePoints.length; i++) {

lib/editor/util/map.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ export async function recalculateShape ({
422422
// the active trip pattern.
423423
return {
424424
updatedControlPoints,
425+
dragId,
425426
coordinates: updatedPatternCoordinates
426427
}
427428
}

0 commit comments

Comments
 (0)