I'd like to calculate a rate of turn from a GPS log (GPX file).

Each trackpoint in a GPX track has location and time, and in addition to speed (a common calculation and sometimes embedded in a tracklog) I'd like to calculate the rate at which the aircraft or vehicle is turning, for each trackpoint. That said, I suspect the 'rate of turn' is more accurately reflected by a moving average over several points, and would be interested to see if anyone has examined that.

Sample GPX file here.

enter image description here

Open-source solutions in R or QGIS preferred!

  • I think you should be more precise about what do you mean with 'turn rate' and what do you expect to find. degree/km? What is the (guessed) 'turn rate' from your figure? 180°/5km? Is it to calculate from the starting point to the last point or between each point i.e the difference between two segments? – nebi Sep 1 '16 at 20:03
  • As stated: "... for each trackpoint." i.e. each point should have an associated rate of turn. Turn rate should be in degrees per minute. In the example, the 180° turn happens over 59 seconds. – Simbamangu Sep 3 '16 at 4:18

Set up a function for calculating the course (angle between points):

angle <- function(pt1, pt2) {
  conv = 180 / pi # conversion radians / degrees
  diff <- pt2 - pt1
  left <- sign(diff[1]) # is X diff < 0?
  left[left == 0] <- -1 # sign(0) = 0, need to keep as nonzero
  angle <- left * (diff[2] * pi + atan(diff[1] / diff[2]))
  angle[angle < 0] <- angle[angle < 0] + 2 * pi
  return(angle * conv)

Load the GPX:

setwd(".") # set to the directory where the sample GPX is located.
gpx.trackpoints <- readOGR(dsn = "tracksample.gpx", layer = "track_points", stringsAsFactors = F)

We're interested in rates by time, so convert GPX time to POSIXlt:

gpx.trackpoints$time <- strptime(gpx.trackpoints$time, "%Y/%m/%d %H:%M:%S", tz = "GMT")

Extract the coordinates as a matrix:

gpx.tp <- coordinates(gpx.trackpoints)

Loop through the GPX points and add distance, time interval and speed for each point, using the previous point as reference.

require(raster) # interestingly, this has pointDistance for vectors
for (tp in 2:nrow(gpx.tp)) {
  gpx.trackpoints$dist[tp] <- pointDistance(gpx.tp[tp,], gpx.tp[tp-1,], lonlat=T)
  gpx.trackpoints$interval[tp] <- as.numeric((gpx.trackpoints$time[tp] - gpx.trackpoints$time[tp-1]))
  gpx.trackpoints$speed[tp] <-  gpx.trackpoints$dist[tp] / gpx.trackpoints$interval[tp] # m/s 

Loop through and add course - the difference between the of the current point and the following one:

for (tp in 1:(nrow(gpx.tp)-1)) {
  gpx.trackpoints$course[tp] <- angle(gpx.tp[tp + 1,], gpx.tp[tp,])

Loop through again and look at turn angle and calc rate. Turn angle is the difference between the current point's course and the following point's course:

for (tp in 2:nrow(gpx.tp)) {
  gpx.trackpoints$turn[tp] <- gpx.trackpoints$course[tp] - gpx.trackpoints$course[tp -1]
  gpx.trackpoints$turn_rate[tp] <- gpx.trackpoints$turn[tp] / gpx.trackpoints$interval[tp]

Let's have a look ... adding the points to QGIS, but styling with a rotation dependent on the "course" field:

enter image description here

enter image description here

Changing the size of the marker depending on the turn rate:

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.