Usage¶
Reading a GPX file¶
from gpx import read_gpx
# Read GPX data from file
gpx = read_gpx("path/to/file.gpx")
# Access basic properties
print(f"Creator: {gpx.creator}")
print(f"Number of waypoints: {len(gpx.wpt)}")
print(f"Number of tracks: {len(gpx.trk)}")
print(f"Number of routes: {len(gpx.rte)}")
Working with waypoints¶
from decimal import Decimal
from gpx import Latitude, Longitude, Waypoint, read_gpx
# Read GPX data from file
gpx = read_gpx("path/to/file.gpx")
# Create a new waypoint
waypoint = Waypoint(
lat=Latitude("52.3676"),
lon=Longitude("4.9041"),
name="Amsterdam",
desc="Capital of the Netherlands",
ele=Decimal("2.0"),
)
# Add waypoint to GPX data
gpx.wpt.append(waypoint)
# Iterate over waypoints
for idx, wpt in enumerate(gpx.wpt):
print(f"{idx}: ({wpt.lat}, {wpt.lon})")
Working with tracks and statistics¶
from gpx import read_gpx
# Read GPX data from file
gpx = read_gpx("path/to/file.gpx")
# Access track data
for track in gpx.trk:
print(f"Track: {track.name}")
# Get statistics from the track
print(f" Total distance: {track.total_distance:.2f} meters")
print(f" Total duration: {track.total_duration}")
print(f" Average speed: {track.avg_speed:.2f} m/s")
print(f" Max elevation: {track.max_elevation} meters")
print(f" Total ascent: {track.total_ascent} meters")
# Iterate over track segments and points
for track_segment in track.trkseg:
print(f" Segment with {len(track_segment.trkpt)} points")
for track_point in track_segment.trkpt:
print(
f" Point: ({track_point.lat}, {track_point.lon}) at {track_point.time}"
)
Creating a GPX from scratch¶
import datetime as dt
from decimal import Decimal
from gpx import (
GPX,
Latitude,
Longitude,
Metadata,
Track,
TrackSegment,
Waypoint,
)
# Create track points
track_points = []
for i in range(5):
point = Waypoint(
lat=Latitude(Decimal("52.0") + i * Decimal("0.01")),
lon=Longitude(Decimal("4.0") + i * Decimal("0.01")),
ele=Decimal("10.0") + Decimal(i) * Decimal("2.0"),
time=dt.datetime.now(dt.UTC),
)
track_points.append(point)
# Create a track with segments
track_segment = TrackSegment(trkpt=track_points)
track = Track(name="Morning Run", trkseg=[track_segment])
# Create metadata
metadata = Metadata(
name="My GPS Track",
desc="A sample track",
time=dt.datetime.now(dt.UTC),
)
# Create GPX object (creator defaults to "*gpx*")
gpx = GPX(
creator="My Application",
metadata=metadata,
trk=[track],
)
Writing GPX files¶
from gpx import from_string
# Write GPX data to file
gpx.write_gpx("output.gpx")
# Convert to string
gpx_string = gpx.to_string()
print(gpx_string)
# Parse from string
gpx = from_string(gpx_string)
Working with routes¶
from gpx import Latitude, Longitude, Route, Waypoint
# Create route points (waypoints)
route_point_1 = Waypoint(
lat=Latitude("52.3676"),
lon=Longitude("4.9041"),
name="Start: Amsterdam Centraal",
)
route_point_2 = Waypoint(
lat=Latitude("52.3731"),
lon=Longitude("4.8922"),
name="Dam Square",
)
# Create a route
route = Route(name="City Tour", rtept=[route_point_1, route_point_2])
gpx.rte.append(route)
# Access route statistics
print(f"Route distance: {route.total_distance:.2f} meters")
Working with GPX Extensions¶
gpx supports reading and writing GPX extensions from any namespace, enabling lossless round-trip handling of vendor-specific data like Garmin’s TrackPointExtension:
from gpx import read_gpx
# Define extension namespace
GARMIN_TPX = "http://www.garmin.com/xmlschemas/TrackPointExtension/v2"
# Read GPX file with extensions
gpx = read_gpx("activity.gpx")
# Access extension data from track points
for track in gpx.trk:
for track_segment in track.trkseg:
for track_point in track_segment.trkpt:
if track_point.extensions:
if (
hr := track_point.extensions.get_int("hr", namespace=GARMIN_TPX)
) is not None:
print(f"Heart rate: {hr} bpm")
Creating GPX files with extensions:
import xml.etree.ElementTree as ET
from decimal import Decimal
from gpx import (
Extensions,
GPX,
Latitude,
Longitude,
Track,
TrackSegment,
Waypoint,
)
# Register namespace prefix for cleaner XML output
GARMIN_TPX = "http://www.garmin.com/xmlschemas/TrackPointExtension/v2"
ET.register_namespace("gpxtpx", GARMIN_TPX)
# Create extension element
tpx = ET.Element(f"{{{GARMIN_TPX}}}TrackPointExtension")
hr = ET.SubElement(tpx, f"{{{GARMIN_TPX}}}hr")
hr.text = "145"
# Create waypoint with extensions
point = Waypoint(
lat=Latitude("52.0"),
lon=Longitude("4.0"),
extensions=Extensions(elements=[tpx]),
)
# Build GPX with the point
gpx = GPX(trk=[Track(trkseg=[TrackSegment(trkpt=[point])])])
gpx.write_gpx("with_extensions.gpx")
Converting to other formats¶
gpx supports converting GPX data to various formats:
from gpx import GPX, read_gpx
gpx = read_gpx("path/to/file.gpx")
# Write to file formats
gpx.write_gpx("output.gpx") # GPX file
gpx.write_geojson("output.geojson") # GeoJSON file
gpx.write_kml("output.kml") # KML file (Google Earth)
# Convert to data formats (strings/bytes)
wkt_string = gpx.to_wkt() # Well-Known Text
wkb_bytes = gpx.to_wkb() # Well-Known Binary
# Access GeoJSON-compatible data via the __geo_interface__ property
geojson_dict = gpx.__geo_interface__
Reading from other file formats¶
gpx can read data from various file formats:
from gpx import read_gpx, read_geojson, read_kml
# Read from files
gpx = read_gpx("path/to/file.gpx")
gpx = read_geojson("path/to/file.geojson")
gpx = read_kml("path/to/file.kml")
Converting between file formats¶
gpx can convert files between the GPX, GeoJSON and KML file formats directly:
from gpx import convert_file
# Formats are auto-detected from the file extensions...
convert_file("input.gpx", "output.geojson")
convert_file("input.kml", "output.gpx")
# ... or can be given explicitly
convert_file("input.json", "output.gpx", input_format="geojson")
Converting from data formats¶
gpx can convert from data formats (strings, bytes, objects):
from gpx import from_geo_interface, from_wkt, from_wkb
# Convert from WKT (Well-Known Text)
gpx = from_wkt("POINT (4.9041 52.3676)")
gpx = from_wkt("LINESTRING (4.9 52.3, 4.91 52.31, 4.92 52.32)")
# Convert from WKB (Well-Known Binary)
gpx = from_wkb(wkb_bytes)
# Convert from any object that implements the __geo_interface__ protocol (e.g., Shapely)
from shapely.geometry import Point, LineString
point = Point(4.9041, 52.3676)
gpx = from_geo_interface(point)
line = LineString([(4.9, 52.3), (4.91, 52.31), (4.92, 52.32)])
gpx = from_geo_interface(line)
# Or convert from a GeoJSON dict directly
geojson = {"type": "Point", "coordinates": [4.9041, 52.3676]}
gpx = from_geo_interface(geojson)
Editing and merging GPX data¶
gpx provides operations for editing and merging GPX data. All operations are pure: they return a new GPX instance and never mutate the input:
import datetime as dt
from gpx import (
crop,
filter_points,
merge,
read_gpx,
reduce_precision,
reverse,
shift_time,
simplify,
smooth,
split,
strip_extensions,
strip_metadata,
trim,
)
gpx = read_gpx("path/to/file.gpx")
# Crop to a geographic bounding box
cropped = crop(gpx, min_lat=52.0, max_lat=53.0, min_lon=4.0, max_lon=5.0)
# Trim to a date/time range
trimmed = trim(gpx, start=dt.datetime(2024, 1, 1, 10, 0, 0, tzinfo=dt.UTC))
# Reverse routes and/or tracks
reversed_gpx = reverse(gpx)
# Strip metadata (fields)
anonymous = strip_metadata(gpx, author=True, copyright=True)
bare = strip_metadata(gpx) # removes all metadata
# Reduce coordinate and/or elevation precision
reduced = reduce_precision(gpx, coordinate_precision=6, elevation_precision=1)
# Filter points with an arbitrary predicate
with_elevation = filter_points(gpx, lambda point: point.ele is not None)
# Split track segments at time and/or distance gaps
split_gpx = split(gpx, time_gap=dt.timedelta(minutes=10))
# Simplify tracks and routes (Ramer-Douglas-Peucker, tolerance in metres)
simplified = simplify(gpx, tolerance=10.0)
# Smooth track and route coordinates and elevations (moving average)
smoothed = smooth(gpx, window=5)
# Shift all point timestamps (e.g., to fix timezone mistakes or clock drift)
shifted = shift_time(gpx, dt.timedelta(hours=-2))
# Strip all extensions (e.g., heart rate, cadence, temperature)
without_extensions = strip_extensions(gpx)
# Merge multiple GPX instances into one
merged = merge([read_gpx("one.gpx"), read_gpx("two.gpx")])
Validating against the GPX 1.1 schema¶
gpx can validate GPX data against the GPX 1.1 schema, catching problems that lenient parsing silently ignores (unknown/misspelled elements, duplicates, out-of-order children, out-of-range values, and more):
from gpx import read_gpx, validate
# Validate a file path, a string of GPX content, or a GPX instance
result = validate("path/to/file.gpx")
if not result.is_valid:
for issue in result.errors:
# e.g. "ERROR line 8 gpx > wpt[0]: unknown element <nmae> (did you mean <name>?)"
print(issue)
# Warnings are off-spec but readable (e.g. out-of-order elements, naive timestamps)
for warning in result.warnings:
print(warning)
# Validate a programmatically built GPX before writing it
gpx = read_gpx("path/to/file.gpx")
assert validate(gpx).is_valid
# Opt in to strict parsing: raises InvalidGPXError on any schema error
strict_gpx = read_gpx("path/to/file.gpx", strict=True)
Command-Line Interface¶
gpx provides a command-line interface (CLI) for common GPX operations:
# Validate a GPX file against the GPX 1.1 schema
gpx validate path/to/file.gpx
gpx validate --strict path/to/file.gpx # also fail on warnings
gpx validate --json path/to/file.gpx # machine-readable report
# Show information and statistics about a GPX file
gpx info path/to/file.gpx
gpx info --json path/to/file.gpx # Output as JSON
# Edit a GPX file
gpx edit input.gpx -o output.gpx --reverse-tracks
gpx edit input.gpx -o output.gpx --min-lat 52.0 --max-lat 53.0
gpx edit input.gpx -o output.gpx --start 2024-01-01T10:00:00 --end 2024-01-01T12:00:00
gpx edit input.gpx -o output.gpx --precision 5 --elevation-precision 1
gpx edit input.gpx -o output.gpx --strip-all-metadata
gpx edit input.gpx -o output.gpx --split-time-gap 600
gpx edit input.gpx -o output.gpx --simplify 10 --smooth 5
gpx edit input.gpx -o output.gpx --shift-time -7200
gpx edit input.gpx -o output.gpx --strip-extensions
# Merge multiple GPX files
gpx merge file1.gpx file2.gpx file3.gpx -o merged.gpx
# Convert between formats
gpx convert input.gpx -o output.geojson
gpx convert input.gpx -o output.kml
gpx convert input.geojson -o output.gpx
# Validate the input first with --strict (info, edit, merge and convert):
# prints warnings and aborts on schema errors before doing any work
gpx info --strict path/to/file.gpx
gpx edit --strict input.gpx -o output.gpx --simplify 10
gpx merge --strict file1.gpx file2.gpx -o merged.gpx