Skip to main content

Command Palette

Search for a command to run...

Mockup point time with gpxpy

title says all

Updated
2 min read
Mockup point time with gpxpy
H

Just run the code, or yourself.

Introduction

The scenario:

  • Some GPX files are painted using software and does not have valid timestamp.
  • Having timestamp is required by GPX standard (Ref: Decode the Chong Li 168 GPX files error ), but not fully respected by many modern software/ hardwares.
  • You may need valid timestamp to upload an activity onto Strava.

The solution:

  • In the basic mode, we start from a preset time and increment the time for each point in chronological order.
  • In the advanced mode, we start from a user defined time and calculate elapsed time between points according to the designated speed.

The Code

'''
Usage:
  gpx_mockup_time.py <input_gpx> <output_gpx> 
  gpx_mockup_time.py <input_gpx> <output_gpx> <mockup_time_start> <mockup_speed_meters_per_second> <track_name>

Options:
  -h help
  -v version
'''

# Example usage:
# %python gpx_mockup_time.py compare-hk100/2025HK100.gpx compare-hk100/hk100-mockup.gpx '2025-01-01 08:08:01 +08:00' 1.38 'HK100 mockup'

import docopt
import logging
import gpxpy 
import gpxpy.gpx
from gpxpy import geo
from datetime import timedelta
from dateutil import parser as dt_parser
from gpxpy import geo

DEFAULT_START_TIME_BASIC_MODE = dt_parser.parse('9999-12-15 0:0:1 +08:00')

def mockup_time_basic(reverse=False):
    if not hasattr(mockup_time_basic, '_last_mockup_time'):
        mockup_time_basic._last_mockup_time = DEFAULT_START_TIME_BASIC_MODE
    if reverse:
        mockup_time_basic._last_mockup_time -= timedelta(seconds=1)
    else:
        mockup_time_basic._last_mockup_time += timedelta(seconds=1)
    return mockup_time_basic._last_mockup_time

if __name__ == "__main__":
    logging.getLogger().setLevel(logging.INFO)
    arguments = docopt.docopt(__doc__)

    gpx_file = open(arguments['<input_gpx>'], 'r') 
    gpx = gpxpy.parse(gpx_file)
    print('Finished loading GPX')
    print(gpx)

    mockup_time_start = arguments['<mockup_time_start>']
    mockup_speed_meters_per_second = arguments['<mockup_speed_meters_per_second>']
    track_name = arguments['<track_name>']
    if mockup_time_start and mockup_speed_meters_per_second:
        # Advanced mode
        mockup_speed_meters_per_second = float(mockup_speed_meters_per_second)
        print('[Advanced] Processing mockup time.')
        cur_time = dt_parser.parse(mockup_time_start)
        gpx.name = track_name
        last_point = None
        for track in gpx.tracks:
            track.name = track_name
            for segment in track.segments:
                for point in segment.points:
                    if last_point is None:
                        pass
                    else:
                        dist = geo.haversine_distance(point.latitude, point.longitude, last_point.latitude, last_point.longitude)
                        delta_time = timedelta(seconds=(dist / mockup_speed_meters_per_second))
                        cur_time += delta_time
                        # print(delta_time)
                    point.time = cur_time
                    last_point = point
    else:
        # Basic mode
        print('[Basic] Processing mockup time.')
        gpx.time = DEFAULT_START_TIME_BASIC_MODE
        for track in gpx.tracks:
            track.name = track_name
            for segment in track.segments:
                for point in segment.points:
                    point.time = mockup_time_basic()
    c = open(arguments['<output_gpx>'], 'w').write(gpx.to_xml())
    print(f'Write {c} chars to new GPX {arguments["<output_gpx>"]}')