mapbox_directions 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +14 -0
  5. data/Gemfile +6 -0
  6. data/Guardfile +5 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +148 -0
  9. data/Rakefile +2 -0
  10. data/lib/mapbox_directions.rb +12 -0
  11. data/lib/mapbox_directions/client.rb +43 -0
  12. data/lib/mapbox_directions/error.rb +7 -0
  13. data/lib/mapbox_directions/model.rb +6 -0
  14. data/lib/mapbox_directions/model/location.rb +19 -0
  15. data/lib/mapbox_directions/model/maneuver.rb +14 -0
  16. data/lib/mapbox_directions/model/point.rb +17 -0
  17. data/lib/mapbox_directions/model/response.rb +16 -0
  18. data/lib/mapbox_directions/model/route.rb +24 -0
  19. data/lib/mapbox_directions/model/step.rb +19 -0
  20. data/lib/mapbox_directions/parametizer.rb +50 -0
  21. data/lib/mapbox_directions/response_parser.rb +69 -0
  22. data/lib/mapbox_directions/version.rb +3 -0
  23. data/mapbox_directions.gemspec +32 -0
  24. data/spec/acceptance/directions_spec.rb +158 -0
  25. data/spec/acceptance/no_routes_found_spec.rb +25 -0
  26. data/spec/acceptance/raising_exceptions_spec.rb +54 -0
  27. data/spec/fixtures/cassettes/driving_geojson_as_geometry.yml +186 -0
  28. data/spec/fixtures/cassettes/driving_polyline_as_geometry.yml +186 -0
  29. data/spec/fixtures/cassettes/no_routes_found.yml +52 -0
  30. data/spec/fixtures/cassettes/unauthorized.yml +144 -0
  31. data/spec/lib/mapbox_directions/client_spec.rb +48 -0
  32. data/spec/lib/mapbox_directions/model/location_spec.rb +21 -0
  33. data/spec/lib/mapbox_directions/model/point_spec.rb +17 -0
  34. data/spec/lib/mapbox_directions/model/route_spec.rb +17 -0
  35. data/spec/lib/mapbox_directions/parametizer_spec.rb +92 -0
  36. data/spec/lib/mapbox_directions/response_parser_spec.rb +140 -0
  37. data/spec/spec_helper.rb +18 -0
  38. metadata +235 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 499d139ed0106ee1428d94f915bd25e856af786b
4
+ data.tar.gz: 11282aac7616703f4be00af438f3af4303c3daac
5
+ SHA512:
6
+ metadata.gz: 46fe7a87a9d0c4040867266f8644c1095de40fc1c66dd645d403d7f97facc454902918ff142c77fc09f53c7bdf5dccf38149d9cb11975529d6423487d8630de8
7
+ data.tar.gz: b4091582b7fecb8eafe012139cf7171243d6effb776f22222ad6bc1fb694ba9993ee9390cc4393b7edb96821d95e4ee36eb6b9f8e857bffd5e71dd26dda8974e
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --require spec_helper
3
+ --require byebug
4
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ script: CODECLIMATE_REPO_TOKEN=5e7eb3ca9f5744cce3649045cd4887324323c2ffc7b6b3a3be7de1bbb064ddbf bundle exec rspec
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.0.0
6
+ - 2.1.0
7
+ - 2.1.1
8
+ - 2.1.2
9
+ - 2.1.3
10
+ - 2.1.4
11
+ - 2.1.5
12
+ - 2.2.0
13
+ - 2.2.1
14
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "codeclimate-test-reporter", group: :test, require: false
4
+
5
+ # Specify your gem's dependencies in mapbox_directions.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 yonelacort
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,148 @@
1
+ # MapboxDirections
2
+
3
+ [![Build Status](https://travis-ci.org/allyapp/mapbox_directions.svg?branch=master)](https://travis-ci.org/allyapp/mapbox_directions)
4
+ [![Code Climate](https://codeclimate.com/github/allyapp/mapbox_directions/badges/gpa.svg)](https://codeclimate.com/github/allyapp/mapbox_directions)
5
+ [![Test Coverage](https://codeclimate.com/github/allyapp/mapbox_directions/badges/coverage.svg)](https://codeclimate.com/github/allyapp/mapbox_directions/coverage)
6
+
7
+ Ruby wrapper for the MapBox Directions Service.
8
+
9
+ Here you can find the documentation of the API interface:
10
+ https://www.mapbox.com/developers/api/directions/
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'mapbox_directions'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install mapbox_directions
27
+
28
+ ## Usage
29
+
30
+ Here's the list of supported parameters and the possible values. Note that parameters that are not optional are required:
31
+
32
+ - **access_token**: public token provided by MapBox.
33
+ - **mode**: mode of transport applied to process the routing.
34
+ Values: ``driving``, ``cycling``or``walking``.
35
+ - **origin**: origin decimal coordinates where the route starts.
36
+ Format: ``"#{lng},#{lat}"``
37
+ - **destination**: destination decimal coordinates where the route ends.
38
+ Format: ``"#{lng},#{lat}"``
39
+ - **geometry**(optional): format for route geometry.
40
+ Values ``geojson``(default), ``polyline``and ``false`` to omit geometry.
41
+ - **alternatives**(optional): whether to get more than one route as an alternative or not.
42
+ Values: ``true``(default) or ``false`` as a Boolean.
43
+ - **instructions**(optional): format for route instructions.
44
+ Values: ``text``(default) or ``html``.
45
+
46
+
47
+ ```ruby
48
+ require "mapbox_directions"
49
+
50
+ parameters = {
51
+ access_token: "<your_access_token>",
52
+ mode: "driving", # %w(driving cycling walking)
53
+ origin: "-122.42,37.78", # "#{lng},#{lat}"
54
+ destination: "-77.03,38.91", # "#{lng},#{lat}"
55
+ geometry: "polyline", # %w(polyline geojson false)
56
+ alternatives: false, # true or false
57
+ instructions: "text", # %w(text html)
58
+ }
59
+ MapboxDirections.directions(parameters)
60
+ ```
61
+
62
+ ### Response body
63
+
64
+ ```ruby
65
+ MapboxDirections.directions(parameters)
66
+ ```
67
+
68
+ Will return a ``MapboxDirections::Response`` that will be composed by:
69
+ - **origin**(``MapboxDirections::Location``): Origin location of the route.
70
+ - **destination**(``MapboxDirections::Location``): Destination location of the route.
71
+ - **waypoints**
72
+ - **routes**(``MapboxDirections::Route``): Array of routes returned.
73
+ - **message**: Informative message.
74
+ - **error**: Informative error message.
75
+
76
+ ``MapboxDirections::Location``:
77
+ - **name**
78
+ - **lat**
79
+ - **lng**
80
+
81
+ ``MapboxDirections::Route``:
82
+ - **distance**
83
+ - **duration**
84
+ - **summary**
85
+ - **geometry**
86
+ - **steps**(``Array[MapboxDirections::Step]``)
87
+ - Methods:
88
+ - **transform_polyline_precision(precision = 1e5)**: When geometry is polyline, polyline representation format are built with precision 6 (``1e6``).
89
+ In order to get the polyline string transformed into another precision use this method passing the desired precision, which by default is 5 ``1e5``.
90
+ To know more about how polyline are built from coordinates:
91
+ https://developers.google.com/maps/documentation/utilities/polylinealgorithm
92
+
93
+
94
+ ``MapboxDirections::Step``:
95
+ - **distance**
96
+ - **duration**
97
+ - **way_name**
98
+ - **direction**
99
+ - **heading**
100
+ - **maneuver**(``MapboxDirections::Maneuver``)
101
+ - Methods:
102
+ - **lat**: Latitude of the maneuver point.
103
+ - **lng**: Longitude of the maneuver point.
104
+
105
+ ``MapboxDirections::Maneuver``:
106
+ - **type**
107
+ - **location**
108
+ - **instruction**
109
+ - Methods:
110
+ - **lat**: Latitude of the maneuver point.
111
+ - **lng**: Longitude of the maneuver point.
112
+
113
+
114
+ For more information about what the values of these attributes contain, please look at the [documentation](https://www.mapbox.com/developers/api/directions/).
115
+
116
+ ### Exceptions
117
+
118
+ Exceptions are triggered to give immediate feedback when there's something wrong in the parameters passed that won't allow to get a successful response.
119
+
120
+ - **``MapboxDirections::MissingAccessTokenError``**: When the access token is not passed in the parameters or its value is ``nil``
121
+
122
+
123
+ - **``MapboxDirections::InvalidAccessTokenError``**: When the access token is passed in the parameters but it's invalid
124
+
125
+ - **``MapboxDirections::CoordinatesFormatError``**: When the decimal coordinates are passed bad formatted given the requirements.
126
+
127
+
128
+ - **``MapboxDirections::UnsupportedTransportModeError``**: When the mode of transport is different that the ones that the service supports.
129
+
130
+
131
+ If the exception raise is an undesired behaviour in the context where the code is being executed, they can be rescued one by one or all at once follows:
132
+ ```ruby
133
+ require "mapbox_directions"
134
+
135
+ def directions(parameters)
136
+ MapboxDirections.directions(parameters)
137
+ rescue MapboxDirections::Error
138
+ # Handle exception
139
+ end
140
+ ```
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it ( https://github.com/[my-github-username]/mapbox_directions/fork )
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
147
+ 4. Push to the branch (`git push origin my-new-feature`)
148
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,12 @@
1
+ require "mapbox_directions/version"
2
+ require "mapbox_directions/parametizer"
3
+ require "mapbox_directions/response_parser"
4
+ require "mapbox_directions/client"
5
+
6
+ module MapboxDirections
7
+ module_function
8
+
9
+ def directions(options)
10
+ Client.new(options).directions
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ require "faraday"
2
+ require_relative "./parametizer"
3
+ require_relative "./model"
4
+ require_relative "./response_parser"
5
+
6
+ module MapboxDirections
7
+ class Client
8
+ BASE_URL = "https://api.tiles.mapbox.com"
9
+ PATH = "/v4/directions"
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ end
14
+
15
+ def directions
16
+ response = Faraday.get(directions_url, parametizer.params_hash)
17
+ parse_response(response)
18
+ end
19
+
20
+ private
21
+
22
+ def parse_response(response)
23
+ body = JSON.parse(response.body)
24
+
25
+ case response.status
26
+ when 200
27
+ ResponseParser.directions(body)
28
+ when 401
29
+ raise InvalidAccessTokenError
30
+ else
31
+ Response.new(message: body["message"], error: body["error"])
32
+ end
33
+ end
34
+
35
+ def directions_url
36
+ "#{BASE_URL}#{PATH}/#{parametizer.profile}/#{parametizer.waypoints}.json"
37
+ end
38
+
39
+ def parametizer
40
+ @parametizer ||= Parametizer.new(@options)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module MapboxDirections
2
+ class Error < StandardError; end
3
+ class CoordinatesFormatError < Error; end
4
+ class UnsupportedTransportModeError < Error; end
5
+ class MissingAccessTokenError < Error; end
6
+ class InvalidAccessTokenError < Error; end
7
+ end
@@ -0,0 +1,6 @@
1
+ require_relative "./model/point"
2
+ require_relative "./model/location"
3
+ require_relative "./model/maneuver"
4
+ require_relative "./model/step"
5
+ require_relative "./model/route"
6
+ require_relative "./model/response"
@@ -0,0 +1,19 @@
1
+ require 'forwardable'
2
+
3
+ module MapboxDirections
4
+ class Location
5
+ extend Forwardable
6
+ delegate %i(lat lng) => :@point
7
+
8
+ attr_reader :point, :name
9
+
10
+ def self.from_geojson(geojson)
11
+ new(point: Point.from_geojson(geojson), name: geojson["properties"]["name"])
12
+ end
13
+
14
+ def initialize(attrs)
15
+ @point = attrs[:point] || Point.new(attrs[:lat], attrs[:lng])
16
+ @name = attrs[:name]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module MapboxDirections
2
+ class Maneuver
3
+ extend Forwardable
4
+ delegate %i(lat lng) => :@location
5
+
6
+ attr_reader :type, :location, :instruction
7
+
8
+ def initialize(attrs)
9
+ @type = attrs[:type]
10
+ @location = attrs[:location]
11
+ @instruction = attrs[:instruction]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "./point"
2
+
3
+ module MapboxDirections
4
+ class Point
5
+ attr_reader :lat, :lng
6
+
7
+ def self.from_geojson(geojson)
8
+ coords = geojson["geometry"]["coordinates"]
9
+ new(coords.last, coords.first)
10
+ end
11
+
12
+ def initialize(lat, lng)
13
+ @lat = lat
14
+ @lng = lng
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require_relative "./route"
2
+
3
+ module MapboxDirections
4
+ class Response
5
+ attr_reader :origin, :destination, :waypoints, :routes, :message, :error
6
+
7
+ def initialize(attrs)
8
+ @origin = attrs[:origin]
9
+ @destination = attrs[:destination]
10
+ @waypoints = attrs[:waypoints] || []
11
+ @routes = attrs[:routes] || []
12
+ @message = attrs[:message]
13
+ @error = attrs[:error]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "./location"
2
+ require "polylines"
3
+
4
+ module MapboxDirections
5
+ class Route
6
+ POLYLINE_PRECISION = 1e6
7
+
8
+ attr_reader :distance, :duration, :summary, :geometry, :steps
9
+
10
+ def initialize(attrs)
11
+ @distance = attrs[:distance]
12
+ @duration = attrs[:duration]
13
+ @summary = attrs[:summary]
14
+ @geometry = attrs[:geometry]
15
+ @steps = attrs[:steps]
16
+ end
17
+
18
+ def transform_polyline_precision(precision = 1e5)
19
+ return @geometry unless @geometry.is_a?(String)
20
+ line_coordinates = Polylines::Decoder.decode_polyline(@geometry, POLYLINE_PRECISION)
21
+ Polylines::Encoder.encode_points(line_coordinates, precision)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "./maneuver"
2
+
3
+ module MapboxDirections
4
+ class Step
5
+ extend Forwardable
6
+ delegate %i(lat lng) => :@maneuver
7
+
8
+ attr_reader :distance, :duration, :way_name, :direction, :heading, :maneuver
9
+
10
+ def initialize(attrs)
11
+ @distance = attrs[:distance]
12
+ @duration = attrs[:duration]
13
+ @way_name = attrs[:way_name]
14
+ @direction = attrs[:direction]
15
+ @heading = attrs[:heading]
16
+ @maneuver = attrs[:maneuver]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ require_relative "./error"
2
+
3
+ module MapboxDirections
4
+ class Parametizer
5
+ ACCEPTED_PARAMS = %w(access_token geometry alternatives instructions)
6
+ TRANSPORT_MODES = %w(driving cycling walking)
7
+ ALLOWED_VALUES = {
8
+ geometry: %w(polyline geojson false),
9
+ alternatives: [true, false],
10
+ instructions: %w(text html)
11
+ }
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ end
16
+
17
+ def params_hash
18
+ raise MissingAccessTokenError unless filtered_params[:access_token]
19
+ filtered_params
20
+ end
21
+
22
+ def profile
23
+ raise UnsupportedTransportModeError unless TRANSPORT_MODES.include?(@options[:mode])
24
+ "mapbox.#{@options[:mode]}"
25
+ end
26
+
27
+ def waypoints
28
+ raise CoordinatesFormatError if invalid_waypoints?
29
+ "#{@options[:origin]};#{@options[:destination]}"
30
+ end
31
+
32
+ private
33
+
34
+ def filtered_params
35
+ @filtered_params ||= @options.select { |key, value| valid_key_value?(key, value) }
36
+ end
37
+
38
+ def invalid_waypoints?
39
+ invalid_coordinates?(@options[:origin]) || invalid_coordinates?(@options[:destination])
40
+ end
41
+
42
+ def invalid_coordinates?(coordinates)
43
+ /^(\-?\d*(\.\d+)?),(\-?\d*(\.\d+)?)$/.match(coordinates).nil?
44
+ end
45
+
46
+ def valid_key_value?(key, value)
47
+ key == :access_token || ACCEPTED_PARAMS.include?(key.to_s) && ALLOWED_VALUES[key].include?(value)
48
+ end
49
+ end
50
+ end