google_distance_matrix 0.0.1
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.
- data/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +1 -0
- data/google_distance_matrix.gemspec +30 -0
- data/lib/google_distance_matrix.rb +38 -0
- data/lib/google_distance_matrix/client.rb +47 -0
- data/lib/google_distance_matrix/configuration.rb +68 -0
- data/lib/google_distance_matrix/errors.rb +88 -0
- data/lib/google_distance_matrix/log_subscriber.rb +14 -0
- data/lib/google_distance_matrix/logger.rb +32 -0
- data/lib/google_distance_matrix/matrix.rb +122 -0
- data/lib/google_distance_matrix/place.rb +101 -0
- data/lib/google_distance_matrix/places.rb +43 -0
- data/lib/google_distance_matrix/railtie.rb +9 -0
- data/lib/google_distance_matrix/route.rb +49 -0
- data/lib/google_distance_matrix/routes_finder.rb +149 -0
- data/lib/google_distance_matrix/url_builder.rb +63 -0
- data/lib/google_distance_matrix/version.rb +3 -0
- data/spec/lib/google_distance_matrix/client_spec.rb +67 -0
- data/spec/lib/google_distance_matrix/configuration_spec.rb +63 -0
- data/spec/lib/google_distance_matrix/logger_spec.rb +38 -0
- data/spec/lib/google_distance_matrix/matrix_spec.rb +169 -0
- data/spec/lib/google_distance_matrix/place_spec.rb +93 -0
- data/spec/lib/google_distance_matrix/places_spec.rb +77 -0
- data/spec/lib/google_distance_matrix/route_spec.rb +28 -0
- data/spec/lib/google_distance_matrix/routes_finder_spec.rb +190 -0
- data/spec/lib/google_distance_matrix/url_builder_spec.rb +105 -0
- data/spec/request_recordings/success +62 -0
- data/spec/request_recordings/zero_results +57 -0
- data/spec/spec_helper.rb +24 -0
- metadata +225 -0
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p194
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Thorbjørn Hermansen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# GoogleDistanceMatrix
|
2
|
+
|
3
|
+
Ruby client for The Google Distance Matrix API.
|
4
|
+
|
5
|
+
This lib makes Google's distance matrix API easy to work with,
|
6
|
+
allowing you to set up some origins and destinations and
|
7
|
+
pull the distance matrix from Google.
|
8
|
+
|
9
|
+
Once you have the matrix you can fetch all routes from a given
|
10
|
+
origin or to a given destination.
|
11
|
+
|
12
|
+
The matrix may also be used as a data set for traveling salesman problem,
|
13
|
+
but to solve it you may look at http://ai4r.org/.
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
## Examples
|
19
|
+
|
20
|
+
### Set up a matrix to work with
|
21
|
+
|
22
|
+
matrix = GoogleDistanceMatrix::Matrix.new
|
23
|
+
|
24
|
+
### Create some places to be used as origins or destinations for the matrix
|
25
|
+
|
26
|
+
lat_lng = GoogleDistanceMatrix::Place.new lng: 12, lat: 12
|
27
|
+
address = GoogleDistanceMatrix::Place.new address: "My address, Oslo"
|
28
|
+
dest_address = GoogleDistanceMatrix::Place.new address: "Home, Oppegaard"
|
29
|
+
|
30
|
+
point_dest = Point.new lat: 1, lng: 2
|
31
|
+
dest_object = GoogleDistanceMatrix::Place.new point_dest
|
32
|
+
|
33
|
+
### Add places to matrix's origins and destinations
|
34
|
+
|
35
|
+
matrix.origins << lat_lng << address
|
36
|
+
matrix.destinations << dest_address << dest_object
|
37
|
+
|
38
|
+
# Added objects will be wrapped in a place automatically, so you may skip manyally creating Places.
|
39
|
+
another_point = Point.new lat: 1, lng: 3
|
40
|
+
matrix.origins << another_point
|
41
|
+
|
42
|
+
### Do some configuration - see GoogleDistanceMatrix.default_configuration as well.
|
43
|
+
|
44
|
+
matrix.configure do |config|
|
45
|
+
config.mode = 'driving'
|
46
|
+
config.avoid = ['tolls']
|
47
|
+
|
48
|
+
# To build signed URLs to use with a Google Business account
|
49
|
+
config.google_business_api_client_id = "123"
|
50
|
+
config.google_business_api_private_key = "your-secret-key"
|
51
|
+
end
|
52
|
+
|
53
|
+
### Get the data for the matrix
|
54
|
+
|
55
|
+
# Returns the data, loaded from Google, for this matrix.
|
56
|
+
# It is a multi dimensional array. Rows are ordered according to the values in the origins.
|
57
|
+
# Each row corresponds to an origin, and each element within that row corresponds to
|
58
|
+
# a pairing of the origin with a destination.
|
59
|
+
matrix.data
|
60
|
+
|
61
|
+
### Query the matrix
|
62
|
+
|
63
|
+
# Returns an array of Google::DistanceMatrix::Route, all having given origin or destination.
|
64
|
+
matrix.routes_for dest_address
|
65
|
+
|
66
|
+
# Returns Google::DistanceMatrix::Route with given origin and destination
|
67
|
+
matrix.route_for origin: lat_lng, destination: dest_address
|
68
|
+
|
69
|
+
matrix.shortest_route_by_distance_to(dest_address) # returns Google::DistanceMatrix::Route with one origin and a destination, together with route data
|
70
|
+
matrix.shortest_route_by_duration_to(dest_address) # returns Google::DistanceMatrix::Route with one origin and a destination, together with route data
|
71
|
+
|
72
|
+
# In cases you built the place with an object (not hash with attributes) you may provide that object
|
73
|
+
# as well asking for routes. This is true for route_for and shortest_route_by_* as well.
|
74
|
+
matrix.routes_for point_dest # Returns routes for dest_object
|
75
|
+
|
76
|
+
You may call query methods with a bang, in which case it will fail with an error if noe all routes in your result set for the called method is ok.
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
## Installation
|
83
|
+
|
84
|
+
Add this line to your application's Gemfile:
|
85
|
+
|
86
|
+
gem 'google_distance_matrix'
|
87
|
+
|
88
|
+
And then execute:
|
89
|
+
|
90
|
+
$ bundle
|
91
|
+
|
92
|
+
Or install it yourself as:
|
93
|
+
|
94
|
+
$ gem install google_distance_matrix
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'google_distance_matrix/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "google_distance_matrix"
|
8
|
+
spec.version = GoogleDistanceMatrix::VERSION
|
9
|
+
spec.authors = ["Thorbjørn Hermansen"]
|
10
|
+
spec.email = ["thhermansen@gmail.com"]
|
11
|
+
spec.description = %q{Ruby client for The Google Distance Matrix API}
|
12
|
+
spec.summary = %q{Ruby client for The Google Distance Matrix API}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport", "~> 3.2.13"
|
22
|
+
spec.add_dependency "activemodel", "~> 3.2.13"
|
23
|
+
spec.add_dependency "google_business_api_url_signer", "~> 0.0.1"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.13"
|
27
|
+
spec.add_development_dependency "shoulda-matchers", "~> 2.0.0"
|
28
|
+
spec.add_development_dependency "webmock", "~> 1.11"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "google_distance_matrix/version"
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
require "json"
|
5
|
+
require "active_model"
|
6
|
+
require "active_support/core_ext/hash"
|
7
|
+
require "google_business_api_url_signer"
|
8
|
+
|
9
|
+
require "google_distance_matrix/logger"
|
10
|
+
require "google_distance_matrix/errors"
|
11
|
+
require "google_distance_matrix/configuration"
|
12
|
+
require "google_distance_matrix/url_builder"
|
13
|
+
require "google_distance_matrix/client"
|
14
|
+
require "google_distance_matrix/routes_finder"
|
15
|
+
require "google_distance_matrix/matrix"
|
16
|
+
require "google_distance_matrix/places"
|
17
|
+
require "google_distance_matrix/place"
|
18
|
+
require "google_distance_matrix/route"
|
19
|
+
|
20
|
+
require "google_distance_matrix/log_subscriber"
|
21
|
+
require 'google_distance_matrix/railtie' if defined? Rails
|
22
|
+
|
23
|
+
|
24
|
+
module GoogleDistanceMatrix
|
25
|
+
extend self
|
26
|
+
|
27
|
+
def default_configuration
|
28
|
+
@default_configuration ||= Configuration.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def configure_defaults
|
32
|
+
yield default_configuration
|
33
|
+
end
|
34
|
+
|
35
|
+
def logger
|
36
|
+
@logger ||= Logger.new default_configuration.logger
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module GoogleDistanceMatrix
|
2
|
+
class Client
|
3
|
+
CLIENT_ERRORS = %w[
|
4
|
+
INVALID_REQUEST
|
5
|
+
MAX_ELEMENTS_EXCEEDED
|
6
|
+
OVER_QUERY_LIMIT
|
7
|
+
REQUEST_DENIED
|
8
|
+
UNKNOWN_ERROR
|
9
|
+
]
|
10
|
+
|
11
|
+
def get(url)
|
12
|
+
uri = URI.parse url
|
13
|
+
|
14
|
+
response = ActiveSupport::Notifications.instrument "client_request_matrix_data.google_distance_matrix", url: url do
|
15
|
+
Net::HTTP.get_response uri
|
16
|
+
end
|
17
|
+
|
18
|
+
case response
|
19
|
+
when Net::HTTPSuccess
|
20
|
+
inspect_for_client_errors! response
|
21
|
+
when Net::HTTPRequestURITooLong
|
22
|
+
fail MatrixUrlTooLong.new url, UrlBuilder::MAX_URL_SIZE, response
|
23
|
+
when Net::HTTPClientError
|
24
|
+
fail ClientError.new response
|
25
|
+
when Net::HTTPServerError
|
26
|
+
fail RequestError.new response
|
27
|
+
else # Handle this as a request error for now. Maybe fine tune this more later.
|
28
|
+
fail RequestError.new response
|
29
|
+
end
|
30
|
+
rescue Timeout::Error => error
|
31
|
+
fail RequestError.new error
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def inspect_for_client_errors!(response)
|
38
|
+
status = JSON.parse(response.body).fetch "status"
|
39
|
+
|
40
|
+
if CLIENT_ERRORS.include? status
|
41
|
+
fail ClientError.new response, status
|
42
|
+
end
|
43
|
+
|
44
|
+
response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module GoogleDistanceMatrix
|
2
|
+
# Public: Configuration of matrix and it's request.
|
3
|
+
#
|
4
|
+
# Holds configuration used when building API URL.
|
5
|
+
#
|
6
|
+
# See https://developers.google.com/maps/documentation/distancematrix/#RequestParameters
|
7
|
+
# for documentation on each configuration.
|
8
|
+
#
|
9
|
+
class Configuration
|
10
|
+
include ActiveModel::Validations
|
11
|
+
|
12
|
+
ATTRIBUTES = %w[sensor mode avoid units]
|
13
|
+
|
14
|
+
API_DEFAULTS = {
|
15
|
+
mode: "driving",
|
16
|
+
units: "metric"
|
17
|
+
}.with_indifferent_access
|
18
|
+
|
19
|
+
attr_accessor *ATTRIBUTES, :protocol, :logger, :lat_lng_scale
|
20
|
+
attr_accessor :google_business_api_client_id, :google_business_api_private_key
|
21
|
+
|
22
|
+
|
23
|
+
validates :sensor, inclusion: {in: [true, false]}
|
24
|
+
validates :mode, inclusion: {in: ["driving", "walking", "bicycling"]}, allow_blank: true
|
25
|
+
validates :avoid, inclusion: {in: ["tolls", "highways"]}, allow_blank: true
|
26
|
+
validates :units, inclusion: {in: ["metric", "imperial"]}, allow_blank: true
|
27
|
+
|
28
|
+
validates :protocol, inclusion: {in: ["http", "https"]}, allow_blank: true
|
29
|
+
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
self.sensor = false
|
33
|
+
self.protocol = "http"
|
34
|
+
self.lat_lng_scale = 5
|
35
|
+
|
36
|
+
API_DEFAULTS.each_pair do |attr_name, value|
|
37
|
+
self[attr_name] = value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_param
|
42
|
+
Hash[array_param]
|
43
|
+
end
|
44
|
+
|
45
|
+
def []=(attr_name, value)
|
46
|
+
public_send "#{attr_name}=", value
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def array_param
|
53
|
+
out = ATTRIBUTES.map { |attr| [attr, public_send(attr)] }.reject do |attr_and_value|
|
54
|
+
attr_and_value[1].nil? || param_same_as_api_default?(attr_and_value)
|
55
|
+
end
|
56
|
+
|
57
|
+
if google_business_api_client_id.present?
|
58
|
+
out << ['client', google_business_api_client_id]
|
59
|
+
end
|
60
|
+
|
61
|
+
out
|
62
|
+
end
|
63
|
+
|
64
|
+
def param_same_as_api_default?(param)
|
65
|
+
API_DEFAULTS[param[0]] == param[1]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module GoogleDistanceMatrix
|
2
|
+
# Public: Error class for lib.
|
3
|
+
class Error < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Public: Matrix has errors.
|
7
|
+
class InvalidMatrix < Error
|
8
|
+
def initialize(matrix)
|
9
|
+
@matrix = matrix
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@matrix.errors.full_messages.to_sentence
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Route seems invalid
|
18
|
+
#
|
19
|
+
# Fails if a route is built, but it's status from
|
20
|
+
# Google isn't OK.
|
21
|
+
#
|
22
|
+
class InvalidRoute < Error
|
23
|
+
def initialize(route)
|
24
|
+
@route = route
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"API did not provide a complete answer for #{@route}."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Got a request error back trying to do a request
|
33
|
+
#
|
34
|
+
# This includes wire errors like timeouts etc, and server errors
|
35
|
+
# like 5xx. Inspect error_or_response for more information.
|
36
|
+
#
|
37
|
+
class RequestError < Error
|
38
|
+
attr_reader :error_or_response
|
39
|
+
|
40
|
+
def initialize(error_or_response)
|
41
|
+
@error_or_response = error_or_response
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"GoogleDistanceMatrix::RequestError - #{error_or_response.inspect}."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Got an error where the client seems to be doing something wrong
|
50
|
+
#
|
51
|
+
# These errors comes from http 4xx errors, or API errors like MAX_ELEMENTS_EXCEEDED etc.
|
52
|
+
# See https://developers.google.com/maps/documentation/distancematrix/#StatusCodes for info.
|
53
|
+
#
|
54
|
+
class ClientError < Error
|
55
|
+
attr_reader :response, :status_read_from_api_response
|
56
|
+
|
57
|
+
def initialize(response, status_read_from_api_response = nil)
|
58
|
+
@response = response
|
59
|
+
@status_read_from_api_response = status_read_from_api_response
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"GoogleDistanceMatrix::ClientError - #{[response, status_read_from_api_response].compact.join('. ')}."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: API URL was too long
|
68
|
+
#
|
69
|
+
# See https://developers.google.com/maps/documentation/distancematrix/#Limits, which states:
|
70
|
+
# "Distance Matrix API URLs are restricted to 2048 characters, before URL encoding."
|
71
|
+
# "As some Distance Matrix API service URLs may involve many locations, be aware of this limit when constructing your URLs."
|
72
|
+
#
|
73
|
+
class MatrixUrlTooLong < ClientError
|
74
|
+
attr_reader :url, :max_url_size
|
75
|
+
|
76
|
+
def initialize(url, max_url_size, response = nil)
|
77
|
+
super response
|
78
|
+
|
79
|
+
@url = url
|
80
|
+
@max_url_size = max_url_size
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
"Matrix API URL max size is: #{max_url_size}. Built URL was: #{url.length}. URL: '#{url}'."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module GoogleDistanceMatrix
|
2
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
|
+
def self.logger
|
4
|
+
GoogleDistanceMatrix.logger
|
5
|
+
end
|
6
|
+
|
7
|
+
def client_request_matrix_data(event)
|
8
|
+
logger.info "(#{event.duration}ms) GET #{event.payload[:url]}", tag: :client
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
GoogleDistanceMatrix::LogSubscriber.attach_to "google_distance_matrix"
|
14
|
+
|