GUI-graticule 0.2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGELOG.txt +46 -0
  2. data/LICENSE.txt +30 -0
  3. data/Manifest.txt +73 -0
  4. data/README.txt +29 -0
  5. data/Rakefile +86 -0
  6. data/bin/geocode +5 -0
  7. data/init.rb +2 -0
  8. data/lib/graticule.rb +26 -0
  9. data/lib/graticule/cli.rb +64 -0
  10. data/lib/graticule/distance.rb +29 -0
  11. data/lib/graticule/distance/haversine.rb +40 -0
  12. data/lib/graticule/distance/spherical.rb +52 -0
  13. data/lib/graticule/distance/vincenty.rb +76 -0
  14. data/lib/graticule/geocoder.rb +21 -0
  15. data/lib/graticule/geocoder/base.rb +138 -0
  16. data/lib/graticule/geocoder/bogus.rb +15 -0
  17. data/lib/graticule/geocoder/geocoder_ca.rb +54 -0
  18. data/lib/graticule/geocoder/geocoder_us.rb +49 -0
  19. data/lib/graticule/geocoder/google.rb +122 -0
  20. data/lib/graticule/geocoder/host_ip.rb +41 -0
  21. data/lib/graticule/geocoder/local_search_maps.rb +45 -0
  22. data/lib/graticule/geocoder/map_quest.rb +111 -0
  23. data/lib/graticule/geocoder/meta_carta.rb +33 -0
  24. data/lib/graticule/geocoder/multi.rb +80 -0
  25. data/lib/graticule/geocoder/postcode_anywhere.rb +63 -0
  26. data/lib/graticule/geocoder/rest.rb +18 -0
  27. data/lib/graticule/geocoder/yahoo.rb +102 -0
  28. data/lib/graticule/location.rb +488 -0
  29. data/lib/graticule/version.rb +9 -0
  30. data/site/index.html +114 -0
  31. data/site/plugin.html +82 -0
  32. data/site/stylesheets/style.css +73 -0
  33. data/test/config.yml.default +36 -0
  34. data/test/fixtures/responses/geocoder_us/success.xml +18 -0
  35. data/test/fixtures/responses/geocoder_us/unknown.xml +1 -0
  36. data/test/fixtures/responses/google/badkey.xml +1 -0
  37. data/test/fixtures/responses/google/limit.xml +10 -0
  38. data/test/fixtures/responses/google/missing_address.xml +1 -0
  39. data/test/fixtures/responses/google/only_coordinates.xml +1 -0
  40. data/test/fixtures/responses/google/partial.xml +1 -0
  41. data/test/fixtures/responses/google/server_error.xml +10 -0
  42. data/test/fixtures/responses/google/success.xml +1 -0
  43. data/test/fixtures/responses/google/unavailable.xml +1 -0
  44. data/test/fixtures/responses/google/unknown_address.xml +1 -0
  45. data/test/fixtures/responses/host_ip/private.txt +4 -0
  46. data/test/fixtures/responses/host_ip/success.txt +4 -0
  47. data/test/fixtures/responses/host_ip/unknown.txt +4 -0
  48. data/test/fixtures/responses/local_search_maps/empty.txt +1 -0
  49. data/test/fixtures/responses/local_search_maps/not_found.txt +1 -0
  50. data/test/fixtures/responses/local_search_maps/success.txt +1 -0
  51. data/test/fixtures/responses/meta_carta/bad_address.xml +17 -0
  52. data/test/fixtures/responses/meta_carta/multiple.xml +33 -0
  53. data/test/fixtures/responses/meta_carta/success.xml +31 -0
  54. data/test/fixtures/responses/postcode_anywhere/badkey.xml +9 -0
  55. data/test/fixtures/responses/postcode_anywhere/canada.xml +16 -0
  56. data/test/fixtures/responses/postcode_anywhere/empty.xml +16 -0
  57. data/test/fixtures/responses/postcode_anywhere/success.xml +16 -0
  58. data/test/fixtures/responses/postcode_anywhere/uk.xml +18 -0
  59. data/test/fixtures/responses/yahoo/success.xml +3 -0
  60. data/test/fixtures/responses/yahoo/unknown_address.xml +6 -0
  61. data/test/mocks/uri.rb +52 -0
  62. data/test/test_helper.rb +32 -0
  63. data/test/unit/graticule/distance_test.rb +58 -0
  64. data/test/unit/graticule/geocoder/geocoder_us_test.rb +43 -0
  65. data/test/unit/graticule/geocoder/google_test.rb +126 -0
  66. data/test/unit/graticule/geocoder/host_ip_test.rb +40 -0
  67. data/test/unit/graticule/geocoder/local_search_maps_test.rb +30 -0
  68. data/test/unit/graticule/geocoder/meta_carta_test.rb +44 -0
  69. data/test/unit/graticule/geocoder/multi_test.rb +43 -0
  70. data/test/unit/graticule/geocoder/postcode_anywhere_test.rb +50 -0
  71. data/test/unit/graticule/geocoder/yahoo_test.rb +58 -0
  72. data/test/unit/graticule/geocoder_test.rb +27 -0
  73. data/test/unit/graticule/location_test.rb +66 -0
  74. metadata +158 -0
@@ -0,0 +1,52 @@
1
+ module Graticule
2
+ module Distance
3
+
4
+ #
5
+ # The Spherical Law of Cosines is the simplist though least accurate distance
6
+ # formula (earth isn't a perfect sphere).
7
+ #
8
+ class Spherical < DistanceFormula
9
+
10
+ # Calculate the distance between two Locations using the Spherical formula
11
+ #
12
+ # Graticule::Distance::Spherical.distance(
13
+ # Graticule::Location.new(:latitude => 42.7654, :longitude => -86.1085),
14
+ # Graticule::Location.new(:latitude => 41.849838, :longitude => -87.648193)
15
+ # )
16
+ # #=> 101.061720831853
17
+ #
18
+ def self.distance(from, to, units = :miles)
19
+ from_longitude = deg2rad(from.longitude)
20
+ from_latitude = deg2rad(from.latitude)
21
+ to_longitude = deg2rad(to.longitude)
22
+ to_latitude = deg2rad(to.latitude)
23
+
24
+ Math.acos(
25
+ Math.sin(from_latitude) *
26
+ Math.sin(to_latitude) +
27
+
28
+ Math.cos(from_latitude) *
29
+ Math.cos(to_latitude) *
30
+ Math.cos(to_longitude - from_longitude)
31
+ ) * EARTH_RADIUS[units.to_sym]
32
+ end
33
+
34
+ def self.to_sql(options)
35
+ options = {
36
+ :units => :miles,
37
+ :latitude_column => 'latitude',
38
+ :longitude_column => 'longitude'
39
+ }.merge(options)
40
+ %{(ACOS(
41
+ SIN(RADIANS(#{options[:latitude]})) *
42
+ SIN(RADIANS(#{options[:latitude_column]})) +
43
+ COS(RADIANS(#{options[:latitude]})) *
44
+ COS(RADIANS(#{options[:latitude_column]})) *
45
+ COS(RADIANS(#{options[:longitude_column]}) - RADIANS(#{options[:longitude]}))
46
+ ) * #{Graticule::Distance::EARTH_RADIUS[options[:units].to_sym]})
47
+ }.gsub("\n", '').squeeze(" ")
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,76 @@
1
+ module Graticule
2
+ module Distance
3
+
4
+ #
5
+ # The Vincenty Formula uses an ellipsoidal model of the earth, which is very accurate.
6
+ #
7
+ # Thanks to Chris Veness (http://www.movable-type.co.uk/scripts/LatLongVincenty.html)
8
+ # for distance formulas.
9
+ #
10
+ class Vincenty < DistanceFormula
11
+
12
+ # Calculate the distance between two Locations using the Vincenty formula
13
+ #
14
+ # Graticule::Distance::Vincenty.distance(
15
+ # Graticule::Location.new(:latitude => 42.7654, :longitude => -86.1085),
16
+ # Graticule::Location.new(:latitude => 41.849838, :longitude => -87.648193)
17
+ # )
18
+ # #=> 101.070118000159
19
+ #
20
+ def self.distance(from, to, units = :miles)
21
+ from_longitude = deg2rad(from.longitude)
22
+ from_latitude = deg2rad(from.latitude)
23
+ to_longitude = deg2rad(to.longitude)
24
+ to_latitude = deg2rad(to.latitude)
25
+
26
+ earth_major_axis_radius = EARTH_MAJOR_AXIS_RADIUS[units.to_sym]
27
+ earth_minor_axis_radius = EARTH_MINOR_AXIS_RADIUS[units.to_sym]
28
+
29
+ f = (earth_major_axis_radius - earth_minor_axis_radius) / earth_major_axis_radius
30
+
31
+ l = to_longitude - from_longitude
32
+ u1 = atan((1-f) * tan(from_latitude))
33
+ u2 = atan((1-f) * tan(to_latitude))
34
+ sin_u1 = sin(u1)
35
+ cos_u1 = cos(u1)
36
+ sin_u2 = sin(u2)
37
+ cos_u2 = cos(u2)
38
+
39
+ lambda = l
40
+ lambda_p = 2 * PI
41
+ iteration_limit = 20
42
+ while (lambda-lambda_p).abs > 1e-12 && (iteration_limit -= 1) > 0
43
+ sin_lambda = sin(lambda)
44
+ cos_lambda = cos(lambda)
45
+ sin_sigma = sqrt((cos_u2*sin_lambda) * (cos_u2*sin_lambda) +
46
+ (cos_u1*sin_u2-sin_u1*cos_u2*cos_lambda) * (cos_u1*sin_u2-sin_u1*cos_u2*cos_lambda))
47
+ return 0 if sin_sigma == 0 # co-incident points
48
+ cos_sigma = sin_u1*sin_u2 + cos_u1*cos_u2*cos_lambda
49
+ sigma = atan2(sin_sigma, cos_sigma)
50
+ sin_alpha = cos_u1 * cos_u2 * sin_lambda / sin_sigma
51
+ cosSqAlpha = 1 - sin_alpha*sin_alpha
52
+ cos2SigmaM = cos_sigma - 2*sin_u1*sin_u2/cosSqAlpha
53
+
54
+ cos2SigmaM = 0 if cos2SigmaM.nan? # equatorial line: cosSqAlpha=0 (§6)
55
+
56
+ c = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
57
+ lambda_p = lambda
58
+ lambda = l + (1-c) * f * sin_alpha *
59
+ (sigma + c*sin_sigma*(cos2SigmaM+c*cos_sigma*(-1+2*cos2SigmaM*cos2SigmaM)))
60
+ end
61
+ # formula failed to converge (happens on antipodal points)
62
+ # We'll call Haversine formula instead.
63
+ return Haversine.distance(from, to, units) if iteration_limit == 0
64
+
65
+ uSq = cosSqAlpha * (earth_major_axis_radius**2 - earth_minor_axis_radius**2) / (earth_minor_axis_radius**2)
66
+ a = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
67
+ b = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
68
+ delta_sigma = b*sin_sigma*(cos2SigmaM+b/4*(cos_sigma*(-1+2*cos2SigmaM*cos2SigmaM)-
69
+ b/6*cos2SigmaM*(-3+4*sin_sigma*sin_sigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
70
+
71
+ earth_minor_axis_radius * a * (sigma-delta_sigma)
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Graticule
3
+
4
+ # Get a geocoder for the given service
5
+ #
6
+ # geocoder = Graticule.service(:google).new "api_key"
7
+ #
8
+ # See the documentation for your specific geocoder for more information
9
+ #
10
+ def self.service(name)
11
+ Geocoder.const_get name.to_s.camelize
12
+ end
13
+
14
+ # Base error class
15
+ class Error < RuntimeError; end
16
+ class CredentialsError < Error; end
17
+
18
+ # Raised when you try to locate an invalid address.
19
+ class AddressError < Error; end
20
+
21
+ end
@@ -0,0 +1,138 @@
1
+ require 'open-uri'
2
+ require "cgi"
3
+
4
+ module Graticule #:nodoc:
5
+ module Geocoder
6
+
7
+ # Abstract class for implementing geocoders.
8
+ #
9
+ # === Example
10
+ #
11
+ # The following methods must be implemented in sublcasses:
12
+ #
13
+ # * +initialize+:: Sets @url to the service enpoint.
14
+ # * +check_error+:: Checks for errors in the server response.
15
+ # * +parse_response+:: Extracts information from the server response.
16
+ #
17
+ # Optionally, you can also override
18
+ #
19
+ # * +prepare_response+:: Convert the string response into a different format
20
+ # that gets passed on to +check_error+ and +parse_response+.
21
+ #
22
+ # If you have extra URL paramaters (application id, output type) or need to
23
+ # perform URL customization, override +make_url+.
24
+ #
25
+ # class FakeGeocoder < Base
26
+ #
27
+ # def initialize(appid)
28
+ # @appid = appid
29
+ # @url = URI.parse 'http://example.com/test'
30
+ # end
31
+ #
32
+ # def locate(query)
33
+ # get :q => query
34
+ # end
35
+ #
36
+ # private
37
+ #
38
+ # def check_error(xml)
39
+ # raise Error, xml.elements['error'].text if xml.elements['error']
40
+ # end
41
+ #
42
+ # def make_url(params)
43
+ # params[:appid] = @appid
44
+ # super params
45
+ # end
46
+ #
47
+ # def parse_first(response)
48
+ # # return Location
49
+ # end
50
+ #
51
+ # end
52
+ #
53
+ class Base
54
+ include Comparable
55
+
56
+ USER_AGENT = "Mozilla/5.0 (compatible; Graticule/#{Graticule::Version::STRING}; http://graticule.rubyforge.org)"
57
+
58
+ attr_accessor :preference
59
+
60
+ def initialize
61
+ raise NotImplementedError
62
+ end
63
+
64
+ def <=>(other)
65
+ other.preference <=> preference
66
+ end
67
+
68
+ private
69
+
70
+ def location_from_params(params)
71
+ case params
72
+ when Location then params
73
+ when Hash then Location.new params
74
+ else
75
+ raise ArgumentError, "Expected a Graticule::Location or a hash with :street, :locality, :region, :postal_code, and :country attributes"
76
+ end
77
+ end
78
+
79
+ # Check for errors in +response+ and raise appropriate error, if any.
80
+ # Must return if no error could be found.
81
+ def check_error(response)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # Performs a GET request with +params+. Calls +check_error+ and returns
86
+ # the result of +parse_response+.
87
+ def get(*args)
88
+ params = args.last.is_a?(::Hash) ? args.pop : args
89
+ response = prepare_response(make_url(params).open('User-Agent' => USER_AGENT).read)
90
+ check_error(response)
91
+
92
+ result = parse_response(args.first, response)
93
+
94
+ # Make sure the parse_response method was overriden by a subclass to
95
+ # return a Location object. If it was, set its geocoder attribute to
96
+ # the current geocoder, so we know where the result came from.
97
+ if(result.kind_of? Array)
98
+ result.each do |location|
99
+ if(location.instance_of? Location)
100
+ location.geocoder = self
101
+ end
102
+ end
103
+ elsif(result.instance_of? Location)
104
+ result.geocoder = self
105
+ end
106
+
107
+ result
108
+ rescue OpenURI::HTTPError => e
109
+ check_error(prepare_response(e.io.read))
110
+ raise
111
+ end
112
+
113
+ # Creates a URI from the Hash +params+. Override this then call super if
114
+ # you need to add extra params like an application id or output type.
115
+ def make_url(params)
116
+ escaped_params = params.sort_by { |k,v| k.to_s }.map do |k,v|
117
+ "#{CGI.escape k.to_s}=#{CGI.escape v.to_s}"
118
+ end
119
+
120
+ url = @url.dup
121
+ url.query = escaped_params.join '&'
122
+ return url
123
+ end
124
+
125
+ # Override to convert the response to something besides a String, which
126
+ # will get passed to +check_error+ and +parse_response+.
127
+ def prepare_response(response)
128
+ response
129
+ end
130
+
131
+ # Must parse :first or :all results from +response+ into a Location.
132
+ def parse_response(parse_type, response)
133
+ raise NotImplementedError
134
+ end
135
+
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,15 @@
1
+ module Graticule #:nodoc:
2
+ module Geocoder #:nodoc:
3
+
4
+ # Bogus geocoder that can be used for test purposes
5
+ class Bogus
6
+
7
+ # returns a new location with the address set to the original query string
8
+ def locate(address)
9
+ Location.new :street => address
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ module Graticule #:nodoc:
2
+ module Geocoder #:nodoc:
3
+
4
+ # TODO: Reverse Geocoding
5
+ class GeocoderCa < Rest
6
+
7
+ def initialize(auth = nil)
8
+ @url = URI.parse 'http://geocoder.ca/'
9
+ @auth = auth
10
+ end
11
+
12
+ def locate(address)
13
+ get :locate => address.is_a?(String) ? address : location_from_params(address).to_s(:country => false)
14
+ end
15
+
16
+ private
17
+
18
+ def parse_response(parse_type, xml) #:nodoc:
19
+ returning Location.new do |location|
20
+ location.latitude = xml.elements['geodata/latt'].text.to_f
21
+ location.longitude = xml.elements['geodata/longt'].text.to_f
22
+ location.street = xml.elements['geodata/standard/staddress'].text
23
+ location.locality = xml.elements['geodata/standard/city'].text
24
+ location.region = xml.elements['geodata/standard/prov'].text
25
+ end
26
+ end
27
+
28
+ def check_error(xml) #:nodoc:
29
+ error = xml.elements['geodata/error']
30
+ if error
31
+ code = error.elements['code'].text.to_i
32
+ message = error.elements['description'].text
33
+ if (1..3).include?(code)
34
+ raise CredentialsError, message
35
+ elsif (4..8).include?(code)
36
+ raise AddressError, message
37
+ else
38
+ raise Error, message
39
+ end
40
+ end
41
+ end
42
+
43
+ def make_url(params) #:nodoc:
44
+ params[:auth] = @auth if @auth
45
+ params[:standard] = 1
46
+ params[:showpostal] = 1
47
+ params[:geoit] = 'XML'
48
+ super params
49
+ end
50
+
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ module Graticule #:nodoc:
2
+ module Geocoder #:nodoc:
3
+
4
+ # A library for lookup up coordinates with geocoder.us' API.
5
+ #
6
+ # http://geocoder.us/help/
7
+ class GeocoderUs < Rest
8
+
9
+ # Creates a new GeocoderUs object optionally using +username+ and
10
+ # +password+.
11
+ #
12
+ # You can sign up for a geocoder.us account here:
13
+ #
14
+ # http://geocoder.us/user/signup
15
+ def initialize(user = nil, password = nil)
16
+ if user and password then
17
+ @url = URI.parse 'http://geocoder.us/member/service/rest/geocode'
18
+ @url.user = user
19
+ @url.password = password
20
+ else
21
+ @url = URI.parse 'http://rpc.geocoder.us/service/rest/geocode'
22
+ end
23
+ end
24
+
25
+ # Locates +address+ and returns the address' latitude and longitude or
26
+ # raises an AddressError.
27
+ def locate(address)
28
+ get :address => address.is_a?(String) ? address : location_from_params(address).to_s(:country => false)
29
+ end
30
+
31
+ private
32
+
33
+ def parse_response(parse_type, xml) #:nodoc:
34
+ location = Location.new
35
+ location.street = xml.elements['rdf:RDF/geo:Point/dc:description'].text
36
+
37
+ location.latitude = xml.elements['rdf:RDF/geo:Point/geo:lat'].text.to_f
38
+ location.longitude = xml.elements['rdf:RDF/geo:Point/geo:long'].text.to_f
39
+
40
+ return location
41
+ end
42
+
43
+ def check_error(xml) #:nodoc:
44
+ raise AddressError, xml.text if xml.text =~ /couldn't find this address! sorry/
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,122 @@
1
+
2
+ module Graticule #:nodoc:
3
+ module Geocoder #:nodoc:
4
+
5
+ # First you need a Google Maps API key. You can register for one here:
6
+ # http://www.google.com/apis/maps/signup.html
7
+ #
8
+ # gg = Graticule.service(:google).new(MAPS_API_KEY)
9
+ # location = gg.locate '1600 Amphitheater Pkwy, Mountain View, CA'
10
+ # p location.coordinates
11
+ # #=> [37.423111, -122.081783
12
+ #
13
+ class Google < Rest
14
+ # http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request
15
+
16
+ # http://www.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
17
+ PRECISION = {
18
+ 0 => Precision.unknown, # Unknown location. (Since 2.59)
19
+ 1 => Precision.country, # Country level accuracy. (Since 2.59)
20
+ 2 => Precision.state, # Region (state, province, prefecture, etc.) level accuracy. (Since 2.59)
21
+ 3 => Precision.state, # Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
22
+ 4 => Precision.city, # Town (city, village) level accuracy. (Since 2.59)
23
+ 5 => Precision.zip, # Post code (zip code) level accuracy. (Since 2.59)
24
+ 6 => Precision.street, # Street level accuracy. (Since 2.59)
25
+ 7 => Precision.street, # Intersection level accuracy. (Since 2.59)
26
+ 8 => Precision.address, # Address level accuracy. (Since 2.59)
27
+ 9 => Precision.premise # Premise (building name, property name, shopping center, etc.) level accuracy. (Since 2.105)
28
+ }
29
+
30
+ # Creates a new GoogleGeocode that will use Google Maps API +key+.
31
+ def initialize(key)
32
+ @key = key
33
+ @url = URI.parse 'http://maps.google.com/maps/geo'
34
+ end
35
+
36
+ # Locates +address+ returning a Location
37
+ def locate(*args)
38
+ case args.first
39
+ when :first then get :first, :q => extract_location(args[1])
40
+ when :all then get :all, :q => extract_location(args[1])
41
+ else get :first, :q => extract_location(args.first)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def extract_location(address)
48
+ address.is_a?(String) ? address : location_from_params(address).to_s
49
+ end
50
+
51
+ # Extracts a Location from +xml+.
52
+ def parse_response(parse_type, xml) #:nodoc:
53
+ # puts xml.elements['//Placemark[1]'].to_s
54
+ if parse_type == :all
55
+ locations = []
56
+ xml.elements.each("//Placemark") do |element|
57
+ locations << parse_location_xml(element)
58
+ end
59
+ return locations
60
+ else
61
+ return parse_location_xml(xml.elements['//Placemark[1]'])
62
+ end
63
+ end
64
+
65
+ def parse_location_xml(xml)
66
+ longitude, latitude, = xml.elements['.//Point/coordinates'].text.split(',').map { |v| v.to_f }
67
+ returning Location.new(:latitude => latitude, :longitude => longitude) do |l|
68
+ address = REXML::XPath.first(xml, './/xal:AddressDetails',
69
+ 'xal' => "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
70
+ if address
71
+ l.premise = value(address.elements['.//PremiseName/text()'])
72
+ l.street = value(address.elements['.//ThoroughfareName/text()'])
73
+ l.locality = value(address.elements['.//LocalityName/text()'])
74
+ l.region = value(address.elements['.//AdministrativeAreaName/text()'])
75
+ l.postal_code = value(address.elements['.//PostalCodeNumber/text()'])
76
+ l.country = value(address.elements['.//CountryNameCode/text()'])
77
+ l.precision = PRECISION[address.attribute('Accuracy').value.to_i] || Precision.unknown
78
+ end
79
+ end
80
+ end
81
+
82
+ # Extracts and raises an error from +xml+, if any.
83
+ def check_error(xml) #:nodoc:
84
+ status = xml.elements['/kml/Response/Status/code'].text.to_i
85
+ case status
86
+ when 200 then # ignore, ok
87
+ when 500 then
88
+ raise Error, 'server error'
89
+ when 601 then
90
+ raise AddressError, 'missing address'
91
+ when 602 then
92
+ raise AddressError, 'unknown address'
93
+ when 603 then
94
+ raise AddressError, 'unavailable address'
95
+ when 610 then
96
+ raise CredentialsError, 'invalid key'
97
+ when 620 then
98
+ raise CredentialsError, 'too many queries'
99
+ else
100
+ raise Error, "unknown error #{status}"
101
+ end
102
+ end
103
+
104
+ # Creates a URL from the Hash +params+. Automatically adds the key and
105
+ # sets the output type to 'xml'.
106
+ def make_url(params) #:nodoc:
107
+ params[:key] = @key
108
+ params[:output] = 'xml'
109
+
110
+ super params
111
+ end
112
+
113
+ def value(element)
114
+ element.value if element
115
+ end
116
+
117
+ def text(element)
118
+ element.text if element
119
+ end
120
+ end
121
+ end
122
+ end