geocoder 1.2.6 → 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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +189 -1
- data/LICENSE +1 -1
- data/README.md +387 -755
- data/examples/autoexpire_cache_redis.rb +5 -3
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +17 -16
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +2 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +2 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +1 -1
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +1 -1
- data/lib/generators/geocoder/migration_version.rb +15 -0
- data/lib/geocoder/cache.rb +6 -2
- data/lib/geocoder/calculations.rb +30 -38
- data/lib/geocoder/cli.rb +2 -2
- data/lib/geocoder/configuration.rb +18 -5
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +19 -0
- data/lib/geocoder/ip_address.rb +16 -11
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +31 -12
- data/lib/geocoder/lookups/amap.rb +63 -0
- data/lib/geocoder/lookups/baidu.rb +17 -9
- data/lib/geocoder/lookups/baidu_ip.rb +7 -31
- data/lib/geocoder/lookups/ban_data_gouv_fr.rb +143 -0
- data/lib/geocoder/lookups/base.rb +73 -25
- data/lib/geocoder/lookups/bing.rb +38 -15
- data/lib/geocoder/lookups/db_ip_com.rb +52 -0
- data/lib/geocoder/lookups/dstk.rb +4 -2
- data/lib/geocoder/lookups/esri.rb +55 -8
- data/lib/geocoder/lookups/freegeoip.rb +18 -5
- data/lib/geocoder/lookups/geocoder_ca.rb +5 -6
- data/lib/geocoder/lookups/geocodio.rb +8 -8
- data/lib/geocoder/lookups/geoip2.rb +10 -5
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +37 -9
- data/lib/geocoder/lookups/google_places_details.rb +9 -9
- data/lib/geocoder/lookups/google_places_search.rb +33 -0
- data/lib/geocoder/lookups/google_premier.rb +11 -1
- data/lib/geocoder/lookups/here.rb +29 -23
- data/lib/geocoder/lookups/ip2location.rb +67 -0
- data/lib/geocoder/lookups/ipapi_com.rb +82 -0
- data/lib/geocoder/lookups/ipdata_co.rb +62 -0
- data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
- data/lib/geocoder/lookups/ipregistry.rb +68 -0
- data/lib/geocoder/lookups/ipstack.rb +63 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/location_iq.rb +50 -0
- data/lib/geocoder/lookups/mapbox.rb +59 -0
- data/lib/geocoder/lookups/mapquest.rb +7 -9
- data/lib/geocoder/lookups/maxmind.rb +7 -7
- data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
- data/lib/geocoder/lookups/maxmind_local.rb +9 -2
- data/lib/geocoder/lookups/nominatim.rb +18 -6
- data/lib/geocoder/lookups/opencagedata.rb +16 -9
- data/lib/geocoder/lookups/osmnames.rb +57 -0
- data/lib/geocoder/lookups/pelias.rb +63 -0
- data/lib/geocoder/lookups/pickpoint.rb +41 -0
- data/lib/geocoder/lookups/pointpin.rb +14 -13
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +7 -8
- data/lib/geocoder/lookups/postcodes_io.rb +31 -0
- data/lib/geocoder/lookups/smarty_streets.rb +23 -5
- data/lib/geocoder/lookups/telize.rb +42 -7
- data/lib/geocoder/lookups/tencent.rb +59 -0
- data/lib/geocoder/lookups/yandex.rb +17 -9
- data/lib/geocoder/models/active_record.rb +4 -3
- data/lib/geocoder/models/mongo_base.rb +0 -2
- data/lib/geocoder/query.rb +15 -1
- data/lib/geocoder/railtie.rb +1 -1
- data/lib/geocoder/request.rb +103 -14
- data/lib/geocoder/results/amap.rb +87 -0
- data/lib/geocoder/results/baidu.rb +10 -14
- data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
- data/lib/geocoder/results/base.rb +13 -1
- data/lib/geocoder/results/bing.rb +5 -1
- data/lib/geocoder/results/db_ip_com.rb +58 -0
- data/lib/geocoder/results/esri.rb +30 -6
- data/lib/geocoder/results/freegeoip.rb +2 -7
- data/lib/geocoder/results/geocoder_ca.rb +3 -3
- data/lib/geocoder/results/geocodio.rb +15 -3
- data/lib/geocoder/results/geoip2.rb +37 -25
- data/lib/geocoder/results/geoportail_lu.rb +71 -0
- data/lib/geocoder/results/google.rb +26 -0
- data/lib/geocoder/results/google_places_details.rb +4 -0
- data/lib/geocoder/results/google_places_search.rb +52 -0
- data/lib/geocoder/results/here.rb +21 -1
- data/lib/geocoder/results/ip2location.rb +22 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipdata_co.rb +40 -0
- data/lib/geocoder/results/ipgeolocation.rb +59 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/ipregistry.rb +308 -0
- data/lib/geocoder/results/ipstack.rb +60 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/location_iq.rb +6 -0
- data/lib/geocoder/results/mapbox.rb +57 -0
- data/lib/geocoder/results/mapquest.rb +5 -8
- data/lib/geocoder/results/maxmind.rb +0 -5
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +0 -5
- data/lib/geocoder/results/nominatim.rb +18 -3
- data/lib/geocoder/results/opencagedata.rb +20 -2
- data/lib/geocoder/results/osmnames.rb +56 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pickpoint.rb +6 -0
- data/lib/geocoder/results/pointpin.rb +0 -4
- data/lib/geocoder/results/postcodes_io.rb +40 -0
- data/lib/geocoder/results/smarty_streets.rb +55 -19
- data/lib/geocoder/results/telize.rb +0 -5
- data/lib/geocoder/results/tencent.rb +72 -0
- data/lib/geocoder/results/test.rb +1 -1
- data/lib/geocoder/results/yandex.rb +57 -7
- data/lib/geocoder/sql.rb +9 -6
- data/lib/geocoder/stores/active_record.rb +49 -10
- data/lib/geocoder/stores/base.rb +2 -14
- data/lib/geocoder/stores/mongo_base.rb +0 -31
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +2 -1
- data/lib/hash_recursive_merge.rb +1 -2
- data/lib/maxmind_database.rb +4 -4
- data/lib/tasks/geocoder.rake +29 -4
- metadata +56 -159
- data/.gitignore +0 -6
- data/.travis.yml +0 -31
- data/Rakefile +0 -25
- data/gemfiles/Gemfile.mongoid-2.4.x +0 -16
- data/lib/geocoder/lookups/geocoder_us.rb +0 -39
- data/lib/geocoder/lookups/okf.rb +0 -43
- data/lib/geocoder/lookups/ovi.rb +0 -62
- data/lib/geocoder/lookups/yahoo.rb +0 -88
- data/lib/geocoder/results/geocoder_us.rb +0 -39
- data/lib/geocoder/results/okf.rb +0 -106
- data/lib/geocoder/results/ovi.rb +0 -62
- data/lib/geocoder/results/yahoo.rb +0 -55
- data/lib/oauth_util.rb +0 -112
- data/test/fixtures/baidu_invalid_key +0 -1
- data/test/fixtures/baidu_ip_202_198_16_3 +0 -19
- data/test/fixtures/baidu_ip_invalid_key +0 -1
- data/test/fixtures/baidu_ip_no_results +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/cloudmade_invalid_key +0 -1
- data/test/fixtures/cloudmade_madison_square_garden +0 -1
- data/test/fixtures/cloudmade_no_results +0 -1
- 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/geocodio_1101_pennsylvania_ave +0 -1
- data/test/fixtures/geocodio_bad_api_key +0 -3
- data/test/fixtures/geocodio_invalid +0 -4
- data/test/fixtures/geocodio_no_results +0 -1
- data/test/fixtures/geocodio_over_query_limit +0 -4
- 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/google_places_details_invalid_request +0 -4
- data/test/fixtures/google_places_details_madison_square_garden +0 -120
- data/test/fixtures/google_places_details_no_results +0 -4
- data/test/fixtures/google_places_details_no_reviews +0 -60
- data/test/fixtures/google_places_details_no_types +0 -66
- data/test/fixtures/here_madison_square_garden +0 -72
- data/test/fixtures/here_no_results +0 -8
- 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/nominatim_over_limit +0 -1
- data/test/fixtures/okf_kirstinmaki +0 -67
- data/test/fixtures/okf_no_results +0 -4
- data/test/fixtures/opencagedata_invalid_api_key +0 -25
- data/test/fixtures/opencagedata_invalid_request +0 -26
- data/test/fixtures/opencagedata_madison_square_garden +0 -73
- data/test/fixtures/opencagedata_no_results +0 -29
- data/test/fixtures/opencagedata_over_limit +0 -31
- data/test/fixtures/ovi_madison_square_garden +0 -72
- data/test/fixtures/ovi_no_results +0 -8
- data/test/fixtures/pointpin_10_10_10_10 +0 -1
- data/test/fixtures/pointpin_555_555_555_555 +0 -1
- data/test/fixtures/pointpin_80_111_555_555 +0 -1
- data/test/fixtures/pointpin_no_results +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key +0 -1
- data/test/fixtures/smarty_streets_11211 +0 -1
- data/test/fixtures/smarty_streets_madison_square_garden +0 -47
- data/test/fixtures/smarty_streets_no_results +0 -1
- data/test/fixtures/telize_10_10_10_10 +0 -1
- data/test/fixtures/telize_555_555_555_555 +0 -4
- data/test/fixtures/telize_74_200_247_59 +0 -1
- data/test/fixtures/telize_no_results +0 -1
- 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_canada_rue_dupuis_14 +0 -446
- data/test/fixtures/yandex_invalid_key +0 -1
- data/test/fixtures/yandex_kremlin +0 -48
- data/test/fixtures/yandex_new_york +0 -1
- data/test/fixtures/yandex_no_city_and_town +0 -112
- data/test/fixtures/yandex_no_results +0 -16
- data/test/integration/http_client_test.rb +0 -31
- data/test/mongoid_test_helper.rb +0 -43
- data/test/test_helper.rb +0 -416
- data/test/unit/active_record_test.rb +0 -16
- data/test/unit/cache_test.rb +0 -37
- data/test/unit/calculations_test.rb +0 -220
- data/test/unit/configuration_test.rb +0 -55
- data/test/unit/error_handling_test.rb +0 -56
- data/test/unit/geocoder_test.rb +0 -78
- data/test/unit/https_test.rb +0 -17
- data/test/unit/ip_address_test.rb +0 -27
- data/test/unit/lookup_test.rb +0 -153
- data/test/unit/lookups/bing_test.rb +0 -68
- data/test/unit/lookups/dstk_test.rb +0 -26
- data/test/unit/lookups/esri_test.rb +0 -48
- data/test/unit/lookups/freegeoip_test.rb +0 -27
- data/test/unit/lookups/geocoder_ca_test.rb +0 -17
- data/test/unit/lookups/geocodio_test.rb +0 -55
- data/test/unit/lookups/geoip2_test.rb +0 -27
- data/test/unit/lookups/google_places_details_test.rb +0 -122
- data/test/unit/lookups/google_premier_test.rb +0 -22
- data/test/unit/lookups/google_test.rb +0 -84
- data/test/unit/lookups/mapquest_test.rb +0 -60
- data/test/unit/lookups/maxmind_local_test.rb +0 -28
- data/test/unit/lookups/maxmind_test.rb +0 -63
- data/test/unit/lookups/nominatim_test.rb +0 -31
- data/test/unit/lookups/okf_test.rb +0 -38
- data/test/unit/lookups/opencagedata_test.rb +0 -64
- data/test/unit/lookups/pointpin_test.rb +0 -30
- data/test/unit/lookups/postcode_anywhere_uk_test.rb +0 -70
- data/test/unit/lookups/smarty_streets_test.rb +0 -71
- data/test/unit/lookups/telize_test.rb +0 -36
- data/test/unit/lookups/yahoo_test.rb +0 -35
- data/test/unit/method_aliases_test.rb +0 -26
- data/test/unit/model_test.rb +0 -38
- data/test/unit/mongoid_test.rb +0 -47
- data/test/unit/near_test.rb +0 -87
- data/test/unit/oauth_util_test.rb +0 -31
- data/test/unit/proxy_test.rb +0 -37
- data/test/unit/query_test.rb +0 -52
- data/test/unit/rake_task_test.rb +0 -21
- data/test/unit/request_test.rb +0 -35
- data/test/unit/result_test.rb +0 -72
- data/test/unit/test_mode_test.rb +0 -70
|
@@ -12,19 +12,23 @@ module Geocoder::Lookup
|
|
|
12
12
|
"http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
|
|
15
|
+
def supported_protocols
|
|
16
|
+
[:https]
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
private # ---------------------------------------------------------------
|
|
20
20
|
|
|
21
|
+
def base_query_url(query)
|
|
22
|
+
"#{protocol}://geocode-maps.yandex.ru/1.x/?"
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def results(query)
|
|
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
|
|
@@ -43,12 +47,16 @@ module Geocoder::Lookup
|
|
|
43
47
|
else
|
|
44
48
|
q = query.sanitized_text
|
|
45
49
|
end
|
|
46
|
-
{
|
|
50
|
+
params = {
|
|
47
51
|
:geocode => q,
|
|
48
52
|
:format => "json",
|
|
49
|
-
:
|
|
50
|
-
:
|
|
51
|
-
}
|
|
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)
|
|
52
60
|
end
|
|
53
61
|
end
|
|
54
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
|
-
|
data/lib/geocoder/query.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Geocoder
|
|
|
32
32
|
# appropriate to the Query text.
|
|
33
33
|
#
|
|
34
34
|
def lookup
|
|
35
|
-
if ip_address?
|
|
35
|
+
if !options[:street_address] and (options[:ip_address] or ip_address?)
|
|
36
36
|
name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
|
|
37
37
|
else
|
|
38
38
|
name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
|
|
@@ -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
|
#
|
data/lib/geocoder/railtie.rb
CHANGED
|
@@ -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
|
data/lib/geocoder/request.rb
CHANGED
|
@@ -1,25 +1,114 @@
|
|
|
1
|
+
require 'ipaddr'
|
|
2
|
+
|
|
1
3
|
module Geocoder
|
|
2
4
|
module Request
|
|
3
5
|
|
|
6
|
+
# The location() method is vulnerable to trivial IP spoofing.
|
|
7
|
+
# Don't use it in authorization/authentication code, or any
|
|
8
|
+
# other security-sensitive application. Use safe_location
|
|
9
|
+
# instead.
|
|
4
10
|
def location
|
|
5
|
-
@location ||=
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
@location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# This safe_location() protects you from trivial IP spoofing.
|
|
15
|
+
# For requests that go through a proxy that you haven't
|
|
16
|
+
# whitelisted as trusted in your Rack config, you will get the
|
|
17
|
+
# location for the IP of the last untrusted proxy in the chain,
|
|
18
|
+
# not the original client IP. You WILL NOT get the location
|
|
19
|
+
# corresponding to the original client IP for any request sent
|
|
20
|
+
# through a non-whitelisted proxy.
|
|
21
|
+
def safe_location
|
|
22
|
+
@safe_location ||= Geocoder.search(ip, ip_address: true).first
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# There's a whole zoo of nonstandard headers added by various
|
|
26
|
+
# proxy softwares to indicate original client IP.
|
|
27
|
+
# ANY of these can be trivially spoofed!
|
|
28
|
+
# (except REMOTE_ADDR, which should by set by your server,
|
|
29
|
+
# and is included at the end as a fallback.
|
|
30
|
+
# Order does matter: we're following the convention established in
|
|
31
|
+
# ActionDispatch::RemoteIp::GetIp::calculate_ip()
|
|
32
|
+
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
|
|
33
|
+
# where the forwarded_for headers, possibly containing lists,
|
|
34
|
+
# are arbitrarily preferred over headers expected to contain a
|
|
35
|
+
# single address.
|
|
36
|
+
GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR',
|
|
37
|
+
'HTTP_X_FORWARDED',
|
|
38
|
+
'HTTP_FORWARDED_FOR',
|
|
39
|
+
'HTTP_FORWARDED',
|
|
40
|
+
'HTTP_X_CLIENT_IP',
|
|
41
|
+
'HTTP_CLIENT_IP',
|
|
42
|
+
'HTTP_X_REAL_IP',
|
|
43
|
+
'HTTP_X_CLUSTER_CLIENT_IP',
|
|
44
|
+
'REMOTE_ADDR']
|
|
45
|
+
|
|
46
|
+
def geocoder_spoofable_ip
|
|
47
|
+
|
|
48
|
+
# We could use a more sophisticated IP-guessing algorithm here,
|
|
49
|
+
# in which we'd try to resolve the use of different headers by
|
|
50
|
+
# different proxies. The idea is that by comparing IPs repeated
|
|
51
|
+
# in different headers, you can sometimes decide which header
|
|
52
|
+
# was used by a proxy further along in the chain, and thus
|
|
53
|
+
# prefer the headers used earlier. However, the gains might not
|
|
54
|
+
# be worth the performance tradeoff, since this method is likely
|
|
55
|
+
# to be called on every request in a lot of applications.
|
|
56
|
+
GEOCODER_CANDIDATE_HEADERS.each do |header|
|
|
57
|
+
if @env.has_key? header
|
|
58
|
+
addrs = geocoder_split_ip_addresses(@env[header])
|
|
59
|
+
addrs = geocoder_remove_port_from_addresses(addrs)
|
|
60
|
+
addrs = geocoder_reject_non_ipv4_addresses(addrs)
|
|
61
|
+
addrs = geocoder_reject_trusted_ip_addresses(addrs)
|
|
62
|
+
return addrs.first if addrs.any?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@env['REMOTE_ADDR']
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def geocoder_split_ip_addresses(ip_addresses)
|
|
72
|
+
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# use Rack's trusted_proxy?() method to filter out IPs that have
|
|
76
|
+
# been configured as trusted; includes private ranges by
|
|
77
|
+
# default. (we don't want every lookup to return the location
|
|
78
|
+
# of our own proxy/load balancer)
|
|
79
|
+
def geocoder_reject_trusted_ip_addresses(ip_addresses)
|
|
80
|
+
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
|
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
|
|
13
92
|
else
|
|
14
|
-
|
|
93
|
+
ip
|
|
15
94
|
end
|
|
16
|
-
Geocoder.search(real_ip).first
|
|
17
95
|
end
|
|
18
|
-
|
|
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
|
|
19
109
|
end
|
|
20
110
|
end
|
|
21
111
|
end
|
|
22
112
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
113
|
+
ActionDispatch::Request.__send__(:include, Geocoder::Request) if defined?(ActionDispatch::Request)
|
|
114
|
+
Rack::Request.__send__(:include, Geocoder::Request) if defined?(Rack::Request)
|
|
@@ -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
|
|
@@ -7,38 +7,34 @@ module Geocoder::Result
|
|
|
7
7
|
['lat', 'lng'].map{ |i| @data['location'][i] }
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def address
|
|
11
|
-
@data['formatted_address']
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def state
|
|
15
|
-
province
|
|
16
|
-
end
|
|
17
|
-
|
|
18
10
|
def province
|
|
19
|
-
@data['addressComponent']['province']
|
|
11
|
+
@data['addressComponent'] and @data['addressComponent']['province'] or ""
|
|
20
12
|
end
|
|
21
13
|
|
|
14
|
+
alias_method :state, :province
|
|
15
|
+
|
|
22
16
|
def city
|
|
23
|
-
@data['addressComponent']['city']
|
|
17
|
+
@data['addressComponent'] and @data['addressComponent']['city'] or ""
|
|
24
18
|
end
|
|
25
19
|
|
|
26
20
|
def district
|
|
27
|
-
@data['addressComponent']['district']
|
|
21
|
+
@data['addressComponent'] and @data['addressComponent']['district'] or ""
|
|
28
22
|
end
|
|
29
23
|
|
|
30
24
|
def street
|
|
31
|
-
@data['addressComponent']['street']
|
|
25
|
+
@data['addressComponent'] and @data['addressComponent']['street'] or ""
|
|
32
26
|
end
|
|
33
27
|
|
|
34
28
|
def street_number
|
|
35
|
-
@data['addressComponent']['street_number']
|
|
29
|
+
@data['addressComponent'] and @data['addressComponent']['street_number']
|
|
36
30
|
end
|
|
37
31
|
|
|
38
32
|
def formatted_address
|
|
39
|
-
@data['formatted_address']
|
|
33
|
+
@data['formatted_address'] or ""
|
|
40
34
|
end
|
|
41
35
|
|
|
36
|
+
alias_method :address, :formatted_address
|
|
37
|
+
|
|
42
38
|
def address_components
|
|
43
39
|
@data['addressComponent']
|
|
44
40
|
end
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'geocoder/results/base'
|
|
3
|
+
|
|
4
|
+
module Geocoder::Result
|
|
5
|
+
class BanDataGouvFr < Base
|
|
6
|
+
|
|
7
|
+
#### BASE METHODS ####
|
|
8
|
+
|
|
9
|
+
def self.response_attributes
|
|
10
|
+
%w[limit attribution version licence type features center]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
response_attributes.each do |a|
|
|
14
|
+
unless method_defined?(a)
|
|
15
|
+
define_method a do
|
|
16
|
+
@data[a]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#### BEST RESULT ####
|
|
22
|
+
|
|
23
|
+
def result
|
|
24
|
+
features[0] if features.any?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#### GEOMETRY ####
|
|
28
|
+
|
|
29
|
+
def geometry
|
|
30
|
+
result['geometry'] if result
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def precision
|
|
34
|
+
geometry['type'] if geometry
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def coordinates
|
|
38
|
+
coords = geometry["coordinates"]
|
|
39
|
+
return [coords[1].to_f, coords[0].to_f]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#### PROPERTIES ####
|
|
43
|
+
|
|
44
|
+
# List of raw attrbutes returned by BAN data gouv fr API:
|
|
45
|
+
#
|
|
46
|
+
# :id => [string] UUID of the result, said to be not stable
|
|
47
|
+
# atm, based on IGN reference (Institut national de
|
|
48
|
+
# l'information géographique et forestière)
|
|
49
|
+
#
|
|
50
|
+
# :type => [string] result type (housenumber, street, city,
|
|
51
|
+
# town, village, locality)
|
|
52
|
+
#
|
|
53
|
+
# :score => [float] value between 0 and 1 giving result's
|
|
54
|
+
# relevancy
|
|
55
|
+
#
|
|
56
|
+
# :housenumber => [string] street number and extra information
|
|
57
|
+
# (bis, ter, A, B)
|
|
58
|
+
#
|
|
59
|
+
# :street => [string] street name
|
|
60
|
+
#
|
|
61
|
+
# :name => [string] housenumber and street name
|
|
62
|
+
#
|
|
63
|
+
# :postcode => [string] city post code (used for mails by La Poste,
|
|
64
|
+
# beware many cities got severeal postcodes)
|
|
65
|
+
#
|
|
66
|
+
# :citycode => [string] city code (INSEE reference,
|
|
67
|
+
# consider it as a french institutional UUID)
|
|
68
|
+
#
|
|
69
|
+
# :city => [string] city name
|
|
70
|
+
#
|
|
71
|
+
# :context => [string] department code, department name and
|
|
72
|
+
# region code
|
|
73
|
+
#
|
|
74
|
+
# :label => [string] full address without state, country name
|
|
75
|
+
# and country code
|
|
76
|
+
#
|
|
77
|
+
# CITIES ONLY PROPERTIES
|
|
78
|
+
#
|
|
79
|
+
# :adm_weight => [string] administrative weight (importance) of
|
|
80
|
+
# the city
|
|
81
|
+
#
|
|
82
|
+
# :population => [float] number of inhabitants with a 1000 factor
|
|
83
|
+
#
|
|
84
|
+
# For up to date doc (in french only) : https://adresse.data.gouv.fr/api/
|
|
85
|
+
#
|
|
86
|
+
def properties
|
|
87
|
+
result['properties'] if result
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# List of usable Geocoder results' methods
|
|
91
|
+
#
|
|
92
|
+
# score => [float] result relevance 0 to 1
|
|
93
|
+
#
|
|
94
|
+
# location_id => [string] location's IGN UUID
|
|
95
|
+
#
|
|
96
|
+
# result_type => [string] housenumber / street / city
|
|
97
|
+
# / town / village / locality
|
|
98
|
+
#
|
|
99
|
+
# international_address => [string] full address with country code
|
|
100
|
+
#
|
|
101
|
+
# national_address => [string] full address with country code
|
|
102
|
+
#
|
|
103
|
+
# street_address => [string] housenumber + extra inf
|
|
104
|
+
# + street name
|
|
105
|
+
#
|
|
106
|
+
# street_number => [string] housenumber + extra inf
|
|
107
|
+
# (bis, ter, etc)
|
|
108
|
+
#
|
|
109
|
+
# street_name => [string] street's name
|
|
110
|
+
#
|
|
111
|
+
# city_name => [string] city's name
|
|
112
|
+
#
|
|
113
|
+
# city_code => [string] city's INSEE UUID
|
|
114
|
+
#
|
|
115
|
+
# postal_code => [string] city's postal code (used for mails)
|
|
116
|
+
#
|
|
117
|
+
# context => [string] city's department code, department
|
|
118
|
+
# name and region name
|
|
119
|
+
#
|
|
120
|
+
# demartment_name => [string] city's department name
|
|
121
|
+
#
|
|
122
|
+
# department_code => [string] city's department INSEE UUID
|
|
123
|
+
#
|
|
124
|
+
# region_name => [string] city's region name
|
|
125
|
+
#
|
|
126
|
+
# population => [string] city's inhabitants count
|
|
127
|
+
#
|
|
128
|
+
# administrative_weight => [integer] city's importance on a scale
|
|
129
|
+
# from 6 (capital city) to 1 (regular village)
|
|
130
|
+
#
|
|
131
|
+
def score
|
|
132
|
+
properties['score']
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def location_id
|
|
136
|
+
properties['id']
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Types
|
|
140
|
+
#
|
|
141
|
+
# housenumber
|
|
142
|
+
# street
|
|
143
|
+
# city
|
|
144
|
+
# town
|
|
145
|
+
# village
|
|
146
|
+
# locality
|
|
147
|
+
#
|
|
148
|
+
def result_type
|
|
149
|
+
properties['type']
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def international_address
|
|
153
|
+
"#{national_address}, #{country}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def national_address
|
|
157
|
+
properties['label']
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def street_address
|
|
161
|
+
properties['name']
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def street_number
|
|
165
|
+
properties['housenumber']
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def street_name
|
|
169
|
+
properties['street']
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def city_name
|
|
173
|
+
properties['city']
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def city_code
|
|
177
|
+
properties['citycode']
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def postal_code
|
|
181
|
+
properties['postcode']
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def context
|
|
185
|
+
properties['context'].split(/,/).map(&:strip)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def department_code
|
|
189
|
+
context[0] if context.length > 0
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Monkey logic to handle fact Paris is both a city and a department
|
|
193
|
+
# in Île-de-France region
|
|
194
|
+
def department_name
|
|
195
|
+
if context.length > 1
|
|
196
|
+
if context[1] == "Île-de-France"
|
|
197
|
+
"Paris"
|
|
198
|
+
else
|
|
199
|
+
context[1]
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def region_name
|
|
205
|
+
if context.length == 2 && context[1] == "Île-de-France"
|
|
206
|
+
context[1]
|
|
207
|
+
elsif context.length > 2
|
|
208
|
+
context[2]
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def country
|
|
213
|
+
"France"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Country code types
|
|
217
|
+
# FR : France
|
|
218
|
+
# GF : Guyane Française
|
|
219
|
+
# RE : Réunion
|
|
220
|
+
# NC : Nouvelle-Calédonie
|
|
221
|
+
# GP : Guadeloupe
|
|
222
|
+
# MQ : Martinique
|
|
223
|
+
# MU : Maurice
|
|
224
|
+
# PF : Polynésie française
|
|
225
|
+
#
|
|
226
|
+
# Will need refacto to handle different country codes, but BAN API
|
|
227
|
+
# is currently mainly designed for geocode FR country code addresses
|
|
228
|
+
def country_code
|
|
229
|
+
"FR"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
#### ALIAS METHODS ####
|
|
233
|
+
|
|
234
|
+
alias_method :address, :international_address
|
|
235
|
+
alias_method :street, :street_name
|
|
236
|
+
alias_method :city, :city_name
|
|
237
|
+
alias_method :state, :region_name
|
|
238
|
+
alias_method :state_code, :state
|
|
239
|
+
|
|
240
|
+
#### CITIES' METHODS ####
|
|
241
|
+
|
|
242
|
+
def population
|
|
243
|
+
(properties['population'].to_f * 1000).to_i if city?(result_type)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def administrative_weight
|
|
247
|
+
properties['adm_weight'].to_i if city?(result_type)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def city?(result_type)
|
|
253
|
+
%w(village town city).include?(result_type)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -20,8 +20,20 @@ module Geocoder
|
|
|
20
20
|
##
|
|
21
21
|
# A string in the given format.
|
|
22
22
|
#
|
|
23
|
+
# This default implementation dumbly follows the United States address
|
|
24
|
+
# format and will return incorrect results for most countries. Some APIs
|
|
25
|
+
# return properly formatted addresses and those should be funneled
|
|
26
|
+
# through this method.
|
|
27
|
+
#
|
|
23
28
|
def address(format = :full)
|
|
24
|
-
|
|
29
|
+
if state_code.to_s != ""
|
|
30
|
+
s = ", #{state_code}"
|
|
31
|
+
elsif state.to_s != ""
|
|
32
|
+
s = ", #{state}"
|
|
33
|
+
else
|
|
34
|
+
s = ""
|
|
35
|
+
end
|
|
36
|
+
"#{city}#{s} #{postal_code}, #{country}".sub(/^[ ,]*/, '')
|
|
25
37
|
end
|
|
26
38
|
|
|
27
39
|
##
|