| 1 | # Ruby API towards Trafikanten, with a builtin web server |
| 2 | # Run this file and point your browser to http://localhost:4567/. Enter the name of a station, select, and view realtime departures |
| 3 | # from the station |
| 4 | %w(rubygems open-uri cgi hpricot yaml).each{|dep|require dep} |
| 5 | # Seems easier |
| 6 | class String |
| 7 | def url_encode;CGI.escape(self);end |
| 8 | def url_decode;CGI.unescape(self);end |
| 9 | end |
| 10 | |
| 11 | class Station |
| 12 | attr_accessor :station_id, :name, :district, :x, :y |
| 13 | def initialize(options={}) |
| 14 | options.each{|k,v|send("#{k}=",v)} |
| 15 | end |
| 16 | |
| 17 | def self.find_by_name(a_name) |
| 18 | uri = "http://www8.trafikanten.no/txml?type=1&stopname=#{a_name.url_encode}" |
| 19 | data = open(uri).read |
| 20 | result = [] |
| 21 | doc = Hpricot(data) |
| 22 | (doc/"//stopmatch").each do |match| |
| 23 | id = (match/"fromid").inner_html.to_i |
| 24 | name = (match/"stopname").inner_html |
| 25 | district = (match/"district").inner_html |
| 26 | x = (match/"xcoordinate").inner_html |
| 27 | y = (match/"ycoordinate").inner_html |
| 28 | result << new(:x => x, :y => y, :station_id => id, :name => name, :district => district) |
| 29 | end |
| 30 | result |
| 31 | end |
| 32 | |
| 33 | # There seem to be different data sources for subway stations and the other stations. |
| 34 | def subway_station? |
| 35 | ["03011930","03012220","03011030","03012365","02190070","02190080","02190090","03010011","03010020","03010031","03010200","03010360","03010370","03010600","03010610","03010770","03010780","03010950","03011010","03011020","03011110","03011120","03011130","03011140","03011200","03011210","03011220","03011310","03011320","03011330","03011400","03011410","03011430","03011440","03011450","03011510","03011520","03011530","03011540","03011610","03011620","03011630","03011710","03011720","03011730","03011810","03011910","03011920","03011940","03012000","03012010","03012020","03012030","03012040","03012100","03012120","03012130","03012210","03012230","03012240","03012260","03012270","03012280","03012305","03012310","03012315","03012320","03012325","03012330","03012340","03012345","03012350","03012355","03012360","03012370","03012375","03012380","03012385","03012390","03012410","03012420","03012430","03012450","03012460","03012560","03012565","03012572","03012630"].include?(station_id.to_s) |
| 36 | end |
| 37 | |
| 38 | def realtime_url |
| 39 | subway_station? ? "http://www.sis.trafikanten.no:8088/xmlrtpi/dis/request?DISID=SN$#{station_id}" : "http://www.sis.trafikanten.no/xmlrtpi/dis/request?DISID=SN$#{station_id}" |
| 40 | end |
| 41 | |
| 42 | def realtime_departures |
| 43 | result = [] |
| 44 | doc = Hpricot(open(realtime_url).read) |
| 45 | (doc/"disdeviation").each do |m| |
| 46 | trip_status = (m/"trip_status").inner_html |
| 47 | departure_field = (trip_status == "real") ? "expecteddisdeparturetime" : "scheduleddisdeparturetime" |
| 48 | scheduled_departure = (m/departure_field).inner_html |
| 49 | destination = (m/"destinationstop").inner_html |
| 50 | line = (m/"lineid").inner_html |
| 51 | result << Departure.new(:status => trip_status, :departure => Time.parse(scheduled_departure), :destination => destination, :line => line, :from => name) |
| 52 | end |
| 53 | return result |
| 54 | end |
| 55 | |
| 56 | end |
| 57 | |
| 58 | class Departure |
| 59 | attr_accessor :status, :departure, :destination, :line, :from |
| 60 | def initialize(options={}) |
| 61 | options.each{|k,v|send("#{k}=",v)} |
| 62 | end |
| 63 | |
| 64 | def to_s |
| 65 | "Linje #{line} fra #{from} til #{destination} klokka #{departure.strftime('%H:%M (%d/%m)')}" |
| 66 | end |
| 67 | end |
| 68 | |
| 69 | # Just to play nice, let's embed a webserver |
| 70 | # if __FILE__ == $0 |
| 71 | require 'sinatra' |
| 72 | |
| 73 | get '/' do |
| 74 | '<h1>Velg stoppested</h1><form method="get" action="/locations"><input name="q" type="text" /><input type="submit"/></form>' |
| 75 | end |
| 76 | |
| 77 | |
| 78 | get '/locations' do |
| 79 | result = [] |
| 80 | result << '<h1>Velg stasjon</h1><ul>' |
| 81 | matches = Station.find_by_name(params[:q]) |
| 82 | matches.each do |m| |
| 83 | result << "<li><a href='/departures/#{m.station_id}/#{m.name}'>#{m.name}</a></li>" |
| 84 | end |
| 85 | result << "</ul>" |
| 86 | result.join("\n") |
| 87 | end |
| 88 | |
| 89 | get '/departures/:station_id/:station_name' do |
| 90 | result = [] |
| 91 | station = Station.new(:station_id => params[:station_id], :name => params[:station_name].url_decode) |
| 92 | result << "<h1>Avganger fra #{station.name}</h1>" |
| 93 | result << "<ul>" |
| 94 | station.realtime_departures.sort{|a,b|a.departure<=>b.departure}.each do |dep| |
| 95 | result << "<li>#{dep.to_s}</li>" |
| 96 | end |
| 97 | result << "</ul>" |
| 98 | result.join("\n") |
| 99 | end |
| 100 | # end |

