geokit 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|