geokit 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/.travis.yml +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +2 -1
- data/MIT-LICENSE +20 -0
- data/README.markdown +44 -39
- data/Rakefile +15 -0
- data/fixtures/vcr_cassettes/bing_full.yml +102 -0
- data/fixtures/vcr_cassettes/bing_full_au.yml +91 -0
- data/fixtures/vcr_cassettes/bing_full_de.yml +91 -0
- data/fixtures/vcr_cassettes/fcc_reverse_geocode.yml +37 -0
- data/fixtures/vcr_cassettes/free_geo_ip_geocode.yml +36 -0
- data/fixtures/vcr_cassettes/geo_plugin_geocode.yml +38 -0
- data/fixtures/vcr_cassettes/geonames_geocode.yml +304 -0
- data/fixtures/vcr_cassettes/{google3_city.yml → google_city.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_country_code_biased_result.yml → google_country_code_biased_result.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_full.yml → google_full.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_full_short.yml → google_full_short.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_language_response_fr.yml → google_language_response_fr.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_multi.yml → google_multi.yml} +0 -0
- data/fixtures/vcr_cassettes/{google3_reverse_madrid.yml → google_reverse_madrid.yml} +0 -0
- data/fixtures/vcr_cassettes/ripe_geocode.yml +66 -0
- data/fixtures/vcr_cassettes/ripe_geocode_au.yml +66 -0
- data/geokit.gemspec +1 -1
- data/lib/geokit.rb +5 -0
- data/lib/geokit/bounds.rb +96 -0
- data/lib/geokit/core_ext.rb +17 -0
- data/lib/geokit/geo_loc.rb +134 -0
- data/lib/geokit/geocoders.rb +48 -35
- data/lib/geokit/geocoders/base_ip.rb +43 -0
- data/lib/geokit/geocoders/bing.rb +101 -0
- data/lib/geokit/geocoders/ca_geocoder.rb +50 -0
- data/lib/geokit/{services → geocoders}/fcc.rb +17 -20
- data/lib/geokit/geocoders/free_geo_ip.rb +34 -0
- data/lib/geokit/geocoders/geo_plugin.rb +33 -0
- data/lib/geokit/geocoders/geonames.rb +53 -0
- data/lib/geokit/{services/google3.rb → geocoders/google.rb} +59 -57
- data/lib/geokit/geocoders/ip.rb +69 -0
- data/lib/geokit/geocoders/mapquest.rb +72 -0
- data/lib/geokit/geocoders/maxmind.rb +29 -0
- data/lib/geokit/geocoders/openstreetmap.rb +119 -0
- data/lib/geokit/geocoders/ripe.rb +41 -0
- data/lib/geokit/{services → geocoders}/us_geocoder.rb +15 -20
- data/lib/geokit/{services → geocoders}/yahoo.rb +52 -55
- data/lib/geokit/geocoders/yandex.rb +61 -0
- data/lib/geokit/inflectors.rb +1 -2
- data/lib/geokit/lat_lng.rb +129 -0
- data/lib/geokit/mappable.rb +41 -424
- data/lib/geokit/multi_geocoder.rb +6 -2
- data/lib/geokit/polygon.rb +46 -0
- data/lib/geokit/version.rb +1 -1
- data/test/helper.rb +2 -12
- data/test/test_base_geocoder.rb +0 -10
- data/test/test_bing_geocoder.rb +60 -0
- data/test/test_fcc_geocoder.rb +23 -0
- data/test/test_free_geo_ip_geocoder.rb +23 -0
- data/test/test_geo_plugin_geocoder.rb +23 -0
- data/test/test_geonames_geocoder.rb +23 -0
- data/test/test_google_geocoder.rb +208 -235
- data/test/test_maxmind_geocoder.rb +35 -4
- data/test/test_multi_geocoder.rb +3 -1
- data/test/test_ripe_geocoder.rb +35 -0
- data/test/test_yahoo_geocoder.rb +0 -12
- metadata +78 -52
- data/LICENSE +0 -25
- data/Manifest.txt +0 -21
- data/data/GeoLiteCity.dat +0 -0
- data/lib/geokit/services/ca_geocoder.rb +0 -55
- data/lib/geokit/services/geo_plugin.rb +0 -31
- data/lib/geokit/services/geonames.rb +0 -53
- data/lib/geokit/services/google.rb +0 -158
- data/lib/geokit/services/ip.rb +0 -103
- data/lib/geokit/services/maxmind.rb +0 -39
- data/lib/geokit/services/openstreetmap.rb +0 -119
- data/lib/geokit/services/ripe.rb +0 -32
- data/lib/geokit/services/yandex.rb +0 -51
- data/test/test_google_geocoder3.rb +0 -238
- data/test/test_google_reverse_geocoder.rb +0 -49
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/fixtures/vcr_cassettes/{google3_language_response_fr.yml → google_language_response_fr.yml}
RENAMED
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,66 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://stat.ripe.net/data/geoloc/data.json?resource=74.125.237.209
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ""
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- "*/*"
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Vary:
|
20
|
+
- Cookie, Accept-Encoding
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Set-Cookie:
|
24
|
+
- stat-session=36d43b5353a80b7f74c583a0bda4ea17; expires=Fri, 27-Dec-2013 15:12:05 GMT; Max-Age=1209600; Path=/
|
25
|
+
Transfer-Encoding:
|
26
|
+
- chunked
|
27
|
+
Date:
|
28
|
+
- Fri, 13 Dec 2013 15:12:05 GMT
|
29
|
+
Server:
|
30
|
+
- lighttpd/1.4.32
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: |-
|
34
|
+
{
|
35
|
+
"cached": true,
|
36
|
+
"data": {
|
37
|
+
"locations": [
|
38
|
+
{
|
39
|
+
"city": "Mountain View",
|
40
|
+
"country": "CA(US)",
|
41
|
+
"covered_percentage": 100,
|
42
|
+
"latitude": 37.419199999999996,
|
43
|
+
"longitude": -122.0574,
|
44
|
+
"prefixes": [
|
45
|
+
"74.125.192.0/18"
|
46
|
+
]
|
47
|
+
}
|
48
|
+
],
|
49
|
+
"query_time": "2013-11-01T00:00:00",
|
50
|
+
"resource": "74.125.237.209",
|
51
|
+
"unknown_percentage": 0.0
|
52
|
+
},
|
53
|
+
"data_call_status": "supported",
|
54
|
+
"messages": [],
|
55
|
+
"process_time": 3,
|
56
|
+
"query_id": "ed345b04-6408-11e3-9667-782bcb346712",
|
57
|
+
"see_also": [],
|
58
|
+
"server_id": "stat-app2",
|
59
|
+
"status": "ok",
|
60
|
+
"status_code": 200,
|
61
|
+
"time": "2013-12-13T15:12:05.342893",
|
62
|
+
"version": "2.0"
|
63
|
+
}
|
64
|
+
http_version:
|
65
|
+
recorded_at: Fri, 13 Dec 2013 15:12:02 GMT
|
66
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,66 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://stat.ripe.net/data/geoloc/data.json?resource=118.210.24.54
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ""
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- "*/*"
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Vary:
|
20
|
+
- Cookie, Accept-Encoding
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Set-Cookie:
|
24
|
+
- stat-session=8dcf7a7e881c4ec64aa1a7dfcc1335c9; expires=Fri, 27-Dec-2013 15:14:20 GMT; Max-Age=1209600; Path=/
|
25
|
+
Transfer-Encoding:
|
26
|
+
- chunked
|
27
|
+
Date:
|
28
|
+
- Fri, 13 Dec 2013 15:14:20 GMT
|
29
|
+
Server:
|
30
|
+
- lighttpd/1.4.32
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: |-
|
34
|
+
{
|
35
|
+
"cached": true,
|
36
|
+
"data": {
|
37
|
+
"locations": [
|
38
|
+
{
|
39
|
+
"city": "Adelaide",
|
40
|
+
"country": "AU",
|
41
|
+
"covered_percentage": 100,
|
42
|
+
"latitude": -34.928699999999999,
|
43
|
+
"longitude": 138.5986,
|
44
|
+
"prefixes": [
|
45
|
+
"118.210.16.0/20"
|
46
|
+
]
|
47
|
+
}
|
48
|
+
],
|
49
|
+
"query_time": "2013-11-01T00:00:00",
|
50
|
+
"resource": "118.210.24.54",
|
51
|
+
"unknown_percentage": 0.0
|
52
|
+
},
|
53
|
+
"data_call_status": "supported",
|
54
|
+
"messages": [],
|
55
|
+
"process_time": 3,
|
56
|
+
"query_id": "3dfcd85e-6409-11e3-a402-782bcb346712",
|
57
|
+
"see_also": [],
|
58
|
+
"server_id": "stat-app2",
|
59
|
+
"status": "ok",
|
60
|
+
"status_code": 200,
|
61
|
+
"time": "2013-12-13T15:14:20.874304",
|
62
|
+
"version": "2.0"
|
63
|
+
}
|
64
|
+
http_version:
|
65
|
+
recorded_at: Fri, 13 Dec 2013 15:14:18 GMT
|
66
|
+
recorded_with: VCR 2.8.0
|
data/geokit.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'geokit/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "geokit"
|
8
8
|
spec.version = Geokit::VERSION
|
9
|
-
spec.authors = ["Michael Noack, James Cox, Andre Lewis
|
9
|
+
spec.authors = ["Michael Noack", "James Cox", "Andre Lewis", "Bill Eisenhauer"]
|
10
10
|
spec.email = ["michael+geokit@noack.com.au"]
|
11
11
|
spec.description = %q{Geokit provides geocoding and distance calculation in an easy-to-use API}
|
12
12
|
spec.summary = %q{Geokit: encoding and distance calculation gem}
|
data/lib/geokit.rb
CHANGED
@@ -22,5 +22,10 @@ end
|
|
22
22
|
|
23
23
|
path = File.expand_path(File.dirname(__FILE__))
|
24
24
|
$:.unshift path unless $:.include?(path)
|
25
|
+
require 'geokit/core_ext'
|
25
26
|
require 'geokit/geocoders'
|
26
27
|
require 'geokit/mappable'
|
28
|
+
require 'geokit/bounds'
|
29
|
+
require 'geokit/lat_lng'
|
30
|
+
require 'geokit/geo_loc'
|
31
|
+
require 'geokit/polygon'
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Geokit
|
2
|
+
# Bounds represents a rectangular bounds, defined by the SW and NE corners
|
3
|
+
class Bounds
|
4
|
+
# sw and ne are LatLng objects
|
5
|
+
attr_accessor :sw, :ne
|
6
|
+
|
7
|
+
# provide sw and ne to instantiate a new Bounds instance
|
8
|
+
def initialize(sw,ne)
|
9
|
+
raise ArgumentError if !(sw.is_a?(Geokit::LatLng) && ne.is_a?(Geokit::LatLng))
|
10
|
+
@sw,@ne=sw,ne
|
11
|
+
end
|
12
|
+
|
13
|
+
#returns the a single point which is the center of the rectangular bounds
|
14
|
+
def center
|
15
|
+
@sw.midpoint_to(@ne)
|
16
|
+
end
|
17
|
+
|
18
|
+
# a simple string representation:sw,ne
|
19
|
+
def to_s
|
20
|
+
"#{@sw.to_s},#{@ne.to_s}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# a two-element array of two-element arrays: sw,ne
|
24
|
+
def to_a
|
25
|
+
[@sw.to_a, @ne.to_a]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the bounds contain the passed point.
|
29
|
+
# allows for bounds which cross the meridian
|
30
|
+
def contains?(point)
|
31
|
+
point=Geokit::LatLng.normalize(point)
|
32
|
+
res = point.lat > @sw.lat && point.lat < @ne.lat
|
33
|
+
if crosses_meridian?
|
34
|
+
res &= point.lng < @ne.lng || point.lng > @sw.lng
|
35
|
+
else
|
36
|
+
res &= point.lng < @ne.lng && point.lng > @sw.lng
|
37
|
+
end
|
38
|
+
res
|
39
|
+
end
|
40
|
+
|
41
|
+
# returns true if the bounds crosses the international dateline
|
42
|
+
def crosses_meridian?
|
43
|
+
@sw.lng > @ne.lng
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if the candidate object is logically equal. Logical equivalence
|
47
|
+
# is true if the lat and lng attributes are the same for both objects.
|
48
|
+
def ==(other)
|
49
|
+
return false unless other.is_a?(Bounds)
|
50
|
+
sw == other.sw && ne == other.ne
|
51
|
+
end
|
52
|
+
|
53
|
+
# Equivalent to Google Maps API's .toSpan() method on GLatLng's.
|
54
|
+
#
|
55
|
+
# Returns a LatLng object, whose coordinates represent the size of a rectangle
|
56
|
+
# defined by these bounds.
|
57
|
+
def to_span
|
58
|
+
lat_span = (@ne.lat - @sw.lat).abs
|
59
|
+
lng_span = (crosses_meridian? ? 360 + @ne.lng - @sw.lng : @ne.lng - @sw.lng).abs
|
60
|
+
Geokit::LatLng.new(lat_span, lng_span)
|
61
|
+
end
|
62
|
+
|
63
|
+
class <<self
|
64
|
+
|
65
|
+
# returns an instance of bounds which completely encompases the given circle
|
66
|
+
def from_point_and_radius(point,radius,options={})
|
67
|
+
point=LatLng.normalize(point)
|
68
|
+
p0=point.endpoint(0,radius,options)
|
69
|
+
p90=point.endpoint(90,radius,options)
|
70
|
+
p180=point.endpoint(180,radius,options)
|
71
|
+
p270=point.endpoint(270,radius,options)
|
72
|
+
sw=Geokit::LatLng.new(p180.lat,p270.lng)
|
73
|
+
ne=Geokit::LatLng.new(p0.lat,p90.lng)
|
74
|
+
Geokit::Bounds.new(sw,ne)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Takes two main combinations of arguments to create a bounds:
|
78
|
+
# point,point (this is the only one which takes two arguments
|
79
|
+
# [point,point]
|
80
|
+
# . . . where a point is anything LatLng#normalize can handle (which is quite a lot)
|
81
|
+
#
|
82
|
+
# NOTE: everything combination is assumed to pass points in the order sw, ne
|
83
|
+
def normalize (thing,other=nil)
|
84
|
+
# maybe this will be simple -- an actual bounds object is passed, and we can all go home
|
85
|
+
return thing if thing.is_a? Bounds
|
86
|
+
|
87
|
+
# no? OK, if there's no "other," the thing better be a two-element array
|
88
|
+
thing,other=thing if !other && thing.is_a?(Array) && thing.size==2
|
89
|
+
|
90
|
+
# Now that we're set with a thing and another thing, let LatLng do the heavy lifting.
|
91
|
+
# Exceptions may be thrown
|
92
|
+
Bounds.new(Geokit::LatLng.normalize(thing),Geokit::LatLng.normalize(other))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Geokit
|
2
|
+
# This class encapsulates the result of a geocoding call.
|
3
|
+
# It's primary purpose is to homogenize the results of multiple
|
4
|
+
# geocoding providers. It also provides some additional functionality, such as
|
5
|
+
# the "full address" method for geocoders that do not provide a
|
6
|
+
# full address in their results (for example, Yahoo), and the "is_us" method.
|
7
|
+
#
|
8
|
+
# Some geocoders can return multple results. Geoloc can capture multiple results through
|
9
|
+
# its "all" method.
|
10
|
+
#
|
11
|
+
# For the geocoder setting the results, it would look something like this:
|
12
|
+
# geo=GeoLoc.new(first_result)
|
13
|
+
# geo.all.push(second_result)
|
14
|
+
# geo.all.push(third_result)
|
15
|
+
#
|
16
|
+
# Then, for the user of the result:
|
17
|
+
#
|
18
|
+
# puts geo.full_address # just like usual
|
19
|
+
# puts geo.all.size => 3 # there's three results total
|
20
|
+
# puts geo.all.first # all is just an array or additional geolocs,
|
21
|
+
# so do what you want with it
|
22
|
+
class GeoLoc < LatLng
|
23
|
+
|
24
|
+
# Location attributes. Full address is a concatenation of all values. For example:
|
25
|
+
# 100 Spear St, San Francisco, CA, 94101, US
|
26
|
+
# Street number and street name are extracted from the street address attribute if they don't exist
|
27
|
+
attr_accessor :street_number, :street_name, :street_address, :city, :state, :zip, :country_code, :country
|
28
|
+
attr_accessor :full_address, :all, :district, :province, :sub_premise, :neighborhood
|
29
|
+
# Attributes set upon return from geocoding. Success will be true for successful
|
30
|
+
# geocode lookups. The provider will be set to the name of the providing geocoder.
|
31
|
+
# Finally, precision is an indicator of the accuracy of the geocoding.
|
32
|
+
attr_accessor :success, :provider, :precision, :suggested_bounds
|
33
|
+
# accuracy is set for Yahoo and Google geocoders, it is a numeric value of the
|
34
|
+
# precision. see http://code.google.com/apis/maps/documentation/geocoding/#GeocodingAccuracy
|
35
|
+
attr_accessor :accuracy
|
36
|
+
# FCC Attributes
|
37
|
+
attr_accessor :district_fips, :state_fips, :block_fips
|
38
|
+
|
39
|
+
|
40
|
+
# Constructor expects a hash of symbols to correspond with attributes.
|
41
|
+
def initialize(h={})
|
42
|
+
@all = [self]
|
43
|
+
|
44
|
+
@street_address=h[:street_address]
|
45
|
+
@sub_premise=nil
|
46
|
+
@street_number=nil
|
47
|
+
@street_name=nil
|
48
|
+
@city=h[:city]
|
49
|
+
@state=h[:state]
|
50
|
+
@zip=h[:zip]
|
51
|
+
@country_code=h[:country_code]
|
52
|
+
@province = h[:province]
|
53
|
+
@success=false
|
54
|
+
@precision='unknown'
|
55
|
+
@full_address=nil
|
56
|
+
super(h[:lat],h[:lng])
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns true if geocoded to the United States.
|
60
|
+
def is_us?
|
61
|
+
country_code == 'US'
|
62
|
+
end
|
63
|
+
|
64
|
+
def success?
|
65
|
+
success == true
|
66
|
+
end
|
67
|
+
|
68
|
+
# full_address is provided by google but not by yahoo. It is intended that the google
|
69
|
+
# geocoding method will provide the full address, whereas for yahoo it will be derived
|
70
|
+
# from the parts of the address we do have.
|
71
|
+
def full_address
|
72
|
+
@full_address ? @full_address : to_geocodeable_s
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extracts the street number from the street address where possible.
|
76
|
+
def street_number
|
77
|
+
@street_number ||= street_address[/(\d*)/] if street_address
|
78
|
+
@street_number
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the street name portion of the street address where possible
|
82
|
+
def street_name
|
83
|
+
@street_name||=street_address[street_number.length, street_address.length].strip if street_address
|
84
|
+
@street_name
|
85
|
+
end
|
86
|
+
|
87
|
+
# gives you all the important fields as key-value pairs
|
88
|
+
def hash
|
89
|
+
res={}
|
90
|
+
[:success, :lat, :lng, :country_code, :city, :state, :zip, :street_address, :province,
|
91
|
+
:district, :provider, :full_address, :is_us?, :ll, :precision, :district_fips, :state_fips,
|
92
|
+
:block_fips, :sub_premise].each { |s| res[s] = self.send(s.to_s) }
|
93
|
+
res
|
94
|
+
end
|
95
|
+
alias to_hash hash
|
96
|
+
|
97
|
+
# Sets the city after capitalizing each word within the city name.
|
98
|
+
def city=(city)
|
99
|
+
@city = Geokit::Inflector::titleize(city) if city
|
100
|
+
end
|
101
|
+
|
102
|
+
# Sets the street address after capitalizing each word within the street address.
|
103
|
+
def street_address=(address)
|
104
|
+
@street_address = if address && provider != 'google'
|
105
|
+
Geokit::Inflector::titleize(address)
|
106
|
+
else
|
107
|
+
address
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a comma-delimited string consisting of the street address, city, state,
|
112
|
+
# zip, and country code. Only includes those attributes that are non-blank.
|
113
|
+
def to_geocodeable_s
|
114
|
+
a=[street_address, district, city, province, state, zip, country_code].compact
|
115
|
+
a.delete_if { |e| !e || e == '' }
|
116
|
+
a.join(', ')
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_yaml_properties
|
120
|
+
(instance_variables - ['@all', :@all]).sort
|
121
|
+
end
|
122
|
+
|
123
|
+
def encode_with(coder)
|
124
|
+
to_yaml_properties.each do |name|
|
125
|
+
coder[name[1..-1].to_s] = instance_variable_get(name.to_s)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns a string representation of the instance.
|
130
|
+
def to_s
|
131
|
+
"Provider: #{provider}\nStreet: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/geokit/geocoders.rb
CHANGED
@@ -10,9 +10,6 @@ require 'multi_json'
|
|
10
10
|
module Geokit
|
11
11
|
require File.join(File.dirname(__FILE__), 'inflectors')
|
12
12
|
|
13
|
-
class TooManyQueriesError < StandardError; end
|
14
|
-
|
15
|
-
|
16
13
|
# Contains a range of geocoders:
|
17
14
|
#
|
18
15
|
# ### "regular" address geocoders
|
@@ -33,15 +30,14 @@ module Geokit
|
|
33
30
|
#
|
34
31
|
# Some of these geocoders require configuration. You don't have to provide it here. See the README.
|
35
32
|
module Geocoders
|
36
|
-
@@
|
37
|
-
@@proxy_port = nil
|
38
|
-
@@proxy_user = nil
|
39
|
-
@@proxy_pass = nil
|
33
|
+
@@proxy = nil
|
40
34
|
@@request_timeout = nil
|
35
|
+
@@bing = 'REPLACE_WITH_YOUR_BING_KEY'
|
36
|
+
@@bing_options = {}
|
41
37
|
@@yahoo_consumer_key = 'REPLACE_WITH_YOUR_YAHOO_BOSS_OAUTH_CONSUMER_KEY'
|
42
38
|
@@yahoo_consumer_secret = 'REPLACE_WITH_YOUR_YAHOO_BOSS_OAUTH_CONSUMER_SECRET'
|
43
|
-
@@yandex =
|
44
|
-
@@
|
39
|
+
@@yandex = nil
|
40
|
+
@@mapquest = 'REPLACE_WITH_YOUR_MAPQUEST_KEY'
|
45
41
|
@@google_client_id = nil
|
46
42
|
@@google_cryptographic_key = nil
|
47
43
|
@@google_channel = nil
|
@@ -84,6 +80,7 @@ module Geokit
|
|
84
80
|
|
85
81
|
# Error which is thrown in the event a geocoding error occurs.
|
86
82
|
class GeocodeError < StandardError; end
|
83
|
+
class TooManyQueriesError < StandardError; end
|
87
84
|
|
88
85
|
# -------------------------------------------------------------------------------------------
|
89
86
|
# Geocoder Base class -- every geocoder should inherit from this
|
@@ -95,36 +92,39 @@ module Geokit
|
|
95
92
|
# Main method which calls the do_geocode template method which subclasses
|
96
93
|
# are responsible for implementing. Returns a populated GeoLoc or an
|
97
94
|
# empty one with a failed success code.
|
98
|
-
def self.geocode(address,
|
99
|
-
|
100
|
-
|
95
|
+
def self.geocode(address, *args)
|
96
|
+
do_geocode(address, *args) || GeoLoc.new
|
97
|
+
rescue TooManyQueriesError, GeocodeError
|
98
|
+
raise
|
99
|
+
rescue
|
100
|
+
logger.error "Caught an error during #{self.class} geocoding call: #{$!}"
|
101
|
+
GeoLoc.new
|
101
102
|
end
|
102
103
|
# Main method which calls the do_reverse_geocode template method which subclasses
|
103
104
|
# are responsible for implementing. Returns a populated GeoLoc or an
|
104
105
|
# empty one with a failed success code.
|
105
106
|
def self.reverse_geocode(latlng)
|
106
|
-
|
107
|
-
return res.success? ? res : GeoLoc.new
|
107
|
+
do_reverse_geocode(latlng) || GeoLoc.new
|
108
108
|
end
|
109
109
|
|
110
110
|
# Call the geocoder service using the timeout if configured.
|
111
111
|
def self.call_geocoder_service(url)
|
112
112
|
Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
|
113
|
-
|
113
|
+
self.do_get(url)
|
114
114
|
rescue TimeoutError
|
115
|
-
|
115
|
+
nil
|
116
116
|
end
|
117
117
|
|
118
118
|
# Not all geocoders can do reverse geocoding. So, unless the subclass explicitly overrides this method,
|
119
119
|
# a call to reverse_geocode will return an empty GeoLoc. If you happen to be using MultiGeocoder,
|
120
120
|
# this will cause it to failover to the next geocoder, which will hopefully be one which supports reverse geocoding.
|
121
121
|
def self.do_reverse_geocode(latlng)
|
122
|
-
|
122
|
+
GeoLoc.new
|
123
123
|
end
|
124
124
|
|
125
125
|
protected
|
126
126
|
|
127
|
-
def self.logger
|
127
|
+
def self.logger
|
128
128
|
Geokit::Geocoders::logger
|
129
129
|
end
|
130
130
|
|
@@ -135,31 +135,44 @@ module Geokit
|
|
135
135
|
uri = URI.parse(url)
|
136
136
|
req = Net::HTTP::Get.new(url)
|
137
137
|
req.basic_auth(uri.user, uri.password) if uri.userinfo
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
138
|
+
net_http_args = [uri.host, uri.port]
|
139
|
+
if (proxy_uri_string = Geokit::Geocoders::proxy)
|
140
|
+
proxy_uri = URI.parse(proxy_uri_string)
|
141
|
+
net_http_args += [proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password]
|
142
|
+
end
|
143
|
+
Net::HTTP::new(*net_http_args).start { |http| http.request(req) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.parse(format, body, *args)
|
147
|
+
case format
|
148
|
+
when :json then parse_json(MultiJson.load(body), *args)
|
149
|
+
when :xml then parse_xml(REXML::Document.new(body), *args)
|
150
|
+
when :yaml then parse_yaml(YAML::load(body), *args)
|
151
|
+
end
|
144
152
|
end
|
145
153
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
154
|
+
def self.set_mappings(loc, xml, mappings)
|
155
|
+
mappings.each_pair do |field, xml_field|
|
156
|
+
loc.send("#{field}=", xml.elements[xml_field].try(:text))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.transcode_to_utf8(body)
|
161
|
+
require 'iconv' unless String.method_defined?(:encode)
|
162
|
+
if String.method_defined?(:encode)
|
163
|
+
body.encode!('UTF-8', 'UTF-8', :invalid => :replace)
|
164
|
+
else
|
165
|
+
ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
|
166
|
+
body = ic.iconv(body)
|
167
|
+
end
|
156
168
|
end
|
157
169
|
end
|
158
170
|
|
159
171
|
# -------------------------------------------------------------------------------------------
|
160
172
|
# "Regular" Address geocoders
|
161
173
|
# -------------------------------------------------------------------------------------------
|
162
|
-
|
174
|
+
require File.join(File.dirname(__FILE__), 'geocoders/base_ip')
|
175
|
+
Dir[File.join(File.dirname(__FILE__), "/geocoders/*.rb")].each {|f| require f}
|
163
176
|
|
164
177
|
require File.join(File.dirname(__FILE__), 'multi_geocoder')
|
165
178
|
end
|