geocoder 1.1.3 → 1.1.4

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 (45) hide show
  1. data/CHANGELOG.rdoc +12 -0
  2. data/README.md +580 -0
  3. data/examples/autoexpire_cache.rb +30 -0
  4. data/lib/geocoder.rb +9 -85
  5. data/lib/geocoder/calculations.rb +1 -1
  6. data/lib/geocoder/cli.rb +9 -10
  7. data/lib/geocoder/configuration.rb +3 -1
  8. data/lib/geocoder/lookup.rb +81 -0
  9. data/lib/geocoder/lookups/base.rb +20 -32
  10. data/lib/geocoder/lookups/bing.rb +12 -8
  11. data/lib/geocoder/lookups/freegeoip.rb +5 -5
  12. data/lib/geocoder/lookups/geocoder_ca.rb +13 -9
  13. data/lib/geocoder/lookups/google.rb +19 -7
  14. data/lib/geocoder/lookups/google_premier.rb +8 -7
  15. data/lib/geocoder/lookups/mapquest.rb +5 -23
  16. data/lib/geocoder/lookups/nominatim.rb +16 -13
  17. data/lib/geocoder/lookups/test.rb +8 -6
  18. data/lib/geocoder/lookups/yahoo.rb +49 -10
  19. data/lib/geocoder/lookups/yandex.rb +15 -8
  20. data/lib/geocoder/models/mongoid.rb +0 -1
  21. data/lib/geocoder/query.rb +88 -0
  22. data/lib/geocoder/results/mapquest.rb +2 -61
  23. data/lib/geocoder/results/nominatim.rb +24 -3
  24. data/lib/geocoder/sql.rb +104 -0
  25. data/lib/geocoder/stores/active_record.rb +68 -140
  26. data/lib/geocoder/stores/mongo_base.rb +2 -2
  27. data/lib/geocoder/version.rb +1 -1
  28. data/test/active_record_test.rb +15 -0
  29. data/test/calculations_test.rb +7 -0
  30. data/test/error_handling_test.rb +7 -7
  31. data/test/fixtures/yahoo_madison_square_garden.json +49 -43
  32. data/test/fixtures/yahoo_v1_madison_square_garden.json +46 -0
  33. data/test/fixtures/yahoo_v1_no_results.json +10 -0
  34. data/test/https_test.rb +2 -2
  35. data/test/integration/smoke_test.rb +6 -4
  36. data/test/lookup_test.rb +13 -6
  37. data/test/query_test.rb +34 -0
  38. data/test/result_test.rb +1 -1
  39. data/test/services_test.rb +48 -7
  40. data/test/test_helper.rb +64 -49
  41. data/test/test_mode_test.rb +0 -1
  42. metadata +13 -7
  43. data/README.rdoc +0 -552
  44. data/test/fixtures/yahoo_garbage.json +0 -50
  45. data/test/input_handling_test.rb +0 -43
@@ -10,11 +10,11 @@ module Geocoder::Lookup
10
10
  raw_data.match(/^<html><title>404/) ? nil : super(raw_data)
11
11
  end
12
12
 
13
- def results(query, reverse = false)
13
+ def results(query)
14
14
  # don't look up a loopback address, just return the stored result
15
- return [reserved_result(query)] if loopback_address?(query)
15
+ return [reserved_result(query.text)] if query.loopback_ip_address?
16
16
  begin
17
- return (doc = fetch_data(query, reverse)) ? [doc] : []
17
+ return (doc = fetch_data(query)) ? [doc] : []
18
18
  rescue StandardError => err # Freegeoip.net returns HTML on bad request
19
19
  raise_error(err)
20
20
  return []
@@ -36,8 +36,8 @@ module Geocoder::Lookup
36
36
  }
37
37
  end
38
38
 
39
- def query_url(query, reverse = false)
40
- "http://freegeoip.net/json/#{query}"
39
+ def query_url(query)
40
+ "http://freegeoip.net/json/#{query.sanitized_text}"
41
41
  end
42
42
  end
43
43
  end
@@ -6,8 +6,8 @@ module Geocoder::Lookup
6
6
 
7
7
  private # ---------------------------------------------------------------
8
8
 
9
- def results(query, reverse = false)
10
- return [] unless doc = fetch_data(query, reverse)
9
+ def results(query)
10
+ return [] unless doc = fetch_data(query)
11
11
  if doc['error'].nil?
12
12
  return [doc]
13
13
  elsif doc['error']['code'] == "005"
@@ -18,24 +18,28 @@ module Geocoder::Lookup
18
18
  return []
19
19
  end
20
20
 
21
- def query_url(query, reverse = false)
22
- params = {
21
+ def query_url_params(query)
22
+ params = super.merge(
23
23
  :geoit => "xml",
24
24
  :jsonp => 1,
25
25
  :callback => "test",
26
26
  :auth => Geocoder::Configuration.api_key
27
- }
28
- if reverse
29
- lat,lon = query.split(',')
27
+ )
28
+ if query.reverse_geocode?
29
+ lat,lon = query.coordinates
30
30
  params[:latt] = lat
31
31
  params[:longt] = lon
32
32
  params[:corner] = 1
33
33
  params[:reverse] = 1
34
34
  else
35
- params[:locate] = query
35
+ params[:locate] = query.sanitized_text
36
36
  params[:showpostal] = 1
37
37
  end
38
- "http://geocoder.ca/?" + hash_to_query(params)
38
+ params
39
+ end
40
+
41
+ def query_url(query)
42
+ "http://geocoder.ca/?" + url_query_string(query)
39
43
  end
40
44
 
41
45
  def parse_raw_data(raw_data)
@@ -10,8 +10,8 @@ module Geocoder::Lookup
10
10
 
11
11
  private # ---------------------------------------------------------------
12
12
 
13
- def results(query, reverse = false)
14
- return [] unless doc = fetch_data(query, reverse)
13
+ def results(query)
14
+ return [] unless doc = fetch_data(query)
15
15
  case doc['status']; when "OK" # OK status implies >0 results
16
16
  return doc['results']
17
17
  when "OVER_QUERY_LIMIT"
@@ -27,14 +27,26 @@ module Geocoder::Lookup
27
27
  return []
28
28
  end
29
29
 
30
- def query_url(query, reverse = false)
30
+ def query_url_google_params(query)
31
31
  params = {
32
- (reverse ? :latlng : :address) => query,
32
+ (query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
33
33
  :sensor => "false",
34
- :language => Geocoder::Configuration.language,
35
- :key => Geocoder::Configuration.api_key
34
+ :language => Geocoder::Configuration.language
36
35
  }
37
- "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + hash_to_query(params)
36
+ unless (bounds = query.options[:bounds]).nil?
37
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
38
+ end
39
+ params
40
+ end
41
+
42
+ def query_url_params(query)
43
+ super.merge(query_url_google_params(query)).merge(
44
+ :key => Geocoder::Configuration.api_key
45
+ )
46
+ end
47
+
48
+ def query_url(query)
49
+ "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
38
50
  end
39
51
  end
40
52
  end
@@ -8,15 +8,16 @@ module Geocoder::Lookup
8
8
 
9
9
  private # ---------------------------------------------------------------
10
10
 
11
- def query_url(query, reverse = false)
12
- params = {
13
- (reverse ? :latlng : :address) => query,
14
- :sensor => 'false',
15
- :language => Geocoder::Configuration.language,
11
+ def query_url_params(query)
12
+ super.merge(query_url_google_params(query)).merge(
13
+ :key => nil, # don't use param inherited from Google lookup
16
14
  :client => Geocoder::Configuration.api_key[1],
17
15
  :channel => Geocoder::Configuration.api_key[2]
18
- }.reject{ |key, value| value.nil? }
19
- path = "/maps/api/geocode/json?#{hash_to_query(params)}"
16
+ )
17
+ end
18
+
19
+ def query_url(query)
20
+ path = "/maps/api/geocode/json?" + url_query_string(query)
20
21
  "#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}"
21
22
  end
22
23
 
@@ -1,33 +1,15 @@
1
1
  require 'geocoder/lookups/base'
2
+ require "geocoder/lookups/nominatim"
2
3
  require "geocoder/results/mapquest"
3
4
 
4
5
  module Geocoder::Lookup
5
- class Mapquest < Base
6
+ class Mapquest < Nominatim
6
7
 
7
8
  private # ---------------------------------------------------------------
8
9
 
9
- def results(query, reverse = false)
10
- return [] unless doc = fetch_data(query, reverse)
11
- doc.is_a?(Array) ? doc : [doc]
12
- end
13
-
14
- def query_url(query, reverse = false)
15
- params = {
16
- :format => "json",
17
- :polygon => "1",
18
- :addressdetails => "1",
19
- :"accept-language" => Geocoder::Configuration.language
20
- }
21
- if (reverse)
22
- method = 'reverse'
23
- parts = query.split(/\s*,\s*/);
24
- params[:lat] = parts[0]
25
- params[:lon] = parts[1]
26
- else
27
- method = 'search'
28
- params[:q] = query
29
- end
30
- "http://open.mapquestapi.com/#{method}?" + hash_to_query(params)
10
+ def query_url(query)
11
+ method = query.reverse_geocode? ? "reverse" : "search"
12
+ "http://open.mapquestapi.com/#{method}?" + url_query_string(query)
31
13
  end
32
14
  end
33
15
  end
@@ -10,28 +10,31 @@ module Geocoder::Lookup
10
10
 
11
11
  private # ---------------------------------------------------------------
12
12
 
13
- def results(query, reverse = false)
14
- return [] unless doc = fetch_data(query, reverse)
13
+ def results(query)
14
+ return [] unless doc = fetch_data(query)
15
15
  doc.is_a?(Array) ? doc : [doc]
16
16
  end
17
17
 
18
- def query_url(query, reverse = false)
19
- params = {
18
+ def query_url_params(query)
19
+ params = super.merge(
20
20
  :format => "json",
21
21
  :polygon => "1",
22
22
  :addressdetails => "1",
23
23
  :"accept-language" => Geocoder::Configuration.language
24
- }
25
- if (reverse)
26
- method = 'reverse'
27
- parts = query.split(/\s*,\s*/);
28
- params[:lat] = parts[0]
29
- params[:lon] = parts[1]
24
+ )
25
+ if query.reverse_geocode?
26
+ lat,lon = query.coordinates
27
+ params[:lat] = lat
28
+ params[:lon] = lon
30
29
  else
31
- method = 'search'
32
- params[:q] = query
30
+ params[:q] = query.sanitized_text
33
31
  end
34
- "http://nominatim.openstreetmap.org/#{method}?" + hash_to_query(params)
32
+ params
33
+ end
34
+
35
+ def query_url(query)
36
+ method = query.reverse_geocode? ? "reverse" : "search"
37
+ "http://nominatim.openstreetmap.org/#{method}?" + url_query_string(query)
35
38
  end
36
39
  end
37
40
  end
@@ -5,12 +5,14 @@ module Geocoder
5
5
  module Lookup
6
6
  class Test < Base
7
7
 
8
- def self.add_stub(query, results)
9
- stubs[query] = results
8
+ def self.add_stub(query_text, results)
9
+ stubs[query_text] = results
10
10
  end
11
11
 
12
- def self.read_stub(query)
13
- stubs.fetch(query) { raise ArgumentError, "unknown stub request #{query}" }
12
+ def self.read_stub(query_text)
13
+ stubs.fetch(query_text) {
14
+ raise ArgumentError, "unknown stub request #{query_text}"
15
+ }
14
16
  end
15
17
 
16
18
  def self.stubs
@@ -23,8 +25,8 @@ module Geocoder
23
25
 
24
26
  private
25
27
 
26
- def results(query, reverse = false)
27
- Geocoder::Lookup::Test.read_stub(query)
28
+ def results(query)
29
+ Geocoder::Lookup::Test.read_stub(query.text)
28
30
  end
29
31
 
30
32
  end
@@ -10,25 +10,64 @@ module Geocoder::Lookup
10
10
 
11
11
  private # ---------------------------------------------------------------
12
12
 
13
- def results(query, reverse = false)
14
- return [] unless doc = fetch_data(query, reverse)
15
- if doc = doc['ResultSet'] and doc['Error'] == 0
16
- return doc['Found'] > 0 ? doc['Results'] : []
13
+ def results(query)
14
+ return [] unless doc = fetch_data(query)
15
+ doc = doc['ResultSet']
16
+ if api_version(doc).to_i == 1
17
+ return version_1_results(doc)
18
+ elsif api_version(doc).to_i == 2
19
+ return version_2_results(doc)
17
20
  else
18
21
  warn "Yahoo Geocoding API error: #{doc['Error']} (#{doc['ErrorMessage']})."
19
22
  return []
20
23
  end
21
24
  end
22
25
 
23
- def query_url(query, reverse = false)
24
- params = {
25
- :location => query,
26
+ def api_version(doc)
27
+ if doc.include?('version')
28
+ return doc['version'].to_f
29
+ elsif doc.include?('@version')
30
+ return doc['@version'].to_f
31
+ end
32
+ end
33
+
34
+ def version_1_results(doc)
35
+ if doc['Error'] == 0
36
+ if doc['Found'] > 0
37
+ return doc['Results']
38
+ else
39
+ return []
40
+ end
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Return array of results, or nil if an error.
46
+ #
47
+ def version_2_results(doc)
48
+ # seems to have Error == 7 when no results, though this is not documented
49
+ if [0, 7].include?(doc['Error'].to_i)
50
+ if doc['Found'].to_i > 0
51
+ r = doc['Result']
52
+ return r.is_a?(Array) ? r : [r]
53
+ else
54
+ return []
55
+ end
56
+ end
57
+ end
58
+
59
+ def query_url_params(query)
60
+ super.merge(
61
+ :location => query.sanitized_text,
26
62
  :flags => "JXTSR",
27
- :gflags => "AC#{'R' if reverse}",
63
+ :gflags => "AC#{'R' if query.reverse_geocode?}",
28
64
  :locale => "#{Geocoder::Configuration.language}_US",
29
65
  :appid => Geocoder::Configuration.api_key
30
- }
31
- "http://where.yahooapis.com/geocode?" + hash_to_query(params)
66
+ )
67
+ end
68
+
69
+ def query_url(query)
70
+ "http://where.yahooapis.com/geocode?" + url_query_string(query)
32
71
  end
33
72
  end
34
73
  end
@@ -10,8 +10,8 @@ module Geocoder::Lookup
10
10
 
11
11
  private # ---------------------------------------------------------------
12
12
 
13
- def results(query, reverse = false)
14
- return [] unless doc = fetch_data(query, reverse)
13
+ def results(query)
14
+ return [] unless doc = fetch_data(query)
15
15
  if err = doc['error']
16
16
  warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
17
17
  return []
@@ -25,15 +25,22 @@ module Geocoder::Lookup
25
25
  end
26
26
  end
27
27
 
28
- def query_url(query, reverse = false)
29
- query = query.split(",").reverse.join(",") if reverse
30
- params = {
31
- :geocode => query,
28
+ def query_url_params(query)
29
+ if query.reverse_geocode?
30
+ q = query.coordinates.reverse.join(",")
31
+ else
32
+ q = query.sanitized_text
33
+ end
34
+ super.merge(
35
+ :geocode => q,
32
36
  :format => "json",
33
37
  :plng => "#{Geocoder::Configuration.language}", # supports ru, uk, be
34
38
  :key => Geocoder::Configuration.api_key
35
- }
36
- "http://geocode-maps.yandex.ru/1.x/?" + hash_to_query(params)
39
+ )
40
+ end
41
+
42
+ def query_url(query)
43
+ "http://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
37
44
  end
38
45
  end
39
46
  end
@@ -1,4 +1,3 @@
1
- require 'mongoid/version'
2
1
  require 'geocoder/models/base'
3
2
  require 'geocoder/models/mongo_base'
4
3
 
@@ -0,0 +1,88 @@
1
+ module Geocoder
2
+ class Query
3
+ attr_accessor :text, :options
4
+
5
+ def initialize(text, options = {})
6
+ self.text = text
7
+ self.options = options
8
+ end
9
+
10
+ def execute
11
+ lookup.search(text, options)
12
+ end
13
+
14
+ def to_s
15
+ text
16
+ end
17
+
18
+ def sanitized_text
19
+ if coordinates?
20
+ text.split(/\s*,\s*/).join(',')
21
+ else
22
+ text
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Get a Lookup object (which communicates with the remote geocoding API)
28
+ # appropriate to the Query text.
29
+ #
30
+ def lookup
31
+ if ip_address?
32
+ name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
33
+ else
34
+ name = Configuration.lookup || Geocoder::Lookup.street_services.first
35
+ end
36
+ Lookup.get(name)
37
+ end
38
+
39
+ ##
40
+ # Is the Query text blank? (ie, should we not bother searching?)
41
+ #
42
+ def blank?
43
+ !!text.to_s.match(/^\s*$/)
44
+ end
45
+
46
+ ##
47
+ # Does the Query text look like an IP address?
48
+ #
49
+ # Does not check for actual validity, just the appearance of four
50
+ # dot-delimited numbers.
51
+ #
52
+ def ip_address?
53
+ !!text.to_s.match(/^(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
54
+ end
55
+
56
+ ##
57
+ # Is the Query text a loopback IP address?
58
+ #
59
+ def loopback_ip_address?
60
+ !!(text == "0.0.0.0" or text.to_s.match(/^127/))
61
+ end
62
+
63
+ ##
64
+ # Does the given string look like latitude/longitude coordinates?
65
+ #
66
+ def coordinates?
67
+ text.is_a?(Array) or (
68
+ text.is_a?(String) and
69
+ !!text.to_s.match(/^-?[0-9\.]+, *-?[0-9\.]+$/)
70
+ )
71
+ end
72
+
73
+ ##
74
+ # Return the latitude/longitude coordinates specified in the query,
75
+ # or nil if none.
76
+ #
77
+ def coordinates
78
+ sanitized_text.split(',') if coordinates?
79
+ end
80
+
81
+ ##
82
+ # Should reverse geocoding be performed for this query?
83
+ #
84
+ def reverse_geocode?
85
+ coordinates?
86
+ end
87
+ end
88
+ end