Contents tagged with GPS

  • SQL 2008 Geography - Combine Data Points into a Geography Line

    I have encountered many instances where having the decimal values for coordinates in a line is more useful than the having the Geography value for that same line. Likewise, in some instances a Geography value is more appropriate. Here is what I mean:

    I need all points in a polygon on a map. For my purposes, it is much (MUCH) faster to determine the overall rectangle that surrounds the polygon, add a bit of latitude and longitude to all axis and search against the individual points. With an index on the decimal values this is fast and guarantees that I get everything in the polygon. I don't mind too much that I also get a bunch of stuff not in that polygon. I use this to find what is visible to the user, not to determine actual spatial relationships among points or between points and the polygon.

    There are also times when I need to find distances and spatial relationship between points in a line. This is where the Geography type comes in and is both fast and accurate.

    In the end, the Geography type seems best suited to calculations and the decimal values are better off used for searching.

    So, once you have a bunch of coordinates in a table (PointsTable) and have them ordered properly and referenced by a header value, here is how you can quickly turn the line into a Geography instance. I do this when I am adding records to the PointsTable and store the output in the header table. (HeaderID is the inbound parameter.) I'm not really fond of the SubString statement in here but I haven't worked out how to append to LineList conditionally.

    Declare @HeaderID Int
    Select @HeaderID = 123

    Declare @LineList VarChar(Max)
    Declare @GeoVal Geography 
    Select @LineList = @LineList +
          Convert(VarChar(100), Longitude)
          + ' '
          + Convert(VarChar(100), Latitude) + ', '
    From dbo.PointsTable
    Where HeaderID = @HeaderID
    Order By Ordinal
     
     
    Select @LineList =
          SubString(@LineList, 1, (Len(@LineList) - 2))
    Select @LineList =
          'LINESTRING(' + @LineList + ')' 
    Select @GeoVal = 
          Geography::STLineFromText(@LineList, 4326);

     

    Read more...

  • 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
      {
        /// <summary>
        /// Load the Xml document for parsing
        /// </summary>
        /// <param name="sFile">Fully qualified file name (local)</param>
        /// <returns>XDocument</returns>
        private XDocument GetGpxDoc(string sFile)
        {
          XDocument gpxDoc = XDocument.Load(sFile);
          return gpxDoc;
        }

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

        /// <summary>
        /// When passed a file, open it and parse all waypoints from it.
        /// </summary>
        /// <param name="sFile">Fully qualified file name (local)</param>
        /// <returns>string containing line delimited waypoints from
        /// the file (for test)
    </returns>
        /// <remarks>Normally, this would be used to populate the
        /// appropriate object model
    </remarks>
        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();
        }

        /// <summary>
        /// When passed a file, open it and parse all tracks
        /// and track segments from it.
        /// </summary>
        /// <param name="sFile">Fully qualified file name (local)</param>
        /// <returns>string containing line delimited waypoints from the
        /// file (for test)</returns>
        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();
        }
      }
    }


     

    GPX Sample File

    <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    <
    gpx xmlns="http://www.topografix.com/GPX/1/1"
       creator="MapSource 6.13.7"
       version="1.1"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.garmin.com/xmlschemas/GpxExtensions/v3
           http://www.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd
           http://www.topografix.com/GPX/1/1
           http://www.topografix.com/GPX/1/1/gpx.xsd
    ">

      <
    metadata>
        <
    link href="http://www.garmin.com">
          <
    text>Garmin International</text>
        </
    link>
        <
    time>2009-03-08T20:11:54Z</time>
        <
    bounds
          maxlat="39.2971185"
          maxlon="-76.6951826"
          minlat="39.2035537"
          minlon="-76.8203088"/>
      </
    metadata>

      <
    wpt lat="39.2445616" lon="-76.7194497">
        <
    ele>88.8067627</ele>
        <
    name>001</name>
        <
    cmt>16-SEP-08 8:50:11AM</cmt>
        <
    desc>16-SEP-08 8:50:11AM</desc>
        <
    sym>Residence</sym>
        <
    extensions>
           <
    gpxx:WaypointExtension
           xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
             <
    gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
          </
    gpxx:WaypointExtension>
        </
    extensions>
      </
    wpt>

      <
    wpt lat="39.2422711" lon="-76.7213488">
        <
    ele>38.3380127</ele>
        <
    name>009</name>
        <
    cmt>16-SEP-08 9:40:46AM</cmt>
        <
    desc>16-SEP-08 9:40:46AM</desc>
        <
    sym>Residence</sym>
        <
    extensions>
          <
    gpxx:WaypointExtension
          xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
            <
    gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
          </
    gpxx:WaypointExtension>
        </
    extensions>
      </
    wpt>

      <
    trk>
        <
    name>Vinyard Springs Loop</name>

        <
    extensions>
          <
    gpxx:TrackExtension
         
    xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
            <
    gpxx:DisplayColor>Transparent</gpxx:DisplayColor>
          </
    gpxx:TrackExtension>
        </
    extensions>

        <
    trkseg>
          <
    trkpt lat="39.2446415" lon="-76.7199907">
            <
    ele>60.2197266</ele>
            <
    time>2009-03-08T13:18:25Z</time>
          </
    trkpt>
          <
    trkpt lat="39.2445078" lon="-76.7193838">
            <
    ele>88.0980225</ele>
            <
    time>2009-03-08T13:19:23Z</time>
          </
    trkpt>
          <
    trkpt lat="39.2440145" lon="-76.7200792">
            <
    ele>81.3687744</ele>
            <
    time>2009-03-08T13:19:43Z</time>
          </
    trkpt>
          <
    trkpt lat="39.2435182" lon="-76.7208523">
            <
    ele>82.3300781</ele>
            <
    time>2009-03-08T13:20:04Z</time>
          </
    trkpt>
          <
    trkpt lat="39.2427701" lon="-76.7212304">
            <
    ele>78.9654541</ele>
            <
    time>2009-03-08T13:20:52Z</time>
          </
    trkpt>
          <
    trkpt lat="39.2417241" lon="-76.7208448">
            <
    ele>65.0263672</ele>
            <
    time>2009-03-08T13:21:43Z</time>
          </
    trkpt>
        </
    trkseg>
      </
    trk>

      <
    trk>
        <
    name>Patapsco Boundry Ref</name>

        <
    extensions>
          <
    gpxx:TrackExtension
          xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
            <
    gpxx:DisplayColor>DarkRed</gpxx:DisplayColor>
          </
    gpxx:TrackExtension>
        </
    extensions>
       
        <
    trkseg>
          <
    trkpt lat="39.2253216" lon="-76.7073387">
            <
    ele>46.2806396</ele>
          </
    trkpt>
          <
    trkpt lat="39.2248415" lon="-76.7065894">
            <
    ele>43.3966064</ele>
          </
    trkpt>
          <
    trkpt lat="39.2194385" lon="-76.7042182">
            <
    ele>17.4411621</ele>
          </
    trkpt>
          <
    trkpt lat="39.2188285" lon="-76.7043919">
            <
    ele>15.0377197</ele>
          </
    trkpt>
          <
    trkpt lat="39.2183665" lon="-76.7051846">
            <
    ele>14.0764160</ele>
          </
    trkpt>
          <
    trkpt lat="39.2180424" lon="-76.7061647">
            <
    ele>12.6343994</ele>
          </
    trkpt>
          <
    trkpt lat="39.2177812" lon="-76.7070982">
            <
    ele>11.1925049</ele>
          </
    trkpt>
          <
    trkpt lat="39.2485097" lon="-76.8098993">
            <
    ele>127.5119629</ele>
          </
    trkpt>
        </
    trkseg>
      </
    trk>

    </
    gpx>


     

    Read more...