google_map_services 0.1.0.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.
@@ -0,0 +1,176 @@
1
+ module GoogleMapsService
2
+
3
+ # Converts Ruby types to string representations suitable for Maps API server.
4
+ module Convert
5
+ module_function
6
+
7
+ # Converts a lat/lon pair to a comma-separated string.
8
+ #
9
+ # @example
10
+ # >> GoogleMapsService::Convert.latlng({"lat": -33.8674869, "lng": 151.2069902})
11
+ # => "-33.867487,151.206990"
12
+ #
13
+ # @param [Hash, Array] arg The lat/lon hash or array pair.
14
+ #
15
+ # @return [String] Comma-separated lat/lng.
16
+ #
17
+ # @raise [ArgumentError] When argument is not lat/lng hash or array.
18
+ def latlng(arg)
19
+ return "%f,%f" % normalize_latlng(arg)
20
+ end
21
+
22
+ # Take the various lat/lng representations and return a tuple.
23
+ #
24
+ # Accepts various representations:
25
+ #
26
+ # 1. Hash with two entries - `lat` and `lng`
27
+ # 2. Array or list - e.g. `[-33, 151]`
28
+ #
29
+ # @param [Hash, Array] arg The lat/lon hash or array pair.
30
+ #
31
+ # @return [Array] Pair of lat and lng array.
32
+ def normalize_latlng(arg)
33
+ if arg.kind_of?(Hash)
34
+ lat = arg[:lat] || arg[:latitude] || arg["lat"] || arg["latitude"]
35
+ lng = arg[:lng] || arg[:longitude] || arg["lng"] || arg["longitude"]
36
+ return lat, lng
37
+ elsif arg.kind_of?(Array)
38
+ return arg[0], arg[1]
39
+ end
40
+
41
+ raise ArgumentError, "Expected a lat/lng Hash or Array, but got #{arg.class}"
42
+ end
43
+
44
+ # If arg is list-like, then joins it with sep.
45
+ #
46
+ # @param [String] sep Separator string.
47
+ # @param [Array, String] arg Value to coerce into a list.
48
+ #
49
+ # @return [String]
50
+ def join_list(sep, arg)
51
+ return as_list(arg).join(sep)
52
+ end
53
+
54
+ # Coerces arg into a list. If arg is already list-like, returns arg.
55
+ # Otherwise, returns a one-element list containing arg.
56
+ #
57
+ # @param [Object] arg
58
+ #
59
+ # @return [Array]
60
+ def as_list(arg)
61
+ if arg.kind_of?(Array)
62
+ return arg
63
+ end
64
+ return [arg]
65
+ end
66
+
67
+
68
+ # Converts the value into a unix time (seconds since unix epoch).
69
+ #
70
+ # @example
71
+ # >> GoogleMapsService::Convert.time(datetime.now())
72
+ # => "1409810596"
73
+ #
74
+ # @param [Time, Date, DateTime, Integer] arg The time.
75
+ #
76
+ # @return [String] String representation of epoch time
77
+ def time(arg)
78
+ if arg.kind_of?(DateTime)
79
+ arg = arg.to_time
80
+ end
81
+ return arg.to_i.to_s
82
+ end
83
+
84
+ # Converts a dict of components to the format expected by the Google Maps
85
+ # server.
86
+ #
87
+ # @example
88
+ # >> GoogleMapsService::Convert.components({"country": "US", "postal_code": "94043"})
89
+ # => "country:US|postal_code:94043"
90
+ #
91
+ # @param [Hash] arg The component filter.
92
+ #
93
+ # @return [String]
94
+ def components(arg)
95
+ if arg.kind_of?(Hash)
96
+ arg = arg.sort.map { |k, v| "#{k}:#{v}" }
97
+ return arg.join("|")
98
+ end
99
+
100
+ raise ArgumentError, "Expected a Hash for components, but got #{arg.class}"
101
+ end
102
+
103
+ # Converts a lat/lon bounds to a comma- and pipe-separated string.
104
+ #
105
+ # Accepts two representations:
106
+ #
107
+ # 1. String: pipe-separated pair of comma-separated lat/lon pairs.
108
+ # 2. Hash with two entries - "southwest" and "northeast". See {.latlng}
109
+ # for information on how these can be represented.
110
+ #
111
+ # For example:
112
+ #
113
+ # >> sydney_bounds = {
114
+ # ?> "northeast": {
115
+ # ?> "lat": -33.4245981,
116
+ # ?> "lng": 151.3426361
117
+ # ?> },
118
+ # ?> "southwest": {
119
+ # ?> "lat": -34.1692489,
120
+ # ?> "lng": 150.502229
121
+ # ?> }
122
+ # ?> }
123
+ # >> GoogleMapsService::Convert.bounds(sydney_bounds)
124
+ # => '-34.169249,150.502229|-33.424598,151.342636'
125
+ #
126
+ # @param [Hash] arg The bounds.
127
+ #
128
+ # @return [String]
129
+ def bounds(arg)
130
+ if arg.kind_of?(Hash)
131
+ southwest = arg[:southwest] || arg["southwest"]
132
+ northeast = arg[:northeast] || arg["northeast"]
133
+ return "#{latlng(southwest)}|#{latlng(northeast)}"
134
+ end
135
+
136
+ raise ArgumentError, "Expected a bounds (southwest/northeast) Hash, but got #{arg.class}"
137
+ end
138
+
139
+ # Converts a waypoints to the format expected by the Google Maps server.
140
+ #
141
+ # Accept two representation of waypoint:
142
+ #
143
+ # 1. String: Name of place or comma-separated lat/lon pair.
144
+ # 2. Hash/Array: Lat/lon pair.
145
+ #
146
+ # @param [Array, String, Hash] waypoint Path.
147
+ #
148
+ # @return [String]
149
+ def waypoint(waypoint)
150
+ if waypoint.kind_of?(String)
151
+ return waypoint
152
+ end
153
+ return GoogleMapsService::Convert.latlng(waypoint)
154
+ end
155
+
156
+ # Converts an array of waypoints (path) to the format expected by the Google Maps
157
+ # server.
158
+ #
159
+ # Accept two representation of waypoint:
160
+ #
161
+ # 1. String: Name of place or comma-separated lat/lon pair.
162
+ # 2. Hash/Array: Lat/lon pair.
163
+ #
164
+ # @param [Array, String, Hash] waypoints Path.
165
+ #
166
+ # @return [String]
167
+ def waypoints(waypoints)
168
+ if waypoints.kind_of?(Array) and waypoints.length == 2 and waypoints[0].kind_of?(Numeric) and waypoints[1].kind_of?(Numeric)
169
+ waypoints = [waypoints]
170
+ end
171
+
172
+ waypoints = as_list(waypoints)
173
+ return join_list('|', waypoints.map { |k| waypoint(k) })
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,56 @@
1
+ module GoogleMapsService
2
+ # Specific Google Maps Service error
3
+ module Error
4
+ # Base error, capable of wrapping another
5
+ class BaseError < StandardError
6
+ # HTTP response object
7
+ # @return [Faraday::Response]
8
+ attr_reader :response
9
+
10
+ # Initialize error
11
+ #
12
+ # @param [Faraday::Response] response HTTP response.
13
+ def initialize(response = nil)
14
+ @response = response
15
+ end
16
+ end
17
+
18
+ # The response redirects to another URL.
19
+ class RedirectError < BaseError
20
+ end
21
+
22
+ # A 4xx class HTTP error occurred.
23
+ # The request is invalid and should not be retried without modification.
24
+ class ClientError < BaseError
25
+ end
26
+
27
+ # A 5xx class HTTP error occurred.
28
+ # An error occurred on the server and the request can be retried.
29
+ class ServerError < BaseError
30
+ end
31
+
32
+ # An unknown error occured.
33
+ class UnknownError < BaseError
34
+ end
35
+
36
+ # General Google Maps Web Service API error occured.
37
+ class ApiError < BaseError
38
+ end
39
+
40
+ # Requiered query is missing
41
+ class InvalidRequestError < ApiError
42
+ end
43
+
44
+ # The quota for the credential is over limit.
45
+ class RateLimitError < ApiError
46
+ end
47
+
48
+ # An unathorized error occurred. It might be caused by invalid key/secret or invalid access.
49
+ class RequestDeniedError < ApiError
50
+ end
51
+
52
+ # When an Address is not found. i.e. An address string could not be geocoded
53
+ class NotFoundError < ApiError
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,90 @@
1
+ require 'google_maps_service/convert'
2
+
3
+ module GoogleMapsService
4
+
5
+ # Encoder/decoder for [Google Encoded Polyline](https://developers.google.com/maps/documentation/utilities/polylinealgorithm).
6
+ module Polyline
7
+ module_function
8
+
9
+ # Decodes a Polyline string into a list of lat/lng hash.
10
+ #
11
+ # See the developer docs for a detailed description of this encoding:
12
+ # https://developers.google.com/maps/documentation/utilities/polylinealgorithm
13
+ #
14
+ # @example
15
+ # encoded_path = '_p~iF~ps|U_ulLnnqC_mqNvxq`@'
16
+ # path = GoogleMapsService::Polyline.decode(encoded_path)
17
+ # #=> [{:lat=>38.5, :lng=>-120.2}, {:lat=>40.7, :lng=>-120.95}, {:lat=>43.252, :lng=>-126.45300000000002}]
18
+ #
19
+ # @param [String] polyline An encoded polyline
20
+ #
21
+ # @return [Array] Array of hash with lat/lng keys
22
+ def decode(polyline)
23
+ points = []
24
+ index = lat = lng = 0
25
+
26
+ while index < polyline.length
27
+ result = 1
28
+ shift = 0
29
+ while true
30
+ b = polyline[index].ord - 63 - 1
31
+ index += 1
32
+ result += b << shift
33
+ shift += 5
34
+ break if b < 0x1f
35
+ end
36
+ lat += (result & 1) != 0 ? (~result >> 1) : (result >> 1)
37
+
38
+ result = 1
39
+ shift = 0
40
+ while true
41
+ b = polyline[index].ord - 63 - 1
42
+ index += 1
43
+ result += b << shift
44
+ shift += 5
45
+ break if b < 0x1f
46
+ end
47
+ lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1)
48
+
49
+ points << {lat: lat * 1e-5, lng: lng * 1e-5}
50
+ end
51
+
52
+ points
53
+ end
54
+
55
+ # Encodes a list of points into a polyline string.
56
+ #
57
+ # See the developer docs for a detailed description of this encoding:
58
+ # https://developers.google.com/maps/documentation/utilities/polylinealgorithm
59
+ #
60
+ # @param [Array<Hash>, Array<Array>] points A list of lat/lng pairs.
61
+ #
62
+ # @return [String]
63
+ def encode(points)
64
+ last_lat = last_lng = 0
65
+ result = ""
66
+
67
+ points.each do |point|
68
+ ll = GoogleMapsService::Convert.normalize_latlng(point)
69
+ lat = (ll[0] * 1e5).round.to_i
70
+ lng = (ll[1] * 1e5).round.to_i
71
+ d_lat = lat - last_lat
72
+ d_lng = lng - last_lng
73
+
74
+ [d_lat, d_lng].each do |v|
75
+ v = (v < 0) ? ~(v << 1) : (v << 1)
76
+ while v >= 0x20
77
+ result += ((0x20 | (v & 0x1f)) + 63).chr
78
+ v >>= 5
79
+ end
80
+ result += (v + 63).chr
81
+ end
82
+
83
+ last_lat = lat
84
+ last_lng = lng
85
+ end
86
+
87
+ result
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,64 @@
1
+ require 'base64'
2
+ require 'uri'
3
+
4
+ module GoogleMapsService
5
+
6
+ # Helper for handling URL.
7
+ module Url
8
+ module_function
9
+
10
+ # Returns a base64-encoded HMAC-SHA1 signature of a given string.
11
+ #
12
+ # @param [String] secret The key used for the signature, base64 encoded.
13
+ # @param [String] payload The payload to sign.
14
+ #
15
+ # @return [String] Base64-encoded HMAC-SHA1 signature
16
+ def sign_hmac(secret, payload)
17
+ secret = secret.encode('ASCII')
18
+ payload = payload.encode('ASCII')
19
+
20
+ # Decode the private key
21
+ raw_key = Base64.urlsafe_decode64(secret)
22
+
23
+ # Create a signature using the private key and the URL
24
+ digest = OpenSSL::Digest.new('sha1')
25
+ raw_signature = OpenSSL::HMAC.digest(digest, raw_key, payload)
26
+
27
+ # Encode the signature into base64 for url use form.
28
+ signature = Base64.urlsafe_encode64(raw_signature)
29
+ return signature
30
+ end
31
+
32
+ # URL encodes the parameters.
33
+ # @param [Hash, Array<Array>] params The parameters
34
+ # @return [String]
35
+ def urlencode_params(params)
36
+ unquote_unreserved(URI.encode_www_form(params))
37
+ end
38
+
39
+ # Un-escape any percent-escape sequences in a URI that are unreserved
40
+ # characters. This leaves all reserved, illegal and non-ASCII bytes encoded.
41
+ #
42
+ # @param [String] uri
43
+ #
44
+ # @return [String]
45
+ def unquote_unreserved(uri)
46
+ parts = uri.split('%')
47
+
48
+ (1..parts.length-1).each do |i|
49
+ h = parts[i][0..1]
50
+
51
+ if h =~ /^([\h]{2})(.*)/ and c = $1.to_i(16).chr and UNRESERVED_SET.include?(c)
52
+ parts[i] = c + $2
53
+ else
54
+ parts[i] = '%' + parts[i]
55
+ end
56
+ end
57
+
58
+ parts.join
59
+ end
60
+
61
+ # The unreserved URI characters (RFC 3986)
62
+ UNRESERVED_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
63
+ end
64
+ end
@@ -0,0 +1,39 @@
1
+ require_relative './convert'
2
+
3
+ module GoogleMapsService
4
+
5
+ # Validate value that is accepted by Google Maps.
6
+ module Validator
7
+ module_function
8
+
9
+ # Validate travel mode. The valid value of travel mode are `driving`, `walking`, `bicycling` or `transit`.
10
+ #
11
+ # @param [String, Symbol] mode Travel mode to be validated.
12
+ #
13
+ # @raise ArgumentError The travel mode is invalid.
14
+ #
15
+ # @return [String] Valid travel mode.
16
+ def travel_mode(mode)
17
+ # NOTE(broady): the mode parameter is not validated by the Maps API
18
+ # server. Check here to prevent silent failures.
19
+ unless [:driving, :walking, :bicycling, :transit].include?(mode.to_sym)
20
+ raise ArgumentError, 'Invalid travel mode.'
21
+ end
22
+ mode
23
+ end
24
+
25
+ # Validate route restriction. The valid value of route restriction are `tolls`, `highways` or `ferries`.
26
+ #
27
+ # @param [String, Symbol] avoid Route restriction to be validated.
28
+ #
29
+ # @raise ArgumentError The route restriction is invalid.
30
+ #
31
+ # @return [String] Valid route restriction.
32
+ def avoid(avoid)
33
+ unless [:tolls, :highways, :ferries].include?(avoid.to_sym)
34
+ raise ArgumentError, 'Invalid route restriction.'
35
+ end
36
+ avoid
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ module GoogleMapsService
2
+ # GoogleMapsService gem version
3
+ VERSION = '0.1.0.0'
4
+
5
+ # Current operating system
6
+ # @private
7
+ OS_VERSION = begin
8
+ if RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/
9
+ `ver`.sub(/\s*\[Version\s*/, '/').sub(']', '').strip
10
+ elsif RUBY_PLATFORM =~ /darwin/i
11
+ "Mac OS X/#{`sw_vers -productVersion`}"
12
+ elsif RUBY_PLATFORM == 'java'
13
+ require 'java'
14
+ name = java.lang.System.getProperty('os.name')
15
+ version = java.lang.System.getProperty('os.version')
16
+ "#{name}/#{version}"
17
+ else
18
+ `uname -sr`.sub(' ', '/')
19
+ end
20
+ rescue
21
+ RUBY_PLATFORM
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+ # Google Maps Web Service API.
2
+ module GoogleMapsService
3
+ class << self
4
+
5
+ # Global key.
6
+ # @see Client#key
7
+ # @return [String]
8
+ attr_accessor :key
9
+
10
+ # Global client_id.
11
+ # @see Client#client_id
12
+ # @return [String]
13
+ attr_accessor :client_id
14
+
15
+ # Global client_secret.
16
+ # @see Client#client_secret
17
+ # @return [String]
18
+ attr_accessor :client_secret
19
+
20
+ # Global retry_timeout.
21
+ # @see Client#retry_timeout
22
+ # @return [Integer]
23
+ attr_accessor :retry_timeout
24
+
25
+ # Global queries_per_second.
26
+ # @see Client#queries_per_second
27
+ # @return [Integer]
28
+ attr_accessor :queries_per_second
29
+
30
+ # Global request_options.
31
+ # @see Client#initialize-instance_method
32
+ # @return [Faraday::RequestOptions]
33
+ attr_accessor :request_options
34
+
35
+ # Global ssl_options.
36
+ # @see Client#initialize-instance_method
37
+ # @return [Faraday::SslOptions]
38
+ attr_accessor :ssl_options
39
+
40
+ # Global connection.
41
+ # @see Client#initialize-instance_method
42
+ # @return [Object]
43
+ attr_accessor :connection
44
+
45
+ # Configure global parameters.
46
+ # @yield [config]
47
+ def configure
48
+ yield self
49
+ true
50
+ end
51
+ end
52
+
53
+ require 'google_maps_service/version'
54
+ require 'google_maps_service/client'
55
+ require 'google_maps_service/polyline'
56
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_map_services
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Edward Samuel Pasaribu
8
+ - Ahmed Abdelhamid
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-01-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multi_json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.12'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.12'
28
+ - !ruby/object:Gem::Dependency
29
+ name: faraday
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: retriable
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ description:
57
+ email:
58
+ - edwardsamuel92@gmail.com
59
+ - eng.a.abdelhamid@outlook.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".rspec"
66
+ - ".ruby-gemset"
67
+ - ".ruby-version"
68
+ - ".travis.yml"
69
+ - ".yardopts"
70
+ - CHANGELOG.md
71
+ - CODE_OF_CONDUCT.md
72
+ - Gemfile
73
+ - LICENSE
74
+ - README.md
75
+ - Rakefile
76
+ - bin/console
77
+ - google_maps_service.gemspec
78
+ - lib/google_maps_service.rb
79
+ - lib/google_maps_service/apis.rb
80
+ - lib/google_maps_service/apis/directions.rb
81
+ - lib/google_maps_service/apis/distance_matrix.rb
82
+ - lib/google_maps_service/apis/elevation.rb
83
+ - lib/google_maps_service/apis/geocoding.rb
84
+ - lib/google_maps_service/apis/places.rb
85
+ - lib/google_maps_service/apis/roads.rb
86
+ - lib/google_maps_service/apis/time_zone.rb
87
+ - lib/google_maps_service/client.rb
88
+ - lib/google_maps_service/convert.rb
89
+ - lib/google_maps_service/errors.rb
90
+ - lib/google_maps_service/polyline.rb
91
+ - lib/google_maps_service/url.rb
92
+ - lib/google_maps_service/validator.rb
93
+ - lib/google_maps_service/version.rb
94
+ homepage: https://github.com/go-illa/google-maps-services-ruby
95
+ licenses:
96
+ - Apache-2.0
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 2.0.0
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.1.6
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Ruby gem for Google Maps Web Service APIs
117
+ test_files: []