2 minute read

As the social part of the sites is growing stronger, everyone of us has to include a user’s location of some sort into the application. There can be many reasons for that, be it location based search, advertising or something else unrelated. But mark this, you will have to deal with locations in the future.

I won’t get into the details of how to get the user’s latitude and longitude, but geocoder gem could be helpful for you. I have found one JavaScript solution with the descriptions of what are the different implementations of the calculation and which one is more correct so

I won’t get into the explanation. You can check the explanation and even more stuff you can do with 2 geo coordinates on the Movable Type Scripts The distance can be calculated using 3 formulas, Haversine, Spherical Law of Cosines, and Equirectangular approximation The prerequisites we will need are degree to radian conversion which is easily done with degree / 180 * Math::PI, but to make the code easier to write we can monkey patch the Float with to_rad method which will calculate this for us. We could use refinements, or make a method object and not pollute the global space but we can leave it like this for now.

class Float
  def to_rad
    self / 180 * Math::PI
  end
end

After we have done the prerequisites let’s assume that we have two objects, and each one has a latitude and a longitude. For the sake of this post we can make them a hash with two keys latitude and longitude. And we take the earth radius as 6371km

class Geodistance
  include Math
  attr_reader :from, :to, :lat1, :lon1, :lat2, :lon2
  RADIUS = 6371
  def initialize(from, to)
    @from = from
    @to = to
    set_variables
  end
  def distance(type = 'haversine')
    begin
      self.send(type.to_sym)
    rescue
      raise NotImplementedError, 'The type you have requested is not implemented, try "cosines" or "approximation", or without params for "haversine"'
    end
  end
  private
  def haversine
    d_lat = (from[:latitude] - to[:latitude]).to_rad
    d_lon = (from[:longitude] - to[:longitude]).to_rad
    a = sin(d_lat / 2) * sin(d_lat / 2) + sin(d_lon / 2) *
      sin(d_lon / 2) * cos(lat1) * cos(lat2)
    c = 2 * atan2(sqrt(a), sqrt(1-a))
    RADIUS * c
  end
  def cosines
    acos(sin(lat1) * sin(lat2) +
         cos(lat1) * cos(lat2) *
         cos(lon2 - lon1)) * RADIUS
  end
  def approximation
    x = (lon2 - lon1) * cos((lat1 + lat2) / 2)
    y = lat2 - lat1
    sqrt(x * x + y * y) * RADIUS
  end
  def set_variables
    @lat1 = from[:latitude].to_rad
    @lat2 = to[:latitude].to_rad
    @lon1 = from[:longitude].to_rad
    @lon2 = to[:longitude].to_rad
  end
end

As you can see by calling the distance method with all 3 parameters, each one will produce a slightly different result. As they say, the haversine one should be the most accurate, but take caution. I would like to benchmark them some day and see which one calculates the result faster.

Thanks to Movable Type Scripts for providing the JavaScript code and all the insight. I just did a rewrite in Ruby.

Comments