Network 10 changes
Network 10 changes

require 'rubygems' require 'rubygems'
require 'nokogiri' require 'nokogiri'
require 'open-uri' require 'open-uri'
require 'pp' require 'pp'
require 'yaml' require 'yaml'
class Array class Array
def to_yaml_style def to_yaml_style
:inline :inline
end end
end end
   
   
def makeTimetable(table, period, short_name) def makeTimetable(table, period, short_name)
timetable = {"between_stops" => [], "short_name" => short_name} timetable = {"between_stops" => [], "short_name" => short_name}
time_points = table.xpath('tr[1]//th').map do |tp| time_points = table.xpath('tr[1]//th').map do |tp|
if tp.content != "\302\240" && tp.content != "" && tp.content != "<br/>" if tp.content != "\302\240" && tp.content != "" && tp.content != "<br/>"
timing_point = tp.content.squeeze(" ").gsub(" Platform"," (Platform").gsub(" - "," - ").gsub("\n","").gsub("\r","").gsub("\\"," / ").squeeze(" ").strip timing_point = tp.content.squeeze(" ").gsub("Bus Station"," Bus Station ").gsub(" Platform"," (Platform").gsub(" - "," - ").gsub("\n"," ").gsub("\r"," ").gsub("\t"," ").gsub("\\"," / ").gsub("/"," / ").gsub(",",", ").gsub("\302\240","").squeeze(" ").strip
if (tp.content.match('Platform')) if (tp.content.match('Platform'))
timing_point.concat(")") timing_point.concat(")")
end; end;
timing_point timing_point
end end
end end
time_points.delete(nil) time_points.delete(nil)
  time_points.delete("WheelchairAccessible")
  time_points.delete("Wheelchair Accessible")
timetable["time_points"] = time_points.to_a timetable["time_points"] = time_points.to_a
timetable["long_name"] = "To " + time_points.last timetable["long_name"] = "To " + time_points.last
periodtimes = [] periodtimes = []
table.css('tr').each do |row| table.css('tr').each do |row|
times = row.css('td').map do |cell| times = row.css('td').map do |cell|
time = cell.content.squeeze(" ").strip time = cell.content.squeeze(" ").strip
time = time.gsub(/ *A\S?M/,"a").gsub(/ ?P\S?M/,"p").gsub("12:08 AM","1208x").gsub(":","").gsub("1.","1").gsub("2.","2") time = time.gsub(/ *A\S?M/,"a").gsub(/ ?P\S?M/,"p").gsub("12:08 AM","1208x").gsub(":","").gsub("1.","1").gsub("2.","2")
time = time.gsub("3.","3").gsub("4.","4") time = time.gsub("3.","3").gsub("4.","4")
time = time.gsub("5.","5").gsub("6.","6").gsub("7.","7").gsub("8.","8").gsub("9.","9").gsub("10.","10") time = time.gsub("5.","5").gsub("6.","6").gsub("7.","7").gsub("8.","8").gsub("9.","9").gsub("10.","10")
time = time.gsub("11.","11").gsub("12.","12").gsub(/\.+/,"-") time = time.gsub("11.","11").gsub("12.","12").gsub(/\.+/,"-").gsub("\302\240","")
  if time == "" then time = nil end
  time
end end
  times.delete(nil)
if not times.empty? if not times.empty?
if not (route = times.shift) if not (route = times.shift)
raise("TODO: account for shifting route numbers eg. intertown/redex 62/162") raise("TODO: account for shifting route numbers eg. intertown/redex 62/162")
end end
periodtimes << times.to_a periodtimes << times.to_a
end end
end end
if periodtimes.size < 1 if periodtimes.size < 1
raise "No times for route " + short_name + " in period " + period raise "No times for route " + short_name + " in period " + period
end end
timetable[period] = periodtimes.to_a timetable[period] = periodtimes.to_a
# pp timetable # pp timetable
filename = timetable["short_name"] + "-" + timetable["long_name"]+ "." + period + ".yml" filename = timetable["short_name"] + "-" + timetable["long_name"]+ "." + period + ".yml"
filename = filename.downcase.gsub(" ","-").gsub("/","-").gsub("(","").gsub(")","") filename = filename.downcase.gsub(" ","-").gsub("/","-").gsub("(","").gsub(")","")
puts "Saving " + filename puts "Saving " + filename
File.open("#{File.dirname(__FILE__)}/output/"+filename, "w") do |f| File.open("#{File.dirname(__FILE__)}/output/"+filename, "w") do |f|
f.write timetable.to_yaml f.write timetable.to_yaml
end end
timetable timetable
end end
   
#TODO fix route 934  
Dir.glob("source-html/Route*.htm*") { |file| Dir.glob("source-html/Route*.htm*") { |file|
puts "Opened " + file puts "Opened " + file
doc = Nokogiri::HTML(open(file)) doc = Nokogiri::HTML(open(file))
# Search for nodes by css # Search for nodes by css
timetables = [] timetables = []
short_name = ""; short_name = "";
doc.xpath('//title').each do |title| doc.xpath('//title').each do |title|
short_name = title.content.gsub("Route_","").gsub("Route ","").gsub(", ","/").squeeze(" ").strip short_name = title.content.gsub("Route_","").gsub("Route ","").gsub(", ","/").gsub("ACTION Buses Timetable for ","").squeeze(" ").strip
end end
if short_name == "" if short_name == ""
raise "Route number(s) not found in <title> tag" raise "Route number(s) not found in <title> tag"
end end
   
doc.xpath('//table[preceding::text()="Weekdays"]').each do |table| doc.xpath('//table[preceding::text()="Weekdays"]').each do |table|
timetables << makeTimetable(table, "stop_times", short_name) timetables << makeTimetable(table, "stop_times", short_name)
end end
  doc.xpath('//table[preceding::text()="This timetable is effective from Monday 15th November 2010."]').each do |table|
  timetables << makeTimetable(table, "stop_times", short_name)
  end
  #all tables are weekdays on some really malformatted timetables
  if short_name == "170"
  doc.xpath('//table').each do |table|
  timetables << makeTimetable(table, "stop_times", short_name)
  end
  end
#weekends #weekends
doc.xpath('//table[preceding::text()="Saturdays" and following::a]').each do |table| doc.xpath('//table[preceding::text()="Saturdays" and following::a]').each do |table|
timetables << makeTimetable(table, "stop_times_saturday", short_name) timetables << makeTimetable(table, "stop_times_saturday", short_name)
end end
doc.xpath('//table[preceding::text()="Sundays"]').each do |table| doc.xpath('//table[preceding::text()="Sundays"]').each do |table|
timetables << makeTimetable(table, "stop_times_sunday", short_name) timetables << makeTimetable(table, "stop_times_sunday", short_name)
end end
#930/934 special cases #930/934 special cases
doc.xpath('//table[preceding::text()="Saturday" and following::h2]').each do |table| doc.xpath('//table[preceding::text()="Saturday" and following::h2]').each do |table|
timetables << makeTimetable(table, "stop_times_saturday", short_name) timetables << makeTimetable(table, "stop_times_saturday", short_name)
end end
doc.xpath('//table[preceding::text()="Sunday"]').each do |table| doc.xpath('//table[preceding::text()="Sunday"]').each do |table|
timetables << makeTimetable(table, "stop_times_sunday", short_name) timetables << makeTimetable(table, "stop_times_sunday", short_name)
end end
#route 81 = Weekdays - School Holidays Only #route 81 = Weekdays - School Holidays Only
doc.xpath('//table[preceding::text()="Weekdays - School Holidays Only "]').each do |table| doc.xpath('//table[preceding::text()="Weekdays - School Holidays Only "]').each do |table|
timetable = makeTimetable(table, "stop_times", short_name) timetable = makeTimetable(table, "stop_times", short_name)
#TODO set active date range to only be holidays #TODO set active date range to only be holidays
timetables << timetable; timetables << timetable;
end end
   
if timetables.size > 2 if timetables.size > 2
puts "WARNING: " + file + " more than 2 timetables (weekend split?):" + timetables.size.to_s puts "WARNING: " + file + " more than 2 timetables (weekend split?):" + timetables.size.to_s
end end
if timetables.size < 2 if timetables.size < 2
puts "WARNING: " + file + " less than 2 timetables (weekday loop service?):" + timetables.size.to_s puts "WARNING: " + file + " less than 2 timetables (weekday loop service?):" + timetables.size.to_s
elsif not (timetables[0]["time_points"] - timetables[1]["time_points"].reverse).empty? elsif not (timetables[0]["time_points"] - timetables[1]["time_points"].reverse).empty?
puts "WARNING: first pair of timetable timing points are not complementary for "+ file puts "WARNING: first pair of timetable timing points are not complementary for "+ file
pp(timetables[0]["time_points"] - timetables[1]["time_points"].reverse) pp(timetables[0]["time_points"] - timetables[1]["time_points"].reverse)
end end
if timetables.size < 1 if timetables.size < 1
raise "No timetables extracted from " + file raise "No timetables extracted from " + file
end end
} }
   
require 'rubygems' require 'rubygems'
require 'pp' require 'pp'
require 'yaml' require 'yaml'
class Array class Array
def to_yaml_style def to_yaml_style
:inline :inline
end end
end end
Dir.chdir("output") Dir.chdir("output")
   
def getTimePoints() def getTimePoints()
$time_points = [] $time_points = []
$time_points_sources = Hash.new([]) $time_points_sources = Hash.new([])
Dir.glob("*.yml") { |file| Dir.glob("*.yml") { |file|
timetable = YAML::load_file(file) timetable = YAML::load_file(file)
$time_points = $time_points | timetable["time_points"] $time_points = $time_points | timetable["time_points"]
timetable["time_points"].each do |timepoint| timetable["time_points"].each do |timepoint|
$time_points_sources[timepoint] = $time_points_sources[timepoint] | [ file ] $time_points_sources[timepoint] = $time_points_sources[timepoint] | [ file ]
end end
} }
end end
   
getTimePoints() getTimePoints()
#pp $time_points.sort! #pp $time_points.sort!
#pp $time_points_sources.sort #pp $time_points_sources.sort
   
time_point_corrections = {"North Lynehamham" => "North Lyneham", time_point_corrections = {"North Lynehamham" => "North Lyneham",
"Lathlain St (Platform 2)" => "Lathlain St Bus Station (Platform 2)", "Woden Bus Station Platform 10)" => "Woden Bus Station (Platform 10)",
"Lathlain St Sation (Platform 5)" => "Lathlain St Bus Station (Platform 5)", "Saint AndrewsVillage Hughes"=>"Saint Andrews Village Hughes",
"Lathlain Steet Station" => "Lathlain St Bus Station", "Flemmington Road / Sandford St"=>"Flemington Road / Sandford St",
"Lathlain St (Platform 3)" => "Lathlain St Bus Station (Platform 3)", "City Interchange"=>"City Bus Station",
"Lathlain Steet Station (Platform 3)" => "Lathlain St Bus Station (Platform 3)", "City Interchange (Platform 9)"=>"City Bus Station (Platform 9)",
"Lathlain St Station" => "Lathlain St Bus Station", "Bridbabella Gardens Nursing Home"=>"Brindabella Gardens Nursing Home",
"Lathlain St Station (Platform 1)" => "Lathlain St Bus Station (Platform 1)", "Bridbabella GardensNursing Home"=> "Brindabella Gardens Nursing Home",
"Lathlain St Station (Platform 2)" => "Lathlain St Bus Station (Platform 2)", "BrindabellaBusiness Park"=> "Brindabella Business Park",
"Lathlain St Station (Platform 3)" => "Lathlain St Bus Station (Platform 3)", "NarrabundahTerminus"=>"Narrabundah Terminus",
"Lathlain St Station (Platform 4)" => "Lathlain St Bus Station (Platform 4)", "Railway StationKingston"=>"Railway Station Kingston",
"Lathlain St Station (Platform 5)" => "Lathlain St Bus Station (Platform 5)", "Saint AndrewsVillage Hughes"=>"Saint Andrews Village Hughes",
"Lathlain St Station (Platform 6)" => "Lathlain St Bus Station (Platform 6)", "Dickson ShopsAntill Street"=>"Dickson Shops",
"Flemington Rd, Sandford St" => "Flemington Rd/Sandford St", "Cohen St Bus Station (Platform 3)"=>"Cohen Street Bus Station (Platform 3)",
"Erindale Centre /Sternberg Crescent" => "Erindale Drive/Sternberg", "Cohen St Bus Station (Platform 6)"=>"Cohen Street Bus Station (Platform 6)",
"Canberra Hospita" => "Canberra Hospital", "Newcastle Streetafter Isa Street"=>"Newcastle Street after Isa Street",
"Cohen Str Station (Platform 1)" => "Cohen St Bus Station (Platform 1)", "William Web / Ginninderra Drive"=>"William Webb / Ginninderra Drive",
"Cohen Street Station" => "Cohen St Bus Station", "Procor / Mead"=>"Proctor / Mead",
"Cohen Street Station (Platform 2)" => "Cohen St Bus Station (Platform 2)", "Fyshwick DirectFactory Outlet"=>"Fyshwick Direct Factory Outlet"
"Cohn St Station (Platform 3)" => "Cohen St Bus Station (Platform 3)", }
"Cohen St Station" => "Cohen St Bus Station",  
"Cohen St Station (Platform 1)" => "Cohen St Bus Station (Platform 1)",  
"Cohen St Station (Platform 2)" => "Cohen St Bus Station (Platform 2)",  
"Cohen St Station (Platform 3)" => "Cohen St Bus Station (Platform 3)",  
"Cohen St Station (Platform 4)" => "Cohen St Bus Station (Platform 4)",  
"Cohen St Station (Platform 5)" => "Cohen St Bus Station (Platform 5)",  
"Cohen St Station (Platform 6)" => "Cohen St Bus Station (Platform 6)",  
"City (Platform 7)" => "City Interchange (Platform 7)",  
"Cameron Avenue Station" => "Cameron Ave Bus Station",  
"Cameron Avenue Station (Platform 1)" => "Cameron Ave Bus Station (Platform 1)",  
"Cameron Avenue Station (Platform 2)" => "Cameron Ave Bus Station (Platform 2)",  
"Cameron Avenue Station (Platform 3)" => "Cameron Ave Bus Station (Platform 3)",  
"Cameron Avenue Station (Platform 4)" => "Cameron Ave Bus Station (Platform 4)",  
"Cameron Avenue Station (Platform 5)" => "Cameron Ave Bus Station (Platform 5)",  
"Cameron Ave Station" => "Cameron Ave Bus Station",  
"Cameron Ave Station (Platform 1)" => "Cameron Ave Bus Station (Platform 1)",  
"Cameron Ave Station (Platform 2)" => "Cameron Ave Bus Station (Platform 2)",  
"Cameron Ave Station (Platform 3)" => "Cameron Ave Bus Station (Platform 3)",  
"Cameron Ave Station (Platform 4)" => "Cameron Ave Bus Station (Platform 4)",  
"Cameron Ave Station (Platform 5)" => "Cameron Ave Bus Station (Platform 5)",  
"Burton & Garranan Hall, Daley Road ANU" => "Burton & Garran Hall/Daley Road ANU",  
"Burton & Garranan Hall,Daley Road ANU" => "Burton & Garran Hall/Daley Road ANU",  
"Garran/Daley Rd" => "Burton & Garran Hall/Daley Road ANU",  
"Kingstons Ave/National Crt" => "Kings Ave/National Crt",  
"Newcastle Street after Isa St" => "Newcastle / Isa Street Fyshwick",  
"National Circ/Canberra Ave" => "National Circuit / Canberra Ave",  
"St Clare of Conder" => "St Clare of Assisi Primary",  
"McKillop College Isabella Campus" => "MacKillop College Isabella Campus",  
"Outrim / Duggan" => "Outtrim / Duggan",  
"Manuka Captain Cook" => "Manuka/Captain Cook",  
"Manuka, Captain Cook" => "Manuka/Captain Cook",  
"Menindee Drive - Hospice" => "Hospice",  
"Hospice, Menindee Drive" => "Hospice",  
"Tharwa/Pocket" => "Tharwa/Pockett",  
"Paul-Co / Mirrabei" => "Paul Coe / Mirrabei"  
}  
time_point_corrections.each do |wrong, right| time_point_corrections.each do |wrong, right|
$time_points_sources[wrong].each do |wrongfile| $time_points_sources[wrong].each do |wrongfile|
badtimetable = YAML::load_file(wrongfile) badtimetable = YAML::load_file(wrongfile)
badentrynumber = badtimetable["time_points"].index wrong badentrynumber = badtimetable["time_points"].index wrong
badtimetable["time_points"][badentrynumber] = right badtimetable["time_points"][badentrynumber] = right
puts "Corrected '" + wrong + "' to '" + right + "' in " + wrongfile puts "Corrected '" + wrong + "' to '" + right + "' in " + wrongfile
File.open(wrongfile, "w") do |f| File.open(wrongfile, "w") do |f|
f.write badtimetable.to_yaml f.write badtimetable.to_yaml
end end
end end
end end
   
getTimePoints() getTimePoints()
pp $time_points.sort! pp $time_points.sort!
   
#!/usr/bin/ruby #!/usr/bin/ruby
require 'postgres'  
   
require 'highline.rb' require 'highline.rb'
include HighLine include HighLine
   
require 'rubygems' require 'rubygems'
  require 'postgres'
require 'json' require 'json'
require 'net/http' require 'net/http'
def cbr_geocode(query) def cbr_geocode(query)
base_url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?query=" base_url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?query="
url = "#{base_url}#{URI.encode(query)}&bbox=-35.47,148.83,-35.16,149.25&return_location=true" url = "#{base_url}#{URI.encode(query)}&bbox=-35.47,148.83,-35.16,149.25&return_location=true"
resp = Net::HTTP.get_response(URI.parse(url)) resp = Net::HTTP.get_response(URI.parse(url))
data = resp.body data = resp.body
   
# we convert the returned JSON data to native Ruby # we convert the returned JSON data to native Ruby
# data structure - a hash # data structure - a hash
result = JSON.parse(data) result = JSON.parse(data)
   
# if the hash has 'Error' as a key, we raise an error # if the hash has 'Error' as a key, we raise an error
if result.has_key? 'Error' if result.has_key? 'Error'
raise "web service error" raise "web service error"
end end
return result return result
end end
class Array class Array
   
def find_dups def find_dups
inject(Hash.new(0)) { |h,e| h[e] += 1; h }.select { |k,v| v > 1 }.collect { |x| x.first } inject(Hash.new(0)) { |h,e| h[e] += 1; h }.select { |k,v| v > 1 }.collect { |x| x.first }
end end
end end
   
require 'yaml' require 'yaml'
require 'pp' require 'pp'
Dir.chdir("output") Dir.chdir("output")
   
def getTimePoints() def getTimePoints()
$time_points = [] $time_points = []
$time_points_sources = Hash.new([]) $time_points_sources = Hash.new([])
Dir.glob("*.yml") { |file| Dir.glob("*.yml") { |file|
timetable = YAML::load_file(file) timetable = YAML::load_file(file)
$time_points = $time_points | timetable["time_points"] $time_points = $time_points | timetable["time_points"]
timetable["time_points"].each do |timepoint| timetable["time_points"].each do |timepoint|
$time_points_sources[timepoint] = $time_points_sources[timepoint] | [ file ] $time_points_sources[timepoint] = $time_points_sources[timepoint] | [ file ]
end end
} }
end end
   
getTimePoints() getTimePoints()
$time_points.sort! $time_points.sort!
   
connbus = PGconn.connect("localhost", 5432, '', '', "bus", "postgres", connbus = PGconn.connect("localhost", 5432, '', '', "bus", "postgres",
"snmc") "snmc")
connosm = PGconn.connect("localhost", 5432, '', '', "openstreetmap", connosm = PGconn.connect("localhost", 5432, '', '', "openstreetmap", "postgres", "snmc")
"postgres", "snmc")  
   
if ask_if("Insert Timing Point names to database?") if ask_if("Insert Timing Point names to database?")
$time_points.each do |time_point| $time_points.each do |time_point|
begin begin
time_point = time_point.gsub(/\\/, '\&\&').gsub(/'/, "''").gsub("St", "%") time_point = time_point.gsub(/\\/, '\&\&').gsub(/'/, "''")
res = connbus.exec("INSERT INTO timing_point (name) VALUES ('#{time_point}')") res = connbus.exec("INSERT INTO timing_point (name) VALUES ('#{time_point}')")
puts "Put '#{time_point}' into DB" puts "Put '#{time_point}' into DB"
rescue PGError => e rescue PGError => e
puts "Error inserting '#{time_point}' to DB #{e}" puts "Error inserting '#{time_point}' to DB #{e}"
#conn.close() if conn #conn.close() if conn
end end
end end
end end
   
   
if ask_if("Fill null Timing Points from OSM bus_stop database?") if ask_if("Fill null Timing Points from OSM bus_stop database?")
# TODO Where there's a "Cnr" or a \/ or a &, Look for 2 ways or nodes and average the closest two! # TODO Where there's a "Cnr" or a \/ or a &, Look for 2 ways or nodes and average the closest two!
begin begin
null_points = connbus.exec('SELECT name FROM timing_point WHERE lat IS null OR lng IS null;') null_points = connbus.exec('SELECT name FROM timing_point WHERE lat IS null OR lng IS null;')
rescue PGError => e rescue PGError => e
puts "Error selecting null points from DB #{e}" puts "Error selecting null points from DB #{e}"
#conn.close() if conn #conn.close() if conn
end end
   
null_points.each do |null_point_name| null_points.each do |null_point_name|
begin begin
name = null_point_name.to_s.gsub(/\\/, '\&\&').gsub(/'/, "''") name = null_point_name.to_s.gsub(/\\/, '\&\&').gsub(/'/, "''")
pp name pp name
search_name = ask("Hmm, if we're still looking, the name is probably wrong. What's the right name?", :string, :default => name) search_name = ask("Hmm, if we're still looking, the name is probably wrong. What's the right name?", :string, :default => name)
matching_nodes = connosm.exec("Select * FROM (SELECT * from current_node_tags, mat