geocoder 1.1.9 → 1.3.7

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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/README.md +467 -70
  4. data/examples/reverse_geocode_job.rb +40 -0
  5. data/lib/generators/geocoder/config/templates/initializer.rb +16 -16
  6. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  7. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  8. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  10. data/lib/geocoder/cache.rb +3 -2
  11. data/lib/geocoder/calculations.rb +44 -2
  12. data/lib/geocoder/configuration.rb +17 -10
  13. data/lib/geocoder/esri_token.rb +38 -0
  14. data/lib/geocoder/exceptions.rb +19 -0
  15. data/lib/geocoder/ip_address.rb +13 -0
  16. data/lib/geocoder/kernel_logger.rb +25 -0
  17. data/lib/geocoder/logger.rb +47 -0
  18. data/lib/geocoder/lookup.rb +32 -8
  19. data/lib/geocoder/lookups/baidu.rb +18 -13
  20. data/lib/geocoder/lookups/baidu_ip.rb +59 -0
  21. data/lib/geocoder/lookups/base.rb +81 -19
  22. data/lib/geocoder/lookups/bing.rb +40 -7
  23. data/lib/geocoder/lookups/esri.rb +42 -5
  24. data/lib/geocoder/lookups/freegeoip.rb +9 -1
  25. data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
  26. data/lib/geocoder/lookups/geocoder_us.rb +6 -2
  27. data/lib/geocoder/lookups/geocodio.rb +42 -0
  28. data/lib/geocoder/lookups/geoip2.rb +45 -0
  29. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  30. data/lib/geocoder/lookups/google.rb +29 -5
  31. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  32. data/lib/geocoder/lookups/google_premier.rb +1 -1
  33. data/lib/geocoder/lookups/here.rb +62 -0
  34. data/lib/geocoder/lookups/ipapi_com.rb +86 -0
  35. data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
  36. data/lib/geocoder/lookups/latlon.rb +59 -0
  37. data/lib/geocoder/lookups/mapbox.rb +53 -0
  38. data/lib/geocoder/lookups/mapquest.rb +6 -6
  39. data/lib/geocoder/lookups/mapzen.rb +15 -0
  40. data/lib/geocoder/lookups/maxmind.rb +4 -2
  41. data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
  42. data/lib/geocoder/lookups/maxmind_local.rb +65 -0
  43. data/lib/geocoder/lookups/nominatim.rb +9 -1
  44. data/lib/geocoder/lookups/okf.rb +44 -0
  45. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  46. data/lib/geocoder/lookups/pelias.rb +64 -0
  47. data/lib/geocoder/lookups/pointpin.rb +68 -0
  48. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +53 -0
  50. data/lib/geocoder/lookups/telize.rb +55 -0
  51. data/lib/geocoder/lookups/yandex.rb +8 -4
  52. data/lib/geocoder/models/active_record.rb +7 -3
  53. data/lib/geocoder/models/base.rb +1 -4
  54. data/lib/geocoder/models/mongo_base.rb +6 -4
  55. data/lib/geocoder/query.rb +9 -5
  56. data/lib/geocoder/railtie.rb +1 -1
  57. data/lib/geocoder/request.rb +74 -12
  58. data/lib/geocoder/results/baidu_ip.rb +62 -0
  59. data/lib/geocoder/results/bing.rb +4 -0
  60. data/lib/geocoder/results/esri.rb +30 -6
  61. data/lib/geocoder/results/freegeoip.rb +2 -2
  62. data/lib/geocoder/results/geocodio.rb +70 -0
  63. data/lib/geocoder/results/geoip2.rb +62 -0
  64. data/lib/geocoder/results/geoportail_lu.rb +69 -0
  65. data/lib/geocoder/results/google.rb +15 -0
  66. data/lib/geocoder/results/google_places_details.rb +35 -0
  67. data/lib/geocoder/results/here.rb +71 -0
  68. data/lib/geocoder/results/ipapi_com.rb +45 -0
  69. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  70. data/lib/geocoder/results/latlon.rb +71 -0
  71. data/lib/geocoder/results/mapbox.rb +47 -0
  72. data/lib/geocoder/results/mapquest.rb +5 -8
  73. data/lib/geocoder/results/mapzen.rb +5 -0
  74. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  75. data/lib/geocoder/results/maxmind_local.rb +49 -0
  76. data/lib/geocoder/results/nominatim.rb +6 -1
  77. data/lib/geocoder/results/okf.rb +106 -0
  78. data/lib/geocoder/results/opencagedata.rb +90 -0
  79. data/lib/geocoder/results/ovi.rb +9 -0
  80. data/lib/geocoder/results/pelias.rb +58 -0
  81. data/lib/geocoder/results/pointpin.rb +40 -0
  82. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  83. data/lib/geocoder/results/smarty_streets.rb +106 -0
  84. data/lib/geocoder/results/telize.rb +45 -0
  85. data/lib/geocoder/results/test.rb +20 -3
  86. data/lib/geocoder/results/yandex.rb +18 -6
  87. data/lib/geocoder/sql.rb +16 -15
  88. data/lib/geocoder/stores/active_record.rb +51 -18
  89. data/lib/geocoder/stores/base.rb +8 -12
  90. data/lib/geocoder/stores/mongo_base.rb +0 -31
  91. data/lib/geocoder/version.rb +1 -1
  92. data/lib/geocoder.rb +6 -13
  93. data/lib/maxmind_database.rb +109 -0
  94. data/lib/tasks/geocoder.rake +14 -3
  95. data/lib/tasks/maxmind.rake +73 -0
  96. metadata +59 -85
  97. data/.gitignore +0 -5
  98. data/.travis.yml +0 -27
  99. data/Rakefile +0 -25
  100. data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
  101. data/lib/geocoder/lookups/yahoo.rb +0 -86
  102. data/lib/geocoder/results/yahoo.rb +0 -55
  103. data/lib/oauth_util.rb +0 -112
  104. data/test/active_record_test.rb +0 -15
  105. data/test/cache_test.rb +0 -35
  106. data/test/calculations_test.rb +0 -211
  107. data/test/configuration_test.rb +0 -78
  108. data/test/custom_block_test.rb +0 -32
  109. data/test/error_handling_test.rb +0 -43
  110. data/test/fixtures/baidu_invalid_key +0 -1
  111. data/test/fixtures/baidu_no_results +0 -1
  112. data/test/fixtures/baidu_reverse +0 -1
  113. data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
  114. data/test/fixtures/bing_invalid_key +0 -1
  115. data/test/fixtures/bing_madison_square_garden +0 -40
  116. data/test/fixtures/bing_no_results +0 -16
  117. data/test/fixtures/bing_reverse +0 -42
  118. data/test/fixtures/esri_madison_square_garden +0 -59
  119. data/test/fixtures/esri_no_results +0 -8
  120. data/test/fixtures/esri_reverse +0 -21
  121. data/test/fixtures/freegeoip_74_200_247_59 +0 -12
  122. data/test/fixtures/freegeoip_no_results +0 -1
  123. data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
  124. data/test/fixtures/geocoder_ca_no_results +0 -1
  125. data/test/fixtures/geocoder_ca_reverse +0 -34
  126. data/test/fixtures/geocoder_us_madison_square_garden +0 -1
  127. data/test/fixtures/geocoder_us_no_results +0 -1
  128. data/test/fixtures/google_garbage +0 -456
  129. data/test/fixtures/google_madison_square_garden +0 -57
  130. data/test/fixtures/google_no_city_data +0 -44
  131. data/test/fixtures/google_no_locality +0 -51
  132. data/test/fixtures/google_no_results +0 -4
  133. data/test/fixtures/google_over_limit +0 -4
  134. data/test/fixtures/mapquest_error +0 -16
  135. data/test/fixtures/mapquest_invalid_api_key +0 -16
  136. data/test/fixtures/mapquest_invalid_request +0 -16
  137. data/test/fixtures/mapquest_madison_square_garden +0 -52
  138. data/test/fixtures/mapquest_no_results +0 -16
  139. data/test/fixtures/maxmind_24_24_24_21 +0 -1
  140. data/test/fixtures/maxmind_24_24_24_22 +0 -1
  141. data/test/fixtures/maxmind_24_24_24_23 +0 -1
  142. data/test/fixtures/maxmind_24_24_24_24 +0 -1
  143. data/test/fixtures/maxmind_74_200_247_59 +0 -1
  144. data/test/fixtures/maxmind_invalid_key +0 -1
  145. data/test/fixtures/maxmind_no_results +0 -1
  146. data/test/fixtures/nominatim_madison_square_garden +0 -150
  147. data/test/fixtures/nominatim_no_results +0 -1
  148. data/test/fixtures/ovi_madison_square_garden +0 -72
  149. data/test/fixtures/ovi_no_results +0 -8
  150. data/test/fixtures/yahoo_error +0 -1
  151. data/test/fixtures/yahoo_invalid_key +0 -2
  152. data/test/fixtures/yahoo_madison_square_garden +0 -52
  153. data/test/fixtures/yahoo_no_results +0 -10
  154. data/test/fixtures/yahoo_over_limit +0 -2
  155. data/test/fixtures/yandex_invalid_key +0 -1
  156. data/test/fixtures/yandex_kremlin +0 -48
  157. data/test/fixtures/yandex_no_city_and_town +0 -112
  158. data/test/fixtures/yandex_no_results +0 -16
  159. data/test/geocoder_test.rb +0 -59
  160. data/test/https_test.rb +0 -16
  161. data/test/integration/smoke_test.rb +0 -26
  162. data/test/lookup_test.rb +0 -117
  163. data/test/method_aliases_test.rb +0 -25
  164. data/test/mongoid_test.rb +0 -46
  165. data/test/mongoid_test_helper.rb +0 -43
  166. data/test/near_test.rb +0 -61
  167. data/test/oauth_util_test.rb +0 -30
  168. data/test/proxy_test.rb +0 -36
  169. data/test/query_test.rb +0 -52
  170. data/test/request_test.rb +0 -29
  171. data/test/result_test.rb +0 -42
  172. data/test/services_test.rb +0 -393
  173. data/test/test_helper.rb +0 -289
  174. data/test/test_mode_test.rb +0 -59
@@ -0,0 +1,40 @@
1
+ # This class implements an ActiveJob job for performing reverse-geocoding
2
+ # asynchronously. Example usage:
3
+
4
+ # if @location.save && @location.address.blank?
5
+ # ReverseGeocodeJob.perform_later(@location)
6
+ # end
7
+
8
+ # Be sure to configure the queue adapter in config/application.rb:
9
+ # config.active_job.queue_adapter = :sidekiq
10
+
11
+ # You can read the Rails docs for more information on configuring ActiveJob:
12
+ # http://edgeguides.rubyonrails.org/active_job_basics.html
13
+
14
+ class ReverseGeocodeJob < ActiveJob::Base
15
+ queue_as :high
16
+
17
+ def perform(location)
18
+ address = address(location)
19
+
20
+ if address.present?
21
+ location.update(address: address)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def address(location)
28
+ Geocoder.address(location.coordinates)
29
+ rescue => exception
30
+ MonitoringService.notify(exception, location: { id: location.id })
31
+
32
+ if retryable?(exception)
33
+ raise exception
34
+ end
35
+ end
36
+
37
+ def retryable?(exception)
38
+ exception.is_a?(Timeout::Error) || exception.is_a?(SocketError)
39
+ end
40
+ end
@@ -1,21 +1,21 @@
1
1
  Geocoder.configure(
2
- # geocoding options
3
- # :timeout => 3, # geocoding service timeout (secs)
4
- # :lookup => :google, # name of geocoding service (symbol)
5
- # :language => :en, # ISO-639 language code
6
- # :use_https => false, # use HTTPS for lookup requests? (if supported)
7
- # :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
8
- # :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
9
- # :api_key => nil, # API key for geocoding service
10
- # :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
11
- # :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
2
+ # Geocoding options
3
+ # timeout: 3, # geocoding service timeout (secs)
4
+ # lookup: :google, # name of geocoding service (symbol)
5
+ # language: :en, # ISO-639 language code
6
+ # use_https: false, # use HTTPS for lookup requests? (if supported)
7
+ # http_proxy: nil, # HTTP proxy server (user:pass@host:port)
8
+ # https_proxy: nil, # HTTPS proxy server (user:pass@host:port)
9
+ # api_key: nil, # API key for geocoding service
10
+ # cache: nil, # cache object (must respond to #[], #[]=, and #keys)
11
+ # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys
12
12
 
13
- # exceptions that should not be rescued by default
13
+ # Exceptions that should not be rescued by default
14
14
  # (if you want to implement custom error handling);
15
- # supports SocketError and TimeoutError
16
- # :always_raise => [],
15
+ # supports SocketError and Timeout::Error
16
+ # always_raise: [],
17
17
 
18
- # calculation options
19
- # :units => :mi, # :km for kilometers or :mi for miles
20
- # :distances => :linear # :spherical or :linear
18
+ # Calculation options
19
+ # units: :mi, # :km for kilometers or :mi for miles
20
+ # distances: :linear # :spherical or :linear
21
21
  )
@@ -0,0 +1,28 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module Geocoder
4
+ module Generators
5
+ module Maxmind
6
+ class GeoliteCityGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def copy_migration_files
12
+ migration_template "migration/geolite_city.rb", "db/migrate/geocoder_maxmind_geolite_city.rb"
13
+ end
14
+
15
+ # Define the next_migration_number method (necessary for the
16
+ # migration_template method to work)
17
+ def self.next_migration_number(dirname)
18
+ if ActiveRecord::Base.timestamped_migrations
19
+ sleep 1 # make sure each time we get a different timestamp
20
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
21
+ else
22
+ "%.3d" % (current_migration_number(dirname) + 1)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module Geocoder
4
+ module Generators
5
+ module Maxmind
6
+ class GeoliteCountryGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def copy_migration_files
12
+ migration_template "migration/geolite_country.rb", "db/migrate/geocoder_maxmind_geolite_country.rb"
13
+ end
14
+
15
+ # Define the next_migration_number method (necessary for the
16
+ # migration_template method to work)
17
+ def self.next_migration_number(dirname)
18
+ if ActiveRecord::Base.timestamped_migrations
19
+ sleep 1 # make sure each time we get a different timestamp
20
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
21
+ else
22
+ "%.3d" % (current_migration_number(dirname) + 1)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :maxmind_geolite_city_blocks, id: false do |t|
4
+ t.column :start_ip_num, :bigint, null: false
5
+ t.column :end_ip_num, :bigint, null: false
6
+ t.column :loc_id, :bigint, null: false
7
+ end
8
+ add_index :maxmind_geolite_city_blocks, :loc_id
9
+ add_index :maxmind_geolite_city_blocks, :start_ip_num, unique: true
10
+ add_index :maxmind_geolite_city_blocks, [:end_ip_num, :start_ip_num], unique: true, name: 'index_maxmind_geolite_city_blocks_on_end_ip_num_range'
11
+
12
+ create_table :maxmind_geolite_city_location, id: false do |t|
13
+ t.column :loc_id, :bigint, null: false
14
+ t.string :country, null: false
15
+ t.string :region, null: false
16
+ t.string :city
17
+ t.string :postal_code, null: false
18
+ t.float :latitude
19
+ t.float :longitude
20
+ t.integer :metro_code
21
+ t.integer :area_code
22
+ end
23
+ add_index :maxmind_geolite_city_location, :loc_id, unique: true
24
+ end
25
+
26
+ def self.down
27
+ drop_table :maxmind_geolite_city_location
28
+ drop_table :maxmind_geolite_city_blocks
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :maxmind_geolite_country, id: false do |t|
4
+ t.column :start_ip, :string
5
+ t.column :end_ip, :string
6
+ t.column :start_ip_num, :bigint, null: false
7
+ t.column :end_ip_num, :bigint, null: false
8
+ t.column :country_code, :string, null: false
9
+ t.column :country, :string, null: false
10
+ end
11
+ add_index :maxmind_geolite_country, :start_ip_num, unique: true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :maxmind_geolite_country
16
+ end
17
+ end
@@ -49,7 +49,8 @@ module Geocoder
49
49
 
50
50
  private # ----------------------------------------------------------------
51
51
 
52
- attr_reader :prefix, :store
52
+ def prefix; @prefix; end
53
+ def store; @store; end
53
54
 
54
55
  ##
55
56
  # Cache key for a given URL.
@@ -63,7 +64,7 @@ module Geocoder
63
64
  # that have non-nil values.
64
65
  #
65
66
  def keys
66
- store.keys.select{ |k| k.match /^#{prefix}/ and interpret(store[k]) }
67
+ store.keys.select{ |k| k.match(/^#{prefix}/) and interpret(store[k]) }
67
68
  end
68
69
 
69
70
  ##
@@ -26,6 +26,11 @@ module Geocoder
26
26
  #
27
27
  KM_IN_NM = 0.539957
28
28
 
29
+ ##
30
+ # Conversion factor: multiply by radians to get degrees.
31
+ #
32
+ DEGREES_PER_RADIAN = 57.2957795
33
+
29
34
  # Not a number constant
30
35
  NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0
31
36
 
@@ -232,11 +237,14 @@ module Geocoder
232
237
  #
233
238
  # * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
234
239
  # Use Geocoder.configure(:units => ...) to configure default units.
240
+ # * <tt>:seed</tt> - The seed for the random number generator
235
241
  def random_point_near(center, radius, options = {})
236
242
 
237
243
  # set default options
238
244
  options[:units] ||= Geocoder.config.units
239
245
 
246
+ random = Random.new(options[:seed] || Random.new_seed)
247
+
240
248
  # convert to coordinate arrays
241
249
  center = extract_coordinates(center)
242
250
 
@@ -244,16 +252,50 @@ module Geocoder
244
252
  max_degree_delta = 360.0 * (radius / earth_circumference)
245
253
 
246
254
  # random bearing in radians
247
- theta = 2 * Math::PI * rand
255
+ theta = 2 * Math::PI * random.rand
248
256
 
249
257
  # random radius, use the square root to ensure a uniform
250
258
  # distribution of points over the circle
251
- r = Math.sqrt(rand) * max_degree_delta
259
+ r = Math.sqrt(random.rand) * max_degree_delta
252
260
 
253
261
  delta_lat, delta_long = [r * Math.cos(theta), r * Math.sin(theta)]
254
262
  [center[0] + delta_lat, center[1] + delta_long]
255
263
  end
256
264
 
265
+ ##
266
+ # Given a start point, heading (in degrees), and distance, provides
267
+ # an endpoint.
268
+ # The starting point is given in the same way that points are given to all
269
+ # Geocoder methods that accept points as arguments. It can be:
270
+ #
271
+ # * an array of coordinates ([lat,lon])
272
+ # * a geocodable address (string)
273
+ # * a geocoded object (one which implements a +to_coordinates+ method
274
+ # which returns a [lat,lon] array
275
+ #
276
+ def endpoint(start, heading, distance, options = {})
277
+ options[:units] ||= Geocoder.config.units
278
+ radius = earth_radius(options[:units])
279
+
280
+ start = extract_coordinates(start)
281
+
282
+ # convert degrees to radians
283
+ start = to_radians(start)
284
+
285
+ lat = start[0]
286
+ lon = start[1]
287
+ heading = to_radians(heading)
288
+ distance = distance.to_f
289
+
290
+ end_lat = Math.asin(Math.sin(lat)*Math.cos(distance/radius) +
291
+ Math.cos(lat)*Math.sin(distance/radius)*Math.cos(heading))
292
+
293
+ end_lon = lon+Math.atan2(Math.sin(heading)*Math.sin(distance/radius)*Math.cos(lat),
294
+ Math.cos(distance/radius)-Math.sin(lat)*Math.sin(end_lat))
295
+
296
+ to_degrees [end_lat, end_lon]
297
+ end
298
+
257
299
  ##
258
300
  # Convert degrees to radians.
259
301
  # If an array (or multiple arguments) is passed,
@@ -14,14 +14,8 @@ module Geocoder
14
14
  # )
15
15
  #
16
16
  def self.configure(options = nil, &block)
17
- if block_given?
18
- warn "WARNING: Passing a block to Geocoder.configure is DEPRECATED. Please pass a hash instead (eg: Geocoder.configure(:units => ..., :api_key => ...))."
19
- block.call(Configuration.instance)
20
- elsif !options.nil?
17
+ if !options.nil?
21
18
  Configuration.instance.configure(options)
22
- else
23
- warn "WARNING: Use of Geocoder.configure to read or write single config options is DEPRECATED. To write to the config please pass a hash (eg: Geocoder.configure(:units => ...)). To read config options please use the Geocoder.config object (eg: Geocoder.config.units)."
24
- Configuration.instance
25
19
  end
26
20
  end
27
21
 
@@ -44,6 +38,14 @@ module Geocoder
44
38
  data
45
39
  end
46
40
 
41
+ ##
42
+ # Merge the given hash into a lookup's existing configuration.
43
+ #
44
+ def self.merge_into_lookup_config(lookup_name, options)
45
+ base = Geocoder.config[lookup_name]
46
+ Geocoder.configure(lookup_name => base.merge(options))
47
+ end
48
+
47
49
  class Configuration
48
50
  include Singleton
49
51
 
@@ -61,7 +63,10 @@ module Geocoder
61
63
  :cache_prefix,
62
64
  :always_raise,
63
65
  :units,
64
- :distances
66
+ :distances,
67
+ :basic_auth,
68
+ :logger,
69
+ :kernel_logger_level
65
70
  ]
66
71
 
67
72
  attr_accessor :data
@@ -102,10 +107,13 @@ module Geocoder
102
107
  @data[:api_key] = nil # API key for geocoding service
103
108
  @data[:cache] = nil # cache object (must respond to #[], #[]=, and #keys)
104
109
  @data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys
110
+ @data[:basic_auth] = {} # user and password for basic auth ({:user => "user", :password => "password"})
111
+ @data[:logger] = :kernel # :kernel or Logger instance
112
+ @data[:kernel_logger_level] = ::Logger::WARN # log level, if kernel logger is used
105
113
 
106
114
  # exceptions that should not be rescued by default
107
115
  # (if you want to implement custom error handling);
108
- # supports SocketError and TimeoutError
116
+ # supports SocketError and Timeout::Error
109
117
  @data[:always_raise] = []
110
118
 
111
119
  # calculation options
@@ -125,6 +133,5 @@ module Geocoder
125
133
  end
126
134
  EOS
127
135
  end.join("\n\n"))
128
-
129
136
  end
130
137
  end
@@ -0,0 +1,38 @@
1
+ module Geocoder
2
+ class EsriToken
3
+ attr_accessor :value, :expires_at
4
+
5
+ def initialize(value, expires_at)
6
+ @value = value
7
+ @expires_at = expires_at
8
+ end
9
+
10
+ def to_s
11
+ @value
12
+ end
13
+
14
+ def active?
15
+ @expires_at > Time.now
16
+ end
17
+
18
+ def self.generate_token(client_id, client_secret, expires=1440)
19
+ # creates a new token that will expire in 1 day by default
20
+ getToken = Net::HTTP.post_form URI('https://www.arcgis.com/sharing/rest/oauth2/token'),
21
+ f: 'json',
22
+ client_id: client_id,
23
+ client_secret: client_secret,
24
+ grant_type: 'client_credentials',
25
+ expiration: expires # (minutes) max: 20160, default: 1 day
26
+
27
+ response = JSON.parse(getToken.body)
28
+
29
+ if response['error']
30
+ Geocoder.log(:warn, response['error'])
31
+ else
32
+ token_value = response['access_token']
33
+ expires_at = Time.now + (expires * 60)
34
+ new(token_value, expires_at)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,5 @@
1
+ require 'timeout' # required for Ruby 1.9.3
2
+
1
3
  module Geocoder
2
4
 
3
5
  class Error < StandardError
@@ -9,6 +11,14 @@ module Geocoder
9
11
  class OverQueryLimitError < Error
10
12
  end
11
13
 
14
+ class ResponseParseError < Error
15
+ attr_reader :response
16
+
17
+ def initialize(response)
18
+ @response = response
19
+ end
20
+ end
21
+
12
22
  class RequestDenied < Error
13
23
  end
14
24
 
@@ -18,4 +28,13 @@ module Geocoder
18
28
  class InvalidApiKey < Error
19
29
  end
20
30
 
31
+ class ServiceUnavailable < Error
32
+ end
33
+
34
+ class LookupTimeout < ::Timeout::Error
35
+ end
36
+
37
+ class NetworkError < Error
38
+ end
39
+
21
40
  end
@@ -0,0 +1,13 @@
1
+ require 'resolv'
2
+ module Geocoder
3
+ class IpAddress < String
4
+
5
+ def loopback?
6
+ valid? and (self == "0.0.0.0" or self.match(/\A127\./) or self == "::1")
7
+ end
8
+
9
+ def valid?
10
+ !!((self =~ Resolv::IPv4::Regex) || (self =~ Resolv::IPv6::Regex))
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Geocoder
2
+ class KernelLogger
3
+ include Singleton
4
+
5
+ def add(level, message)
6
+ return unless log_message_at_level?(level)
7
+ case level
8
+ when ::Logger::DEBUG, ::Logger::INFO
9
+ puts message
10
+ when ::Logger::WARN
11
+ warn message
12
+ when ::Logger::ERROR
13
+ raise message
14
+ when ::Logger::FATAL
15
+ fail message
16
+ end
17
+ end
18
+
19
+ private # ----------------------------------------------------------------
20
+
21
+ def log_message_at_level?(level)
22
+ level >= Geocoder.config.kernel_logger_level
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ require 'logger'
2
+
3
+ module Geocoder
4
+
5
+ def self.log(level, message)
6
+ Logger.instance.log(level, message)
7
+ end
8
+
9
+ class Logger
10
+ include Singleton
11
+
12
+ SEVERITY = {
13
+ debug: ::Logger::DEBUG,
14
+ info: ::Logger::INFO,
15
+ warn: ::Logger::WARN,
16
+ error: ::Logger::ERROR,
17
+ fatal: ::Logger::FATAL
18
+ }
19
+
20
+ def log(level, message)
21
+ unless valid_level?(level)
22
+ raise StandardError, "Geocoder tried to log a message with an invalid log level."
23
+ end
24
+ if current_logger.respond_to? :add
25
+ current_logger.add(SEVERITY[level], message)
26
+ else
27
+ raise Geocoder::ConfigurationError, "Please specify valid logger for Geocoder. " +
28
+ "Logger specified must be :kernel or must respond to `add(level, message)`."
29
+ end
30
+ nil
31
+ end
32
+
33
+ private # ----------------------------------------------------------------
34
+
35
+ def current_logger
36
+ logger = Geocoder.config[:logger]
37
+ if logger == :kernel
38
+ logger = Geocoder::KernelLogger.instance
39
+ end
40
+ logger
41
+ end
42
+
43
+ def valid_level?(level)
44
+ SEVERITY.keys.include?(level)
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,5 @@
1
+ require "geocoder/lookups/test"
2
+
1
3
  module Geocoder
2
4
  module Lookup
3
5
  extend self
@@ -20,21 +22,32 @@ module Geocoder
20
22
  # All street address lookup services, default first.
21
23
  #
22
24
  def street_services
23
- [
25
+ @street_services ||= [
24
26
  :dstk,
25
27
  :esri,
26
28
  :google,
27
29
  :google_premier,
28
- :yahoo,
30
+ :google_places_details,
29
31
  :bing,
30
32
  :geocoder_ca,
31
33
  :geocoder_us,
32
34
  :yandex,
33
35
  :nominatim,
36
+ :mapbox,
34
37
  :mapquest,
38
+ :mapzen,
39
+ :opencagedata,
35
40
  :ovi,
41
+ :pelias,
42
+ :here,
36
43
  :baidu,
37
- :test
44
+ :geocodio,
45
+ :smarty_streets,
46
+ :okf,
47
+ :postcode_anywhere_uk,
48
+ :geoportail_lu,
49
+ :test,
50
+ :latlon
38
51
  ]
39
52
  end
40
53
 
@@ -42,9 +55,22 @@ module Geocoder
42
55
  # All IP address lookup services, default first.
43
56
  #
44
57
  def ip_services
45
- [:freegeoip, :maxmind]
58
+ @ip_services ||= [
59
+ :baidu_ip,
60
+ :freegeoip,
61
+ :geoip2,
62
+ :maxmind,
63
+ :maxmind_local,
64
+ :telize,
65
+ :pointpin,
66
+ :maxmind_geoip2,
67
+ :ipinfo_io,
68
+ :ipapi_com
69
+ ]
46
70
  end
47
71
 
72
+ attr_writer :street_services, :ip_services
73
+
48
74
  ##
49
75
  # Retrieve a Lookup object from the store.
50
76
  # Use this instead of Geocoder::Lookup::X.new to get an
@@ -64,6 +90,8 @@ module Geocoder
64
90
  #
65
91
  def spawn(name)
66
92
  if all_services.include?(name)
93
+ name = name.to_s
94
+ require "geocoder/lookups/#{name}"
67
95
  Geocoder::Lookup.const_get(classify_name(name)).new
68
96
  else
69
97
  valids = all_services.map(&:inspect).join(", ")
@@ -80,7 +108,3 @@ module Geocoder
80
108
  end
81
109
  end
82
110
  end
83
-
84
- Geocoder::Lookup.all_services.each do |name|
85
- require "geocoder/lookups/#{name}"
86
- end
@@ -13,30 +13,36 @@ module Geocoder::Lookup
13
13
  end
14
14
 
15
15
  def query_url(query)
16
- "http://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
16
+ "#{protocol}://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
17
+ end
18
+
19
+ # HTTP only
20
+ def supported_protocols
21
+ [:http]
17
22
  end
18
23
 
19
24
  private # ---------------------------------------------------------------
20
25
 
21
26
  def results(query, reverse = false)
22
27
  return [] unless doc = fetch_data(query)
23
- case doc['status']; when 0
28
+ case doc['status']
29
+ when 0
24
30
  return [doc['result']] unless doc['result'].blank?
25
31
  when 1, 3, 4
26
- raise_error(Geocoder::Error, messages) ||
27
- warn("Baidu Geocoding API error: server error.")
32
+ raise_error(Geocoder::Error, "server error.") ||
33
+ Geocoder.log(:warn, "Baidu Geocoding API error: server error.")
28
34
  when 2
29
- raise_error(Geocoder::InvalidRequest, messages) ||
30
- warn("Baidu Geocoding API error: invalid request.")
35
+ raise_error(Geocoder::InvalidRequest, "invalid request.") ||
36
+ Geocoder.log(:warn, "Baidu Geocoding API error: invalid request.")
31
37
  when 5
32
- raise_error(Geocoder::InvalidApiKey, messages) ||
33
- warn("Baidu Geocoding API error: invalid api key.")
38
+ raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
39
+ Geocoder.log(:warn, "Baidu Geocoding API error: invalid api key.")
34
40
  when 101, 102, 200..299
35
- raise_error(Geocoder::RequestDenied) ||
36
- warn("Baidu Geocoding API error: request denied.")
41
+ raise_error(Geocoder::RequestDenied, "request denied") ||
42
+ Geocoder.log(:warn, "Baidu Geocoding API error: request denied.")
37
43
  when 300..399
38
- raise_error(Geocoder::OverQueryLimitError) ||
39
- warn("Baidu Geocoding API error: over query limit.")
44
+ raise_error(Geocoder::OverQueryLimitError, "over query limit.") ||
45
+ Geocoder.log(:warn, "Baidu Geocoding API error: over query limit.")
40
46
  end
41
47
  return []
42
48
  end
@@ -51,4 +57,3 @@ module Geocoder::Lookup
51
57
 
52
58
  end
53
59
  end
54
-