geocoder 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +8 -0
- data/lib/geocoder/lookups/base.rb +12 -11
- data/lib/geocoder/results/yandex.rb +11 -1
- data/lib/geocoder/stores/active_record.rb +54 -22
- data/lib/geocoder/stores/mongo_base.rb +5 -4
- data/lib/geocoder/version.rb +1 -1
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
Per-release changes to Geocoder.
|
4
4
|
|
5
|
+
== 1.1.1 (2012 Feb 16)
|
6
|
+
|
7
|
+
* Add distance_from_sql class method to geocoded class (thanks github.com/dwilkie).
|
8
|
+
* Add OverQueryLimitError and raise when relevant for Google lookup.
|
9
|
+
* Fix: don't cache API data if response indicates an error.
|
10
|
+
* Fix: within_bounding_box now uses correct lat/lon DB columns (thanks github.com/kongo).
|
11
|
+
* Fix: error accessing city in some cases with Yandex result (thanks github.com/kor6n and sld).
|
12
|
+
|
5
13
|
== 1.1.0 (2011 Dec 3)
|
6
14
|
|
7
15
|
* A block passed to geocoded_by is now always executed, even if the geocoding service returns no results. This means you need to make sure you have results before trying to assign data to your object.
|
@@ -120,14 +120,14 @@ module Geocoder
|
|
120
120
|
# Parses a raw search result (returns hash or array).
|
121
121
|
#
|
122
122
|
def parse_raw_data(raw_data)
|
123
|
-
|
124
|
-
ActiveSupport::JSON
|
125
|
-
|
126
|
-
|
123
|
+
begin
|
124
|
+
if defined?(ActiveSupport::JSON)
|
125
|
+
ActiveSupport::JSON.decode(raw_data)
|
126
|
+
else
|
127
127
|
JSON.parse(raw_data)
|
128
|
-
rescue
|
129
|
-
warn "Geocoding API's response was not valid JSON."
|
130
128
|
end
|
129
|
+
rescue
|
130
|
+
warn "Geocoding API's response was not valid JSON."
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
@@ -146,15 +146,16 @@ module Geocoder
|
|
146
146
|
timeout(Geocoder::Configuration.timeout) do
|
147
147
|
url = query_url(query, reverse)
|
148
148
|
uri = URI.parse(url)
|
149
|
-
unless cache and
|
149
|
+
unless cache and body = cache[url]
|
150
150
|
client = http_client.new(uri.host, uri.port)
|
151
151
|
client.use_ssl = true if Geocoder::Configuration.use_https
|
152
|
-
response = client.get(uri.request_uri)
|
153
|
-
|
154
|
-
|
152
|
+
response = client.get(uri.request_uri)
|
153
|
+
body = response.body
|
154
|
+
if cache and (200..399).include?(response.code.to_i)
|
155
|
+
cache[url] = body
|
155
156
|
end
|
156
157
|
end
|
157
|
-
|
158
|
+
body
|
158
159
|
end
|
159
160
|
end
|
160
161
|
|
@@ -14,8 +14,10 @@ module Geocoder::Result
|
|
14
14
|
def city
|
15
15
|
if state.empty?
|
16
16
|
address_details['Locality']['LocalityName']
|
17
|
-
|
17
|
+
elsif sub_state.empty?
|
18
18
|
address_details['AdministrativeArea']['Locality']['LocalityName']
|
19
|
+
else
|
20
|
+
address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName']
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -35,6 +37,14 @@ module Geocoder::Result
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
40
|
+
def sub_state
|
41
|
+
if !state.empty? and address_details['AdministrativeArea']['SubAdministrativeArea']
|
42
|
+
address_details['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName']
|
43
|
+
else
|
44
|
+
""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
38
48
|
def state_code
|
39
49
|
""
|
40
50
|
end
|
@@ -50,11 +50,11 @@ module Geocoder::Store
|
|
50
50
|
scope :within_bounding_box, lambda{ |bounds|
|
51
51
|
sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
|
52
52
|
return where(:id => false) unless sw_lat && sw_lng && ne_lat && ne_lng
|
53
|
-
spans = "latitude BETWEEN #{sw_lat} AND #{ne_lat} AND "
|
53
|
+
spans = "#{geocoder_options[:latitude]} BETWEEN #{sw_lat} AND #{ne_lat} AND "
|
54
54
|
spans << if sw_lng > ne_lng # Handle a box that spans 180
|
55
|
-
"longitude BETWEEN #{sw_lng} AND 180 OR longitude BETWEEN -180 AND #{ne_lng}"
|
55
|
+
"#{geocoder_options[:longitude]} BETWEEN #{sw_lng} AND 180 OR #{geocoder_options[:longitude]} BETWEEN -180 AND #{ne_lng}"
|
56
56
|
else
|
57
|
-
"longitude BETWEEN #{sw_lng} AND #{ne_lng}"
|
57
|
+
"#{geocoder_options[:longitude]} BETWEEN #{sw_lng} AND #{ne_lng}"
|
58
58
|
end
|
59
59
|
{ :conditions => spans }
|
60
60
|
}
|
@@ -66,6 +66,11 @@ module Geocoder::Store
|
|
66
66
|
#
|
67
67
|
module ClassMethods
|
68
68
|
|
69
|
+
def distance_from_sql(location, *args)
|
70
|
+
latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
|
71
|
+
distance_from_sql_options(latitude, longitude, *args) if latitude and longitude
|
72
|
+
end
|
73
|
+
|
69
74
|
private # ----------------------------------------------------------------
|
70
75
|
|
71
76
|
##
|
@@ -92,14 +97,19 @@ module Geocoder::Store
|
|
92
97
|
end
|
93
98
|
end
|
94
99
|
|
100
|
+
def distance_from_sql_options(latitude, longitude, options = {})
|
101
|
+
if connection.adapter_name.match /sqlite/i
|
102
|
+
approx_distance_from_sql(latitude, longitude, options)
|
103
|
+
else
|
104
|
+
full_distance_from_sql(latitude, longitude, options)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
95
108
|
##
|
96
109
|
# Scope options hash for use with a database that supports POWER(),
|
97
110
|
# SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
|
98
111
|
# ATAN2(), DEGREES(), and RADIANS().
|
99
112
|
#
|
100
|
-
# Distance calculations based on the excellent tutorial at:
|
101
|
-
# http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
|
102
|
-
#
|
103
113
|
# Bearing calculation based on:
|
104
114
|
# http://www.beginningspatial.com/calculating_bearing_one_point_another
|
105
115
|
#
|
@@ -128,11 +138,8 @@ module Geocoder::Store
|
|
128
138
|
")) + 360 " +
|
129
139
|
"AS decimal) % 360"
|
130
140
|
end
|
131
|
-
|
132
|
-
distance =
|
133
|
-
"POWER(SIN((#{latitude} - #{lat_attr}) * PI() / 180 / 2), 2) + " +
|
134
|
-
"COS(#{latitude} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " +
|
135
|
-
"POWER(SIN((#{longitude} - #{lon_attr}) * PI() / 180 / 2), 2) ))"
|
141
|
+
|
142
|
+
distance = full_distance_from_sql(latitude, longitude, options)
|
136
143
|
conditions = ["#{distance} <= ?", radius]
|
137
144
|
default_near_scope_options(latitude, longitude, radius, options).merge(
|
138
145
|
:select => "#{options[:select] || "#{table_name}.*"}, " +
|
@@ -142,6 +149,36 @@ module Geocoder::Store
|
|
142
149
|
)
|
143
150
|
end
|
144
151
|
|
152
|
+
|
153
|
+
# Distance calculations based on the excellent tutorial at:
|
154
|
+
# http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
|
155
|
+
|
156
|
+
def full_distance_from_sql(latitude, longitude, options)
|
157
|
+
lat_attr = geocoder_options[:latitude]
|
158
|
+
lon_attr = geocoder_options[:longitude]
|
159
|
+
|
160
|
+
earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
|
161
|
+
|
162
|
+
"#{earth} * 2 * ASIN(SQRT(" +
|
163
|
+
"POWER(SIN((#{latitude} - #{table_name}.#{lat_attr}) * PI() / 180 / 2), 2) + " +
|
164
|
+
"COS(#{latitude} * PI() / 180) * COS(#{table_name}.#{lat_attr} * PI() / 180) * " +
|
165
|
+
"POWER(SIN((#{longitude} - #{table_name}.#{lon_attr}) * PI() / 180 / 2), 2) ))"
|
166
|
+
end
|
167
|
+
|
168
|
+
def approx_distance_from_sql(latitude, longitude, options)
|
169
|
+
lat_attr = geocoder_options[:latitude]
|
170
|
+
lon_attr = geocoder_options[:longitude]
|
171
|
+
|
172
|
+
dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
|
173
|
+
dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
|
174
|
+
|
175
|
+
# sin of 45 degrees = average x or y component of vector
|
176
|
+
factor = Math.sin(Math::PI / 4)
|
177
|
+
|
178
|
+
"(#{dy} * ABS(#{table_name}.#{lat_attr} - #{latitude}) * #{factor}) + " +
|
179
|
+
"(#{dx} * ABS(#{table_name}.#{lon_attr} - #{longitude}) * #{factor})"
|
180
|
+
end
|
181
|
+
|
145
182
|
##
|
146
183
|
# Scope options hash for use with a database without trigonometric
|
147
184
|
# functions, like SQLite. Approach is to find objects within a square
|
@@ -166,14 +203,8 @@ module Geocoder::Store
|
|
166
203
|
bearing = false
|
167
204
|
end
|
168
205
|
|
169
|
-
|
170
|
-
dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
|
171
|
-
|
172
|
-
# sin of 45 degrees = average x or y component of vector
|
173
|
-
factor = Math.sin(Math::PI / 4)
|
206
|
+
distance = approx_distance_from_sql(latitude, longitude, options)
|
174
207
|
|
175
|
-
distance = "(#{dy} * ABS(#{lat_attr} - #{latitude}) * #{factor}) + " +
|
176
|
-
"(#{dx} * ABS(#{lon_attr} - #{longitude}) * #{factor})"
|
177
208
|
b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
|
178
209
|
conditions = [
|
179
210
|
"#{lat_attr} BETWEEN ? AND ? AND #{lon_attr} BETWEEN ? AND ?"] +
|
@@ -235,11 +266,12 @@ module Geocoder::Store
|
|
235
266
|
#
|
236
267
|
def reverse_geocode
|
237
268
|
do_lookup(true) do |o,rs|
|
238
|
-
r = rs.first
|
239
|
-
|
240
|
-
|
269
|
+
if r = rs.first
|
270
|
+
unless r.address.nil?
|
271
|
+
o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
|
272
|
+
end
|
273
|
+
r.address
|
241
274
|
end
|
242
|
-
r.address
|
243
275
|
end
|
244
276
|
end
|
245
277
|
|
@@ -71,11 +71,12 @@ module Geocoder::Store
|
|
71
71
|
#
|
72
72
|
def reverse_geocode
|
73
73
|
do_lookup(true) do |o,rs|
|
74
|
-
r = rs.first
|
75
|
-
|
76
|
-
|
74
|
+
if r = rs.first
|
75
|
+
unless r.address.nil?
|
76
|
+
o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
|
77
|
+
end
|
78
|
+
r.address
|
77
79
|
end
|
78
|
-
r.address
|
79
80
|
end
|
80
81
|
end
|
81
82
|
end
|
data/lib/geocoder/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: geocoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.1.
|
5
|
+
version: 1.1.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Alex Reisner
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2012-02-16 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|