geokit 1.7.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +6 -14
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +2 -1
  5. data/MIT-LICENSE +20 -0
  6. data/README.markdown +44 -39
  7. data/Rakefile +15 -0
  8. data/fixtures/vcr_cassettes/bing_full.yml +102 -0
  9. data/fixtures/vcr_cassettes/bing_full_au.yml +91 -0
  10. data/fixtures/vcr_cassettes/bing_full_de.yml +91 -0
  11. data/fixtures/vcr_cassettes/fcc_reverse_geocode.yml +37 -0
  12. data/fixtures/vcr_cassettes/free_geo_ip_geocode.yml +36 -0
  13. data/fixtures/vcr_cassettes/geo_plugin_geocode.yml +38 -0
  14. data/fixtures/vcr_cassettes/geonames_geocode.yml +304 -0
  15. data/fixtures/vcr_cassettes/{google3_city.yml → google_city.yml} +0 -0
  16. data/fixtures/vcr_cassettes/{google3_country_code_biased_result.yml → google_country_code_biased_result.yml} +0 -0
  17. data/fixtures/vcr_cassettes/{google3_full.yml → google_full.yml} +0 -0
  18. data/fixtures/vcr_cassettes/{google3_full_short.yml → google_full_short.yml} +0 -0
  19. data/fixtures/vcr_cassettes/{google3_language_response_fr.yml → google_language_response_fr.yml} +0 -0
  20. data/fixtures/vcr_cassettes/{google3_multi.yml → google_multi.yml} +0 -0
  21. data/fixtures/vcr_cassettes/{google3_reverse_madrid.yml → google_reverse_madrid.yml} +0 -0
  22. data/fixtures/vcr_cassettes/ripe_geocode.yml +66 -0
  23. data/fixtures/vcr_cassettes/ripe_geocode_au.yml +66 -0
  24. data/geokit.gemspec +1 -1
  25. data/lib/geokit.rb +5 -0
  26. data/lib/geokit/bounds.rb +96 -0
  27. data/lib/geokit/core_ext.rb +17 -0
  28. data/lib/geokit/geo_loc.rb +134 -0
  29. data/lib/geokit/geocoders.rb +48 -35
  30. data/lib/geokit/geocoders/base_ip.rb +43 -0
  31. data/lib/geokit/geocoders/bing.rb +101 -0
  32. data/lib/geokit/geocoders/ca_geocoder.rb +50 -0
  33. data/lib/geokit/{services → geocoders}/fcc.rb +17 -20
  34. data/lib/geokit/geocoders/free_geo_ip.rb +34 -0
  35. data/lib/geokit/geocoders/geo_plugin.rb +33 -0
  36. data/lib/geokit/geocoders/geonames.rb +53 -0
  37. data/lib/geokit/{services/google3.rb → geocoders/google.rb} +59 -57
  38. data/lib/geokit/geocoders/ip.rb +69 -0
  39. data/lib/geokit/geocoders/mapquest.rb +72 -0
  40. data/lib/geokit/geocoders/maxmind.rb +29 -0
  41. data/lib/geokit/geocoders/openstreetmap.rb +119 -0
  42. data/lib/geokit/geocoders/ripe.rb +41 -0
  43. data/lib/geokit/{services → geocoders}/us_geocoder.rb +15 -20
  44. data/lib/geokit/{services → geocoders}/yahoo.rb +52 -55
  45. data/lib/geokit/geocoders/yandex.rb +61 -0
  46. data/lib/geokit/inflectors.rb +1 -2
  47. data/lib/geokit/lat_lng.rb +129 -0
  48. data/lib/geokit/mappable.rb +41 -424
  49. data/lib/geokit/multi_geocoder.rb +6 -2
  50. data/lib/geokit/polygon.rb +46 -0
  51. data/lib/geokit/version.rb +1 -1
  52. data/test/helper.rb +2 -12
  53. data/test/test_base_geocoder.rb +0 -10
  54. data/test/test_bing_geocoder.rb +60 -0
  55. data/test/test_fcc_geocoder.rb +23 -0
  56. data/test/test_free_geo_ip_geocoder.rb +23 -0
  57. data/test/test_geo_plugin_geocoder.rb +23 -0
  58. data/test/test_geonames_geocoder.rb +23 -0
  59. data/test/test_google_geocoder.rb +208 -235
  60. data/test/test_maxmind_geocoder.rb +35 -4
  61. data/test/test_multi_geocoder.rb +3 -1
  62. data/test/test_ripe_geocoder.rb +35 -0
  63. data/test/test_yahoo_geocoder.rb +0 -12
  64. metadata +78 -52
  65. data/LICENSE +0 -25
  66. data/Manifest.txt +0 -21
  67. data/data/GeoLiteCity.dat +0 -0
  68. data/lib/geokit/services/ca_geocoder.rb +0 -55
  69. data/lib/geokit/services/geo_plugin.rb +0 -31
  70. data/lib/geokit/services/geonames.rb +0 -53
  71. data/lib/geokit/services/google.rb +0 -158
  72. data/lib/geokit/services/ip.rb +0 -103
  73. data/lib/geokit/services/maxmind.rb +0 -39
  74. data/lib/geokit/services/openstreetmap.rb +0 -119
  75. data/lib/geokit/services/ripe.rb +0 -32
  76. data/lib/geokit/services/yandex.rb +0 -51
  77. data/test/test_google_geocoder3.rb +0 -238
  78. data/test/test_google_reverse_geocoder.rb +0 -49
@@ -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 & Bill Eisenhauer"]
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,17 @@
1
+ unless nil.respond_to?(:try)
2
+ class Object
3
+ def try(*a, &b)
4
+ if a.empty? && block_given?
5
+ yield self
6
+ else
7
+ __send__(*a, &b)
8
+ end
9
+ end
10
+ end
11
+
12
+ class NilClass
13
+ def try(*args)
14
+ nil
15
+ end
16
+ end
17
+ 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
@@ -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
- @@proxy_addr = nil
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 = 'REPLACE_WITH_YOUR_YANDEX_KEY'
44
- @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
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, options = {})
99
- res = do_geocode(address, options)
100
- return res.nil? ? GeoLoc.new : res
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
- res = do_reverse_geocode(latlng)
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
- return self.do_get(url)
113
+ self.do_get(url)
114
114
  rescue TimeoutError
115
- return nil
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
- return GeoLoc.new
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
- res = Net::HTTP::new(uri.host, uri.port,
139
- Geokit::Geocoders::proxy_addr,
140
- Geokit::Geocoders::proxy_port,
141
- Geokit::Geocoders::proxy_user,
142
- Geokit::Geocoders::proxy_pass).start { |http| http.request(req) }
143
- return res
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
- # Adds subclass' geocode method making it conveniently available through
147
- # the base class.
148
- def self.inherited(clazz)
149
- class_name = clazz.name.split('::').last
150
- src = <<-END_SRC
151
- def self.#{Geokit::Inflector.underscore(class_name)}(address, options = {})
152
- #{class_name}.geocode(address, options)
153
- end
154
- END_SRC
155
- class_eval(src)
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
- Dir[File.join(File.dirname(__FILE__), "/services/*.rb")].each {|f| require f}
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