GPX
editGPX is the XML format of GPS devices and sport apps. Gpx reads GPX 1.0 and 1.1, and writes GPX 1.1. This example reads an activity and computes the length of its track:
GeoFeatureCollection features;
using (var stream = File.OpenRead("activity.gpx"))
{
features = Gpx.Read(stream);
}
// A track is a multi-line string, with one line per segment
var track = features.Single(feature => (string?)feature.Properties[Gpx.TypePropertyName] is "trk");
var segments = ((GeoMultiLineString)track.Geometry!).LineStrings;
var length = segments.Sum(segment => segment.GetLength());
Assert.Equal("Lunch Run", track.Properties["name"]);
Assert.Equal(173, Math.Round(length, MidpointRounding.AwayFromZero));What the reader keeps
Each waypoint, route and track is a feature, in the order of the document:
| GPX | Geometry |
|---|---|
wpt, a waypoint | GeoPoint |
rte, a route | GeoLineString |
trk, a track | GeoMultiLineString, a line per segment, even when it has one |
- The elevations (
ele) are the altitudes. The name of the file (metadata/name) is the Name of the collection. - The property named by Gpx.TypePropertyName,
gpxType, iswpt,rteortrk. - The other properties are
name,cmt,desc,src,typeandsymas strings, thenumberof a route or a track as adouble, and thetimeof a waypoint as aDateTimeOffset. - The
timeof each point is the Time of its position. The other data of the points of the routes and tracks, such as the heart rate of theirextensions, is dropped. The links and the other data of the waypoints are dropped too.
The times give the speed of a track:
GeoFeatureCollection features;
using (var stream = File.OpenRead("run.gpx"))
{
features = Gpx.Read(stream);
}
// The time of each point of the track is the Time of its position
var segment = ((GeoMultiLineString)features[0].Geometry!).LineStrings[0];
var start = segment.Positions[0].Time!.Value;
var end = segment.Positions[^1].Time!.Value;
var speed = segment.GetLength() / (end - start).TotalSeconds; // meters per second
Assert.Equal(new DateTimeOffset(2024, 9, 1, 7, 0, 0, TimeSpan.Zero), start);
Assert.Equal(11.6, speed, 1);Write a GPX file
GeoFeatureCollection features =
[
// A point is a waypoint, and a multi-line string is a track with one segment per line
new GeoFeature(new GeoPoint(new GeoPosition(6.8652, 45.8326, 4808)), new Dictionary<string, object?>(StringComparer.Ordinal)
{
["name"] = "Mont Blanc",
["time"] = new DateTimeOffset(2024, 7, 1, 12, 0, 0, TimeSpan.Zero),
}),
new GeoFeature(new GeoMultiLineString([new GeoLineString([new(6.8693, 45.9237, 1035), new(6.8814, 45.8775, 2372)])]), new Dictionary<string, object?>(StringComparer.Ordinal)
{
["name"] = "Ascent",
}),
];
features.Name = "Mont Blanc";
using (var stream = File.Create("mont-blanc.gpx"))
{
Gpx.Write(features, stream, new GpxWriterOptions { Creator = "My application", Indented = true });
}- A point is a waypoint, a line is a route, and a multi-line is a track. Set
gpxTypetotrkto write a line as a track of one segment. - As GPX requires, the waypoints come first, then the routes, then the tracks.
- The time of a position is the
timeof its point, in UTC. A waypoint whose position has no time gets itstimeproperty. - The writer writes the properties the reader reads; the other properties, the identifiers and the measures are dropped, as GPX has no element for them.
- GPX has no polygons: writing a polygon, a collection or a feature without a geometry throws an
ArgumentException. - GpxWriterOptions sets the Creator of the file and whether it is Indented.