geocoder 1.1.1 → 1.1.2

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.

@@ -33,7 +33,11 @@ module Geocoder
33
33
  else
34
34
  reverse = false
35
35
  end
36
- results(query, reverse).map{ |r| result_class.new(r) }
36
+ results(query, reverse).map{ |r|
37
+ result = result_class.new(r)
38
+ result.cache_hit = @cache_hit if cache
39
+ result
40
+ }
37
41
  end
38
42
 
39
43
  ##
@@ -95,7 +99,7 @@ module Geocoder
95
99
  # Return false if exception not raised.
96
100
  #
97
101
  def raise_error(error, message = nil)
98
- if Geocoder::Configuration.always_raise.include?(error.class)
102
+ if Geocoder::Configuration.always_raise.include?( error.is_a?(Class) ? error : error.class )
99
103
  raise error, message
100
104
  else
101
105
  false
@@ -106,29 +110,25 @@ module Geocoder
106
110
  # Returns a parsed search result (Ruby hash).
107
111
  #
108
112
  def fetch_data(query, reverse = false)
109
- begin
110
- parse_raw_data fetch_raw_data(query, reverse)
111
- rescue SocketError => err
112
- raise_error(err) or warn "Geocoding API connection cannot be established."
113
- rescue TimeoutError => err
114
- raise_error(err) or warn "Geocoding API not responding fast enough " +
115
- "(see Geocoder::Configuration.timeout to set limit)."
116
- end
113
+ parse_raw_data fetch_raw_data(query, reverse)
114
+ rescue SocketError => err
115
+ raise_error(err) or warn "Geocoding API connection cannot be established."
116
+ rescue TimeoutError => err
117
+ raise_error(err) or warn "Geocoding API not responding fast enough " +
118
+ "(see Geocoder::Configuration.timeout to set limit)."
117
119
  end
118
120
 
119
121
  ##
120
122
  # Parses a raw search result (returns hash or array).
121
123
  #
122
124
  def parse_raw_data(raw_data)
123
- begin
124
- if defined?(ActiveSupport::JSON)
125
- ActiveSupport::JSON.decode(raw_data)
126
- else
127
- JSON.parse(raw_data)
128
- end
129
- rescue
130
- warn "Geocoding API's response was not valid JSON."
125
+ if defined?(ActiveSupport::JSON)
126
+ ActiveSupport::JSON.decode(raw_data)
127
+ else
128
+ JSON.parse(raw_data)
131
129
  end
130
+ rescue
131
+ warn "Geocoding API's response was not valid JSON."
132
132
  end
133
133
 
134
134
  ##
@@ -146,14 +146,17 @@ 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 body = cache[url]
149
+ if cache and body = cache[url]
150
+ @cache_hit = true
151
+ else
150
152
  client = http_client.new(uri.host, uri.port)
151
153
  client.use_ssl = true if Geocoder::Configuration.use_https
152
- response = client.get(uri.request_uri)
154
+ response = client.get(uri.request_uri, Geocoder::Configuration.http_headers)
153
155
  body = response.body
154
156
  if cache and (200..399).include?(response.code.to_i)
155
157
  cache[url] = body
156
158
  end
159
+ @cache_hit = false
157
160
  end
158
161
  body
159
162
  end
@@ -6,6 +6,10 @@ module Geocoder::Lookup
6
6
 
7
7
  private # ---------------------------------------------------------------
8
8
 
9
+ def parse_raw_data(raw_data)
10
+ raw_data.match(/^<html><title>404/) ? nil : super(raw_data)
11
+ end
12
+
9
13
  def results(query, reverse = false)
10
14
  # don't look up a loopback address, just return the stored result
11
15
  return [reserved_result(query)] if loopback_address?(query)
@@ -33,6 +33,7 @@ module Geocoder::Lookup
33
33
  params[:reverse] = 1
34
34
  else
35
35
  params[:locate] = query
36
+ params[:showpostal] = 1
36
37
  end
37
38
  "http://geocoder.ca/?" + hash_to_query(params)
38
39
  end
@@ -14,7 +14,9 @@ module Geocoder
14
14
  :user_address => address_attr,
15
15
  :latitude => options[:latitude] || :latitude,
16
16
  :longitude => options[:longitude] || :longitude,
17
- :geocode_block => block
17
+ :geocode_block => block,
18
+ :units => options[:units],
19
+ :method => options[:method]
18
20
  )
19
21
  end
20
22
 
@@ -27,7 +29,9 @@ module Geocoder
27
29
  :fetched_address => options[:address] || :address,
28
30
  :latitude => latitude_attr,
29
31
  :longitude => longitude_attr,
30
- :reverse_block => block
32
+ :reverse_block => block,
33
+ :units => options[:units],
34
+ :method => options[:method]
31
35
  )
32
36
  end
33
37
 
@@ -39,3 +43,4 @@ module Geocoder
39
43
  end
40
44
  end
41
45
  end
46
+
@@ -12,7 +12,9 @@ module Geocoder
12
12
  if defined?(@geocoder_options)
13
13
  @geocoder_options
14
14
  elsif superclass.respond_to?(:geocoder_options)
15
- superclass.geocoder_options
15
+ superclass.geocoder_options || { }
16
+ else
17
+ { }
16
18
  end
17
19
  end
18
20
 
@@ -24,7 +26,6 @@ module Geocoder
24
26
  fail
25
27
  end
26
28
 
27
-
28
29
  private # ----------------------------------------------------------------
29
30
 
30
31
  def geocoder_init(options)
@@ -38,3 +39,4 @@ module Geocoder
38
39
  end
39
40
  end
40
41
  end
42
+
@@ -16,7 +16,10 @@ module Geocoder
16
16
  :geocode => true,
17
17
  :user_address => address_attr,
18
18
  :coordinates => options[:coordinates] || :coordinates,
19
- :geocode_block => block
19
+ :geocode_block => block,
20
+ :units => options[:units],
21
+ :method => options[:method],
22
+ :skip_index => options[:skip_index] || false
20
23
  )
21
24
  end
22
25
 
@@ -28,7 +31,10 @@ module Geocoder
28
31
  :reverse_geocode => true,
29
32
  :fetched_address => options[:address] || :address,
30
33
  :coordinates => coordinates_attr,
31
- :reverse_block => block
34
+ :reverse_block => block,
35
+ :units => options[:units],
36
+ :method => options[:method],
37
+ :skip_index => options[:skip_index] || false
32
38
  )
33
39
  end
34
40
 
@@ -36,7 +42,7 @@ module Geocoder
36
42
 
37
43
  def geocoder_init(options)
38
44
  unless geocoder_initialized?
39
- @geocoder_options = {}
45
+ @geocoder_options = { }
40
46
  require "geocoder/stores/#{geocoder_file_name}"
41
47
  include Geocoder::Store.const_get(geocoder_module_name)
42
48
  end
@@ -44,12 +50,11 @@ module Geocoder
44
50
  end
45
51
 
46
52
  def geocoder_initialized?
47
- begin
48
- included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
49
- rescue NameError
50
- false
51
- end
53
+ included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
54
+ rescue NameError
55
+ false
52
56
  end
53
57
  end
54
58
  end
55
59
  end
60
+
@@ -16,8 +16,10 @@ module Geocoder
16
16
 
17
17
  def geocoder_init(options)
18
18
  super(options)
19
- ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
20
- :min => -180, :max => 180 # create 2d index
19
+ if options[:skip_index] == false
20
+ ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
21
+ :min => -180, :max => 180 # create 2d index
22
+ end
21
23
  end
22
24
  end
23
25
  end
@@ -16,8 +16,10 @@ module Geocoder
16
16
 
17
17
  def geocoder_init(options)
18
18
  super(options)
19
- index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
20
- :min => -180, :max => 180 # create 2d index
19
+ if options[:skip_index] == false
20
+ index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
21
+ :min => -180, :max => 180 # create 2d index
22
+ end
21
23
  end
22
24
  end
23
25
  end
@@ -1,13 +1,14 @@
1
1
  module Geocoder
2
2
  module Result
3
3
  class Base
4
- attr_accessor :data
4
+ attr_accessor :data, :cache_hit
5
5
 
6
6
  ##
7
7
  # Takes a hash of result data from a parsed Google result document.
8
8
  #
9
9
  def initialize(data)
10
10
  @data = data
11
+ @cache_hit = nil
11
12
  end
12
13
 
13
14
  ##
@@ -20,7 +20,7 @@ module Geocoder::Result
20
20
  end
21
21
 
22
22
  def village
23
- @data['address']['villiage']
23
+ @data['address']['village']
24
24
  end
25
25
 
26
26
  def town
@@ -33,10 +33,10 @@ module Geocoder::Store
33
33
  #
34
34
  scope :near, lambda{ |location, *args|
35
35
  latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
36
- if latitude and longitude
36
+ if latitude and longitude and ![latitude, longitude].include?(Geocoder::Calculations::NAN)
37
37
  near_scope_options(latitude, longitude, *args)
38
38
  else
39
- where(:id => false) # no results if no lat/lon given
39
+ where(false_condition) # no results if no lat/lon given
40
40
  end
41
41
  }
42
42
 
@@ -49,7 +49,7 @@ module Geocoder::Store
49
49
  #
50
50
  scope :within_bounding_box, lambda{ |bounds|
51
51
  sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
52
- return where(:id => false) unless sw_lat && sw_lng && ne_lat && ne_lng
52
+ return where(false_condition) unless sw_lat && sw_lng && ne_lat && ne_lng
53
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
55
  "#{geocoder_options[:longitude]} BETWEEN #{sw_lng} AND 180 OR #{geocoder_options[:longitude]} BETWEEN -180 AND #{ne_lng}"
@@ -75,22 +75,24 @@ module Geocoder::Store
75
75
 
76
76
  ##
77
77
  # Get options hash suitable for passing to ActiveRecord.find to get
78
- # records within a radius (in miles) of the given point.
78
+ # records within a radius (in kilometers) of the given point.
79
79
  # Options hash may include:
80
80
  #
81
- # * +:units+ - <tt>:mi</tt> (default) or <tt>:km</tt>; to be used
81
+ # * +:units+ - <tt>:mi</tt> or <tt>:km</tt>; to be used.
82
82
  # for interpreting radius as well as the +distance+ attribute which
83
- # is added to each found nearby object
84
- # * +:bearing+ - <tt>:linear</tt> (default) or <tt>:spherical</tt>;
83
+ # is added to each found nearby object.
84
+ # See Geocoder::Configuration to know how configure default units.
85
+ # * +:bearing+ - <tt>:linear</tt> or <tt>:spherical</tt>.
85
86
  # the method to be used for calculating the bearing (direction)
86
87
  # between the given point and each found nearby point;
87
- # set to false for no bearing calculation
88
+ # set to false for no bearing calculation.
89
+ # See Geocoder::Configuration to know how configure default method.
88
90
  # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
89
91
  # * +:order+ - column(s) for ORDER BY SQL clause; default is distance
90
92
  # * +:exclude+ - an object to exclude (used by the +nearbys+ method)
91
93
  #
92
94
  def near_scope_options(latitude, longitude, radius = 20, options = {})
93
- if connection.adapter_name.match /sqlite/i
95
+ if using_sqlite?
94
96
  approx_near_scope_options(latitude, longitude, radius, options)
95
97
  else
96
98
  full_near_scope_options(latitude, longitude, radius, options)
@@ -98,7 +100,7 @@ module Geocoder::Store
98
100
  end
99
101
 
100
102
  def distance_from_sql_options(latitude, longitude, options = {})
101
- if connection.adapter_name.match /sqlite/i
103
+ if using_sqlite?
102
104
  approx_distance_from_sql(latitude, longitude, options)
103
105
  else
104
106
  full_distance_from_sql(latitude, longitude, options)
@@ -116,33 +118,35 @@ module Geocoder::Store
116
118
  def full_near_scope_options(latitude, longitude, radius, options)
117
119
  lat_attr = geocoder_options[:latitude]
118
120
  lon_attr = geocoder_options[:longitude]
119
- options[:bearing] = :linear unless options.include?(:bearing)
121
+ options[:bearing] ||= (options[:method] ||
122
+ geocoder_options[:method] ||
123
+ Geocoder::Configuration.distances)
120
124
  bearing = case options[:bearing]
121
125
  when :linear
122
126
  "CAST(" +
123
127
  "DEGREES(ATAN2( " +
124
- "RADIANS(#{lon_attr} - #{longitude}), " +
125
- "RADIANS(#{lat_attr} - #{latitude})" +
128
+ "RADIANS(#{full_column_name(lon_attr)} - #{longitude}), " +
129
+ "RADIANS(#{full_column_name(lat_attr)} - #{latitude})" +
126
130
  ")) + 360 " +
127
131
  "AS decimal) % 360"
128
132
  when :spherical
129
133
  "CAST(" +
130
134
  "DEGREES(ATAN2( " +
131
- "SIN(RADIANS(#{lon_attr} - #{longitude})) * " +
132
- "COS(RADIANS(#{lat_attr})), (" +
133
- "COS(RADIANS(#{latitude})) * SIN(RADIANS(#{lat_attr}))" +
135
+ "SIN(RADIANS(#{full_column_name(lon_attr)} - #{longitude})) * " +
136
+ "COS(RADIANS(#{full_column_name(lat_attr)})), (" +
137
+ "COS(RADIANS(#{latitude})) * SIN(RADIANS(#{full_column_name(lat_attr)}))" +
134
138
  ") - (" +
135
- "SIN(RADIANS(#{latitude})) * COS(RADIANS(#{lat_attr})) * " +
136
- "COS(RADIANS(#{lon_attr} - #{longitude}))" +
139
+ "SIN(RADIANS(#{latitude})) * COS(RADIANS(#{full_column_name(lat_attr)})) * " +
140
+ "COS(RADIANS(#{full_column_name(lon_attr)} - #{longitude}))" +
137
141
  ")" +
138
142
  ")) + 360 " +
139
143
  "AS decimal) % 360"
140
144
  end
141
-
145
+ options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
142
146
  distance = full_distance_from_sql(latitude, longitude, options)
143
147
  conditions = ["#{distance} <= ?", radius]
144
148
  default_near_scope_options(latitude, longitude, radius, options).merge(
145
- :select => "#{options[:select] || "#{table_name}.*"}, " +
149
+ :select => "#{options[:select] || full_column_name("*")}, " +
146
150
  "#{distance} AS distance" +
147
151
  (bearing ? ", #{bearing} AS bearing" : ""),
148
152
  :conditions => add_exclude_condition(conditions, options[:exclude])
@@ -160,9 +164,9 @@ module Geocoder::Store
160
164
  earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
161
165
 
162
166
  "#{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) ))"
167
+ "POWER(SIN((#{latitude} - #{full_column_name(lat_attr)}) * PI() / 180 / 2), 2) + " +
168
+ "COS(#{latitude} * PI() / 180) * COS(#{full_column_name(lat_attr)} * PI() / 180) * " +
169
+ "POWER(SIN((#{longitude} - #{full_column_name(lon_attr)}) * PI() / 180 / 2), 2) ))"
166
170
  end
167
171
 
168
172
  def approx_distance_from_sql(latitude, longitude, options)
@@ -175,8 +179,8 @@ module Geocoder::Store
175
179
  # sin of 45 degrees = average x or y component of vector
176
180
  factor = Math.sin(Math::PI / 4)
177
181
 
178
- "(#{dy} * ABS(#{table_name}.#{lat_attr} - #{latitude}) * #{factor}) + " +
179
- "(#{dx} * ABS(#{table_name}.#{lon_attr} - #{longitude}) * #{factor})"
182
+ "(#{dy} * ABS(#{full_column_name(lat_attr)} - #{latitude}) * #{factor}) + " +
183
+ "(#{dx} * ABS(#{full_column_name(lon_attr)} - #{longitude}) * #{factor})"
180
184
  end
181
185
 
182
186
  ##
@@ -191,7 +195,11 @@ module Geocoder::Store
191
195
  def approx_near_scope_options(latitude, longitude, radius, options)
192
196
  lat_attr = geocoder_options[:latitude]
193
197
  lon_attr = geocoder_options[:longitude]
194
- options[:bearing] = :linear unless options.include?(:bearing)
198
+ unless options.include?(:bearing)
199
+ options[:bearing] = (options[:method] || \
200
+ geocoder_options[:method] || \
201
+ Geocoder::Configuration.distances)
202
+ end
195
203
  if options[:bearing]
196
204
  bearing = "CASE " +
197
205
  "WHEN (#{lat_attr} >= #{latitude} AND #{lon_attr} >= #{longitude}) THEN 45.0 " +
@@ -204,6 +212,7 @@ module Geocoder::Store
204
212
  end
205
213
 
206
214
  distance = approx_distance_from_sql(latitude, longitude, options)
215
+ options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
207
216
 
208
217
  b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
209
218
  conditions = [
@@ -211,7 +220,7 @@ module Geocoder::Store
211
220
  [b[0], b[2], b[1], b[3]
212
221
  ]
213
222
  default_near_scope_options(latitude, longitude, radius, options).merge(
214
- :select => "#{options[:select] || "#{table_name}.*"}, " +
223
+ :select => "#{options[:select] || full_column_name("*")}, " +
215
224
  "#{distance} AS distance" +
216
225
  (bearing ? ", #{bearing} AS bearing" : ""),
217
226
  :conditions => add_exclude_condition(conditions, options[:exclude])
@@ -235,11 +244,30 @@ module Geocoder::Store
235
244
  #
236
245
  def add_exclude_condition(conditions, exclude)
237
246
  if exclude
238
- conditions[0] << " AND #{table_name}.id != ?"
247
+ conditions[0] << " AND #{full_column_name(:id)} != ?"
239
248
  conditions << exclude.id
240
249
  end
241
250
  conditions
242
251
  end
252
+
253
+ def using_sqlite?
254
+ connection.adapter_name.match /sqlite/i
255
+ end
256
+
257
+ ##
258
+ # Value which can be passed to where() to produce no results.
259
+ #
260
+ def false_condition
261
+ using_sqlite? ? 0 : "false"
262
+ end
263
+
264
+ ##
265
+ # Prepend table name if column name doesn't already contain one.
266
+ #
267
+ def full_column_name(column)
268
+ column = column.to_s
269
+ column.include?(".") ? column : [table_name, column].join(".")
270
+ end
243
271
  end
244
272
 
245
273
  ##
@@ -278,3 +306,4 @@ module Geocoder::Store
278
306
  alias_method :fetch_address, :reverse_geocode
279
307
  end
280
308
  end
309
+