class DistanceFox::Calculations

Constants

EARTH_RADIUS

Public Class Methods

driving_distance(origin, destination) click to toggle source

Origin and destination can be:

  • an array of coordinates ([lat,lon])

  • an array of coordinates (['lat','lon'])

  • a geocodable address (string)

# File lib/distance_fox/calculations.rb, line 62
def self.driving_distance(origin, destination)
  origin = extract_coordinates(origin).join(',')
  destination = extract_coordinates(destination).join(',')
  google_distance_query = "https://maps.googleapis.com/maps/api/distancematrix/json?mode=driving&origins=#{origin}&destinations=#{destination}&key=#{DistanceFox.configuration.api_key}"
  google_distance_response = HTTParty.get(URI.escape(google_distance_query))
  google_distance_response['rows'][0]['elements'][0]['distance']['value'].to_f / 1000
end
geocode(address) click to toggle source

Address can be: a geocodable address (string)

# File lib/distance_fox/calculations.rb, line 88
def self.geocode(address)
  google_distance_response = HTTParty.get(URI.escape(google_distance_query))
  case google_distance_response["results"]
    when []
      raise "Status: #{google_distance_response['status']}"
    else
      coordinates = google_distance_response['results'][0]['geometry']['location']
  end
  [coordinates['lat'], coordinates['lng']]
end
linear_distance(origin, destination) click to toggle source

Origin and destination can be:

  • an array of coordinates ([lat,lon])

  • an array of coordinates (['lat','lon'])

  • a geocodable address (string)

# File lib/distance_fox/calculations.rb, line 12
def self.linear_distance(origin, destination)
  lat_origin, lon_origin = extract_coordinates origin
  lat_destination, lon_destination = extract_coordinates destination

  delta_lat = (lat_destination - lat_origin) * Math::PI / 180
  delta_lon = (lon_destination - lon_origin) * Math::PI / 180

  a = Math.sin(delta_lat / 2) *
      Math.sin(delta_lat / 2) +
      Math.cos(lat_origin * Math::PI / 180) *
      Math.cos(lat_destination * Math::PI / 180) *
      Math.sin(delta_lon / 2) * Math.sin(delta_lon / 2)

  c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))

  d = EARTH_RADIUS * c
  d.round(3)
end
nearest_driving_distance(origin, destinations) click to toggle source

Driving Distance to nearest location from multiple locations

Origin can be:

  • an array of coordinates ([lat,lon])

  • an array of coordinates (['lat','lon'])

  • a geocodable address (string)

Destinations can be:

  • an array of hashes ([{'city' => 'City', 'coordinates' => ['lat','lon']}])

# File lib/distance_fox/calculations.rb, line 80
def self.nearest_driving_distance(origin, destinations)
  origin = origin.join(',')
  destination = nearest_linear_distance(origin, destinations)['coordinates']['destination'].join(',')
  driving_distance(origin, destination)
end
nearest_linear_distance(origin, destinations) click to toggle source

Linear Distance to nearest location from multiple locations Origin can be:

  • an array of float coordinates ([lat,lon])

  • an array of string coordinates (['lat','lon'])

Destinations can be:

  • an array of hashes ([{'city' => 'City', 'coordinates' => ['lat','lon']}])

# File lib/distance_fox/calculations.rb, line 39
def self.nearest_linear_distance(origin, destinations)
  origins_with_distance = []
  origin_coordinates = extract_coordinates origin
  destinations.each do |destination|
    distance = linear_distance(origin_coordinates, destination['coordinates'])
    origins_with_distance.push(
      'destination' => destination['city'],
      'distance' => distance,
      'coordinates' => {
        'origin' => origin_coordinates,
        'destination' => destination['coordinates']
      }
    )
  end
  origins_with_distance.min_by { |key| key['distance'] }
end

Private Class Methods

extract_coordinates(point) click to toggle source

Takes an object which is a [lat,lon] array or a geocodable string. Note that if a string is passed this may be a slow- running method and may return nil.

# File lib/distance_fox/calculations.rb, line 105
def self.extract_coordinates(point)
  case point
  when Array
    if point.size == 2
      lat, lon = point
      if !lat.nil? && lat.respond_to?(:to_f) &&
         !lon.nil? && lon.respond_to?(:to_f)
        return [lat.to_f, lon.to_f]
      end
    end
  when String
    (point = geocode(point)) && (return point)
  end
  raise "#{point} is not geocodable"
end