# GPX to GeoJSON converter

> Turn a GPX file from a GPS watch, Strava or Garmin Connect into a GeoJSON FeatureCollection for Leaflet, MapLibre or your own code. Point times and heart rate come along. Free, and no account needed.

Open the tool: https://spatibase.com/tools/gpx-to-geojson/

The Spatibase GPX to GeoJSON converter reads a GPX file and writes an RFC 7946 FeatureCollection in your web browser. How the GPX maps to GeoJSON:

- **Tracks become lines.** A `LineString`, or a `MultiLineString` with one part per `trkseg`, with the elevation as the third coordinate.
- **Per-point data stays.** Times, heart rate, cadence, temperature and power sit in `properties.coordinateProperties`, one array entry per point.
- **Waypoints become Points.** With `name`, `description`, `sym`, `type` and vendor fields such as OsmAnd’s `osmand_icon`.
- **Nothing is uploaded.** Files up to 50 MB, free, no account.

## What it does
- Convert GPX to GeoJSON (RFC 7946, WGS 84)
- Tracks as LineString or MultiLineString, one part per track segment
- Elevation kept as the third coordinate
- Point times, heart rate, cadence, temperature and power in coordinateProperties
- Reads the heart rate in Garmin Connect (ns3) and Strava (gpxtpx) files
- Map preview of every track, route and waypoint
- Runs in the browser: nothing is uploaded

## How to convert GPX to GeoJSON
1. Find a GPX: an activity exported from Strava or Garmin Connect, a route planned on gpx.studio or Komoot, or a log straight off a GPS unit.
2. Drop it on the box above, or press Choose a file. The sample holds a walk through Barcelona, three areas saved as closed tracks and five waypoints.
3. Check the preview: every track, route and waypoint is one feature in the list.
4. Press Download GeoJSON, or Copy GeoJSON to paste it into geojson.io or your code.
5. Load it in Leaflet with `L.geoJSON(data)` or in MapLibre as a `geojson` source, or import it into a Spatibase project.

## Formats and limits
- **Opens:** .gpx, up to 50 MB. Other map files (KML, KMZ, GPX, GeoJSON, shapefile, CSV) convert too.
- **Downloads:** GeoJSON, CSV of the same features.
- **Privacy:** the file is read in your browser and never uploaded.
- Try it with a sample: https://spatibase.com/tools/gpx-to-geojson/?sample=1
- Download the sample file (the one Open a sample opens): https://spatibase.com/tools/samples/barcelona.gpx

## How GPX elements land in the GeoJSON
| What | What happens |
|---|---|
| Track (`trk`) | A `LineString` feature, or a `MultiLineString` when the track has more than one segment (`trkseg`). Its `_gpxType` is `"trk"`. |
| Route (`rte`) | A `LineString` through the route points, with `_gpxType: "rte"`. The names of single route points (`rtept`) are not kept. |
| Waypoint (`wpt`) | A `Point` feature with `name`, `description` (from `desc`), `cmt`, `sym`, `type` and `time`. |
| Elevation (`ele`) | The third number of a position: `[2.17, 41.38, 12.4]`. A point without `<ele>` keeps two numbers, so one line can mix both. |
| Point times | `coordinateProperties.times`: one array per track, or one per segment for a `MultiLineString`. The track’s own `time` is its first point’s time. |
| Heart rate, cadence, temperature, power | `coordinateProperties.heart`, `cads`, `atemps` and `powers`, aligned with the coordinates, with `null` where a point has no value. |
| Vendor fields in their own namespace | Kept with the prefix: OsmAnd’s `<osmand:icon>` becomes `osmand_icon`. |
| Feature order | Tracks first, then routes, then waypoints, whatever the order in the file. |
| Not kept | The file’s metadata (author, bounds), route-point names and times, and any segment or route with a single point. |

## Reading a GPS track in GeoJSON
**GeoJSON has no time.** RFC 7946 stores shapes and properties, nothing per vertex. `coordinateProperties` is a convention of togeojson, the open-source library this converter uses: arrays that run parallel to the coordinates. GDAL and GPSBabel don’t write it, and Leaflet and MapLibre ignore it when they draw.

Three details to handle when you read the file in code:

- **Times can be shorter than the line.** Only points with a `<time>` are listed. If a few points have none, `times[i]` no longer belongs to `coordinates[i]`, so compare the lengths before pairing them.
- **Segments mark gaps.** A `MultiLineString` part ends where recording stopped. Flattening the parts into one line connects the gap with a straight stroke.
- **Sensor arrays hold nulls.** A point without a heart-rate reading is `null` in `heart`, so filter before you average.

**Need one feature per point?** For charts and tables, the GPX to CSV converter writes a row per track point with time, elevation and heart rate.

## Convert GPX to GeoJSON in JavaScript
togeojson (`@tmcw/togeojson`) reads GPX in a browser or in Node.js. In Node it needs a DOM parser such as `@xmldom/xmldom`:

```js
import { readFile, writeFile } from 'node:fs/promises'
import { DOMParser } from '@xmldom/xmldom' // browsers have DOMParser built in
import { gpx } from '@tmcw/togeojson'

const xml = new DOMParser().parseFromString(await readFile('ride.gpx', 'utf8'), 'text/xml')
const geojson = gpx(xml) // tracks, then routes, then waypoints
await writeFile('ride.geojson', JSON.stringify(geojson))
```

**Two fixes this page adds.** Plain togeojson copies the last point’s sensor values onto the whole track (`gpxtpx_hr: "151"` on a Strava run), and it misses the heart rate in Garmin Connect files, which write `ns3:` instead of `gpxtpx:`. The converter renames the prefix and drops those keys.

**On the command line,** `ogr2ogr -f GeoJSON tracks.geojson ride.gpx tracks` writes each track as a 2D `MultiLineString` without times, and its `track_points` layer gives one Point per track point with `ele` and `time`. GPSBabel’s `-o geojson` joins a track’s segments into one line and drops the times.

## Frequently asked questions
### Where do the GPX timestamps go in the GeoJSON?
Into properties.coordinateProperties.times, one ISO 8601 time per track point, in the same order as the coordinates. A track with several segments gets one array per segment. The geometry itself has no place for time in GeoJSON.

### Is elevation kept when converting GPX to GeoJSON?
Yes, as the third value of each position, in metres: [2.17, 41.38, 12.4]. RFC 7946 allows a third value, and web maps draw the line flat and ignore it.

### Why is my track a MultiLineString?
Your GPX track has more than one segment (trkseg): recording stopped and started again. Each segment becomes one part, so no line is drawn across the gap. A track with a single segment comes out as a plain LineString.

### How do I show a Strava or Garmin activity on a Leaflet map?
Export the activity as GPX, convert it here and load the file with L.geoJSON(data).addTo(map). Leaflet draws the track and the waypoints; the times and heart rate stay in the properties for your own popups or charts.

### What is the _gpxType property?
togeojson marks every line with where it came from: "trk" for a recorded track and "rte" for a planned route. Waypoints have none. Filter on it when you only want the tracks.

## Next step
Turn the file into a live map your team can edit: **Save to Spatibase (free)** on the page saves it to a free Spatibase project (https://app.spatibase.com/signup), your places on a map you can edit and a live GeoJSON address for your site. Or download the GeoJSON and import it by hand (https://spatibase.com/docs/content/import/).
