I am working with a GPS dataset where GPS speed is not available, only the lat/lon coordinates and timestamp.

Currently, I calculate point-level speed from rate of distance between two consecutive points (distance/delta_time). However, the result usually gave large errors due to the errors in GPS.

Are the other means or techniques to estimate the point-point speed?


Currently, I calculate the speed in python using the geopy package, like so:

from geopy.distance import vincenty
A = (Data[i, 0], Data[i, 1])
B = (Data[i + 1, 0], Data[i + 1, 1])
pointSpeed = vincenty(A, B).meters/delta_time[i]
  • 1
    Which software are you working with?
    – Erik
    Commented Jul 23, 2020 at 10:16
  • I do this in Python using geopy.vincenty package to calculate the speed as speed =vincenty(A, B).meters/delta_time.
    – arilwan
    Commented Jul 23, 2020 at 10:49
  • 2
    Please Edit the Question in response to requests for clarification.
    – Vince
    Commented Jul 23, 2020 at 11:02
  • 3
    Perhaps try calculating a moving average instead of each pair of successive points individually? It could help smooth some of the jumps in speed that you get.
    – FSimardGIS
    Commented Jul 23, 2020 at 16:29
  • Over what sort of distances are you recording these GPS track points? And what kind of time deltas? Knowing where the scale of the errors is coming from (i.e. multipath / atmospheric range issues which cause 'movement' where there is none but usually only the scale of metres, vs. limitations in the GPS source) would inform how you deal with the 'large errors' you see.
    – Simbamangu
    Commented Jul 30, 2020 at 16:43

1 Answer 1


I took a shot at @FSimardGIS 's suggestion for moving average.
Inputs: pandas dataframe df of the gps data containing timestamp, latitude, longitude columns, sorted by timestamp and with pure index (0 to end).
And a time_interval in mins, default: 5 mins

import pandas as pd
# main function
def findSpeed(df, time_interval = 5):    
    df['offset'] = getTimeOffsets(df)
    df['dspan'] = df['tspan'] = df['cspeed'] = df['diff'] = None
    for N in range(len(df)):
        if N == 0: continue
        backi = N
        totOffset = df.at[N,'offset']
        while totOffset <= time_interval*60 :
            backi -= 1
            totOffset += df.at[backi,'offset']
            if backi == 0: break

        tspan = df.at[N,'timestamp'] - df.at[backi,'timestamp']
        df.at[N,'tspan'] = tspan.seconds
        distance = df.at[N,'ll_dist_traveled'] - df.at[backi,'ll_dist_traveled']
        df.at[N,'dspan'] = round(distance,3)
        if distance == 0:
            speed = df.at[N, 'cspeed'] = 0
            speed = round(distance / tspan.seconds * 3600,2)
            df.at[N, 'cspeed'] = speed
        speed_diff = round(df.at[N,'speed'] - speed, 2)
        df.at[N,'diff'] = speed_diff
    return df

# calling the function
df1 = findSpeed(df, time_interval = 5)

There are some supporting functions I've developed in earlier projects: computeDistance: finds distance between consecutive lat-longs and adds it up to a cumulative distance column ll_dist_traveled
getTimeOffsets: gives a column of time offset between timestamps in seconds

These are used by the program to identify [time_interval] (or lower) time period correspondng to each row and calculate speed from that group of data.

from math import sin, cos, sqrt, atan2, radians
def lat_long_dist(lat1,lon1,lat2,lon2):
    # function for calculating ground distance between two lat-long locations
    R = 6373.0 # approximate radius of earth in km. 
    lat1 = radians( float(lat1) )
    lon1 = radians( float(lon1) )
    lat2 = radians( float(lat2) )
    lon2 = radians( float(lon2) )
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    distance = round(R * c, 6)
    return distance

def computeDistance(sequencedf):
    prevLat = prevLon = 0 # dummy initiation
    total_dist = 0
    for N in range(len(sequencedf)):
        lat = float(sequencedf.at[N,'latitude'])
        lon = float(sequencedf.at[N,'longitude'])
        if N == 0:
            sequencedf.at[N,'ll_dist'] = 0
            sequencedf.at[N,'ll_dist'] = lat_long_dist(lat,lon, prevLat,prevLon)
        total_dist += sequencedf.at[N,'ll_dist']
        sequencedf.at[N,'ll_dist_traveled'] = round(total_dist,6)
        prevLat = lat
        prevLon = lon
    return round(total_dist,6)
    # also the original df gets ll_dist and ll_dist_traveled columns added to it

def getTimeOffsets(df, timestamp_col='timestamp'):
    timeOffsets = (df[timestamp_col] - df[timestamp_col].shift()).fillna(pd.Timedelta(seconds=0))
    return timeOffsets.dt.total_seconds().astype(int)

Feel free to change the lat_long_dist function to your preferred method (or use a routing tool like OSRM).

Output: same df (table / dataframe), with some middle-step new columns and a 'cspeed' column with calculated speed in km/hr.

I had a gps stream with original speed values from the device. Calculated with different time interval settings and plotted.






As observable, even the original data (in orange) is quite jittery. Moving average helps smoothen it out, but there's a tradeoff with accuracy. I'm still not clear about which time interval value to take. Hope this helps someone!

  • Interesting... this is really helpful. Thanks for the detailed analysis.
    – arilwan
    Commented Jun 10, 2021 at 16:59
  • 1
    @arilwan thanks, pls upvote the answer if you can
    – Nikhil VJ
    Commented Jun 11, 2021 at 6:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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