google_map_services 0.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []