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,58 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/opencagedata'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class Opencagedata < Base
|
|
6
|
+
|
|
7
|
+
def name
|
|
8
|
+
"OpenCageData"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def query_url(query)
|
|
12
|
+
"#{protocol}://api.opencagedata.com/geocode/v1/json?key=#{configuration.api_key}&#{url_query_string(query)}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def required_api_key_parts
|
|
16
|
+
["key"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def results(query)
|
|
22
|
+
return [] unless doc = fetch_data(query)
|
|
23
|
+
# return doc["results"]
|
|
24
|
+
|
|
25
|
+
messages = doc['status']['message']
|
|
26
|
+
case doc['status']['code']
|
|
27
|
+
when 400 # Error with input
|
|
28
|
+
raise_error(Geocoder::InvalidRequest, messages) ||
|
|
29
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
|
30
|
+
when 403 # Key related error
|
|
31
|
+
raise_error(Geocoder::InvalidApiKey, messages) ||
|
|
32
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
|
33
|
+
when 402 # Quata Exceeded
|
|
34
|
+
raise_error(Geocoder::OverQueryLimitError, messages) ||
|
|
35
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
|
36
|
+
when 500 # Unknown error
|
|
37
|
+
raise_error(Geocoder::Error, messages) ||
|
|
38
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
return doc["results"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def query_url_params(query)
|
|
45
|
+
params = {
|
|
46
|
+
:q => query.sanitized_text,
|
|
47
|
+
:language => (query.language || configuration.language)
|
|
48
|
+
}.merge(super)
|
|
49
|
+
|
|
50
|
+
unless (bounds = query.options[:bounds]).nil?
|
|
51
|
+
params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
params
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/pelias'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class Pelias < Base
|
|
6
|
+
def name
|
|
7
|
+
'Pelias'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def endpoint
|
|
11
|
+
configuration[:endpoint] || 'localhost'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def query_url(query)
|
|
15
|
+
query_type = query.reverse_geocode? ? 'reverse' : 'search'
|
|
16
|
+
"#{protocol}://#{endpoint}/v1/#{query_type}?" + url_query_string(query)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def required_api_key_parts
|
|
20
|
+
['search-XXXX']
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def query_url_params(query)
|
|
26
|
+
params = {
|
|
27
|
+
api_key: configuration.api_key,
|
|
28
|
+
size: 1
|
|
29
|
+
}.merge(super)
|
|
30
|
+
|
|
31
|
+
if query.reverse_geocode?
|
|
32
|
+
lat,lon = query.coordinates
|
|
33
|
+
params[:'point.lat'] = lat
|
|
34
|
+
params[:'point.lon'] = lon
|
|
35
|
+
else
|
|
36
|
+
params[:text] = query.text
|
|
37
|
+
end
|
|
38
|
+
params
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def results(query)
|
|
42
|
+
return [] unless doc = fetch_data(query)
|
|
43
|
+
|
|
44
|
+
# not all responses include a meta
|
|
45
|
+
if doc['meta']
|
|
46
|
+
error = doc.fetch('results', {}).fetch('error', {})
|
|
47
|
+
message = error.fetch('type', 'Unknown Error') + ': ' + error.fetch('message', 'No message')
|
|
48
|
+
log_message = 'Pelias Geocoding API error - ' + message
|
|
49
|
+
case doc['meta']['status_code']
|
|
50
|
+
when '200'
|
|
51
|
+
# nothing to see here
|
|
52
|
+
when '403'
|
|
53
|
+
raise_error(Geocoder::RequestDenied, message) || Geocoder.log(:warn, log_message)
|
|
54
|
+
when '429'
|
|
55
|
+
raise_error(Geocoder::OverQueryLimitError, message) || Geocoder.log(:warn, log_message)
|
|
56
|
+
else
|
|
57
|
+
raise_error(Geocoder::Error, message) || Geocoder.log(:warn, log_message)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
doc['features'] || []
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/pointpin'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class Pointpin < Base
|
|
6
|
+
|
|
7
|
+
def name
|
|
8
|
+
"Pointpin"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def required_api_key_parts
|
|
12
|
+
["key"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def query_url(query)
|
|
16
|
+
"#{ protocol }://geo.pointp.in/#{ api_key }/json/#{ query.sanitized_text }"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def results(query)
|
|
22
|
+
# don't look up a loopback address, just return the stored result
|
|
23
|
+
return [] if query.loopback_ip_address?
|
|
24
|
+
doc = fetch_data(query)
|
|
25
|
+
if doc and doc.is_a?(Hash)
|
|
26
|
+
if !data_contains_error?(doc)
|
|
27
|
+
return [doc]
|
|
28
|
+
elsif doc['error']
|
|
29
|
+
case doc['error']
|
|
30
|
+
when "Invalid IP address"
|
|
31
|
+
raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Invalid Pointpin request.")
|
|
32
|
+
when "Invalid API key"
|
|
33
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Pointpin API key.")
|
|
34
|
+
when "Address not found"
|
|
35
|
+
Geocoder.log(:warn, "Address not found.")
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
raise_error(Geocoder::Error) || Geocoder.log(:warn, "Pointpin server error")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return []
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def data_contains_error?(parsed_data)
|
|
46
|
+
parsed_data.keys.include?('error')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def reserved_result(ip)
|
|
50
|
+
{
|
|
51
|
+
"ip" => ip,
|
|
52
|
+
"city" => "",
|
|
53
|
+
"region_code" => "",
|
|
54
|
+
"region_name" => "",
|
|
55
|
+
"metrocode" => "",
|
|
56
|
+
"zipcode" => "",
|
|
57
|
+
"latitude" => "0",
|
|
58
|
+
"longitude" => "0",
|
|
59
|
+
"country_name" => "Reserved",
|
|
60
|
+
"country_code" => "RD"
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def api_key
|
|
65
|
+
configuration.api_key
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/postcode_anywhere_uk'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class PostcodeAnywhereUk < Base
|
|
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
|
+
DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error
|
|
9
|
+
INVALID_API_KEY_ERROR_CODE = '2'
|
|
10
|
+
|
|
11
|
+
def name
|
|
12
|
+
'PostcodeAnywhereUk'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def required_api_key_parts
|
|
16
|
+
%w(key)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def query_url(query)
|
|
20
|
+
format('%s://%s?%s', protocol, BASE_URL_GEOCODE_V2_00, url_query_string(query))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def results(query)
|
|
26
|
+
response = fetch_data(query)
|
|
27
|
+
return [] if response.nil? || !response.is_a?(Array) || response.empty?
|
|
28
|
+
|
|
29
|
+
raise_exception_for_response(response[0]) if response[0]['Error']
|
|
30
|
+
response
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def raise_exception_for_response(response)
|
|
34
|
+
case response['Error']
|
|
35
|
+
when *DAILY_LIMIT_EXEEDED_ERROR_CODES
|
|
36
|
+
raise_error(Geocoder::OverQueryLimitError, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
|
37
|
+
when INVALID_API_KEY_ERROR_CODE
|
|
38
|
+
raise_error(Geocoder::InvalidApiKey, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
|
39
|
+
else # anything else just raise general error with the api cause
|
|
40
|
+
raise_error(Geocoder::Error, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def query_url_params(query)
|
|
45
|
+
{
|
|
46
|
+
:location => query.sanitized_text,
|
|
47
|
+
:key => configuration.api_key
|
|
48
|
+
}.merge(super)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/smarty_streets'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class SmartyStreets < Base
|
|
6
|
+
def name
|
|
7
|
+
"SmartyStreets"
|
|
8
|
+
end
|
|
9
|
+
|
|
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}://api.smartystreets.com/street-address?#{url_query_string(query)}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# required by API as of 26 March 2015
|
|
23
|
+
def supported_protocols
|
|
24
|
+
[:https]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private # ---------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def zipcode_only?(query)
|
|
30
|
+
!query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def query_url_params(query)
|
|
34
|
+
params = {}
|
|
35
|
+
if zipcode_only?(query)
|
|
36
|
+
params[:zipcode] = query.sanitized_text
|
|
37
|
+
else
|
|
38
|
+
params[:street] = query.sanitized_text
|
|
39
|
+
end
|
|
40
|
+
if configuration.api_key.is_a?(Array)
|
|
41
|
+
params[:"auth-id"] = configuration.api_key[0]
|
|
42
|
+
params[:"auth-token"] = configuration.api_key[1]
|
|
43
|
+
else
|
|
44
|
+
params[:"auth-token"] = configuration.api_key
|
|
45
|
+
end
|
|
46
|
+
params.merge(super)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def results(query)
|
|
50
|
+
fetch_data(query) || []
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'geocoder/lookups/base'
|
|
2
|
+
require 'geocoder/results/telize'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Lookup
|
|
5
|
+
class Telize < Base
|
|
6
|
+
|
|
7
|
+
def name
|
|
8
|
+
"Telize"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def required_api_key_parts
|
|
12
|
+
configuration[:host] ? [] : ["key"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def query_url(query)
|
|
16
|
+
if configuration[:host]
|
|
17
|
+
"#{protocol}://#{configuration[:host]}/geoip/#{query.sanitized_text}"
|
|
18
|
+
else
|
|
19
|
+
"#{protocol}://telize-v1.p.mashape.com/geoip/#{query.sanitized_text}?mashape-key=#{api_key}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def supported_protocols
|
|
24
|
+
[].tap do |array|
|
|
25
|
+
array << :https
|
|
26
|
+
array << :http if configuration[:host]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private # ---------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
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?
|
|
35
|
+
if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
|
|
36
|
+
[]
|
|
37
|
+
else
|
|
38
|
+
[doc]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def empty_result?(doc)
|
|
43
|
+
!doc.is_a?(Hash) or doc.keys == ["ip"]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def reserved_result(ip)
|
|
47
|
+
{"message" => "Input string is not a valid IP address", "code" => 401}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def api_key
|
|
51
|
+
configuration.api_key
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -15,6 +15,10 @@ module Geocoder::Lookup
|
|
|
15
15
|
def query_url(query)
|
|
16
16
|
"#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
def supported_protocols
|
|
20
|
+
[:https]
|
|
21
|
+
end
|
|
18
22
|
|
|
19
23
|
private # ---------------------------------------------------------------
|
|
20
24
|
|
|
@@ -22,9 +26,9 @@ module Geocoder::Lookup
|
|
|
22
26
|
return [] unless doc = fetch_data(query)
|
|
23
27
|
if err = doc['error']
|
|
24
28
|
if err["status"] == 401 and err["message"] == "invalid key"
|
|
25
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
|
29
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
|
|
26
30
|
else
|
|
27
|
-
warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
|
|
31
|
+
Geocoder.log(:warn, "Yandex Geocoding API error: #{err['status']} (#{err['message']}).")
|
|
28
32
|
end
|
|
29
33
|
return []
|
|
30
34
|
end
|
|
@@ -32,7 +36,7 @@ module Geocoder::Lookup
|
|
|
32
36
|
meta = doc['metaDataProperty']['GeocoderResponseMetaData']
|
|
33
37
|
return meta['found'].to_i > 0 ? doc['featureMember'] : []
|
|
34
38
|
else
|
|
35
|
-
warn "Yandex Geocoding API error: unexpected response format."
|
|
39
|
+
Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.")
|
|
36
40
|
return []
|
|
37
41
|
end
|
|
38
42
|
end
|
|
@@ -46,7 +50,7 @@ module Geocoder::Lookup
|
|
|
46
50
|
{
|
|
47
51
|
:geocode => q,
|
|
48
52
|
:format => "json",
|
|
49
|
-
:plng => "#{configuration.language}", # supports ru, uk, be
|
|
53
|
+
:plng => "#{query.language || configuration.language}", # supports ru, uk, be
|
|
50
54
|
:key => configuration.api_key
|
|
51
55
|
}.merge(super)
|
|
52
56
|
end
|
|
@@ -16,7 +16,9 @@ module Geocoder
|
|
|
16
16
|
:longitude => options[:longitude] || :longitude,
|
|
17
17
|
:geocode_block => block,
|
|
18
18
|
:units => options[:units],
|
|
19
|
-
:method => options[:method]
|
|
19
|
+
:method => options[:method],
|
|
20
|
+
:lookup => options[:lookup],
|
|
21
|
+
:language => options[:language]
|
|
20
22
|
)
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -30,8 +32,10 @@ module Geocoder
|
|
|
30
32
|
:latitude => latitude_attr,
|
|
31
33
|
:longitude => longitude_attr,
|
|
32
34
|
:reverse_block => block,
|
|
33
|
-
:units
|
|
34
|
-
:method
|
|
35
|
+
:units => options[:units],
|
|
36
|
+
:method => options[:method],
|
|
37
|
+
:lookup => options[:lookup],
|
|
38
|
+
:language => options[:language]
|
|
35
39
|
)
|
|
36
40
|
end
|
|
37
41
|
|
data/lib/geocoder/models/base.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'geocoder'
|
|
2
|
-
|
|
3
1
|
module Geocoder
|
|
4
2
|
|
|
5
3
|
##
|
|
@@ -29,7 +27,7 @@ module Geocoder
|
|
|
29
27
|
private # ----------------------------------------------------------------
|
|
30
28
|
|
|
31
29
|
def geocoder_init(options)
|
|
32
|
-
unless @geocoder_options
|
|
30
|
+
unless defined?(@geocoder_options)
|
|
33
31
|
@geocoder_options = {}
|
|
34
32
|
require "geocoder/stores/#{geocoder_file_name}"
|
|
35
33
|
include Geocoder::Store.const_get(geocoder_module_name)
|
|
@@ -39,4 +37,3 @@ module Geocoder
|
|
|
39
37
|
end
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
|
-
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'geocoder'
|
|
2
|
-
|
|
3
1
|
module Geocoder
|
|
4
2
|
|
|
5
3
|
##
|
|
@@ -19,7 +17,9 @@ module Geocoder
|
|
|
19
17
|
:geocode_block => block,
|
|
20
18
|
:units => options[:units],
|
|
21
19
|
:method => options[:method],
|
|
22
|
-
:skip_index => options[:skip_index] || false
|
|
20
|
+
:skip_index => options[:skip_index] || false,
|
|
21
|
+
:lookup => options[:lookup],
|
|
22
|
+
:language => options[:language]
|
|
23
23
|
)
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -34,7 +34,9 @@ module Geocoder
|
|
|
34
34
|
:reverse_block => block,
|
|
35
35
|
:units => options[:units],
|
|
36
36
|
:method => options[:method],
|
|
37
|
-
:skip_index => options[:skip_index] || false
|
|
37
|
+
:skip_index => options[:skip_index] || false,
|
|
38
|
+
:lookup => options[:lookup],
|
|
39
|
+
:language => options[:language]
|
|
38
40
|
)
|
|
39
41
|
end
|
|
40
42
|
|
data/lib/geocoder/query.rb
CHANGED
|
@@ -32,10 +32,10 @@ module Geocoder
|
|
|
32
32
|
# appropriate to the Query text.
|
|
33
33
|
#
|
|
34
34
|
def lookup
|
|
35
|
-
if ip_address?
|
|
36
|
-
name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
|
|
35
|
+
if !options[:street_address] and (options[:ip_address] or ip_address?)
|
|
36
|
+
name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
|
|
37
37
|
else
|
|
38
|
-
name = Configuration.lookup || Geocoder::Lookup.street_services.first
|
|
38
|
+
name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
|
|
39
39
|
end
|
|
40
40
|
Lookup.get(name)
|
|
41
41
|
end
|
|
@@ -63,14 +63,14 @@ module Geocoder
|
|
|
63
63
|
# dot-delimited numbers.
|
|
64
64
|
#
|
|
65
65
|
def ip_address?
|
|
66
|
-
|
|
66
|
+
IpAddress.new(text).valid? rescue false
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
##
|
|
70
70
|
# Is the Query text a loopback IP address?
|
|
71
71
|
#
|
|
72
72
|
def loopback_ip_address?
|
|
73
|
-
|
|
73
|
+
ip_address? && IpAddress.new(text).loopback?
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
##
|
|
@@ -98,6 +98,10 @@ module Geocoder
|
|
|
98
98
|
coordinates?
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
def language
|
|
102
|
+
options[:language]
|
|
103
|
+
end
|
|
104
|
+
|
|
101
105
|
private # ----------------------------------------------------------------
|
|
102
106
|
|
|
103
107
|
def params_given?
|
data/lib/geocoder/railtie.rb
CHANGED
data/lib/geocoder/request.rb
CHANGED
|
@@ -1,23 +1,85 @@
|
|
|
1
|
-
require 'geocoder'
|
|
2
|
-
|
|
3
1
|
module Geocoder
|
|
4
2
|
module Request
|
|
5
3
|
|
|
4
|
+
# The location() method is vulnerable to trivial IP spoofing.
|
|
5
|
+
# Don't use it in authorization/authentication code, or any
|
|
6
|
+
# other security-sensitive application. Use safe_location
|
|
7
|
+
# instead.
|
|
6
8
|
def location
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
@location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# This safe_location() protects you from trivial IP spoofing.
|
|
13
|
+
# For requests that go through a proxy that you haven't
|
|
14
|
+
# whitelisted as trusted in your Rack config, you will get the
|
|
15
|
+
# location for the IP of the last untrusted proxy in the chain,
|
|
16
|
+
# not the original client IP. You WILL NOT get the location
|
|
17
|
+
# corresponding to the original client IP for any request sent
|
|
18
|
+
# through a non-whitelisted proxy.
|
|
19
|
+
def safe_location
|
|
20
|
+
@safe_location ||= Geocoder.search(ip, ip_address: true).first
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# There's a whole zoo of nonstandard headers added by various
|
|
24
|
+
# proxy softwares to indicate original client IP.
|
|
25
|
+
# ANY of these can be trivially spoofed!
|
|
26
|
+
# (except REMOTE_ADDR, which should by set by your server,
|
|
27
|
+
# and is included at the end as a fallback.
|
|
28
|
+
# Order does matter: we're following the convention established in
|
|
29
|
+
# ActionDispatch::RemoteIp::GetIp::calculate_ip()
|
|
30
|
+
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
|
|
31
|
+
# where the forwarded_for headers, possibly containing lists,
|
|
32
|
+
# are arbitrarily preferred over headers expected to contain a
|
|
33
|
+
# single address.
|
|
34
|
+
GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR',
|
|
35
|
+
'HTTP_X_FORWARDED',
|
|
36
|
+
'HTTP_FORWARDED_FOR',
|
|
37
|
+
'HTTP_FORWARDED',
|
|
38
|
+
'HTTP_X_CLIENT_IP',
|
|
39
|
+
'HTTP_CLIENT_IP',
|
|
40
|
+
'HTTP_X_REAL_IP',
|
|
41
|
+
'HTTP_X_CLUSTER_CLIENT_IP',
|
|
42
|
+
'REMOTE_ADDR']
|
|
43
|
+
|
|
44
|
+
def geocoder_spoofable_ip
|
|
45
|
+
|
|
46
|
+
# We could use a more sophisticated IP-guessing algorithm here,
|
|
47
|
+
# in which we'd try to resolve the use of different headers by
|
|
48
|
+
# different proxies. The idea is that by comparing IPs repeated
|
|
49
|
+
# in different headers, you can sometimes decide which header
|
|
50
|
+
# was used by a proxy further along in the chain, and thus
|
|
51
|
+
# prefer the headers used earlier. However, the gains might not
|
|
52
|
+
# be worth the performance tradeoff, since this method is likely
|
|
53
|
+
# to be called on every request in a lot of applications.
|
|
54
|
+
GEOCODER_CANDIDATE_HEADERS.each do |header|
|
|
55
|
+
if @env.has_key? header
|
|
56
|
+
addrs = geocoder_split_ip_addresses(@env[header])
|
|
57
|
+
addrs = geocoder_reject_trusted_ip_addresses(addrs)
|
|
58
|
+
return addrs.first if addrs.any?
|
|
14
59
|
end
|
|
15
60
|
end
|
|
16
|
-
|
|
61
|
+
|
|
62
|
+
@env['REMOTE_ADDR']
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def geocoder_split_ip_addresses(ip_addresses)
|
|
68
|
+
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# use Rack's trusted_proxy?() method to filter out IPs that have
|
|
72
|
+
# been configured as trusted; includes private ranges by
|
|
73
|
+
# default. (we don't want every lookup to return the location
|
|
74
|
+
# of our own proxy/load balancer)
|
|
75
|
+
def geocoder_reject_trusted_ip_addresses(ip_addresses)
|
|
76
|
+
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
|
17
77
|
end
|
|
18
78
|
end
|
|
19
79
|
end
|
|
20
80
|
|
|
21
|
-
if defined?(
|
|
22
|
-
|
|
81
|
+
if defined?(ActionDispatch::Request)
|
|
82
|
+
ActionDispatch::Request.__send__(:include, Geocoder::Request)
|
|
83
|
+
elsif defined?(Rack::Request)
|
|
84
|
+
Rack::Request.__send__(:include, Geocoder::Request)
|
|
23
85
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'geocoder/results/base'
|
|
2
|
+
|
|
3
|
+
module Geocoder::Result
|
|
4
|
+
class BaiduIp < Base
|
|
5
|
+
def coordinates
|
|
6
|
+
[point['y'].to_f, point['x'].to_f]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def address
|
|
10
|
+
@data['address']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def state
|
|
14
|
+
province
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def province
|
|
18
|
+
address_detail['province']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def city
|
|
22
|
+
address_detail['city']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def district
|
|
26
|
+
address_detail['district']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def street
|
|
30
|
+
address_detail['street']
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def street_number
|
|
34
|
+
address_detail['street_number']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def state_code
|
|
38
|
+
""
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def postal_code
|
|
42
|
+
""
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def country
|
|
46
|
+
"China"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def country_code
|
|
50
|
+
"CN"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def address_detail
|
|
55
|
+
@data['address_detail']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def point
|
|
59
|
+
@data['point']
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|