Pages

Wednesday, May 4, 2011


Using Linq to XML with C# to Read Gpx Files

GPX is the standardized file format for GPS file exchanges. A GPX file can contain a lot of different kinds of information. Take a look at the schema here. In general, the major things that you will work with are:

Waypoints

A waypoint is a specific position that is manually marked by a user for future reference. So when you get to the suspension bridge, mark a waypoint and you can find it again later as well as tell everyone else about it.

Tracks

Tracks are where you've been. When I want to mark out a trail for users of my application, I set up my GPS on my bike and just go for a ride. GPS antennae have come a long way in the last few years and my inexpensive Garmin eTrex keeps pretty accurate markers even when I'm in a deep draw. When I get home I have a complete listing of a few hundred points on my route, depending on how far apart or how long to wait I've preset my GPS to mark between saved track points.

Routes

A route is what you load into your GPS. It's essentially a list of positions you build by looking at a map or a file you get from someone else's track. When loaded, it will direct you to each point along the route in the appropriate order.

My Garmin saves files in a GDB format which is proprietary for the product. I load this file onto a machine with Garmin MapSource and immediately save the file as a GPX. This gets it into the standardized format that nearly all other GPS units and mapping software can use and I'm ready to load my data. At the end of this post is a well formed (but incomplete) GPX file. The original file had about 6,500 lines in it.

The Code

It's really pretty straight forward once you realize that you need to pull in the namespace object and then include it in each call to an element. My initial run at this netted attribute values but no element values which was really frustrating. Also, when working with Xml, remember that an element that doesn't exist results in a null object reference so you'll see in the code how I handled that for each element. The biggest issue for me with Linq is the inability to debug line-by-line. Still it's crazy fast and I'm loading a lot of data in only a few lines of code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;

namespace LinqXMLTester
{
public class GPXLoader
{
///
/// Load the Xml document for parsing
///
/// Fully qualified file name (local)
/// XDocument
private XDocument GetGpxDoc(string sFile)
{
XDocument gpxDoc = XDocument.Load(sFile);
return gpxDoc;
}

///
/// Load the namespace for a standard GPX document
///
///
private XNamespace GetGpxNameSpace()
{
XNamespace gpx = XNamespace.Get("http://www.topografix.com/GPX/1/1");
return gpx;
}

///
/// When passed a file, open it and parse all waypoints from it.
///
/// Fully qualified file name (local)
/// string containing line delimited waypoints from
/// the file (for test)

/// Normally, this would be used to populate the
/// appropriate object model

public string LoadGPXWaypoints(string sFile)
{
XDocument gpxDoc = GetGpxDoc(sFile);
XNamespace gpx = GetGpxNameSpace();

var waypoints = from waypoint in gpxDoc.Descendants(gpx + "wpt")
select new
{
Latitude = waypoint.Attribute("lat").Value,
Longitude = waypoint.Attribute("lon").Value,
Elevation = waypoint.Element(gpx + "ele") != null ?
waypoint.Element(gpx + "ele").Value : null,
Name = waypoint.Element(gpx + "name") != null ?
waypoint.Element(gpx + "name").Value : null,
Dt = waypoint.Element(gpx + "cmt") != null ?
waypoint.Element(gpx + "cmt").Value : null
};

StringBuilder sb = new StringBuilder();
foreach (var wpt in waypoints)
{
// This is where we'd instantiate data
// containers for the information retrieved.
sb.Append(
string.Format("Name:{0} Latitude:{1} Longitude:{2} Elevation:{3} Date:{4}\n",
wpt.Name,wpt.Latitude,wpt.Longitude,
wpt.Elevation, wpt.Dt));
}

return sb.ToString();
}

///
/// When passed a file, open it and parse all tracks
/// and track segments from it.
///
/// Fully qualified file name (local)
/// string containing line delimited waypoints from the
/// file (for test)
public string LoadGPXTracks(string sFile)
{
XDocument gpxDoc = GetGpxDoc(sFile);
XNamespace gpx = GetGpxNameSpace();
var tracks = from track in gpxDoc.Descendants(gpx + "trk")
select new
{
Name = track.Element(gpx + "name") != null ?
track.Element(gpx + "name").Value : null,
Segs = (
from trackpoint in track.Descendants(gpx + "trkpt")
select new
{
Latitude = trackpoint.Attribute("lat").Value,
Longitude = trackpoint.Attribute("lon").Value,
Elevation = trackpoint.Element(gpx + "ele") != null ?
trackpoint.Element(gpx + "ele").Value : null,
Time = trackpoint.Element(gpx + "time") != null ?
trackpoint.Element(gpx + "time").Value : null
}
)
};

StringBuilder sb = new StringBuilder();
foreach (var trk in tracks)
{
// Populate track data objects.
foreach (var trkSeg in trk.Segs)
{
// Populate detailed track segments
// in the object model here.
sb.Append(
string.Format("Track:{0} - Latitude:{1} Longitude:{2} " +
"Elevation:{3} Date:{4}\n"
,
trk.Name, trkSeg.Latitude,
trkSeg.Longitude, trkSeg.Elevation,
trkSeg.Time));
}
}
return sb.ToString();
}
}
}

I have found the article from the following blog.
Thanks to Jim Jackson
http://weblogs.asp.net/jimjackson/archive/2009/03/10/using-linq-to-xml-with-c-to-read-
gpx-files.aspx

Posted by Posted by Muhammad Hussain at 1:59 AM
Categories:

0 comments  

 
>