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,43 @@
1
+ module Geokit
2
+ module Geocoders
3
+ class BaseIpGeocoder < Geocoder
4
+ # A number of non-routable IP ranges.
5
+ #
6
+ # --
7
+ # Sources for these:
8
+ # RFC 3330: Special-Use IPv4 Addresses
9
+ # The bogon list: http://www.cymru.com/Documents/bogon-list.html
10
+
11
+ NON_ROUTABLE_IP_RANGES = [
12
+ IPAddr.new('0.0.0.0/8'), # "This" Network
13
+ IPAddr.new('10.0.0.0/8'), # Private-Use Networks
14
+ IPAddr.new('14.0.0.0/8'), # Public-Data Networks
15
+ IPAddr.new('127.0.0.0/8'), # Loopback
16
+ IPAddr.new('169.254.0.0/16'), # Link local
17
+ IPAddr.new('172.16.0.0/12'), # Private-Use Networks
18
+ IPAddr.new('192.0.2.0/24'), # Test-Net
19
+ IPAddr.new('192.168.0.0/16'), # Private-Use Networks
20
+ IPAddr.new('198.18.0.0/15'), # Network Interconnect Device Benchmark Testing
21
+ IPAddr.new('224.0.0.0/4'), # Multicast
22
+ IPAddr.new('240.0.0.0/4') # Reserved for future use
23
+ ].freeze
24
+
25
+ def self.valid_ip?(ip)
26
+ ip?(ip) && !private_ip_address?(ip)
27
+ end
28
+
29
+ def self.ip?(ip)
30
+ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
31
+ end
32
+
33
+ # Checks whether the IP address belongs to a private address range.
34
+ #
35
+ # This function is used to reduce the number of useless queries made to
36
+ # the geocoding service. Such queries can occur frequently during
37
+ # integration tests.
38
+ def self.private_ip_address?(ip)
39
+ NON_ROUTABLE_IP_RANGES.any? { |range| range.include?(ip) }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,101 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Bing geocoder implementation. Requires the Geokit::Geocoders::bing variable to
4
+ # contain a Bing Maps API key. Conforms to the interface set by the Geocoder class.
5
+ class BingGeocoder < Geocoder
6
+
7
+ private
8
+
9
+ # Template method which does the geocode lookup.
10
+ def self.do_geocode(address)
11
+ url = submit_url(address)
12
+ res = call_geocoder_service(url)
13
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
14
+ xml = transcode_to_utf8(res.body)
15
+ logger.debug "Bing geocoding. Address: #{address}. Result: #{xml}"
16
+ parse :xml, xml
17
+ end
18
+
19
+ def self.submit_url(address)
20
+ options = Geokit::Geocoders::bing_options
21
+ culture = options && options[:culture]
22
+ culture_string = culture ? "&c=#{culture}" : ''
23
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
24
+ "http://dev.virtualearth.net/REST/v1/Locations/#{URI.escape(address_str)}?key=#{Geokit::Geocoders::bing}#{culture_string}&o=xml"
25
+ end
26
+
27
+ def self.parse_xml(xml)
28
+ return GeoLoc.new if xml.elements['//Response/StatusCode'].try(:text) != '200'
29
+ loc = nil
30
+ # Bing can return multiple results as //Location elements.
31
+ # iterate through each and extract each location as a geoloc
32
+ xml.each_element('//Location') do |l|
33
+ extracted_geoloc = extract_location(l)
34
+ loc.nil? ? loc = extracted_geoloc : loc.all.push(extracted_geoloc)
35
+ end
36
+ loc
37
+ end
38
+
39
+ # extracts a single geoloc from a //Location element in the bing results xml
40
+ def self.extract_location(xml)
41
+ loc = GeoLoc.new
42
+ loc.provider = 'bing'
43
+ set_address_components(loc, xml)
44
+ set_precision(loc, xml)
45
+ set_bounds(loc, xml)
46
+ loc.success = true
47
+ loc
48
+ end
49
+
50
+ XML_MAPPINGS = {
51
+ :street_address => 'Address/AddressLine',
52
+ :full_address => 'Address/FormattedAddress',
53
+ :city => 'Address/Locality',
54
+ :state => 'Address/AdminDistrict',
55
+ :province => 'Address/AdminDistrict2',
56
+ :zip => 'Address/PostalCode',
57
+ :country => 'Address/CountryRegion',
58
+ :lat => 'Point/Latitude',
59
+ :lng => 'Point/Longitude'
60
+ }
61
+
62
+ def self.set_address_components(loc, xml)
63
+ set_mappings(loc, xml, XML_MAPPINGS)
64
+ end
65
+
66
+ def self.set_precision(loc, xml)
67
+ if xml.elements['.//Confidence']
68
+ loc.accuracy = case xml.elements['.//Confidence'].text
69
+ when 'High' then 8
70
+ when 'Medium' then 5
71
+ when 'Low' then 2
72
+ else 0
73
+ end
74
+ end
75
+
76
+ if xml.elements['.//EntityType']
77
+ loc.precision = case xml.elements['.//EntityType'].text
78
+ when 'Sovereign' then 'country'
79
+ when 'AdminDivision1' then 'state'
80
+ when 'AdminDivision2' then 'state'
81
+ when 'PopulatedPlace' then 'city'
82
+ when 'Postcode1' then 'zip'
83
+ when 'Postcode2' then 'zip'
84
+ when 'RoadBlock' then 'street'
85
+ when 'Address' then 'address'
86
+ else 'unkown'
87
+ end
88
+ end
89
+ end
90
+
91
+ def self.set_bounds(loc, xml)
92
+ if suggested_bounds = xml.elements['.//BoundingBox']
93
+ bounds = suggested_bounds.elements
94
+ loc.suggested_bounds = Bounds.normalize(
95
+ [bounds['.//SouthLatitude'].text, bounds['.//WestLongitude'].text],
96
+ [bounds['.//NorthLatitude'].text, bounds['.//EastLongitude'].text])
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,50 @@
1
+
2
+ # Geocoder CA geocoder implementation. Requires the Geokit::Geocoders::GEOCODER_CA variable to
3
+ # contain true or false based upon whether authentication is to occur. Conforms to the
4
+ # interface set by the Geocoder class.
5
+ #
6
+ # Returns a response like:
7
+ # <?xml version="1.0" encoding="UTF-8" ?>
8
+ # <geodata>
9
+ # <latt>49.243086</latt>
10
+ # <longt>-123.153684</longt>
11
+ # </geodata>
12
+ module Geokit
13
+ module Geocoders
14
+ class CaGeocoder < Geocoder
15
+
16
+ private
17
+
18
+ # Template method which does the geocode lookup.
19
+ def self.do_geocode(loc)
20
+ raise ArgumentError('Geocoder.ca requires a GeoLoc argument') unless loc.is_a?(GeoLoc)
21
+ url = submit_url(loc)
22
+ res = call_geocoder_service(url)
23
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
24
+ xml = res.body
25
+ logger.debug "Geocoder.ca geocoding. Address: #{loc}. Result: #{xml}"
26
+ parse :xml, xml, loc
27
+ end
28
+
29
+ def self.parse_xml(xml, loc)
30
+ loc.lat = xml.elements['//latt'].text
31
+ loc.lng = xml.elements['//longt'].text
32
+ loc.success = true
33
+ loc
34
+ end
35
+
36
+ # Formats the request in the format acceptable by the CA geocoder.
37
+ def self.submit_url(loc)
38
+ args = []
39
+ args << "stno=#{loc.street_number}" if loc.street_address
40
+ args << "addresst=#{Geokit::Inflector::url_escape(loc.street_name)}" if loc.street_address
41
+ args << "city=#{Geokit::Inflector::url_escape(loc.city)}" if loc.city
42
+ args << "prov=#{loc.state}" if loc.state
43
+ args << "postal=#{loc.zip}" if loc.zip
44
+ args << "auth=#{Geokit::Geocoders::geocoder_ca}" if Geokit::Geocoders::geocoder_ca
45
+ args << "geoit=xml"
46
+ 'http://geocoder.ca/?' + args.join('&')
47
+ end
48
+ end
49
+ end
50
+ end
@@ -6,11 +6,12 @@ module Geokit
6
6
  # Template method which does the reverse-geocode lookup.
7
7
  def self.do_reverse_geocode(latlng)
8
8
  latlng=LatLng.normalize(latlng)
9
- res = self.call_geocoder_service("http://data.fcc.gov/api/block/find?format=json&latitude=#{Geokit::Inflector::url_escape(latlng.lat.to_s)}&longitude=#{Geokit::Inflector::url_escape(latlng.lng.to_s)}")
10
- return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
9
+ url = "http://data.fcc.gov/api/block/find?format=json&latitude=#{Geokit::Inflector::url_escape(latlng.lat.to_s)}&longitude=#{Geokit::Inflector::url_escape(latlng.lng.to_s)}"
10
+ res = call_geocoder_service(url)
11
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
11
12
  json = res.body
12
13
  logger.debug "FCC reverse-geocoding. LL: #{latlng}. Result: #{json}"
13
- return self.json2GeoLoc(json)
14
+ parse :json, json
14
15
  end
15
16
 
16
17
  # Template method which does the geocode lookup.
@@ -26,11 +27,8 @@ module Geokit
26
27
  # "State"=>{"name"=>"Indiana", "code"=>"IN", "FIPS"=>"18"},
27
28
  # "status"=>"OK"}
28
29
 
29
- def self.json2GeoLoc(json, address="")
30
- ret = nil
31
- results = MultiJson.load(json)
32
-
33
- if results.has_key?('Err') and results['Err']["msg"] == 'There are no results for this location'
30
+ def self.parse_json(results)
31
+ if results.has_key?('Err') && results['Err']["msg"] == 'There are no results for this location'
34
32
  return GeoLoc.new
35
33
  end
36
34
  # this should probably be smarter.
@@ -38,18 +36,17 @@ module Geokit
38
36
  raise Geokit::Geocoders::GeocodeError
39
37
  end
40
38
 
41
- res = GeoLoc.new
42
- res.provider = 'fcc'
43
- res.success = true
44
- res.precision = 'block'
45
- res.country_code = 'US'
46
- res.district = results['County']['name']
47
- res.district_fips = results['County']['FIPS']
48
- res.state = results['State']['code']
49
- res.state_fips = results['State']['FIPS']
50
- res.block_fips = results['Block']['FIPS']
51
-
52
- res
39
+ loc = GeoLoc.new
40
+ loc.provider = 'fcc'
41
+ loc.success = true
42
+ loc.precision = 'block'
43
+ loc.country_code = 'US'
44
+ loc.district = results['County']['name']
45
+ loc.district_fips = results['County']['FIPS']
46
+ loc.state = results['State']['code']
47
+ loc.state_fips = results['State']['FIPS']
48
+ loc.block_fips = results['Block']['FIPS']
49
+ loc
53
50
  end
54
51
  end
55
52
 
@@ -0,0 +1,34 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Provides geocoding based upon an IP address. The underlying web service is freegeoip.net
4
+ class FreeGeoIpGeocoder < BaseIpGeocoder
5
+ private
6
+
7
+ def self.do_geocode(ip)
8
+ return GeoLoc.new unless valid_ip?(ip)
9
+ url = "http://freegeoip.net/xml/#{ip}"
10
+ res = call_geocoder_service(url)
11
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
12
+ parse :xml, res.body
13
+ end
14
+
15
+ XML_MAPPINGS = {
16
+ :city => 'City',
17
+ :state => 'RegionCode',
18
+ :zip => 'ZipCode',
19
+ :country_code => 'CountryCode',
20
+ :lat => 'Latitude',
21
+ :lng => 'Longitude'
22
+ }
23
+
24
+ def self.parse_xml(xml)
25
+ loc = GeoLoc.new
26
+ loc.provider = 'freegeoip'
27
+ set_mappings(loc, xml.elements['Response'], XML_MAPPINGS)
28
+ loc.success = !!loc.city && !loc.city.empty?
29
+ loc
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Provides geocoding based upon an IP address. The underlying web service is geoplugin.net
4
+ class GeoPluginGeocoder < BaseIpGeocoder
5
+ private
6
+
7
+ def self.do_geocode(ip)
8
+ return GeoLoc.new unless valid_ip?(ip)
9
+ url = "http://www.geoplugin.net/xml.gp?ip=#{ip}"
10
+ res = call_geocoder_service(url)
11
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
12
+ parse :xml, res.body
13
+ end
14
+
15
+ XML_MAPPINGS = {
16
+ :city => 'geoplugin_city',
17
+ :state => 'geoplugin_region',
18
+ :country_code => 'geoplugin_countryCode',
19
+ :lat => 'geoplugin_latitude',
20
+ :lng => 'geoplugin_longitude'
21
+ }
22
+
23
+ def self.parse_xml(xml)
24
+ loc = GeoLoc.new
25
+ loc.provider = 'geoPlugin'
26
+ set_mappings(loc, xml.elements['geoPlugin'], XML_MAPPINGS)
27
+ loc.success = !!loc.city && !loc.city.empty?
28
+ loc
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Another geocoding web service
4
+ # http://www.geonames.org
5
+ class GeonamesGeocoder < Geocoder
6
+
7
+ private
8
+
9
+ # Template method which does the geocode lookup.
10
+ def self.do_geocode(address)
11
+ url = submit_url(address)
12
+ res = call_geocoder_service(url)
13
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
14
+
15
+ xml = res.body
16
+ logger.debug "Geonames geocoding. Address: #{address}. Result: #{xml}"
17
+ parse :xml, xml
18
+ end
19
+
20
+ def self.submit_url(address)
21
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
22
+ # geonames need a space seperated search string
23
+ address_str.gsub!(/,/, " ")
24
+ params = "/postalCodeSearch?placename=#{Geokit::Inflector::url_escape(address_str)}&maxRows=10"
25
+
26
+ if Geokit::Geocoders::geonames
27
+ "http://ws.geonames.net#{params}&username=#{Geokit::Geocoders::geonames}"
28
+ else
29
+ "http://ws.geonames.org#{params}"
30
+ end
31
+ end
32
+
33
+ XML_MAPPINGS = {
34
+ :city => 'name',
35
+ :state => 'adminName1',
36
+ :zip => 'postalcode',
37
+ :country_code => 'countryCode',
38
+ :lat => 'lat',
39
+ :lng => 'lng'
40
+ }
41
+
42
+ def self.parse_xml(xml)
43
+ return GeoLoc.new unless xml.elements['geonames/totalResultsCount'].text.to_i > 0
44
+ loc = GeoLoc.new
45
+ loc.provider = 'genomes'
46
+ # only take the first result
47
+ set_mappings(loc, xml.elements['geonames/code'], XML_MAPPINGS)
48
+ loc.success = true
49
+ loc
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,17 +1,17 @@
1
1
  module Geokit
2
2
  module Geocoders
3
- class GoogleGeocoder3 < Geocoder
3
+ class GoogleGeocoder < Geocoder
4
4
 
5
5
  private
6
6
  # Template method which does the reverse-geocode lookup.
7
7
  def self.do_reverse_geocode(latlng)
8
8
  latlng=LatLng.normalize(latlng)
9
- submit_url = submit_url("/maps/api/geocode/json?sensor=false&latlng=#{Geokit::Inflector::url_escape(latlng.ll)}")
10
- res = self.call_geocoder_service(submit_url)
11
- return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
9
+ url = submit_url("/maps/api/geocode/json?sensor=false&latlng=#{Geokit::Inflector::url_escape(latlng.ll)}")
10
+ res = call_geocoder_service(url)
11
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
12
12
  json = res.body
13
13
  logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{CGI.escape(json)}"
14
- return self.json2GeoLoc(json)
14
+ parse :json, json
15
15
  end
16
16
 
17
17
  # Template method which does the geocode lookup.
@@ -43,15 +43,15 @@ module Geokit
43
43
  bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
44
44
  language_str = options[:language] ? "&language=#{options[:language]}" : ''
45
45
  address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
46
- submit_url = submit_url("/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(address_str)}#{bias_str}#{language_str}")
46
+ url = submit_url("/maps/api/geocode/json?sensor=false&address=#{Geokit::Inflector::url_escape(address_str)}#{bias_str}#{language_str}")
47
47
 
48
- res = self.call_geocoder_service(submit_url)
48
+ res = call_geocoder_service(url)
49
49
  return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
50
50
 
51
51
  json = res.body
52
52
  logger.debug "Google geocoding. Address: #{address}. Result: #{CGI.escape(json)}"
53
53
 
54
- return self.json2GeoLoc(json, address)
54
+ parse :json, json
55
55
  end
56
56
 
57
57
  # This code comes from Googles Examples
@@ -64,13 +64,14 @@ module Geokit
64
64
  # create a signature using the private key and the URL
65
65
  rawSignature = OpenSSL::HMAC.digest('sha1', rawKey, urlToSign)
66
66
  # encode the signature into base64 for url use form.
67
- return Base64.encode64(rawSignature).tr('+/','-_').gsub(/\n/, '')
67
+ Base64.encode64(rawSignature).tr('+/','-_').gsub(/\n/, '')
68
68
  end
69
69
 
70
70
 
71
71
  def self.submit_url(query_string)
72
- if !Geokit::Geocoders::google_client_id.nil? and !Geokit::Geocoders::google_cryptographic_key.nil?
73
- urlToSign = query_string + "&client=#{Geokit::Geocoders::google_client_id}" + "#{(!Geokit::Geocoders::google_channel.nil? ? ("&channel="+ Geokit::Geocoders::google_channel) : "")}"
72
+ if Geokit::Geocoders::google_client_id && Geokit::Geocoders::google_cryptographic_key
73
+ channel = Geokit::Geocoders::google_channel ? "&channel=#{Geokit::Geocoders::google_channel}" : ''
74
+ urlToSign = query_string + "&client=#{Geokit::Geocoders::google_client_id}" + channel
74
75
  signature = sign_gmap_bus_api_url(urlToSign, Geokit::Geocoders::google_cryptographic_key)
75
76
  "http://maps.googleapis.com" + urlToSign + "&signature=#{signature}"
76
77
  else
@@ -91,11 +92,9 @@ module Geokit
91
92
  end
92
93
  end
93
94
 
94
- def self.json2GeoLoc(json, address="")
95
- results = MultiJson.load(json)
96
-
95
+ def self.parse_json(results)
97
96
  case results['status']
98
- when 'OVER_QUERY_LIMIT' then raise Geokit::TooManyQueriesError
97
+ when 'OVER_QUERY_LIMIT' then raise Geokit::Geocoders::TooManyQueriesError
99
98
  when 'ZERO_RESULTS' then return GeoLoc.new
100
99
  end
101
100
  # this should probably be smarter.
@@ -138,66 +137,69 @@ module Geokit
138
137
  }
139
138
 
140
139
  def self.single_json_to_geoloc(addr)
141
- res = GeoLoc.new
142
- res.provider = 'google3'
143
- res.success = true
144
- res.full_address = addr['formatted_address']
140
+ loc = GeoLoc.new
141
+ loc.provider = 'google'
142
+ loc.success = true
143
+ loc.full_address = addr['formatted_address']
144
+
145
+ set_address_components(loc, addr)
146
+ set_precision(loc, addr)
147
+ if loc.street_name
148
+ loc.street_address=[loc.street_number, loc.street_name].join(' ').strip
149
+ end
150
+
151
+ ll = addr['geometry']['location']
152
+ loc.lat = ll['lat'].to_f
153
+ loc.lng = ll['lng'].to_f
154
+
155
+ viewport = addr['geometry']['viewport']
156
+ ne = Geokit::LatLng.from_json(viewport['northeast'])
157
+ sw = Geokit::LatLng.from_json(viewport['southwest'])
158
+ loc.suggested_bounds = Geokit::Bounds.new(sw, ne)
159
+
160
+ loc
161
+ end
145
162
 
163
+ def self.set_address_components(loc, addr)
146
164
  addr['address_components'].each do |comp|
147
165
  case
148
166
  when comp['types'].include?("subpremise")
149
- res.sub_premise = comp['short_name']
167
+ loc.sub_premise = comp['short_name']
150
168
  when comp['types'].include?("street_number")
151
- res.street_number = comp['short_name']
169
+ loc.street_number = comp['short_name']
152
170
  when comp['types'].include?("route")
153
- res.street_name = comp['long_name']
171
+ loc.street_name = comp['long_name']
154
172
  when comp['types'].include?("locality")
155
- res.city = comp['long_name']
173
+ loc.city = comp['long_name']
156
174
  when comp['types'].include?("administrative_area_level_1")
157
- res.state = comp['short_name']
158
- res.province = comp['short_name']
175
+ loc.state = comp['short_name']
176
+ loc.province = comp['short_name']
159
177
  when comp['types'].include?("postal_code")
160
- res.zip = comp['long_name']
178
+ loc.zip = comp['long_name']
161
179
  when comp['types'].include?("country")
162
- res.country_code = comp['short_name']
163
- res.country = comp['long_name']
180
+ loc.country_code = comp['short_name']
181
+ loc.country = comp['long_name']
164
182
  when comp['types'].include?("administrative_area_level_2")
165
- res.district = comp['long_name']
183
+ loc.district = comp['long_name']
166
184
  when comp['types'].include?('neighborhood')
167
- res.neighborhood = comp['short_name']
185
+ loc.neighborhood = comp['short_name']
168
186
  end
169
187
  end
170
- if res.street_name
171
- res.street_address=[res.street_number,res.street_name].join(' ').strip
172
- end
173
- res.accuracy = ACCURACY[addr['geometry']['location_type']]
174
- res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
188
+ end
189
+
190
+ def self.set_precision(loc, addr)
191
+ loc.accuracy = ACCURACY[addr['geometry']['location_type']]
192
+ loc.precision=%w{unknown country state state city zip zip+4 street address building}[loc.accuracy]
175
193
  # try a few overrides where we can
176
- if res.sub_premise
177
- res.accuracy = 9
178
- res.precision = 'building'
194
+ if loc.sub_premise
195
+ loc.accuracy = 9
196
+ loc.precision = 'building'
179
197
  end
180
- if res.street_name && res.precision=='city'
181
- res.precision = 'street'
182
- res.accuracy = 7
198
+ if loc.street_name && loc.precision=='city'
199
+ loc.precision = 'street'
200
+ loc.accuracy = 7
183
201
  end
184
-
185
- res.lat=addr['geometry']['location']['lat'].to_f
186
- res.lng=addr['geometry']['location']['lng'].to_f
187
-
188
- ne=Geokit::LatLng.new(
189
- addr['geometry']['viewport']['northeast']['lat'].to_f,
190
- addr['geometry']['viewport']['northeast']['lng'].to_f
191
- )
192
- sw=Geokit::LatLng.new(
193
- addr['geometry']['viewport']['southwest']['lat'].to_f,
194
- addr['geometry']['viewport']['southwest']['lng'].to_f
195
- )
196
- res.suggested_bounds = Geokit::Bounds.new(sw,ne)
197
-
198
- res
199
202
  end
200
203
  end
201
- Google3Geocoder = GoogleGeocoder3
202
204
  end
203
205
  end