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