<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: distance code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sun, 18 May 2008 13:48:06 GMT</pubDate>
    <description>DZone Snippets: distance 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>
    <item>
      <title>Variance, Mean, Normalizing Functions, Euclidean and other Distances</title>
      <link>http://snippets.dzone.com/posts/show/3488</link>
      <description>Here &lt;b&gt;sum&lt;/b&gt;, &lt;b&gt;mean&lt;/b&gt; and &lt;b&gt;variance&lt;/b&gt; were inspired by the Peter's inline sum code:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Array; def sum; inject( nil ) { |sum,x| sum ? sum+x : x }; end; end&lt;br /&gt;class Array; def mean; self.sum/self.size.to_f; end; end&lt;br /&gt;class Array; def variance; mean = self.mean; Math.sqrt(inject( nil ) { |var,x| var ? var+((x-mean)**2) : ((x-mean)**2)}/self.size.to_f); end; end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If you want to normalize a random variable (array) so that mean = 0 and variance = 1, you can transform your array &lt;b&gt;x&lt;/b&gt; by calling:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# inputs a random variable, sets mean = 0 and variance = 1&lt;br /&gt;def standardize_random_variable(x)&lt;br /&gt;  mean = x.mean&lt;br /&gt;  variance = x.variance&lt;br /&gt;  x.map!{|a| (a-mean)/variance }&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If you want to compute distance, call these functions between two arrays of data, a and b.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;## Distance Functions&lt;br /&gt;&lt;br /&gt;# Sum of (x-y)^2&lt;br /&gt;def euclidean_squared_distance(a,b)&lt;br /&gt;  b = b.to_a&lt;br /&gt;  a = a.to_a&lt;br /&gt;  sum_of_diff_sq = 0&lt;br /&gt;  (0...a.size).each { |i| sum_of_diff_sq+=((a[i].to_f-b[i].to_f)**2)}&lt;br /&gt;  sum_of_diff_sq &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# Square root of sum of (x-y)^2&lt;br /&gt;def euclidean_distance(neighbor,xq)&lt;br /&gt;  Math.sqrt(euclidean_squared_distance(neighbor,xq))&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# Sum of abs(x,y)&lt;br /&gt;def cityblock_distance(neighbor,xq)&lt;br /&gt;  xq = xq.to_a&lt;br /&gt;  abs_diff = 0&lt;br /&gt;  (0...xq.size).each { |i| abs_diff+=(Math.abs(xq[i].to_f-neighbor[i].to_f)}&lt;br /&gt;  abs_diff&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Sat, 10 Feb 2007 02:14:52 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/3488</guid>
      <author>dustinsmith (dustin smith)</author>
    </item>
    <item>
      <title>Date.distance_to method for Ruby</title>
      <link>http://snippets.dzone.com/posts/show/2109</link>
      <description>Gives years, months, and days between two dates (like a human would expect)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Date&lt;br /&gt;  def distance_to(end_date)&lt;br /&gt;    years = end_date.year - year&lt;br /&gt;    months = end_date.month - month&lt;br /&gt;    days = end_date.day - day&lt;br /&gt;    if days &lt; 0&lt;br /&gt;      days += 30&lt;br /&gt;      months -= 1&lt;br /&gt;    end&lt;br /&gt;    if months &lt; 0&lt;br /&gt;      months += 12&lt;br /&gt;      years -= 1&lt;br /&gt;    end&lt;br /&gt;    {:years =&gt; years, :months =&gt; months, :days =&gt; days}&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Sat, 27 May 2006 00:58:55 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2109</guid>
      <author>timmorgan (Tim Morgan)</author>
    </item>
  </channel>
</rss>
