Skip to content

Commit 401ff4d

Browse files
authored
Merge pull request #572 from ibi-group/fix-pattern-builder
Fix graphhopper handling into segments
2 parents cc9f0ab + 75a59fe commit 401ff4d

File tree

5 files changed

+62
-34
lines changed

5 files changed

+62
-34
lines changed

lib/editor/components/pattern/EditShapePanel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default class EditShapePanel extends Component<Props> {
6060
}
6161
})
6262
}
63-
if (patternSegments && patternSegments.length) {
63+
if (patternSegments && patternSegments.length > 0) {
6464
const controlPoints = controlPointsFromSegments(pattern.patternStops, patternSegments)
6565
updatePatternGeometry({
6666
controlPoints,

lib/editor/util/debug.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @flow
2+
3+
import featurecollection from 'turf-featurecollection'
4+
import point from 'turf-point'
5+
6+
import type {Coordinates, GeoJsonFeatureCollection} from '../../types'
7+
8+
/**
9+
* Log a link to the input feature collection rendered in geojson.io
10+
*/
11+
export function logGeojsonioUrl (features: GeoJsonFeatureCollection) {
12+
console.log(`http://geojson.io/#data=data:application/json,${encodeURIComponent(JSON.stringify(features))}`)
13+
}
14+
15+
/**
16+
* Convert array of coordinates to a feature collection.
17+
*/
18+
export function logCoordsToGeojsonio (coords: Coordinates) {
19+
const features = coordsToFeatureCollection(coords)
20+
logGeojsonioUrl(features)
21+
}
22+
23+
function coordsToFeatureCollection (coords: Coordinates): GeoJsonFeatureCollection {
24+
// Feature collection used for debug logging to geojson.io
25+
return featurecollection(coords.map(c => point(c)))
26+
}

lib/editor/util/map.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,8 @@ export function controlPointsFromSegments (
865865
): Array<ControlPoint> {
866866
const controlPoints = []
867867
let cumulativeDistance = 0
868+
// Iterate over pattern segments and generate control points at the fence
869+
// posts.
868870
for (let i = 0; i <= patternSegments.length; i++) {
869871
let coordinate
870872
if (i === patternSegments.length) {

lib/scenario-editor/utils/valhalla.js

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import {decode as decodePolyline} from 'polyline'
55
import {isEqual as coordinatesAreEqual} from '@conveyal/lonlat'
66
import qs from 'qs'
77
import lineString from 'turf-linestring'
8-
import lineSliceAlong from '@turf/line-slice-along'
8+
9+
// This can be used for logging line strings to geojson.io URLs for easy
10+
// debugging.
11+
// import {logCoordsToGeojsonio} from '../../editor/util/debug'
912

1013
import type {
1114
Coordinates,
@@ -58,38 +61,29 @@ function handleGraphHopperRouting (path: Path, individualLegs: boolean = false):
5861
// Decode polyline and reverse coordinates.
5962
const decodedPolyline = decodePolyline(points).map(c => ([c[1], c[0]]))
6063
if (individualLegs) {
61-
// Reconstruct individual legs from the instructions. NOTE: we do not simply
62-
// use the waypoints found in the response because for lines that share
63-
// street segments, slicing on these points results in unpredictable splits.
64-
// Slicing the line along distances is much more reliable.
6564
const segments = []
66-
const waypointDistances = [0]
67-
let distance = 0
68-
// Iterate over the instructions, accumulating distance and storing the
69-
// distance at each waypoint encountered. Distances are used to slice the
70-
// line geometry if individual legs are needed. NOTE: Waypoint === routing
71-
// point provided in the request.
72-
instructions.forEach(instruction => {
73-
if (instruction.text.match(/Waypoint (\d+)/)) {
74-
// Add distance value to list
75-
waypointDistances.push(distance)
76-
} else {
77-
distance += instruction.distance
65+
// Keep track of the segment point intervals to split the line segment at.
66+
// This appears to be the most reliable way to split up the geometry
67+
// (previously distance was used here, but that provided inconstent results).
68+
const segmentPointIndices = [0]
69+
// Iterate over the instructions, accumulating segment point indices at each
70+
// waypoint encountered. Indices are used to slice the line geometry when
71+
// individual legs are needed. NOTE: Waypoint === routing point provided in
72+
// the request.
73+
instructions.forEach((instruction, i) => {
74+
if (instruction.text.match(/Waypoint (\d+)/) || i === instructions.length - 1) {
75+
segmentPointIndices.push(instruction.interval[0])
7876
}
7977
})
80-
// Add last distance measure.
81-
// FIXME: Should this just be the length of the entire line?
82-
// console.log(waypointDistances, json.paths[0].distance)
83-
waypointDistances.push(distance)
84-
const decodedLineString = lineString(decodedPolyline)
85-
if (waypointDistances.length > 2) {
86-
for (var i = 1; i < waypointDistances.length; i++) {
87-
const slicedSegment = lineSliceAlong(
88-
decodedLineString,
89-
waypointDistances[i - 1] / 1000,
90-
waypointDistances[i] / 1000
91-
)
92-
segments.push(slicedSegment.geometry.coordinates)
78+
// Once all of the indices have been found, slice the decoded polyline up
79+
// at the provided indices.
80+
if (segmentPointIndices.length > 2) {
81+
for (var i = 1; i < segmentPointIndices.length; i++) {
82+
// Get the indices of the points that the entire path should be sliced at
83+
// Note: 'to' index is incremented by one because it is not inclusive.
84+
const [from, to] = [segmentPointIndices[i - 1], segmentPointIndices[i] + 1]
85+
const segment = decodedPolyline.slice(from, to)
86+
segments.push(segment)
9387
}
9488
// console.log('individual legs', segments)
9589
return segments
@@ -126,15 +120,16 @@ export async function polyline (
126120
let count = 0
127121
const j = points.length
128122
for (let i = 0; i < j; i += chunk) {
129-
// Offset the slice indexes so that the next chunk begins with the
123+
// Offset the slice indices so that the next chunk begins with the
130124
const offset = count * -1
131125
const beginIndex = i + offset
132126
const endIndex = i + chunk + offset
133127
const chunkedPoints = points.slice(beginIndex, endIndex)
134128
json = await routeWithGraphHopper(chunkedPoints)
129+
const path = json && json.paths && json.paths[0]
135130
// Route between chunked list of points
136-
if (json && json.paths && json.paths[0]) {
137-
const result = handleGraphHopperRouting(json.paths[0], individualLegs)
131+
if (path) {
132+
const result = handleGraphHopperRouting(path, individualLegs)
138133
geometry.push(...result)
139134
} else {
140135
// If any of the routed legs fails, default to straight line (return null).

lib/types/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ export type GeoJsonLinestring = {
438438
type: 'Feature'
439439
}
440440

441+
export type GeoJsonFeatureCollection = {
442+
features: Array<GeoJsonLinestring | GeoJsonPoint>,
443+
type: 'FeatureCollection'
444+
}
445+
441446
export type PatternStop = {
442447
defaultDwellTime: number,
443448
defaultTravelTime: number,

0 commit comments

Comments
 (0)