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.
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