Skip to content

Commit

Permalink
refactored spec file structure
Browse files Browse the repository at this point in the history
  • Loading branch information
kristianmandrup committed Jun 22, 2011
1 parent d45e59b commit 74bc126
Show file tree
Hide file tree
Showing 22 changed files with 425 additions and 226 deletions.
15 changes: 6 additions & 9 deletions Gemfile
@@ -1,13 +1,10 @@
source "http://rubygems.org"
# Add dependencies required to use your gem here.
# Example:
# gem "activesupport", ">= 2.3.5"

# Add dependencies to develop your gem here.
# Include everything needed to run rake, tests, features, etc.
gem 'geo_point', '~> 2.5.0'

group :development do
gem "rspec", ">= 2.3.0"
gem "bundler", "~> 1.0.0"
gem "jeweler", "~> 1.5.2"
gem "rcov", ">= 0"
gem "rspec", ">= 2.6.0"
gem "bundler", "~> 1.0.10"
gem "jeweler", "~> 1.6.2"
gem "rcov", ">= 0"
end
36 changes: 28 additions & 8 deletions README.textile
Expand Up @@ -10,6 +10,7 @@ The formulas curently supported are
* _Haversine_
* _Spherical_
* _Vincenty_
* _Flat_

h2. Install & Usage

Expand All @@ -22,17 +23,36 @@ it "should work" do
lon2 = -104.80
lat2 = lat1

dist = GeoDistance::Haversine.distance( lat1, lon1, lat2, lon2 )
dist = GeoDistance.distance( lat1, lon1, lat2, lon2 ) # in radians

puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist[:meters].number} meters"
dist = GeoDistance::Haversine.geo_distance( lat1, lon1, lat2, lon2 ).to_meters

puts "#{dist[:feet]}"
puts "#{dist.meters}"
puts "#{dist[:km]}"
puts "#{dist[:miles]}"
dist[:km].to_s.should match(/7\.376*/)
# Change to (lng, lat) mode
GeoPoint.coord_mode = :lng_lat

p1, p2 = [[lon1, lat1].geo_point, [lon2, lat2].geo_point]

dist = GeoDistance::Haversine.geo_distance( lon1, lat1, lon2, lat2)
dist = GeoDistance::Haversine.geo_distance( p1, p2).to_miles

puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist.meters} meters"

dist.feet
dist.meters
dist.kms
dist.miles
end
</pre>
</pre>

h2. Distance API

The call to distance returns an instance of GeoDistance

@dist = GeoDistance::Haversine.distance( lat1, lon1, lat2, lon2 )@

The #kms, #meters, #miles and #feet methods return an instance og GeoUnit

@dist.kms@

h3. Setting default algorithm

Expand Down
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
0.1.2
0.1.3
66 changes: 5 additions & 61 deletions lib/geo-distance.rb
@@ -1,62 +1,6 @@
module GeoDistance
# this is global because if computing lots of track point distances, it didn't make
# sense to new a Hash each time over potentially 100's of thousands of points

class << self
# radius of the great circle in miles
# radius in kilometers...some algorithms use 6367
def earth_radius
{:km => 6371, :miles => 3956, :feet => 20895592, :meters => 6371000}
end

def radians_per_degree
0.017453293 # PI/180
end

def units
[:miles, :km, :feet, :meters]
end

def radians_ratio unit
GeoDistance.radians_per_degree * earth_radius[unit]
end

def default_algorithm= name
raise ArgumentError, "Not a valid algorithm. Must be one of: #{algorithms}" if !algorithms.include?(name.to_sym)
@default_algorithm = name
end

def distance( lat1, lon1, lat2, lon2)
klass = case default_algorithm
when :haversine
GeoDistance::Haversine
when :spherical
GeoDistance::Spherical
when :vincenty
GeoDistance::Vincenty
else
raise ArgumentError, "Not a valid algorithm. Must be one of: #{algorithms}"
end
klass.distance lat1, lon1, lat2, lon2
end

def default_algorithm
@default_algorithm || :haversine
end

protected

def algorithms
[:haversine, :spherical, :vincenty]
end
end

def self.wants? unit_opts, unit
unit_opts == unit || unit_opts[unit]
end
end

require 'geo_point'
require 'geo-distance/core_ext'
require 'geo-distance/class_methods'
require 'geo-distance/distance'
require 'geo-distance/haversine'
require 'geo-distance/spherical'
require 'geo-distance/vincenty'
require 'geo-distance/formula'

58 changes: 58 additions & 0 deletions lib/geo-distance/class_methods.rb
@@ -0,0 +1,58 @@
class GeoDistance
# this is global because if computing lots of track point distances, it didn't make
# sense to new a Hash each time over potentially 100's of thousands of points

module ClassMethods
# radius of the great circle in miles
# radius in kilometers...some algorithms use 6367

def earth_radius units
GeoDistance.EARTH_RADIUS[units.to_sym]
end

def radians_per_degree
0.017453293 # PI/180
end

def units
[:feet, :meters, :kms, :miles]
end

def radians_ratio unit
GeoDistance.radians_per_degree * earth_radius[unit]
end

def default_algorithm= name
raise ArgumentError, "Not a valid algorithm. Must be one of: #{algorithms}" if !algorithms.include?(name.to_sym)
@default_algorithm = name
end

def distance( lat1, lon1, lat2, lon2)
klass = case default_algorithm
when :flat
GeoDistance::Flat
when :haversine
GeoDistance::Haversine
when :spherical
GeoDistance::Spherical
when :vincenty
GeoDistance::Vincenty
else
raise ArgumentError, "Not a valid algorithm. Must be one of: #{algorithms}"
end
klass.distance lat1, lon1, lat2, lon2
end

def default_algorithm
@default_algorithm || :haversine
end

protected

def algorithms
[:flat, :haversine, :spherical, :vincenty]
end
end

extend ClassMethods
end
38 changes: 17 additions & 21 deletions lib/geo-distance/core_ext.rb
@@ -1,31 +1,27 @@
class Float
def round_to(x)
(self * 10**x).round.to_f / 10**x
end
require 'sugar-high/numeric'

def ceil_to(x)
(self * 10**x).ceil.to_f / 10**x
module GeoUnitExt
::GeoDistance.units.each do |unit|
class_eval %{
def #{unit}
GeoDistance.new(self, :#{unit})
end
}
end

def floor_to(x)
(self * 10**x).floor.to_f / 10**x
end

include NumberDslExt # from sugar-high

def rpd
self * GeoDistance.radians_per_degree
end
alias_method :to_radians, :rpd
end

require 'geo-distance'
end

class Integer
::GeoDistance.units.each do |unit|
class_eval %{
def #{unit}
GeoDistance::Distance.new(self, :#{unit})
end
}
end
class Fixnum
include GeoUnitExt
end

class Float
include GeoUnitExt
end

32 changes: 32 additions & 0 deletions lib/geo-distance/distance.rb
@@ -0,0 +1,32 @@
class GeoDistance
attr_reader :distance, :unit

def initialize distance, unit = :kms
@distance = distance
return if !unit

raise ArgumentError, "Invalid unit: #{unit} - must be one of #{GeoDistance.units}" if !GeoDistance.units.include?(unit.to_sym)
@unit = unit.to_sym
end

GeoDistance.units.each do |unit|
class_eval %{
def #{unit}
delta_#{unit}
end
alias_method :to_#{unit}, :#{unit}
}
end

protected

# delta between the two points in miles
GeoDistance.units.each do |unit|
class_eval %{
def delta_#{unit}
unit = GeoUnits.key(:#{unit})
GeoDistance.earth_radius[:#{unit}] * distance
end
}
end
end
93 changes: 89 additions & 4 deletions lib/geo-distance/formula.rb
@@ -1,16 +1,101 @@
module GeoDistance
EARTH_RADIUS = { :kilometers => 6378.135, :miles => 3963.1676 }
class GeoDistance
# WGS-84 numbers
EARTH_MAJOR_AXIS_RADIUS = { :kilometers => 6378.137, :miles => 3963.19059 }
EARTH_MINOR_AXIS_RADIUS = { :kilometers => 6356.7523142, :miles => 3949.90276 }

EARTH_RADIUS = {
:miles => 3963.1676,
:kilometers => 6378.135,
:meters => 6378135,
:feet => 20925639.8
}


EARTH_MAJOR_AXIS_RADIUS = {
:miles => 3963.19059
:kilometers => 6378.137,
:meters => 6378137,
:feet => 20925646.36
}

EARTH_MINOR_AXIS_RADIUS = {
:kilometers => 6356.7523142,
:miles => 3949.90276,
:meters => 6356752.3142,
:feet => 20855486.627
}

autoload :Haversine, 'geo-distance/formula/haversine'
autoload :Spherical, 'geo-distance/formula/spherical'
autoload :Vincenty, 'geo-distance/formula/vincenty'
autoload :Flat, 'geo-distance/formula/flat'

class DistanceFormula
include Math
extend Math

RADIAN_PER_DEGREE = Math::PI / 180.0

# Haversine Formula
# Adapted from Geokit Gem
# https://github.com/andre/geokit-gem.git
# By: Andre Lewis
PI_DIV_RAD = 0.0174

KMS_PER_MILE = 1.609
METERS_PER_FEET = 3.2808399

MILES_PER_LATITUDE_DEGREE = 69.1
KMS_PER_LATITUDE_DEGREE = MILES_PER_LATITUDE_DEGREE * KMS_PER_MILE
LATITUDE_DEGREES = EARTH_RADIUS[:miles] / MILES_PER_LATITUDE_DEGREE

def initialize
raise NotImplementedError
end

# use underlying distance formula
def self.geo_distance *args
GeoDistance.new distance(args), get_units(args.last_option)
end

# used to convert various argument types into GeoPoints
def self.get_points(*args)
units = args.delete(args.last_option)

case args.size
when 2
[GeoPoint.new(args.first), GeoPoint.new(args.last), units]
when 4
[GeoPoint.new(args[0..1]), GeoPoint.new(args[2..3]), units]
else
raise "Distance from point A to B, must be given either as 4 arguments (lat1, lng1, lat2, lng2) or 2 arguments: (pointA, pointB), was: #{args}"
end
end

# used to get the units for how to calculate the distance
def self.get_units options = {}
GeoUnits.key(options[:units] || :kms)
end

def self.degrees_to_radians(degrees)
degrees.to_f / 180.0 * Math::PI
end

def self.units_sphere_multiplier(units)
EARTH_RADIUS[GeoUnit.key units]
end

def self.units_per_latitude_degree(units)
GeoUnits.radian_multiplier(units.to_sym)
end

def self.units_per_longitude_degree(lat, units)
miles_per_longitude_degree = (LATITUDE_DEGREES * Math.cos(lat * PI_DIV_RAD)).abs
case units
when :kms
miles_per_longitude_degree * KMS_PER_MILE
when :miles
miles_per_longitude_degree
end
end
end
end

0 comments on commit 74bc126

Please sign in to comment.