gis-distance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +2 -0
- data/MANIFEST +7 -0
- data/README +22 -0
- data/Rakefile +27 -0
- data/gis-distance.gemspec +22 -0
- data/lib/gis/distance.rb +135 -0
- data/test/test_gis_distance.rb +98 -0
- metadata +64 -0
data/CHANGES
ADDED
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
== Description
|
2
|
+
The gis-distance library allows you to calculate geographic distance between
|
3
|
+
two points using the formula of your choice.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
gem install gis-distance
|
7
|
+
|
8
|
+
== Synopsis
|
9
|
+
require 'gis/distance'
|
10
|
+
|
11
|
+
# New York to Los Angeles
|
12
|
+
gis = GIS::Distance.new(40.47, 73.58, 34.3, 118.15)
|
13
|
+
|
14
|
+
p gis.distance # Kilometers
|
15
|
+
p gis.distance.mi # Miles
|
16
|
+
|
17
|
+
== License
|
18
|
+
Artistic 2.0
|
19
|
+
|
20
|
+
== Authors
|
21
|
+
* Daniel Berger
|
22
|
+
* Ardith Faulkner
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
include Config
|
4
|
+
|
5
|
+
desc "Install the gis-distance library"
|
6
|
+
task :install_lib do
|
7
|
+
dest = File.join(CONFIG['sitelibdir'], 'gis')
|
8
|
+
Dir.mkdir(dest) unless File.exists? dest
|
9
|
+
cp 'lib/gis/distance.rb', dest, :verbose => true
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Build the gis-distance gem'
|
13
|
+
task :gem do
|
14
|
+
spec = eval(IO.read('gis-distance.gemspec'))
|
15
|
+
Gem::Builder.new(spec).build
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Install the gis-distance library as a gem'
|
19
|
+
task :install_gem => [:gem] do
|
20
|
+
file = Dir["*.gem"].first
|
21
|
+
sh "gem install #{file}"
|
22
|
+
end
|
23
|
+
|
24
|
+
Rake::TestTask.new do |t|
|
25
|
+
t.warning = true
|
26
|
+
t.verbose = true
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'gis-distance'
|
5
|
+
gem.version = '1.0.0'
|
6
|
+
gem.authors = ['Daniel J. Berger', 'Ardith Faulkner']
|
7
|
+
gem.license = 'Artistic 2.0'
|
8
|
+
gem.description = 'Calculate the distance between 2 points on Earth'
|
9
|
+
gem.email = 'djberg96@gmail.com'
|
10
|
+
gem.files = Dir['**/*'].reject{ |f| f.include?('CVS') }
|
11
|
+
gem.test_files = ['test/test_gis_distance.rb']
|
12
|
+
gem.has_rdoc = true
|
13
|
+
gem.homepage = 'http://github.com/djberg96/gis-distance'
|
14
|
+
|
15
|
+
gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
16
|
+
|
17
|
+
gem.summary = <<-EOF
|
18
|
+
The gis-distance library provides a simple interface for
|
19
|
+
calculating the distance between two points on Earth using
|
20
|
+
latitude and longitude.
|
21
|
+
EOF
|
22
|
+
end
|
data/lib/gis/distance.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# The GIS module serves as a namespace only.
|
2
|
+
module GIS
|
3
|
+
# The Distance class encapsulates methods related to geographic distance.
|
4
|
+
class Distance
|
5
|
+
# Error raised if latitude or longitude values are invalid.
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
# The version of the gis-distance library
|
9
|
+
VERSION = '1.0.0'
|
10
|
+
|
11
|
+
# Create a new GIS::Distance object using the two sets of coordinates
|
12
|
+
# that are provided.
|
13
|
+
#
|
14
|
+
# If invalid coordinates are provided a GIS::Distance::Error is raised.
|
15
|
+
#
|
16
|
+
def initialize(latitude1, longitude1, latitude2, longitude2)
|
17
|
+
validate(latitude1, longitude1, latitude2, longitude2)
|
18
|
+
|
19
|
+
@latitude1 = latitude1
|
20
|
+
@longitude1 = longitude1
|
21
|
+
@latitude2 = latitude2
|
22
|
+
@longitude2 = longitude2
|
23
|
+
|
24
|
+
@radius = 6367.45
|
25
|
+
@formula = 'haversine'
|
26
|
+
@distance = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the radius of the Earth in kilometers. The default is 6367.45.
|
30
|
+
#
|
31
|
+
def radius
|
32
|
+
@radius
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets the radius of the Earth in kilometers. This is variable because
|
36
|
+
# the Earth is not perfectly spherical, and you may wish to adjust it.
|
37
|
+
#
|
38
|
+
# However, the possible range of values is limited from 6357.0 to 6378.0.
|
39
|
+
# If a value outside of this range is provided a GIS::Distance::Error
|
40
|
+
# is raised.
|
41
|
+
#
|
42
|
+
# The default value is 6367.45.
|
43
|
+
#
|
44
|
+
# See http://en.wikipedia.org/wiki/Earth_radius for more information.
|
45
|
+
#
|
46
|
+
def radius=(kms)
|
47
|
+
if kms < 6357.0 || kms > 6378.0
|
48
|
+
raise Error, "Proposed radius '#{kms}' is out of range"
|
49
|
+
end
|
50
|
+
@radius = kms
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the formula used to calculate the distance. The default formula
|
54
|
+
# is 'haversine'.
|
55
|
+
#--
|
56
|
+
# See http://en.wikipedia.org/wiki/Haversine_formula for details.
|
57
|
+
def formula
|
58
|
+
@formula
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets the formula to be used internally for calculating the distance.
|
62
|
+
# The default is 'haversine'.
|
63
|
+
#
|
64
|
+
# If an unsupported formula is provided a GIS::Distance::Error is raised.
|
65
|
+
#
|
66
|
+
def formula=(formula)
|
67
|
+
case formula.to_s.downcase
|
68
|
+
when 'haversine'
|
69
|
+
@formula = 'haversine'
|
70
|
+
else
|
71
|
+
raise Error, "Formula '#{formula}' not supported"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the distance (in kilometers) between the two coordinates
|
76
|
+
# provided in the constructor.
|
77
|
+
#
|
78
|
+
def distance
|
79
|
+
@distance =
|
80
|
+
case @formula.to_s.downcase
|
81
|
+
when 'haversine'
|
82
|
+
haversine_formula
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Validate the latitude and longitude values. Latitudes must be between
|
89
|
+
# 90.0 and -90.0 while longitudes must be between 180.0 and -180.0.
|
90
|
+
#
|
91
|
+
def validate(lat1, lon1, lat2, lon2)
|
92
|
+
[lat1, lat2].each{ |lat|
|
93
|
+
if lat > 90 || lat < -90
|
94
|
+
msg = "Latitude '#{lat}' is invalid - must be between -90 and 90"
|
95
|
+
raise Error, msg
|
96
|
+
end
|
97
|
+
}
|
98
|
+
|
99
|
+
[lon1, lon2].each{ |lon|
|
100
|
+
if lon > 180 || lon < -180
|
101
|
+
msg = "Longitude '#{lon}' is invalid - must be between -180 and 180"
|
102
|
+
raise Error, msg
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
# See http://en.wikipedia.org/wiki/Haversine_formula
|
108
|
+
#
|
109
|
+
def haversine_formula
|
110
|
+
lat1 = @latitude1 * Math::PI / 180
|
111
|
+
lon1 = @longitude1 * Math::PI / 180
|
112
|
+
lat2 = @latitude2 * Math::PI / 180
|
113
|
+
lon2 = @longitude2 * Math::PI / 180
|
114
|
+
|
115
|
+
dlat = lat2 - lat1
|
116
|
+
dlon = lon2 - lon1
|
117
|
+
|
118
|
+
a = ((Math.sin(dlat/2))**2) + (Math.cos(lat1) * Math.cos(lat2) * (Math.sin(dlon/2)) ** 2)
|
119
|
+
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
120
|
+
|
121
|
+
@radius * c
|
122
|
+
end
|
123
|
+
|
124
|
+
# Add a custom method to the base Float class if it isn't already defined.
|
125
|
+
class ::Float
|
126
|
+
unless self.respond_to?(:mi)
|
127
|
+
# Convert miles to kilometers.
|
128
|
+
def mi
|
129
|
+
self * 0.621371192
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'gis/distance'
|
3
|
+
|
4
|
+
class TC_GIS_Distance < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@gis = GIS::Distance.new(40.47, 73.58, 34.3, 118.15)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_version
|
10
|
+
assert_equal('1.0.0', GIS::Distance::VERSION)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_distance_basic_functionality
|
14
|
+
assert_respond_to(@gis, :distance)
|
15
|
+
assert_nothing_raised{ @gis.distance }
|
16
|
+
assert_kind_of(Float, @gis.distance)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_distance
|
20
|
+
assert_in_delta(0.01, 3952.39, @gis.distance)
|
21
|
+
assert_equal(0.0, GIS::Distance.new(40.47, 73.58, 40.47, 73.58).distance)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_distance_expected_argument_errors
|
25
|
+
assert_raise(ArgumentError){ GIS::Distance.new }
|
26
|
+
assert_raise(ArgumentError){ GIS::Distance.new(40.47) }
|
27
|
+
assert_raise(ArgumentError){ GIS::Distance.new(40.47, 73.58) }
|
28
|
+
assert_raise(ArgumentError){ GIS::Distance.new(40.47, 73.58, 34.3) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_distance_expected_range_errors
|
32
|
+
assert_raise(GIS::Distance::Error){ GIS::Distance.new(91.0, 100.0, 45.0, 45.0) }
|
33
|
+
assert_raise(GIS::Distance::Error){ GIS::Distance.new(90.0, 190.0, 45.0, 45.0) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_radius_basic_functionality
|
37
|
+
assert_respond_to(@gis, :radius)
|
38
|
+
assert_nothing_raised{ @gis.radius }
|
39
|
+
assert_kind_of(Float, @gis.radius)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_default_radius
|
43
|
+
assert_equal(6367.45, @gis.radius)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_radius_expected_errors
|
47
|
+
assert_raise(ArgumentError){ @gis.radius(1) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_radius_setter_basic_functionality
|
51
|
+
assert_respond_to(@gis, :radius=)
|
52
|
+
assert_nothing_raised{ @gis.radius = 6368.0 }
|
53
|
+
assert_equal(6368.0, @gis.radius)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_radius_setter_expected_errors
|
57
|
+
assert_raise(GIS::Distance::Error){ @gis.radius = 6200 }
|
58
|
+
assert_raise(GIS::Distance::Error){ @gis.radius = 6400 }
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_formula_basic_functionality
|
62
|
+
assert_respond_to(@gis, :formula)
|
63
|
+
assert_nothing_raised{ @gis.formula }
|
64
|
+
assert_kind_of(String, @gis.formula)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_formula
|
68
|
+
assert_equal('haversine', @gis.formula)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_formula_setter_basic_functionality
|
72
|
+
assert_respond_to(@gis, :formula=)
|
73
|
+
assert_nothing_raised{ @gis.formula = 'haversine' }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_formula_expected_errors
|
77
|
+
assert_raise(ArgumentError){ @gis.formula(1) }
|
78
|
+
assert_raise(GIS::Distance::Error){ @gis.formula = 'foo' }
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_mi_basic_functionality
|
82
|
+
assert_respond_to(@gis.distance, :mi)
|
83
|
+
assert_nothing_raised{ @gis.distance.mi }
|
84
|
+
assert_kind_of(Float, @gis.distance.mi)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_mi
|
88
|
+
assert(@gis.distance > @gis.distance.mi)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_mi_expected_errors
|
92
|
+
assert_raise(ArgumentError){ @gis.distance.mi(1) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def teardown
|
96
|
+
@gis = nil
|
97
|
+
end
|
98
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gis-distance
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel J. Berger
|
8
|
+
- Ardith Faulkner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-10-10 00:00:00 -06:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Calculate the distance between 2 points on Earth
|
18
|
+
email: djberg96@gmail.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README
|
25
|
+
- CHANGES
|
26
|
+
- MANIFEST
|
27
|
+
files:
|
28
|
+
- CHANGES
|
29
|
+
- gis-distance.gemspec
|
30
|
+
- lib/gis/distance.rb
|
31
|
+
- MANIFEST
|
32
|
+
- Rakefile
|
33
|
+
- README
|
34
|
+
- test/test_gis_distance.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/djberg96/gis-distance
|
37
|
+
licenses:
|
38
|
+
- Artistic 2.0
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: The gis-distance library provides a simple interface for calculating the distance between two points on Earth using latitude and longitude.
|
63
|
+
test_files:
|
64
|
+
- test/test_gis_distance.rb
|