Skip to main content

gpx-player

CI codecov PyPI version PyPI downloads License

GPX Race Visualizer

GPX Race Visualizer is a Python script to visualize the progression of multiple GPS tracks (e.g., from a race) on a 2D map. It takes as input one or more GPX files and creates an animation showing the movement of each track over time. This is a simple, open-source alternative to features like Strava's Flyby, which require an account and can have privacy issues.

Modes

The player supports two modes:

1. "Video" mode

Produce an MP4 or a GIF file showing how the situation developed. For sailing races, it also calculates the distance covered after the 'start' signal and the current speed.

Example:

Example output

2. Map mode

Displays the track on OpenSeaMap. You can see the full tracks with colour-coded speeds, and you can 'play' the tracks and see the markers move around the map. While playing, a legend shows each boat's distance travelled (in nautical miles), current speed (in knots), and average speed in knots.

Example:

Since GitHub Markdown doesn't allow embedding HTML, you can see an interactive example here.

Screenshot: OPS Example

Installation

Install directly from PyPI using pip:

pip install gpx-player

Alternatively, clone the repository and install the required dependencies manually:

git clone https://github.com/kirienko/gpx-player.git
cd gpx-player
pip install -r requirements.txt

Usage

To run the script producing mp4, pass one or more GPX file paths as command-line arguments:

gpx-player example-data/track1.gpx example-data/track2.gpx

To get a sea map, run the module:

python -m gpx_player.openseamap --title 'Gin Sul Regatta 2024' --names Alex Yury Richard \
     --files example-data/osm-demo-Alex.gpx example-data/osm-demo-Richard.gpx \
             example-data/osm-demo-Yury.gpx

This writes a static playable HTML map to boat_tracks.html with the title, speed legend, boat stats legend, play/pause button, moving markers, and time slider. Playback maps also include a per-boat track visibility control. Each boat can show the full speed-coloured track, show only a moving tail in the boat colour, or hide both the track and marker.

The same playable OpenSeaMap can be created from Python code:

from gpx_player.openseamap import create_playback_map

folium_map = create_playback_map(
    [
        "example-data/osm-demo-Alex.gpx",
        "example-data/osm-demo-Richard.gpx",
        "example-data/osm-demo-Yury.gpx",
    ],
    names=["Alex", "Richard", "Yury"],
    max_speed=12,
    title="Gin Sul Regatta 2024",
    slider_active_color="#6e6e6e",
    slider_inactive_color="#d0d0d0",
    tail_length="normal",
)
folium_map.save("boat_tracks.html")

slider_active_color and slider_inactive_color are optional Python-only arguments for theming the played and unplayed sections of the playback slider. tail_length controls the point-based tail length in map playback mode: short keeps 30 points, normal keeps 60 points, and long keeps 120 points.

Playback templates and JavaScript are bundled as package assets, so downstream applications can call this API from any current working directory after pip install gpx-player; no source checkout or local asset copies are needed.

OpenStreetMap tile access

The generated HTML uses live raster tiles from tile.openstreetmap.org for the base map and an OpenSeaMap seamark overlay. OpenStreetMap data is open, but the public OSM tile servers are a shared service with a tile usage policy. In particular, browser requests from web pages must send a valid HTTP Referer header.

If you open a generated map directly as a local file://.../boat_tracks.html file, many browsers will not send a valid HTTP Referer for tile requests. OSM may then return "blocked" placeholder tiles linking to https://osm.wiki/blocked. This can happen even for an old HTML file that works normally when hosted on a website.

For local viewing, serve the directory over HTTP instead:

python3 -m http.server 8000

Then open http://localhost:8000/boat_tracks.html in the browser.

For public or production applications, do not rely on the community tile.openstreetmap.org service as an application tile backend. Host the HTML on a normal web origin and use a tile provider, self-hosted tiles, or vector tiles whose terms fit your traffic and offline/static distribution needs.

Restrict the map to a specific time window with --start / --end (same format as in video mode). Speed, distance, map bounds and the animation slider all reflect only the filtered segment:

python -m gpx_player.openseamap --files example-data/osm-demo-Alex.gpx \
     --start 2024-07-24T18:10:00+0200 --end 2024-07-24T18:30:00+0200

Use --tail-length short|normal|long to control the moving tail length in the map track visibility control.

A more sophisticated example, that produced a video above:

gpx-player example-data/track1.gpx example-data/track2.gpx example-data/track3.gpx \
       --start 2023-07-01T10:53:00+0000 \
       --names "Mr. Pommeroy" "Miss Sophie" "Sir Toby²" \
       --title "Elbe-Damm Regatta (01.07.2023), Race 1" \
       --race_start 2023-07-01T10:58:00+0000 --marks example-data/marks.txt -g

Additional parameters:

  • --title or -t: The title of the video
  • --start or -s: start time in the format 2023-06-30T12:53:00+0200, all points before that will not be plotted
  • --end or -e: end time in the format 2023-06-30T13:53:00+0200, all points after that will not be plotted
  • --names or -n: names of the participants (otherwise the file names will be used in the legend)
  • --race_start, -r: Race start time in the format YYYY-MM-DDTHH:MM:SS%z, e.g. 2023-07-01T12:29:00+0200
  • --names or -n: Names of the participants
  • --marksor-m: The file with the static marks to put onto the map. One pair of coordinates per line, see below.
  • --gif or -g: Save as GIF moving picture instead of MP4
  • --timezone or -tz: Local timezone to use for processing timestamps, e.g. America/Los_Angeles, see here (default: Europe/Berlin).

Marks

The script also supports visualizing predefined marks on the map, which can be useful for events like sailing regattas. The marks are defined as a list of (latitude, longitude) tuples in a separate text file and can be added to the script as follows:

53.542484632728, 9.801163896918299
53.542997846049374, 9.80611324310303
53.54823800356785, 9.812614917755129
53.54921647691311, 9.807373881340029
53.54508251196638, 9.80433225631714

Getting GPX Files

GPX files can be obtained from several GPS-tracking services:

  • Strava: Go to the activity page and click on the wrench icon. Then select "Export GPX".
  • Garmin Connect: Open the activity, go to the gear icon and select "Export to GPX".
  • Endomondo: From the workout page, click the three-dot menu icon and select "Export". Then choose "GPX".

GPX Validation

For the gpx-player to work properly, it needs the correct GPX files. You can verify that you are inputting the correct file by using the special validator included in this package.

The validator.py script is a command-line utility and module for validating GPX files. It checks for XML schema conformance and timestamp consistency, supporting both strict and lenient modes. Errors are raised as GPXValidationError which can be caught in Python code. To run as a CLI tool, use:

gpx-validate path/to/yourfile.gpx --strict

Use as a Python module

from gpx_player.validator import validate_gpx, GPXValidationError

try:
    validate_gpx("path/to/yourfile.gpx", strict=True)
    print("GPX file is valid")
except GPXValidationError as e:
    print("GPX validation failed:", e)

The --strict parameter is optional. In most cases you do not need it, because files that strictly correspond to the GPX schema are rare. For example, almost all modern files contain coordinates, elevations and time stamps with more decimal places than originally planned.

Also, to better understand your GPX file, you can use the gpxinfo console command that comes with gpxpy. If you are already using the player, you have it:

$ gpxinfo example-data/osm_track1.gpx 
File: example-data/osm_track1.gpx
    Waypoints: 0
    Routes: 0
    Length 2D: 9.621km
    Length 3D: 9.648km
    Moving time: 01:05:22
    Stopped time: n/a
    Max speed: 3.12m/s = 11.22km/h
    Avg speed: 2.46m/s = 8.85km/h
    Total uphill: 97.20m
    Total downhill: 98.40m
    Started: 2024-07-24 15:59:05+00:00
    Ended: 2024-07-24 17:04:27+00:00
    Points: 776
    Avg distance between points: 12.40m

GPX Cleanup

For convenience, the repository provides clean_gpx.py. This utility first validates a GPX file using validator.py and then removes all <extensions> blocks using :func:remove_extensions_tags from gpx_utils. By default the cleaned file is saved alongside the original with _noext appended to its name. If the optional --overwrite flag is used, the original file is modified in place.

Run it from the command line as follows:

python -m gpx_player.clean_gpx path/to/yourfile.gpx [--overwrite]

If validation fails, the command exits with an error message. The output reports how many extension blocks were removed.

Support

Now you can buy me a coffee to encourage further development!

"Buy Me A Coffee"

Release files for gpx-player 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gpx-player 0.5.1
File Size Uploaded
gpx_player-0.5.1.tar.gz 49.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gpx-player 0.5.1
File Interpreter ABI Platform
gpx_player-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 99.0 kB

Release files / gpx_player-0.5.1.tar.gz

Download URL gpx_player-0.5.1.tar.gz
Size 49.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5629af6beea83e09e516c6daa24851072568997c66ffdcf1aa830bbf7b209f93
BLAKE2b-256 checksum
How to use checksums
b01e0e947ed5d6d5a22e15a4c612ca030a7b95145dd2a29a204c0f7703a85b0d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / gpx_player-0.5.1-py3-none-any.whl

Download URL gpx_player-0.5.1-py3-none-any.whl
Size 49.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e3217c20ced52ddd86c6be50522b134f51f5742119d2c5045251477ffe0cd953
BLAKE2b-256 checksum
How to use checksums
973a13dddd22c0ae464e2d030993af3fa730a258444024ae79c138bff03d8da8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.3

2 release files

0.5.2

2 release files

This release

0.5.1 This release

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page