Skip to content

Instantly share code, notes, and snippets.

@airbreather
Last active August 27, 2017 14:24
Show Gist options
  • Save airbreather/f374f8085af45ff185e539c0ae4f0024 to your computer and use it in GitHub Desktop.
Save airbreather/f374f8085af45ff185e539c0ae4f0024 to your computer and use it in GitHub Desktop.
using System;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace GpxCore
{
public sealed class Gpx
{
public const string GpxNamespace = "http://www.topografix.com/GPX/1/1";
public static readonly XName GpxElementName = XName.Get("gpx", GpxNamespace);
public GpxWaypoint[] Waypoints;
public GpxTrack[] Tracks;
public GpxRoute[] Routes;
public static Gpx Load(XElement gpxElement) =>
new Gpx
{
Waypoints = gpxElement.Elements(XName.Get("wpt", GpxNamespace)).Select(GpxWaypoint.Load).ToArray(),
Routes = gpxElement.Elements(XName.Get("rte", GpxNamespace)).Select(GpxRoute.Load).ToArray(),
Tracks = gpxElement.Elements(XName.Get("trk", GpxNamespace)).Select(GpxTrack.Load).ToArray(),
};
}
public sealed class GpxWaypoint
{
public double Longitude;
public double Latitude;
public static GpxWaypoint Load(XElement wptElement) =>
new GpxWaypoint
{
Longitude = Double.Parse(wptElement.Attribute("lon").Value, NumberStyles.Float, CultureInfo.InvariantCulture),
Latitude = Double.Parse(wptElement.Attribute("lat").Value, NumberStyles.Float, CultureInfo.InvariantCulture),
};
}
public sealed class GpxTrack
{
public GpxTrackSegment[] Segments;
public static GpxTrack Load(XElement trkElement) =>
new GpxTrack
{
Segments = trkElement.Elements(XName.Get("trkseg", Gpx.GpxNamespace)).Select(GpxTrackSegment.Load).ToArray(),
};
}
public sealed class GpxTrackSegment
{
public GpxWaypoint[] Waypoints;
public static GpxTrackSegment Load(XElement trksegElement) =>
new GpxTrackSegment
{
Waypoints = trksegElement.Elements(XName.Get("trkpt", Gpx.GpxNamespace)).Select(GpxWaypoint.Load).ToArray(),
};
}
public sealed class GpxRoute
{
public GpxWaypoint[] Waypoints;
public static GpxRoute Load(XElement rteElement) =>
new GpxRoute
{
Waypoints = rteElement.Elements(XName.Get("rtept", Gpx.GpxNamespace)).Select(GpxWaypoint.Load).ToArray(),
};
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using GpxCore;
namespace GpxDemo
{
internal static class Program
{
private const string GpxList = @"<?xml version=""1.0"" encoding=""utf-8""?>
<myList>
<gpx xmlns=""http://www.topografix.com/GPX/1/1"">
<wpt lon=""-100"" lat=""42"" />
<wpt lon=""-101"" lat=""43"" />
<wpt lon=""-102"" lat=""44"" />
<wpt lon=""-103"" lat=""45"" />
<wpt lon=""-104"" lat=""46"" />
<wpt lon=""-105"" lat=""47"" />
<rte>
<rtept lon=""-100.2"" lat=""42.9"" />
<rtept lon=""-100.3"" lat=""42.8"" />
<rtept lon=""-100.4"" lat=""42.7"" />
<rtept lon=""-100.5"" lat=""42.6"" />
<rtept lon=""-100.6"" lat=""42.5"" />
<rtept lon=""-100.7"" lat=""42.4"" />
</rte>
<rte>
<rtept lon=""-101.2"" lat=""43.9"" />
<rtept lon=""-101.3"" lat=""43.8"" />
<rtept lon=""-101.4"" lat=""43.7"" />
<rtept lon=""-101.5"" lat=""43.6"" />
<rtept lon=""-101.6"" lat=""43.5"" />
<rtept lon=""-101.7"" lat=""43.4"" />
</rte>
<trk>
<trkseg>
<trkpt lon=""-102.2"" lat=""44.9"" />
<trkpt lon=""-102.3"" lat=""44.8"" />
<trkpt lon=""-102.4"" lat=""44.7"" />
<trkpt lon=""-102.5"" lat=""44.6"" />
<trkpt lon=""-102.6"" lat=""44.5"" />
<trkpt lon=""-102.7"" lat=""44.4"" />
</trkseg>
<trkseg>
<trkpt lon=""-103.2"" lat=""42.9"" />
<trkpt lon=""-103.3"" lat=""42.8"" />
<trkpt lon=""-103.4"" lat=""42.7"" />
<trkpt lon=""-103.5"" lat=""42.6"" />
<trkpt lon=""-103.6"" lat=""42.5"" />
<trkpt lon=""-103.7"" lat=""42.4"" />
</trkseg>
</trk>
<trk>
<trkseg>
<trkpt lon=""-112.2"" lat=""47.9"" />
<trkpt lon=""-112.3"" lat=""47.8"" />
<trkpt lon=""-112.4"" lat=""47.7"" />
<trkpt lon=""-112.5"" lat=""47.6"" />
<trkpt lon=""-112.6"" lat=""47.5"" />
<trkpt lon=""-112.7"" lat=""47.4"" />
</trkseg>
<trkseg>
<trkpt lon=""-113.2"" lat=""46.9"" />
<trkpt lon=""-113.3"" lat=""46.8"" />
<trkpt lon=""-113.4"" lat=""46.7"" />
<trkpt lon=""-113.5"" lat=""46.6"" />
<trkpt lon=""-113.6"" lat=""46.5"" />
<trkpt lon=""-113.7"" lat=""46.4"" />
</trkseg>
</trk>
</gpx>
<gpx xmlns=""http://www.topografix.com/GPX/1/1"">
<wpt lon=""-130"" lat=""52"" />
<wpt lon=""-131"" lat=""53"" />
<wpt lon=""-132"" lat=""54"" />
<wpt lon=""-133"" lat=""55"" />
<wpt lon=""-134"" lat=""56"" />
<wpt lon=""-135"" lat=""57"" />
<rte>
<rtept lon=""-130.2"" lat=""52.9"" />
<rtept lon=""-130.3"" lat=""52.8"" />
<rtept lon=""-130.4"" lat=""52.7"" />
<rtept lon=""-130.5"" lat=""52.6"" />
<rtept lon=""-130.6"" lat=""52.5"" />
<rtept lon=""-130.7"" lat=""52.4"" />
</rte>
<rte>
<rtept lon=""-131.2"" lat=""53.9"" />
<rtept lon=""-131.3"" lat=""53.8"" />
<rtept lon=""-131.4"" lat=""53.7"" />
<rtept lon=""-131.5"" lat=""53.6"" />
<rtept lon=""-131.6"" lat=""53.5"" />
<rtept lon=""-131.7"" lat=""53.4"" />
</rte>
<trk>
<trkseg>
<trkpt lon=""-132.2"" lat=""54.9"" />
<trkpt lon=""-132.3"" lat=""54.8"" />
<trkpt lon=""-132.4"" lat=""54.7"" />
<trkpt lon=""-132.5"" lat=""54.6"" />
<trkpt lon=""-132.6"" lat=""54.5"" />
<trkpt lon=""-132.7"" lat=""54.4"" />
</trkseg>
<trkseg>
<trkpt lon=""-133.2"" lat=""52.9"" />
<trkpt lon=""-133.3"" lat=""52.8"" />
<trkpt lon=""-133.4"" lat=""52.7"" />
<trkpt lon=""-133.5"" lat=""52.6"" />
<trkpt lon=""-133.6"" lat=""52.5"" />
<trkpt lon=""-133.7"" lat=""52.4"" />
</trkseg>
</trk>
<trk>
<trkseg>
<trkpt lon=""-132.2"" lat=""57.9"" />
<trkpt lon=""-132.3"" lat=""57.8"" />
<trkpt lon=""-132.4"" lat=""57.7"" />
<trkpt lon=""-132.5"" lat=""57.6"" />
<trkpt lon=""-132.6"" lat=""57.5"" />
<trkpt lon=""-132.7"" lat=""57.4"" />
</trkseg>
<trkseg>
<trkpt lon=""-133.2"" lat=""56.9"" />
<trkpt lon=""-133.3"" lat=""56.8"" />
<trkpt lon=""-133.4"" lat=""56.7"" />
<trkpt lon=""-133.5"" lat=""56.6"" />
<trkpt lon=""-133.6"" lat=""56.5"" />
<trkpt lon=""-133.7"" lat=""56.4"" />
</trkseg>
</trk>
</gpx>
</myList>
";
private static void Main(string[] args)
{
var doc = XDocument.Parse(GpxList);
// get all track segments in the list
var allTrackSegments = from gpxElement in doc.Descendants(Gpx.GpxElementName)
let gpx = Gpx.Load(gpxElement)
from track in gpx.Tracks
from trackSegment in track.Segments
select trackSegment;
foreach (var trackSegment in allTrackSegments)
{
var begin = trackSegment.Waypoints[0];
var end = trackSegment.Waypoints[trackSegment.Waypoints.Length - 1];
Console.WriteLine($"track segment has {trackSegment.Waypoints.Length} waypoints and goes from (lon: {begin.Longitude}, lat: {begin.Latitude}) to (lon: {end.Longitude}, lat: {end.Latitude})");
}
// get all routes that have any points west of 130 degrees west longitude.
var matchingRoutes = from gpxElement in doc.Descendants(Gpx.GpxElementName)
let gpx = Gpx.Load(gpxElement)
from route in gpx.Routes
where route.Waypoints.Any(wpt => wpt.Longitude < -130)
select route;
foreach (var matchingRoute in matchingRoutes)
{
var begin = matchingRoute.Waypoints[0];
var end = matchingRoute.Waypoints[matchingRoute.Waypoints.Length - 1];
Console.WriteLine($"route has {matchingRoute.Waypoints.Length} waypoints and goes from (lon: {begin.Longitude}, lat: {begin.Latitude}) to (lon: {end.Longitude}, lat: {end.Latitude})");
}
// find the bounding box of all points.
// imagine that the list was HUGE and we didn't want to fully buffer it, so this reader
// would actually be reading from some remote stream instead of the string.
double minlon = Double.MaxValue;
double minlat = Double.MaxValue;
double maxlon = Double.MinValue;
double maxlat = Double.MinValue;
using (var stringReader = new StringReader(GpxList))
using (var reader = XmlReader.Create(stringReader))
{
foreach (var wpt in ReadAllWaypoints(reader))
{
if (wpt.Longitude < minlon)
{
minlon = wpt.Longitude;
}
if (wpt.Latitude < minlat)
{
minlat = wpt.Latitude;
}
if (wpt.Longitude > maxlon)
{
maxlon = wpt.Longitude;
}
if (wpt.Latitude > maxlat)
{
maxlat = wpt.Latitude;
}
}
}
Console.WriteLine($"minlon: {minlon}");
Console.WriteLine($"minlat: {minlat}");
Console.WriteLine($"maxlon: {maxlon}");
Console.WriteLine($"maxlat: {maxlat}");
}
private static IEnumerable<GpxWaypoint> ReadAllWaypoints(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType != XmlNodeType.Element || reader.NamespaceURI != Gpx.GpxNamespace)
{
continue;
}
switch (reader.Name)
{
case "wpt":
case "trkpt":
case "rtept":
yield return GpxWaypoint.Load((XElement)XElement.ReadFrom(reader));
break;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment