Skip to content

Commit

Permalink
fix(editor): fix shape point snapping
Browse files Browse the repository at this point in the history
  • Loading branch information
landonreed committed Feb 27, 2018
1 parent 3f33195 commit df8a5bb
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 46 deletions.
7 changes: 5 additions & 2 deletions lib/editor/components/map/ControlPoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default class ControlPoint extends Component {
} = this.props
const latlng = e.latlng
this.setState({latlng})
console.log(`drag to`, latlng)
handleControlPointDrag(controlPoints, index, latlng, activePattern, patternCoordinates)
}, 500)

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

render () {
// console.log(this.state)
const {editSettings, icon, position, isActive} = this.props
const {controlPoint, editSettings, icon, position, isActive} = this.props
// keep track of position in state because we need this to be cleared once
// the user has stopped dragging the marker, at which point
// this.state.latlng will be null and the marker will "snap" back to the
// polyline
const {latlng} = this.state
const tooltipMessage = isActive
const tooltipMessage = isActive && controlPoint.stopId
? 'Drag handle to change stop snap point'
: isActive
? 'Drag handle to change shape (click to remove)'
: 'Click to begin editing segment'
const markerPosition = latlng
Expand Down
2 changes: 1 addition & 1 deletion lib/editor/components/map/PatternsLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class PatternSegment extends Component {
patternIsActive,
pattern
} = this.props
// isEditing && segmentIsActive && console.log('rendering pattern', pattern)
// isEditing && segmentIsActive && console.log(`rendering active segment #${index}`, pattern, coordinates)
if (!coordinates || !coordinates.length) {
console.warn(`Could not render segment #${index} of pattern ID ${pattern.id}`, coordinates)
return null
Expand Down
4 changes: 2 additions & 2 deletions lib/editor/components/pattern/EditShapePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export default class EditShapePanel extends Component {
value={patternSegment - 1}
disabled={!patternSegment || patternSegment < 1}
bsSize='xsmall'>
Previous
<Icon type='square' style={{color: 'blue'}} /> Prev.
</OptionButton>
<small>
{!patternSegment && patternSegment !== 0
Expand All @@ -209,7 +209,7 @@ export default class EditShapePanel extends Component {
}
disabled={patternSegment >= controlPoints.length - 1}
bsSize='xsmall'>
Next
<Icon type='square' style={{color: 'yellow'}} /> Next
</OptionButton>
</div>
<ButtonGroup justified>
Expand Down
2 changes: 0 additions & 2 deletions lib/editor/components/pattern/PatternStopContainer.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import clone from 'lodash.clonedeep'
import React, { Component, PropTypes } from 'react'
import update from 'react/lib/update'
import { shallowEqual } from 'react-pure-render'
import { DropTarget, DragDropContext } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'

import {POINT_TYPE} from '../../constants'
import PatternStopCard from './PatternStopCard'

/* This component and its child components (Card.js) are based on the react-dnd sortable
Expand Down
95 changes: 56 additions & 39 deletions lib/editor/selectors/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clone from 'lodash.clonedeep'
import oneLine from 'common-tags/lib/oneLine'
// import oneLine from 'common-tags/lib/oneLine'
import {List} from 'immutable'
import randomColor from 'randomcolor'
import SortDirection from 'react-virtualized/dist/commonjs/Table/SortDirection'
Expand Down Expand Up @@ -107,7 +107,7 @@ export const getControlPoints = createSelector(
return {patternSegments, controlPoints}
}
if (previousControlPoints && previousControlPoints.length && previousPatternSegments && previousPatternSegments.length) {
console.log('defaulting to state\'s control points/patternSegments', previousControlPoints, previousPatternSegments)
// console.log('defaulting to state\'s control points/patternSegments', previousControlPoints, previousPatternSegments)
return {
controlPoints: previousControlPoints,
patternSegments: previousPatternSegments
Expand All @@ -124,7 +124,7 @@ export const getControlPoints = createSelector(
// stop, rather than assuming they're all missing if the first one is.
const projectStops = patternStops[0].shapeDistTraveled === null &&
shapePoints.filter(sp => sp.pointType === POINT_TYPE.STOP) < patternStops.length
console.log('projecting stops, shape dist traveled:', patternStops[0].shapeDistTraveled)
// console.log('projecting stops, shape dist traveled:', patternStops[0].shapeDistTraveled)
// if distance values are null, calculate distance values.
// let newShapePoints = shapePoints
// if (shapePoints.filter(sp => sp.pointType === POINT_TYPE.STOP) < patternStops.length) {
Expand All @@ -143,7 +143,7 @@ export const getControlPoints = createSelector(
}
patternSegments = result.patternSegments
controlPoints = getControlPointsFromShapePoints(result.shapePoints)
console.log(controlPoints, patternSegments)
// console.log(controlPoints, patternSegments)
if (controlPoints.length - 1 > patternSegments.length) {
// There are non-stop control points that need to be added in.
for (let i = 0; i < controlPoints.length; i++) {
Expand All @@ -152,20 +152,21 @@ export const getControlPoints = createSelector(
// If the following control point is a user-defined anchor, slice
// current line at the following control point and splice into
// array of segments.
console.log(`control point ${i}; pattern segment ${i - 1}`, nextControlPoint, patternSegments[i])
// console.log(`control point ${i}; pattern segment ${i - 1}`, nextControlPoint, patternSegments[i])
const {beforeSlice, afterSlice} = getLineSlices(lineString(patternSegments[i]), nextControlPoint.point)
patternSegments.splice(i, 1, beforeSlice.geometry.coordinates, afterSlice.geometry.coordinates)
}
}
}
console.log(featurecollection([
featurecollection([
...patternSegments.map(ps => {
const lineSegment = lineString(ps)
lineSegment.properties.stroke = randomColor()
return lineSegment
}),
...controlPoints.map(cp => cp.point)
]))
])
// console.log()
} else {
// No pattern stops, return empty array (below).
// FIXME: Return control points from shape points instead?
Expand Down Expand Up @@ -208,7 +209,7 @@ export const getControlPoints = createSelector(
}
// console.log('control points', controlPoints)
const sortedControlPoints = controlPoints.sort((a, b) => a.distance - b.distance)
console.log('sorted control points', sortedControlPoints)
// console.log('sorted control points', sortedControlPoints)
return {
controlPoints: sortedControlPoints,
patternSegments
Expand All @@ -226,12 +227,12 @@ export const getPatternCoordinates = createSelector(
return null
}
if (patternSegments) {
console.log('using coordinates from history', patternSegments)
// console.log('using coordinates from history', patternSegments)
return patternSegments
}
console.log('using shapepoints', shapePoints)
// console.log('using shapepoints', shapePoints)
// if (shapePoints.filter(sp => sp.pointType).length === 0) {
// console.log('shape points do not contain control points. Merging.')
// // console.log('shape points do not contain control points. Merging.')
// controlPoints.forEach((cp, index) => {
// const itemsToReplace = index < 0 && index < controlPoints.length - 1
// ? 0
Expand Down Expand Up @@ -268,13 +269,14 @@ export const getPatternCoordinates = createSelector(
}
splitAndAddSegment(shapePoints, segments, fromIndex, toIndex)
}
let sum = 0
segments.forEach(e => { sum += e.length })
console.log(oneLine`
points: ${sum}; shapepoints: ${shapePoints.length};
cp: ${controlPoints.length}; seg: ${segments.length}
${controlPoints.length === segments.length - 1 ? '✅' : '❌'}
`, featurecollection(segments.map(seg => lineString(seg))))
// let sum = 0
// segments.forEach(e => { sum += e.length })
// const qcString = oneLine`
// points: ${sum}; shapepoints: ${shapePoints.length};
// cp: ${controlPoints.length}; seg: ${segments.length}
// ${controlPoints.length === segments.length - 1 ? '✅' : '❌'}
// `
// console.log(qcString, featurecollection(segments.map(seg => lineString(seg)))
return segments
}
)
Expand All @@ -285,7 +287,7 @@ function splitAndAddSegment (shapePoints, segments, fromIndex, toIndex) {
} else if ((toIndex - fromIndex < 2) || (fromIndex === shapePoints.length - 1)) {
console.warn(`Should not slice shapepoints for segment with less than two coordinates (from ${fromIndex} to ${toIndex}).`)
} else {
console.log(`slicing ${shapePoints.length} items from ${fromIndex} to ${toIndex}`)
// console.log(`slicing ${shapePoints.length} items from ${fromIndex} to ${toIndex}`)
}
const nextSegment = shapePoints
.slice(fromIndex, toIndex)
Expand Down Expand Up @@ -334,17 +336,17 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
const patternSegments = []
const computedShapePoints = []
// Keep only those shape points that are associated with stops
// (this is an empty array if pattern shape is unedited).
// (this is an empty array if pattern shape has never been edited).
const stopControlPoints = shapePointsCopy.filter(sp => sp.pointType === POINT_TYPE.STOP)
const patternLine = lineString(coordinatesFromShapePoints(shapePointsCopy))
const patternLength = lineDistance(patternLine, 'meters')
const beginPoint = point(patternLine.geometry.coordinates[0])
const endPointIndex = patternLine.geometry.coordinates.length - 1
const endPoint = point(patternLine.geometry.coordinates[endPointIndex])
if (projectStops) {
console.log('Projecting pattern stops onto shape (distances are null)', shapePointsCopy, stopControlPoints)
// console.log('Projecting pattern stops onto shape (distances are null)', shapePointsCopy, stopControlPoints)
} else {
console.log('Generating control points from pattern stops (using pre-existing stop distances)', shapePointsCopy, stopControlPoints)
// console.log('Generating control points from pattern stops (using pre-existing stop distances)', shapePointsCopy, stopControlPoints)
}
let previousDistance = 0
let remainingLine = patternLine
Expand All @@ -363,14 +365,14 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
if (i === 0) {
// First stop must always be placed at first latlng coordinate
insertPoint = beginPoint
console.log('first stop placed at begin point')
// console.log('first stop placed at begin point')
insertIndex = 0
distance = 0
itemsToDelete = 1
} else if (i === patternStops.length - 1) {
// Last stop must always be placed at last latlng coordinate
insertPoint = endPoint
console.log('last stop placed at end point')
// console.log('last stop placed at end point')
insertIndex = endPointIndex
distance = patternLength
itemsToDelete = 1
Expand All @@ -393,13 +395,13 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
if (i < stopControlPoints.length && stopControlPoints[i] && stopControlPoints[i].shapeDistTraveled !== null) {
// Check shape points for existence of control points. If index is less
// than number of stop control points, skip.
console.log(`skipping index ${i} (control point should exist for stop)`)
// console.log(`skipping index ${i} (control point should exist for stop)`)
stopControlPoints[i].stopId = stopId
distance = stopControlPoints[i].shapeDistTraveled
if (i > 0) {
const sliceDistance = (distance - previousDistance) / 1000
let lineSegment
console.log(remainingLine, sliceDistance, stopControlPoints[i])
// console.log(remainingLine, sliceDistance, stopControlPoints[i])
if (sliceDistance <= 0) {
// If there is no more line segment left, extend line to stop control
// point and TODO signal warning to user?
Expand All @@ -417,7 +419,7 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
}
continue
}
console.log(`generating shape point for stop #${i} at distance: ${patternStop.shapeDistTraveled}`, patternStop, remainingLine)
// console.log(`generating shape point for stop #${i} at distance: ${patternStop.shapeDistTraveled}`, patternStop, remainingLine)
if (i === 0 && patternStop.shapeDistTraveled !== 0) {
console.warn(`Distance for first stop is not zero. Coercing to zero.`)
distance = 0
Expand All @@ -436,28 +438,32 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
// FIXME: what if this happens not on the first pattern stop
if (i !== 0) console.warn(`shape dist traveled for pattern stop ${i} equals zero. this should not be the case!`)
insertPoint = pointAtFirstCoordinate(remainingLine)
insertIndex = 0
distance = 0
itemsToDelete = 0
itemsToDelete = 1
} else {
// This should cover all cases except the first stop.
const remainingLineDistance = lineDistance(remainingLine, 'meters')
// FIXME this is breaking stuff when the remaining line has only one coordinate!!!
console.log(`remaining line dist = ${remainingLineDistance}`, remainingLine, patternStop.shapeDistTraveled / 1000)
// console.log(`remaining line dist = ${remainingLineDistance}`, remainingLine, patternStop.shapeDistTraveled / 1000)
if (patternStop.shapeDistTraveled === remainingLineDistance) {
// This should only be the case with the last stop
console.log(`pattern stop ${i}/${patternStops.length} distance is equal to remaining lint distance, setting to end of shape`)
// console.log(`pattern stop ${i}/${patternStops.length} distance is equal to remaining lint distance, setting to end of shape`)
// FIXME: what if this happens not on the last pattern stop
insertPoint = pointAtLastCoordinate(remainingLine)
insertIndex = shapePointsCopy.length - 1
distance = remainingLineDistance
itemsToDelete = 0
itemsToDelete = 1
} else {
// This should be the case for all but first and last stops
const lineSegment = lineSliceAlong(remainingLine, 0, patternStop.shapeDistTraveled / 1000)
console.log(`pattern stop ${i}/${patternStops.length} remaining segment and previous dist`, lineSegment, previousDistance)
distance = previousDistance + lineDistance(lineSegment, 'meters')
distance = patternStop.shapeDistTraveled
// Use the pattern line to find the lat/lng to use for the generated
// shape point.
const lineSegment = lineSliceAlong(patternLine, 0, patternStop.shapeDistTraveled / 1000)
insertPoint = pointAtLastCoordinate(lineSegment)
// FIXME: How to determine insert index?
// insertIndex = result.index
// Insert generated shape point in the correct location according to
// its distance along the line.
insertIndex = shapePointsCopy.findIndex(sp => sp.shapeDistTraveled > distance)
itemsToDelete = 0
}
}
Expand All @@ -474,22 +480,33 @@ function addPatternStopsToShapePoints (oldShapePoints, patternStops, stops, proj
// every shape point below in the re-mapping.
}
// Insert closest point into pattern shape
// console.log(`inserting shape point at ${insertIndex}`, newShapePoint, shapePointsCopy)
shapePointsCopy.splice(insertIndex, itemsToDelete, newShapePoint)
if (i > 0) {
// Add pattern segment to sliced segments
let sliceDistance = (distance - previousDistance) / 1000
if (sliceDistance <= 0) {
console.warn(`Slice distance is ${sliceDistance}, setting to 1 meter`)
sliceDistance = 1 / 1000 // set slice distance to 1 meter if negative or zero
}
console.log(`slicing line at ${sliceDistance} km`, remainingLine)
// console.log(`slicing line at ${sliceDistance} km`, remainingLine)
const lineSegment = lineSliceAlong(remainingLine, 0, sliceDistance)
console.log(`slice #${i}/${patternStops.length - 1}`, lineSegment)
// console.log(`slice #${i}/${patternStops.length - 1}`, lineSegment)
remainingLine = lineSliceAlong(remainingLine, sliceDistance, lineDistance(remainingLine))
console.log('remainingLine', remainingLine)
// console.log('remainingLine', remainingLine)
if (remainingLine.geometry.coordinates.length < 2) {
throw new Error(`Remaining line after pattern stop #${i}/${patternStops.length - 1} has fewer than two coordinates`)
}
patternSegments.push(lineSegment.geometry.coordinates)
previousDistance = distance
}
computedShapePoints.push(newShapePoint)
// const shapePointFeatures = featurecollection(computedShapePoints.map(sp => {
// const p = point([sp.shapePtLon, sp.shapePtLat])
// p.properties = {stopId: sp.stopId}
// return p
// }))
// console.log(shapePointFeatures)
}
let previousShapePoint
for (let i = 0; i < computedShapePoints.length; i++) {
Expand Down
1 change: 1 addition & 0 deletions lib/editor/util/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ export async function recalculateShape ({
// the active trip pattern.
return {
updatedControlPoints,
dragId,
coordinates: updatedPatternCoordinates
}
}
Expand Down

0 comments on commit df8a5bb

Please sign in to comment.