traveltime-api 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/traveltime_api.rb +264 -0
  2. metadata +79 -0
@@ -0,0 +1,264 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ # Class for using TravelTime API service. Learn more about TravelTime at
5
+ # www.traveltimeapp.com
6
+ #
7
+ # ## Defined types ##
8
+ #
9
+ # * TransportationMode - string with one of following values:
10
+ # "walking", "driving", "walking_train", "walking_bus", "public_transport"
11
+ #
12
+ # * EdgeType - describes type of the edge. String with one of following values:
13
+ # "car", "walk", "train", "bus", "cable_car", "plane", "ship"
14
+ #
15
+ # ## Thrown errors ##
16
+ #
17
+ # Each method that does a call to the server can raise TravelTimeAPI::BadRequest
18
+ # or TravelTimeAPI::ServerError errors.
19
+ #
20
+ class TravelTimeAPI
21
+ DEFAULT_URL = "http://api.traveltimeapp.com"
22
+ OUT_OF_REACH_CODE = "out-of-reach"
23
+ NO_DATA_CODE = "no-data"
24
+
25
+ # Class representing authentification data for TravelTime API.
26
+ class Auth < Struct.new(:app_id, :app_key)
27
+ def to_h
28
+ {:app_id => app_id, :app_key => app_key}
29
+ end
30
+ end
31
+
32
+ # Raised if API user made bad request.
33
+ class BadRequest < RuntimeError
34
+ # Server provided String code for this error.
35
+ attr_reader :code
36
+ # Server provided String explanation of error.
37
+ attr_reader :details
38
+
39
+ def initialize(code, details)
40
+ @code = code
41
+ @details = details
42
+ end
43
+
44
+ def to_s
45
+ "TravelTimeAPI::BadRequest[#{@code}: #{details}]"
46
+ end
47
+ end
48
+
49
+ # Raised if API server has encountered an error.
50
+ class ServerError < RuntimeError
51
+ # Human readable error message.
52
+ attr_reader :error
53
+
54
+ def initialize(error)
55
+ @error = error
56
+ end
57
+
58
+ def to_s
59
+ "TravelTimeAPI::ServerError[#{@error}]"
60
+ end
61
+ end
62
+
63
+ # Represents that API does has a dataset for this point/region but cannot
64
+ # calculate any useful data from this point.
65
+ class OutOfReach < RuntimeError; end
66
+
67
+ # Represents that API does not have data for this point/region.
68
+ class NoData < RuntimeError; end
69
+
70
+ # Result of #time_filter method for one mode.
71
+ #
72
+ # Properties is a Hash that can have (if you requested them) following pairs:
73
+ # * "time" => Fixnum - time from origin to point in seconds.
74
+ # * "distance" => Fixnum - distance from origin to point in meters.
75
+ #
76
+ # @param points [Hash] {"id1" => Properties, ...}
77
+ # @param accuracy [Symbol] :exact or :approx. Decrease travel time to switch
78
+ # from approximate to exact accuracy.
79
+ class TimeFilterResult < Struct.new(:points, :accuracy); end
80
+
81
+ # Result of #time_map method.
82
+ #
83
+ # @param shape [Array] array of polylines that represent areas reachable by given time.
84
+ # [Cluster, Cluster, ...], where Cluster is [[lat, lon], [lat, lon], ...]
85
+ # @param accuracy [Symbol] :exact or :approx. Decrease travel time to switch
86
+ # from approximate to exact accuracy.
87
+ class TimeMapResult < Struct.new(:shape, :accuracy); end
88
+
89
+ # Result of #routes method.
90
+ #
91
+ # * RouteDescriptionPart is a structure describing one part of the route:
92
+ # {
93
+ # "mode": EdgeType,
94
+ # # Array of coordinates for this route part. Can be used to draw a polyline
95
+ # # on a map.
96
+ # "coords": [[lat, lon], [lat, lon], [lat, lon], ...],
97
+ # "directions: String, # textual description in english of this route part
98
+ # "distance": Int, # distance covered in metres for this route part
99
+ # "time": Int # time taken to travel through this route part in seconds
100
+ # }
101
+ #
102
+ # @param routes [Hash] {"id1" -> [RouteDescriptionPart, RouteDescriptionPart, ...], ...}
103
+ class RoutesResult < Struct.new(:routes); end
104
+
105
+ # Initializes API. You need to specify your auth here.
106
+ #
107
+ # Example:
108
+ # api = TravelTimeAPI.new(TravelTimeAPI::Auth.new(app_id, app_key))
109
+ #
110
+ def initialize(auth, url=DEFAULT_URL)
111
+ @auth = auth
112
+ @url = url
113
+ end
114
+
115
+ # Given latitude and longtitude of a point return whether TravelTime has
116
+ # data for those coordinates or not.
117
+ #
118
+ # WARNING: even though ending in ? this method does not return a boolean
119
+ # value and you cannot use it directly in boolean expressions!
120
+ #
121
+ # This is due to the fact that is needs to return 3 possible outcomes:
122
+ # - :data - we have data for this point
123
+ # - :out_of_reach - we have a data set for this point but we couldn't attach
124
+ # that point exactly to any significant roads or routes.
125
+ # - :no_data - we do not have any data for this point.
126
+ #
127
+ # @param lat [Float]
128
+ # @param lng [Float]
129
+ # @return [Symbol]
130
+ def has_data?(lat, lng)
131
+ request = {:coords => [lat, lng]}
132
+ response = raw_post("/has_data", request)
133
+ if response["has"] == true
134
+ :data
135
+ elsif response["code"] == OUT_OF_REACH_CODE
136
+ :out_of_reach
137
+ elsif response["code"] == NO_DATA_CODE
138
+ :no_data
139
+ else
140
+ json_error(response)
141
+ end
142
+ end
143
+
144
+ # Takes input parameters and returns how long does it take to reach each point
145
+ # in chosen mode of transportation (in seconds).
146
+ #
147
+ # Points whose travel times which exceed _travel_time_ are not included in the
148
+ # result.
149
+ #
150
+ # @param start_time [Time] Time moment when we start our search.
151
+ # @param travel_time [Int] Max time in seconds to the point.
152
+ # @param modes [Array] Array of transportation modes you want calculated.
153
+ # See class documentation for transportation mode info.
154
+ # @param origin [Array] [latitude, longtitude]
155
+ # @param points [Hash] Points th{"id1" => [latitude, longtitude], ...}
156
+ # @param properties [Array] array of requested point properties. Valid values: :time,
157
+ # :distance. :distance cannot be used with any mode that
158
+ # involves public transport as the results will be 0.
159
+ # @return [Hash] {mode => TravelTimeAPI::TimeFilterResult, ...}
160
+ def time_filter(start_time, travel_time, modes, origin, points, properties=[:time])
161
+ modes = Array(modes)
162
+ request = {
163
+ :start_time => format_time(start_time),
164
+ :travel_time => travel_time,
165
+ :modes => modes,
166
+ :origin => origin,
167
+ :points => points,
168
+ :properties => properties
169
+ }
170
+
171
+ response = post("/v2/time_filter", request)
172
+ response.inject({}) do |hash, (mode, ranked)|
173
+ hash[mode] = TimeFilterResult.new(ranked["times"], ranked["accuracy"].to_sym)
174
+ hash
175
+ end
176
+ end
177
+
178
+ # Takes input parameters and returns polylines for each cluster that you can
179
+ # reach by given time.
180
+ #
181
+ # @param start_time [Time] Time moment when we start our search.
182
+ # @param travel_time [Int] Max time in seconds to the point.
183
+ # @param mode [String] Transportation mode. See class documentation for info.
184
+ # @param origin [Array] [latitude, longtitude]
185
+ # @param smooth [Boolean] Shall the returned shaped be smoothed?
186
+ # @param max_points [Fixnum] Maximum number of points in returned polygons. nil
187
+ # for no limit, Fixnum for limit. Minimum of 4 points required!
188
+ # @return [TravelTimeAPI::TimeMapResult]
189
+ def time_map(start_time, travel_time, mode, origin, smooth, max_points=nil)
190
+ raise ArgumentError.new("At least 4 points are required in max_points!") \
191
+ unless max_points.nil? || max_points >= 4
192
+
193
+ request = {
194
+ :start_time => format_time(start_time),
195
+ :travel_time => travel_time,
196
+ :mode => mode,
197
+ :origin => origin,
198
+ :smooth => smooth,
199
+ :max_points => max_points
200
+ }
201
+
202
+ response = post("/v2/time_map", request)
203
+ TimeMapResult.new(response["shape"], response["accuracy"].to_sym)
204
+ end
205
+
206
+ # Takes input parameters and returns routes to each of the points within
207
+ # travel_time.
208
+ #
209
+ # Input parameters are the same as #travel_time.
210
+ # @return [TravelTimeAPI::RoutesResult]
211
+ def routes(start_time, travel_time, mode, origin, points)
212
+ request = {
213
+ :start_time => format_time(start_time),
214
+ :travel_time => travel_time,
215
+ :mode => mode,
216
+ :origin => origin,
217
+ :points => points
218
+ }
219
+
220
+ response = post("/v2/routes", request)
221
+ RoutesResult.new(response)
222
+ end
223
+
224
+ private
225
+
226
+ def format_time(time)
227
+ time.strftime("%FT%T%:z")
228
+ end
229
+
230
+ def raw_post(action, request)
231
+ response = RestClient.post(
232
+ @url + action,
233
+ request.merge(@auth.to_h).to_json,
234
+ :content_type => :json
235
+ )
236
+ JSON.parse(response)
237
+ rescue RestClient::ExceptionWithResponse => e
238
+ raise ServerError.new(
239
+ "server error, http_status: #{e.http_code}, http_body: #{e.http_body}"
240
+ )
241
+ rescue JSON::ParserError => e
242
+ raise ServerError.new("Cannot parse server response as JSON: #{response}")
243
+ end
244
+
245
+ def post(action, request)
246
+ json = raw_post(action, request)
247
+
248
+ case json["code"]
249
+ when OUT_OF_REACH_CODE
250
+ raise OutOfReach
251
+ when NO_DATA_CODE
252
+ raise NoData
253
+ when nil
254
+ json
255
+ else
256
+ json_error(json)
257
+ end
258
+ end
259
+
260
+ def json_error(json)
261
+ raise ServerError.new("code: #{json["code"]}, details: #{json["details"]}")
262
+ end
263
+
264
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: traveltime-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Artūras Šlajus
9
+ - IGeolise
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rest-client
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 1.6.7
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 1.6.7
31
+ - !ruby/object:Gem::Dependency
32
+ name: json
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 1.7.0
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.7.0
47
+ description: ''
48
+ email:
49
+ - arturas@igeolise.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - lib/traveltime_api.rb
55
+ homepage: http://traveltimeapp.com
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.23
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Simple wrapper for TravelTime REST API.
79
+ test_files: []