geocoder 1.1.8 → 1.1.9

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.

Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +4 -0
  3. data/CHANGELOG.md +13 -1
  4. data/README.md +100 -30
  5. data/Rakefile +1 -1
  6. data/examples/autoexpire_cache_dalli.rb +2 -2
  7. data/examples/cache_bypass.rb +48 -0
  8. data/lib/geocoder/calculations.rb +38 -0
  9. data/lib/geocoder/cli.rb +8 -1
  10. data/lib/geocoder/lookup.rb +3 -0
  11. data/lib/geocoder/lookups/baidu.rb +54 -0
  12. data/lib/geocoder/lookups/base.rb +26 -11
  13. data/lib/geocoder/lookups/dstk.rb +20 -0
  14. data/lib/geocoder/lookups/freegeoip.rb +2 -6
  15. data/lib/geocoder/lookups/geocoder_us.rb +39 -0
  16. data/lib/geocoder/lookups/google.rb +5 -0
  17. data/lib/geocoder/lookups/mapquest.rb +21 -5
  18. data/lib/geocoder/lookups/maxmind.rb +1 -1
  19. data/lib/geocoder/lookups/nominatim.rb +0 -1
  20. data/lib/geocoder/lookups/ovi.rb +14 -4
  21. data/lib/geocoder/query.rb +5 -1
  22. data/lib/geocoder/results/baidu.rb +79 -0
  23. data/lib/geocoder/results/dstk.rb +6 -0
  24. data/lib/geocoder/results/geocoder_us.rb +39 -0
  25. data/lib/geocoder/results/yandex.rb +1 -1
  26. data/lib/geocoder/stores/active_record.rb +12 -6
  27. data/lib/geocoder/stores/mongo_base.rb +5 -2
  28. data/lib/geocoder/version.rb +1 -1
  29. data/lib/tasks/geocoder.rake +2 -0
  30. data/test/cache_test.rb +16 -0
  31. data/test/calculations_test.rb +31 -21
  32. data/test/fixtures/baidu_invalid_key +1 -0
  33. data/test/fixtures/baidu_no_results +1 -0
  34. data/test/fixtures/baidu_reverse +1 -0
  35. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  36. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  37. data/test/fixtures/geocoder_us_no_results +1 -0
  38. data/test/fixtures/google_over_limit +4 -0
  39. data/test/fixtures/mapquest_error +16 -0
  40. data/test/fixtures/mapquest_invalid_api_key +16 -0
  41. data/test/fixtures/mapquest_invalid_request +16 -0
  42. data/test/fixtures/mapquest_no_results +10 -1
  43. data/test/mongoid_test.rb +7 -0
  44. data/test/near_test.rb +18 -0
  45. data/test/proxy_test.rb +13 -0
  46. data/test/query_test.rb +5 -0
  47. data/test/services_test.rb +75 -2
  48. data/test/test_helper.rb +16 -6
  49. metadata +24 -9
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/baidu"
3
+
4
+ module Geocoder::Lookup
5
+ class Baidu < Base
6
+
7
+ def name
8
+ "Baidu"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "http://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query, reverse = false)
22
+ return [] unless doc = fetch_data(query)
23
+ case doc['status']; when 0
24
+ return [doc['result']] unless doc['result'].blank?
25
+ when 1, 3, 4
26
+ raise_error(Geocoder::Error, messages) ||
27
+ warn("Baidu Geocoding API error: server error.")
28
+ when 2
29
+ raise_error(Geocoder::InvalidRequest, messages) ||
30
+ warn("Baidu Geocoding API error: invalid request.")
31
+ when 5
32
+ raise_error(Geocoder::InvalidApiKey, messages) ||
33
+ warn("Baidu Geocoding API error: invalid api key.")
34
+ when 101, 102, 200..299
35
+ raise_error(Geocoder::RequestDenied) ||
36
+ warn("Baidu Geocoding API error: request denied.")
37
+ when 300..399
38
+ raise_error(Geocoder::OverQueryLimitError) ||
39
+ warn("Baidu Geocoding API error: over query limit.")
40
+ end
41
+ return []
42
+ end
43
+
44
+ def query_url_params(query)
45
+ {
46
+ (query.reverse_geocode? ? :location : :address) => query.sanitized_text,
47
+ :ak => configuration.api_key,
48
+ :output => "json"
49
+ }.merge(super)
50
+ end
51
+
52
+ end
53
+ end
54
+
@@ -72,7 +72,7 @@ module Geocoder
72
72
  def query_url(query)
73
73
  fail
74
74
  end
75
-
75
+
76
76
  ##
77
77
  # The working Cache object.
78
78
  #
@@ -99,7 +99,7 @@ module Geocoder
99
99
  protocol = "http#{'s' if configuration.use_https}"
100
100
  proxy_name = "#{protocol}_proxy"
101
101
  if proxy = configuration.send(proxy_name)
102
- proxy_url = protocol + '://' + proxy
102
+ proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
103
103
  begin
104
104
  uri = URI.parse(proxy_url)
105
105
  rescue URI::InvalidURIError
@@ -171,15 +171,19 @@ module Geocoder
171
171
  "(use Geocoder.configure(:timeout => ...) to set limit)."
172
172
  end
173
173
 
174
+ def parse_json(data)
175
+ if defined?(ActiveSupport::JSON)
176
+ ActiveSupport::JSON.decode(data)
177
+ else
178
+ JSON.parse(data)
179
+ end
180
+ end
181
+
174
182
  ##
175
183
  # Parses a raw search result (returns hash or array).
176
184
  #
177
185
  def parse_raw_data(raw_data)
178
- if defined?(ActiveSupport::JSON)
179
- ActiveSupport::JSON.decode(raw_data)
180
- else
181
- JSON.parse(raw_data)
182
- end
186
+ parse_json(raw_data)
183
187
  rescue
184
188
  warn "Geocoding API's response was not valid JSON."
185
189
  end
@@ -192,6 +196,10 @@ module Geocoder
192
196
  "http" + (configuration.use_https ? "s" : "")
193
197
  end
194
198
 
199
+ def valid_response?(response)
200
+ (200..399).include?(response.code.to_i)
201
+ end
202
+
195
203
  ##
196
204
  # Fetch a raw geocoding result (JSON string).
197
205
  # The result might or might not be cached.
@@ -204,7 +212,7 @@ module Geocoder
204
212
  check_api_key_configuration!(query)
205
213
  response = make_api_request(query)
206
214
  body = response.body
207
- if cache and (200..399).include?(response.code.to_i)
215
+ if cache and valid_response?(response)
208
216
  cache[key] = body
209
217
  end
210
218
  @cache_hit = false
@@ -219,9 +227,16 @@ module Geocoder
219
227
  def make_api_request(query)
220
228
  timeout(configuration.timeout) do
221
229
  uri = URI.parse(query_url(query))
222
- client = http_client.new(uri.host, uri.port)
223
- client.use_ssl = true if configuration.use_https
224
- client.get(uri.request_uri, configuration.http_headers)
230
+ # client = http_client.new(uri.host, uri.port)
231
+ # client.use_ssl = true if configuration.use_https
232
+ # client.get(uri.request_uri, configuration.http_headers)
233
+
234
+ http_client.start(uri.host, uri.port) do |client|
235
+ client.use_ssl = true if configuration.use_https
236
+ req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
237
+ req.basic_auth(uri.user, uri.password) if uri.user and uri.password
238
+ client.request(req)
239
+ end
225
240
  end
226
241
  end
227
242
 
@@ -0,0 +1,20 @@
1
+ # More information about the Data Science Toolkit can be found at:
2
+ # http://www.datasciencetoolkit.org/. The provided APIs mimic the
3
+ # Google geocoding api.
4
+
5
+ require 'geocoder/lookups/google'
6
+ require 'geocoder/results/dstk'
7
+
8
+ module Geocoder::Lookup
9
+ class Dstk < Google
10
+
11
+ def name
12
+ "Data Science Toolkit"
13
+ end
14
+
15
+ def query_url(query)
16
+ host = configuration[:host] || "www.datasciencetoolkit.org"
17
+ "#{protocol}://#{host}/maps/api/geocode/json?" + url_query_string(query)
18
+ end
19
+ end
20
+ end
@@ -21,12 +21,8 @@ module Geocoder::Lookup
21
21
  def results(query)
22
22
  # don't look up a loopback address, just return the stored result
23
23
  return [reserved_result(query.text)] if query.loopback_ip_address?
24
- begin
25
- return (doc = fetch_data(query)) ? [doc] : []
26
- rescue StandardError => err # Freegeoip.net returns HTML on bad request
27
- raise_error(err)
28
- return []
29
- end
24
+ # note: Freegeoip.net returns plain text "Not Found" on bad request
25
+ (doc = fetch_data(query)) ? [doc] : []
30
26
  end
31
27
 
32
28
  def reserved_result(ip)
@@ -0,0 +1,39 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geocoder_us"
3
+
4
+ module Geocoder::Lookup
5
+ class GeocoderUs < Base
6
+
7
+ def name
8
+ "Geocoder.us"
9
+ end
10
+
11
+ def query_url(query)
12
+ if configuration.api_key
13
+ "http://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
14
+ else
15
+ "http://geocoder.us/service/csv/geocode?" + url_query_string(query)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ if doc[0].to_s =~ /^(\d+)\:/
24
+ return []
25
+ else
26
+ return [doc.size == 5 ? ((doc[0..1] << nil) + doc[2..4]) : doc]
27
+ end
28
+ end
29
+
30
+ def query_url_params(query)
31
+ (query.text =~ /^\d{5}(?:-\d{4})?$/ ? {:zip => query} : {:address => query.sanitized_text}).merge(super)
32
+ end
33
+
34
+ def parse_raw_data(raw_data)
35
+ raw_data.chomp.split(',')
36
+ end
37
+ end
38
+ end
39
+
@@ -18,6 +18,11 @@ module Geocoder::Lookup
18
18
 
19
19
  private # ---------------------------------------------------------------
20
20
 
21
+ def valid_response?(response)
22
+ status = parse_json(response.body)["status"]
23
+ super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
24
+ end
25
+
21
26
  def results(query)
22
27
  return [] unless doc = fetch_data(query)
23
28
  case doc['status']; when "OK" # OK status implies >0 results
@@ -10,13 +10,13 @@ module Geocoder::Lookup
10
10
  end
11
11
 
12
12
  def required_api_key_parts
13
- []
13
+ ["key"]
14
14
  end
15
15
 
16
16
  def query_url(query)
17
- key = configuration.api_key
18
- domain = key ? "www" : "open"
19
- url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
17
+ domain = configuration[:licensed] ? "www" : "open"
18
+ version = configuration[:version] || 1
19
+ url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
20
20
  url + url_query_string(query)
21
21
  end
22
22
 
@@ -34,9 +34,25 @@ module Geocoder::Lookup
34
34
  params.merge(super)
35
35
  end
36
36
 
37
+ # http://www.mapquestapi.com/geocoding/status_codes.html
38
+ # http://open.mapquestapi.com/geocoding/status_codes.html
37
39
  def results(query)
38
40
  return [] unless doc = fetch_data(query)
39
- doc["results"][0]['locations']
41
+ return doc["results"][0]['locations'] if doc['info']['statuscode'] == 0 # A successful geocode call
42
+
43
+ messages = doc['info']['messages'].join
44
+
45
+ case doc['info']['statuscode']
46
+ when 400 # Error with input
47
+ raise_error(Geocoder::InvalidRequest, messages) ||
48
+ warn("Mapquest Geocoding API error: #{messages}")
49
+ when 403 # Key related error
50
+ raise_error(Geocoder::InvalidApiKey, messages) ||
51
+ warn("Mapquest Geocoding API error: #{messages}")
52
+ when 500 # Unknown error
53
+ raise_error(Geocoder::Error, messages) ||
54
+ warn("Mapquest Geocoding API error: #{messages}")
55
+ end
40
56
  end
41
57
 
42
58
  end
@@ -75,7 +75,7 @@ module Geocoder::Lookup
75
75
  end
76
76
 
77
77
  def reserved_result
78
- ",,,,0,0,0,0,,,"
78
+ ",,,,0,0,0,0,,,".split(",")
79
79
  end
80
80
 
81
81
  def query_url_params(query)
@@ -28,7 +28,6 @@ module Geocoder::Lookup
28
28
  def query_url_params(query)
29
29
  params = {
30
30
  :format => "json",
31
- :polygon => "1",
32
31
  :addressdetails => "1",
33
32
  :"accept-language" => configuration.language
34
33
  }.merge(super)
@@ -13,7 +13,7 @@ module Geocoder::Lookup
13
13
  end
14
14
 
15
15
  def query_url(query)
16
- "#{protocol}://lbs.ovi.com/search/6.2/geocode.json?" + url_query_string(query)
16
+ "#{protocol}://lbs.ovi.com/search/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
17
17
  end
18
18
 
19
19
  private # ---------------------------------------------------------------
@@ -29,12 +29,22 @@ module Geocoder::Lookup
29
29
  end
30
30
 
31
31
  def query_url_params(query)
32
- super.merge(
33
- :searchtext=>query.sanitized_text,
32
+ options = {
34
33
  :gen=>1,
35
34
  :app_id=>api_key,
36
35
  :app_code=>api_code
37
- )
36
+ }
37
+
38
+ if query.reverse_geocode?
39
+ super.merge(options).merge(
40
+ :prox=>query.sanitized_text,
41
+ :mode=>:retrieveAddresses
42
+ )
43
+ else
44
+ super.merge(options).merge(
45
+ :searchtext=>query.sanitized_text
46
+ )
47
+ end
38
48
  end
39
49
 
40
50
  def api_key
@@ -17,7 +17,11 @@ module Geocoder
17
17
 
18
18
  def sanitized_text
19
19
  if coordinates?
20
- text.split(/\s*,\s*/).join(',')
20
+ if text.is_a?(Array)
21
+ text.join(',')
22
+ else
23
+ text.split(/\s*,\s*/).join(',')
24
+ end
21
25
  else
22
26
  text
23
27
  end
@@ -0,0 +1,79 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Baidu < Base
5
+
6
+ def coordinates
7
+ ['lat', 'lng'].map{ |i| @data['location'][i] }
8
+ end
9
+
10
+ def address
11
+ @data['formatted_address']
12
+ end
13
+
14
+ def state
15
+ province
16
+ end
17
+
18
+ def province
19
+ @data['addressComponent']['province']
20
+ end
21
+
22
+ def city
23
+ @data['addressComponent']['city']
24
+ end
25
+
26
+ def district
27
+ @data['addressComponent']['district']
28
+ end
29
+
30
+ def street
31
+ @data['addressComponent']['street']
32
+ end
33
+
34
+ def street_number
35
+ @data['addressComponent']['street_number']
36
+ end
37
+
38
+ def formatted_address
39
+ @data['formatted_address']
40
+ end
41
+
42
+ def address_components
43
+ @data['addressComponent']
44
+ end
45
+
46
+ def state_code
47
+ ""
48
+ end
49
+
50
+ def postal_code
51
+ ""
52
+ end
53
+
54
+ def country
55
+ "China"
56
+ end
57
+
58
+ def country_code
59
+ "CN"
60
+ end
61
+
62
+ ##
63
+ # Get address components of a given type. Valid types are defined in
64
+ # Baidu's Geocoding API documentation and include (among others):
65
+ #
66
+ # :business
67
+ # :cityCode
68
+ #
69
+ def self.response_attributes
70
+ %w[business cityCode]
71
+ end
72
+
73
+ response_attributes.each do |a|
74
+ define_method a do
75
+ @data[a]
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,6 @@
1
+ require 'geocoder/results/google'
2
+
3
+ module Geocoder::Result
4
+ class Dstk < Google
5
+ end
6
+ end
@@ -0,0 +1,39 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class GeocoderUs < Base
5
+ def coordinates
6
+ [@data[0].to_f, @data[1].to_f]
7
+ end
8
+
9
+ def address(format = :full)
10
+ "#{street_address}, #{city}, #{state} #{postal_code}, #{country}".sub(/^[ ,]*/, "")
11
+ end
12
+
13
+ def street_address
14
+ @data[2]
15
+ end
16
+
17
+ def city
18
+ @data[3]
19
+ end
20
+
21
+ def state
22
+ @data[4]
23
+ end
24
+
25
+ alias_method :state_code, :state
26
+
27
+ def postal_code
28
+ @data[5]
29
+ end
30
+
31
+ def country
32
+ 'United States'
33
+ end
34
+
35
+ def country_code
36
+ 'US'
37
+ end
38
+ end
39
+ end