geocoder 1.4.3 → 1.6.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.
Files changed (109) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +64 -0
  3. data/LICENSE +1 -1
  4. data/README.md +365 -883
  5. data/examples/autoexpire_cache_redis.rb +5 -3
  6. data/lib/generators/geocoder/config/templates/initializer.rb +3 -2
  7. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +2 -0
  8. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +2 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +1 -1
  10. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +1 -1
  11. data/lib/generators/geocoder/migration_version.rb +15 -0
  12. data/lib/geocoder/cache.rb +6 -2
  13. data/lib/geocoder/calculations.rb +1 -1
  14. data/lib/geocoder/cli.rb +2 -2
  15. data/lib/geocoder/configuration.rb +2 -2
  16. data/lib/geocoder/exceptions.rb +1 -1
  17. data/lib/geocoder/ip_address.rb +14 -1
  18. data/lib/geocoder/lookup.rb +13 -6
  19. data/lib/geocoder/lookups/amap.rb +63 -0
  20. data/lib/geocoder/lookups/baidu.rb +14 -10
  21. data/lib/geocoder/lookups/baidu_ip.rb +7 -36
  22. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +17 -4
  23. data/lib/geocoder/lookups/base.rb +28 -5
  24. data/lib/geocoder/lookups/bing.rb +15 -13
  25. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  26. data/lib/geocoder/lookups/dstk.rb +4 -2
  27. data/lib/geocoder/lookups/esri.rb +39 -29
  28. data/lib/geocoder/lookups/freegeoip.rb +16 -7
  29. data/lib/geocoder/lookups/geocoder_ca.rb +4 -4
  30. data/lib/geocoder/lookups/geocodio.rb +5 -5
  31. data/lib/geocoder/lookups/geoportail_lu.rb +7 -7
  32. data/lib/geocoder/lookups/google.rb +13 -9
  33. data/lib/geocoder/lookups/google_places_details.rb +4 -4
  34. data/lib/geocoder/lookups/google_places_search.rb +4 -4
  35. data/lib/geocoder/lookups/google_premier.rb +11 -1
  36. data/lib/geocoder/lookups/here.rb +29 -23
  37. data/lib/geocoder/lookups/ip2location.rb +67 -0
  38. data/lib/geocoder/lookups/ipapi_com.rb +9 -13
  39. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  40. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  41. data/lib/geocoder/lookups/ipinfo_io.rb +11 -29
  42. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  43. data/lib/geocoder/lookups/ipstack.rb +63 -0
  44. data/lib/geocoder/lookups/latlon.rb +4 -4
  45. data/lib/geocoder/lookups/location_iq.rb +26 -8
  46. data/lib/geocoder/lookups/mapbox.rb +12 -6
  47. data/lib/geocoder/lookups/mapquest.rb +4 -5
  48. data/lib/geocoder/lookups/maxmind.rb +6 -6
  49. data/lib/geocoder/lookups/maxmind_geoip2.rb +8 -7
  50. data/lib/geocoder/lookups/nominatim.rb +17 -5
  51. data/lib/geocoder/lookups/opencagedata.rb +7 -6
  52. data/lib/geocoder/lookups/osmnames.rb +57 -0
  53. data/lib/geocoder/lookups/pelias.rb +8 -9
  54. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  55. data/lib/geocoder/lookups/pointpin.rb +10 -9
  56. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +4 -5
  57. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  58. data/lib/geocoder/lookups/smarty_streets.rb +20 -10
  59. data/lib/geocoder/lookups/telize.rb +26 -6
  60. data/lib/geocoder/lookups/tencent.rb +59 -0
  61. data/lib/geocoder/lookups/yandex.rb +12 -8
  62. data/lib/geocoder/models/active_record.rb +4 -3
  63. data/lib/geocoder/query.rb +14 -0
  64. data/lib/geocoder/railtie.rb +1 -1
  65. data/lib/geocoder/request.rb +32 -0
  66. data/lib/geocoder/results/amap.rb +87 -0
  67. data/lib/geocoder/results/baidu.rb +10 -14
  68. data/lib/geocoder/results/ban_data_gouv_fr.rb +1 -1
  69. data/lib/geocoder/results/base.rb +13 -1
  70. data/lib/geocoder/results/bing.rb +1 -1
  71. data/lib/geocoder/results/db_ip_com.rb +58 -0
  72. data/lib/geocoder/results/freegeoip.rb +0 -5
  73. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  74. data/lib/geocoder/results/geoip2.rb +24 -10
  75. data/lib/geocoder/results/geoportail_lu.rb +5 -3
  76. data/lib/geocoder/results/google.rb +16 -5
  77. data/lib/geocoder/results/here.rb +12 -1
  78. data/lib/geocoder/results/ip2location.rb +22 -0
  79. data/lib/geocoder/results/ipdata_co.rb +40 -0
  80. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  81. data/lib/geocoder/results/ipregistry.rb +308 -0
  82. data/lib/geocoder/results/ipstack.rb +60 -0
  83. data/lib/geocoder/results/maxmind.rb +0 -5
  84. data/lib/geocoder/results/maxmind_local.rb +0 -5
  85. data/lib/geocoder/results/nominatim.rb +12 -0
  86. data/lib/geocoder/results/opencagedata.rb +12 -2
  87. data/lib/geocoder/results/osmnames.rb +56 -0
  88. data/lib/geocoder/results/pickpoint.rb +6 -0
  89. data/lib/geocoder/results/postcodes_io.rb +40 -0
  90. data/lib/geocoder/results/smarty_streets.rb +55 -19
  91. data/lib/geocoder/results/telize.rb +0 -5
  92. data/lib/geocoder/results/tencent.rb +72 -0
  93. data/lib/geocoder/results/test.rb +1 -1
  94. data/lib/geocoder/sql.rb +4 -4
  95. data/lib/geocoder/stores/active_record.rb +16 -5
  96. data/lib/geocoder/stores/base.rb +1 -2
  97. data/lib/geocoder/version.rb +1 -1
  98. data/lib/hash_recursive_merge.rb +1 -2
  99. data/lib/maxmind_database.rb +3 -3
  100. data/lib/tasks/geocoder.rake +11 -3
  101. metadata +30 -14
  102. data/lib/geocoder/lookups/geocoder_us.rb +0 -43
  103. data/lib/geocoder/lookups/mapzen.rb +0 -15
  104. data/lib/geocoder/lookups/okf.rb +0 -44
  105. data/lib/geocoder/lookups/ovi.rb +0 -62
  106. data/lib/geocoder/results/geocoder_us.rb +0 -39
  107. data/lib/geocoder/results/mapzen.rb +0 -5
  108. data/lib/geocoder/results/okf.rb +0 -106
  109. data/lib/geocoder/results/ovi.rb +0 -71
@@ -13,14 +13,18 @@ module Geocoder::Lookup
13
13
  end
14
14
 
15
15
  def query_url(query)
16
- "#{ protocol }://geo.pointp.in/#{ api_key }/json/#{ query.sanitized_text }"
16
+ "#{protocol}://geo.pointp.in/#{configuration.api_key}/json/#{query.sanitized_text}"
17
17
  end
18
18
 
19
- private
19
+ private # ----------------------------------------------------------------
20
+
21
+ def cache_key(query)
22
+ "#{protocol}://geo.pointp.in/json/#{query.sanitized_text}"
23
+ end
20
24
 
21
25
  def results(query)
22
- # don't look up a loopback address, just return the stored result
23
- return [] if query.loopback_ip_address?
26
+ # don't look up a loopback or private address, just return the stored result
27
+ return [] if query.internal_ip_address?
24
28
  doc = fetch_data(query)
25
29
  if doc and doc.is_a?(Hash)
26
30
  if !data_contains_error?(doc)
@@ -38,7 +42,7 @@ module Geocoder::Lookup
38
42
  raise_error(Geocoder::Error) || Geocoder.log(:warn, "Pointpin server error")
39
43
  end
40
44
  end
41
-
45
+
42
46
  return []
43
47
  end
44
48
 
@@ -46,6 +50,7 @@ module Geocoder::Lookup
46
50
  parsed_data.keys.include?('error')
47
51
  end
48
52
 
53
+ # TODO: replace this hash with what's actually returned by Pointpin
49
54
  def reserved_result(ip)
50
55
  {
51
56
  "ip" => ip,
@@ -60,9 +65,5 @@ module Geocoder::Lookup
60
65
  "country_code" => "RD"
61
66
  }
62
67
  end
63
-
64
- def api_key
65
- configuration.api_key
66
- end
67
68
  end
68
69
  end
@@ -4,7 +4,6 @@ require 'geocoder/results/postcode_anywhere_uk'
4
4
  module Geocoder::Lookup
5
5
  class PostcodeAnywhereUk < Base
6
6
  # API documentation: http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/
7
- BASE_URL_GEOCODE_V2_00 = 'services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws'
8
7
  DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error
9
8
  INVALID_API_KEY_ERROR_CODE = '2'
10
9
 
@@ -16,11 +15,11 @@ module Geocoder::Lookup
16
15
  %w(key)
17
16
  end
18
17
 
19
- def query_url(query)
20
- format('%s://%s?%s', protocol, BASE_URL_GEOCODE_V2_00, url_query_string(query))
21
- end
18
+ private # ----------------------------------------------------------------
22
19
 
23
- private
20
+ def base_query_url(query)
21
+ "#{protocol}://services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws?"
22
+ end
24
23
 
25
24
  def results(query)
26
25
  response = fetch_data(query)
@@ -0,0 +1,31 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/postcodes_io'
3
+
4
+ module Geocoder::Lookup
5
+ class PostcodesIo < Base
6
+ def name
7
+ 'Postcodes.io'
8
+ end
9
+
10
+ def query_url(query)
11
+ "#{protocol}://api.postcodes.io/postcodes/#{query.sanitized_text.gsub(/\s/, '')}"
12
+ end
13
+
14
+ def supported_protocols
15
+ [:https]
16
+ end
17
+
18
+ private # ----------------------------------------------------------------
19
+
20
+ def cache_key(query)
21
+ query_url(query)
22
+ end
23
+
24
+ def results(query)
25
+ response = fetch_data(query)
26
+ return [] if response.nil? || response['status'] != 200 || response.empty?
27
+
28
+ [response['result']]
29
+ end
30
+ end
31
+ end
@@ -8,15 +8,7 @@ module Geocoder::Lookup
8
8
  end
9
9
 
10
10
  def required_api_key_parts
11
- %w(auti-id auth-token)
12
- end
13
-
14
- def query_url(query)
15
- if zipcode_only?(query)
16
- "#{protocol}://us-zipcode.api.smartystreets.com/lookup?#{url_query_string(query)}"
17
- else
18
- "#{protocol}://us-street.api.smartystreets.com/street-address?#{url_query_string(query)}"
19
- end
11
+ %w(auth-id auth-token)
20
12
  end
21
13
 
22
14
  # required by API as of 26 March 2015
@@ -26,13 +18,31 @@ module Geocoder::Lookup
26
18
 
27
19
  private # ---------------------------------------------------------------
28
20
 
21
+ def base_query_url(query)
22
+ if international?(query)
23
+ "#{protocol}://international-street.api.smartystreets.com/verify?"
24
+ elsif zipcode_only?(query)
25
+ "#{protocol}://us-zipcode.api.smartystreets.com/lookup?"
26
+ else
27
+ "#{protocol}://us-street.api.smartystreets.com/street-address?"
28
+ end
29
+ end
30
+
29
31
  def zipcode_only?(query)
30
32
  !query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
31
33
  end
32
34
 
35
+ def international?(query)
36
+ !query.options[:country].nil?
37
+ end
38
+
33
39
  def query_url_params(query)
34
40
  params = {}
35
- if zipcode_only?(query)
41
+ if international?(query)
42
+ params[:freeform] = query.sanitized_text
43
+ params[:country] = query.options[:country]
44
+ params[:geocode] = true
45
+ elsif zipcode_only?(query)
36
46
  params[:zipcode] = query.sanitized_text
37
47
  else
38
48
  params[:street] = query.sanitized_text
@@ -14,9 +14,9 @@ module Geocoder::Lookup
14
14
 
15
15
  def query_url(query)
16
16
  if configuration[:host]
17
- "#{protocol}://#{configuration[:host]}/geoip/#{query.sanitized_text}"
17
+ "#{protocol}://#{configuration[:host]}/location/#{query.sanitized_text}"
18
18
  else
19
- "#{protocol}://telize-v1.p.mashape.com/geoip/#{query.sanitized_text}?mashape-key=#{api_key}"
19
+ "#{protocol}://telize-v1.p.mashape.com/location/#{query.sanitized_text}?mashape-key=#{api_key}"
20
20
  end
21
21
  end
22
22
 
@@ -29,9 +29,13 @@ module Geocoder::Lookup
29
29
 
30
30
  private # ---------------------------------------------------------------
31
31
 
32
+ def cache_key(query)
33
+ query_url(query)[/(.*)\?.*/, 1]
34
+ end
35
+
32
36
  def results(query)
33
- # don't look up a loopback address, just return the stored result
34
- return [reserved_result(query.text)] if query.loopback_ip_address?
37
+ # don't look up a loopback or private address, just return the stored result
38
+ return [reserved_result(query.text)] if query.internal_ip_address?
35
39
  if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
36
40
  []
37
41
  else
@@ -44,12 +48,28 @@ module Geocoder::Lookup
44
48
  end
45
49
 
46
50
  def reserved_result(ip)
47
- {"message" => "Input string is not a valid IP address", "code" => 401}
51
+ {
52
+ "ip" => ip,
53
+ "latitude" => 0,
54
+ "longitude" => 0,
55
+ "city" => "",
56
+ "timezone" => "",
57
+ "asn" => 0,
58
+ "region" => "",
59
+ "offset" => 0,
60
+ "organization" => "",
61
+ "country_code" => "",
62
+ "country_code3" => "",
63
+ "postal_code" => "",
64
+ "continent_code" => "",
65
+ "country" => "",
66
+ "region_code" => ""
67
+ }
48
68
  end
49
69
 
50
70
  def api_key
51
71
  configuration.api_key
52
72
  end
53
-
73
+
54
74
  end
55
75
  end
@@ -0,0 +1,59 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/tencent"
3
+
4
+ module Geocoder::Lookup
5
+ class Tencent < Base
6
+
7
+ def name
8
+ "Tencent"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def supported_protocols
16
+ [:https]
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def base_query_url(query)
22
+ "#{protocol}://apis.map.qq.com/ws/geocoder/v1/?"
23
+ end
24
+
25
+ def content_key
26
+ 'result'
27
+ end
28
+
29
+ def results(query, reverse = false)
30
+ return [] unless doc = fetch_data(query)
31
+ case doc['status']
32
+ when 0
33
+ return [doc[content_key]]
34
+ when 311
35
+ raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
36
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid api key.")
37
+ when 310
38
+ raise_error(Geocoder::InvalidRequest, "invalid request.") ||
39
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid request, invalid parameters.")
40
+ when 306
41
+ raise_error(Geocoder::InvalidRequest, "invalid request.") ||
42
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid request, check response for more info.")
43
+ when 110
44
+ raise_error(Geocoder::RequestDenied, "request denied.") ||
45
+ Geocoder.log(:warn, "#{name} Geocoding API error: request source is not authorized.")
46
+ end
47
+ return []
48
+ end
49
+
50
+ def query_url_params(query)
51
+ {
52
+ (query.reverse_geocode? ? :location : :address) => query.sanitized_text,
53
+ :key => configuration.api_key,
54
+ :output => "json"
55
+ }.merge(super)
56
+ end
57
+
58
+ end
59
+ end
@@ -12,16 +12,16 @@ module Geocoder::Lookup
12
12
  "http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
13
13
  end
14
14
 
15
- def query_url(query)
16
- "#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
17
- end
18
-
19
15
  def supported_protocols
20
16
  [:https]
21
17
  end
22
18
 
23
19
  private # ---------------------------------------------------------------
24
20
 
21
+ def base_query_url(query)
22
+ "#{protocol}://geocode-maps.yandex.ru/1.x/?"
23
+ end
24
+
25
25
  def results(query)
26
26
  return [] unless doc = fetch_data(query)
27
27
  if err = doc['error']
@@ -47,12 +47,16 @@ module Geocoder::Lookup
47
47
  else
48
48
  q = query.sanitized_text
49
49
  end
50
- {
50
+ params = {
51
51
  :geocode => q,
52
52
  :format => "json",
53
- :plng => "#{query.language || configuration.language}", # supports ru, uk, be
54
- :key => configuration.api_key
55
- }.merge(super)
53
+ :lang => "#{query.language || configuration.language}", # supports ru, uk, be, default -> ru
54
+ :apikey => configuration.api_key
55
+ }
56
+ unless (bounds = query.options[:bounds]).nil?
57
+ params[:bbox] = bounds.map{ |point| "%f,%f" % point }.join('~')
58
+ end
59
+ params.merge(super)
56
60
  end
57
61
  end
58
62
  end
@@ -18,7 +18,8 @@ module Geocoder
18
18
  :units => options[:units],
19
19
  :method => options[:method],
20
20
  :lookup => options[:lookup],
21
- :language => options[:language]
21
+ :language => options[:language],
22
+ :params => options[:params]
22
23
  )
23
24
  end
24
25
 
@@ -35,7 +36,8 @@ module Geocoder
35
36
  :units => options[:units],
36
37
  :method => options[:method],
37
38
  :lookup => options[:lookup],
38
- :language => options[:language]
39
+ :language => options[:language],
40
+ :params => options[:params]
39
41
  )
40
42
  end
41
43
 
@@ -47,4 +49,3 @@ module Geocoder
47
49
  end
48
50
  end
49
51
  end
50
-
@@ -66,6 +66,13 @@ module Geocoder
66
66
  IpAddress.new(text).valid? rescue false
67
67
  end
68
68
 
69
+ ##
70
+ # Is the Query text a loopback or private IP address?
71
+ #
72
+ def internal_ip_address?
73
+ ip_address? && IpAddress.new(text).internal?
74
+ end
75
+
69
76
  ##
70
77
  # Is the Query text a loopback IP address?
71
78
  #
@@ -73,6 +80,13 @@ module Geocoder
73
80
  ip_address? && IpAddress.new(text).loopback?
74
81
  end
75
82
 
83
+ ##
84
+ # Is the Query text a private IP address?
85
+ #
86
+ def private_ip_address?
87
+ ip_address? && IpAddress.new(text).private?
88
+ end
89
+
76
90
  ##
77
91
  # Does the given string look like latitude/longitude coordinates?
78
92
  #
@@ -4,7 +4,7 @@ module Geocoder
4
4
  if defined? Rails::Railtie
5
5
  require 'rails'
6
6
  class Railtie < Rails::Railtie
7
- initializer 'geocoder.insert_into_active_record' do
7
+ initializer 'geocoder.insert_into_active_record', before: :load_config_initializers do
8
8
  ActiveSupport.on_load :active_record do
9
9
  Geocoder::Railtie.insert
10
10
  end
@@ -1,3 +1,5 @@
1
+ require 'ipaddr'
2
+
1
3
  module Geocoder
2
4
  module Request
3
5
 
@@ -54,6 +56,8 @@ module Geocoder
54
56
  GEOCODER_CANDIDATE_HEADERS.each do |header|
55
57
  if @env.has_key? header
56
58
  addrs = geocoder_split_ip_addresses(@env[header])
59
+ addrs = geocoder_remove_port_from_addresses(addrs)
60
+ addrs = geocoder_reject_non_ipv4_addresses(addrs)
57
61
  addrs = geocoder_reject_trusted_ip_addresses(addrs)
58
62
  return addrs.first if addrs.any?
59
63
  end
@@ -75,6 +79,34 @@ module Geocoder
75
79
  def geocoder_reject_trusted_ip_addresses(ip_addresses)
76
80
  ip_addresses.reject { |ip| trusted_proxy?(ip) }
77
81
  end
82
+
83
+ def geocoder_remove_port_from_addresses(ip_addresses)
84
+ ip_addresses.map do |ip|
85
+ # IPv4
86
+ if ip.count('.') > 0
87
+ ip.split(':').first
88
+ # IPv6 bracket notation
89
+ elsif match = ip.match(/\[(\S+)\]/)
90
+ match.captures.first
91
+ # IPv6 bare notation
92
+ else
93
+ ip
94
+ end
95
+ end
96
+ end
97
+
98
+ def geocoder_reject_non_ipv4_addresses(ip_addresses)
99
+ ips = []
100
+ for ip in ip_addresses
101
+ begin
102
+ valid_ip = IPAddr.new(ip)
103
+ rescue
104
+ valid_ip = false
105
+ end
106
+ ips << valid_ip.to_s if valid_ip
107
+ end
108
+ return ips.any? ? ips : ip_addresses
109
+ end
78
110
  end
79
111
  end
80
112
 
@@ -0,0 +1,87 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Amap < Base
5
+
6
+ def coordinates
7
+ location = @data['location'] || @data['roadinters'].try(:first).try(:[], 'location') \
8
+ || address_components.try(:[], 'streetNumber').try(:[], 'location')
9
+ location.to_s.split(",").reverse.map(&:to_f)
10
+ end
11
+
12
+ def address
13
+ formatted_address
14
+ end
15
+
16
+ def state
17
+ province
18
+ end
19
+
20
+ def province
21
+ address_components['province']
22
+ end
23
+
24
+ def city
25
+ address_components['city'] == [] ? province : address_components["city"]
26
+ end
27
+
28
+ def district
29
+ address_components['district']
30
+ end
31
+
32
+ def street
33
+ if address_components["neighborhood"]["name"] != []
34
+ return address_components["neighborhood"]["name"]
35
+ elsif address_components['township'] != []
36
+ return address_components["township"]
37
+ else
38
+ return @data['street'] || address_components['streetNumber'].try(:[], 'street')
39
+ end
40
+ end
41
+
42
+ def street_number
43
+ @data['number'] || address_components['streetNumber'].try(:[], 'number')
44
+ end
45
+
46
+ def formatted_address
47
+ @data['formatted_address']
48
+ end
49
+
50
+ def address_components
51
+ @data['addressComponent'] || @data
52
+ end
53
+
54
+ def state_code
55
+ ""
56
+ end
57
+
58
+ def postal_code
59
+ ""
60
+ end
61
+
62
+ def country
63
+ "China"
64
+ end
65
+
66
+ def country_code
67
+ "CN"
68
+ end
69
+
70
+ ##
71
+ # Get address components of a given type. Valid types are defined in
72
+ # Baidu's Geocoding API documentation and include (among others):
73
+ #
74
+ # :business
75
+ # :cityCode
76
+ #
77
+ def self.response_attributes
78
+ %w[roads pois roadinters]
79
+ end
80
+
81
+ response_attributes.each do |a|
82
+ define_method a do
83
+ @data[a]
84
+ end
85
+ end
86
+ end
87
+ end