geo-calculator 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rbenv-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +3 -0
- data/geo-calculator.gemspec +27 -0
- data/lib/geo-calculator.rb +6 -0
- data/lib/geo-calculator/calculations.rb +428 -0
- data/lib/geo-calculator/configuration.rb +124 -0
- data/lib/geo-calculator/configuration_hash.rb +11 -0
- data/lib/geo-calculator/hash_recursive_merge.rb +74 -0
- data/lib/geo-calculator/sql.rb +107 -0
- data/lib/geo-calculator/store/active_record.rb +290 -0
- data/lib/geo-calculator/store/base.rb +126 -0
- data/lib/geo-calculator/version.rb +3 -0
- data/spec/includes_spec.rb +21 -0
- data/spec/spec_helper.rb +6 -0
- metadata +149 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: ad31f2eb55e43264bc6f0b7c6e7cf73a4bf89f13
|
|
4
|
+
data.tar.gz: 62de18df9d4dc630fae7c4ddd4004bb4a515575b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2149633f7a90f466839b83d83d0b256b8eeb006ea0c3d304055e14618687f40adcdcd080b66175b67abeb9314697331c60d8efbce58ae396528a6cb3c1278d5e
|
|
7
|
+
data.tar.gz: fff58386134bfe6cb04c504051b322f698be8f7735bca9fbd66c0156844ffda53c704103b34b617825fecf12f6f2dbf98eed9810185e2e97ab3b72a2b3ec270e
|
data/.gitignore
ADDED
data/.rbenv-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.2.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2015 Ryan MacInnes
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# GeoCalculator
|
|
2
|
+
|
|
3
|
+
GeoCalculator is an extraction of the calculation methods from the [Ruby Geocoder](https://github.com/alexreisner/geocoder) gem. Its purpose is to allow the geocoding calculation methods for models that already have latitude and longitude (obtained from the browser or an iphone app) to use distance calculations to find nearby model instances. It was extracted for use in Heat.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'geo-calculator'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install geo-calculator
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Add the line `include Geocoder::Store::ActiveRecord` into your model like so:
|
|
22
|
+
|
|
23
|
+
# app/models/location.rb
|
|
24
|
+
class Location < ActiveRecord::Base
|
|
25
|
+
include Geocoder::Store::ActiveRecord
|
|
26
|
+
|
|
27
|
+
def self.geocoder_options
|
|
28
|
+
{
|
|
29
|
+
latitude: 'latitude',
|
|
30
|
+
longitude: 'longitude',
|
|
31
|
+
units: :mi
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
make sure that your model has a `latitude` and `longitude` column.
|
|
37
|
+
|
|
38
|
+
Make sure to define the `geocoder_options` method, and specify the names of your latitude / longitude columns plus the units you want to use.
|
|
39
|
+
|
|
40
|
+
This will give your model a bunch of calculation methods, like `.near` and `.within_bounding_box`. See them all [here](https://github.com/alexreisner/geocoder/blob/master/lib/geocoder/stores/active_record.rb)
|
|
41
|
+
|
|
42
|
+
## Contributing
|
|
43
|
+
|
|
44
|
+
1. Fork it
|
|
45
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
46
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
47
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
48
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'geo-calculator/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'geo-calculator'
|
|
8
|
+
spec.version = GeoCalculator::VERSION
|
|
9
|
+
spec.authors = ['Ryan MacInnes']
|
|
10
|
+
spec.email = ['ryan.macinnes@gmail.com']
|
|
11
|
+
spec.description = %q{Use ruby geocoder calculation methods without geocoding}
|
|
12
|
+
spec.summary = %q{This gem uses the ruby geocoder gem calculation methods to allow calculation of nearby locations without needing all the geocoding code.}
|
|
13
|
+
spec.homepage = 'https://github.com/goddamnyouryan/geo-calculator'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
|
22
|
+
spec.add_development_dependency 'rake'
|
|
23
|
+
spec.add_development_dependency 'rspec'
|
|
24
|
+
spec.add_development_dependency 'byebug'
|
|
25
|
+
spec.add_development_dependency 'activerecord'
|
|
26
|
+
spec.add_development_dependency 'sqlite3'
|
|
27
|
+
end
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
module Geocoder
|
|
2
|
+
module Calculations
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Compass point names, listed clockwise starting at North.
|
|
7
|
+
#
|
|
8
|
+
# If you want bearings named using more, fewer, or different points
|
|
9
|
+
# override Geocoder::Calculations.COMPASS_POINTS with your own array.
|
|
10
|
+
#
|
|
11
|
+
COMPASS_POINTS = %w[N NE E SE S SW W NW]
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# Radius of the Earth, in kilometers.
|
|
15
|
+
# Value taken from: http://en.wikipedia.org/wiki/Earth_radius
|
|
16
|
+
#
|
|
17
|
+
EARTH_RADIUS = 6371.0
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# Conversion factor: multiply by kilometers to get miles.
|
|
21
|
+
#
|
|
22
|
+
KM_IN_MI = 0.621371192
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Conversion factor: multiply by nautical miles to get miles.
|
|
26
|
+
#
|
|
27
|
+
KM_IN_NM = 0.539957
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Conversion factor: multiply by radians to get degrees.
|
|
31
|
+
#
|
|
32
|
+
DEGREES_PER_RADIAN = 57.2957795
|
|
33
|
+
|
|
34
|
+
# Not a number constant
|
|
35
|
+
NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Returns true if all given arguments are valid latitude/longitude values.
|
|
39
|
+
#
|
|
40
|
+
def coordinates_present?(*args)
|
|
41
|
+
args.each do |a|
|
|
42
|
+
# note that Float::NAN != Float::NAN
|
|
43
|
+
# still, this could probably be improved:
|
|
44
|
+
return false if (!a.is_a?(Numeric) or a.to_s == "NaN")
|
|
45
|
+
end
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Distance spanned by one degree of latitude in the given units.
|
|
51
|
+
#
|
|
52
|
+
def latitude_degree_distance(units = nil)
|
|
53
|
+
units ||= Geocoder.config.units
|
|
54
|
+
2 * Math::PI * earth_radius(units) / 360
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Distance spanned by one degree of longitude at the given latitude.
|
|
59
|
+
# This ranges from around 69 miles at the equator to zero at the poles.
|
|
60
|
+
#
|
|
61
|
+
def longitude_degree_distance(latitude, units = nil)
|
|
62
|
+
units ||= Geocoder.config.units
|
|
63
|
+
latitude_degree_distance(units) * Math.cos(to_radians(latitude))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Distance between two points on Earth (Haversine formula).
|
|
68
|
+
# Takes two points and an options hash.
|
|
69
|
+
# The points are given in the same way that points are given to all
|
|
70
|
+
# Geocoder methods that accept points as arguments. They can be:
|
|
71
|
+
#
|
|
72
|
+
# * an array of coordinates ([lat,lon])
|
|
73
|
+
# * a geocodable address (string)
|
|
74
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
|
75
|
+
# which returns a [lat,lon] array
|
|
76
|
+
#
|
|
77
|
+
# The options hash supports:
|
|
78
|
+
#
|
|
79
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
|
|
80
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
81
|
+
#
|
|
82
|
+
def distance_between(point1, point2, options = {})
|
|
83
|
+
|
|
84
|
+
# set default options
|
|
85
|
+
options[:units] ||= Geocoder.config.units
|
|
86
|
+
|
|
87
|
+
# convert to coordinate arrays
|
|
88
|
+
point1 = extract_coordinates(point1)
|
|
89
|
+
point2 = extract_coordinates(point2)
|
|
90
|
+
|
|
91
|
+
# convert degrees to radians
|
|
92
|
+
point1 = to_radians(point1)
|
|
93
|
+
point2 = to_radians(point2)
|
|
94
|
+
|
|
95
|
+
# compute deltas
|
|
96
|
+
dlat = point2[0] - point1[0]
|
|
97
|
+
dlon = point2[1] - point1[1]
|
|
98
|
+
|
|
99
|
+
a = (Math.sin(dlat / 2))**2 + Math.cos(point1[0]) *
|
|
100
|
+
(Math.sin(dlon / 2))**2 * Math.cos(point2[0])
|
|
101
|
+
c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
|
|
102
|
+
c * earth_radius(options[:units])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Bearing between two points on Earth.
|
|
107
|
+
# Returns a number of degrees from due north (clockwise).
|
|
108
|
+
#
|
|
109
|
+
# See Geocoder::Calculations.distance_between for
|
|
110
|
+
# ways of specifying the points. Also accepts an options hash:
|
|
111
|
+
#
|
|
112
|
+
# * <tt>:method</tt> - <tt>:linear</tt> or <tt>:spherical</tt>;
|
|
113
|
+
# the spherical method is "correct" in that it returns the shortest path
|
|
114
|
+
# (one along a great circle) but the linear method is less confusing
|
|
115
|
+
# (returns due east or west when given two points with the same latitude).
|
|
116
|
+
# Use Geocoder.configure(:distances => ...) to configure calculation method.
|
|
117
|
+
#
|
|
118
|
+
# Based on: http://www.movable-type.co.uk/scripts/latlong.html
|
|
119
|
+
#
|
|
120
|
+
def bearing_between(point1, point2, options = {})
|
|
121
|
+
|
|
122
|
+
# set default options
|
|
123
|
+
options[:method] ||= Geocoder.config.distances
|
|
124
|
+
options[:method] = :linear unless options[:method] == :spherical
|
|
125
|
+
|
|
126
|
+
# convert to coordinate arrays
|
|
127
|
+
point1 = extract_coordinates(point1)
|
|
128
|
+
point2 = extract_coordinates(point2)
|
|
129
|
+
|
|
130
|
+
# convert degrees to radians
|
|
131
|
+
point1 = to_radians(point1)
|
|
132
|
+
point2 = to_radians(point2)
|
|
133
|
+
|
|
134
|
+
# compute deltas
|
|
135
|
+
dlat = point2[0] - point1[0]
|
|
136
|
+
dlon = point2[1] - point1[1]
|
|
137
|
+
|
|
138
|
+
case options[:method]
|
|
139
|
+
when :linear
|
|
140
|
+
y = dlon
|
|
141
|
+
x = dlat
|
|
142
|
+
|
|
143
|
+
when :spherical
|
|
144
|
+
y = Math.sin(dlon) * Math.cos(point2[0])
|
|
145
|
+
x = Math.cos(point1[0]) * Math.sin(point2[0]) -
|
|
146
|
+
Math.sin(point1[0]) * Math.cos(point2[0]) * Math.cos(dlon)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
bearing = Math.atan2(x,y)
|
|
150
|
+
# Answer is in radians counterclockwise from due east.
|
|
151
|
+
# Convert to degrees clockwise from due north:
|
|
152
|
+
(90 - to_degrees(bearing) + 360) % 360
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# Translate a bearing (float) into a compass direction (string, eg "North").
|
|
157
|
+
#
|
|
158
|
+
def compass_point(bearing, points = COMPASS_POINTS)
|
|
159
|
+
seg_size = 360 / points.size
|
|
160
|
+
points[((bearing + (seg_size / 2)) % 360) / seg_size]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
##
|
|
164
|
+
# Compute the geographic center (aka geographic midpoint, center of
|
|
165
|
+
# gravity) for an array of geocoded objects and/or [lat,lon] arrays
|
|
166
|
+
# (can be mixed). Any objects missing coordinates are ignored. Follows
|
|
167
|
+
# the procedure documented at http://www.geomidpoint.com/calculation.html.
|
|
168
|
+
#
|
|
169
|
+
def geographic_center(points)
|
|
170
|
+
|
|
171
|
+
# convert objects to [lat,lon] arrays and convert degrees to radians
|
|
172
|
+
coords = points.map{ |p| to_radians(extract_coordinates(p)) }
|
|
173
|
+
|
|
174
|
+
# convert to Cartesian coordinates
|
|
175
|
+
x = []; y = []; z = []
|
|
176
|
+
coords.each do |p|
|
|
177
|
+
x << Math.cos(p[0]) * Math.cos(p[1])
|
|
178
|
+
y << Math.cos(p[0]) * Math.sin(p[1])
|
|
179
|
+
z << Math.sin(p[0])
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# compute average coordinate values
|
|
183
|
+
xa, ya, za = [x,y,z].map do |c|
|
|
184
|
+
c.inject(0){ |tot,i| tot += i } / c.size.to_f
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# convert back to latitude/longitude
|
|
188
|
+
lon = Math.atan2(ya, xa)
|
|
189
|
+
hyp = Math.sqrt(xa**2 + ya**2)
|
|
190
|
+
lat = Math.atan2(za, hyp)
|
|
191
|
+
|
|
192
|
+
# return answer in degrees
|
|
193
|
+
to_degrees [lat, lon]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
# Returns coordinates of the southwest and northeast corners of a box
|
|
198
|
+
# with the given point at its center. The radius is the shortest distance
|
|
199
|
+
# from the center point to any side of the box (the length of each side
|
|
200
|
+
# is twice the radius).
|
|
201
|
+
#
|
|
202
|
+
# This is useful for finding corner points of a map viewport, or for
|
|
203
|
+
# roughly limiting the possible solutions in a geo-spatial search
|
|
204
|
+
# (ActiveRecord queries use it thusly).
|
|
205
|
+
#
|
|
206
|
+
# See Geocoder::Calculations.distance_between for
|
|
207
|
+
# ways of specifying the point. Also accepts an options hash:
|
|
208
|
+
#
|
|
209
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>.
|
|
210
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
211
|
+
#
|
|
212
|
+
def bounding_box(point, radius, options = {})
|
|
213
|
+
lat,lon = extract_coordinates(point)
|
|
214
|
+
radius = radius.to_f
|
|
215
|
+
units = options[:units] || Geocoder.config.units
|
|
216
|
+
[
|
|
217
|
+
lat - (radius / latitude_degree_distance(units)),
|
|
218
|
+
lon - (radius / longitude_degree_distance(lat, units)),
|
|
219
|
+
lat + (radius / latitude_degree_distance(units)),
|
|
220
|
+
lon + (radius / longitude_degree_distance(lat, units))
|
|
221
|
+
]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
##
|
|
225
|
+
# Random point within a circle of provided radius centered
|
|
226
|
+
# around the provided point
|
|
227
|
+
# Takes one point, one radius, and an options hash.
|
|
228
|
+
# The points are given in the same way that points are given to all
|
|
229
|
+
# Geocoder methods that accept points as arguments. They can be:
|
|
230
|
+
#
|
|
231
|
+
# * an array of coordinates ([lat,lon])
|
|
232
|
+
# * a geocodable address (string)
|
|
233
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
|
234
|
+
# which returns a [lat,lon] array
|
|
235
|
+
#
|
|
236
|
+
# The options hash supports:
|
|
237
|
+
#
|
|
238
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
|
|
239
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
240
|
+
def random_point_near(center, radius, options = {})
|
|
241
|
+
|
|
242
|
+
# set default options
|
|
243
|
+
options[:units] ||= Geocoder.config.units
|
|
244
|
+
|
|
245
|
+
# convert to coordinate arrays
|
|
246
|
+
center = extract_coordinates(center)
|
|
247
|
+
|
|
248
|
+
earth_circumference = 2 * Math::PI * earth_radius(options[:units])
|
|
249
|
+
max_degree_delta = 360.0 * (radius / earth_circumference)
|
|
250
|
+
|
|
251
|
+
# random bearing in radians
|
|
252
|
+
theta = 2 * Math::PI * rand
|
|
253
|
+
|
|
254
|
+
# random radius, use the square root to ensure a uniform
|
|
255
|
+
# distribution of points over the circle
|
|
256
|
+
r = Math.sqrt(rand) * max_degree_delta
|
|
257
|
+
|
|
258
|
+
delta_lat, delta_long = [r * Math.cos(theta), r * Math.sin(theta)]
|
|
259
|
+
[center[0] + delta_lat, center[1] + delta_long]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
##
|
|
263
|
+
# Given a start point, distance, and heading (in degrees), provides
|
|
264
|
+
# an endpoint.
|
|
265
|
+
# The starting point is given in the same way that points are given to all
|
|
266
|
+
# Geocoder methods that accept points as arguments. It can be:
|
|
267
|
+
#
|
|
268
|
+
# * an array of coordinates ([lat,lon])
|
|
269
|
+
# * a geocodable address (string)
|
|
270
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
|
271
|
+
# which returns a [lat,lon] array
|
|
272
|
+
#
|
|
273
|
+
def endpoint(start, heading, distance, options = {})
|
|
274
|
+
options[:units] ||= Geocoder.config.units
|
|
275
|
+
radius = earth_radius(options[:units])
|
|
276
|
+
|
|
277
|
+
start = extract_coordinates(start)
|
|
278
|
+
|
|
279
|
+
# convert degrees to radians
|
|
280
|
+
start = to_radians(start)
|
|
281
|
+
|
|
282
|
+
lat = start[0]
|
|
283
|
+
lon = start[1]
|
|
284
|
+
heading = to_radians(heading)
|
|
285
|
+
distance = distance.to_f
|
|
286
|
+
|
|
287
|
+
end_lat = Math.asin(Math.sin(lat)*Math.cos(distance/radius) +
|
|
288
|
+
Math.cos(lat)*Math.sin(distance/radius)*Math.cos(heading))
|
|
289
|
+
|
|
290
|
+
end_lon = lon+Math.atan2(Math.sin(heading)*Math.sin(distance/radius)*Math.cos(lat),
|
|
291
|
+
Math.cos(distance/radius)-Math.sin(lat)*Math.sin(end_lat))
|
|
292
|
+
|
|
293
|
+
to_degrees [end_lat, end_lon]
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
##
|
|
297
|
+
# Convert degrees to radians.
|
|
298
|
+
# If an array (or multiple arguments) is passed,
|
|
299
|
+
# converts each value and returns array.
|
|
300
|
+
#
|
|
301
|
+
def to_radians(*args)
|
|
302
|
+
args = args.first if args.first.is_a?(Array)
|
|
303
|
+
if args.size == 1
|
|
304
|
+
args.first * (Math::PI / 180)
|
|
305
|
+
else
|
|
306
|
+
args.map{ |i| to_radians(i) }
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
##
|
|
311
|
+
# Convert radians to degrees.
|
|
312
|
+
# If an array (or multiple arguments) is passed,
|
|
313
|
+
# converts each value and returns array.
|
|
314
|
+
#
|
|
315
|
+
def to_degrees(*args)
|
|
316
|
+
args = args.first if args.first.is_a?(Array)
|
|
317
|
+
if args.size == 1
|
|
318
|
+
(args.first * 180.0) / Math::PI
|
|
319
|
+
else
|
|
320
|
+
args.map{ |i| to_degrees(i) }
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def distance_to_radians(distance, units = nil)
|
|
325
|
+
units ||= Geocoder.config.units
|
|
326
|
+
distance.to_f / earth_radius(units)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def radians_to_distance(radians, units = nil)
|
|
330
|
+
units ||= Geocoder.config.units
|
|
331
|
+
radians * earth_radius(units)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
##
|
|
335
|
+
# Convert miles to kilometers.
|
|
336
|
+
#
|
|
337
|
+
def to_kilometers(mi)
|
|
338
|
+
mi * mi_in_km
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
##
|
|
342
|
+
# Convert kilometers to miles.
|
|
343
|
+
#
|
|
344
|
+
def to_miles(km)
|
|
345
|
+
km * km_in_mi
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
##
|
|
349
|
+
# Convert kilometers to nautical miles.
|
|
350
|
+
#
|
|
351
|
+
def to_nautical_miles(km)
|
|
352
|
+
km * km_in_nm
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
##
|
|
356
|
+
# Radius of the Earth in the given units (:mi or :km).
|
|
357
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
|
358
|
+
#
|
|
359
|
+
def earth_radius(units = nil)
|
|
360
|
+
units ||= Geocoder.config.units
|
|
361
|
+
case units
|
|
362
|
+
when :km; EARTH_RADIUS
|
|
363
|
+
when :mi; to_miles(EARTH_RADIUS)
|
|
364
|
+
when :nm; to_nautical_miles(EARTH_RADIUS)
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
##
|
|
369
|
+
# Conversion factor: km to mi.
|
|
370
|
+
#
|
|
371
|
+
def km_in_mi
|
|
372
|
+
KM_IN_MI
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
##
|
|
376
|
+
# Conversion factor: km to nm.
|
|
377
|
+
#
|
|
378
|
+
def km_in_nm
|
|
379
|
+
KM_IN_NM
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
##
|
|
385
|
+
# Conversion factor: mi to km.
|
|
386
|
+
#
|
|
387
|
+
def mi_in_km
|
|
388
|
+
1.0 / KM_IN_MI
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
##
|
|
392
|
+
# Conversion factor: nm to km.
|
|
393
|
+
#
|
|
394
|
+
def nm_in_km
|
|
395
|
+
1.0 / KM_IN_NM
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
##
|
|
399
|
+
# Takes an object which is a [lat,lon] array, a geocodable string,
|
|
400
|
+
# or an object that implements +to_coordinates+ and returns a
|
|
401
|
+
# [lat,lon] array. Note that if a string is passed this may be a slow-
|
|
402
|
+
# running method and may return nil.
|
|
403
|
+
#
|
|
404
|
+
def extract_coordinates(point)
|
|
405
|
+
case point
|
|
406
|
+
when Array
|
|
407
|
+
if point.size == 2
|
|
408
|
+
lat, lon = point
|
|
409
|
+
if !lat.nil? && lat.respond_to?(:to_f) and
|
|
410
|
+
!lon.nil? && lon.respond_to?(:to_f)
|
|
411
|
+
then
|
|
412
|
+
return [ lat.to_f, lon.to_f ]
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
when String
|
|
416
|
+
point = Geocoder.coordinates(point) and return point
|
|
417
|
+
else
|
|
418
|
+
if point.respond_to?(:to_coordinates)
|
|
419
|
+
if Array === array = point.to_coordinates
|
|
420
|
+
return extract_coordinates(array)
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
[ NAN, NAN ]
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|