From d71d0eaff1066cc6508afbb33dad53c0885af570 Mon Sep 17 00:00:00 2001
From: Mat Ellis <mat@tecnh.com>
Date: Fri, 18 Mar 2011 14:11:09 -0700
Subject: [PATCH] Add option "bearing". When set to :line or :spherical it
 returns the bearing of the record from the search point, i.e. if the record
 is due north you'll see 0.0 returned. :line is a straight line, :spherical is
 the shortest path where the bearing between two points varies with distance
 to take into account curvature of the planet.

---
 lib/geocoder/orms/active_record.rb | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/lib/geocoder/orms/active_record.rb b/lib/geocoder/orms/active_record.rb
index c93b8e61..0481f307 100644
--- a/lib/geocoder/orms/active_record.rb
+++ b/lib/geocoder/orms/active_record.rb
@@ -59,6 +59,7 @@ module Geocoder::Orm
       # +limit+   :: number of records to return (for LIMIT SQL clause)
       # +offset+  :: number of records to skip (for OFFSET SQL clause)
       # +select+  :: string with the SELECT SQL fragment (e.g. “id, name”)
+      # +bearing+ :: :line for straight line calcs, :sphere for spherical
       #
       def near_scope_options(latitude, longitude, radius = 20, options = {})
         radius *= Geocoder::Calculations.km_in_mi if options[:units] == :km
@@ -82,6 +83,15 @@ module Geocoder::Orm
       def full_near_scope_options(latitude, longitude, radius, options)
         lat_attr = geocoder_options[:latitude]
         lon_attr = geocoder_options[:longitude]
+        bearing = case options[:bearing]
+          # Credit for SQL query (adapted) http://www.beginningspatial.com/calculating_bearing_one_point_another
+          when :line
+            ", (DEGREES(ATAN2(( longitude - #{longitude} ), ( latitude - #{latitude} )))+360) % 360 AS bearing"
+          when :spherical
+            ", (DEGREES( ATAN2( SIN(RADIANS(longitude - #{longitude})) * COS(RADIANS(latitude)), ( COS(RADIANS(#{latitude})) * SIN(RADIANS(latitude)) ) - ( SIN(RADIANS(#{latitude})) * COS(RADIANS(latitude)) * COS(RADIANS(longitude - #{longitude})) ) )) + 360 ) % 360 AS bearing"
+          else
+            ""
+          end
         distance = "3956 * 2 * ASIN(SQRT(" +
           "POWER(SIN((#{latitude} - #{lat_attr}) * " +
           "PI() / 180 / 2), 2) + COS(#{latitude} * PI()/180) * " +
@@ -90,7 +100,7 @@ module Geocoder::Orm
           "PI() / 180 / 2), 2) ))"
         options[:order] ||= "#{distance} ASC"
         default_near_scope_options(latitude, longitude, radius, options).merge(
-          :select => "#{options[:select] || '*'}, #{distance} AS distance",
+          :select => "#{options[:select] || '*'}, #{distance} AS distance#{bearing}",
           :having => "#{distance} <= #{radius}"
         )
       end
-- 
GitLab