google_maps_service_ruby 0.6.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 +7 -0
- data/CHANGELOG.md +72 -0
- data/CODE_OF_CONDUCT.md +133 -0
- data/LICENSE +202 -0
- data/README.md +375 -0
- data/lib/google_maps_service/apis/directions.rb +99 -0
- data/lib/google_maps_service/apis/distance_matrix.rb +83 -0
- data/lib/google_maps_service/apis/elevation.rb +56 -0
- data/lib/google_maps_service/apis/geocoding.rb +82 -0
- data/lib/google_maps_service/apis/roads.rb +183 -0
- data/lib/google_maps_service/apis/time_zone.rb +39 -0
- data/lib/google_maps_service/apis.rb +5 -0
- data/lib/google_maps_service/client.rb +255 -0
- data/lib/google_maps_service/convert.rb +174 -0
- data/lib/google_maps_service/errors.rb +52 -0
- data/lib/google_maps_service/polyline.rb +89 -0
- data/lib/google_maps_service/url.rb +62 -0
- data/lib/google_maps_service/validator.rb +38 -0
- data/lib/google_maps_service/version.rb +23 -0
- data/lib/google_maps_service.rb +40 -0
- data/lib/google_maps_service_ruby.rb +3 -0
- metadata +210 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require "google_maps_service/convert"
|
2
|
+
|
3
|
+
module GoogleMapsService::Apis
|
4
|
+
# Performs requests to the Google Maps Geocoding API.
|
5
|
+
module Geocoding
|
6
|
+
# Geocoding is the process of converting addresses
|
7
|
+
# (like `"1600 Amphitheatre Parkway, Mountain View, CA"`) into geographic
|
8
|
+
# coordinates (like latitude 37.423021 and longitude -122.083739), which you
|
9
|
+
# can use to place markers or position the map.
|
10
|
+
#
|
11
|
+
# @example Geocode an address
|
12
|
+
# results = client.geocode('Sydney')
|
13
|
+
#
|
14
|
+
# @example Geocode a component only
|
15
|
+
# results = client.geocode(nil, components: {administrative_area: 'TX', country: 'US'})
|
16
|
+
#
|
17
|
+
# @example Geocode an address and component
|
18
|
+
# results = client.geocode('Sydney', components: {administrative_area: 'TX', country: 'US'})
|
19
|
+
#
|
20
|
+
# @example Multiple parameters
|
21
|
+
# results = client.geocode('Sydney',
|
22
|
+
# components: {administrative_area: 'TX', country: 'US'},
|
23
|
+
# bounds: {
|
24
|
+
# northeast: {lat: 32.7183997, lng: -97.26864001970849},
|
25
|
+
# southwest: {lat: 32.7052583, lng: -97.27133798029149}
|
26
|
+
# },
|
27
|
+
# region: 'us')
|
28
|
+
#
|
29
|
+
# @param [String] address The address to geocode. You must specify either this value and/or `components`.
|
30
|
+
# @param [Hash] components A component filter for which you wish to obtain a geocode,
|
31
|
+
# for example: `{'administrative_area': 'TX','country': 'US'}`
|
32
|
+
# @param [String, Hash] bounds The bounding box of the viewport within which to bias geocode
|
33
|
+
# results more prominently. Accept string or hash with `northeast` and `southwest` keys.
|
34
|
+
# @param [String] region The region code, specified as a ccTLD (_top-level domain_)
|
35
|
+
# two-character value.
|
36
|
+
# @param [String] language The language in which to return results.
|
37
|
+
#
|
38
|
+
# @return [Array] Array of geocoding results.
|
39
|
+
def geocode(address, components: nil, bounds: nil, region: nil, language: nil)
|
40
|
+
params = {}
|
41
|
+
|
42
|
+
params[:address] = address if address
|
43
|
+
params[:components] = GoogleMapsService::Convert.components(components) if components
|
44
|
+
params[:bounds] = GoogleMapsService::Convert.bounds(bounds) if bounds
|
45
|
+
params[:region] = region if region
|
46
|
+
params[:language] = language if language
|
47
|
+
|
48
|
+
get("/maps/api/geocode/json", params)[:results]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Reverse geocoding is the process of converting geographic coordinates into a
|
52
|
+
# human-readable address.
|
53
|
+
#
|
54
|
+
# @example Simple lat/lon pair
|
55
|
+
# client.reverse_geocode({lat: 40.714224, lng: -73.961452})
|
56
|
+
#
|
57
|
+
# @example Multiple parameters
|
58
|
+
# client.reverse_geocode([40.714224, -73.961452],
|
59
|
+
# location_type: ['ROOFTOP', 'RANGE_INTERPOLATED'],
|
60
|
+
# result_type: ['street_address', 'route'],
|
61
|
+
# language: 'es')
|
62
|
+
#
|
63
|
+
# @param [Hash, Array] latlng The latitude/longitude value for which you wish to obtain
|
64
|
+
# the closest, human-readable address.
|
65
|
+
# @param [String, Array<String>] location_type One or more location types to restrict results to.
|
66
|
+
# @param [String, Array<String>] result_type One or more address types to restrict results to.
|
67
|
+
# @param [String] language The language in which to return results.
|
68
|
+
#
|
69
|
+
# @return [Array] Array of reverse geocoding results.
|
70
|
+
def reverse_geocode(latlng, location_type: nil, result_type: nil, language: nil)
|
71
|
+
params = {
|
72
|
+
latlng: GoogleMapsService::Convert.latlng(latlng)
|
73
|
+
}
|
74
|
+
|
75
|
+
params[:result_type] = GoogleMapsService::Convert.join_list("|", result_type) if result_type
|
76
|
+
params[:location_type] = GoogleMapsService::Convert.join_list("|", location_type) if location_type
|
77
|
+
params[:language] = language if language
|
78
|
+
|
79
|
+
get("/maps/api/geocode/json", params)[:results]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
|
3
|
+
module GoogleMapsService::Apis
|
4
|
+
# Performs requests to the Google Maps Roads API.
|
5
|
+
module Roads
|
6
|
+
# Base URL of Google Maps Roads API
|
7
|
+
ROADS_BASE_URL = "https://roads.googleapis.com"
|
8
|
+
|
9
|
+
# Snaps a path to the most likely roads travelled.
|
10
|
+
#
|
11
|
+
# Takes up to 100 GPS points collected along a route, and returns a similar
|
12
|
+
# set of data with the points snapped to the most likely roads the vehicle
|
13
|
+
# was traveling along.
|
14
|
+
#
|
15
|
+
# @example Single point snap
|
16
|
+
# results = client.snap_to_roads([40.714728, -73.998672])
|
17
|
+
#
|
18
|
+
# @example Multi points snap
|
19
|
+
# path = [
|
20
|
+
# [-33.8671, 151.20714],
|
21
|
+
# [-33.86708, 151.20683000000002],
|
22
|
+
# [-33.867070000000005, 151.20674000000002],
|
23
|
+
# [-33.86703, 151.20625]
|
24
|
+
# ]
|
25
|
+
# results = client.snap_to_roads(path, interpolate: true)
|
26
|
+
#
|
27
|
+
# @param [Array] path The path to be snapped. Array of latitude/longitude pairs.
|
28
|
+
# @param [Boolean] interpolate Whether to interpolate a path to include all points
|
29
|
+
# forming the full road-geometry. When true, additional interpolated
|
30
|
+
# points will also be returned, resulting in a path that smoothly
|
31
|
+
# follows the geometry of the road, even around corners and through
|
32
|
+
# tunnels. Interpolated paths may contain more points than the
|
33
|
+
# original path.
|
34
|
+
#
|
35
|
+
# @return [Array] Array of snapped points.
|
36
|
+
def snap_to_roads(path, interpolate: false)
|
37
|
+
path = GoogleMapsService::Convert.waypoints(path)
|
38
|
+
|
39
|
+
params = {
|
40
|
+
path: path
|
41
|
+
}
|
42
|
+
|
43
|
+
params[:interpolate] = "true" if interpolate
|
44
|
+
|
45
|
+
get("/v1/snapToRoads", params,
|
46
|
+
base_url: ROADS_BASE_URL,
|
47
|
+
accepts_client_id: false,
|
48
|
+
custom_response_decoder: method(:extract_roads_body))[:snappedPoints]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the posted speed limit (in km/h) for given road segments.
|
52
|
+
#
|
53
|
+
# @example Multi places snap
|
54
|
+
# place_ids = [
|
55
|
+
# 'ChIJ0wawjUCuEmsRgfqC5Wd9ARM',
|
56
|
+
# 'ChIJ6cs2kkCuEmsRUfqC5Wd9ARM'
|
57
|
+
# ]
|
58
|
+
# results = client.speed_limits(place_ids)
|
59
|
+
#
|
60
|
+
# @param [String, Array<String>] place_ids The Place ID of the road segment. Place IDs are returned
|
61
|
+
# by the snap_to_roads function. You can pass up to 100 Place IDs.
|
62
|
+
#
|
63
|
+
# @return [Array] Array of speed limits.
|
64
|
+
def speed_limits(place_ids)
|
65
|
+
params = GoogleMapsService::Convert.as_list(place_ids).map { |place_id| ["placeId", place_id] }
|
66
|
+
|
67
|
+
get("/v1/speedLimits", params,
|
68
|
+
base_url: ROADS_BASE_URL,
|
69
|
+
accepts_client_id: false,
|
70
|
+
custom_response_decoder: method(:extract_roads_body))[:speedLimits]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the posted speed limit (in km/h) for given road segments.
|
74
|
+
#
|
75
|
+
# The provided points will first be snapped to the most likely roads the
|
76
|
+
# vehicle was traveling along.
|
77
|
+
#
|
78
|
+
# @example Multi points snap
|
79
|
+
# path = [
|
80
|
+
# [-33.8671, 151.20714],
|
81
|
+
# [-33.86708, 151.20683000000002],
|
82
|
+
# [-33.867070000000005, 151.20674000000002],
|
83
|
+
# [-33.86703, 151.20625]
|
84
|
+
# ]
|
85
|
+
# results = client.snapped_speed_limits(path)
|
86
|
+
#
|
87
|
+
# @param [Hash, Array] path The path of points to be snapped. A list of (or single)
|
88
|
+
# latitude/longitude tuples.
|
89
|
+
#
|
90
|
+
# @return [Hash] A hash with both a list of speed limits and a list of the snapped
|
91
|
+
# points.
|
92
|
+
def snapped_speed_limits(path)
|
93
|
+
path = GoogleMapsService::Convert.waypoints(path)
|
94
|
+
|
95
|
+
params = {
|
96
|
+
path: path
|
97
|
+
}
|
98
|
+
|
99
|
+
get("/v1/speedLimits", params,
|
100
|
+
base_url: ROADS_BASE_URL,
|
101
|
+
accepts_client_id: false,
|
102
|
+
custom_response_decoder: method(:extract_roads_body))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the nearest road segments for provided points.
|
106
|
+
# The points passed do not need to be part of a continuous path.
|
107
|
+
#
|
108
|
+
# @example Single point snap
|
109
|
+
# results = client.nearest_roads([40.714728, -73.998672])
|
110
|
+
#
|
111
|
+
# @example Multi points snap
|
112
|
+
# points = [
|
113
|
+
# [-33.8671, 151.20714],
|
114
|
+
# [-33.86708, 151.20683000000002],
|
115
|
+
# [-33.867070000000005, 151.20674000000002],
|
116
|
+
# [-33.86703, 151.20625]
|
117
|
+
# ]
|
118
|
+
# results = client.nearest_roads(points)
|
119
|
+
#
|
120
|
+
# @param [Array] points The points to be used for nearest road segment lookup. Array of latitude/longitude pairs
|
121
|
+
# which do not need to be a part of continuous part.
|
122
|
+
# Takes up to 100 independent coordinates, and returns the closest road segment for each point.
|
123
|
+
#
|
124
|
+
# @return [Array] Array of snapped points.
|
125
|
+
|
126
|
+
def nearest_roads(points)
|
127
|
+
points = GoogleMapsService::Convert.waypoints(points)
|
128
|
+
|
129
|
+
params = {
|
130
|
+
points: points
|
131
|
+
}
|
132
|
+
|
133
|
+
get("/v1/nearestRoads", params,
|
134
|
+
base_url: ROADS_BASE_URL,
|
135
|
+
accepts_client_id: false,
|
136
|
+
custom_response_decoder: method(:extract_roads_body))[:snappedPoints]
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
# Extracts a result from a Roads API HTTP response.
|
142
|
+
def extract_roads_body(response)
|
143
|
+
begin
|
144
|
+
body = MultiJson.load(response.body, symbolize_keys: true)
|
145
|
+
rescue
|
146
|
+
unless response.code == "200"
|
147
|
+
check_response_status_code(response)
|
148
|
+
end
|
149
|
+
raise GoogleMapsService::Error::ApiError.new(response), "Received a malformed response."
|
150
|
+
end
|
151
|
+
|
152
|
+
check_roads_body_error(response, body)
|
153
|
+
|
154
|
+
unless response.code == "200"
|
155
|
+
raise GoogleMapsService::Error::ApiError.new(response)
|
156
|
+
end
|
157
|
+
body
|
158
|
+
end
|
159
|
+
|
160
|
+
# Check response body for error status.
|
161
|
+
#
|
162
|
+
# @param [Net::HTTPResponse] response Response object.
|
163
|
+
# @param [Hash] body Response body.
|
164
|
+
def check_roads_body_error(response, body)
|
165
|
+
error = body[:error]
|
166
|
+
return unless error
|
167
|
+
|
168
|
+
case error[:status]
|
169
|
+
when "INVALID_ARGUMENT"
|
170
|
+
if error[:message] == "The provided API key is invalid."
|
171
|
+
raise GoogleMapsService::Error::RequestDeniedError.new(response), error[:message]
|
172
|
+
end
|
173
|
+
raise GoogleMapsService::Error::InvalidRequestError.new(response), error[:message]
|
174
|
+
when "PERMISSION_DENIED"
|
175
|
+
raise GoogleMapsService::Error::RequestDeniedError.new(response), error[:message]
|
176
|
+
when "RESOURCE_EXHAUSTED"
|
177
|
+
raise GoogleMapsService::Error::RateLimitError.new(response), error[:message]
|
178
|
+
else
|
179
|
+
raise GoogleMapsService::Error::ApiError.new(response), error[:message]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
module GoogleMapsService::Apis
|
4
|
+
# Performs requests to the Google Maps TimeZone API."""
|
5
|
+
module TimeZone
|
6
|
+
# Get time zone for a location on the earth, as well as that location's
|
7
|
+
# time offset from UTC.
|
8
|
+
#
|
9
|
+
# @example Current time zone
|
10
|
+
# timezone = client.timezone([39.603481, -119.682251])
|
11
|
+
#
|
12
|
+
# @example Time zone at certain time
|
13
|
+
# timezone = client.timezone([39.603481, -119.682251], timestamp: Time.at(1608))
|
14
|
+
#
|
15
|
+
# @param [Hash, Array] location The latitude/longitude value representing the location to
|
16
|
+
# look up.
|
17
|
+
# @param [Integer, DateTime] timestamp Timestamp specifies the desired time as seconds since
|
18
|
+
# midnight, January 1, 1970 UTC. The Time Zone API uses the timestamp to
|
19
|
+
# determine whether or not Daylight Savings should be applied. Times
|
20
|
+
# before 1970 can be expressed as negative values. Optional. Defaults to
|
21
|
+
# `Time.now`.
|
22
|
+
# @param [String] language The language in which to return results.
|
23
|
+
#
|
24
|
+
# @return [Hash] Time zone object.
|
25
|
+
def timezone(location, timestamp: Time.now, language: nil)
|
26
|
+
location = GoogleMapsService::Convert.latlng(location)
|
27
|
+
timestamp = GoogleMapsService::Convert.time(timestamp)
|
28
|
+
|
29
|
+
params = {
|
30
|
+
location: location,
|
31
|
+
timestamp: timestamp
|
32
|
+
}
|
33
|
+
|
34
|
+
params[:language] = language if language
|
35
|
+
|
36
|
+
get("/maps/api/timezone/json", params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
require "net/http"
|
3
|
+
require "retriable"
|
4
|
+
require "google_maps_service/errors"
|
5
|
+
require "google_maps_service/convert"
|
6
|
+
require "google_maps_service/url"
|
7
|
+
require "google_maps_service/apis/directions"
|
8
|
+
require "google_maps_service/apis/distance_matrix"
|
9
|
+
require "google_maps_service/apis/elevation"
|
10
|
+
require "google_maps_service/apis/geocoding"
|
11
|
+
require "google_maps_service/apis/roads"
|
12
|
+
require "google_maps_service/apis/time_zone"
|
13
|
+
|
14
|
+
module GoogleMapsService
|
15
|
+
# Core client functionality, common across all API requests (including performing
|
16
|
+
# HTTP requests).
|
17
|
+
class Client
|
18
|
+
# Default Google Maps Web Service base endpoints
|
19
|
+
DEFAULT_BASE_URL = "https://maps.googleapis.com"
|
20
|
+
|
21
|
+
# Errors those could be retriable.
|
22
|
+
RETRIABLE_ERRORS = [GoogleMapsService::Error::ServerError, GoogleMapsService::Error::RateLimitError]
|
23
|
+
|
24
|
+
include GoogleMapsService::Apis::Directions
|
25
|
+
include GoogleMapsService::Apis::DistanceMatrix
|
26
|
+
include GoogleMapsService::Apis::Elevation
|
27
|
+
include GoogleMapsService::Apis::Geocoding
|
28
|
+
include GoogleMapsService::Apis::Roads
|
29
|
+
include GoogleMapsService::Apis::TimeZone
|
30
|
+
|
31
|
+
# Secret key for accessing Google Maps Web Service.
|
32
|
+
# Can be obtained at https://developers.google.com/maps/documentation/geocoding/get-api-key#key.
|
33
|
+
# @return [String]
|
34
|
+
attr_accessor :key
|
35
|
+
|
36
|
+
# Client id for using Maps API for Work services.
|
37
|
+
# @return [String]
|
38
|
+
attr_accessor :client_id
|
39
|
+
|
40
|
+
# Client secret for using Maps API for Work services.
|
41
|
+
# @return [String]
|
42
|
+
attr_accessor :client_secret
|
43
|
+
|
44
|
+
# Timeout across multiple retriable requests, in seconds.
|
45
|
+
# @return [Integer]
|
46
|
+
attr_accessor :retry_timeout
|
47
|
+
|
48
|
+
# Number of queries per second permitted.
|
49
|
+
# If the rate limit is reached, the client will sleep for
|
50
|
+
# the appropriate amount of time before it runs the current query.
|
51
|
+
# @return [Integer]
|
52
|
+
attr_reader :queries_per_second
|
53
|
+
|
54
|
+
# Construct Google Maps Web Service API client.
|
55
|
+
#
|
56
|
+
# @example Setup API keys
|
57
|
+
# gmaps = GoogleMapsService::Client.new(key: 'Add your key here')
|
58
|
+
#
|
59
|
+
# @example Setup client IDs
|
60
|
+
# gmaps = GoogleMapsService::Client.new(
|
61
|
+
# client_id: 'Add your client id here',
|
62
|
+
# client_secret: 'Add your client secret here'
|
63
|
+
# )
|
64
|
+
#
|
65
|
+
# @example Setup time out and QPS limit
|
66
|
+
# gmaps = GoogleMapsService::Client.new(
|
67
|
+
# key: 'Add your key here',
|
68
|
+
# retry_timeout: 20,
|
69
|
+
# queries_per_second: 10
|
70
|
+
# )
|
71
|
+
#
|
72
|
+
# @option options [String] :key Secret key for accessing Google Maps Web Service.
|
73
|
+
# Can be obtained at https://developers.google.com/maps/documentation/geocoding/get-api-key#key.
|
74
|
+
# @option options [String] :client_id Client id for using Maps API for Work services.
|
75
|
+
# @option options [String] :client_secret Client secret for using Maps API for Work services.
|
76
|
+
# @option options [Integer] :retry_timeout Timeout across multiple retriable requests, in seconds.
|
77
|
+
# @option options [Integer] :queries_per_second Number of queries per second permitted.
|
78
|
+
def initialize(**options)
|
79
|
+
[:key, :client_id, :client_secret,
|
80
|
+
:retry_timeout, :queries_per_second].each do |key|
|
81
|
+
instance_variable_set("@#{key}".to_sym, options[key] || GoogleMapsService.instance_variable_get("@#{key}"))
|
82
|
+
end
|
83
|
+
[:request_options, :ssl_options, :connection].each do |key|
|
84
|
+
if options.has_key?(key)
|
85
|
+
raise "GoogleMapsService::Client.new no longer supports #{key}."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
initialize_query_tickets
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get the current HTTP client.
|
93
|
+
# @deprecated
|
94
|
+
def client
|
95
|
+
raise "GoogleMapsService::Client.client is no longer implemented."
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
# Initialize QPS queue. QPS queue is a "tickets" for calling API
|
101
|
+
def initialize_query_tickets
|
102
|
+
if @queries_per_second
|
103
|
+
@qps_queue = SizedQueue.new @queries_per_second
|
104
|
+
@queries_per_second.times do
|
105
|
+
@qps_queue << 0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Create a new HTTP client.
|
111
|
+
# @deprecated
|
112
|
+
def new_client
|
113
|
+
raise "GoogleMapsService::Client.new_client is no longer implemented."
|
114
|
+
end
|
115
|
+
|
116
|
+
# Build the user agent header
|
117
|
+
# @return [String]
|
118
|
+
def user_agent
|
119
|
+
@user_agent ||= sprintf("google-maps-services-ruby/%s %s",
|
120
|
+
GoogleMapsService::VERSION,
|
121
|
+
GoogleMapsService::OS_VERSION)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Make API call.
|
125
|
+
#
|
126
|
+
# @param [String] path Url path.
|
127
|
+
# @param [String] params Request parameters.
|
128
|
+
# @param [String] base_url Base Google Maps Web Service API endpoint url.
|
129
|
+
# @param [Boolean] accepts_client_id Sign the request using API {#keys} instead of {#client_id}.
|
130
|
+
# @param [Method] custom_response_decoder Custom method to decode raw API response.
|
131
|
+
#
|
132
|
+
# @return [Object] Decoded response body.
|
133
|
+
def get(path, params, base_url: DEFAULT_BASE_URL, accepts_client_id: true, custom_response_decoder: nil)
|
134
|
+
url = URI(base_url + generate_auth_url(path, params, accepts_client_id))
|
135
|
+
|
136
|
+
Retriable.retriable timeout: @retry_timeout, on: RETRIABLE_ERRORS do |try|
|
137
|
+
begin
|
138
|
+
request_query_ticket
|
139
|
+
request = Net::HTTP::Get.new(url)
|
140
|
+
request["User-Agent"] = user_agent
|
141
|
+
response = Net::HTTP.start(url.hostname, url.port, use_ssl: url.scheme == "https") do |http|
|
142
|
+
http.request(request)
|
143
|
+
end
|
144
|
+
ensure
|
145
|
+
release_query_ticket
|
146
|
+
end
|
147
|
+
|
148
|
+
return custom_response_decoder.call(response) if custom_response_decoder
|
149
|
+
decode_response_body(response)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Get/wait the request "ticket" if QPS is configured.
|
154
|
+
# Check for previous request time, it must be more than a second ago before calling new request.
|
155
|
+
#
|
156
|
+
# @return [void]
|
157
|
+
def request_query_ticket
|
158
|
+
if @qps_queue
|
159
|
+
elapsed_since_earliest = Time.now - @qps_queue.pop
|
160
|
+
sleep(1 - elapsed_since_earliest) if elapsed_since_earliest.to_f < 1
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Release request "ticket".
|
165
|
+
#
|
166
|
+
# @return [void]
|
167
|
+
def release_query_ticket
|
168
|
+
@qps_queue << Time.now if @qps_queue
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the path and query string portion of the request URL,
|
172
|
+
# first adding any necessary parameters.
|
173
|
+
#
|
174
|
+
# @param [String] path The path portion of the URL.
|
175
|
+
# @param [Hash] params URL parameters.
|
176
|
+
# @param [Boolean] accepts_client_id Sign the request using API {#keys} instead of {#client_id}.
|
177
|
+
#
|
178
|
+
# @return [String]
|
179
|
+
def generate_auth_url(path, params, accepts_client_id)
|
180
|
+
# Deterministic ordering through sorting by key.
|
181
|
+
# Useful for tests, and in the future, any caching.
|
182
|
+
params = if params.is_a?(Hash)
|
183
|
+
params.sort
|
184
|
+
else
|
185
|
+
params.dup
|
186
|
+
end
|
187
|
+
|
188
|
+
if accepts_client_id && @client_id && @client_secret
|
189
|
+
params << ["client", @client_id]
|
190
|
+
|
191
|
+
path = [path, GoogleMapsService::Url.urlencode_params(params)].join("?")
|
192
|
+
sig = GoogleMapsService::Url.sign_hmac(@client_secret, path)
|
193
|
+
return path + "&signature=" + sig
|
194
|
+
end
|
195
|
+
|
196
|
+
if @key
|
197
|
+
params << ["key", @key]
|
198
|
+
return path + "?" + GoogleMapsService::Url.urlencode_params(params)
|
199
|
+
end
|
200
|
+
|
201
|
+
raise ArgumentError, "Must provide API key for this API. It does not accept enterprise credentials."
|
202
|
+
end
|
203
|
+
|
204
|
+
# Extract and parse body response as hash. Throw an error if there is something wrong with the response.
|
205
|
+
#
|
206
|
+
# @param [Net::HTTPResponse] response Web API response.
|
207
|
+
#
|
208
|
+
# @return [Hash] Response body as hash. The hash key will be symbolized.
|
209
|
+
def decode_response_body(response)
|
210
|
+
check_response_status_code(response)
|
211
|
+
body = MultiJson.load(response.body, symbolize_keys: true)
|
212
|
+
check_body_error(response, body)
|
213
|
+
body
|
214
|
+
end
|
215
|
+
|
216
|
+
# Check HTTP response status code. Raise error if the status is not 2xx.
|
217
|
+
#
|
218
|
+
# @param [Net::HTTPResponse] response Web API response.
|
219
|
+
def check_response_status_code(response)
|
220
|
+
case response.code.to_i
|
221
|
+
when 200..300
|
222
|
+
# Do-nothing
|
223
|
+
when 301, 302, 303, 307
|
224
|
+
raise GoogleMapsService::Error::RedirectError.new(response), sprintf("Redirect to %s", response.header[:location])
|
225
|
+
when 401
|
226
|
+
raise GoogleMapsService::Error::ClientError.new(response), "Unauthorized"
|
227
|
+
when 304, 400, 402...500
|
228
|
+
raise GoogleMapsService::Error::ClientError.new(response), "Invalid request"
|
229
|
+
when 500..600
|
230
|
+
raise GoogleMapsService::Error::ServerError.new(response), "Server error"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Check response body for error status.
|
235
|
+
#
|
236
|
+
# @param [Net::HTTPResponse] response Response object.
|
237
|
+
# @param [Hash] body Response body.
|
238
|
+
#
|
239
|
+
# @return [void]
|
240
|
+
def check_body_error(response, body)
|
241
|
+
case body[:status]
|
242
|
+
when "OK", "ZERO_RESULTS"
|
243
|
+
# Do-nothing
|
244
|
+
when "OVER_QUERY_LIMIT"
|
245
|
+
raise GoogleMapsService::Error::RateLimitError.new(response), body[:error_message]
|
246
|
+
when "REQUEST_DENIED"
|
247
|
+
raise GoogleMapsService::Error::RequestDeniedError.new(response), body[:error_message]
|
248
|
+
when "INVALID_REQUEST"
|
249
|
+
raise GoogleMapsService::Error::InvalidRequestError.new(response), body[:error_message]
|
250
|
+
else
|
251
|
+
raise GoogleMapsService::Error::ApiError.new(response), body[:error_message]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|