mybustracker 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e485184e0c61f0acea90948b5a905cfac6985f00
4
- data.tar.gz: '06593b9b4fa21e9d7d2f1f5c49b71aa413f6168f'
3
+ metadata.gz: 54a03b29fb608175ede8ece1d2edd85a90fdf3df
4
+ data.tar.gz: b9fee2a737759b20a6c0b830db3f0e5e40329f08
5
5
  SHA512:
6
- metadata.gz: 3d4f85c5063bc643bd1c27c8f7b0f8a1af0fcd8a3fe2699ae334a6b9cb2ca67af17505d3a95600b6205066a73924cc6eae13242d3ebdf9358c487fd8ef9c783b
7
- data.tar.gz: 2092051f1aa751d9b945e70512ae248e81a1787ad7b5f400be5c207987558e0848c2616d5653e957f773e53119886851b1037009a6024917ee7feb1e5b66fef7
6
+ metadata.gz: 416ab8ca11c80f68f38d38ac40a8b8a03d6149988d4737612ff892405db1df1220d4fc7000f86e4c7892d7bb5ec8b0189cfa2f588f003507cc52873aaed7397b
7
+ data.tar.gz: df8b09df88105badb3f94c01593b0b9a058997ffc819181ada379935ef8791b422fcbf2dab481bdc3630739dca2395b39500378b64e4b5dfdcf4f0f779dd5c13
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -4,11 +4,194 @@
4
4
 
5
5
  require 'savon'
6
6
  require 'digest'
7
+ require 'subunit'
8
+ require 'geocoder'
9
+ require 'geodesic'
10
+
7
11
 
8
12
  class MyBusTrackerException < Exception
13
+
14
+ class Service < Exception
15
+ end
16
+
9
17
  end
10
18
 
19
+
11
20
  class MyBusTracker
21
+
22
+ class Service
23
+
24
+ attr_reader :ref, :operator, :number, :name, :bus_stops
25
+
26
+ def initialize(client, appkey, service, all_dests,
27
+ all_bus_stops, number='')
28
+
29
+ if number.empty? then
30
+ raise MyBusTrackerException::Service, 'provide a bus service number'
31
+ end
32
+
33
+ @client, @appkey = client, appkey
34
+
35
+ # find out the reference number from the service hash object
36
+
37
+ #e.g. => {:ref=>"7", :operator_id=>"LB", :mnemo=>"4", :name=>"The Jewel
38
+ #-- Hillend", :type=>nil, :dests=>{:list=>["458752", ...]}
39
+
40
+ @number, @name, @ref, relative_dests, operator = \
41
+ %i(mnemo name ref dests operator).map {|field| service[field]}
42
+
43
+ @all_bus_stops = all_bus_stops
44
+
45
+
46
+ end
47
+
48
+ # accepts a bus service number and returns the relative bus times
49
+ #
50
+ def query(times: 'next hour', from: nil, to: nil)
51
+
52
+ #from ||=
53
+
54
+ start_bus_stop1, start_bus_stop2 = find_bus_stop(from)
55
+ end_bus_stop1, end_bus_stop2 = find_bus_stop(to)
56
+
57
+ # get the bus times
58
+
59
+ @bus_stops, journey_times = get_stop_journeys(start_bus_stop1)
60
+
61
+ # select the bus stop end
62
+
63
+ end_stop = journey_times.find do |x|
64
+ x[:stop_id] == end_bus_stop1[:stop_id] or x[:stop_id] == end_bus_stop2[:stop_id]
65
+ end
66
+
67
+ if end_stop then
68
+ start_bus_stop = start_bus_stop1
69
+ else
70
+ start_bus_stop = start_bus_stop2
71
+ @bus_stops, journey_times = get_stop_journeys(start_bus_stop2) unless end_stop
72
+ return unless @bus_stops
73
+ # select the bus stop end
74
+
75
+ end_stop = journey_times.find do |x|
76
+ (x[:stop_id] == end_bus_stop1[:stop_id]) or (x[:stop_id] == end_bus_stop2[:stop_id])
77
+ end
78
+
79
+ end
80
+
81
+ tstart = Time.strptime(journey_times[0][:time],"%H:%M")
82
+ tend = Time.strptime(end_stop[:time],"%H:%M")
83
+ travel_time = '~' + Subunit.new(units={minutes:60, hours:60}, \
84
+ seconds: tend - tstart).to_s(omit: [:seconds])
85
+
86
+ {
87
+ from: {bus_stop: start_bus_stop[:name], bus_stop_id: start_bus_stop[:stop_id]},
88
+ to: {bus_stop: end_stop[:stop_name], bus_stop_id: end_stop[:stop_id]},
89
+ start: journey_times[0][:time],
90
+ end: end_stop[:time],
91
+ travel_time: travel_time
92
+ }
93
+
94
+ end
95
+
96
+ private
97
+
98
+ def find_bus_stop(address)
99
+
100
+ results = Geocoder.search(address.sub(/,? *edinburgh$/i,'') \
101
+ + ", Edinburgh")
102
+
103
+ p1 = Geodesic::Position.new(*results[0].coordinates)
104
+
105
+ a = @all_bus_stops.sort_by do |h|
106
+
107
+ x, y = %i(x y).map {|fields| h[fields].to_f.round(4)}
108
+
109
+ p2 = Geodesic::Position.new(x, y)
110
+ d = Geodesic::dist_haversine(p1.lat, p1.lon, p2.lat, p2.lon).round(4)
111
+ end
112
+ a.take 2
113
+
114
+ end
115
+
116
+ def get_stop_journeys(start_bus_stop)
117
+
118
+ response = @client.call(:get_bus_times, message: { key: @appkey, time_requests: {list: [{stop_id: start_bus_stop[:stop_id] }]} })
119
+
120
+ bus_times = response.body[:bus_times_response][:bus_times][:list].find do |bus_time|
121
+ bus_time[:ref_service] == @ref
122
+ end
123
+
124
+ #e.g.r.keys => [:operator_id, :stop_id, :stop_name, :ref_service, :mnemo_service, :name_service,
125
+ # :time_datas, :global_disruption, :service_disruption, :bus_stop_disruption, :service_diversion]
126
+
127
+ return unless bus_times
128
+
129
+ # get the 1st journey id
130
+ list = bus_times[:time_datas][:list]
131
+ journey_id = list.is_a?(Array) ? list[0][:journey_id] : list[:journey_id]
132
+
133
+ response = @client.call(:get_journey_times, message: { key: @appkey, journey_id: journey_id, stop_id: start_bus_stop[:stop_id] })
134
+ journey = response.body[:journey_times_response][:journey_times][:list]
135
+ #=> [:journey_id, :bus_id, :operator_id, :ref_service, :mnemo_service, :name_service, :ref_dest, :name_dest, :terminus, :journey_time_datas, :global_disruption, :service_disruption, :service_diversion]
136
+
137
+ journey_times = journey[:journey_time_datas][:list]
138
+ # get the bus stops
139
+ [journey_times.inject({}) {|r,x| r.merge(x[:stop_name] => {id: x[:stop_id]}) }, journey_times]
140
+ end
141
+
142
+ # method no longer used
143
+ #
144
+ def fetch_bus_stops2()
145
+ # find the outbound destination
146
+ dest = relative_dests[:list].find do |ref|
147
+ all_dests.find {|x| x[:ref] == ref and x[:direction] == 'R' }
148
+ end
149
+
150
+ # go through each bus stop to find the dests
151
+
152
+ raw_bus_stops = all_bus_stops.select do |bus_stop|
153
+ bus_stop[:dests][:list].include? dest
154
+ end
155
+
156
+ c = ->(coord){ coord.to_f.round(4)}
157
+
158
+ response = client.call(:get_service_points, message: \
159
+ { key: appkey, ref: ref })
160
+ all_service_points = response.body[:service_points_response]\
161
+ [:service_points][:list]
162
+
163
+ # select only the 1st chain
164
+ #service_points = all_service_points#.group_by {|x| x[:chainage]}.first[-1]
165
+ #puts 'service_points: ' + service_points.inspect
166
+
167
+ unsorted_bus_stops = []
168
+ raw_bus_stops.each do |bus_stop|
169
+
170
+ x, y = %i(x y).map {|field| bus_stop[field]}.map(&c)
171
+
172
+ r = all_service_points.select do |point|
173
+ [point[:x], point[:y]].map(&c) == [x, y]
174
+ end
175
+
176
+ r.each do |x|
177
+ unsorted_bus_stops << [bus_stop[:name], bus_stop[:stop_id], x, x[:order].to_i ]
178
+ end
179
+
180
+ end
181
+
182
+
183
+ #@bus_stops = unsorted_bus_stops.compact.sort_by(&:last)
184
+ #=begin
185
+ # find the correct chainage by the most records
186
+ h = unsorted_bus_stops.group_by {|x| x[2][:chainage]}
187
+ a = h.map {|k,v| v.count {|x| x[2][:chainage] == k}}
188
+
189
+ @bus_stops = h.to_a[a.index(a.max)].last\
190
+ .sort_by(&:last).inject({}){|r, x| r.merge(x[0] => {id: x[1]}) }
191
+ #=end
192
+ end
193
+
194
+ end
12
195
 
13
196
 
14
197
  def initialize(api_key: '')
@@ -16,7 +199,7 @@ class MyBusTracker
16
199
 
17
200
  raise MyBusTrackerException, 'api_key missing' if api_key.empty?
18
201
 
19
- @client = Savon.client(wsdl: 'http://ws.mybustracker.co.uk/?wsdl')
202
+ @client = client = Savon.client(wsdl: 'http://ws.mybustracker.co.uk/?wsdl')
20
203
 
21
204
  #@client.operations
22
205
 
@@ -24,23 +207,37 @@ class MyBusTracker
24
207
  # :get_bus_stops, :get_bus_times, :get_journey_times, :get_disruptions,
25
208
  # :get_diversions, :get_diversion_points]
26
209
 
27
- @appkey = Digest::MD5.hexdigest( api_key + Time.now.strftime("%Y%m%d%H"))
210
+ @appkey = appkey = Digest::MD5.hexdigest( api_key +
211
+ Time.now.strftime("%Y%m%d%H"))
212
+
213
+ response = client.call(:get_services, message: { key: appkey })
214
+ @all_services= response.body[:services_response][:services][:list]
215
+
216
+ response = client.call(:get_dests, message: { key: appkey })
217
+ @all_dests = response.body[:dests_response][:dests][:list]
218
+
219
+ response = client.call(:get_bus_stops, message: { key: appkey })
220
+ @all_bus_stops = response.body[:bus_stops_response][:bus_stops][:list]
28
221
 
29
222
 
30
223
  end
31
224
 
225
+ # returns the number and name of all bus services
226
+ #
32
227
  def services()
33
228
 
34
- # call the get_services operation
35
- response = @client.call(:get_services, message: { key: @appkey })
36
- r = response.body
37
-
38
- a = r[:services_response][:services][:list].map do |x|
39
- [x[:mnemo], x[:name]]
40
- end
41
-
42
- a.to_h
229
+ @all_services.map {|x| [x[:mnemo], x[:name]] }.to_h
43
230
 
44
231
  end
232
+
233
+ # accepts a bus service number and returns the relative bus times
234
+ #
235
+ def service(number='')
236
+
237
+ service = @all_services.find {|x| x[:mnemo] == number }
238
+ Service.new @client, @appkey, service, @all_dests, @all_bus_stops, number
239
+
240
+ end
241
+
242
+
45
243
  end
46
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mybustracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Robertson
@@ -53,6 +53,66 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: 2.11.2
56
+ - !ruby/object:Gem::Dependency
57
+ name: subunit
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.2'
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 0.2.7
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '0.2'
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.7
76
+ - !ruby/object:Gem::Dependency
77
+ name: geocoder
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 1.4.4
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '1.4'
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 1.4.4
96
+ - !ruby/object:Gem::Dependency
97
+ name: geodesic
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.0'
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 1.0.1
106
+ type: :runtime
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '1.0'
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 1.0.1
56
116
  description:
57
117
  email: james@jamesrobertson.eu
58
118
  executables: []
@@ -83,6 +143,6 @@ rubyforge_project:
83
143
  rubygems_version: 2.6.8
84
144
  signing_key:
85
145
  specification_version: 4
86
- summary: "*Currently under development*. An unoficial gem to query the mybustracker.co.uk
146
+ summary: "*Currently under development*. An unofficial gem to query the mybustracker.co.uk
87
147
  web service for bus times etc. #edinburgh"
88
148
  test_files: []
metadata.gz.sig CHANGED
Binary file