Linear Referencing GPS Routes using JavaScript and PostGIS

Published

There are many cool GIS applications that marry together elevation and distance. It is common for the position on the map to update as the user drags their cursor along the elevation chart.

Screen grab taken from Strava in June of 2020

While trying to build this feature into one of my projects, I learned that it required something called Linear Referencing. Below is a bit about what Linear Referencing is, and how I used JavaScript and PostGIS to incorporate it in to my existing GIS application.

Linear Referencing Standards

My current GPS routes contained latitude, longitude, and elevation (i.e. they were 3-Dimensional points). I needed to know how far each point was from the beginning of the route in order populate the x-axis of the elevation diagram.

Fortunately the GIS community has come up with standards around Linear Referencing. The point’s distance from the beginning of the line (referred to as the m value) can be stored as the 3rd or 4th Dimension of the GPS point.

Geometries with linear references contain an M in the name, for example:

  • LINESTRING M: contains 3-Dimensional points (latitude, longitude, linear reference)
  • LINESTING ZM: contains 4-Dimensional points (latitude, longitude, elevation, linear reference)

JavaScript Calculation

Applying the Linear References along a LINESTING was fairly straightforward: simply loop through each point along the line, calculate the distance from the start, and append it to the end of the point.

I used two JavaScript libraries to help with these calculations:

  • Turf – my go-to geospatial library for geometry calculations in Node/JavaScript
  • Wellknown – another library by MapBox, used to convert between WKT and GeoJSON

The resulting JavaScript accepts takes a LINESTRING Z in WKT on line 5, calculates the references using Turf’s distance function on line 21, and returns the updated LINESTING ZM on line 31.

const parse = require('wellknown')
const turf = require('@turf/turf')

// convert WKT to geoJSON (optional; required by Turf library)
const inputGeoJSON = parse(inputWKT).coordinates

// parse geoJSON into Turf LineString
const linestring = turf.lineString(inputGeoJSON)
  
// add linear referencing - loop through points and add 4th "M" dimension
let previousLength = 0 // track previous length
let previousCoord = null
linestring.geometry.coordinates.forEach( (currentCoord, index) => {
  
  const currentLength = 0

  // if first point set length to 0, otherwise append length to end of point array
  if (index===0) {
    currentCoord.push(0)
  } else {
    const distance = turf.distance(previousCoord, currentCoord, {units: 'miles'})
    currentCoord.push(previousCoord[3] + distance)
  }

  previousLength = currentLength
  previousCoord = currentCoord

})

// output back to WKT (including 4th dimension)
const updatedWKT = parse.stringify(linestring)

This JavaScript logic has been packaged into a Node Module, and is also posted on GitHub with an example included in the README.

Storing Referenced Geometries in PostGIS

PostGIS supports the Linear Referencing standards discussed above. Basic spatial functions for storing and querying geometries work as expected, and I used ST_GeomFromText() to insert my LINESTRING ZM without any issues.

INSERT INTO sampletable(id, geom) VALUES(1, ST_GeomFromText('LINESTRING ZM (1 1 0 0, 1 2 1 1, 1 3 2 2, 2 2 3 3)'));

Retrieving Geometries in WKT

I quickly learned that GeoJSON does not support 4-Dimensional geometries 1 2. As a result, I had to use the WKT format when exporting my geometries from PostGIS so that I could include both elevation and references (done via the ST_AsText() function).

SELECT id, ST_AsText(geom) FROM sampletable;

Sending WKT to the Frontend

GeoJSON is normally how I encode geometries over REST APIs, however in this case I had to transmit the geometries in WKT format. This was so that all 4-Dimensions of my points could be included (remember, GeoJSON only supports 3-Dimensions!).

Fortunately major mapping libraries support importing geometries from WKT either natively or with plugins. The Wellknown JavaScript library mentioned earlier is another great tool for converting between WKT and GeoJSON if needed.

References

  1. Open Geospatial Consortum Engineering Report
  2. Gaia Geometry Survival Guide

Comments

Loading comments..

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *