geocoder2 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +329 -0
- data/LICENSE +20 -0
- data/README.md +796 -0
- data/Rakefile +25 -0
- data/bin/geocode2 +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 +15 -0
- data/json?address=26+leonard+street%2C+Belmont&key=AIzaSyDoltU6YL8XeIQrSLFGk6ZfpKaWkPukwYQ&language=en +68 -0
- data/lib/generators/geocoder2/config/config_generator.rb +14 -0
- data/lib/generators/geocoder2/config/templates/initializer.rb +21 -0
- data/lib/geocoder2/cache.rb +89 -0
- data/lib/geocoder2/calculations.rb +389 -0
- data/lib/geocoder2/cli.rb +121 -0
- data/lib/geocoder2/configuration.rb +130 -0
- data/lib/geocoder2/configuration_hash.rb +11 -0
- data/lib/geocoder2/exceptions.rb +21 -0
- data/lib/geocoder2/lookup.rb +86 -0
- data/lib/geocoder2/lookups/baidu.rb +54 -0
- data/lib/geocoder2/lookups/base.rb +266 -0
- data/lib/geocoder2/lookups/bing.rb +47 -0
- data/lib/geocoder2/lookups/dstk.rb +20 -0
- data/lib/geocoder2/lookups/esri.rb +48 -0
- data/lib/geocoder2/lookups/freegeoip.rb +43 -0
- data/lib/geocoder2/lookups/geocoder_ca.rb +54 -0
- data/lib/geocoder2/lookups/geocoder_us.rb +39 -0
- data/lib/geocoder2/lookups/google.rb +69 -0
- data/lib/geocoder2/lookups/google_premier.rb +47 -0
- data/lib/geocoder2/lookups/mapquest.rb +59 -0
- data/lib/geocoder2/lookups/maxmind.rb +88 -0
- data/lib/geocoder2/lookups/nominatim.rb +44 -0
- data/lib/geocoder2/lookups/ovi.rb +62 -0
- data/lib/geocoder2/lookups/test.rb +44 -0
- data/lib/geocoder2/lookups/yahoo.rb +86 -0
- data/lib/geocoder2/lookups/yandex.rb +54 -0
- data/lib/geocoder2/models/active_record.rb +46 -0
- data/lib/geocoder2/models/base.rb +42 -0
- data/lib/geocoder2/models/mongo_base.rb +60 -0
- data/lib/geocoder2/models/mongo_mapper.rb +26 -0
- data/lib/geocoder2/models/mongoid.rb +32 -0
- data/lib/geocoder2/query.rb +107 -0
- data/lib/geocoder2/railtie.rb +26 -0
- data/lib/geocoder2/request.rb +23 -0
- data/lib/geocoder2/results/baidu.rb +79 -0
- data/lib/geocoder2/results/base.rb +67 -0
- data/lib/geocoder2/results/bing.rb +48 -0
- data/lib/geocoder2/results/dstk.rb +6 -0
- data/lib/geocoder2/results/esri.rb +51 -0
- data/lib/geocoder2/results/freegeoip.rb +45 -0
- data/lib/geocoder2/results/geocoder_ca.rb +60 -0
- data/lib/geocoder2/results/geocoder_us.rb +39 -0
- data/lib/geocoder2/results/google.rb +124 -0
- data/lib/geocoder2/results/google_premier.rb +6 -0
- data/lib/geocoder2/results/mapquest.rb +51 -0
- data/lib/geocoder2/results/maxmind.rb +135 -0
- data/lib/geocoder2/results/nominatim.rb +94 -0
- data/lib/geocoder2/results/ovi.rb +62 -0
- data/lib/geocoder2/results/test.rb +16 -0
- data/lib/geocoder2/results/yahoo.rb +55 -0
- data/lib/geocoder2/results/yandex.rb +80 -0
- data/lib/geocoder2/sql.rb +106 -0
- data/lib/geocoder2/stores/active_record.rb +272 -0
- data/lib/geocoder2/stores/base.rb +120 -0
- data/lib/geocoder2/stores/mongo_base.rb +89 -0
- data/lib/geocoder2/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder2/stores/mongoid.rb +13 -0
- data/lib/geocoder2/version.rb +3 -0
- data/lib/geocoder2.rb +55 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/oauth_util.rb +112 -0
- data/lib/tasks/geocoder2.rake +27 -0
- data/test/active_record_test.rb +15 -0
- data/test/cache_test.rb +35 -0
- data/test/calculations_test.rb +211 -0
- data/test/configuration_test.rb +78 -0
- data/test/custom_block_test.rb +32 -0
- data/test/error_handling_test.rb +43 -0
- data/test/fixtures/baidu_invalid_key +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/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/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/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/ovi_madison_square_garden +72 -0
- data/test/fixtures/ovi_no_results +8 -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_invalid_key +1 -0
- data/test/fixtures/yandex_kremlin +48 -0
- data/test/fixtures/yandex_no_city_and_town +112 -0
- data/test/fixtures/yandex_no_results +16 -0
- data/test/geocoder_test.rb +59 -0
- data/test/https_test.rb +16 -0
- data/test/integration/smoke_test.rb +26 -0
- data/test/lookup_test.rb +117 -0
- data/test/method_aliases_test.rb +25 -0
- data/test/mongoid_test.rb +46 -0
- data/test/mongoid_test_helper.rb +43 -0
- data/test/near_test.rb +61 -0
- data/test/oauth_util_test.rb +30 -0
- data/test/proxy_test.rb +36 -0
- data/test/query_test.rb +52 -0
- data/test/request_test.rb +29 -0
- data/test/result_test.rb +42 -0
- data/test/services_test.rb +393 -0
- data/test/test_helper.rb +289 -0
- data/test/test_mode_test.rb +59 -0
- metadata +213 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# = Hash Recursive Merge
|
3
|
+
#
|
4
|
+
# Merges a Ruby Hash recursively, Also known as deep merge.
|
5
|
+
# Recursive version of Hash#merge and Hash#merge!.
|
6
|
+
#
|
7
|
+
# Category:: Ruby
|
8
|
+
# Package:: Hash
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# Copyright:: 2007-2008 The Authors
|
11
|
+
# License:: MIT License
|
12
|
+
# Link:: http://www.simonecarletti.com/
|
13
|
+
# Source:: http://gist.github.com/gists/6391/
|
14
|
+
#
|
15
|
+
module HashRecursiveMerge
|
16
|
+
|
17
|
+
#
|
18
|
+
# Recursive version of Hash#merge!
|
19
|
+
#
|
20
|
+
# Adds the contents of +other_hash+ to +hsh+,
|
21
|
+
# merging entries in +hsh+ with duplicate keys with those from +other_hash+.
|
22
|
+
#
|
23
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
24
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
25
|
+
# it merges and returns the values from both arrays.
|
26
|
+
#
|
27
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
28
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
29
|
+
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
30
|
+
#
|
31
|
+
# Simply using Hash#merge! would return
|
32
|
+
#
|
33
|
+
# h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
34
|
+
#
|
35
|
+
def rmerge!(other_hash)
|
36
|
+
merge!(other_hash) do |key, oldval, newval|
|
37
|
+
oldval.class == self.class ? oldval.rmerge!(newval) : newval
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Recursive version of Hash#merge
|
43
|
+
#
|
44
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
45
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
46
|
+
# it merges and returns the values from both arrays.
|
47
|
+
#
|
48
|
+
# Compared with Hash#merge, this method provides a different approch
|
49
|
+
# for merging nasted hashes.
|
50
|
+
# If the value of a given key is an Hash and both +other_hash+ abd +hsh
|
51
|
+
# includes the same key, the value is merged instead replaced with
|
52
|
+
# +other_hash+ value.
|
53
|
+
#
|
54
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
55
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
56
|
+
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
57
|
+
#
|
58
|
+
# Simply using Hash#merge would return
|
59
|
+
#
|
60
|
+
# h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
61
|
+
#
|
62
|
+
def rmerge(other_hash)
|
63
|
+
r = {}
|
64
|
+
merge(other_hash) do |key, oldval, newval|
|
65
|
+
r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
class Hash
|
73
|
+
include HashRecursiveMerge
|
74
|
+
end
|
data/lib/oauth_util.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# A utility for signing an url using OAuth in a way that's convenient for debugging
|
2
|
+
# Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
|
3
|
+
# Source: http://gist.github.com/383159
|
4
|
+
# License: http://gist.github.com/375593
|
5
|
+
# Usage: see example.rb below
|
6
|
+
#
|
7
|
+
# NOTE: This file has been modified from the original Gist:
|
8
|
+
#
|
9
|
+
# 1. Fix to prevent param-array conversion, as mentioned in Gist comment.
|
10
|
+
# 2. Query string escaping has been changed. See:
|
11
|
+
# https://github.com/alexreisner/geocoder2/pull/360
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'uri'
|
15
|
+
require 'cgi'
|
16
|
+
require 'openssl'
|
17
|
+
require 'base64'
|
18
|
+
|
19
|
+
class OauthUtil
|
20
|
+
|
21
|
+
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
|
22
|
+
:sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@consumer_key = ''
|
26
|
+
@consumer_secret = ''
|
27
|
+
@token = ''
|
28
|
+
@token_secret = ''
|
29
|
+
@req_method = 'GET'
|
30
|
+
@sig_method = 'HMAC-SHA1'
|
31
|
+
@oauth_version = '1.0'
|
32
|
+
@callback_url = ''
|
33
|
+
end
|
34
|
+
|
35
|
+
# openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
|
36
|
+
# ref http://snippets.dzone.com/posts/show/491
|
37
|
+
def nonce
|
38
|
+
Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
|
39
|
+
end
|
40
|
+
|
41
|
+
def percent_encode( string )
|
42
|
+
|
43
|
+
# ref http://snippets.dzone.com/posts/show/1260
|
44
|
+
return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
|
45
|
+
end
|
46
|
+
|
47
|
+
# @ref http://oauth.net/core/1.0/#rfc.section.9.2
|
48
|
+
def signature
|
49
|
+
key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
|
50
|
+
|
51
|
+
# ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
|
52
|
+
digest = OpenSSL::Digest::Digest.new( 'sha1' )
|
53
|
+
hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
|
54
|
+
|
55
|
+
# ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
|
56
|
+
Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
|
57
|
+
end
|
58
|
+
|
59
|
+
# sort (very important as it affects the signature), concat, and percent encode
|
60
|
+
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
|
61
|
+
# @ref http://oauth.net/core/1.0/#9.2.1
|
62
|
+
# @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
|
63
|
+
def query_string
|
64
|
+
pairs = []
|
65
|
+
@params.sort.each { | key, val |
|
66
|
+
pairs.push( "#{ CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') } }=#{ CGI.escape(val.to_s) }" )
|
67
|
+
}
|
68
|
+
pairs.join '&'
|
69
|
+
end
|
70
|
+
|
71
|
+
# organize params & create signature
|
72
|
+
def sign( parsed_url )
|
73
|
+
|
74
|
+
@params = {
|
75
|
+
'oauth_consumer_key' => @consumer_key,
|
76
|
+
'oauth_nonce' => nonce,
|
77
|
+
'oauth_signature_method' => @sig_method,
|
78
|
+
'oauth_timestamp' => Time.now.to_i.to_s,
|
79
|
+
'oauth_version' => @oauth_version
|
80
|
+
}
|
81
|
+
|
82
|
+
# if url has query, merge key/values into params obj overwriting defaults
|
83
|
+
if parsed_url.query
|
84
|
+
CGI.parse( parsed_url.query ).each do |k,v|
|
85
|
+
if v.is_a?(Array) && v.count == 1
|
86
|
+
@params[k] = v.first
|
87
|
+
else
|
88
|
+
@params[k] = v
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
|
94
|
+
@req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
|
95
|
+
|
96
|
+
# create base str. make it an object attr for ez debugging
|
97
|
+
# ref http://oauth.net/core/1.0/#anchor14
|
98
|
+
@base_str = [
|
99
|
+
@req_method,
|
100
|
+
percent_encode( req_url ),
|
101
|
+
|
102
|
+
# normalization is just x-www-form-urlencoded
|
103
|
+
percent_encode( query_string )
|
104
|
+
|
105
|
+
].join( '&' )
|
106
|
+
|
107
|
+
# add signature
|
108
|
+
@params[ 'oauth_signature' ] = signature
|
109
|
+
|
110
|
+
return self
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
namespace :geocode do
|
2
|
+
desc "Geocode all objects without coordinates."
|
3
|
+
task :all => :environment do
|
4
|
+
class_name = ENV['CLASS'] || ENV['class']
|
5
|
+
sleep_timer = ENV['SLEEP'] || ENV['sleep']
|
6
|
+
raise "Please specify a CLASS (model)" unless class_name
|
7
|
+
klass = class_from_string(class_name)
|
8
|
+
|
9
|
+
klass.not_geocoded.each do |obj|
|
10
|
+
obj.geocode; obj.save
|
11
|
+
sleep(sleep_timer.to_f) unless sleep_timer.nil?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Get a class object from the string given in the shell environment.
|
18
|
+
# Similar to ActiveSupport's +constantize+ method.
|
19
|
+
#
|
20
|
+
def class_from_string(class_name)
|
21
|
+
parts = class_name.split("::")
|
22
|
+
constant = Object
|
23
|
+
parts.each do |part|
|
24
|
+
constant = constant.const_get(part)
|
25
|
+
end
|
26
|
+
constant
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class ActiveRecordTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_exclude_condition_when_model_has_a_custom_primary_key
|
7
|
+
venue = VenuePlus.new(*venue_params(:msg))
|
8
|
+
|
9
|
+
# just call private method directly so we don't have to stub .near scope
|
10
|
+
conditions = venue.class.send(:add_exclude_condition, ["fake_condition"], venue)
|
11
|
+
|
12
|
+
assert_match( /#{VenuePlus.primary_key}/, conditions.join)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/cache_test.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class CacheTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_second_occurrence_of_request_is_cache_hit
|
7
|
+
Geocoder2.configure(:cache => {})
|
8
|
+
Geocoder2::Lookup.all_services_except_test.each do |l|
|
9
|
+
Geocoder2.configure(:lookup => l)
|
10
|
+
set_api_key!(l)
|
11
|
+
results = Geocoder2.search("Madison Square Garden")
|
12
|
+
assert !results.first.cache_hit,
|
13
|
+
"Lookup #{l} returned erroneously cached result."
|
14
|
+
results = Geocoder2.search("Madison Square Garden")
|
15
|
+
assert results.first.cache_hit,
|
16
|
+
"Lookup #{l} did not return cached result."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_google_over_query_limit_does_not_hit_cache
|
21
|
+
Geocoder2.configure(:cache => {})
|
22
|
+
Geocoder2.configure(:lookup => :google)
|
23
|
+
set_api_key!(:google)
|
24
|
+
Geocoder2.configure(:always_raise => :all)
|
25
|
+
assert_raises Geocoder2::OverQueryLimitError do
|
26
|
+
Geocoder2.search("over limit")
|
27
|
+
end
|
28
|
+
lookup = Geocoder2::Lookup.get(:google)
|
29
|
+
assert_equal false, lookup.instance_variable_get(:@cache_hit)
|
30
|
+
assert_raises Geocoder2::OverQueryLimitError do
|
31
|
+
Geocoder2.search("over limit")
|
32
|
+
end
|
33
|
+
assert_equal false, lookup.instance_variable_get(:@cache_hit)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class CalculationsTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Geocoder2.configure(
|
7
|
+
:units => :mi,
|
8
|
+
:distances => :linear
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
# --- degree distance ---
|
13
|
+
|
14
|
+
def test_longitude_degree_distance_at_equator
|
15
|
+
assert_equal 69, Geocoder2::Calculations.longitude_degree_distance(0).round
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_longitude_degree_distance_at_new_york
|
19
|
+
assert_equal 53, Geocoder2::Calculations.longitude_degree_distance(40).round
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_longitude_degree_distance_at_north_pole
|
23
|
+
assert_equal 0, Geocoder2::Calculations.longitude_degree_distance(89.98).round
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# --- distance between ---
|
28
|
+
|
29
|
+
def test_distance_between_in_miles
|
30
|
+
assert_equal 69, Geocoder2::Calculations.distance_between([0,0], [0,1]).round
|
31
|
+
la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74]).round
|
32
|
+
assert (la_to_ny - 2444).abs < 10
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_distance_between_in_kilometers
|
36
|
+
assert_equal 111, Geocoder2::Calculations.distance_between([0,0], [0,1], :units => :km).round
|
37
|
+
la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :km).round
|
38
|
+
assert (la_to_ny - 3942).abs < 10
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_distance_between_in_nautical_miles
|
42
|
+
assert_equal 60, Geocoder2::Calculations.distance_between([0,0], [0,1], :units => :nm).round
|
43
|
+
la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :nm).round
|
44
|
+
assert (la_to_ny - 2124).abs < 10
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# --- geographic center ---
|
49
|
+
|
50
|
+
def test_geographic_center_with_arrays
|
51
|
+
assert_equal [0.0, 0.5],
|
52
|
+
Geocoder2::Calculations.geographic_center([[0,0], [0,1]])
|
53
|
+
assert_equal [0.0, 1.0],
|
54
|
+
Geocoder2::Calculations.geographic_center([[0,0], [0,1], [0,2]])
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_geographic_center_with_mixed_arguments
|
58
|
+
p1 = [0, 0]
|
59
|
+
p2 = Landmark.new("Some Cold Place", 0, 1)
|
60
|
+
assert_equal [0.0, 0.5], Geocoder2::Calculations.geographic_center([p1, p2])
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# --- bounding box ---
|
65
|
+
|
66
|
+
def test_bounding_box_calculation_in_miles
|
67
|
+
center = [51, 7] # Cologne, DE
|
68
|
+
radius = 10 # miles
|
69
|
+
dlon = radius / Geocoder2::Calculations.latitude_degree_distance
|
70
|
+
dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0])
|
71
|
+
corners = [50.86, 6.77, 51.14, 7.23]
|
72
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
73
|
+
Geocoder2::Calculations.bounding_box(center, radius).map{ |i| (i * 100).round }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_bounding_box_calculation_in_kilometers
|
77
|
+
center = [51, 7] # Cologne, DE
|
78
|
+
radius = 111 # kilometers (= 1 degree latitude)
|
79
|
+
dlon = radius / Geocoder2::Calculations.latitude_degree_distance(:km)
|
80
|
+
dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0], :km)
|
81
|
+
corners = [50, 5.41, 52, 8.59]
|
82
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
83
|
+
Geocoder2::Calculations.bounding_box(center, radius, :units => :km).map{ |i| (i * 100).round }
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_bounding_box_calculation_with_object
|
87
|
+
center = [51, 7] # Cologne, DE
|
88
|
+
radius = 10 # miles
|
89
|
+
dlon = radius / Geocoder2::Calculations.latitude_degree_distance
|
90
|
+
dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0])
|
91
|
+
corners = [50.86, 6.77, 51.14, 7.23]
|
92
|
+
obj = Landmark.new("Cologne", center[0], center[1])
|
93
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
94
|
+
Geocoder2::Calculations.bounding_box(obj, radius).map{ |i| (i * 100).round }
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_bounding_box_calculation_with_address_string
|
98
|
+
assert_nothing_raised do
|
99
|
+
Geocoder2::Calculations.bounding_box("4893 Clay St, San Francisco, CA", 50)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# --- random point ---
|
104
|
+
|
105
|
+
def test_random_point_within_radius
|
106
|
+
20.times do
|
107
|
+
center = [51, 7] # Cologne, DE
|
108
|
+
radius = 10 # miles
|
109
|
+
random_point = Geocoder2::Calculations.random_point_near(center, radius)
|
110
|
+
distance = Geocoder2::Calculations.distance_between(center, random_point)
|
111
|
+
assert distance <= radius
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# --- bearing ---
|
116
|
+
|
117
|
+
def test_compass_points
|
118
|
+
assert_equal "N", Geocoder2::Calculations.compass_point(0)
|
119
|
+
assert_equal "N", Geocoder2::Calculations.compass_point(1.0)
|
120
|
+
assert_equal "N", Geocoder2::Calculations.compass_point(360)
|
121
|
+
assert_equal "N", Geocoder2::Calculations.compass_point(361)
|
122
|
+
assert_equal "N", Geocoder2::Calculations.compass_point(-22)
|
123
|
+
assert_equal "NW", Geocoder2::Calculations.compass_point(-23)
|
124
|
+
assert_equal "S", Geocoder2::Calculations.compass_point(180)
|
125
|
+
assert_equal "S", Geocoder2::Calculations.compass_point(181)
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_bearing_between
|
129
|
+
bearings = {
|
130
|
+
:n => 0,
|
131
|
+
:e => 90,
|
132
|
+
:s => 180,
|
133
|
+
:w => 270
|
134
|
+
}
|
135
|
+
points = {
|
136
|
+
:n => [41, -75],
|
137
|
+
:e => [40, -74],
|
138
|
+
:s => [39, -75],
|
139
|
+
:w => [40, -76]
|
140
|
+
}
|
141
|
+
directions = [:n, :e, :s, :w]
|
142
|
+
methods = [:linear, :spherical]
|
143
|
+
|
144
|
+
methods.each do |m|
|
145
|
+
directions.each_with_index do |d,i|
|
146
|
+
opp = directions[(i + 2) % 4] # opposite direction
|
147
|
+
b = Geocoder2::Calculations.bearing_between(
|
148
|
+
points[d], points[opp], :method => m)
|
149
|
+
assert (b - bearings[opp]).abs < 1,
|
150
|
+
"Bearing (#{m}) should be close to #{bearings[opp]} but was #{b}."
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_spherical_bearing_to
|
156
|
+
l = Landmark.new(*landmark_params(:msg))
|
157
|
+
assert_equal 324, l.bearing_to([50,-85], :method => :spherical).round
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_spherical_bearing_from
|
161
|
+
l = Landmark.new(*landmark_params(:msg))
|
162
|
+
assert_equal 136, l.bearing_from([50,-85], :method => :spherical).round
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_linear_bearing_from_and_to_are_exactly_opposite
|
166
|
+
l = Landmark.new(*landmark_params(:msg))
|
167
|
+
assert_equal l.bearing_from([50,-86.1]), l.bearing_to([50,-86.1]) - 180
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_extract_coordinates
|
171
|
+
coords = [-23,47]
|
172
|
+
l = Landmark.new("Madagascar", coords[0], coords[1])
|
173
|
+
assert_equal coords, Geocoder2::Calculations.extract_coordinates(l)
|
174
|
+
assert_equal coords, Geocoder2::Calculations.extract_coordinates(coords)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_extract_nan_coordinates
|
178
|
+
result = Geocoder2::Calculations.extract_coordinates([ nil, nil ])
|
179
|
+
assert_nan_coordinates?(result)
|
180
|
+
|
181
|
+
result = Geocoder2::Calculations.extract_coordinates(nil)
|
182
|
+
assert_nan_coordinates?(result)
|
183
|
+
|
184
|
+
result = Geocoder2::Calculations.extract_coordinates('')
|
185
|
+
assert_nan_coordinates?(result)
|
186
|
+
|
187
|
+
result = Geocoder2::Calculations.extract_coordinates([ 'nix' ])
|
188
|
+
assert_nan_coordinates?(result)
|
189
|
+
|
190
|
+
o = Object.new
|
191
|
+
result = Geocoder2::Calculations.extract_coordinates(o)
|
192
|
+
assert_nan_coordinates?(result)
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_coordinates_present
|
196
|
+
assert Geocoder2::Calculations.coordinates_present?(3.23)
|
197
|
+
assert !Geocoder2::Calculations.coordinates_present?(nil)
|
198
|
+
assert !Geocoder2::Calculations.coordinates_present?(Geocoder2::Calculations::NAN)
|
199
|
+
assert !Geocoder2::Calculations.coordinates_present?(3.23, nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
private # ------------------------------------------------------------------
|
203
|
+
|
204
|
+
def assert_nan_coordinates?(value)
|
205
|
+
assert value.is_a?(Array) &&
|
206
|
+
value.size == 2 &&
|
207
|
+
value[0].nan? &&
|
208
|
+
value[1].nan?,
|
209
|
+
"Expected value to be [NaN, NaN] but was #{value}"
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class ConfigurationTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Geocoder2::Configuration.set_defaults
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_exception_raised_on_bad_lookup_config
|
10
|
+
Geocoder2.configure(:lookup => :stoopid)
|
11
|
+
assert_raises Geocoder2::ConfigurationError do
|
12
|
+
Geocoder2.search "something dumb"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_setting_with_class_method
|
17
|
+
Geocoder2::Configuration.units = :test
|
18
|
+
assert_equal :test, Geocoder2.config.units
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_setting_with_configure_method
|
22
|
+
Geocoder2.configure(:units => :test)
|
23
|
+
assert_equal :test, Geocoder2.config.units
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_setting_with_block_syntax
|
27
|
+
orig = $VERBOSE; $VERBOSE = nil
|
28
|
+
Geocoder2.configure do |config|
|
29
|
+
config.units = :test
|
30
|
+
end
|
31
|
+
assert_equal :test, Geocoder2.config.units
|
32
|
+
ensure
|
33
|
+
$VERBOSE = orig
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_config_for_lookup
|
37
|
+
Geocoder2.configure(
|
38
|
+
:timeout => 5,
|
39
|
+
:api_key => "aaa",
|
40
|
+
:google => {
|
41
|
+
:timeout => 2
|
42
|
+
}
|
43
|
+
)
|
44
|
+
assert_equal 2, Geocoder2.config_for_lookup(:google).timeout
|
45
|
+
assert_equal "aaa", Geocoder2.config_for_lookup(:google).api_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_model_configuration
|
49
|
+
Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :km
|
50
|
+
assert_equal :km, Landmark.geocoder2_options[:units]
|
51
|
+
assert_equal :spherical, Landmark.geocoder2_options[:method]
|
52
|
+
|
53
|
+
v = Landmark.new(*landmark_params(:msg))
|
54
|
+
v.latitude = 0
|
55
|
+
v.longitude = 0
|
56
|
+
assert_equal 111, v.distance_to([0,1]).round
|
57
|
+
v.latitude = 40.750354
|
58
|
+
v.longitude = -73.993371
|
59
|
+
assert_equal 136, v.bearing_from([50,-85]).round
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_configuration_chain
|
63
|
+
v = Landmark.new(*landmark_params(:msg))
|
64
|
+
v.latitude = 0
|
65
|
+
v.longitude = 0
|
66
|
+
|
67
|
+
# method option > global configuration
|
68
|
+
Geocoder2.configure(:units => :km)
|
69
|
+
assert_equal 69, v.distance_to([0,1], :mi).round
|
70
|
+
|
71
|
+
# per-model configuration > global configuration
|
72
|
+
Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :mi
|
73
|
+
assert_equal 69, v.distance_to([0,1]).round
|
74
|
+
|
75
|
+
# method option > per-model configuration
|
76
|
+
assert_equal 111, v.distance_to([0,1], :km).round
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class CustomBlockTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_geocode_with_block_runs_block
|
7
|
+
e = Event.new(*venue_params(:msg))
|
8
|
+
coords = [40.750354, -73.993371]
|
9
|
+
e.geocode
|
10
|
+
assert_equal coords.map{ |c| c.to_s }.join(','), e.coords_string
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_geocode_with_block_doesnt_auto_assign_coordinates
|
14
|
+
e = Event.new(*venue_params(:msg))
|
15
|
+
e.geocode
|
16
|
+
assert_nil e.latitude
|
17
|
+
assert_nil e.longitude
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_reverse_geocode_with_block_runs_block
|
21
|
+
e = Party.new(*landmark_params(:msg))
|
22
|
+
e.reverse_geocode
|
23
|
+
assert_equal "US", e.country
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_reverse_geocode_with_block_doesnt_auto_assign_address
|
27
|
+
e = Party.new(*landmark_params(:msg))
|
28
|
+
e.reverse_geocode
|
29
|
+
assert_nil e.address
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class ErrorHandlingTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def teardown
|
7
|
+
Geocoder2.configure(:always_raise => [])
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_does_not_choke_on_timeout
|
11
|
+
# keep test output clean: suppress timeout warning
|
12
|
+
orig = $VERBOSE; $VERBOSE = nil
|
13
|
+
Geocoder2::Lookup.all_services_except_test.each do |l|
|
14
|
+
Geocoder2.configure(:lookup => l)
|
15
|
+
set_api_key!(l)
|
16
|
+
assert_nothing_raised { Geocoder2.search("timeout") }
|
17
|
+
end
|
18
|
+
ensure
|
19
|
+
$VERBOSE = orig
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_always_raise_timeout_error
|
23
|
+
Geocoder2.configure(:always_raise => [TimeoutError])
|
24
|
+
Geocoder2::Lookup.all_services_except_test.each do |l|
|
25
|
+
lookup = Geocoder2::Lookup.get(l)
|
26
|
+
set_api_key!(l)
|
27
|
+
assert_raises TimeoutError do
|
28
|
+
lookup.send(:results, Geocoder2::Query.new("timeout"))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_always_raise_socket_error
|
34
|
+
Geocoder2.configure(:always_raise => [SocketError])
|
35
|
+
Geocoder2::Lookup.all_services_except_test.each do |l|
|
36
|
+
lookup = Geocoder2::Lookup.get(l)
|
37
|
+
set_api_key!(l)
|
38
|
+
assert_raises SocketError do
|
39
|
+
lookup.send(:results, Geocoder2::Query.new("socket_error"))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"results":[],"status":5,"msg":"AK Illegal or Not Exist:"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status":0,"result":[]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status":0,"result":{"location":{"lng":121.48789948569,"lat":31.249161555654},"formatted_address":"上海市闸北区天潼路619号","business":"七浦路,海宁路,北京东路","addressComponent":{"city":"上海市","district":"闸北区","province":"上海市","street":"天潼路","street_number":"619号"},"cityCode":289}}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"authenticationResultCode":"InvalidCredentials","brandLogoUri":"http:\\/\\/dev.virtualearth.net\\/Branding\\/logo_powered_by.png","copyright":"Copyright \xC2\xA9 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.","errorDetails":["Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation."],"resourceSets":[],"statusCode":401,"statusDescription":"Unauthorized","traceId":"5c539f6e70c44b2e858741b6c932318e|EWRM001670|02.00.83.1900|"}
|