bw-geocoder 1.2.5
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.
- data/.gitignore +6 -0
- data/.travis.yml +31 -0
- data/CHANGELOG.md +377 -0
- data/LICENSE +20 -0
- data/README.md +1041 -0
- data/Rakefile +25 -0
- data/bin/geocode +5 -0
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/autoexpire_cache_redis.rb +28 -0
- data/examples/cache_bypass.rb +48 -0
- data/gemfiles/Gemfile.mongoid-2.4.x +16 -0
- data/lib/generators/geocoder/config/config_generator.rb +14 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
- 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 +90 -0
- data/lib/geocoder/calculations.rb +428 -0
- data/lib/geocoder/cli.rb +121 -0
- data/lib/geocoder/configuration.rb +124 -0
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/exceptions.rb +21 -0
- data/lib/geocoder/ip_address.rb +21 -0
- data/lib/geocoder/lookup.rb +100 -0
- data/lib/geocoder/lookups/baidu.rb +55 -0
- data/lib/geocoder/lookups/baidu_ip.rb +54 -0
- data/lib/geocoder/lookups/base.rb +302 -0
- data/lib/geocoder/lookups/bing.rb +59 -0
- data/lib/geocoder/lookups/dstk.rb +20 -0
- data/lib/geocoder/lookups/esri.rb +48 -0
- data/lib/geocoder/lookups/freegeoip.rb +47 -0
- data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
- data/lib/geocoder/lookups/geocoder_us.rb +39 -0
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/google.rb +67 -0
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_premier.rb +47 -0
- data/lib/geocoder/lookups/here.rb +62 -0
- data/lib/geocoder/lookups/ip_address_labs.rb +43 -0
- data/lib/geocoder/lookups/mapquest.rb +60 -0
- data/lib/geocoder/lookups/maxmind.rb +90 -0
- data/lib/geocoder/lookups/maxmind_local.rb +58 -0
- data/lib/geocoder/lookups/nominatim.rb +52 -0
- data/lib/geocoder/lookups/okf.rb +43 -0
- data/lib/geocoder/lookups/opencagedata.rb +58 -0
- data/lib/geocoder/lookups/ovi.rb +62 -0
- data/lib/geocoder/lookups/pointpin.rb +68 -0
- data/lib/geocoder/lookups/smarty_streets.rb +45 -0
- data/lib/geocoder/lookups/telize.rb +40 -0
- data/lib/geocoder/lookups/test.rb +44 -0
- data/lib/geocoder/lookups/yahoo.rb +88 -0
- data/lib/geocoder/lookups/yandex.rb +54 -0
- data/lib/geocoder/models/active_record.rb +50 -0
- data/lib/geocoder/models/base.rb +39 -0
- data/lib/geocoder/models/mongo_base.rb +64 -0
- data/lib/geocoder/models/mongo_mapper.rb +26 -0
- data/lib/geocoder/models/mongoid.rb +32 -0
- data/lib/geocoder/query.rb +111 -0
- data/lib/geocoder/railtie.rb +26 -0
- data/lib/geocoder/request.rb +25 -0
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/base.rb +67 -0
- data/lib/geocoder/results/bing.rb +48 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/esri.rb +51 -0
- data/lib/geocoder/results/freegeoip.rb +45 -0
- data/lib/geocoder/results/geocoder_ca.rb +60 -0
- data/lib/geocoder/results/geocoder_us.rb +39 -0
- data/lib/geocoder/results/geocodio.rb +66 -0
- data/lib/geocoder/results/google.rb +124 -0
- data/lib/geocoder/results/google_places_details.rb +35 -0
- data/lib/geocoder/results/google_premier.rb +6 -0
- data/lib/geocoder/results/here.rb +62 -0
- data/lib/geocoder/results/ip_address_labs.rb +78 -0
- data/lib/geocoder/results/mapquest.rb +51 -0
- data/lib/geocoder/results/maxmind.rb +135 -0
- data/lib/geocoder/results/maxmind_local.rb +49 -0
- data/lib/geocoder/results/nominatim.rb +94 -0
- data/lib/geocoder/results/okf.rb +106 -0
- data/lib/geocoder/results/opencagedata.rb +82 -0
- data/lib/geocoder/results/ovi.rb +62 -0
- data/lib/geocoder/results/pointpin.rb +44 -0
- data/lib/geocoder/results/smarty_streets.rb +106 -0
- data/lib/geocoder/results/telize.rb +45 -0
- data/lib/geocoder/results/test.rb +33 -0
- data/lib/geocoder/results/yahoo.rb +55 -0
- data/lib/geocoder/results/yandex.rb +84 -0
- data/lib/geocoder/sql.rb +107 -0
- data/lib/geocoder/stores/active_record.rb +278 -0
- data/lib/geocoder/stores/base.rb +127 -0
- data/lib/geocoder/stores/mongo_base.rb +89 -0
- data/lib/geocoder/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder/stores/mongoid.rb +13 -0
- data/lib/geocoder/version.rb +3 -0
- data/lib/geocoder.rb +47 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/maxmind_database.rb +109 -0
- data/lib/oauth_util.rb +112 -0
- data/lib/tasks/geocoder.rake +29 -0
- data/lib/tasks/maxmind.rake +73 -0
- data/test/fixtures/baidu_invalid_key +1 -0
- data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
- data/test/fixtures/baidu_ip_invalid_key +1 -0
- data/test/fixtures/baidu_ip_no_results +1 -0
- data/test/fixtures/baidu_no_results +1 -0
- data/test/fixtures/baidu_reverse +1 -0
- data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
- data/test/fixtures/bing_invalid_key +1 -0
- data/test/fixtures/bing_madison_square_garden +40 -0
- data/test/fixtures/bing_no_results +16 -0
- data/test/fixtures/bing_reverse +42 -0
- data/test/fixtures/cloudmade_invalid_key +1 -0
- data/test/fixtures/cloudmade_madison_square_garden +1 -0
- data/test/fixtures/cloudmade_no_results +1 -0
- data/test/fixtures/esri_madison_square_garden +59 -0
- data/test/fixtures/esri_no_results +8 -0
- data/test/fixtures/esri_reverse +21 -0
- data/test/fixtures/freegeoip_74_200_247_59 +12 -0
- data/test/fixtures/freegeoip_no_results +1 -0
- data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
- data/test/fixtures/geocoder_ca_no_results +1 -0
- data/test/fixtures/geocoder_ca_reverse +34 -0
- data/test/fixtures/geocoder_us_madison_square_garden +1 -0
- data/test/fixtures/geocoder_us_no_results +1 -0
- data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
- data/test/fixtures/geocodio_bad_api_key +3 -0
- data/test/fixtures/geocodio_invalid +4 -0
- data/test/fixtures/geocodio_no_results +1 -0
- data/test/fixtures/geocodio_over_query_limit +4 -0
- data/test/fixtures/google_garbage +456 -0
- data/test/fixtures/google_madison_square_garden +57 -0
- data/test/fixtures/google_no_city_data +44 -0
- data/test/fixtures/google_no_locality +51 -0
- data/test/fixtures/google_no_results +4 -0
- data/test/fixtures/google_over_limit +4 -0
- data/test/fixtures/google_places_details_invalid_request +4 -0
- data/test/fixtures/google_places_details_madison_square_garden +120 -0
- data/test/fixtures/google_places_details_no_results +4 -0
- data/test/fixtures/google_places_details_no_reviews +60 -0
- data/test/fixtures/google_places_details_no_types +66 -0
- data/test/fixtures/here_madison_square_garden +72 -0
- data/test/fixtures/here_no_results +8 -0
- data/test/fixtures/mapquest_error +16 -0
- data/test/fixtures/mapquest_invalid_api_key +16 -0
- data/test/fixtures/mapquest_invalid_request +16 -0
- data/test/fixtures/mapquest_madison_square_garden +52 -0
- data/test/fixtures/mapquest_no_results +16 -0
- data/test/fixtures/maxmind_24_24_24_21 +1 -0
- data/test/fixtures/maxmind_24_24_24_22 +1 -0
- data/test/fixtures/maxmind_24_24_24_23 +1 -0
- data/test/fixtures/maxmind_24_24_24_24 +1 -0
- data/test/fixtures/maxmind_74_200_247_59 +1 -0
- data/test/fixtures/maxmind_invalid_key +1 -0
- data/test/fixtures/maxmind_no_results +1 -0
- data/test/fixtures/nominatim_madison_square_garden +150 -0
- data/test/fixtures/nominatim_no_results +1 -0
- data/test/fixtures/nominatim_over_limit +1 -0
- data/test/fixtures/okf_kirstinmaki +67 -0
- data/test/fixtures/okf_no_results +4 -0
- data/test/fixtures/opencagedata_invalid_api_key +25 -0
- data/test/fixtures/opencagedata_invalid_request +26 -0
- data/test/fixtures/opencagedata_madison_square_garden +73 -0
- data/test/fixtures/opencagedata_no_results +29 -0
- data/test/fixtures/opencagedata_over_limit +31 -0
- data/test/fixtures/ovi_madison_square_garden +72 -0
- data/test/fixtures/ovi_no_results +8 -0
- data/test/fixtures/pointpin_10_10_10_10 +1 -0
- data/test/fixtures/pointpin_555_555_555_555 +1 -0
- data/test/fixtures/pointpin_80_111_555_555 +1 -0
- data/test/fixtures/pointpin_no_results +1 -0
- data/test/fixtures/smarty_streets_11211 +1 -0
- data/test/fixtures/smarty_streets_madison_square_garden +47 -0
- data/test/fixtures/smarty_streets_no_results +1 -0
- data/test/fixtures/telize_10_10_10_10 +1 -0
- data/test/fixtures/telize_555_555_555_555 +4 -0
- data/test/fixtures/telize_74_200_247_59 +1 -0
- data/test/fixtures/telize_no_results +1 -0
- data/test/fixtures/yahoo_error +1 -0
- data/test/fixtures/yahoo_invalid_key +2 -0
- data/test/fixtures/yahoo_madison_square_garden +52 -0
- data/test/fixtures/yahoo_no_results +10 -0
- data/test/fixtures/yahoo_over_limit +2 -0
- data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
- data/test/fixtures/yandex_invalid_key +1 -0
- data/test/fixtures/yandex_kremlin +48 -0
- data/test/fixtures/yandex_new_york +1 -0
- data/test/fixtures/yandex_no_city_and_town +112 -0
- data/test/fixtures/yandex_no_results +16 -0
- data/test/integration/http_client_test.rb +31 -0
- data/test/mongoid_test_helper.rb +43 -0
- data/test/test_helper.rb +386 -0
- data/test/unit/active_record_test.rb +16 -0
- data/test/unit/cache_test.rb +37 -0
- data/test/unit/calculations_test.rb +220 -0
- data/test/unit/configuration_test.rb +55 -0
- data/test/unit/error_handling_test.rb +56 -0
- data/test/unit/geocoder_test.rb +78 -0
- data/test/unit/https_test.rb +17 -0
- data/test/unit/ip_address_test.rb +27 -0
- data/test/unit/lookup_test.rb +153 -0
- data/test/unit/lookups/bing_test.rb +68 -0
- data/test/unit/lookups/dstk_test.rb +26 -0
- data/test/unit/lookups/esri_test.rb +48 -0
- data/test/unit/lookups/freegeoip_test.rb +27 -0
- data/test/unit/lookups/geocoder_ca_test.rb +17 -0
- data/test/unit/lookups/geocodio_test.rb +55 -0
- data/test/unit/lookups/google_places_details_test.rb +122 -0
- data/test/unit/lookups/google_premier_test.rb +22 -0
- data/test/unit/lookups/google_test.rb +84 -0
- data/test/unit/lookups/mapquest_test.rb +60 -0
- data/test/unit/lookups/maxmind_local_test.rb +28 -0
- data/test/unit/lookups/maxmind_test.rb +63 -0
- data/test/unit/lookups/nominatim_test.rb +31 -0
- data/test/unit/lookups/okf_test.rb +38 -0
- data/test/unit/lookups/opencagedata_test.rb +64 -0
- data/test/unit/lookups/pointpin_test.rb +30 -0
- data/test/unit/lookups/smarty_streets_test.rb +71 -0
- data/test/unit/lookups/telize_test.rb +36 -0
- data/test/unit/lookups/yahoo_test.rb +35 -0
- data/test/unit/method_aliases_test.rb +26 -0
- data/test/unit/model_test.rb +38 -0
- data/test/unit/mongoid_test.rb +47 -0
- data/test/unit/near_test.rb +87 -0
- data/test/unit/oauth_util_test.rb +31 -0
- data/test/unit/proxy_test.rb +37 -0
- data/test/unit/query_test.rb +52 -0
- data/test/unit/rake_task_test.rb +21 -0
- data/test/unit/request_test.rb +35 -0
- data/test/unit/result_test.rb +72 -0
- data/test/unit/test_mode_test.rb +70 -0
- metadata +281 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/geocoder_ca"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class GeocoderCa < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Geocoder.ca"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
"#{protocol}://geocoder.ca/?" + url_query_string(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
private # ---------------------------------------------------------------
|
16
|
+
|
17
|
+
def results(query)
|
18
|
+
return [] unless doc = fetch_data(query)
|
19
|
+
if doc['error'].nil?
|
20
|
+
return [doc]
|
21
|
+
elsif doc['error']['code'] == "005"
|
22
|
+
# "Postal Code is not in the proper Format" => no results, just shut up
|
23
|
+
else
|
24
|
+
warn "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']})."
|
25
|
+
end
|
26
|
+
return []
|
27
|
+
end
|
28
|
+
|
29
|
+
def query_url_params(query)
|
30
|
+
params = {
|
31
|
+
:geoit => "xml",
|
32
|
+
:jsonp => 1,
|
33
|
+
:callback => "test",
|
34
|
+
:auth => configuration.api_key
|
35
|
+
}.merge(super)
|
36
|
+
if query.reverse_geocode?
|
37
|
+
lat,lon = query.coordinates
|
38
|
+
params[:latt] = lat
|
39
|
+
params[:longt] = lon
|
40
|
+
params[:corner] = 1
|
41
|
+
params[:reverse] = 1
|
42
|
+
else
|
43
|
+
params[:locate] = query.sanitized_text
|
44
|
+
params[:showpostal] = 1
|
45
|
+
end
|
46
|
+
params
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_raw_data(raw_data)
|
50
|
+
super raw_data[/^test\((.*)\)\;\s*$/, 1]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/geocoder_us"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class GeocoderUs < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Geocoder.us"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
if configuration.api_key
|
13
|
+
"http://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
|
14
|
+
else
|
15
|
+
"http://geocoder.us/service/csv/geocode?" + url_query_string(query)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
if doc[0].to_s =~ /^(\d+)\:/
|
24
|
+
return []
|
25
|
+
else
|
26
|
+
return [doc.size == 5 ? ((doc[0..1] << nil) + doc[2..4]) : doc]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def query_url_params(query)
|
31
|
+
(query.text =~ /^\d{5}(?:-\d{4})?$/ ? {:zip => query} : {:address => query.sanitized_text}).merge(super)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_raw_data(raw_data)
|
35
|
+
raw_data.chomp.split(',')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/geocodio"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Geocodio < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Geocodio"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
path = query.reverse_geocode? ? "reverse" : "geocode"
|
13
|
+
"#{protocol}://api.geocod.io/v1/#{path}?#{url_query_string(query)}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def results(query)
|
17
|
+
return [] unless doc = fetch_data(query)
|
18
|
+
return doc["results"] if doc['error'].nil?
|
19
|
+
|
20
|
+
if doc['error'] == 'Invalid API key'
|
21
|
+
raise_error(Geocoder::InvalidApiKey) ||
|
22
|
+
warn("Geocodio service error: invalid API key.")
|
23
|
+
elsif doc['error'].match(/You have reached your daily maximum/)
|
24
|
+
raise_error(Geocoder::OverQueryLimitError, doc['error']) ||
|
25
|
+
warn("Geocodio service error: #{doc['error']}.")
|
26
|
+
else
|
27
|
+
raise_error(Geocoder::InvalidRequest, doc['error']) ||
|
28
|
+
warn("Geocodio service error: #{doc['error']}.")
|
29
|
+
end
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
private # ---------------------------------------------------------------
|
34
|
+
|
35
|
+
def query_url_params(query)
|
36
|
+
{
|
37
|
+
:api_key => configuration.api_key,
|
38
|
+
:q => query.sanitized_text
|
39
|
+
}.merge(super)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/google"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Google < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Google"
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_link_url(coordinates)
|
12
|
+
"http://maps.google.com/maps?q=#{coordinates.join(',')}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def valid_response?(response)
|
22
|
+
status = parse_json(response.body)["status"]
|
23
|
+
super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
|
24
|
+
end
|
25
|
+
|
26
|
+
def results(query)
|
27
|
+
return [] unless doc = fetch_data(query)
|
28
|
+
case doc['status']; when "OK" # OK status implies >0 results
|
29
|
+
return doc['results']
|
30
|
+
when "OVER_QUERY_LIMIT"
|
31
|
+
raise_error(Geocoder::OverQueryLimitError) ||
|
32
|
+
warn("Google Geocoding API error: over query limit.")
|
33
|
+
when "REQUEST_DENIED"
|
34
|
+
raise_error(Geocoder::RequestDenied) ||
|
35
|
+
warn("Google Geocoding API error: request denied.")
|
36
|
+
when "INVALID_REQUEST"
|
37
|
+
raise_error(Geocoder::InvalidRequest) ||
|
38
|
+
warn("Google Geocoding API error: invalid request.")
|
39
|
+
end
|
40
|
+
return []
|
41
|
+
end
|
42
|
+
|
43
|
+
def query_url_google_params(query)
|
44
|
+
params = {
|
45
|
+
(query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
|
46
|
+
:sensor => "false",
|
47
|
+
:language => (query.language || configuration.language)
|
48
|
+
}
|
49
|
+
unless (bounds = query.options[:bounds]).nil?
|
50
|
+
params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
|
51
|
+
end
|
52
|
+
unless (region = query.options[:region]).nil?
|
53
|
+
params[:region] = region
|
54
|
+
end
|
55
|
+
unless (components = query.options[:components]).nil?
|
56
|
+
params[:components] = components.is_a?(Array) ? components.join("|") : components
|
57
|
+
end
|
58
|
+
params
|
59
|
+
end
|
60
|
+
|
61
|
+
def query_url_params(query)
|
62
|
+
query_url_google_params(query).merge(
|
63
|
+
:key => configuration.api_key
|
64
|
+
).merge(super)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "geocoder/lookups/google"
|
2
|
+
require "geocoder/results/google_places_details"
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
module Lookup
|
6
|
+
class GooglePlacesDetails < Google
|
7
|
+
def name
|
8
|
+
"Google Places Details"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
["key"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def use_ssl?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_url(query)
|
20
|
+
"#{protocol}://maps.googleapis.com/maps/api/place/details/json?#{url_query_string(query)}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def results(query)
|
26
|
+
return [] unless doc = fetch_data(query)
|
27
|
+
|
28
|
+
case doc["status"]
|
29
|
+
when "OK"
|
30
|
+
return [doc["result"]]
|
31
|
+
when "OVER_QUERY_LIMIT"
|
32
|
+
raise_error(Geocoder::OverQueryLimitError) || warn("Google Places Details API error: over query limit.")
|
33
|
+
when "REQUEST_DENIED"
|
34
|
+
raise_error(Geocoder::RequestDenied) || warn("Google Places Details API error: request denied.")
|
35
|
+
when "INVALID_REQUEST"
|
36
|
+
raise_error(Geocoder::InvalidRequest) || warn("Google Places Details API error: invalid request.")
|
37
|
+
end
|
38
|
+
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
def query_url_google_params(query)
|
43
|
+
{
|
44
|
+
placeid: query.text,
|
45
|
+
language: query.language || configuration.language
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
require 'geocoder/lookups/google'
|
4
|
+
require 'geocoder/results/google_premier'
|
5
|
+
|
6
|
+
module Geocoder::Lookup
|
7
|
+
class GooglePremier < Google
|
8
|
+
|
9
|
+
def name
|
10
|
+
"Google Premier"
|
11
|
+
end
|
12
|
+
|
13
|
+
def required_api_key_parts
|
14
|
+
["private key", "client", "channel"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_url(query)
|
18
|
+
path = "/maps/api/geocode/json?" + url_query_string(query)
|
19
|
+
"#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}"
|
20
|
+
end
|
21
|
+
|
22
|
+
private # ---------------------------------------------------------------
|
23
|
+
|
24
|
+
def query_url_params(query)
|
25
|
+
query_url_google_params(query).merge(super).merge(
|
26
|
+
:key => nil, # don't use param inherited from Google lookup
|
27
|
+
:client => configuration.api_key[1],
|
28
|
+
:channel => configuration.api_key[2]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def sign(string)
|
33
|
+
raw_private_key = url_safe_base64_decode(configuration.api_key[0])
|
34
|
+
digest = OpenSSL::Digest.new('sha1')
|
35
|
+
raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
|
36
|
+
url_safe_base64_encode(raw_signature)
|
37
|
+
end
|
38
|
+
|
39
|
+
def url_safe_base64_decode(base64_string)
|
40
|
+
Base64.decode64(base64_string.tr('-_', '+/'))
|
41
|
+
end
|
42
|
+
|
43
|
+
def url_safe_base64_encode(raw)
|
44
|
+
Base64.encode64(raw).tr('+/', '-_').strip
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/here'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Here < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Here"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
return [] unless doc['Response'] && doc['Response']['View']
|
24
|
+
if r=doc['Response']['View']
|
25
|
+
return [] if r.nil? || !r.is_a?(Array) || r.empty?
|
26
|
+
return r.first['Result']
|
27
|
+
end
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_url_params(query)
|
32
|
+
options = {
|
33
|
+
:gen=>4,
|
34
|
+
:app_id=>api_key,
|
35
|
+
:app_code=>api_code
|
36
|
+
}
|
37
|
+
|
38
|
+
if query.reverse_geocode?
|
39
|
+
super.merge(options).merge(
|
40
|
+
:prox=>query.sanitized_text,
|
41
|
+
:mode=>:retrieveAddresses
|
42
|
+
)
|
43
|
+
else
|
44
|
+
super.merge(options).merge(
|
45
|
+
:searchtext=>query.sanitized_text
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def api_key
|
51
|
+
if a=configuration.api_key
|
52
|
+
return a.first if a.is_a?(Array)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def api_code
|
57
|
+
if a=configuration.api_key
|
58
|
+
return a.last if a.is_a?(Array)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/ip_address_labs'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class IpAddressLabs < Base
|
7
|
+
|
8
|
+
def name
|
9
|
+
"IpAddressLabs"
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_url(query)
|
13
|
+
"#{protocol}://api.ipaddresslabs.com/iplocation/v1.7/locateip?key=#{configuration[:api_key]}&ip=#{query}&format=JSON"
|
14
|
+
end
|
15
|
+
|
16
|
+
def reserved_result
|
17
|
+
{
|
18
|
+
"city" => "New York",
|
19
|
+
"region_code" => "NY",
|
20
|
+
"latitude" => 40.7127837,
|
21
|
+
"longitude" => -74.0059413
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
private # ---------------------------------------------------------------
|
26
|
+
|
27
|
+
|
28
|
+
def results(query)
|
29
|
+
# don't look up a loopback address, just return the stored result
|
30
|
+
return [reserved_result] if query.loopback_ip_address?
|
31
|
+
doc = fetch_data(query)
|
32
|
+
status = doc["query_status"]["query_status_code"]
|
33
|
+
message = doc["query_status"]["query_status_description"]
|
34
|
+
|
35
|
+
if status == "OK"
|
36
|
+
return [ doc["geolocation_data"] ]
|
37
|
+
else
|
38
|
+
warn("IP Address Labs API Error: #{message}")
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'geocoder/lookups/base'
|
3
|
+
require "geocoder/results/mapquest"
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class Mapquest < Base
|
7
|
+
|
8
|
+
def name
|
9
|
+
"Mapquest"
|
10
|
+
end
|
11
|
+
|
12
|
+
def required_api_key_parts
|
13
|
+
["key"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def query_url(query)
|
17
|
+
domain = configuration[:open] ? "open" : "www"
|
18
|
+
version = configuration[:version] || 1
|
19
|
+
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
|
20
|
+
url + url_query_string(query)
|
21
|
+
end
|
22
|
+
|
23
|
+
private # ---------------------------------------------------------------
|
24
|
+
|
25
|
+
def search_type(query)
|
26
|
+
query.reverse_geocode? ? "reverse" : "address"
|
27
|
+
end
|
28
|
+
|
29
|
+
def query_url_params(query)
|
30
|
+
params = { :location => query.sanitized_text }
|
31
|
+
if key = configuration.api_key
|
32
|
+
params[:key] = CGI.unescape(key)
|
33
|
+
end
|
34
|
+
params.merge(super)
|
35
|
+
end
|
36
|
+
|
37
|
+
# http://www.mapquestapi.com/geocoding/status_codes.html
|
38
|
+
# http://open.mapquestapi.com/geocoding/status_codes.html
|
39
|
+
def results(query)
|
40
|
+
return [] unless doc = fetch_data(query)
|
41
|
+
return doc["results"][0]['locations'] if doc['info']['statuscode'] == 0 # A successful geocode call
|
42
|
+
|
43
|
+
messages = doc['info']['messages'].join
|
44
|
+
|
45
|
+
case doc['info']['statuscode']
|
46
|
+
when 400 # Error with input
|
47
|
+
raise_error(Geocoder::InvalidRequest, messages) ||
|
48
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
49
|
+
when 403 # Key related error
|
50
|
+
raise_error(Geocoder::InvalidApiKey, messages) ||
|
51
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
52
|
+
when 500 # Unknown error
|
53
|
+
raise_error(Geocoder::Error, messages) ||
|
54
|
+
warn("Mapquest Geocoding API error: #{messages}")
|
55
|
+
end
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/maxmind'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class Maxmind < Base
|
7
|
+
|
8
|
+
def name
|
9
|
+
"MaxMind"
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_url(query)
|
13
|
+
"#{protocol}://geoip.maxmind.com/#{service_code}?" + url_query_string(query)
|
14
|
+
end
|
15
|
+
|
16
|
+
private # ---------------------------------------------------------------
|
17
|
+
|
18
|
+
##
|
19
|
+
# Return the name of the configured service, or raise an exception.
|
20
|
+
#
|
21
|
+
def configured_service!
|
22
|
+
if s = configuration[:service] and services.keys.include?(s)
|
23
|
+
return s
|
24
|
+
else
|
25
|
+
raise(
|
26
|
+
Geocoder::ConfigurationError,
|
27
|
+
"When using MaxMind you MUST specify a service name: " +
|
28
|
+
"Geocoder.configure(:maxmind => {:service => ...}), " +
|
29
|
+
"where '...' is one of: #{services.keys.inspect}"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def service_code
|
35
|
+
services[configured_service!]
|
36
|
+
end
|
37
|
+
|
38
|
+
def service_response_fields_count
|
39
|
+
Geocoder::Result::Maxmind.field_names[configured_service!].size
|
40
|
+
end
|
41
|
+
|
42
|
+
def data_contains_error?(parsed_data)
|
43
|
+
# if all fields given then there is an error
|
44
|
+
parsed_data.size == service_response_fields_count and !parsed_data.last.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Service names mapped to code used in URL.
|
49
|
+
#
|
50
|
+
def services
|
51
|
+
{
|
52
|
+
:country => "a",
|
53
|
+
:city => "b",
|
54
|
+
:city_isp_org => "f",
|
55
|
+
:omni => "e"
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def results(query)
|
60
|
+
# don't look up a loopback address, just return the stored result
|
61
|
+
return [reserved_result] if query.loopback_ip_address?
|
62
|
+
doc = fetch_data(query)
|
63
|
+
if doc and doc.is_a?(Array)
|
64
|
+
if !data_contains_error?(doc)
|
65
|
+
return [doc]
|
66
|
+
elsif doc.last == "INVALID_LICENSE_KEY"
|
67
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid MaxMind API key.")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return []
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_raw_data(raw_data)
|
74
|
+
# Maxmind just returns text/plain as csv format but according to documentation,
|
75
|
+
# we get ISO-8859-1 encoded string. We need to convert it.
|
76
|
+
CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
|
77
|
+
end
|
78
|
+
|
79
|
+
def reserved_result
|
80
|
+
",,,,0,0,0,0,,,".split(",")
|
81
|
+
end
|
82
|
+
|
83
|
+
def query_url_params(query)
|
84
|
+
{
|
85
|
+
:l => configuration.api_key,
|
86
|
+
:i => query.sanitized_text
|
87
|
+
}.merge(super)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'geocoder/lookups/base'
|
3
|
+
require 'geocoder/results/maxmind_local'
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class MaxmindLocal < Base
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
if !configuration[:file].nil?
|
10
|
+
begin
|
11
|
+
gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip'
|
12
|
+
require gem
|
13
|
+
rescue LoadError
|
14
|
+
raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
"MaxMind Local"
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_api_key_parts
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def results(query)
|
31
|
+
if configuration[:file]
|
32
|
+
geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP
|
33
|
+
result = geoip_class.new(configuration[:file]).city(query.to_s)
|
34
|
+
result.nil? ? [] : [result.to_hash]
|
35
|
+
elsif configuration[:package] == :city
|
36
|
+
addr = IPAddr.new(query.text).to_i
|
37
|
+
q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
|
38
|
+
FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
|
39
|
+
WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num LIMIT 1)"
|
40
|
+
format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
|
41
|
+
elsif configuration[:package] == :country
|
42
|
+
addr = IPAddr.new(query.text).to_i
|
43
|
+
q = "SELECT country, country_code FROM maxmind_geolite_country
|
44
|
+
WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
|
45
|
+
format_result(q, [:country_name, :country_code2])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def format_result(query, attr_names)
|
50
|
+
if r = ActiveRecord::Base.connection.execute(query).first
|
51
|
+
r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
|
52
|
+
[Hash[*attr_names.zip(r).flatten]]
|
53
|
+
else
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/nominatim"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Nominatim < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Nominatim"
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_link_url(coordinates)
|
12
|
+
"http://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
method = query.reverse_geocode? ? "reverse" : "search"
|
17
|
+
host = configuration[:host] || "nominatim.openstreetmap.org"
|
18
|
+
"#{protocol}://#{host}/#{method}?" + url_query_string(query)
|
19
|
+
end
|
20
|
+
|
21
|
+
private # ---------------------------------------------------------------
|
22
|
+
|
23
|
+
def results(query)
|
24
|
+
return [] unless doc = fetch_data(query)
|
25
|
+
doc.is_a?(Array) ? doc : [doc]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_raw_data(raw_data)
|
29
|
+
if raw_data.include?("Bandwidth limit exceeded")
|
30
|
+
raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
|
31
|
+
else
|
32
|
+
super(raw_data)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def query_url_params(query)
|
37
|
+
params = {
|
38
|
+
:format => "json",
|
39
|
+
:addressdetails => "1",
|
40
|
+
:"accept-language" => (query.language || configuration.language)
|
41
|
+
}.merge(super)
|
42
|
+
if query.reverse_geocode?
|
43
|
+
lat,lon = query.coordinates
|
44
|
+
params[:lat] = lat
|
45
|
+
params[:lon] = lon
|
46
|
+
else
|
47
|
+
params[:q] = query.sanitized_text
|
48
|
+
end
|
49
|
+
params
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|