Skip to content

Commit

Permalink
Merge pull request #5 from algonauti/4-directions-endpoint
Browse files Browse the repository at this point in the history
4-directions-endpoint
  • Loading branch information
safeforge committed Oct 30, 2019
2 parents 6e37676 + 45868e6 commit 53363fd
Show file tree
Hide file tree
Showing 15 changed files with 1,294 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
@@ -1 +1 @@
2.5.0
2.6.3
4 changes: 2 additions & 2 deletions .travis.yml
@@ -1,9 +1,9 @@
sudo: false
language: ruby
rvm:
- 2.5.0
- 2.6.3

before_install: gem install bundler -v 1.16.4
before_install: gem install bundler -v 1.17.2

install: bundle install

Expand Down
52 changes: 52 additions & 0 deletions README.md
Expand Up @@ -17,6 +17,7 @@ This gem aims at progressively covering a fair amount of those widely-used servi

* Geocoding
* Time Zone
* Directions

Contributors are welcome!

Expand Down Expand Up @@ -181,6 +182,57 @@ The `by_location` method returns a `GoogleMapsJuice::Timezone::Response`. It's a
* `dst_offset`: the offset for daylight-savings time in seconds


## Directions

[Google's Directions API](https://developers.google.com/maps/documentation/directions/intro#DirectionsRequests) returns the possible routes of given origin and destination geographic locations; Google's API accepts address, textual latitude/longitude value, or place ID of which you wish to calculate directions. Currently this gem implements only latitude/longitude mode.
`GoogleMapsJuice` will raise an `ArgumentError` if some unsupported param is passed, or when none of the required params are passed.

```ruby
response = GoogleMapsJuice::Directions.find(origin: '41.8892732,12.4921921', destination: '41.9016488,12.4583003')
```

Compared to Google's raw API request, it provides validation of both origin and destination, in order to avoid sending requests when they would fail for sure - to learn more see `spec/unit/directions_spec.rb`.
**Accepted params:**
* Both `origin` and `destination` are mandatory
* `origin` is composed by `latitude` and `longitude`, comma separated float values
* `destination` same format as `origin`
### Directions Response
The `find` method returns a `GoogleMapsJuice::Directions::Response`. It's a `Hash` representation of Google's JSON response. However, it also provides a few useful methods:
* `results`: the `Hash` raw result
* `routes`: an `Array` of `GoogleMapsJuice::Directions::Response::Route` objects
* `first`: the first `Route` of the `routes` `List`
As described in [Google's Directions API](https://developers.google.com/maps/documentation/directions/intro#Routes), the response contains all possible routes. Each route has some attributes and one or more *legs*, wich in turn have one or more *steps*.
If no waypoints are passed, the route response will contain a single leg. Since `GoogleMapsJuice::Directions` doesn't handles waypoints yet, only the first leg is considered for each route.
The `GoogleMapsJuice::Directions::Response::Route` is a representation of a response route and provides methods to access all route's attributes:

* `summary`: a brief description of the route

* `legs`: all legs of the route, generally a single one

* `steps`: all steps of the first route's leg
* `duration`: time duration of the first route's leg

* `distance`: distance between origin and destination of first route's leg
* `start_location`: `latitude`/`longitude` of the origin first route's leg

* `end_location`: `latitude`/`longitude` of the destination first route's leg
* `start_address`: address of the origin first route's leg

* `end_address`: address of the destination first route's leg
## Development
After checking out the repo, run `bin/setup` to install dependencies. Create a `.env` file and save your Google API key there; if you want to use a different key for testing, put it in `.env.test` and it will override the one in `.env`.
Expand Down
2 changes: 1 addition & 1 deletion google_maps_juice.gemspec
Expand Up @@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'activesupport', '~> 5.2'
spec.add_dependency 'excon'

spec.add_development_dependency 'bundler', '~> 1.16'
spec.add_development_dependency 'bundler', '~> 1.17.2'
spec.add_development_dependency 'rake', '~> 12.3'
spec.add_development_dependency 'rspec', '~> 3.5'
spec.add_development_dependency 'webmock', '~> 3.4'
Expand Down
1 change: 1 addition & 0 deletions lib/google_maps_juice.rb
Expand Up @@ -14,6 +14,7 @@ module GoogleMapsJuice
autoload :Endpoint, 'google_maps_juice/endpoint'
autoload :Geocoding, 'google_maps_juice/geocoding'
autoload :Timezone, 'google_maps_juice/timezone'
autoload :Directions, 'google_maps_juice/directions'

class Error < Exception; end
class ApiLimitError < Exception; end
Expand Down
55 changes: 55 additions & 0 deletions lib/google_maps_juice/directions.rb
@@ -0,0 +1,55 @@
module GoogleMapsJuice
class Directions < Endpoint

ENDPOINT = '/directions'

autoload :Response, 'google_maps_juice/directions/response'

class << self
def find(params, api_key: GoogleMapsJuice.config.api_key)
client = GoogleMapsJuice::Client.new(api_key: api_key)
new(client).find(params)
end
end

def find(params)
validate_find_params(params)
response_text = @client.get("#{ENDPOINT}/json", params)
response = JSON.parse(response_text, object_class: Response)
detect_errors(response)
end

def validate_find_params(params)
raise ArgumentError, 'Hash argument expected' unless params.is_a?(Hash)

supported_keys = %w[origin destination]
validate_supported_params(params, supported_keys)

required_keys = %w[origin destination]
validate_any_required_params(params, required_keys)

validate_geo_coordinate(params)
end

def validate_geo_coordinate(params)
raise ArgumentError, 'String argument expected' unless params.values.all?(String)

geocoords = params.values.map { |x| x.split(',') }.flatten
geocoords.map! { |x| Float(x).round(7) }
raise ArgumentError, 'Wrong geo-coordinates' if geocoords.size != 4

validate_location_params(geocoords)
end

def validate_location_params(params)
latitudes = params[0], params[2]
if latitudes.any? { |l| l.abs > 90 }
raise ArgumentError, 'Wrong latitude value'
end
longitudes = params[1], params[3]
if longitudes.any? { |l| l.abs > 180 }
raise ArgumentError, 'Wrong longitude value'
end
end
end
end
63 changes: 63 additions & 0 deletions lib/google_maps_juice/directions/response.rb
@@ -0,0 +1,63 @@
# frozen_string_literal: true

require 'active_support/core_ext/hash/slice'

module GoogleMapsJuice
class Directions::Response < GoogleMapsJuice::Endpoint::Response
def results
self['routes']
end

def routes
results.map { |r| Route.new(r) }
end

def first
routes.first
end

class Route
attr_reader :route, :first_leg
def initialize(route)
@route = route
@first_leg = route['legs'].first
end

def legs
route['legs']
end

def summary
route['summary']
end

def steps
first_leg['steps']
end

def duration
first_leg['duration']
end

def distance
first_leg['distance']
end

def start_location
first_leg['start_location']
end

def end_location
first_leg['end_location']
end

def start_address
first_leg['start_address']
end

def end_address
first_leg['end_address']
end
end
end
end
93 changes: 93 additions & 0 deletions spec/fixtures/response/directions/colosseum-colosseum.json
@@ -0,0 +1,93 @@
{
"geocoded_waypoints": [
{
"geocoder_status": "OK",
"place_id": "ChIJrRMgU7ZhLxMRxAOFkC7I8Sg",
"types": [
"establishment",
"point_of_interest",
"tourist_attraction"
]
},
{
"geocoder_status": "OK",
"place_id": "ChIJrRMgU7ZhLxMRxAOFkC7I8Sg",
"types": [
"establishment",
"point_of_interest",
"tourist_attraction"
]
}
],
"routes": [
{
"bounds": {
"northeast": {
"lat": 41.8892732,
"lng": 12.4921921
},
"southwest": {
"lat": 41.8892732,
"lng": 12.4921921
}
},
"copyrights": "Map data ©2019 Google",
"legs": [
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"end_address": "Piazza del Colosseo, 1, 00184 Roma RM, Italy",
"end_location": {
"lat": 41.8892732,
"lng": 12.4921921
},
"start_address": "Piazza del Colosseo, 1, 00184 Roma RM, Italy",
"start_location": {
"lat": 41.8892732,
"lng": 12.4921921
},
"steps": [
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 0
},
"end_location": {
"lat": 41.8892732,
"lng": 12.4921921
},
"html_instructions": "Head on <b>Via Celio Vibenna</b>",
"polyline": {
"points": "}nt~Fe{fkA"
},
"start_location": {
"lat": 41.8892732,
"lng": 12.4921921
},
"travel_mode": "DRIVING"
}
],
"traffic_speed_entry": [],
"via_waypoint": []
}
],
"overview_polyline": {
"points": "}nt~Fe{fkA"
},
"summary": "Via Celio Vibenna",
"warnings": [],
"waypoint_order": []
}
],
"status": "OK"
}

0 comments on commit 53363fd

Please sign in to comment.