mapbox_directions 0.1.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.
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