<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: bearing code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Fri, 08 Aug 2008 11:57:18 GMT</pubDate>
    <description>DZone Snippets: bearing code</description>
    <item>
      <title>GPS distance and initial bearing between points (MySQL)</title>
      <link>http://snippets.dzone.com/posts/show/4991</link>
      <description>Assume you have a table of locations with Latitude and Longitude for each one. In my case the table is "station" and the primary key is "LocID".&lt;br /&gt;&lt;br /&gt;First we create a view to help with the 3D geometry (6378 = Earth's radius in km):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE VIEW gpsGlb AS&lt;br /&gt;    SELECT &lt;br /&gt;        LocID&lt;br /&gt;        ,6378 * COS(RADIANS(Latitude)) * COS(RADIANS(Longitude)) AS x&lt;br /&gt;        ,6378 * COS(RADIANS(Latitude)) * SIN(RADIANS(Longitude)) AS y&lt;br /&gt;        ,6378 * SIN(RADIANS(Latitude)) AS z&lt;br /&gt;    FROM station;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Here I query for distances to all my locations that are NOT LocID = 405 (rounded miles in my case):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT &lt;br /&gt;    LocID&lt;br /&gt;    ,ROUND((2 * 6378 * ASIN(d / 2 / 6378)) * 0.621371192) AS dist_mi&lt;br /&gt;FROM&lt;br /&gt;    (SELECT&lt;br /&gt;        SQRT(dx * dx + dy * dy + dz * dz) AS d&lt;br /&gt;        ,LocID&lt;br /&gt;     FROM&lt;br /&gt;        (SELECT&lt;br /&gt;            p1.x - p2.x AS dx&lt;br /&gt;            ,p1.y - p2.y AS dy&lt;br /&gt;            ,p1.z - p2.z AS dz&lt;br /&gt;            ,p2.LocID&lt;br /&gt;        FROM gpsGlb p1&lt;br /&gt;        JOIN gpsGlb p2 ON (p1.LocID = 405 AND p2.LocID != 405)&lt;br /&gt;       ) t1&lt;br /&gt;    ) t2&lt;br /&gt;ORDER BY dist_mi&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Here I get the initial bearing to the locations. The "boxed" calculation will come in handy later.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT&lt;br /&gt;    LocID&lt;br /&gt;    ,(360 + DEGREES(ATAN2(y, x))) % 360 AS initBearing_deg&lt;br /&gt;    ,ROUND(((360 + DEGREES(ATAN2(y, x))) % 360) / 22.5) * 22.5 &lt;br /&gt;     AS initBearingBoxed_deg&lt;br /&gt;FROM&lt;br /&gt;    (SELECT&lt;br /&gt;        SIN(RADIANS(s2.Longitude - s1.Longitude)) * COS(RADIANS(s2.Latitude)) &lt;br /&gt;        AS y&lt;br /&gt;        ,COS(RADIANS(s1.Latitude)) * SIN(RADIANS(s2.Latitude))&lt;br /&gt;            - SIN(RADIANS(s1.Latitude)) * COS(RADIANS(s2.Latitude))&lt;br /&gt;               * COS(RADIANS(s2.Longitude - s1.Longitude)) &lt;br /&gt;        AS x&lt;br /&gt;        ,s2.LocID&lt;br /&gt;    FROM station s1&lt;br /&gt;    JOIN station s2 ON (s1.LocID = 405 AND s2.LocID != 405)&lt;br /&gt;    ) q1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Here's the combined query plus boxed degrees converted to 'NNE', etc. I've also added a limit for the distance in the qq1 subquery.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT&lt;br /&gt;    qq2.LocID&lt;br /&gt;    ,dist_mi&lt;br /&gt;    ,CASE initBearingBoxed_deg&lt;br /&gt;        WHEN 22.5 THEN 'NNE'   WHEN 45 THEN 'NE'&lt;br /&gt;        WHEN 67.5 THEN 'ENE'   WHEN 90 THEN 'E'&lt;br /&gt;        WHEN 112.5 THEN 'ESE'  WHEN 135 THEN 'SE'&lt;br /&gt;        WHEN 157.5 THEN 'SSE'  WHEN 180 THEN 'S'&lt;br /&gt;        WHEN 202.5 THEN 'SSW'  WHEN 225 THEN 'SW'&lt;br /&gt;        WHEN 247.5 THEN 'WSW'  WHEN 270 THEN 'W'&lt;br /&gt;        WHEN 292.5 THEN 'WNW'  WHEN 315 THEN 'NW'&lt;br /&gt;        WHEN 337.5 THEN 'NNW'  ELSE 'N'&lt;br /&gt;     END AS bearing&lt;br /&gt;FROM (&lt;br /&gt;    SELECT &lt;br /&gt;        LocID&lt;br /&gt;        ,ROUND((2 * 6378 * ASIN(d / 2 / 6378)) * 0.621371192) AS dist_mi&lt;br /&gt;    FROM&lt;br /&gt;        (SELECT&lt;br /&gt;            SQRT(dx * dx + dy * dy + dz * dz) AS d&lt;br /&gt;            ,LocID&lt;br /&gt;         FROM&lt;br /&gt;            (SELECT&lt;br /&gt;                p1.x - p2.x AS dx&lt;br /&gt;                ,p1.y - p2.y AS dy&lt;br /&gt;                ,p1.z - p2.z AS dz&lt;br /&gt;                ,p2.LocID&lt;br /&gt;            FROM gpsGlb p1&lt;br /&gt;            JOIN gpsGlb p2 ON (p1.LocID = 405 AND p2.LocID != 405)&lt;br /&gt;           ) t1&lt;br /&gt;        ) t2&lt;br /&gt;    ) qq1&lt;br /&gt;JOIN (&lt;br /&gt;    SELECT&lt;br /&gt;        LocID&lt;br /&gt;        ,(360 + DEGREES(ATAN2(y, x))) % 360 AS initBearing_deg&lt;br /&gt;        ,(360 + ROUND((DEGREES(ATAN2(y, x))) / 22.5) * 22.5) % 360 &lt;br /&gt;         AS initBearingBoxed_deg&lt;br /&gt;    FROM&lt;br /&gt;        (SELECT&lt;br /&gt;            SIN(RADIANS(s2.Longitude - s1.Longitude)) * COS(RADIANS(s2.Latitude)) &lt;br /&gt;             AS y&lt;br /&gt;            ,COS(RADIANS(s1.Latitude)) * SIN(RADIANS(s2.Latitude))&lt;br /&gt;                - SIN(RADIANS(s1.Latitude)) * COS(RADIANS(s2.Latitude))&lt;br /&gt;                   * COS(RADIANS(s2.Longitude - s1.Longitude)) &lt;br /&gt;             AS x&lt;br /&gt;            ,s2.LocID&lt;br /&gt;        FROM station s1&lt;br /&gt;        JOIN station s2 ON (s1.LocID = 405 AND s2.LocID != 405)&lt;br /&gt;        ) q1&lt;br /&gt;    ) qq2 ON (qq1.LocID = qq2.LocID&lt;br /&gt;              AND qq1.dist_mi &lt;= 60)&lt;br /&gt;ORDER BY dist_mi&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 16 Jan 2008 21:14:20 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4991</guid>
      <author>mrclay (Steve Clay)</author>
    </item>
  </channel>
</rss>
