lib/gis/distance.rb in gis-distance-1.1.0 vs lib/gis/distance.rb in gis-distance-1.2.0
- old
+ new
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
# The GIS module serves as a namespace only.
module GIS
# The Distance class encapsulates methods related to geographic distance.
class Distance
# Error raised if latitude or longitude values are invalid.
class Error < StandardError; end
# The version of the gis-distance library
- VERSION = '1.1.0'.freeze
+ VERSION = '1.2.0'
# Create a new GIS::Distance object using the two sets of coordinates
# that are provided.
#
# If invalid coordinates are provided a GIS::Distance::Error is raised.
@@ -26,13 +28,11 @@
@distance = nil
end
# Returns the radius of the Earth in kilometers. The default is 6367.45.
#
- def radius
- @radius
- end
+ attr_reader :radius
# Sets the radius of the Earth in kilometers. This is variable because
# the Earth is not perfectly spherical, and you may wish to adjust it.
#
# However, the possible range of values is limited from 6357.0 to 6378.0.
@@ -42,85 +42,85 @@
# The default value is 6367.45.
#
# See http://en.wikipedia.org/wiki/Earth_radius for more information.
#
def radius=(kms)
- if kms < 6357.0 || kms > 6378.0
- raise Error, "Proposed radius '#{kms}' is out of range"
- end
+ raise Error, "Proposed radius '#{kms}' is out of range" if kms < 6357.0 || kms > 6378.0
@radius = kms
end
# Returns the formula used to calculate the distance. The default formula
# is 'haversine'.
#--
# See http://en.wikipedia.org/wiki/Haversine_formula for details.
- def formula
- @formula
- end
+ attr_reader :formula
# Sets the formula to be used internally for calculating the distance.
# The default is 'haversine'. Your other option is 'cosines' (i.e. the
# Law of Cosines).
#
# If an unsupported formula is provided a GIS::Distance::Error is raised.
#
def formula=(formula)
case formula.to_s.downcase
- when 'haversine'
- @formula = 'haversine'
- when 'cosines'
- @formula = 'cosines'
- else
- raise Error, "Formula '#{formula}' not supported"
+ when 'haversine'
+ @formula = 'haversine'
+ when 'cosines'
+ @formula = 'cosines'
+ when 'vincenty'
+ @formula = 'vincenty'
+ else
+ raise Error, "Formula '#{formula}' not supported"
end
end
# Returns the distance (in kilometers) between the two coordinates
# provided in the constructor.
#
def distance
@distance =
- case @formula.to_s.downcase
- when 'haversine'
- haversine_formula
- when 'cosines'
- law_of_cosines_formula
- end
+ case @formula.to_s.downcase
+ when 'haversine'
+ haversine_formula
+ when 'cosines'
+ law_of_cosines_formula
+ when 'vincenty'
+ vincenty_formula
+ end
end
private
# Validate the latitude and longitude values. Latitudes must be between
# 90.0 and -90.0 while longitudes must be between 180.0 and -180.0.
#
def validate(lat1, lon1, lat2, lon2)
- [lat1, lat2].each{ |lat|
+ [lat1, lat2].each do |lat|
if lat > 90 || lat < -90
msg = "Latitude '#{lat}' is invalid - must be between -90 and 90"
raise Error, msg
end
- }
+ end
- [lon1, lon2].each{ |lon|
+ [lon1, lon2].each do |lon|
if lon > 180 || lon < -180
msg = "Longitude '#{lon}' is invalid - must be between -180 and 180"
raise Error, msg
end
- }
+ end
end
# See https://en.wikipedia.org/wiki/Law_of_cosines
#
def law_of_cosines_formula
sin1 = Math.sin(@latitude1 * Math::PI / 180)
sin2 = Math.sin(@latitude2 * Math::PI / 180)
cos1 = Math.cos(@latitude1 * Math::PI / 180)
cos2 = Math.cos(@latitude2 * Math::PI / 180)
- cos3 = Math.cos(@longitude2 * Math::PI / 180 - @longitude1 * Math::PI / 180)
+ cos3 = Math.cos((@longitude2 * Math::PI / 180) - (@longitude1 * Math::PI / 180))
- Math.acos(sin1 * sin2 + cos1 * cos2 * cos3) * radius
+ Math.acos((sin1 * sin2) + (cos1 * cos2 * cos3)) * radius
end
# See http://en.wikipedia.org/wiki/Haversine_formula
#
def haversine_formula
@@ -130,18 +130,24 @@
lon2 = @longitude2 * Math::PI / 180
dlat = lat2 - lat1
dlon = lon2 - lon1
- a = ((Math.sin(dlat/2))**2) + (Math.cos(lat1) * Math.cos(lat2) * (Math.sin(dlon/2)) ** 2)
- c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
+ a = (Math.sin(dlat / 2)**2) + (Math.cos(lat1) * Math.cos(lat2) * (Math.sin(dlon / 2)**2))
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
radius * c
end
+ # See https://en.wikipedia.org/wiki/Vincenty's_formulae
+ def vincenty_formula
+ require 'rvincenty'
+ RVincenty.distance([@latitude1, @longitude1], [@latitude2, @longitude2]) / 1000.0
+ end
+
# Add a custom method to the base Float class if it isn't already defined.
class ::Float
- unless self.respond_to?(:mi)
+ unless respond_to?(:mi)
# Convert miles to kilometers.
def mi
self * 0.621371192
end
end