googlemaps-services 1.2.5 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|