really-broken-geocoder 1.5.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/CHANGELOG.md +557 -0
- data/LICENSE +20 -0
- data/README.md +3 -0
- data/bin/geocode +5 -0
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/autoexpire_cache_redis.rb +28 -0
- data/examples/cache_bypass.rb +48 -0
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/config_generator.rb +14 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +22 -0
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
- data/lib/generators/geocoder/migration_version.rb +15 -0
- data/lib/geocoder.rb +48 -0
- data/lib/geocoder/cache.rb +94 -0
- data/lib/geocoder/calculations.rb +420 -0
- data/lib/geocoder/cli.rb +121 -0
- data/lib/geocoder/configuration.rb +137 -0
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +40 -0
- data/lib/geocoder/ip_address.rb +26 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +118 -0
- data/lib/geocoder/lookups/amap.rb +63 -0
- data/lib/geocoder/lookups/baidu.rb +63 -0
- data/lib/geocoder/lookups/baidu_ip.rb +30 -0
- data/lib/geocoder/lookups/ban_data_gouv_fr.rb +130 -0
- data/lib/geocoder/lookups/base.rb +348 -0
- data/lib/geocoder/lookups/bing.rb +82 -0
- data/lib/geocoder/lookups/db_ip_com.rb +52 -0
- data/lib/geocoder/lookups/dstk.rb +22 -0
- data/lib/geocoder/lookups/esri.rb +95 -0
- data/lib/geocoder/lookups/freegeoip.rb +60 -0
- data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
- data/lib/geocoder/lookups/geocoder_us.rb +51 -0
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/geoip2.rb +45 -0
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +95 -0
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_places_search.rb +33 -0
- data/lib/geocoder/lookups/google_premier.rb +57 -0
- data/lib/geocoder/lookups/here.rb +77 -0
- data/lib/geocoder/lookups/ip2location.rb +75 -0
- data/lib/geocoder/lookups/ipapi_com.rb +82 -0
- data/lib/geocoder/lookups/ipdata_co.rb +62 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
- data/lib/geocoder/lookups/ipstack.rb +63 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/location_iq.rb +50 -0
- data/lib/geocoder/lookups/mapbox.rb +59 -0
- data/lib/geocoder/lookups/mapquest.rb +58 -0
- data/lib/geocoder/lookups/maxmind.rb +90 -0
- data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +64 -0
- data/lib/geocoder/lookups/opencagedata.rb +65 -0
- data/lib/geocoder/lookups/pelias.rb +63 -0
- data/lib/geocoder/lookups/pickpoint.rb +41 -0
- data/lib/geocoder/lookups/pointpin.rb +69 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
- data/lib/geocoder/lookups/postcodes_io.rb +31 -0
- data/lib/geocoder/lookups/smarty_streets.rb +63 -0
- data/lib/geocoder/lookups/telize.rb +75 -0
- data/lib/geocoder/lookups/tencent.rb +59 -0
- data/lib/geocoder/lookups/test.rb +44 -0
- data/lib/geocoder/lookups/yandex.rb +62 -0
- data/lib/geocoder/models/active_record.rb +51 -0
- data/lib/geocoder/models/base.rb +39 -0
- data/lib/geocoder/models/mongo_base.rb +62 -0
- data/lib/geocoder/models/mongo_mapper.rb +26 -0
- data/lib/geocoder/models/mongoid.rb +32 -0
- data/lib/geocoder/query.rb +125 -0
- data/lib/geocoder/railtie.rb +26 -0
- data/lib/geocoder/request.rb +114 -0
- data/lib/geocoder/results/amap.rb +87 -0
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
- data/lib/geocoder/results/base.rb +79 -0
- data/lib/geocoder/results/bing.rb +52 -0
- data/lib/geocoder/results/db_ip_com.rb +58 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/esri.rb +75 -0
- data/lib/geocoder/results/freegeoip.rb +40 -0
- data/lib/geocoder/results/geocoder_ca.rb +60 -0
- data/lib/geocoder/results/geocoder_us.rb +39 -0
- data/lib/geocoder/results/geocodio.rb +78 -0
- data/lib/geocoder/results/geoip2.rb +76 -0
- data/lib/geocoder/results/geoportail_lu.rb +71 -0
- data/lib/geocoder/results/google.rb +150 -0
- data/lib/geocoder/results/google_places_details.rb +39 -0
- data/lib/geocoder/results/google_places_search.rb +52 -0
- data/lib/geocoder/results/google_premier.rb +6 -0
- data/lib/geocoder/results/here.rb +79 -0
- data/lib/geocoder/results/ip2location.rb +22 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipdata_co.rb +40 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/ipstack.rb +60 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/location_iq.rb +6 -0
- data/lib/geocoder/results/mapbox.rb +57 -0
- data/lib/geocoder/results/mapquest.rb +48 -0
- data/lib/geocoder/results/maxmind.rb +130 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +44 -0
- data/lib/geocoder/results/nominatim.rb +109 -0
- data/lib/geocoder/results/opencagedata.rb +100 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pickpoint.rb +6 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/postcodes_io.rb +40 -0
- data/lib/geocoder/results/smarty_streets.rb +142 -0
- data/lib/geocoder/results/telize.rb +40 -0
- data/lib/geocoder/results/tencent.rb +72 -0
- data/lib/geocoder/results/test.rb +33 -0
- data/lib/geocoder/results/yandex.rb +134 -0
- data/lib/geocoder/sql.rb +110 -0
- data/lib/geocoder/stores/active_record.rb +328 -0
- data/lib/geocoder/stores/base.rb +115 -0
- data/lib/geocoder/stores/mongo_base.rb +58 -0
- data/lib/geocoder/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder/stores/mongoid.rb +13 -0
- data/lib/geocoder/version.rb +3 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/maxmind_database.rb +109 -0
- data/lib/tasks/geocoder.rake +54 -0
- data/lib/tasks/maxmind.rake +73 -0
- metadata +186 -0
@@ -0,0 +1,420 @@
|
|
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
|
+
# Conversion factor: multiply by kilometers to get miles.
|
15
|
+
#
|
16
|
+
KM_IN_MI = 0.621371192
|
17
|
+
|
18
|
+
##
|
19
|
+
# Conversion factor: multiply by nautical miles to get miles.
|
20
|
+
#
|
21
|
+
KM_IN_NM = 0.539957
|
22
|
+
|
23
|
+
##
|
24
|
+
# Conversion factor: multiply by radians to get degrees.
|
25
|
+
#
|
26
|
+
DEGREES_PER_RADIAN = 57.2957795
|
27
|
+
|
28
|
+
##
|
29
|
+
# Radius of the Earth, in kilometers.
|
30
|
+
# Value taken from: http://en.wikipedia.org/wiki/Earth_radius
|
31
|
+
#
|
32
|
+
EARTH_RADII = {km: 6371.0}
|
33
|
+
EARTH_RADII[:mi] = EARTH_RADII[:km] * KM_IN_MI
|
34
|
+
EARTH_RADII[:nm] = EARTH_RADII[:km] * KM_IN_NM
|
35
|
+
|
36
|
+
EARTH_RADIUS = EARTH_RADII[:km] # TODO: deprecate this constant (use `EARTH_RADII[:km]`)
|
37
|
+
|
38
|
+
# Not a number constant
|
39
|
+
NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0
|
40
|
+
|
41
|
+
##
|
42
|
+
# Returns true if all given arguments are valid latitude/longitude values.
|
43
|
+
#
|
44
|
+
def coordinates_present?(*args)
|
45
|
+
args.each do |a|
|
46
|
+
# note that Float::NAN != Float::NAN
|
47
|
+
# still, this could probably be improved:
|
48
|
+
return false if (!a.is_a?(Numeric) or a.to_s == "NaN")
|
49
|
+
end
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Distance spanned by one degree of latitude in the given units.
|
55
|
+
#
|
56
|
+
def latitude_degree_distance(units = nil)
|
57
|
+
2 * Math::PI * earth_radius(units) / 360
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Distance spanned by one degree of longitude at the given latitude.
|
62
|
+
# This ranges from around 69 miles at the equator to zero at the poles.
|
63
|
+
#
|
64
|
+
def longitude_degree_distance(latitude, units = nil)
|
65
|
+
latitude_degree_distance(units) * Math.cos(to_radians(latitude))
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Distance between two points on Earth (Haversine formula).
|
70
|
+
# Takes two points and an options hash.
|
71
|
+
# The points are given in the same way that points are given to all
|
72
|
+
# Geocoder methods that accept points as arguments. They can be:
|
73
|
+
#
|
74
|
+
# * an array of coordinates ([lat,lon])
|
75
|
+
# * a geocodable address (string)
|
76
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
77
|
+
# which returns a [lat,lon] array
|
78
|
+
#
|
79
|
+
# The options hash supports:
|
80
|
+
#
|
81
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
|
82
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
83
|
+
#
|
84
|
+
def distance_between(point1, point2, options = {})
|
85
|
+
# convert to coordinate arrays
|
86
|
+
point1 = extract_coordinates(point1)
|
87
|
+
point2 = extract_coordinates(point2)
|
88
|
+
|
89
|
+
# convert degrees to radians
|
90
|
+
point1 = to_radians(point1)
|
91
|
+
point2 = to_radians(point2)
|
92
|
+
|
93
|
+
# compute deltas
|
94
|
+
dlat = point2[0] - point1[0]
|
95
|
+
dlon = point2[1] - point1[1]
|
96
|
+
|
97
|
+
a = (Math.sin(dlat / 2))**2 + Math.cos(point1[0]) *
|
98
|
+
(Math.sin(dlon / 2))**2 * Math.cos(point2[0])
|
99
|
+
c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
|
100
|
+
c * earth_radius(options[:units])
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Bearing between two points on Earth.
|
105
|
+
# Returns a number of degrees from due north (clockwise).
|
106
|
+
#
|
107
|
+
# See Geocoder::Calculations.distance_between for
|
108
|
+
# ways of specifying the points. Also accepts an options hash:
|
109
|
+
#
|
110
|
+
# * <tt>:method</tt> - <tt>:linear</tt> or <tt>:spherical</tt>;
|
111
|
+
# the spherical method is "correct" in that it returns the shortest path
|
112
|
+
# (one along a great circle) but the linear method is less confusing
|
113
|
+
# (returns due east or west when given two points with the same latitude).
|
114
|
+
# Use Geocoder.configure(:distances => ...) to configure calculation method.
|
115
|
+
#
|
116
|
+
# Based on: http://www.movable-type.co.uk/scripts/latlong.html
|
117
|
+
#
|
118
|
+
def bearing_between(point1, point2, options = {})
|
119
|
+
|
120
|
+
# set default options
|
121
|
+
options[:method] ||= Geocoder.config.distances
|
122
|
+
options[:method] = :linear unless options[:method] == :spherical
|
123
|
+
|
124
|
+
# convert to coordinate arrays
|
125
|
+
point1 = extract_coordinates(point1)
|
126
|
+
point2 = extract_coordinates(point2)
|
127
|
+
|
128
|
+
# convert degrees to radians
|
129
|
+
point1 = to_radians(point1)
|
130
|
+
point2 = to_radians(point2)
|
131
|
+
|
132
|
+
# compute deltas
|
133
|
+
dlat = point2[0] - point1[0]
|
134
|
+
dlon = point2[1] - point1[1]
|
135
|
+
|
136
|
+
case options[:method]
|
137
|
+
when :linear
|
138
|
+
y = dlon
|
139
|
+
x = dlat
|
140
|
+
|
141
|
+
when :spherical
|
142
|
+
y = Math.sin(dlon) * Math.cos(point2[0])
|
143
|
+
x = Math.cos(point1[0]) * Math.sin(point2[0]) -
|
144
|
+
Math.sin(point1[0]) * Math.cos(point2[0]) * Math.cos(dlon)
|
145
|
+
end
|
146
|
+
|
147
|
+
bearing = Math.atan2(x,y)
|
148
|
+
# Answer is in radians counterclockwise from due east.
|
149
|
+
# Convert to degrees clockwise from due north:
|
150
|
+
(90 - to_degrees(bearing) + 360) % 360
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Translate a bearing (float) into a compass direction (string, eg "North").
|
155
|
+
#
|
156
|
+
def compass_point(bearing, points = COMPASS_POINTS)
|
157
|
+
seg_size = 360.0 / points.size
|
158
|
+
points[((bearing + (seg_size / 2)) % 360) / seg_size]
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Compute the geographic center (aka geographic midpoint, center of
|
163
|
+
# gravity) for an array of geocoded objects and/or [lat,lon] arrays
|
164
|
+
# (can be mixed). Any objects missing coordinates are ignored. Follows
|
165
|
+
# the procedure documented at http://www.geomidpoint.com/calculation.html.
|
166
|
+
#
|
167
|
+
def geographic_center(points)
|
168
|
+
|
169
|
+
# convert objects to [lat,lon] arrays and convert degrees to radians
|
170
|
+
coords = points.map{ |p| to_radians(extract_coordinates(p)) }
|
171
|
+
|
172
|
+
# convert to Cartesian coordinates
|
173
|
+
x = []; y = []; z = []
|
174
|
+
coords.each do |p|
|
175
|
+
x << Math.cos(p[0]) * Math.cos(p[1])
|
176
|
+
y << Math.cos(p[0]) * Math.sin(p[1])
|
177
|
+
z << Math.sin(p[0])
|
178
|
+
end
|
179
|
+
|
180
|
+
# compute average coordinate values
|
181
|
+
xa, ya, za = [x,y,z].map do |c|
|
182
|
+
c.inject(0){ |tot,i| tot += i } / c.size.to_f
|
183
|
+
end
|
184
|
+
|
185
|
+
# convert back to latitude/longitude
|
186
|
+
lon = Math.atan2(ya, xa)
|
187
|
+
hyp = Math.sqrt(xa**2 + ya**2)
|
188
|
+
lat = Math.atan2(za, hyp)
|
189
|
+
|
190
|
+
# return answer in degrees
|
191
|
+
to_degrees [lat, lon]
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Returns coordinates of the southwest and northeast corners of a box
|
196
|
+
# with the given point at its center. The radius is the shortest distance
|
197
|
+
# from the center point to any side of the box (the length of each side
|
198
|
+
# is twice the radius).
|
199
|
+
#
|
200
|
+
# This is useful for finding corner points of a map viewport, or for
|
201
|
+
# roughly limiting the possible solutions in a geo-spatial search
|
202
|
+
# (ActiveRecord queries use it thusly).
|
203
|
+
#
|
204
|
+
# See Geocoder::Calculations.distance_between for
|
205
|
+
# ways of specifying the point. Also accepts an options hash:
|
206
|
+
#
|
207
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>.
|
208
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
209
|
+
#
|
210
|
+
def bounding_box(point, radius, options = {})
|
211
|
+
lat,lon = extract_coordinates(point)
|
212
|
+
radius = radius.to_f
|
213
|
+
[
|
214
|
+
lat - (radius / latitude_degree_distance(options[:units])),
|
215
|
+
lon - (radius / longitude_degree_distance(lat, options[:units])),
|
216
|
+
lat + (radius / latitude_degree_distance(options[:units])),
|
217
|
+
lon + (radius / longitude_degree_distance(lat, options[:units]))
|
218
|
+
]
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Random point within a circle of provided radius centered
|
223
|
+
# around the provided point
|
224
|
+
# Takes one point, one radius, and an options hash.
|
225
|
+
# The points are given in the same way that points are given to all
|
226
|
+
# Geocoder methods that accept points as arguments. They can be:
|
227
|
+
#
|
228
|
+
# * an array of coordinates ([lat,lon])
|
229
|
+
# * a geocodable address (string)
|
230
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
231
|
+
# which returns a [lat,lon] array
|
232
|
+
#
|
233
|
+
# The options hash supports:
|
234
|
+
#
|
235
|
+
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
|
236
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
237
|
+
# * <tt>:seed</tt> - The seed for the random number generator
|
238
|
+
def random_point_near(center, radius, options = {})
|
239
|
+
random = Random.new(options[:seed] || Random.new_seed)
|
240
|
+
|
241
|
+
# convert to coordinate arrays
|
242
|
+
center = extract_coordinates(center)
|
243
|
+
|
244
|
+
earth_circumference = 2 * Math::PI * earth_radius(options[:units])
|
245
|
+
max_degree_delta = 360.0 * (radius / earth_circumference)
|
246
|
+
|
247
|
+
# random bearing in radians
|
248
|
+
theta = 2 * Math::PI * random.rand
|
249
|
+
|
250
|
+
# random radius, use the square root to ensure a uniform
|
251
|
+
# distribution of points over the circle
|
252
|
+
r = Math.sqrt(random.rand) * max_degree_delta
|
253
|
+
|
254
|
+
delta_lat, delta_long = [r * Math.cos(theta), r * Math.sin(theta)]
|
255
|
+
[center[0] + delta_lat, center[1] + delta_long]
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# Given a start point, heading (in degrees), and distance, provides
|
260
|
+
# an endpoint.
|
261
|
+
# The starting point is given in the same way that points are given to all
|
262
|
+
# Geocoder methods that accept points as arguments. It can be:
|
263
|
+
#
|
264
|
+
# * an array of coordinates ([lat,lon])
|
265
|
+
# * a geocodable address (string)
|
266
|
+
# * a geocoded object (one which implements a +to_coordinates+ method
|
267
|
+
# which returns a [lat,lon] array
|
268
|
+
#
|
269
|
+
def endpoint(start, heading, distance, options = {})
|
270
|
+
radius = earth_radius(options[:units])
|
271
|
+
|
272
|
+
start = extract_coordinates(start)
|
273
|
+
|
274
|
+
# convert degrees to radians
|
275
|
+
start = to_radians(start)
|
276
|
+
|
277
|
+
lat = start[0]
|
278
|
+
lon = start[1]
|
279
|
+
heading = to_radians(heading)
|
280
|
+
distance = distance.to_f
|
281
|
+
|
282
|
+
end_lat = Math.asin(Math.sin(lat)*Math.cos(distance/radius) +
|
283
|
+
Math.cos(lat)*Math.sin(distance/radius)*Math.cos(heading))
|
284
|
+
|
285
|
+
end_lon = lon+Math.atan2(Math.sin(heading)*Math.sin(distance/radius)*Math.cos(lat),
|
286
|
+
Math.cos(distance/radius)-Math.sin(lat)*Math.sin(end_lat))
|
287
|
+
|
288
|
+
to_degrees [end_lat, end_lon]
|
289
|
+
end
|
290
|
+
|
291
|
+
##
|
292
|
+
# Convert degrees to radians.
|
293
|
+
# If an array (or multiple arguments) is passed,
|
294
|
+
# converts each value and returns array.
|
295
|
+
#
|
296
|
+
def to_radians(*args)
|
297
|
+
args = args.first if args.first.is_a?(Array)
|
298
|
+
if args.size == 1
|
299
|
+
args.first * (Math::PI / 180)
|
300
|
+
else
|
301
|
+
args.map{ |i| to_radians(i) }
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
##
|
306
|
+
# Convert radians to degrees.
|
307
|
+
# If an array (or multiple arguments) is passed,
|
308
|
+
# converts each value and returns array.
|
309
|
+
#
|
310
|
+
def to_degrees(*args)
|
311
|
+
args = args.first if args.first.is_a?(Array)
|
312
|
+
if args.size == 1
|
313
|
+
(args.first * 180.0) / Math::PI
|
314
|
+
else
|
315
|
+
args.map{ |i| to_degrees(i) }
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def distance_to_radians(distance, units = nil)
|
320
|
+
distance.to_f / earth_radius(units)
|
321
|
+
end
|
322
|
+
|
323
|
+
def radians_to_distance(radians, units = nil)
|
324
|
+
radians * earth_radius(units)
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# Convert miles to kilometers.
|
329
|
+
#
|
330
|
+
def to_kilometers(mi)
|
331
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_kilometers is deprecated and will be removed in Geocoder 1.5.0. Please multiply by MI_IN_KM instead.")
|
332
|
+
mi * mi_in_km
|
333
|
+
end
|
334
|
+
|
335
|
+
##
|
336
|
+
# Convert kilometers to miles.
|
337
|
+
#
|
338
|
+
def to_miles(km)
|
339
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_miles is deprecated and will be removed in Geocoder 1.5.0. Please multiply by KM_IN_MI instead.")
|
340
|
+
km * KM_IN_MI
|
341
|
+
end
|
342
|
+
|
343
|
+
##
|
344
|
+
# Convert kilometers to nautical miles.
|
345
|
+
#
|
346
|
+
def to_nautical_miles(km)
|
347
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_nautical_miles is deprecated and will be removed in Geocoder 1.5.0. Please multiply by KM_IN_NM instead.")
|
348
|
+
km * KM_IN_NM
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# Radius of the Earth in the given units (:mi or :km).
|
353
|
+
# Use Geocoder.configure(:units => ...) to configure default units.
|
354
|
+
#
|
355
|
+
def earth_radius(units = nil)
|
356
|
+
EARTH_RADII[units || Geocoder.config.units]
|
357
|
+
end
|
358
|
+
|
359
|
+
##
|
360
|
+
# Conversion factor: km to mi.
|
361
|
+
#
|
362
|
+
def km_in_mi
|
363
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.km_in_mi is deprecated and will be removed in Geocoder 1.5.0. Please use the constant KM_IN_MI instead.")
|
364
|
+
KM_IN_MI
|
365
|
+
end
|
366
|
+
|
367
|
+
##
|
368
|
+
# Conversion factor: km to nm.
|
369
|
+
#
|
370
|
+
def km_in_nm
|
371
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.km_in_nm is deprecated and will be removed in Geocoder 1.5.0. Please use the constant KM_IN_NM instead.")
|
372
|
+
KM_IN_NM
|
373
|
+
end
|
374
|
+
|
375
|
+
##
|
376
|
+
# Conversion factor: mi to km.
|
377
|
+
#
|
378
|
+
def mi_in_km
|
379
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.mi_in_km is deprecated and will be removed in Geocoder 1.5.0. Please use 1.0 / KM_IN_MI instead.")
|
380
|
+
1.0 / KM_IN_MI
|
381
|
+
end
|
382
|
+
|
383
|
+
##
|
384
|
+
# Conversion factor: nm to km.
|
385
|
+
#
|
386
|
+
def nm_in_km
|
387
|
+
Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.nm_in_km is deprecated and will be removed in Geocoder 1.5.0. Please use 1.0 / KM_IN_NM instead.")
|
388
|
+
1.0 / KM_IN_NM
|
389
|
+
end
|
390
|
+
|
391
|
+
##
|
392
|
+
# Takes an object which is a [lat,lon] array, a geocodable string,
|
393
|
+
# or an object that implements +to_coordinates+ and returns a
|
394
|
+
# [lat,lon] array. Note that if a string is passed this may be a slow-
|
395
|
+
# running method and may return nil.
|
396
|
+
#
|
397
|
+
def extract_coordinates(point)
|
398
|
+
case point
|
399
|
+
when Array
|
400
|
+
if point.size == 2
|
401
|
+
lat, lon = point
|
402
|
+
if !lat.nil? && lat.respond_to?(:to_f) and
|
403
|
+
!lon.nil? && lon.respond_to?(:to_f)
|
404
|
+
then
|
405
|
+
return [ lat.to_f, lon.to_f ]
|
406
|
+
end
|
407
|
+
end
|
408
|
+
when String
|
409
|
+
point = Geocoder.coordinates(point) and return point
|
410
|
+
else
|
411
|
+
if point.respond_to?(:to_coordinates)
|
412
|
+
if Array === array = point.to_coordinates
|
413
|
+
return extract_coordinates(array)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
[ NAN, NAN ]
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
data/lib/geocoder/cli.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'geocoder'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
class Cli
|
6
|
+
|
7
|
+
def self.run(args, out = STDOUT)
|
8
|
+
show_url = false
|
9
|
+
show_json = false
|
10
|
+
|
11
|
+
# remove arguments that are probably coordinates so they are not
|
12
|
+
# processed as arguments (eg: -31.96047031,115.84274631)
|
13
|
+
coords = args.select{ |i| i.match(/^-\d/) }
|
14
|
+
args -= coords
|
15
|
+
|
16
|
+
OptionParser.new{ |opts|
|
17
|
+
opts.banner = "Usage:\n geocode [options] <location>"
|
18
|
+
opts.separator "\nOptions: "
|
19
|
+
|
20
|
+
opts.on("-k <key>", "--key <key>",
|
21
|
+
"Key for geocoding API (usually optional). Enclose multi-part keys in quotes and separate parts by spaces") do |key|
|
22
|
+
if (key_parts = key.split(/\s+/)).size > 1
|
23
|
+
Geocoder.configure(:api_key => key_parts)
|
24
|
+
else
|
25
|
+
Geocoder.configure(:api_key => key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-l <language>", "--language <language>",
|
30
|
+
"Language of output (see API docs for valid choices)") do |language|
|
31
|
+
Geocoder.configure(:language => language)
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-p <proxy>", "--proxy <proxy>",
|
35
|
+
"HTTP proxy server to use (user:pass@host:port)") do |proxy|
|
36
|
+
Geocoder.configure(:http_proxy => proxy)
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-s <service>", Geocoder::Lookup.all_services_except_test, "--service <service>",
|
40
|
+
"Geocoding service: #{Geocoder::Lookup.all_services_except_test * ', '}") do |service|
|
41
|
+
Geocoder.configure(:lookup => service.to_sym)
|
42
|
+
Geocoder.configure(:ip_lookup => service.to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-t <seconds>", "--timeout <seconds>",
|
46
|
+
"Maximum number of seconds to wait for API response") do |timeout|
|
47
|
+
Geocoder.configure(:timeout => timeout.to_i)
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on("-j", "--json", "Print API's raw JSON response") do
|
51
|
+
show_json = true
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on("-u", "--url", "Print URL for API query instead of result") do
|
55
|
+
show_url = true
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on_tail("-v", "--version", "Print version number") do
|
59
|
+
require "geocoder/version"
|
60
|
+
out << "Geocoder #{Geocoder::VERSION}\n"
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on_tail("-h", "--help", "Print this help") do
|
65
|
+
out << "Look up geographic information about a location.\n\n"
|
66
|
+
out << opts
|
67
|
+
out << "\nCreated and maintained by Alex Reisner, available under the MIT License.\n"
|
68
|
+
out << "Report bugs and contribute at http://github.com/alexreisner/geocoder\n"
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
}.parse!(args)
|
72
|
+
|
73
|
+
# concatenate args with coords that might have been removed
|
74
|
+
# before option processing
|
75
|
+
query = (args + coords).join(" ")
|
76
|
+
|
77
|
+
if query == ""
|
78
|
+
out << "Please specify a location (run `geocode -h` for more info).\n"
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
|
82
|
+
if show_url and show_json
|
83
|
+
out << "You can only specify one of -j and -u.\n"
|
84
|
+
exit 2
|
85
|
+
end
|
86
|
+
|
87
|
+
if show_url
|
88
|
+
q = Geocoder::Query.new(query)
|
89
|
+
out << q.url + "\n"
|
90
|
+
exit 0
|
91
|
+
end
|
92
|
+
|
93
|
+
if show_json
|
94
|
+
q = Geocoder::Query.new(query)
|
95
|
+
out << q.lookup.send(:fetch_raw_data, q) + "\n"
|
96
|
+
exit 0
|
97
|
+
end
|
98
|
+
|
99
|
+
if (result = Geocoder.search(query).first)
|
100
|
+
nominatim = Geocoder::Lookup.get(:nominatim)
|
101
|
+
lines = [
|
102
|
+
["Latitude", result.latitude],
|
103
|
+
["Longitude", result.longitude],
|
104
|
+
["Full address", result.address],
|
105
|
+
["City", result.city],
|
106
|
+
["State/province", result.state],
|
107
|
+
["Postal code", result.postal_code],
|
108
|
+
["Country", result.country],
|
109
|
+
["Map", nominatim.map_link_url(result.coordinates)],
|
110
|
+
]
|
111
|
+
lines.each do |line|
|
112
|
+
out << (line[0] + ": ").ljust(18) + line[1].to_s + "\n"
|
113
|
+
end
|
114
|
+
exit 0
|
115
|
+
else
|
116
|
+
out << "Location '#{query}' not found.\n"
|
117
|
+
exit 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|