Tracks, Routes and Waypoints: The Anatomy of a GPX File

Here's a puzzle. Take the official course file for the Ultra-Trail du Mont-Blanc - one single GPX file - and open it in two different apps. One tells you the route is 172.4 km. The other says 288 km, and draws a strange spider's web of straight lines slicing across the Mont Blanc glaciers.

Same file. Same bytes. A 116 km disagreement.

A route map showing the real UTMB course plus phantom straight lines cutting across the Mont Blanc massif

The culprit isn't a corrupt file. It's a perfectly legal GPX making full use of the format - and one of the apps (ours, as it happens, until we fixed it) misreading a 30-year-old design decision. To see why, you need to know what actually lives inside a GPX file.

Three containers, three meanings

Strip away the angle brackets and a GPX file is just XML holding up to three kinds of location data:

<gpx creator="SomeApp" version="1.1">

  <wpt lat="45.9235" lon="6.8690">      <!-- a WAYPOINT: one named place -->
    <name>Chamonix Start Line</name>
  </wpt>

  <rte>                                  <!-- a ROUTE: places to go VIA -->
    <rtept lat="45.9235" lon="6.8690"/>
    <rtept lat="45.8875" lon="6.7742"/>
  </rte>

  <trk>                                  <!-- a TRACK: the line itself -->
    <trkseg>
      <trkpt lat="45.923539" lon="6.869039"><ele>1043.3</ele></trkpt>
      <trkpt lat="45.923553" lon="6.869055"><ele>1043.5</ele></trkpt>
    </trkseg>
  </trk>

</gpx>

Any combination is allowed - all three at once, if the author likes. They mean very different things:

Diagram of the three GPX containers: a waypoint as a single pin, a route as sparse ordered points with unspecified dashed connections, and a track as a dense breadcrumb line

A track (<trk>) is geometry. Dense breadcrumbs - a point every few metres, usually with elevation, sometimes with timestamps - describing the exact line. When you say "the GPX", you almost always mean the track.

One trap hides here: a track describes geometry, not necessarily history. A ride recorded by your bike computer is a track - but so is a course planned in Komoot that nobody has ever ridden. Both use identical <trk> markup; the presence of <time> stamps on the points is usually the clue to which one you're holding:

Kind of track Example Timestamps?
Recorded An activity off your GPS watch Usually yes
Planned A course exported from a route planner Usually no

A route (<rte>) is a list of intentions. A sparse, ordered sequence of points meaning "go via here, then here, then here". It's a relic of 1990s GPS units that couldn't hold dense lines: you gave them a couple of dozen points and the device navigated between them.

A route is not a polyline. Two route points do not mean "draw a straight line between these coordinates" - they mean "visit these locations, in this order". What happens in between is deliberately unspecified: a device is free to calculate roads between them, snap them to trails, draw a straight segment, or ask you. This single fact is the root cause of the 288 km bug.

Route points can also carry nearly everything a waypoint can - a name, a description, an icon. That's how early GPS units encoded turn-by-turn instructions:

<rtept lat="45.8875" lon="6.7742">
  <name>Turn right</name>
  <desc>Take the forest road</desc>
  <sym>Right</sym>
</rtept>

A useful way to hold the pair in your head: an <rtept> is a waypoint inside a sequence; a <wpt> is a waypoint independent of one.

A waypoint (<wpt>) is a standalone place. No order, no line - a named pin: a water source, a summit, your parked car. (PitStopper imports these as POIs, and writes your POIs out as waypoints - that's a whole article of its own.)

The UTMB file, dissected

The official UTMB 2026 course file (exported from TrailSplits) is a lovely specimen because it carries two containers at once. Here's what's actually in it:

What Value
File size 1.8 MB of plain XML
creator attribute TrailSplits
Track points (<trkpt>) 26,738 - one every ~6.4 metres
Elevation tags (<ele>) 26,738 - every point, from 810 m to 2,538 m
Timestamps (<time>) 0 - it's a planned course, nobody has ridden it
Route points (<rtept>) 35 - unnamed, sparse
Waypoints (<wpt>) 0

And its metadata block politely hints at the answer to the puzzle up front:

<metadata>
  <name>TrailSplits Route</name>
  <link href="https://trailsplits.com/planner">
    <text>TrailSplits route link</text>
  </link>
  <desc>172.4 km Hike/Run route via gpx. Planned with TrailSplits...</desc>
</metadata>

The file describes itself as 172.4 km - and in this case the 26,738-point track measures exactly that. But note the wording: metadata is descriptive, not authoritative. A <desc> is free text typed by whoever made the file; a stale or careless one could say "100 mile ultra" above 400 km of track. Distances in names, descriptions and links are hints. Geometry is the source of truth - always measure the track.

So what are those 35 route points? Plot them and it's obvious: they're the race's checkpoints and aid stations, in race order - a checklist wearing a route's clothes. The organiser packed the course (track) and the controls (route) into one convenient file. Completely legal. Rather elegant, actually.

How 172 km became 288 km

Now the bug. An app that naively treats every line-like thing in the file as "the route" will read the track, then hit the route, and staple the two together: after the final track point back in Chamonix, the line suddenly teleports checkpoint-to-checkpoint around the entire massif in straight lines - over the Col du Bonhomme, across the glaciers, through several mountains. Thirty-five points, ~115 phantom kilometres, and that spider's web in the screenshot above. (The flat-lining tail on the elevation profile is the giveaway - checkpoint hops carry no elevation.)

Garmin Connect happens to interpret this file the way you'd hope: the track becomes the course geometry (172.61 km) and the route points don't add distance.

Here's the part that makes the whole mess honest, though: GPX itself never says who wins. The specification defines what the three containers are; it says nothing about which one an application should prefer when a file carries more than one. The format describes containers, not behaviour. A turn-by-turn navigation app could reasonably prioritise routes - that's what they were designed for; a fitness platform reasonably prioritises tracks - that's the drawable line. "Track wins over route" is a convention that grew out of modern outdoor use, not a rule anyone can point to in the spec. Which is exactly how two correct-ish apps can read one legal file 116 km apart.

The same UTMB file loaded correctly: a clean 172.4 km loop around Mont Blanc

That's the rule PitStopper follows now too - with one improvement we think other apps should steal: instead of throwing the <rte> away, it imports the checkpoints as POIs, pinned to the track with distances, ready to export to your bike computer or watch. The organiser put the aid stations in the file; it seems rude to ignore them.

The rest of the family: what else lives in a GPX

Tracks, routes and waypoints are the big three, but the GPX 1.1 schema has more in the cupboard. A field guide:

<metadata> - file-level information: name, desc, author, copyright, link, a file time, keywords, and bounds (the bounding box, so an app can frame the map before parsing everything).

<trkseg> - track segments, and a classic developer trap. One track can hold several segments; the convention is that a new one starts wherever recording was interrupted - a tunnel, a paused watch, airplane mode, a battery swap. The gap between segments is intentional: the device genuinely doesn't know where you went, and an app must not bridge it with an invented straight line (that's the miniature cousin of the 288 km bug).

Diagram of one track with two segments and a crossed-out straight line bridging the gap, labelled do not invent this line

Multiple whole <trk> elements are also legal - one file holding several distinct courses (PitStopper shows a picker when it meets one).

Per-point children - each <trkpt>, <rtept> and <wpt> can carry:

  • <ele> - elevation in metres (the UTMB file has all 26,738)
  • <time> - ISO timestamp; this is how recorded rides know your moving time
  • <name>, <desc>, <cmt> - naming and notes; <cmt> (comment) is the traditional field for text meant for the device, and it's where PitStopper stashes your POI notes on export
  • <sym> and <type> - the icon and category a device should show; write <sym>Drinking Water</sym> and a Garmin renders the proper glyph (PitStopper's exports do exactly this)

The deep cuts - GPS-nerd fields from the format's surveying roots, rarely seen in the wild: <magvar> (magnetic variation), <geoidheight>, <fix> (2d/3d/dgps), <sat> (satellites used), <hdop>/<vdop>/<pdop> (dilution-of-precision - how much to trust the fix), <dgpsid>. If you ever meet a file using all of these, it probably came from a professional survey unit.

<extensions> - the format's escape hatch, and the wildest corner. Anything a vendor wants: Garmin's TrackPointExtension adds heart rate, cadence and temperature to each point; its WaypointExtension adds display colours; power meters write watts. Extensions are namespaced XML, so apps that don't understand them skip them safely - which is also why your heart-rate data silently vanishes when a file passes through an app that doesn't preserve extensions.

The creator attribute - the little signature on the <gpx> tag (creator="TrailSplits", creator="StravaGPX"...). More load-bearing than it looks: some platforms treat files differently depending on who made them - Garmin, for instance, trusts elevation data from some sources and re-smooths it from others. PitStopper preserves the original creator on export for exactly that reason.

Conversion damage: GPX is only lossless in theory

One more thing worth knowing, because it explains so many "where did my data go?" mysteries. Files rarely live in one app. A typical journey:

Komoot -> GPX export -> Garmin Connect -> ride it -> Strava -> GPX export -> a friend

Every hop is a chance for something to fall off. One app discards routes, another flattens extensions (bye, heart rate), a third strips timestamps, simplifies 26,000 points down to 2,000, or quietly replaces your elevation with its own model. Each program writes valid GPX out the other side - just less of it.

GPX is a lossless container only if every application in the chain preserves everything. In practice, it's a lossy interchange format. If some field matters to you - power data, your carefully named waypoints, the original elevation - check it survived the journey, and keep the original file.

The takeaway

A GPX file is not "a route" - it's a container that can hold geometry (tracks), intentions (routes), and places (waypoints), plus a generous attic of metadata. The three don't compete; they answer different questions.

The deeper story isn't the format's age - it's the mismatch of eras. Routes were designed for 1990s handhelds that navigated between a handful of points; modern apps assume every file resolves to one canonical drawable line. The format never picked a side, so every application has to - and when two of them choose differently, you get a 116 km disagreement over the same bytes.

When a file behaves strangely - wrong distance, phantom lines, missing data - open it in a text editor. It's human-readable XML, and thirty seconds of scrolling usually reveals which container is playing which role. That's precisely how the UTMB's extra kilometres unmasked themselves: grep "<rtept" and there were the 35 aid stations, hiding in plain sight.

New to GPX entirely? Start with our complete beginner's guide. Curious what happened when we let PitStopper loose on this very file? The UTMB night-planning post puts those 35 checkpoints - and every refuge and shelter on the course - to work.


Course file © the UTMB organisation, exported via TrailSplits. Screenshots from PitStopper; the 288 km version is a faithful reconstruction of the parsing bug we found (and fixed) while writing this. Thumbnail photo by Jack Anstey on Unsplash.