Skip to content

Shortest path, distance + cumulative elev change #29

@kylebarron

Description

@kylebarron

GeoJSON Path finder is very helpful.

Usage

var helpers = require("@turf/helpers");
var length = require('@turf/length').default
var PathFinder = require("geojson-path-finder");
var geojson = require("./data/CA_Sec_A_tracks.json");
// Each degree of latitude is 111km, so precision of .001 snaps vertices within 100m
var pathFinder = new PathFinder(geojson, { precision: 0.001 });

// Same line
var start = Point([-116.467875, 32.590604]);
var end = Point([-116.503565, 32.607618]);
var path = pathFinder.findPath(start, end);

// # Different routes
var start = Point([-116.467078, 32.593434]);
var end = Point([-116.523801, 32.979053]);
var path = pathFinder.findPath(start, end);

I found that the default precision (.00001) was too small. I think precision of .001 degrees should be fine, although it would be ideal if alternates had an exact point where they intersected the PCT line.

By default, geojson-path-finder uses turf.distance as the weight function, and the returned distance is in kilometers.

Didn't check start/end points far off the tracks in the GeoJSON.

The difference between turf.length (on the resulting LineString of path.path's coordinates) and path.weight is like .6%, which might just be from floating point rounding? Not sure, but it's close enough to definitely not calculate path length again.

Cumulative elevation change

path.path reports the same coordinates that are passed into it. So if there are elevation values in the original GeoJSON LineStrings, there will be elevation values in the returned coordinates. This means that the easiest way to get cumulative elevation gain/loss to a point is to sum up the third values of the coords.

Here's JS code to calculate elevation change from an array of coordinates, where each coordinate is an array of [lon, lat, elevation]. elevChangeReduce should be faster than elevChangeFilter because the former only makes two passes through the list of coordinates instead of 4. After finding out the importance of setting the initial value in Array.prototype.reduce, they give the same result.

// Given an array of coordinates, find the marginal elevation
// gain/loss of each segment
// [1, 3, 2, 5] -> [2, -1, 3]
function getIncrementalDifference(coords) {
  const changes = coords.map((point, index) => {
    const nextPoint = coords[index + 1];
    if (nextPoint) {
      return nextPoint[2] - point[2]
    }
  })
  return changes;
}

function elevChangeReduce(coords) {
  const changes = getIncrementalDifference(coords)
  const positiveReducer = (acc, cur) => {
    if (cur > 0) {
      return acc + cur
    }
    else {
      return acc
    }
  }
  const negativeReducer = (acc, cur) => {
    if (cur < 0) {
      return acc + cur
    }
    else {
      return acc
    }
  }
  // Note: You need to set 0 as the initial value, or else the initial value is the first element!
  positiveSum = changes.reduce(positiveReducer, 0)
  negativeSum = changes.reduce(negativeReducer, 0)
  return [positiveSum, negativeSum]
}

function elevChangeFilter(coords) {
  const changes = getIncrementalDifference(coords)
  const positiveVals = changes.filter(val => val > 0)
  const negativeVals = changes.filter(val => val < 0)
  const positiveSum = positiveVals.reduce((acc, cur) => acc + cur)
  const negativeSum = negativeVals.reduce((acc, cur) => acc + cur)
  return [positiveSum, negativeSum]
}

elevChangeReduce(coords) // -> [ 2875.0599999999995, -2209.789999999999 ]
elevChangeFilter(coords) // -> [ 2875.0599999999995, -2209.789999999999 ]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions