graticule 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,12 @@
1
+ 2.3.0 (2013-04-01)
2
+ * Google v3 API [Adamlb]
3
+ * Escape XML entities in GeoCoder::MapQuest queries [Simon Coffey]
4
+
5
+ 2.2.0 (2011-09-14)
6
+ * Added yandex geocoder
7
+ * Lots of other changes since September 2009.
8
+ Check https://github.com/collectiveidea/graticule/compare/v0.2.12...master for more info
9
+
1
10
  * Added freethepostcode.org geocoder [Chris Lowis]
2
11
 
3
12
  0.2.12 (2009-09-06)
@@ -38,16 +38,15 @@ module Graticule
38
38
  :latitude_column => 'latitude',
39
39
  :longitude_column => 'longitude'
40
40
  }.merge(options)
41
- %{(ACOS(
41
+ %{(ACOS(LEAST(1,
42
42
  SIN(RADIANS(#{options[:latitude]})) *
43
43
  SIN(RADIANS(#{options[:latitude_column]})) +
44
44
  COS(RADIANS(#{options[:latitude]})) *
45
45
  COS(RADIANS(#{options[:latitude_column]})) *
46
46
  COS(RADIANS(#{options[:longitude_column]}) - RADIANS(#{options[:longitude]}))
47
- ) * #{Graticule::Distance::EARTH_RADIUS[options[:units].to_sym]})
47
+ )) * #{Graticule::Distance::EARTH_RADIUS[options[:units].to_sym]})
48
48
  }.gsub("\n", '').squeeze(" ")
49
49
  end
50
-
51
50
  end
52
51
  end
53
52
  end
@@ -81,6 +81,7 @@ module Graticule #:nodoc:
81
81
  check_error(response)
82
82
  return parse_response(response)
83
83
  rescue OpenURI::HTTPError => e
84
+ raise e.inspect
84
85
  check_error(prepare_response(e.io.read))
85
86
  raise
86
87
  end
@@ -91,7 +92,6 @@ module Graticule #:nodoc:
91
92
  escaped_params = params.sort_by { |k,v| k.to_s }.map do |k,v|
92
93
  "#{escape k.to_s}=#{escape v.to_s}"
93
94
  end
94
-
95
95
  url = @url.dup
96
96
  url.query = escaped_params.join '&'
97
97
  return url
@@ -1,93 +1,102 @@
1
1
  # encoding: UTF-8
2
+ require 'json'
2
3
  module Graticule #:nodoc:
3
4
  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)
5
+ # gg = Graticule.service(:google).new(MAPS_API_KEY) API Key is not required
9
6
  # location = gg.locate '1600 Amphitheater Pkwy, Mountain View, CA'
10
7
  # p location.coordinates
11
8
  # #=> [37.423111, -122.081783
12
9
  #
13
10
  class Google < Base
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.
19
- 1 => Precision::Country, # Country level accuracy.
20
- 2 => Precision::Region, # Region (state, province, prefecture, etc.) level accuracy.
21
- 3 => Precision::Region, # Sub-region (county, municipality, etc.) level accuracy.
22
- 4 => Precision::Locality, # Town (city, village) level accuracy.
23
- 5 => Precision::PostalCode, # Post code (zip code) level accuracy.
24
- 6 => Precision::Street, # Street level accuracy.
25
- 7 => Precision::Street, # Intersection level accuracy.
26
- 8 => Precision::Address, # Address level accuracy.
27
- 9 => Precision::Premise # Premise (building name, property name, shopping center, etc.) level accuracy.
28
- }
11
+ # https://developers.google.com/maps/documentation/geocoding/
29
12
 
30
13
  def initialize(key)
31
14
  @key = key
32
- @url = URI.parse 'http://maps.google.com/maps/geo'
15
+ @url = URI.parse 'http://maps.googleapis.com/maps/api/geocode/json'
33
16
  end
34
17
 
35
18
  # Locates +address+ returning a Location
36
19
  def locate(address)
37
- get :q => address.is_a?(String) ? address : location_from_params(address).to_s
20
+ get :address => address.is_a?(String) ? address : location_from_params(address).to_s
38
21
  end
39
22
 
40
23
  private
41
- class Address
42
- include HappyMapper
43
- tag 'AddressDetails'
44
- namespace 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'
45
-
46
- attribute :accuracy, Integer, :tag => 'Accuracy'
47
- end
48
-
49
- class Placemark
50
- include HappyMapper
51
- tag 'Placemark'
52
- element :coordinates, String, :deep => true
53
- has_one :address, Address
54
-
55
- attr_reader :longitude, :latitude
56
-
57
- with_options :deep => true, :namespace => 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0' do |map|
58
- map.element :street, String, :tag => 'ThoroughfareName'
59
- map.element :locality, String, :tag => 'LocalityName'
60
- map.element :region, String, :tag => 'AdministrativeAreaName'
61
- map.element :postal_code, String, :tag => 'PostalCodeNumber'
62
- map.element :country, String, :tag => 'CountryNameCode'
24
+ class Result
25
+ attr_accessor :latitude, :longitude, :street_number, :route, :locality, :region, :postal_code, :country, :precision, :formatted_address
26
+ def initialize(attribs)
27
+ @latitude = attribs["geometry"]["location"]["lat"]
28
+ @longitude = attribs["geometry"]["location"]["lng"]
29
+ @formatted_address = attribs["formatted_address"]
30
+ @precision = determine_precision(attribs["types"])
31
+ parse_address_components(attribs["address_components"])
63
32
  end
64
33
 
65
- def coordinates=(coordinates)
66
- @longitude, @latitude, _ = coordinates.split(',').map { |v| v.to_f }
34
+ def parse_address_components(components)
35
+ components.each do |component|
36
+ component["types"].each do |type|
37
+ case type
38
+ when "street_number"
39
+ @street_number = component["short_name"]
40
+ when "route"
41
+ @route = component["short_name"]
42
+ when "locality"
43
+ @locality = component["long_name"]
44
+ when "administrative_area_level_1"
45
+ @region = component["short_name"]
46
+ when "country"
47
+ @country = component["short_name"]
48
+ when "postal_code"
49
+ @postal_code = component["long_name"]
50
+ end
51
+ end
52
+ end
67
53
  end
68
54
 
69
- def accuracy
70
- address.accuracy if address
55
+ def street
56
+ "#{@street_number.to_s}#{" " unless @street_number.blank? || @route.blank?}#{@route.to_s}"
71
57
  end
72
58
 
73
- def precision
74
- PRECISION[accuracy] || :unknown
59
+ def determine_precision(types)
60
+ precision = Precision::Unknown
61
+ types.each do |type|
62
+ precision = case type
63
+ when "premise", "subpremise"
64
+ Precision::Premise
65
+ when "street_address"
66
+ Precision::Address
67
+ when "route", "intersection"
68
+ Precision::Street
69
+ when "postal_code"
70
+ Precision::PostalCode
71
+ when "locality","sublocality","neighborhood"
72
+ Precision::Locality
73
+ when "administrative_area_level_1", "administrative_area_level_2","administrative_area_level_3"
74
+ Precision::Region
75
+ when "country"
76
+ Precision::Country
77
+ else
78
+ precision
79
+ end
80
+ end
81
+ return precision
75
82
  end
76
83
  end
77
84
 
78
85
  class Response
79
- include HappyMapper
80
- tag 'Response'
81
- element :code, Integer, :tag => 'code', :deep => true
82
- has_many :placemarks, Placemark
86
+ attr_reader :results, :status
87
+ def initialize(json)
88
+ result = JSON.parse(json)
89
+ @results = result["results"].collect{|attribs| Result.new(attribs)}
90
+ @status = result["status"]
91
+ end
83
92
  end
84
93
 
85
- def prepare_response(xml)
86
- Response.parse(xml, :single => true)
94
+ def prepare_response(json)
95
+ Response.new(json)
87
96
  end
88
97
 
89
98
  def parse_response(response) #:nodoc:
90
- result = response.placemarks.first
99
+ result = response.results.first
91
100
  Location.new(
92
101
  :latitude => result.latitude,
93
102
  :longitude => result.longitude,
@@ -100,31 +109,28 @@ module Graticule #:nodoc:
100
109
  )
101
110
  end
102
111
 
103
- # Extracts and raises an error from +xml+, if any.
112
+ # Extracts and raises an error from +json+, if any.
104
113
  def check_error(response) #:nodoc:
105
- case response.code
106
- when 200 then # ignore, ok
107
- when 500 then
108
- raise Error, 'server error'
109
- when 601 then
110
- raise AddressError, 'missing address'
111
- when 602 then
114
+ case response.status
115
+ when "OK" then # ignore, ok
116
+ when "ZERO_RESULTS" then
112
117
  raise AddressError, 'unknown address'
113
- when 603 then
114
- raise AddressError, 'unavailable address'
115
- when 610 then
116
- raise CredentialsError, 'invalid key'
117
- when 620 then
118
- raise CredentialsError, 'too many queries'
118
+ when "OVER_QUERY_LIMIT"
119
+ raise CredentialsError, 'over query limit'
120
+ when "REQUEST_DENIED"
121
+ raise CredentialsError, 'request denied'
122
+ when "INVALID_REQUEST"
123
+ raise AddressError, 'missing address'
124
+ when "UNKNOWN_ERROR"
125
+ raise Error, "unknown server error. Try again."
119
126
  else
120
- raise Error, "unknown error #{response.code}"
127
+ raise Error, "unkown error #{response.status}"
121
128
  end
122
129
  end
123
130
 
124
- # Creates a URL from the Hash +params+.
125
- # sets the output type to 'xml'.
131
+ # Creates a URL from the Hash +params+..
126
132
  def make_url(params) #:nodoc:
127
- super params.merge(:key => @key, :oe => 'utf8', :output => 'xml', :sensor => false)
133
+ super params.merge(:key => @key, :sensor => false)
128
134
  end
129
135
  end
130
136
  end
@@ -1,3 +1,5 @@
1
+ require 'htmlentities'
2
+
1
3
  # encoding: UTF-8
2
4
  module Graticule #:nodoc:
3
5
  module Geocoder #:nodoc:
@@ -43,13 +45,36 @@ module Graticule #:nodoc:
43
45
  protected
44
46
 
45
47
  def make_url(params) #:nodoc
46
- query = "e=5&<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><Geocode Version=\"1\"> \
47
- #{address_string(params[:q])}#{authentication_string}</Geocode>"
48
+ request = Mapquest::Request.new(params[:q], @client_id, @password)
48
49
  url = @url.dup
49
- url.query = escape(query)
50
+ url.query = escape(request.query)
50
51
  url
51
52
  end
52
53
 
54
+ class Request
55
+ def initialize(address, client_id, password)
56
+ @address = address
57
+ @client_id = client_id
58
+ @password = password
59
+ end
60
+
61
+ def query
62
+ "e=5&<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><Geocode Version=\"1\">#{address_string}#{authentication_string}</Geocode>"
63
+ end
64
+
65
+ def address_string
66
+ "<Address><Street>#{escaped_address}</Street></Address><GeocodeOptionsCollection Count=\"0\"/>"
67
+ end
68
+
69
+ def authentication_string
70
+ "<Authentication Version=\"2\"><Password>#{@password}</Password><ClientId>#{@client_id}</ClientId></Authentication>"
71
+ end
72
+
73
+ def escaped_address
74
+ HTMLEntities.new.encode(@address, :basic)
75
+ end
76
+ end
77
+
53
78
  class Address
54
79
  include HappyMapper
55
80
  tag 'GeoAddress'
@@ -96,13 +121,6 @@ module Graticule #:nodoc:
96
121
  def check_error(xml) #:nodoc
97
122
  end
98
123
 
99
- def authentication_string
100
- "<Authentication Version=\"2\"><Password>#{@password}</Password><ClientId>#{@client_id}</ClientId></Authentication>"
101
- end
102
-
103
- def address_string(query)
104
- "<Address><Street>#{query}</Street></Address><GeocodeOptionsCollection Count=\"0\"/>"
105
- end
106
124
  end
107
125
  end
108
126
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module Graticule
3
- VERSION = '2.2.0' unless defined?(::Graticule::VERSION)
3
+ VERSION = '2.3.0' unless defined?(::Graticule::VERSION)
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graticule
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-09-14 00:00:00.000000000Z
13
+ date: 2013-04-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: &70276838707980 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,15 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70276838707980
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
26
31
  - !ruby/object:Gem::Dependency
27
32
  name: i18n
28
- requirement: &70276838705640 !ruby/object:Gem::Requirement
33
+ requirement: !ruby/object:Gem::Requirement
29
34
  none: false
30
35
  requirements:
31
36
  - - ! '>='
@@ -33,10 +38,31 @@ dependencies:
33
38
  version: '0'
34
39
  type: :runtime
35
40
  prerelease: false
36
- version_requirements: *70276838705640
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: htmlentities
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
37
63
  - !ruby/object:Gem::Dependency
38
64
  name: happymapper
39
- requirement: &70276838703420 !ruby/object:Gem::Requirement
65
+ requirement: !ruby/object:Gem::Requirement
40
66
  none: false
41
67
  requirements:
42
68
  - - ! '>='
@@ -44,10 +70,15 @@ dependencies:
44
70
  version: 0.3.0
45
71
  type: :runtime
46
72
  prerelease: false
47
- version_requirements: *70276838703420
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 0.3.0
48
79
  - !ruby/object:Gem::Dependency
49
80
  name: json
50
- requirement: &70276838701800 !ruby/object:Gem::Requirement
81
+ requirement: !ruby/object:Gem::Requirement
51
82
  none: false
52
83
  requirements:
53
84
  - - ! '>='
@@ -55,10 +86,15 @@ dependencies:
55
86
  version: '0'
56
87
  type: :runtime
57
88
  prerelease: false
58
- version_requirements: *70276838701800
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
59
95
  - !ruby/object:Gem::Dependency
60
96
  name: mocha
61
- requirement: &70276838699580 !ruby/object:Gem::Requirement
97
+ requirement: !ruby/object:Gem::Requirement
62
98
  none: false
63
99
  requirements:
64
100
  - - ! '>='
@@ -66,10 +102,15 @@ dependencies:
66
102
  version: '0'
67
103
  type: :development
68
104
  prerelease: false
69
- version_requirements: *70276838699580
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
70
111
  - !ruby/object:Gem::Dependency
71
112
  name: rcov
72
- requirement: &70276838697520 !ruby/object:Gem::Requirement
113
+ requirement: !ruby/object:Gem::Requirement
73
114
  none: false
74
115
  requirements:
75
116
  - - ! '>='
@@ -77,7 +118,12 @@ dependencies:
77
118
  version: '0'
78
119
  type: :development
79
120
  prerelease: false
80
- version_requirements: *70276838697520
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
81
127
  description: Graticule is a geocoding API that provides a common interface to all
82
128
  the popular services, including Google, Yahoo, Geocoder.us, and MetaCarta.
83
129
  email: brandon@opensoul.org
@@ -135,7 +181,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
181
  version: '0'
136
182
  segments:
137
183
  - 0
138
- hash: 473484385783211524
184
+ hash: 3804880588215325222
139
185
  required_rubygems_version: !ruby/object:Gem::Requirement
140
186
  none: false
141
187
  requirements:
@@ -144,10 +190,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
190
  version: '0'
145
191
  segments:
146
192
  - 0
147
- hash: 473484385783211524
193
+ hash: 3804880588215325222
148
194
  requirements: []
149
195
  rubyforge_project: graticule
150
- rubygems_version: 1.8.6
196
+ rubygems_version: 1.8.25
151
197
  signing_key:
152
198
  specification_version: 3
153
199
  summary: API for using all the popular geocoding services.