1 package net.jaekl.frank;
3 import java.io.PrintWriter;
4 import java.text.DateFormat;
5 import java.text.MessageFormat;
6 import java.text.SimpleDateFormat;
8 import java.util.Locale;
9 import java.util.TimeZone;
11 import net.jaekl.frank.octranspo.Route;
12 import net.jaekl.frank.octranspo.StopInfo;
13 import net.jaekl.frank.octranspo.Trip;
15 public class Schedule {
18 DateFormat m_hourMinFmt;
19 DateFormat m_hourMinSecFmt;
21 public Schedule(Locale locale) {
23 m_bundle = FrankBundle.getInst(locale);
24 m_hourMinFmt = new SimpleDateFormat("hh:mma", locale);
25 m_hourMinSecFmt = new SimpleDateFormat("hh:mm:ssa", locale);
27 // We always display times in Ottawa (local) time, because that's the only
28 // time that makes sense for a user who wants to know when to catch a bus.
29 // In the (unlikely?) event that our server is in a different timezone,
30 // we need to explicitly specify the time zone that we want to use.
31 TimeZone tz = TimeZone.getTimeZone("America/Toronto");
32 m_hourMinFmt.setTimeZone(tz);
33 m_hourMinSecFmt.setTimeZone(tz);
36 String trans(String key) {
37 return m_bundle.get(key);
40 String mapUrl(double latitude, double longitude) {
41 return "http://www.openstreetmap.org/?mlat=" + latitude + "&mlon=" + longitude + "&zoom=15";
44 void writeStyle(PrintWriter pw) {
45 pw.println("<STYLE>");
46 pw.println(" body {background-color: #F0F0C0; font-size: 1.5em; }");
47 pw.println(" #trips {border-collapse: collapse; font-size: 1.5em; }");
48 pw.println(" #trips td, #trips th {border: 1px solid #600000; padding: 3px 3px 3px 3px; text-align: center;}");
49 pw.println(" #trips th {background-color: #800000; color: #FFFFFF; }");
50 pw.println(" #trips tr.ghost td {background-color: #C0C0C0;}");
51 pw.println("</STYLE>");
54 // Countdown timer that updates time remaining until each bus is expected.
55 void writeScript(PrintWriter pw, String remainArray, int remainCount) {
56 String min = trans(FrankBundle.MINUTES);
57 String sec = trans(FrankBundle.SECONDS);
59 pw.println("<SCRIPT>");
60 pw.println("var start; var interval; var remain;");
61 pw.println("window.onload = function() { start = new Date(); interval = setInterval(\"Tick()\", 1000); remain = " + remainArray + "; }");
62 pw.println("function Tick() { ");
63 pw.println(" var now = new Date(); var delta = Math.floor((now.getTime() - start.getTime()) / 1000); ");
64 pw.println(" var secs = delta % 60; var mins = Math.floor(delta / 60);");
65 pw.println(" document.getElementById(\"elapsed\").innerHTML = mins + \"" + min + " \" + secs + \"" + sec + "\";");
66 pw.println(" for (var i = 0; i < " + remainCount + "; ++i) {");
67 pw.println(" var id = \"in_\" + i;");
68 pw.println(" var element = document.getElementById(id);");
69 pw.println(" var remain_delta = (remain[i] * 60) + 30 - delta;");
70 pw.println(" if (remain_delta <= 0) {");
71 pw.println(" element.innerHTML = \"----\";");
72 pw.println(" } else {");
73 pw.println(" element.innerHTML = (Math.floor(remain_delta / 60)) + \"" + trans(min) + "\";");
77 pw.println("</SCRIPT>");
81 void writeHeader(PrintWriter pw, String title) {
84 pw.println("<TITLE>" + title + "</TITLE>");
86 pw.println("</HEAD>");
89 void writePage(PrintWriter pw, StopInfo stopInfo)
91 StringBuilder remainArray = new StringBuilder("[ ");
94 String title = stopInfo.getDescr() + " (" + stopInfo.getStopNo() + ")";
95 String min = trans(FrankBundle.MINUTES);
97 writeHeader(pw, trans(FrankBundle.FRANK) + ": " + title);
100 pw.println(" <TABLE ID=\"trips\">");
101 pw.println(" <TR><TH COLSPAN=\"5\">" + title + "</TH></TR>");
102 pw.println(" <TR><TH>" +
103 trans(FrankBundle.ROUTE) +
105 trans(FrankBundle.DESTINATION) +
107 trans(FrankBundle.ETA) +
109 trans(FrankBundle.REMAIN) +
111 trans(FrankBundle.GPS_READ) +
114 for (int routeIdx = 0; routeIdx < stopInfo.getNumRoutes(); ++routeIdx) {
115 Route route = stopInfo.getRoute(routeIdx);
116 for (int tripIdx = 0; tripIdx < route.getNumTrips(); ++tripIdx) {
117 Trip trip = route.getTrip(tripIdx);
118 boolean isGhost = ((-1) == trip.getAdjAge());
120 // GPS is off. This bus may not exist.
121 pw.println(" <TR CLASS=\"ghost\">");
125 pw.println(" <TD>" + route.getRouteNo() + "</TD>");
126 pw.println(" <TD>" + trip.getDest() + "</TD>");
127 pw.println(" <TD>" + m_hourMinFmt.format(trip.getETA()) + "</TD>");
129 pw.println(" <TD><SPAN ID=\"in_" + remainCount + "\">"+ trip.getAdjTime()+min+"</SPAN></TD>");
130 if (remainCount > 0) {
131 remainArray.append(", ");
133 remainArray.append(trip.getAdjTime());
136 if (trip.getAdjAge() < 0) {
137 pw.println(" <TD>" + trans(FrankBundle.GPS_OFF) + "</TD>");
140 pw.println(" <TD><A HREF=\"" + mapUrl(trip.getLatitude(), trip.getLongitude()) +
141 "\">" + m_hourMinSecFmt.format(trip.getGPSTime()) + "</A></TD>");
143 pw.println(" </TR>");
147 pw.println(" </TABLE>");
149 String dataCollectedFormat = trans(FrankBundle.DATA_COLLECTED);
150 String dataCollected = MessageFormat.format(dataCollectedFormat, m_hourMinSecFmt.format(new Date()));
151 pw.println(" <P>" + dataCollected + "</P>");
153 remainArray.append(" ]");
154 writeScript(pw, remainArray.toString(), remainCount);
155 pw.println("</BODY></HTML>");