module SunbusConnect
Constants
- MIN_TRANSFER_SEC
Public Instance Methods
Source
# File lib/sunbus/connect.rb, line 98 def best_connecting_trip(data:, from_stop:, to_stop:, now_sec:, date:) best = nil transfer_stops(data, from_stop: from_stop, to_stop: to_stop).each do |transfer_stop| leg1 = best_leg_between( data: data, from_stop: from_stop, to_stop: transfer_stop, min_depart_sec: now_sec - 60, date: date ) next unless leg1 leg2 = best_leg_between( data: data, from_stop: transfer_stop, to_stop: to_stop, min_depart_sec: leg1[:arrive_sec] + MIN_TRANSFER_SEC, date: date ) next unless leg2 candidate = build_connection( from_stop: from_stop, to_stop: to_stop, transfer_stop: transfer_stop, leg1: leg1, leg2: leg2 ) best = candidate if best.nil? || candidate[:depart_sec] < best[:depart_sec] end best end
Source
# File lib/sunbus/connect.rb, line 88 def best_direct_trip(data:, from_stop:, to_stop:, now_sec:, date:) best_leg_between( data: data, from_stop: from_stop, to_stop: to_stop, min_depart_sec: now_sec - 60, date: date ) end
Source
# File lib/sunbus/connect.rb, line 133 def best_leg_between(data:, from_stop:, to_stop:, min_depart_sec:, date:) best = nil data[:trips].each_value do |trip| next unless SunbusGtfs.service_active?(trip[:service_id], date, data) times = data[:stop_times][trip[:trip_id]] from_row = times.find { |row| row[:stop_id] == from_stop[:stop_id] } to_row = times.find { |row| row[:stop_id] == to_stop[:stop_id] } next unless from_row && to_row next unless from_row[:stop_sequence] < to_row[:stop_sequence] depart_sec = SunbusGtfs.parse_gtfs_clock(from_row[:departure_time]) arrive_sec = SunbusGtfs.parse_gtfs_clock(to_row[:arrival_time]) next if depart_sec < min_depart_sec route = data[:routes][trip[:route_id]] || {} candidate = { from_stop: from_stop, to_stop: to_stop, route_short_name: route[:route_short_name].to_s, route_long_name: route[:route_long_name].to_s, headsign: trip[:headsign], depart: SunbusGtfs.format_gtfs_clock(depart_sec), arrive: SunbusGtfs.format_gtfs_clock(arrive_sec), duration_min: ((arrive_sec - depart_sec) / 60.0).round, depart_sec: depart_sec, arrive_sec: arrive_sec } best = candidate if best.nil? || candidate[:depart_sec] < best[:depart_sec] end best end
Source
# File lib/sunbus/connect.rb, line 168 def build_connection(from_stop:, to_stop:, transfer_stop:, leg1:, leg2:) wait_min = ((leg2[:depart_sec] - leg1[:arrive_sec]) / 60.0).round { from_stop: from_stop, to_stop: to_stop, depart: leg1[:depart], arrive: leg2[:arrive], duration_min: ((leg2[:arrive_sec] - leg1[:depart_sec]) / 60.0).round, depart_sec: leg1[:depart_sec], legs: [leg1, leg2], transfer_stop: { stop_id: transfer_stop[:stop_id], name: transfer_stop[:name], arrive: leg1[:arrive], depart: leg2[:depart], wait_min: wait_min } } end
Source
# File lib/sunbus/connect.rb, line 18 def plan(from:, to:, region: SunbusConfig::DEFAULT_REGION, fetch: nil, time: Time.now) from_stop = SunbusGtfs.find_stop(from, region: region, fetch: fetch) return { error: "Origin stop not found — try !sunbus stop #{from}" } unless from_stop to_stop = SunbusGtfs.find_stop(to, region: region, fetch: fetch) return { error: "Destination stop not found — try !sunbus stop #{to}" } unless to_stop SunbusDayLookup.each_service_day(time) do |lookup_time, day_offset| data = SunbusGtfs.feed(region: region, fetch: fetch, time: lookup_time) date = SunbusGtfs.local_date(lookup_time) now_sec = (date.hour * 3600) + (date.min * 60) + date.sec direct = best_direct_trip( data: data, from_stop: from_stop, to_stop: to_stop, now_sec: now_sec, date: date ) if direct return direct.merge( connecting: false, service_date: date, day_offset: day_offset ) end connecting = best_connecting_trip( data: data, from_stop: from_stop, to_stop: to_stop, now_sec: now_sec, date: date ) next unless connecting return connecting.merge( connecting: true, service_date: date, day_offset: day_offset ) end { error: "No scheduled trip found from #{from_stop[:name]} to #{to_stop[:name]} in the next " \ "#{SunbusDayLookup::MAX_ADVANCE_DAYS + 1} days." } end
Source
# File lib/sunbus/connect.rb, line 67 def transfer_stops(data, from_stop:, to_stop:) reachable_from = Set.new can_reach_to = Set.new data[:trips].each_value do |trip| times = data[:stop_times][trip[:trip_id]] from_idx = times.index { |row| row[:stop_id] == from_stop[:stop_id] } to_idx = times.index { |row| row[:stop_id] == to_stop[:stop_id] } times[(from_idx + 1)..].each { |row| reachable_from << row[:stop_id] } if from_idx times[0...to_idx].each { |row| can_reach_to << row[:stop_id] } if to_idx end stop_lookup = data[:stops].each_with_object({}) { |row, hash| hash[row[:stop_id]] = row } (reachable_from & can_reach_to).filter_map do |stop_id| next if stop_id == from_stop[:stop_id] || stop_id == to_stop[:stop_id] stop_lookup[stop_id] end end