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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -0
- data/README.md +467 -70
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +16 -16
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
- data/lib/geocoder/cache.rb +3 -2
- data/lib/geocoder/calculations.rb +44 -2
- data/lib/geocoder/configuration.rb +17 -10
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +19 -0
- data/lib/geocoder/ip_address.rb +13 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +32 -8
- data/lib/geocoder/lookups/baidu.rb +18 -13
- data/lib/geocoder/lookups/baidu_ip.rb +59 -0
- data/lib/geocoder/lookups/base.rb +81 -19
- data/lib/geocoder/lookups/bing.rb +40 -7
- data/lib/geocoder/lookups/esri.rb +42 -5
- data/lib/geocoder/lookups/freegeoip.rb +9 -1
- data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
- data/lib/geocoder/lookups/geocoder_us.rb +6 -2
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/geoip2.rb +45 -0
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +29 -5
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_premier.rb +1 -1
- data/lib/geocoder/lookups/here.rb +62 -0
- data/lib/geocoder/lookups/ipapi_com.rb +86 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/mapbox.rb +53 -0
- data/lib/geocoder/lookups/mapquest.rb +6 -6
- data/lib/geocoder/lookups/mapzen.rb +15 -0
- data/lib/geocoder/lookups/maxmind.rb +4 -2
- data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +9 -1
- data/lib/geocoder/lookups/okf.rb +44 -0
- data/lib/geocoder/lookups/opencagedata.rb +58 -0
- data/lib/geocoder/lookups/pelias.rb +64 -0
- data/lib/geocoder/lookups/pointpin.rb +68 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
- data/lib/geocoder/lookups/smarty_streets.rb +53 -0
- data/lib/geocoder/lookups/telize.rb +55 -0
- data/lib/geocoder/lookups/yandex.rb +8 -4
- data/lib/geocoder/models/active_record.rb +7 -3
- data/lib/geocoder/models/base.rb +1 -4
- data/lib/geocoder/models/mongo_base.rb +6 -4
- data/lib/geocoder/query.rb +9 -5
- data/lib/geocoder/railtie.rb +1 -1
- data/lib/geocoder/request.rb +74 -12
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/bing.rb +4 -0
- data/lib/geocoder/results/esri.rb +30 -6
- data/lib/geocoder/results/freegeoip.rb +2 -2
- data/lib/geocoder/results/geocodio.rb +70 -0
- data/lib/geocoder/results/geoip2.rb +62 -0
- data/lib/geocoder/results/geoportail_lu.rb +69 -0
- data/lib/geocoder/results/google.rb +15 -0
- data/lib/geocoder/results/google_places_details.rb +35 -0
- data/lib/geocoder/results/here.rb +71 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/mapbox.rb +47 -0
- data/lib/geocoder/results/mapquest.rb +5 -8
- data/lib/geocoder/results/mapzen.rb +5 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +49 -0
- data/lib/geocoder/results/nominatim.rb +6 -1
- data/lib/geocoder/results/okf.rb +106 -0
- data/lib/geocoder/results/opencagedata.rb +90 -0
- data/lib/geocoder/results/ovi.rb +9 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/smarty_streets.rb +106 -0
- data/lib/geocoder/results/telize.rb +45 -0
- data/lib/geocoder/results/test.rb +20 -3
- data/lib/geocoder/results/yandex.rb +18 -6
- data/lib/geocoder/sql.rb +16 -15
- data/lib/geocoder/stores/active_record.rb +51 -18
- data/lib/geocoder/stores/base.rb +8 -12
- data/lib/geocoder/stores/mongo_base.rb +0 -31
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +6 -13
- data/lib/maxmind_database.rb +109 -0
- data/lib/tasks/geocoder.rake +14 -3
- data/lib/tasks/maxmind.rake +73 -0
- metadata +59 -85
- data/.gitignore +0 -5
- data/.travis.yml +0 -27
- data/Rakefile +0 -25
- data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
- data/lib/geocoder/lookups/yahoo.rb +0 -86
- data/lib/geocoder/results/yahoo.rb +0 -55
- data/lib/oauth_util.rb +0 -112
- data/test/active_record_test.rb +0 -15
- data/test/cache_test.rb +0 -35
- data/test/calculations_test.rb +0 -211
- data/test/configuration_test.rb +0 -78
- data/test/custom_block_test.rb +0 -32
- data/test/error_handling_test.rb +0 -43
- data/test/fixtures/baidu_invalid_key +0 -1
- data/test/fixtures/baidu_no_results +0 -1
- data/test/fixtures/baidu_reverse +0 -1
- data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
- data/test/fixtures/bing_invalid_key +0 -1
- data/test/fixtures/bing_madison_square_garden +0 -40
- data/test/fixtures/bing_no_results +0 -16
- data/test/fixtures/bing_reverse +0 -42
- data/test/fixtures/esri_madison_square_garden +0 -59
- data/test/fixtures/esri_no_results +0 -8
- data/test/fixtures/esri_reverse +0 -21
- data/test/fixtures/freegeoip_74_200_247_59 +0 -12
- data/test/fixtures/freegeoip_no_results +0 -1
- data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
- data/test/fixtures/geocoder_ca_no_results +0 -1
- data/test/fixtures/geocoder_ca_reverse +0 -34
- data/test/fixtures/geocoder_us_madison_square_garden +0 -1
- data/test/fixtures/geocoder_us_no_results +0 -1
- data/test/fixtures/google_garbage +0 -456
- data/test/fixtures/google_madison_square_garden +0 -57
- data/test/fixtures/google_no_city_data +0 -44
- data/test/fixtures/google_no_locality +0 -51
- data/test/fixtures/google_no_results +0 -4
- data/test/fixtures/google_over_limit +0 -4
- data/test/fixtures/mapquest_error +0 -16
- data/test/fixtures/mapquest_invalid_api_key +0 -16
- data/test/fixtures/mapquest_invalid_request +0 -16
- data/test/fixtures/mapquest_madison_square_garden +0 -52
- data/test/fixtures/mapquest_no_results +0 -16
- data/test/fixtures/maxmind_24_24_24_21 +0 -1
- data/test/fixtures/maxmind_24_24_24_22 +0 -1
- data/test/fixtures/maxmind_24_24_24_23 +0 -1
- data/test/fixtures/maxmind_24_24_24_24 +0 -1
- data/test/fixtures/maxmind_74_200_247_59 +0 -1
- data/test/fixtures/maxmind_invalid_key +0 -1
- data/test/fixtures/maxmind_no_results +0 -1
- data/test/fixtures/nominatim_madison_square_garden +0 -150
- data/test/fixtures/nominatim_no_results +0 -1
- data/test/fixtures/ovi_madison_square_garden +0 -72
- data/test/fixtures/ovi_no_results +0 -8
- data/test/fixtures/yahoo_error +0 -1
- data/test/fixtures/yahoo_invalid_key +0 -2
- data/test/fixtures/yahoo_madison_square_garden +0 -52
- data/test/fixtures/yahoo_no_results +0 -10
- data/test/fixtures/yahoo_over_limit +0 -2
- data/test/fixtures/yandex_invalid_key +0 -1
- data/test/fixtures/yandex_kremlin +0 -48
- data/test/fixtures/yandex_no_city_and_town +0 -112
- data/test/fixtures/yandex_no_results +0 -16
- data/test/geocoder_test.rb +0 -59
- data/test/https_test.rb +0 -16
- data/test/integration/smoke_test.rb +0 -26
- data/test/lookup_test.rb +0 -117
- data/test/method_aliases_test.rb +0 -25
- data/test/mongoid_test.rb +0 -46
- data/test/mongoid_test_helper.rb +0 -43
- data/test/near_test.rb +0 -61
- data/test/oauth_util_test.rb +0 -30
- data/test/proxy_test.rb +0 -36
- data/test/query_test.rb +0 -52
- data/test/request_test.rb +0 -29
- data/test/result_test.rb +0 -42
- data/test/services_test.rb +0 -393
- data/test/test_helper.rb +0 -289
- 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
|
-
#
|
|
3
|
-
# :
|
|
4
|
-
# :
|
|
5
|
-
# :
|
|
6
|
-
# :
|
|
7
|
-
# :
|
|
8
|
-
# :
|
|
9
|
-
# :
|
|
10
|
-
# :
|
|
11
|
-
# :
|
|
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
|
-
#
|
|
13
|
+
# Exceptions that should not be rescued by default
|
|
14
14
|
# (if you want to implement custom error handling);
|
|
15
|
-
# supports SocketError and
|
|
16
|
-
# :
|
|
15
|
+
# supports SocketError and Timeout::Error
|
|
16
|
+
# always_raise: [],
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
# :
|
|
20
|
-
# :
|
|
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
|
data/lib/geocoder/cache.rb
CHANGED
|
@@ -49,7 +49,8 @@ module Geocoder
|
|
|
49
49
|
|
|
50
50
|
private # ----------------------------------------------------------------
|
|
51
51
|
|
|
52
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
data/lib/geocoder/exceptions.rb
CHANGED
|
@@ -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
|
data/lib/geocoder/lookup.rb
CHANGED
|
@@ -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
|
-
:
|
|
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
|
-
:
|
|
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
|
-
[
|
|
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
|
-
"
|
|
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']
|
|
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,
|
|
27
|
-
warn
|
|
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,
|
|
30
|
-
warn
|
|
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,
|
|
33
|
-
warn
|
|
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
|
|
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
|
|
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
|
-
|