From d81b08b2ca43732601ddd3883db06a6977afb51a Mon Sep 17 00:00:00 2001
From: Florian Frank <flori@ping.de>
Date: Thu, 12 Apr 2012 12:09:28 +0200
Subject: [PATCH] Avoid crashes during calculations

Geocoder.coordinates sometimes returns nil which causes crashes in the distance
and bearing calculations. By returning a point array of [ NaN, NaN ] this
distances will be also NaN which seems to be a sensible result. If the argument
to extract_coordinates has an unexpected form, also return [ NaN, NaN ].
---
 lib/geocoder/calculations.rb | 24 +++++++++++++++++++++---
 test/calculations_test.rb    | 29 +++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/lib/geocoder/calculations.rb b/lib/geocoder/calculations.rb
index abc8b947..06beb8df 100644
--- a/lib/geocoder/calculations.rb
+++ b/lib/geocoder/calculations.rb
@@ -21,6 +21,9 @@ module Geocoder
     #
     KM_IN_MI = 0.621371192
 
+    # Not a number constant
+    NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0
+
     ##
     # Distance spanned by one degree of latitude in the given units.
     #
@@ -270,10 +273,25 @@ module Geocoder
     #
     def extract_coordinates(point)
       case point
-        when Array; point
-        when String; Geocoder.coordinates(point)
-        else point.to_coordinates
+      when Array
+        if point.size == 2
+          lat, lon = point
+          if !lat.nil? && lat.respond_to?(:to_f) and
+            !lon.nil? && lon.respond_to?(:to_f)
+          then
+            return [ lat.to_f, lon.to_f ]
+          end
+        end
+      when String
+        point = Geocoder.coordinates(point) and return point
+      else
+        if point.respond_to?(:to_coordinates)
+          if Array === array = point.to_coordinates
+            return extract_coordinates(array)
+          end
+        end
       end
+      [ NAN, NAN ]
     end
   end
 end
diff --git a/test/calculations_test.rb b/test/calculations_test.rb
index 25343cb7..74816e20 100644
--- a/test/calculations_test.rb
+++ b/test/calculations_test.rb
@@ -144,4 +144,33 @@ class CalculationsTest < Test::Unit::TestCase
     l = Landmark.new(*landmark_params(:msg))
     assert_equal l.bearing_from([50,-86.1]), l.bearing_to([50,-86.1]) - 180
   end
+
+  def test_extract_coordinates
+    result = Geocoder::Calculations.extract_coordinates([ nil, nil ])
+    assert_equal [ Geocoder::Calculations::NAN ] * 2, result
+
+    result = Geocoder::Calculations.extract_coordinates([ 1.0 / 3, 2.0 / 3 ])
+    assert_in_delta 1.0 / 3, result.first, 1E-5
+    assert_in_delta 2.0 / 3, result.last, 1E-5
+
+    result = Geocoder::Calculations.extract_coordinates(nil)
+    assert_equal [ Geocoder::Calculations::NAN ] * 2, result
+
+    result = Geocoder::Calculations.extract_coordinates('')
+    assert_equal [ Geocoder::Calculations::NAN ] * 2, result
+
+    result = Geocoder::Calculations.extract_coordinates([ 'nix' ])
+    assert_equal [ Geocoder::Calculations::NAN ] * 2, result
+
+    o = Object.new
+    result = Geocoder::Calculations.extract_coordinates(o)
+    assert_equal [ Geocoder::Calculations::NAN ] * 2, result
+
+    def o.to_coordinates
+      [ 1.0 / 3, 2.0 / 3 ]
+    end
+    result = Geocoder::Calculations.extract_coordinates(o)
+    assert_in_delta 1.0 / 3, result.first, 1E-5
+    assert_in_delta 2.0 / 3, result.last, 1E-5
+  end
 end
-- 
GitLab