googlemaps-services 1.2.5 → 1.3.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.
- checksums.yaml +4 -4
- data/lib/googlemaps/services/client.rb +88 -73
- data/lib/googlemaps/services/directions.rb +9 -3
- data/lib/googlemaps/services/distancematrix.rb +4 -3
- data/lib/googlemaps/services/elevation.rb +13 -10
- data/lib/googlemaps/services/geocoding.rb +7 -3
- data/lib/googlemaps/services/places.rb +10 -12
- data/lib/googlemaps/services/roads.rb +42 -35
- data/lib/googlemaps/services/staticmap.rb +99 -0
- data/lib/googlemaps/services/timezone.rb +1 -1
- data/lib/googlemaps/services/util.rb +48 -6
- data/lib/googlemaps/services/version.rb +1 -1
- metadata +27 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9af6339c46954459057f1f720aa53f4275158c2
|
4
|
+
data.tar.gz: e167756c771106d77778d36284960c429cb9105f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dba8b6e7c27f3ddd0e90ef28d981930ab97c148a7202b3b98862a0bd18d8c80467b15df9751bb17827f596dbc3592eca30a0c988e5e7fdb31f1ce989f13d5c22
|
7
|
+
data.tar.gz: ab24f530bae29cf059851bfcea3682766b693909addb9dfa16e37e7d1379be0c4f8a59eecb1c271bb2cef2b0fee89ab5e5c28b2439b0ee9f5c7c5895c6f58b9e
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'googlemaps/services/exceptions'
|
2
3
|
require 'googlemaps/services/version'
|
3
4
|
require 'googlemaps/services/util'
|
4
5
|
require 'nokogiri'
|
5
|
-
require '
|
6
|
+
require 'base64'
|
6
7
|
require 'json'
|
8
|
+
require 'http'
|
7
9
|
|
8
10
|
# Core functionality, common across all API requests.
|
9
11
|
#
|
@@ -13,28 +15,33 @@ module GoogleMaps
|
|
13
15
|
#
|
14
16
|
# @since 1.0.0
|
15
17
|
module Services
|
16
|
-
USER_AGENT = 'GoogleMapsRubyClient/' + VERSION
|
17
|
-
DEFAULT_BASE_URL = 'https://maps.googleapis.com'
|
18
|
-
RETRIABLE_STATUSES = [500, 503, 504]
|
19
18
|
|
20
19
|
# Performs requests to the Google Maps API web services.
|
21
20
|
class GoogleClient
|
21
|
+
USER_AGENT = 'GoogleMapsRubyClient/' + VERSION
|
22
|
+
DEFAULT_BASE_URL = 'https://maps.googleapis.com'
|
23
|
+
RETRIABLE_STATUSES = [500, 503, 504]
|
24
|
+
|
22
25
|
include GoogleMaps::Services::Exceptions
|
23
26
|
|
24
27
|
# @return [Symbol] API key. Required, unless "client_id" and "client_secret" are set.
|
25
28
|
attr_accessor :key
|
26
|
-
# @return [Symbol]
|
27
|
-
attr_accessor :
|
29
|
+
# @return [Symbol] Write timeout for the HTTP request, in seconds.
|
30
|
+
attr_accessor :write_timeout
|
31
|
+
# @return [Symbol] Connect timeout for the HTTP request, in seconds.
|
32
|
+
attr_accessor :connect_timeout
|
33
|
+
# @return [Symbol] Read timeout for the HTTP request, in seconds.
|
34
|
+
attr_accessor :read_timeout
|
28
35
|
# @return [Symbol] Client ID (for Maps API for Work).
|
29
36
|
attr_accessor :client_id
|
30
|
-
# @return [Symbol]
|
37
|
+
# @return [Symbol] Base64-encoded client secret (for Maps API for Work).
|
31
38
|
attr_accessor :client_secret
|
32
|
-
# @return [Symbol]
|
39
|
+
# @return [Symbol] Attribute used for tracking purposes. Can only be used with a Client ID.
|
33
40
|
attr_accessor :channel
|
34
|
-
# @return [Symbol]
|
41
|
+
# @return [Symbol] Timeout across multiple retriable requests, in seconds.
|
35
42
|
attr_accessor :retry_timeout
|
36
|
-
# @return [Symbol]
|
37
|
-
attr_accessor :
|
43
|
+
# @return [Symbol] HTTP headers per request.
|
44
|
+
attr_accessor :request_headers
|
38
45
|
# @return [Symbol] number of queries per second permitted. If the rate limit is reached, the client will sleep for the appropriate amout of time before it runs the current query.
|
39
46
|
attr_accessor :queries_per_second
|
40
47
|
# @return [Symbol] keeps track of sent queries.
|
@@ -42,10 +49,10 @@ module GoogleMaps
|
|
42
49
|
# @return [Symbol] Response format. Either :json or :xml
|
43
50
|
attr_accessor :response_format
|
44
51
|
|
45
|
-
def initialize(key
|
46
|
-
connect_timeout:
|
52
|
+
def initialize(key: nil, client_id: nil, client_secret: nil, write_timeout: 1,
|
53
|
+
connect_timeout: 1, read_timeout: 1,retry_timeout: 60, request_headers: {},
|
47
54
|
queries_per_second: 10, channel: nil, response_format: :json)
|
48
|
-
|
55
|
+
unless key || (client_secret && client_id)
|
49
56
|
raise StandardError, 'Must provide API key or enterprise credentials when creationg client.'
|
50
57
|
end
|
51
58
|
|
@@ -54,9 +61,8 @@ module GoogleMaps
|
|
54
61
|
end
|
55
62
|
|
56
63
|
if channel
|
57
|
-
unless client_id
|
58
|
-
|
59
|
-
end
|
64
|
+
raise StandardError, 'The channel argument must be used with a client ID.' unless client_id
|
65
|
+
|
60
66
|
|
61
67
|
unless /^[a-zA-Z0-9._-]*$/.match(channel)
|
62
68
|
raise StandardError, 'The channel argument must be an ASCII alphanumeric string. The period (.), underscore (_) and hyphen (-) characters are allowed.'
|
@@ -65,25 +71,16 @@ module GoogleMaps
|
|
65
71
|
|
66
72
|
self.key = key
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
if connect_timeout && read_timeout
|
73
|
-
self.timeout = { :connect_timeout => connect_timeout, :read_timeout => read_timeout }
|
74
|
-
else
|
75
|
-
self.timeout = timeout
|
76
|
-
end
|
74
|
+
# Set the timeout for write/connect/read calls
|
75
|
+
self.write_timeout = write_timeout
|
76
|
+
self.connect_timeout = connect_timeout
|
77
|
+
self.read_timeout = read_timeout
|
77
78
|
|
78
79
|
self.client_id = client_id
|
79
80
|
self.client_secret = client_secret
|
80
81
|
self.channel = channel
|
81
82
|
self.retry_timeout = retry_timeout
|
82
|
-
self.
|
83
|
-
:headers => {'User-Agent' => USER_AGENT},
|
84
|
-
:timeout => self.timeout,
|
85
|
-
:verify => true
|
86
|
-
})
|
83
|
+
self.request_headers = request_headers.merge({ 'User-Agent' => USER_AGENT })
|
87
84
|
self.queries_per_second = queries_per_second
|
88
85
|
self.sent_times = Array.new
|
89
86
|
|
@@ -93,7 +90,7 @@ module GoogleMaps
|
|
93
90
|
end
|
94
91
|
end
|
95
92
|
|
96
|
-
# Performs HTTP GET requests with credentials, returning the body as JSON or XML
|
93
|
+
# Performs HTTP GET requests with credentials, returning the body as JSON or XML.
|
97
94
|
#
|
98
95
|
# @param [String] url URL path for the request. Should begin with a slash.
|
99
96
|
# @param [Hash] params HTTP GET parameters.
|
@@ -102,12 +99,12 @@ module GoogleMaps
|
|
102
99
|
# @param [String] base_url The base URL for the request. Defaults to the Google Maps API server. Should not have a trailing slash.
|
103
100
|
# @param [TrueClass, FalseClass] accepts_clientid Flag whether this call supports the client/signature params. Some APIs require API keys (e.g. Roads).
|
104
101
|
# @param [Proc] extract_body A function that extracts the body from the request. If the request was not successful, the function should raise a
|
105
|
-
# GoogleMaps::Services::Exceptions::
|
106
|
-
# @param [Hash]
|
102
|
+
# GoogleMaps::Services::Exceptions::HTTPError or GoogleMaps::Services::Exceptions::APIError as appropriate.
|
103
|
+
# @param [Hash] request_headers HTTP headers per request.
|
107
104
|
#
|
108
|
-
# @return [Hash, Array] response body
|
105
|
+
# @return [Hash, Array, nil] response body (either in JSON or XML) or nil.
|
109
106
|
def get(url:, params:, first_request_time: nil, retry_counter: nil, base_url: DEFAULT_BASE_URL,
|
110
|
-
accepts_clientid: true, extract_body: nil,
|
107
|
+
accepts_clientid: true, extract_body: nil, request_headers: nil)
|
111
108
|
unless first_request_time
|
112
109
|
first_request_time = Util.current_time
|
113
110
|
end
|
@@ -128,35 +125,21 @@ module GoogleMaps
|
|
128
125
|
|
129
126
|
authed_url = generate_auth_url(url, params, accepts_clientid)
|
130
127
|
|
131
|
-
# Default to the client-level self.
|
132
|
-
#
|
133
|
-
|
128
|
+
# Default to the client-level self.request_headers, with method-level
|
129
|
+
# request_headers arg overriding.
|
130
|
+
request_headers = self.request_headers.merge(request_headers || {})
|
134
131
|
|
135
132
|
# Construct the Request URI
|
136
|
-
uri = URI.parse(base_url + authed_url)
|
137
|
-
|
138
|
-
# Add request headers
|
139
|
-
req = Net::HTTP::Get.new(uri.to_s)
|
140
|
-
|
141
|
-
request_opts[:headers].each { |header,value| req.add_field(header, value) }
|
133
|
+
uri = HTTP::URI.parse(base_url + authed_url)
|
142
134
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
# Handle response errors
|
149
|
-
case resp
|
150
|
-
when Net::HTTPRequestTimeOut
|
151
|
-
raise Timeout
|
152
|
-
when Exception
|
153
|
-
raise TransportError, 'HTTP GET request failed.'
|
154
|
-
end
|
135
|
+
# Create the request, add the headers and make the GET request
|
136
|
+
resp = HTTP.headers(request_headers)
|
137
|
+
.timeout(:write => self.write_timeout, :connect => self.connect_timeout, :read => self.read_timeout)
|
138
|
+
.get(uri.to_s)
|
155
139
|
|
156
140
|
if RETRIABLE_STATUSES.include? resp.code.to_i
|
157
141
|
# Retry request
|
158
|
-
self.get(url, params, first_request_time, retry_counter + 1,
|
159
|
-
base_url, accepts_clientid, extract_body)
|
142
|
+
self.get(url, params, first_request_time, retry_counter + 1, base_url, accepts_clientid, extract_body)
|
160
143
|
end
|
161
144
|
|
162
145
|
# Check if the time of the nth previous query (where n is queries_per_second)
|
@@ -173,11 +156,15 @@ module GoogleMaps
|
|
173
156
|
if extract_body
|
174
157
|
result = extract_body.call(resp)
|
175
158
|
else
|
176
|
-
case
|
177
|
-
when
|
159
|
+
case resp.content_type.mime_type
|
160
|
+
when 'application/xml'
|
178
161
|
result = get_xml_body(resp)
|
179
|
-
|
162
|
+
when 'application/json'
|
180
163
|
result = get_json_body(resp)
|
164
|
+
when 'text/html'
|
165
|
+
result = get_redirection_url(resp)
|
166
|
+
else
|
167
|
+
result = get_map_image(resp)
|
181
168
|
end
|
182
169
|
end
|
183
170
|
self.sent_times.push(Util.current_time)
|
@@ -188,6 +175,18 @@ module GoogleMaps
|
|
188
175
|
end
|
189
176
|
end
|
190
177
|
|
178
|
+
# Returns the redirection URL from the Response in case of 3XX status code.
|
179
|
+
#
|
180
|
+
# @private
|
181
|
+
#
|
182
|
+
# @param [Net::HTTPResponse] resp HTTP response object.
|
183
|
+
#
|
184
|
+
# @return [String] Redirection URL.
|
185
|
+
def get_redirection_url(resp)
|
186
|
+
status_code = resp.code.to_i
|
187
|
+
(status_code >= 300 && status_code < 400) ? resp['location'] : nil
|
188
|
+
end
|
189
|
+
|
191
190
|
# Extracts the JSON body of the HTTP response.
|
192
191
|
#
|
193
192
|
# @private
|
@@ -197,15 +196,12 @@ module GoogleMaps
|
|
197
196
|
# @return [Hash, Array] Valid JSON response.
|
198
197
|
def get_json_body(resp)
|
199
198
|
status_code = resp.code.to_i
|
200
|
-
if status_code >= 300 && status_code < 400
|
201
|
-
return resp['location']
|
202
|
-
end
|
203
199
|
|
204
200
|
if status_code != 200
|
205
|
-
raise HTTPError.new(
|
201
|
+
raise HTTPError.new(status_code)
|
206
202
|
end
|
207
203
|
|
208
|
-
# Parse the response body
|
204
|
+
# Parse the JSON response body
|
209
205
|
begin
|
210
206
|
body = JSON.parse(resp.body)
|
211
207
|
rescue JSON::ParserError
|
@@ -237,16 +233,14 @@ module GoogleMaps
|
|
237
233
|
# @return [Nokogiri::XML::Document] Valid XML document.
|
238
234
|
def get_xml_body(resp)
|
239
235
|
status_code = resp.code.to_i
|
240
|
-
if status_code >= 300 && status_code < 400
|
241
|
-
return resp['location']
|
242
|
-
end
|
243
236
|
|
244
237
|
if status_code != 200
|
245
|
-
raise HTTPError.new(
|
238
|
+
raise HTTPError.new(status_code)
|
246
239
|
end
|
247
240
|
|
241
|
+
# Parse the XML response body
|
248
242
|
begin
|
249
|
-
doc = Nokogiri::XML
|
243
|
+
doc = Nokogiri::XML(resp.body) { |config| config.strict }
|
250
244
|
rescue
|
251
245
|
raise APIError.new(status_code), 'Received a malformed XML response.'
|
252
246
|
end
|
@@ -268,6 +262,27 @@ module GoogleMaps
|
|
268
262
|
end
|
269
263
|
end
|
270
264
|
|
265
|
+
# Extracts the static map image from the HTTP response.
|
266
|
+
#
|
267
|
+
# @private
|
268
|
+
#
|
269
|
+
# @param [Net::HTTPResponse] resp HTTP response object.
|
270
|
+
#
|
271
|
+
# @return [Hash] Hash with image URL, MIME type and its base64-encoded value.
|
272
|
+
def get_map_image(resp)
|
273
|
+
status_code = resp.code.to_i
|
274
|
+
|
275
|
+
if status_code != 200
|
276
|
+
raise HTTPError.new(status_code)
|
277
|
+
end
|
278
|
+
|
279
|
+
{
|
280
|
+
:url => resp.uri.to_s,
|
281
|
+
:mime_type => resp.content_type.mime_type,
|
282
|
+
:image_data => Base64.encode64(resp.body).gsub(/\n/, '')
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
271
286
|
# Returns the path and query string portion of the request URL, first adding any necessary parameters.
|
272
287
|
#
|
273
288
|
# @private
|
@@ -297,7 +312,7 @@ module GoogleMaps
|
|
297
312
|
raise StandardError, 'Must provide API key for this API. It does not accept enterprise credentials.'
|
298
313
|
end
|
299
314
|
|
300
|
-
private :get_json_body, :get_xml_body, :generate_auth_url
|
315
|
+
private :get_json_body, :get_xml_body, :get_map_image, :get_redirection_url, :generate_auth_url
|
301
316
|
end
|
302
317
|
|
303
318
|
end
|
@@ -2,14 +2,15 @@ require 'googlemaps/services/util'
|
|
2
2
|
|
3
3
|
module GoogleMaps
|
4
4
|
module Services
|
5
|
-
TRAVEL_MODES = %w(driving walking bicycling transit)
|
6
|
-
|
7
5
|
# Performs requests to the Google Maps Directions API.
|
8
6
|
#
|
9
7
|
# @example
|
10
8
|
# directions = GoogleMaps::Services::Directions.new(client)
|
11
9
|
# result = directions.query(origin: "Brussels", destination: {:lat => 52.520645, :lng => 13.409779})
|
12
10
|
class Directions
|
11
|
+
TRAVEL_MODES = %w(driving walking bicycling transit)
|
12
|
+
AVOID_FEATURES = %w(tolls highways ferries indoor)
|
13
|
+
|
13
14
|
# @return [Symbol] The HTTP client.
|
14
15
|
attr_accessor :client
|
15
16
|
|
@@ -72,6 +73,9 @@ module GoogleMaps
|
|
72
73
|
end
|
73
74
|
|
74
75
|
if avoid
|
76
|
+
unless ArrayBox.contains_all?(AVOID_FEATURES, avoid)
|
77
|
+
raise StandardError, 'invalid avoid feature.'
|
78
|
+
end
|
75
79
|
params['avoid'] = Convert.join_array('|', avoid)
|
76
80
|
end
|
77
81
|
|
@@ -114,8 +118,10 @@ module GoogleMaps
|
|
114
118
|
case self.client.response_format
|
115
119
|
when :xml
|
116
120
|
self.client.get(url: '/maps/api/directions/xml', params: params).xpath('//route')
|
117
|
-
|
121
|
+
when :json
|
118
122
|
self.client.get(url: '/maps/api/directions/json', params: params)['routes']
|
123
|
+
else
|
124
|
+
raise StandardError, 'Unsupported response format. Should be either :json or :xml.'
|
119
125
|
end
|
120
126
|
end
|
121
127
|
end
|
@@ -2,14 +2,15 @@ require 'googlemaps/services/util'
|
|
2
2
|
|
3
3
|
module GoogleMaps
|
4
4
|
module Services
|
5
|
-
AVOIDS = %w(tolls highways ferries)
|
6
|
-
|
7
5
|
# Performs requests to the Google Maps Distance Matrix API.
|
8
6
|
#
|
9
7
|
# @example
|
10
|
-
# distancematrix = GoogleMaps::Services::DistanceMatrix(client)
|
8
|
+
# distancematrix = GoogleMaps::Services::DistanceMatrix.new(client)
|
11
9
|
# result = distancematrix.query(origins: ["Brussels", "Ghent"], destinations: ["Bruges"])
|
12
10
|
class DistanceMatrix
|
11
|
+
AVOIDS = %w(tolls highways ferries)
|
12
|
+
TRAVEL_MODES = %w(driving walking bicycling transit)
|
13
|
+
|
13
14
|
# @return [Symbol] the HTTP client.
|
14
15
|
attr_accessor :client
|
15
16
|
|
@@ -7,9 +7,11 @@ module GoogleMaps
|
|
7
7
|
# Performs requests to the Google Maps Elevation API.
|
8
8
|
#
|
9
9
|
# @example
|
10
|
-
# elevation = GoogleMaps::Services::Elevation(client)
|
10
|
+
# elevation = GoogleMaps::Services::Elevation.new(client)
|
11
11
|
# result = elevation.query(locations: [{:lat => 52.520645, :lng => 13.409779}, "Brussels"])
|
12
12
|
class Elevation
|
13
|
+
|
14
|
+
# @return [Symbol] The HTTP client.
|
13
15
|
attr_accessor :client
|
14
16
|
|
15
17
|
def initialize(client)
|
@@ -25,22 +27,17 @@ module GoogleMaps
|
|
25
27
|
# @param [Integer] samples The number of sample points along a path for which to return elevation data.
|
26
28
|
#
|
27
29
|
# @return [Array, Nokogiri::XML::NodeSet] Valid JSON or XML response.
|
28
|
-
def query(locations:
|
30
|
+
def query(locations: nil, path: nil, samples: 0)
|
29
31
|
params = {}
|
30
32
|
|
31
|
-
if path && locations
|
32
|
-
raise StandardError, 'Should not specify both path and locations.'
|
33
|
-
end
|
34
|
-
|
35
33
|
if locations
|
36
34
|
params['locations'] = Convert.shortest_path(locations)
|
37
35
|
end
|
38
36
|
|
39
37
|
if path
|
40
|
-
|
41
|
-
when String
|
38
|
+
if path.instance_of? String
|
42
39
|
path = "enc:#{path}"
|
43
|
-
|
40
|
+
elsif path.instance_of? Array
|
44
41
|
path = Convert.shortest_path(path)
|
45
42
|
else
|
46
43
|
raise TypeError, 'Path should be either a String or an Array.'
|
@@ -49,11 +46,17 @@ module GoogleMaps
|
|
49
46
|
params = {'path' => path, 'samples' => samples }
|
50
47
|
end
|
51
48
|
|
49
|
+
if path && locations
|
50
|
+
raise StandardError, 'Should not specify both path and locations.'
|
51
|
+
end
|
52
|
+
|
52
53
|
case self.client.response_format
|
53
54
|
when :xml
|
54
55
|
self.client.get(url: '/maps/api/elevation/xml', params: params).xpath('//result')
|
55
|
-
|
56
|
+
when :json
|
56
57
|
self.client.get(url: '/maps/api/elevation/json', params: params)['results']
|
58
|
+
else
|
59
|
+
raise StandardError, 'Unsupported response format. Should be either :json or :xml.'
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
@@ -56,8 +56,10 @@ module GoogleMaps
|
|
56
56
|
case self.client.response_format
|
57
57
|
when :xml
|
58
58
|
self.client.get(url: '/maps/api/geocode/xml', params: params).xpath('//result')
|
59
|
-
|
59
|
+
when :json
|
60
60
|
self.client.get(url: '/maps/api/geocode/json', params: params)['results']
|
61
|
+
else
|
62
|
+
raise StandardError, 'Unsupported response format. Should be either :json or :xml.'
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
@@ -85,7 +87,7 @@ module GoogleMaps
|
|
85
87
|
def query(latlng:, result_type: nil, location_type: nil, language: nil)
|
86
88
|
# Check if latlng param is a place_id string.
|
87
89
|
# 'place_id' strings do not contain commas; latlng strings do.
|
88
|
-
if latlng.is_a?(String) && !latlng.include?("
|
90
|
+
if latlng.is_a?(String) && !latlng.include?(",")
|
89
91
|
params = {'place_id' => latlng}
|
90
92
|
else
|
91
93
|
params = {'latlng' => Convert.to_latlng(latlng)}
|
@@ -106,8 +108,10 @@ module GoogleMaps
|
|
106
108
|
case self.client.response_format
|
107
109
|
when :xml
|
108
110
|
self.client.get(url: '/maps/api/geocode/xml', params: params).xpath('//result')
|
109
|
-
|
111
|
+
when :json
|
110
112
|
self.client.get(url: '/maps/api/geocode/json', params: params)['results']
|
113
|
+
else
|
114
|
+
raise StandardError, 'Unsupported response format. Should be either :json or :xml.'
|
111
115
|
end
|
112
116
|
end
|
113
117
|
end
|
@@ -62,7 +62,7 @@ module GoogleMaps
|
|
62
62
|
type: type, page_token: page_token)
|
63
63
|
end
|
64
64
|
|
65
|
-
# Performs radar search for places
|
65
|
+
# Performs radar search for places.
|
66
66
|
#
|
67
67
|
# @param [String, Hash] location The latitude/longitude value for which you wish to obtain the closest, human-readable address.
|
68
68
|
# @param [Integer] radius Distance in meters within which to bias results.
|
@@ -71,14 +71,12 @@ module GoogleMaps
|
|
71
71
|
# @param [Integer] max_price Restricts results to only those places with no greater than this price level. Valid values are in the range from 0 (most affordable) to 4 (most expensive).
|
72
72
|
# @param [Array] name One or more terms to be matched against the names of places.
|
73
73
|
# @param [TrueClass, FalseClass] open_now Return only those places that are open for business at the time the query is sent.
|
74
|
-
# @param [String] type
|
74
|
+
# @param [String] type Restricts the results to places matching the specified type. The full list of supported types is available here: https://developers.google.com/places/supported_types
|
75
75
|
#
|
76
76
|
# @return [Hash, Nokogiri::XML::Document] Valid JSON or XML response.
|
77
77
|
def radar(location:, radius:, keyword: nil, min_price: nil,
|
78
78
|
max_price: nil, name: nil, open_now: false, type: nil)
|
79
|
-
unless keyword || name || type
|
80
|
-
raise StandardError, 'either a keyword, name, or type arg is required.'
|
81
|
-
end
|
79
|
+
raise StandardError, 'either a keyword, name, or type arg is required.' unless (keyword || name || type)
|
82
80
|
|
83
81
|
_places(url_part: 'radar', location: location, radius: radius,
|
84
82
|
keyword: keyword, min_price: min_price, max_price: max_price,
|
@@ -152,15 +150,13 @@ module GoogleMaps
|
|
152
150
|
|
153
151
|
# Downloads a photo from the Places API.
|
154
152
|
#
|
155
|
-
# @param [String] photo_reference
|
156
|
-
# @param [Integer] max_width
|
157
|
-
# @param [Integer] max_height
|
153
|
+
# @param [String] photo_reference A string identifier that uniquely identifies a photo, as provided by either a Places search or Places detail request.
|
154
|
+
# @param [Integer] max_width Specifies the maximum desired width, in pixels.
|
155
|
+
# @param [Integer] max_height Specifies the maximum desired height, in pixels.
|
158
156
|
#
|
159
157
|
# @return [String] URL of the photo.
|
160
158
|
def place_photo(photo_reference:, max_width: nil, max_height: nil)
|
161
|
-
unless max_width || max_height
|
162
|
-
raise StandardError, 'a max_width or max_height arg is required'
|
163
|
-
end
|
159
|
+
raise StandardError, 'a max_width or max_height arg is required' unless (max_width || max_height)
|
164
160
|
|
165
161
|
params = {'photoreference' => photo_reference}
|
166
162
|
|
@@ -238,8 +234,10 @@ module GoogleMaps
|
|
238
234
|
case self.client.response_format
|
239
235
|
when :xml
|
240
236
|
self.client.get(url: "/maps/api/place/#{url_part}autocomplete/xml", params: params).xpath('//prediction')
|
241
|
-
|
237
|
+
when :json
|
242
238
|
self.client.get(url: "/maps/api/place/#{url_part}autocomplete/json", params: params)['predictions']
|
239
|
+
else
|
240
|
+
raise StandardError, 'Unsupported response format. Should be either :json or :xml.'
|
243
241
|
end
|
244
242
|
end
|
245
243
|
|
@@ -10,36 +10,6 @@ module GoogleMaps
|
|
10
10
|
class Roads
|
11
11
|
include GoogleMaps::Services::Exceptions
|
12
12
|
|
13
|
-
# Extracts a result from a Roads API HTTP response.
|
14
|
-
@@_roads_extract = Proc.new { |resp|
|
15
|
-
status_code = resp.code.to_i
|
16
|
-
begin
|
17
|
-
body = JSON.parse(resp.body)
|
18
|
-
rescue JSON::ParserError
|
19
|
-
raise APIError.new(status_code), 'Received malformed response.'
|
20
|
-
end
|
21
|
-
|
22
|
-
if body.key?('error')
|
23
|
-
error = body['error']
|
24
|
-
status = error['status']
|
25
|
-
|
26
|
-
if status == 'RESOURCE_EXHAUSTED'
|
27
|
-
raise RetriableRequest
|
28
|
-
end
|
29
|
-
|
30
|
-
if error.key?('message')
|
31
|
-
raise APIError.new(status), error['message']
|
32
|
-
else
|
33
|
-
raise APIError.new(status)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
if status_code != 200
|
38
|
-
raise HTTPError.new(status_code)
|
39
|
-
end
|
40
|
-
body
|
41
|
-
}
|
42
|
-
|
43
13
|
# @return [Symbol] the HTTP client.
|
44
14
|
attr_accessor :client
|
45
15
|
|
@@ -65,7 +35,7 @@ module GoogleMaps
|
|
65
35
|
end
|
66
36
|
|
67
37
|
self.client.get(url: '/v1/snapToRoads', params: params, base_url: ROADS_BASE_URL,
|
68
|
-
accepts_clientid: false, extract_body:
|
38
|
+
accepts_clientid: false, extract_body: lambda(&method(:_roads_extract)))['snappedPoints']
|
69
39
|
end
|
70
40
|
|
71
41
|
# Returns the posted speed limit (in km/h) for given road segments.
|
@@ -80,7 +50,7 @@ module GoogleMaps
|
|
80
50
|
params = {'placeId' => place_ids}
|
81
51
|
|
82
52
|
self.client.get(url: '/v1/speedLimits', params: params, base_url: ROADS_BASE_URL,
|
83
|
-
accepts_clientid: false, extract_body:
|
53
|
+
accepts_clientid: false, extract_body: lambda(&method(:_roads_extract)))['speedLimits']
|
84
54
|
end
|
85
55
|
|
86
56
|
# Returns the posted speed limit (in km/h) for given road segments.
|
@@ -93,7 +63,7 @@ module GoogleMaps
|
|
93
63
|
params = {'path' => Convert.piped_location(path)}
|
94
64
|
|
95
65
|
self.client.get(url: '/v1/speedLimits', params: params, base_url: ROADS_BASE_URL,
|
96
|
-
accepts_clientid: false, extract_body:
|
66
|
+
accepts_clientid: false, extract_body: lambda(&method(:_roads_extract)))
|
97
67
|
end
|
98
68
|
|
99
69
|
# Find the closest road segments for each point.
|
@@ -107,9 +77,46 @@ module GoogleMaps
|
|
107
77
|
params = {'points' => Convert.piped_location(points)}
|
108
78
|
|
109
79
|
self.client.get(url: '/v1/nearestRoads', params: params, base_url: ROADS_BASE_URL,
|
110
|
-
accepts_clientid: false, extract_body:
|
80
|
+
accepts_clientid: false, extract_body: lambda(&method(:_roads_extract)))['snappedPoints']
|
111
81
|
end
|
112
|
-
end
|
113
82
|
|
83
|
+
# Extracts a result from a Roads API HTTP response.
|
84
|
+
#
|
85
|
+
# @private
|
86
|
+
#
|
87
|
+
# @param [Net::HTTPResponse] resp HTTP response object.
|
88
|
+
#
|
89
|
+
# @return [Hash, Array] Valid JSON response.
|
90
|
+
def _roads_extract(resp)
|
91
|
+
status_code = resp.code.to_i
|
92
|
+
begin
|
93
|
+
body = JSON.parse(resp.body)
|
94
|
+
rescue JSON::ParserError
|
95
|
+
raise APIError.new(status_code), 'Received malformed response.'
|
96
|
+
end
|
97
|
+
|
98
|
+
if body.key?('error')
|
99
|
+
error = body['error']
|
100
|
+
status = error['status']
|
101
|
+
|
102
|
+
if status == 'RESOURCE_EXHAUSTED'
|
103
|
+
raise RetriableRequest
|
104
|
+
end
|
105
|
+
|
106
|
+
if error.respond_to?(:key?) && error.key?('message')
|
107
|
+
raise APIError.new(status), error['message']
|
108
|
+
else
|
109
|
+
raise APIError.new(status)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
if status_code != 200
|
114
|
+
raise HTTPError.new(status_code)
|
115
|
+
end
|
116
|
+
body
|
117
|
+
end
|
118
|
+
|
119
|
+
private :_roads_extract
|
120
|
+
end
|
114
121
|
end
|
115
122
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'googlemaps/services/util'
|
2
|
+
|
3
|
+
module GoogleMaps
|
4
|
+
module Services
|
5
|
+
ALLOWED_SCALES = [2, 4]
|
6
|
+
SUPPORTED_IMG_FORMATS = ["png32", "gif", "jpg", "jpg-baseline"]
|
7
|
+
SUPPORTED_MAP_TYPES = ["satellite", "hybrid", "terrain"]
|
8
|
+
|
9
|
+
# Performs requests to the Google Static Map API.
|
10
|
+
#
|
11
|
+
# @example Get static map image
|
12
|
+
# staticmap = GoogleMaps::Services::StaticMap.new(client)
|
13
|
+
# map_img = staticmap.query(size: {:length => 640, :width => 400},
|
14
|
+
# center: "50.8449925,4.362961",
|
15
|
+
# maptype: "hybrid",
|
16
|
+
# zoom: 16)
|
17
|
+
# # {
|
18
|
+
# # :url => "https://maps.googleapis.com/maps/api/staticmap?size=640x400¢er=50.8449925%2C4.362961&zoom=16&maptype=hybrid",
|
19
|
+
# # :mime_type => "image/png",
|
20
|
+
# # :image_data => "iVBORw0KGgoAAAANSUhEUgAAAoAAAAGQCAMAAAAJLSEXAAADAFBMVEU..."
|
21
|
+
# # }
|
22
|
+
class StaticMap
|
23
|
+
|
24
|
+
# @return [Symbol] The HTTP client.
|
25
|
+
attr_accessor :client
|
26
|
+
|
27
|
+
def initialize(client)
|
28
|
+
self.client = client
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the static map image.
|
32
|
+
#
|
33
|
+
# @param [Hash] size The rectangular dimensions of the map image.
|
34
|
+
# @param [String, Hash] center The address or lat/lng value of the map's center.
|
35
|
+
# @param [Integer] zoom The magnification level of the map.
|
36
|
+
# @param [Integer] scale The scale of the map. This affects the number of pixels that are returned.
|
37
|
+
# @param [String] format The format of the resulting image. Defaults to "png8" or "png".
|
38
|
+
# @param [String] maptype The type of map to construct. Defaults to "roadmap".
|
39
|
+
# @param [String] language The language to use for display of labels on map tiles.
|
40
|
+
# @param [String] region The region code specified as a two-character ccTLD ('top-level domain') value.
|
41
|
+
# @param [String, Array] markers One or more markers to attach to the image at specified locations.
|
42
|
+
# @param [String, Array] path The single path of two or more connected points to overlay in the image at the specified locations.
|
43
|
+
# @param [String, Array] visible One or more locations that should remain visible on the map.
|
44
|
+
# @param [String] style A custom style to alter the presentation of a specific feature (roads, parks, and other features) of the map.
|
45
|
+
def query(size:, center: nil, zoom: nil, scale: 1, format: "png", maptype: "roadmap",
|
46
|
+
language: nil, region: nil, markers: nil, path: nil, visible: nil, style: nil)
|
47
|
+
params = { 'size' => Convert.rectangular_dimensions(size) }
|
48
|
+
|
49
|
+
if markers
|
50
|
+
params['markers'] = markers
|
51
|
+
else
|
52
|
+
raise StandardError, "both center and zoom are required if markers not present." unless (center && zoom)
|
53
|
+
|
54
|
+
params['center'] = Convert.to_latlng(center)
|
55
|
+
params['zoom'] = zoom
|
56
|
+
end
|
57
|
+
|
58
|
+
if scale != 1
|
59
|
+
raise StandardError, "invalid scale value." unless ALLOWED_SCALES.include? scale
|
60
|
+
params['scale'] = scale
|
61
|
+
end
|
62
|
+
|
63
|
+
if format != "png"
|
64
|
+
raise StandardError, "invalid image format." unless SUPPORTED_IMG_FORMATS.include? format
|
65
|
+
params['format'] = format
|
66
|
+
end
|
67
|
+
|
68
|
+
if maptype != "roadmap"
|
69
|
+
raise StandardError, "invalid maptype value." unless SUPPORTED_MAP_TYPES.include? maptype
|
70
|
+
params['maptype'] = maptype
|
71
|
+
end
|
72
|
+
|
73
|
+
if language
|
74
|
+
params['language'] = language
|
75
|
+
end
|
76
|
+
|
77
|
+
if region
|
78
|
+
params['region'] = region
|
79
|
+
end
|
80
|
+
|
81
|
+
if path
|
82
|
+
params['path'] = path
|
83
|
+
end
|
84
|
+
|
85
|
+
if visible
|
86
|
+
params['visible'] = visible
|
87
|
+
end
|
88
|
+
|
89
|
+
if style
|
90
|
+
params['style'] = style
|
91
|
+
end
|
92
|
+
|
93
|
+
self.client.get(url: "/maps/api/staticmap", params: params)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -6,7 +6,7 @@ module GoogleMaps
|
|
6
6
|
# Performs requests to the Google Maps Timezone API.
|
7
7
|
#
|
8
8
|
# @example
|
9
|
-
# timezone = GoogleMaps::Services::Timezone(client)
|
9
|
+
# timezone = GoogleMaps::Services::Timezone.new(client)
|
10
10
|
# result = timezone.query(location: "38.908133,-77.047119")
|
11
11
|
class Timezone
|
12
12
|
# @return [Symbol] The HTTP client.
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'net/http'
|
2
1
|
require 'openssl'
|
3
2
|
require 'base64'
|
4
3
|
require 'date'
|
@@ -8,12 +7,14 @@ module GoogleMaps
|
|
8
7
|
module Services
|
9
8
|
|
10
9
|
module HashDot
|
11
|
-
def method_missing(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def method_missing(method, *opts)
|
11
|
+
m = method.to_s
|
12
|
+
if self.has_key?(m)
|
13
|
+
return self[m]
|
14
|
+
elsif self.has_key?(m.to_sym)
|
15
|
+
return self[m.to_sym]
|
16
16
|
end
|
17
|
+
super
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -37,6 +38,25 @@ module GoogleMaps
|
|
37
38
|
[object]
|
38
39
|
end
|
39
40
|
end
|
41
|
+
|
42
|
+
# Determines if one array contains all elements of another array.
|
43
|
+
#
|
44
|
+
# @param [Array] arr target array.
|
45
|
+
# @param [Array] other array to look for in the target array.
|
46
|
+
# @example
|
47
|
+
# myarr = ["hello", "world"]
|
48
|
+
# ArrayBox.contains_all? myarr, ["hello"] # true
|
49
|
+
#
|
50
|
+
# @return [TrueClass, FalseClass] a boolean.
|
51
|
+
def self.contains_all?(arr, other)
|
52
|
+
h = arr.inject(Hash.new(0)) {|h, i| h[i] += 1; h}
|
53
|
+
other.each do |i|
|
54
|
+
return false unless h.has_key?(i)
|
55
|
+
return false if h[i].zero?
|
56
|
+
h[i] -= 1
|
57
|
+
end
|
58
|
+
return true
|
59
|
+
end
|
40
60
|
end
|
41
61
|
|
42
62
|
# Set of utility methods.
|
@@ -295,6 +315,28 @@ module GoogleMaps
|
|
295
315
|
unencoded = piped_location(locations)
|
296
316
|
encoded.length < unencoded.length ? encoded : unencoded
|
297
317
|
end
|
318
|
+
|
319
|
+
# Returns the MIME type from the given header value.
|
320
|
+
#
|
321
|
+
# @param [String] content_type The Content-Type header value.
|
322
|
+
#
|
323
|
+
# @return [String] the MIME type value.
|
324
|
+
def self.get_mime_type(content_type)
|
325
|
+
content_type.split(';').first
|
326
|
+
end
|
327
|
+
|
328
|
+
# Returns the rectangular dimensions in the form {horizontal_value}x{vertical_value}.
|
329
|
+
#
|
330
|
+
# @example
|
331
|
+
# Convert.rectangular_dimensions({:length => 500, :width => 400}) # "500x400"
|
332
|
+
#
|
333
|
+
# @param [Hash] size The size hash.
|
334
|
+
#
|
335
|
+
# @return [String] a string value in the form "lengthxwidth".
|
336
|
+
def self.rectangular_dimensions(size)
|
337
|
+
raise TypeError, "#{__method__.to_s} expected a Hash." unless size.is_a? Hash
|
338
|
+
"#{size[:length]}x#{size[:width]}"
|
339
|
+
end
|
298
340
|
end
|
299
341
|
|
300
342
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: googlemaps-services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Faissal Elamraoui
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -16,20 +16,40 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.7'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.
|
22
|
+
version: 1.7.0.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '1.
|
29
|
+
version: '1.7'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.
|
32
|
+
version: 1.7.0.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: http
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.1'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 2.1.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '2.1'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.1.0
|
33
53
|
- !ruby/object:Gem::Dependency
|
34
54
|
name: bundler
|
35
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +108,7 @@ files:
|
|
88
108
|
- lib/googlemaps/services/geocoding.rb
|
89
109
|
- lib/googlemaps/services/places.rb
|
90
110
|
- lib/googlemaps/services/roads.rb
|
111
|
+
- lib/googlemaps/services/staticmap.rb
|
91
112
|
- lib/googlemaps/services/timezone.rb
|
92
113
|
- lib/googlemaps/services/util.rb
|
93
114
|
- lib/googlemaps/services/version.rb
|