Skip to content

Commit

Permalink
Merge pull request #3 from jonatasrancan/restruct
Browse files Browse the repository at this point in the history
Restruct
  • Loading branch information
jonatasrancan committed Jun 9, 2015
2 parents a8c1df2 + 931a403 commit fbcc72e
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 58 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.1.0
36 changes: 6 additions & 30 deletions lib/navigate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,22 @@ module Navigate
EARTH_RADIUS = { 'KM' => 6371.009, 'MI' => 3958.761, 'NM' => 3440.070, 'YD' => 6967420, 'FT' => 20902260 }

def self.distance_between(position1, position2, radius_unit = 'KM')
Math::acos(
Math::sin(position1.latitude.to_radians) * Math::sin(position2.latitude.to_radians) +
Math::cos(position1.latitude.to_radians) * Math::cos(position2.latitude.to_radians) *
Math::cos((position2.longitude - position1.longitude).to_radians)
) * EARTH_RADIUS[radius_unit]
DistanceBetween.calculate(position1, position2, radius_unit)
end

def self.bearing(position1, position2)
dLon = (position2.longitude - position1.longitude).to_radians

y = Math::sin(dLon) * Math::cos(position2.latitude.to_radians)
x = Math::cos(position1.latitude.to_radians) * Math::sin(position2.latitude.to_radians) -
Math::sin(position1.latitude.to_radians) * Math::cos(position2.latitude.to_radians) * Math::cos(dLon)

Math::atan2(y, x).to_bearing
Bearing.calculate(position1, position2)
end

def self.destination(position, options = {})
radius = EARTH_RADIUS[options[:radius_unit] || 'KM']
distance = options[:distance] || 100.0
brng = options[:bearing] || 90.0

new_lat = Math::asin(
Math::sin(position.latitude.to_radians) * Math::cos(distance / radius) +
Math::cos(position.latitude.to_radians) * Math::sin(distance / radius) *
Math::cos(brng)
)

new_lon = position.longitude.to_radians + Math::atan2(
Math::sin(brng) * Math::sin(distance / radius) * Math::cos(position.latitude.to_radians),
Math::cos(distance / radius) - Math::sin(position.latitude.to_radians) * Math::sin(new_lat)
)

new_lon = (new_lon + Math::PI) % (2 * Math::PI) - Math::PI

Position.new new_lat.to_degrees, new_lon.to_degrees
Destination.calculate(position, options)
end
end

path = File.expand_path(File.dirname(__FILE__))
$:.unshift path unless $:.include?(path)
require 'navigate/conversions'
require 'navigate/position'
require 'navigate/bearing'
require 'navigate/distance_between'
require 'navigate/destination'
37 changes: 37 additions & 0 deletions lib/navigate/bearing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Navigate
private

class Bearing
def self.calculate(*args)
new(*args).calculate
end

def initialize(position1, position2)
valid_args?(position1, position2)

@latitude1 = position1.latitude.to_radians
@longitude1 = position1.longitude.to_radians
@latitude2 = position2.latitude.to_radians
@longitude2 = position2.longitude.to_radians
end

def calculate
dLon = longitude2 - longitude1

y = Math::sin(dLon) * Math::cos(latitude2)
x = Math::cos(latitude1) * Math::sin(latitude2) -
Math::sin(latitude1) * Math::cos(latitude2) * Math::cos(dLon)

Math::atan2(y, x).to_bearing
end

private

attr_reader :latitude1, :latitude2, :longitude1, :longitude2

def valid_args?(position1, position2)
raise 'First arg, must be a Navigate::Position instance' unless position1.is_a?(Navigate::Position)
raise 'Second arg, must be a Navigate::Position instance' unless position2.is_a?(Navigate::Position)
end
end
end
45 changes: 45 additions & 0 deletions lib/navigate/destination.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Navigate
private

class Destination
def self.calculate(*args)
new(*args).calculate
end

def initialize(position, options = {})
valid_args?(position, options[:bearing])

@latitude = position.latitude.to_radians
@longitude = position.longitude.to_radians
@radius = EARTH_RADIUS[options[:radius_unit] || 'KM']
@bearing = options[:bearing] || 90.0
@distance = options[:distance] || 100.0
end

def calculate
new_lat = Math::asin(
Math::sin(latitude) * Math::cos(distance / radius) +
Math::cos(latitude) * Math::sin(distance / radius) *
Math::cos(bearing)
)

new_lon = longitude + Math::atan2(
Math::sin(bearing) * Math::sin(distance / radius) * Math::cos(latitude),
Math::cos(distance / radius) - Math::sin(latitude) * Math::sin(new_lat)
)

new_lon = (new_lon + Math::PI) % (2 * Math::PI) - Math::PI

Position.new new_lat.to_degrees, new_lon.to_degrees
end

private

attr_reader :latitude, :longitude, :radius, :bearing, :distance

def valid_args?(position, bearing)
raise 'First arg, must be a Navigate::Position instance' unless position.is_a?(Navigate::Position)
raise 'Bearing must be between 0.0 and 360.0' if bearing && !(0.0..360.0).include?(bearing)
end
end
end
36 changes: 36 additions & 0 deletions lib/navigate/distance_between.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Navigate
private

class DistanceBetween
def self.calculate(*args)
new(*args).calculate
end

def initialize(position1, position2, radius_unit)
valid_args?(position1, position2)

@latitude1 = position1.latitude.to_radians
@longitude1 = position1.longitude.to_radians
@latitude2 = position2.latitude.to_radians
@longitude2 = position2.longitude.to_radians
@radius_unit = EARTH_RADIUS[radius_unit || 'KM']
end

def calculate
Math::acos(
Math::sin(latitude1) * Math::sin(latitude2) +
Math::cos(latitude1) * Math::cos(latitude2) *
Math::cos(longitude2 - longitude1)
) * radius_unit
end

private

attr_reader :latitude1, :latitude2, :longitude1, :longitude2, :radius_unit

def valid_args?(position1, position2)
raise 'First arg, must be a Navigate::Position instance' unless position1.is_a?(Navigate::Position)
raise 'Second arg, must be a Navigate::Position instance' unless position2.is_a?(Navigate::Position)
end
end
end
2 changes: 0 additions & 2 deletions lib/navigate/position.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ def valid_longitude?
def valid?
valid_latitude?
valid_longitude?

true
end
end
end
2 changes: 1 addition & 1 deletion navigate.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Gem::Specification.new do |s|
s.name = "navigate"
s.version = "1.0.0"
s.version = "1.1.0"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
Expand Down
71 changes: 47 additions & 24 deletions test/test_navigate.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
require 'helper'

describe Navigate do
context "#distance" do
it 'calculates the distance between 2 positions in kilometers' do
position1 = Navigate::Position.new(90.0, 120.0)
position2 = Navigate::Position.new(90.0, 120.0)
before do
@position1 = Navigate::Position.new(90.0, 120.0)
@position2 = Navigate::Position.new(80.0, 120.0)
end

Navigate::distance_between(position1, position2).must_equal 0
describe "#distance_between" do
it 'calculates the distance between 2 positions in kilometers' do
Navigate::distance_between(@position1, @position1).must_equal 0
end

it 'calculates the distance between 2 positions in kilometers' do
position1 = Navigate::Position.new(90.0, 120.0)
position2 = Navigate::Position.new(80.0, 120.0)

km = Navigate::distance_between(position1, position2).round(2)
km = Navigate::distance_between(@position1, @position2).round(2)

km.must_equal 1111.95
end

it 'calculates the distance between 2 positions in nautic miles' do
position1 = Navigate::Position.new(90.0, 120.0)
position2 = Navigate::Position.new(80.0, 120.0)

nm = Navigate::distance_between(position1, position2, 'NM').round(2)
nm = Navigate::distance_between(@position1, @position2, 'NM').round(2)

nm.must_equal 600.41
end
end

describe '#bearing' do
before do
@position1 = Navigate::Position.new(90.0, 120.0)
@position2 = Navigate::Position.new(80.0, 120.0)
it "raise exception when first arg isn't a Navigate::Position" do
exception = assert_raises(RuntimeError) { Navigate::distance_between('wrong type', 90.0) }

assert_match /First arg, must be a Navigate::Position instance/, exception.message
end

it "raise exception when second arg isn't a Navigate::Position" do
exception = assert_raises(RuntimeError) { Navigate::distance_between(@position1, 500) }

assert_match /Second arg, must be a Navigate::Position instance/, exception.message
end
end

describe '#bearing' do
it 'calculates the bearing between 2 positions' do
bearing = Navigate::bearing(@position1, @position2)

Expand All @@ -45,13 +48,23 @@

bearing.must_equal 0
end

it "raise exception when first arg isn't a Navigate::Position" do
exception = assert_raises(RuntimeError) { Navigate::bearing('wrong type', 90.0) }

assert_match /First arg, must be a Navigate::Position instance/, exception.message
end

it "raise exception when second arg isn't a Navigate::Position" do
exception = assert_raises(RuntimeError) { Navigate::bearing(@position1, 500) }

assert_match /Second arg, must be a Navigate::Position instance/, exception.message
end
end

describe '#destination' do
it 'returns a final position, using default radius unit, distance and bearing' do
position1 = Navigate::Position.new(90.0, 120.0)

destination = Navigate::destination(position1)
destination = Navigate::destination(@position1)

assert_kind_of Navigate::Position, destination

Expand All @@ -60,12 +73,22 @@
end

it 'returns a final positions, passing custom radius unit, distance and bearing' do
position1 = Navigate::Position.new(90.0, 120.0)

destination = Navigate::destination(position1, bearing: 270, radius_unit: 'NM', distance: 200)
destination = Navigate::destination(@position1, bearing: 270, radius_unit: 'NM', distance: 200)

destination.longitude.round(2).must_equal 30.0
destination.latitude.round(2).must_equal 86.67
end

it "raise exception when first arg isn't a Navigate::Position" do
exception = assert_raises(RuntimeError) { Navigate::destination('wrong type') }

assert_match /First arg, must be a Navigate::Position instance/, exception.message
end

it "raise exception when bearing is out of range" do
exception = assert_raises(RuntimeError) { Navigate::destination(@position1, bearing: 500) }

assert_match /Bearing must be between 0.0 and 360.0/, exception.message
end
end
end

0 comments on commit fbcc72e

Please sign in to comment.