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,43 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/okf"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Okf < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Okf"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
"#{protocol}://data.okf.fi/gis/1/geocode/json?" + url_query_string(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
private # ---------------------------------------------------------------
|
16
|
+
|
17
|
+
def valid_response?(response)
|
18
|
+
status = parse_json(response.body)["status"]
|
19
|
+
super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
|
20
|
+
end
|
21
|
+
|
22
|
+
def results(query)
|
23
|
+
return [] unless doc = fetch_data(query)
|
24
|
+
case doc['status']; when "OK" # OK status implies >0 results
|
25
|
+
return doc['results']
|
26
|
+
end
|
27
|
+
return []
|
28
|
+
end
|
29
|
+
|
30
|
+
def query_url_okf_params(query)
|
31
|
+
params = {
|
32
|
+
(query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
|
33
|
+
:sensor => "false",
|
34
|
+
:language => (query.language || configuration.language)
|
35
|
+
}
|
36
|
+
params
|
37
|
+
end
|
38
|
+
|
39
|
+
def query_url_params(query)
|
40
|
+
query_url_okf_params(query).merge(super)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -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
|
+
warn("Opencagedata Geocoding API error: #{messages}")
|
30
|
+
when 403 # Key related error
|
31
|
+
raise_error(Geocoder::InvalidApiKey, messages) ||
|
32
|
+
warn("Opencagedata Geocoding API error: #{messages}")
|
33
|
+
when 402 # Quata Exceeded
|
34
|
+
raise_error(Geocoder::OverQueryLimitError, messages) ||
|
35
|
+
warn("Opencagedata Geocoding API error: #{messages}")
|
36
|
+
when 500 # Unknown error
|
37
|
+
raise_error(Geocoder::Error, messages) ||
|
38
|
+
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,62 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/ovi'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Ovi < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Ovi"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://lbs.ovi.com/search/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=>1,
|
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,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) || warn("Invalid Pointpin request.")
|
32
|
+
when "Invalid API key"
|
33
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid Pointpin API key.")
|
34
|
+
when "Address not found"
|
35
|
+
warn("Address not found.")
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise_error(Geocoder::Error) || 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,45 @@
|
|
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
|
+
path = zipcode_only?(query) ? "zipcode" : "street-address"
|
16
|
+
"#{protocol}://api.smartystreets.com/#{path}?#{url_query_string(query)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def zipcode_only?(query)
|
22
|
+
!query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
|
23
|
+
end
|
24
|
+
|
25
|
+
def query_url_params(query)
|
26
|
+
params = {}
|
27
|
+
if zipcode_only?(query)
|
28
|
+
params[:zipcode] = query.sanitized_text
|
29
|
+
else
|
30
|
+
params[:street] = query.sanitized_text
|
31
|
+
end
|
32
|
+
if configuration.api_key.is_a?(Array)
|
33
|
+
params[:"auth-id"] = configuration.api_key[0]
|
34
|
+
params[:"auth-token"] = configuration.api_key[1]
|
35
|
+
else
|
36
|
+
params[:"auth-token"] = configuration.api_key
|
37
|
+
end
|
38
|
+
params.merge(super)
|
39
|
+
end
|
40
|
+
|
41
|
+
def results(query)
|
42
|
+
fetch_data(query) || []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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 query_url(query)
|
12
|
+
#currently doesn't support HTTPS
|
13
|
+
"http://www.telize.com/geoip/#{query.sanitized_text}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private # ---------------------------------------------------------------
|
17
|
+
|
18
|
+
def use_ssl?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def results(query)
|
23
|
+
# don't look up a loopback address, just return the stored result
|
24
|
+
return [reserved_result(query.text)] if query.loopback_ip_address?
|
25
|
+
if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
|
26
|
+
[]
|
27
|
+
else
|
28
|
+
[doc]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def empty_result?(doc)
|
33
|
+
!doc.is_a?(Hash) or doc.keys == ["ip"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def reserved_result(ip)
|
37
|
+
{"message" => "Input string is not a valid IP address", "code" => 401}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/test'
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
module Lookup
|
6
|
+
class Test < Base
|
7
|
+
|
8
|
+
def name
|
9
|
+
"Test"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.add_stub(query_text, results)
|
13
|
+
stubs[query_text] = results
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.set_default_stub(results)
|
17
|
+
@default_stub = results
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.read_stub(query_text)
|
21
|
+
stubs.fetch(query_text) {
|
22
|
+
return @default_stub unless @default_stub.nil?
|
23
|
+
raise ArgumentError, "unknown stub request #{query_text}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.stubs
|
28
|
+
@stubs ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.reset
|
32
|
+
@stubs = {}
|
33
|
+
@default_stub = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def results(query)
|
39
|
+
Geocoder::Lookup::Test.read_stub(query.text)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/yahoo"
|
3
|
+
require 'oauth_util'
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class Yahoo < Base
|
7
|
+
|
8
|
+
def name
|
9
|
+
"Yahoo BOSS"
|
10
|
+
end
|
11
|
+
|
12
|
+
def map_link_url(coordinates)
|
13
|
+
"http://maps.yahoo.com/#lat=#{coordinates[0]}&lon=#{coordinates[1]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def required_api_key_parts
|
17
|
+
["consumer key", "consumer secret"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def query_url(query)
|
21
|
+
parsed_url = URI.parse(raw_url(query))
|
22
|
+
o = OauthUtil.new
|
23
|
+
o.consumer_key = configuration.api_key[0]
|
24
|
+
o.consumer_secret = configuration.api_key[1]
|
25
|
+
base_url + o.sign(parsed_url).query_string
|
26
|
+
end
|
27
|
+
|
28
|
+
private # ---------------------------------------------------------------
|
29
|
+
|
30
|
+
def results(query)
|
31
|
+
return [] unless doc = fetch_data(query)
|
32
|
+
doc = doc['bossresponse']
|
33
|
+
if doc['responsecode'].to_i == 200
|
34
|
+
if doc['placefinder']['count'].to_i > 0
|
35
|
+
return doc['placefinder']['results']
|
36
|
+
else
|
37
|
+
return []
|
38
|
+
end
|
39
|
+
else
|
40
|
+
warn "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']})."
|
41
|
+
return []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Yahoo returns errors as XML even when JSON format is specified.
|
47
|
+
# Handle that here, without parsing the XML
|
48
|
+
# (which would add unnecessary complexity).
|
49
|
+
# Yahoo auth errors can also be cryptic, so add raw error desc
|
50
|
+
# to warning message.
|
51
|
+
#
|
52
|
+
def parse_raw_data(raw_data)
|
53
|
+
if raw_data.match(/^<\?xml/)
|
54
|
+
if raw_data.include?("Rate Limit Exceeded")
|
55
|
+
raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
|
56
|
+
elsif raw_data =~ /<yahoo:description>(Please provide valid credentials.*)<\/yahoo:description>/i
|
57
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key. Error response: #{$1}")
|
58
|
+
end
|
59
|
+
else
|
60
|
+
super(raw_data)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def query_url_params(query)
|
65
|
+
lang = (query.language || configuration.language).to_s
|
66
|
+
lang += '_US' if lang == 'en'
|
67
|
+
{
|
68
|
+
:location => query.sanitized_text,
|
69
|
+
:flags => "JXTSR",
|
70
|
+
:gflags => "AC#{'R' if query.reverse_geocode?}",
|
71
|
+
:locale => lang,
|
72
|
+
:appid => configuration.api_key
|
73
|
+
}.merge(super)
|
74
|
+
end
|
75
|
+
|
76
|
+
def cache_key(query)
|
77
|
+
raw_url(query)
|
78
|
+
end
|
79
|
+
|
80
|
+
def base_url
|
81
|
+
"#{protocol}://yboss.yahooapis.com/geo/placefinder?"
|
82
|
+
end
|
83
|
+
|
84
|
+
def raw_url(query)
|
85
|
+
base_url + url_query_string(query)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/yandex"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Yandex < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Yandex"
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_link_url(coordinates)
|
12
|
+
"http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
if err = doc['error']
|
24
|
+
if err["status"] == 401 and err["message"] == "invalid key"
|
25
|
+
raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
|
26
|
+
else
|
27
|
+
warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
|
28
|
+
end
|
29
|
+
return []
|
30
|
+
end
|
31
|
+
if doc = doc['response']['GeoObjectCollection']
|
32
|
+
meta = doc['metaDataProperty']['GeocoderResponseMetaData']
|
33
|
+
return meta['found'].to_i > 0 ? doc['featureMember'] : []
|
34
|
+
else
|
35
|
+
warn "Yandex Geocoding API error: unexpected response format."
|
36
|
+
return []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def query_url_params(query)
|
41
|
+
if query.reverse_geocode?
|
42
|
+
q = query.coordinates.reverse.join(",")
|
43
|
+
else
|
44
|
+
q = query.sanitized_text
|
45
|
+
end
|
46
|
+
{
|
47
|
+
:geocode => q,
|
48
|
+
:format => "json",
|
49
|
+
:plng => "#{query.language || configuration.language}", # supports ru, uk, be
|
50
|
+
:key => configuration.api_key
|
51
|
+
}.merge(super)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'geocoder/models/base'
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
module Model
|
5
|
+
module ActiveRecord
|
6
|
+
include Base
|
7
|
+
|
8
|
+
##
|
9
|
+
# Set attribute names and include the Geocoder module.
|
10
|
+
#
|
11
|
+
def geocoded_by(address_attr, options = {}, &block)
|
12
|
+
geocoder_init(
|
13
|
+
:geocode => true,
|
14
|
+
:user_address => address_attr,
|
15
|
+
:latitude => options[:latitude] || :latitude,
|
16
|
+
:longitude => options[:longitude] || :longitude,
|
17
|
+
:geocode_block => block,
|
18
|
+
:units => options[:units],
|
19
|
+
:method => options[:method],
|
20
|
+
:lookup => options[:lookup],
|
21
|
+
:language => options[:language]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Set attribute names and include the Geocoder module.
|
27
|
+
#
|
28
|
+
def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
|
29
|
+
geocoder_init(
|
30
|
+
:reverse_geocode => true,
|
31
|
+
:fetched_address => options[:address] || :address,
|
32
|
+
:latitude => latitude_attr,
|
33
|
+
:longitude => longitude_attr,
|
34
|
+
:reverse_block => block,
|
35
|
+
:units => options[:units],
|
36
|
+
:method => options[:method],
|
37
|
+
:lookup => options[:lookup],
|
38
|
+
:language => options[:language]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
private # --------------------------------------------------------------
|
44
|
+
|
45
|
+
def geocoder_file_name; "active_record"; end
|
46
|
+
def geocoder_module_name; "ActiveRecord"; end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Geocoder
|
2
|
+
|
3
|
+
##
|
4
|
+
# Methods for invoking Geocoder in a model.
|
5
|
+
#
|
6
|
+
module Model
|
7
|
+
module Base
|
8
|
+
|
9
|
+
def geocoder_options
|
10
|
+
if defined?(@geocoder_options)
|
11
|
+
@geocoder_options
|
12
|
+
elsif superclass.respond_to?(:geocoder_options)
|
13
|
+
superclass.geocoder_options || { }
|
14
|
+
else
|
15
|
+
{ }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def geocoded_by
|
20
|
+
fail
|
21
|
+
end
|
22
|
+
|
23
|
+
def reverse_geocoded_by
|
24
|
+
fail
|
25
|
+
end
|
26
|
+
|
27
|
+
private # ----------------------------------------------------------------
|
28
|
+
|
29
|
+
def geocoder_init(options)
|
30
|
+
unless defined?(@geocoder_options)
|
31
|
+
@geocoder_options = {}
|
32
|
+
require "geocoder/stores/#{geocoder_file_name}"
|
33
|
+
include Geocoder::Store.const_get(geocoder_module_name)
|
34
|
+
end
|
35
|
+
@geocoder_options.merge! options
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'geocoder'
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
|
5
|
+
##
|
6
|
+
# Methods for invoking Geocoder in a model.
|
7
|
+
#
|
8
|
+
module Model
|
9
|
+
module MongoBase
|
10
|
+
|
11
|
+
##
|
12
|
+
# Set attribute names and include the Geocoder module.
|
13
|
+
#
|
14
|
+
def geocoded_by(address_attr, options = {}, &block)
|
15
|
+
geocoder_init(
|
16
|
+
:geocode => true,
|
17
|
+
:user_address => address_attr,
|
18
|
+
:coordinates => options[:coordinates] || :coordinates,
|
19
|
+
:geocode_block => block,
|
20
|
+
:units => options[:units],
|
21
|
+
:method => options[:method],
|
22
|
+
:skip_index => options[:skip_index] || false,
|
23
|
+
:lookup => options[:lookup],
|
24
|
+
:language => options[:language]
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Set attribute names and include the Geocoder module.
|
30
|
+
#
|
31
|
+
def reverse_geocoded_by(coordinates_attr, options = {}, &block)
|
32
|
+
geocoder_init(
|
33
|
+
:reverse_geocode => true,
|
34
|
+
:fetched_address => options[:address] || :address,
|
35
|
+
:coordinates => coordinates_attr,
|
36
|
+
:reverse_block => block,
|
37
|
+
:units => options[:units],
|
38
|
+
:method => options[:method],
|
39
|
+
:skip_index => options[:skip_index] || false,
|
40
|
+
:lookup => options[:lookup],
|
41
|
+
:language => options[:language]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private # ----------------------------------------------------------------
|
46
|
+
|
47
|
+
def geocoder_init(options)
|
48
|
+
unless geocoder_initialized?
|
49
|
+
@geocoder_options = { }
|
50
|
+
require "geocoder/stores/#{geocoder_file_name}"
|
51
|
+
include Geocoder::Store.const_get(geocoder_module_name)
|
52
|
+
end
|
53
|
+
@geocoder_options.merge! options
|
54
|
+
end
|
55
|
+
|
56
|
+
def geocoder_initialized?
|
57
|
+
included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
|
58
|
+
rescue NameError
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'geocoder/models/base'
|
2
|
+
require 'geocoder/models/mongo_base'
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
module Model
|
6
|
+
module MongoMapper
|
7
|
+
include Base
|
8
|
+
include MongoBase
|
9
|
+
|
10
|
+
def self.included(base); base.extend(self); end
|
11
|
+
|
12
|
+
private # --------------------------------------------------------------
|
13
|
+
|
14
|
+
def geocoder_file_name; "mongo_mapper"; end
|
15
|
+
def geocoder_module_name; "MongoMapper"; end
|
16
|
+
|
17
|
+
def geocoder_init(options)
|
18
|
+
super(options)
|
19
|
+
if options[:skip_index] == false
|
20
|
+
ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
|
21
|
+
:min => -180, :max => 180 # create 2d index
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|