From e8c17f28243523b5f9c0e21e911cc4b2ff030b2d Mon Sep 17 00:00:00 2001
From: Mat Ellis <mat@tecnh.com>
Date: Fri, 18 Mar 2011 14:50:04 -0700
Subject: [PATCH] Adds methods for calculating the bearing between two points
 and for converting that bearing into a compass point, e.g. North or South
 West. If you want fewer or additional compass points (e.g. South By South
 West, or just NESW) provide a second parameter containing your array of
 points, e.g.
 Geocoder::Calculations::compass_point(90,['north','east','south','west'])

---
 lib/geocoder/calculations.rb | 30 ++++++++++++++++++++++++++++++
 test/geocoder_test.rb        | 13 +++++++++++++
 2 files changed, 43 insertions(+)

diff --git a/lib/geocoder/calculations.rb b/lib/geocoder/calculations.rb
index 35136960..b99e35f0 100644
--- a/lib/geocoder/calculations.rb
+++ b/lib/geocoder/calculations.rb
@@ -88,5 +88,35 @@ module Geocoder
     def km_in_mi
       0.621371192
     end
+
+    ##
+    # Calculate bearing between two sets of co-ordinates
+    #
+    def bearing_between(lat1, lon1, lat2, lon2, options = {})
+      # Math courtesy of http://www.movable-type.co.uk/scripts/latlong.html
+      dlon = to_radians((lon1 - lon2).abs)
+
+      y = Math.sin(dlon) * Math.cos(lat2)
+      x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dlon)
+      brng = Math.atan2(x,y)
+      (to_degrees(brng) + 360) % 360
+    end
+
+    # If you want more or fewer points simply override Geocoder::Calculations.COMPASS_POINTS with your own array
+    COMPASS_POINTS = [{:name => "North", :abbr => "N"},
+                      {:name => "North East", :abbr => "NE"},
+                      {:name => "East", :abbr => "E"},
+                      {:name => "South East", :abbr => "SE"},
+                      {:name => "South", :abbr => "S"},
+                      {:name => "South West", :abbr => "SW"},
+                      {:name => "West", :abbr => "W"},
+                      {:name => "North West", :abbr => "NW"}]
+
+    ##
+    # Compass direction (North, South, etc.) between two sets of co-ordinates
+    def compass_point(bearing, points = COMPASS_POINTS)
+      seg_size = 360/points.length
+      points[((bearing + (seg_size/2) ) % 360) / seg_size]
+    end
   end
 end
diff --git a/test/geocoder_test.rb b/test/geocoder_test.rb
index 2d29fc10..458b59ee 100644
--- a/test/geocoder_test.rb
+++ b/test/geocoder_test.rb
@@ -23,6 +23,19 @@ class GeocoderTest < Test::Unit::TestCase
     assert_equal 69, Geocoder::Calculations.distance_between(0,0, 0,1).round
   end
 
+  def test_distance_between
+    hash_north = {:name => "North", :abbr => "N"}
+    hash_south = {:name => "South", :abbr => "S"}
+    hash_nw = {:name => "North West", :abbr => "NW"}
+    assert_equal hash_north, Geocoder::Calculations.compass_point(0)
+    assert_equal hash_north, Geocoder::Calculations.compass_point(360)
+    assert_equal hash_north, Geocoder::Calculations.compass_point(361)
+    assert_equal hash_north, Geocoder::Calculations.compass_point(-22)
+    assert_equal hash_nw, Geocoder::Calculations.compass_point(-23)
+    assert_equal hash_south, Geocoder::Calculations.compass_point(180)
+    assert_equal hash_south, Geocoder::Calculations.compass_point(181)
+  end
+
   def test_geographic_center_with_arrays
     assert_equal [0.0, 0.5],
       Geocoder::Calculations.geographic_center([[0,0], [0,1]])
-- 
GitLab