MyWay timeliness by stop/route
--- a/aws/awsStartup.sh
+++ b/aws/awsStartup.sh
@@ -26,6 +26,9 @@
#createuser transitdata -SDRP
#password transitdata
#psql -d transitdata -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\"
+#psql -d transitdata -c "GRANT SELECT,INSERT ON TABLE myway_observations,myway_routes,myway_stops,myway_timingdeltas TO transitdata;"
+#psql -d transitdata -c "GRANT SELECT,INSERT,UPDATE ON TABLE myway_routes,myway_stops TO transitdata;"
+##psql -d transitdata -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO transitdata;"
php /var/www/updatedb.php
wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \
--- a/labs/index.php
+++ b/labs/index.php
@@ -6,13 +6,22 @@
<li data-role="list-divider" > Experimental Features </li>
<li><a href="mywaybalance.php"><h3>MyWay Balance for mobile</h3>
<p>Mobile viewer for MyWay balance. Warning! No HTTPS security.</p></a></li>
- <li><a href="networkstats.php"><h3>Route Statistics</h3>
- <p>Analysis of route timing points</p></a></li>
<li><a href="busstopdensity.php"><h3>Bus Stop Density Map</h3>
<p>Analysis of bus stop coverage</p></a></li>
<li><a href="stopBrowser.php"><h3>Bus Stop Browser Map</h3>
<p>Bus stop location/route browser</p></a></li>
- <li>More coming soon!</li>
+ </ul>
+ <ul data-role="listview" data-theme="e" data-groupingtheme="e">
+
+ <li data-role="list-divider" > MyWay Timeliness Graphs </li>
+ <li><a href="myway_timeliness.php"><h3>Timeliness over Day</h3>
+ <p>Displays the deviation from the timetable over the day</p></a></li>
+ <li><a href="myway_timeliness_freqdist.php"><h3>Frequency Distribution of Time Deviation</h3>
+ <p>Displays spread of time deviations</p></a></li>
+ <li><a href="myway_timeliness_route.php"><h3>Timeliness over Route</h3>
+ <p>Displays the deviation from timetable as a specific route progresses</p></a></li>
+ <li><a href="myway_timeliness_stop.php"><h3>Timeliness at Stop</h3>
+ <p>Displays the deviation from the timetable at a specific stop</p></a></li>
</ul>
</div>
<?php
--- a/labs/myway_timeliness.php
+++ b/labs/myway_timeliness.php
@@ -6,7 +6,7 @@
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script>
- <div id="placeholder" style="width:1000px;height:600px"></div>
+ <center><div id="placeholder" style="width:900px;height:550px"></div></center>
<script type="text/javascript">
$(function () {
var d = new Date();
@@ -26,16 +26,16 @@
$labels = Array();
$lastRoute = "";
foreach ($query->fetchAll() as $delta) {
- $routeName = $delta['route_full_name'];
- if (strstr($routeName," 3")) $routeName = "312-319";
- else $routeName = preg_replace('/\D/', '', $routeName);
- if ($routeName != $lastRoute) {
- $i++;
- echo " var d$i = [];";
- $lastRoute = $routeName;
- $labels[$i] = $routeName;
- }
- echo "d$i.push([ midnight+ (1000*" . midnight_seconds(strtotime($delta['time'])) . "), {$delta['timing_delta']}]); \n";
+ $routeName = $delta['route_full_name'];
+ if (strstr($routeName, " 3")) $routeName = "312-319";
+ else $routeName = preg_replace('/\D/', '', $routeName);
+ if ($routeName != $lastRoute) {
+ $i++;
+ echo " var d$i = [];";
+ $lastRoute = $routeName;
+ $labels[$i] = $routeName;
+ }
+ echo "d$i.push([ midnight+ (1000*" . midnight_seconds(strtotime($delta['time'])) . "), ".intval($delta['timing_delta'])."]); \n";
};
?>
@@ -44,13 +44,13 @@
var plot = $.plot(placeholder, [
<?php
foreach ($labels as $key => $label) {
- echo " {
+ echo " {
data: d$key,
points: { show: true },
label: '$label'
},";
}
- ?>
+?>
],
{
xaxis: {
@@ -61,17 +61,18 @@
yaxis: {
tickFormatter: yformatter
},
- grid: { hoverable: true, clickable: true },
+ grid: { hoverable: true, clickable: true, labelMargin: 32 },
});
var o;
o = plot.pointOffset({ x: midnight+ (9*60*60*1000), y: -1.2});
placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">9am</div>');
- o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
+ o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">4pm</div>');
});
function yformatter(v) {
- return Math.floor(v/60) + " minutes " + (v == 0 ? "" : (v >0 ? "early":"late"))
+ if (Math.floor(v/60) < -9) return "";
+ return Math.abs(Math.floor(v/60)) + " min " + (v == 0 ? "" : (v >0 ? "early":"late"))
}
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
@@ -105,7 +106,7 @@
showTooltip(item.pageX, item.pageY,
- item.series.label + " at "+ time +" = " + y +" ( "+ y/60+" minutes )");
+ item.series.label + " at "+ time +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
}
}
else {
--- a/labs/myway_timeliness_calculate.php
+++ b/labs/myway_timeliness_calculate.php
@@ -87,7 +87,8 @@
//work out time delta, put into array with index of delta
$timeDeltas[] = Array(
"timeDiff" => $timeDiff,
- "stop_code" => $potentialStop['stop_code']
+ "stop_code" => $potentialStop['stop_code'],
+ "stop_sequence" => $timedTrip['stop_sequence']
);
echo "Found trip {$trip['trip_id']} at stop {$potentialStop['stop_code']} (#{$potentialStop['stop_id']}, sequence #{$trip['stop_sequence']})<br>";
echo "Arriving at {$timedTrip['arrival_time']}, difference of " . round($timeDiff / 60, 2) . " minutes<br>";
@@ -110,18 +111,18 @@
echo "Lowest difference of " . round($lowestDelta / 60, 2) . " minutes will be recorded for this observation<br>";
$observation_id = $obsv['observation_id'];
$route_full_name = $obsv['route_full_name'];
- $myway_route = $obsv['myway_stop'];
$stop_code = $timeDeltas[0]["stop_code"];
- $stmt = $conn->prepare("insert into myway_timingdeltas (observation_id, route_full_name, myway_route, stop_code, timing_delta, time, date, timing_period)
- values (:observation_id, :route_full_name, :myway_route, :stop_code, :timing_delta, :time, :date, :timing_period)");
+ $stop_sequence = $timeDeltas[0]["stop_sequence"];
+ $stmt = $conn->prepare("insert into myway_timingdeltas (observation_id, route_full_name, stop_code, timing_delta, time, date, timing_period, stop_sequence)
+ values (:observation_id, :route_full_name, :stop_code, :timing_delta, :time, :date, :timing_period, :stop_sequence)");
$stmt->bindParam(':observation_id', $observation_id);
$stmt->bindParam(':route_full_name', $route_full_name);
- $stmt->bindParam(':myway_route', $myway_route);
$stmt->bindParam(':stop_code', $stop_code);
$stmt->bindParam(':timing_delta', $lowestDelta);
$stmt->bindParam(':time', $time);
$stmt->bindParam(':date', $date);
$stmt->bindParam(':timing_period', $timing_period);
+ $stmt->bindParam(':stop_sequence', $stop_sequence);
// insert a record
$stmt->execute();
if ($stmt->rowCount() > 0) {
--- /dev/null
+++ b/labs/myway_timeliness_freqdist.php
@@ -1,1 +1,78 @@
+<?php
+include ('../include/common.inc.php');
+include_header("MyWay Deltas", "mywayDelta");
+?>
+ <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]-->
+
+ <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script>
+ <center><div id="placeholder" style="width:900px;height:550px"></div></center>
+<script type="text/javascript">
+$(function () {
+
+ var d1 = [];
+<?php
+$query = "select td, count(*) from (select (timing_delta - MOD(timing_delta,10)) as td from myway_timingdeltas where abs(timing_delta) < 2*(select stddev(timing_delta) from myway_timingdeltas)) as a group by td order by td";
+$query = $conn->prepare($query);
+$query->execute();
+if (!$query) {
+ databaseError($conn->errorInfo());
+ return Array();
+}
+
+foreach ($query->fetchAll() as $delta) {
+
+ echo "d1.push([ ".intval($delta['td']).", ".intval($delta['count'])."]); \n";
+};
+?>
+
+ var placeholder = $("#placeholder");
+
+ var plot = $.plot(placeholder, [
+ {
+ data: d1,
+ bars: { show: true }
+ },
+ ],
+ {
+
+ grid: { hoverable: true, clickable: true, labelMargin: 17 },
+ });
+ /* var o;
+ o = plot.pointOffset({ x: midnight+ (9*60*60*1000), y: -1.2});
+ placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">9am</div>');
+ o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
+ placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">4pm</div>');
+ */
+ });
+
+ /*
+ var previousPoint = null;
+ $("#placeholder").bind("plothover", function (event, pos, item) {
+ $("#x").text(pos.x.toFixed(2));
+ $("#y").text(pos.y.toFixed(2));
+
+ if (item) {
+ if (previousPoint != item.dataIndex) {
+ previousPoint = item.dataIndex;
+
+ $("#tooltip").remove();
+ var x = item.datapoint[0].toFixed(2),
+ y = item.datapoint[1].toFixed(2);
+
+ var d = new Date();
+d.setTime(x);
+var time = d.getUTCHours() +':'+ (d.getUTCMinutes().toString().length == 1 ? '0'+ d.getMinutes(): d.getUTCMinutes())
+
+
+ showTooltip(item.pageX, item.pageY,
+ item.series.label + " at "+ time +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
+ }
+ }
+ else {
+ $("#tooltip").remove();
+ previousPoint = null;
+ }
+ });
+ */
+</script>
--- a/labs/myway_timeliness_reconcile.php
+++ b/labs/myway_timeliness_reconcile.php
@@ -7,8 +7,8 @@
$query = "update myway_routes set route_full_name = :route_full_name where myway_route = :myway_route";
debug($query, "database");
$query = $conn->prepare($query);
- $query->bindParam(":myway_route", $myway_route);
- $query->bindParam(":route_full_name", $route_full_name);
+ $query->bindParam(":myway_route", $myway_route,PDO::PARAM_STR, 5);
+ $query->bindParam(":route_full_name", $route_full_name,PDO::PARAM_STR, 42);
$query->execute();
die(print_r($conn->errorInfo() , true));
}
@@ -19,8 +19,8 @@
$query = "update myway_stops set stop_code = :stop_code, stop_street = :stop_street where myway_stop = :myway_stop";
debug($query, "database");
$query = $conn->prepare($query);
- $query->bindParam(":myway_stop", $myway_stop);
- $query->bindParam(":stop_code", $stop_code);
+ $query->bindParam(":myway_stop", $myway_stop, PDO::PARAM_STR, 25);
+ $query->bindParam(":stop_code", $stop_code, PDO::PARAM_STR, 32);
$query->bindParam(":stop_street", $stop_street);
$query->execute();
die(print_r($conn->errorInfo() , true));
--- /dev/null
+++ b/labs/myway_timeliness_route.json.php
@@ -1,1 +1,25 @@
-
+<?php
+include ('../include/common.inc.php');
+header('Content-Type: text/javascript; charset=utf8');
+// header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/');
+header('Access-Control-Max-Age: 3628800');
+header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
+?>
+{
+ "label": "<?php echo $_REQUEST['routeid']; ?>",
+ "data": <?php
+ $query = "select * from myway_timingdeltas where route_full_name = :route_full_name AND abs(timing_delta) < 2*(select stddev(timing_delta) from myway_timingdeltas) order by stop_sequence;";
+$query = $conn->prepare($query);
+$query->bindParam(':route_full_name', $_REQUEST['routeid'],PDO::PARAM_STR, 42);
+
+$query->execute();
+if (!$query) {
+ databaseError($conn->errorInfo());
+ return Array();
+}
+foreach ($query->fetchAll() as $delta) {
+ $points[] = "[{$delta['stop_sequence']}, {$delta['timing_delta']}]";
+};
+echo "[".implode(",",$points)."]";
+?>
+}
--- /dev/null
+++ b/labs/myway_timeliness_route.php
@@ -1,1 +1,120 @@
+<?php
+include ('../include/common.inc.php');
+include_header("MyWay Deltas", "mywayDelta");
+?>
+ <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]-->
+
+ <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script>
+ <form method="get" action="">
+ <select id="routeid" name="routeid">
+<?php
+$query = "select distinct route_full_name from myway_routes where myway_route != '' order by route_full_name";
+$query = $conn->prepare($query);
+$query->execute();
+if (!$query) {
+ databaseError($conn->errorInfo());
+ return Array();
+}
+foreach ($query->fetchAll() as $route) {
+ echo "<option value=\"{$route['route_full_name']}\">{$route['route_full_name']}</option>";
+
+};
+?> </select>
+ <center><div id="placeholder" style="width:900px;height:550px"></div></center>
+<script type="text/javascript">
+$(function () {
+
+ var placeholder = $("#placeholder");
+ var data = [];
+ var options = {
+ xaxis: {
+ },
+ yaxis: {
+ tickFormatter: yformatter
+ },
+ grid: { hoverable: true, clickable: true, labelMargin: 32 },
+ };
+
+ var plot = $.plot(placeholder, data, options);
+
+// fetch one series, adding to what we got
+ var alreadyFetched = {};
+
+ $("#routeid").change(function () {
+ var select = $(this);
+
+ // find the URL in the link right next to us
+ // var dataurl = button.siblings('a').attr('href');
+ var dataurl = "myway_timeliness_route.json.php?routeid=" + select.val();
+ // then fetch the data with jQuery
+ function onDataReceived(series) {
+ // extract the first coordinate pair so you can see that
+ // data is now an ordinary Javascript object
+ var firstcoordinate = '(' + series.data[0][0] + ', ' + series.data[0][1] + ')';
+
+
+ // let's add it to our current data
+ if (!alreadyFetched[series.label]) {
+ alreadyFetched[series.label] = true;
+ data.push(series);
+ }
+
+ // and plot all we got
+ $.plot(placeholder, data, options);
+ }
+
+ $.ajax({
+ url: dataurl,
+ method: 'GET',
+ dataType: 'json',
+ success: onDataReceived
+ });
+ });
+
+
+ });
+
+
+
+function yformatter(v) {
+ if (Math.floor(v/60) < -9) return "";
+ return Math.abs(Math.floor(v/60)) + " min " + (v == 0 ? "" : (v >0 ? "early":"late"))
+}
+ function showTooltip(x, y, contents) {
+ $('<div id="tooltip">' + contents + '</div>').css( {
+ position: 'absolute',
+ display: 'none',
+ top: y + 5,
+ left: x + 5,
+ border: '1px solid #fdd',
+ padding: '2px',
+ 'background-color': '#fee',
+ opacity: 0.80
+ }).appendTo("body").fadeIn(200);
+ }
+
+ var previousPoint = null;
+ $("#placeholder").bind("plothover", function (event, pos, item) {
+ $("#x").text(pos.x.toFixed(2));
+ $("#y").text(pos.y.toFixed(2));
+
+ if (item) {
+ if (previousPoint != item.dataIndex) {
+ previousPoint = item.dataIndex;
+
+ $("#tooltip").remove();
+ var x = item.datapoint[0],
+ y = item.datapoint[1].toFixed(2);
+
+ showTooltip(item.pageX, item.pageY,
+ item.series.label + " at stop_sequence "+ x +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
+ }
+ }
+ else {
+ $("#tooltip").remove();
+ previousPoint = null;
+ }
+ });
+
+</script>
--- /dev/null
+++ b/labs/myway_timeliness_stop.json.php
@@ -1,1 +1,31 @@
-
+<?php
+include ('../include/common.inc.php');
+header('Content-Type: text/javascript; charset=utf8');
+// header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/');
+header('Access-Control-Max-Age: 3628800');
+header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
+?>
+{
+ "label": "<?php echo $_REQUEST['stopid']; ?>",
+ "data": <?php
+ $query = "select * from myway_timingdeltas INNER JOIN myway_observations
+ON myway_observations.observation_id=myway_timingdeltas.observation_id
+ where myway_stop = :myway_stop
+ AND abs(timing_delta) < 2*(select stddev(timing_delta) from myway_timingdeltas)
+ order by myway_timingdeltas.time;";
+$query = $conn->prepare($query);
+$query->bindParam(':myway_stop', $_REQUEST['stopid'],PDO::PARAM_STR, 42);
+
+$query->execute();
+if (!$query) {
+ databaseError($conn->errorInfo());
+ return Array();
+}
+foreach ($query->fetchAll() as $delta) {
+ $points[] = "[".((strtotime("00:00Z") + midnight_seconds(strtotime($delta['time'])))*1000).", {$delta['timing_delta']}]";
+};
+if (count($points) == 0) {
+ echo "[]"; }
+ else echo "[".implode(",",$points)."]";
+?>
+}
--- /dev/null
+++ b/labs/myway_timeliness_stop.php
@@ -1,1 +1,131 @@
+<?php
+include ('../include/common.inc.php');
+include_header("MyWay Deltas", "mywayDelta");
+?>
+ <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]-->
+
+ <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script>
+ <form method="get" action="">
+ <select id="stopid" name="stopid">
+<?php
+$query = "select distinct myway_stop from myway_stops where myway_stop != '' order by myway_stop";
+$query = $conn->prepare($query);
+$query->execute();
+if (!$query) {
+ databaseError($conn->errorInfo());
+ return Array();
+}
+foreach ($query->fetchAll() as $stop) {
+ echo "<option value=\"{$stop['myway_stop']}\">{$stop['myway_stop']}</option>";
+
+};
+?> </select> <center><div id="placeholder" style="width:900px;height:550px"></div></center>
+<script type="text/javascript">
+$(function () {
+ var d = new Date();
+ d.setUTCMinutes(0);
+ d.setUTCHours(0);
+ var midnight = d.getTime();
+
+ var placeholder = $("#placeholder");
+ var data = [];
+ var options = {
+ xaxis: {
+ mode: "time",
+ min: midnight + (1000*60*60*8),
+ max: midnight + (1000*60*60*23.5)
+ },
+ yaxis: {
+ tickFormatter: yformatter
+ },
+ grid: { hoverable: true, clickable: true, labelMargin: 32 },
+ };
+
+ var plot = $.plot(placeholder, data, options);
+ var o;
+ o = plot.pointOffset({ x: midnight+ (9*60*60*1000), y: -1.2});
+ placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">9am</div>');
+ o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
+ placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">4pm</div>');
+// fetch one series, adding to what we got
+ var alreadyFetched = {};
+
+ $("#stopid").change(function () {
+ var select = $(this);
+
+ // find the URL in the link right next to us
+ // var dataurl = button.siblings('a').attr('href');
+ var dataurl = "myway_timeliness_stop.json.php?stopid=" + select.val();
+ // then fetch the data with jQuery
+ function onDataReceived(series) {
+ // extract the first coordinate pair so you can see that
+ // data is now an ordinary Javascript object
+ var firstcoordinate = '(' + series.data[0][0] + ', ' + series.data[0][1] + ')';
+
+
+ // let's add it to our current data
+ if (!alreadyFetched[series.label]) {
+ alreadyFetched[series.label] = true;
+ data.push(series);
+ }
+
+ // and plot all we got
+ $.plot(placeholder, data, options);
+ }
+
+ $.ajax({
+ url: dataurl,
+ method: 'GET',
+ dataType: 'json',
+ success: onDataReceived
+ });
+ });
+
+ });
+function yformatter(v) {
+ if (Math.floor(v/60) < -9) return "";
+ return Math.abs(Math.floor(v/60)) + " min " + (v == 0 ? "" : (v >0 ? "early":"late"))
+}
+ function showTooltip(x, y, contents) {
+ $('<div id="tooltip">' + contents + '</div>').css( {
+ position: 'absolute',
+ display: 'none',
+ top: y + 5,
+ left: x + 5,
+ border: '1px solid #fdd',
+ padding: '2px',
+ 'background-color': '#fee',
+ opacity: 0.80
+ }).appendTo("body").fadeIn(200);
+ }
+
+ var previousPoint = null;
+ $("#placeholder").bind("plothover", function (event, pos, item) {
+ $("#x").text(pos.x.toFixed(2));
+ $("#y").text(pos.y.toFixed(2));
+
+ if (item) {
+ if (previousPoint != item.dataIndex) {
+ previousPoint = item.dataIndex;
+
+ $("#tooltip").remove();
+ var x = item.datapoint[0].toFixed(2),
+ y = item.datapoint[1].toFixed(2);
+
+ var d = new Date();
+d.setTime(x);
+var time = d.getUTCHours() +':'+ (d.getUTCMinutes().toString().length == 1 ? '0'+ d.getMinutes(): d.getUTCMinutes())
+
+
+ showTooltip(item.pageX, item.pageY,
+ item.series.label + " at "+ time +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
+ }
+ }
+ else {
+ $("#tooltip").remove();
+ previousPoint = null;
+ }
+ });
+
+</script>
--- a/servicealerts_api.php
+++ b/servicealerts_api.php
@@ -36,7 +36,7 @@
$return['entities'][] = $entity;
}
}
-//header('Content-Type: text/javascript; charset=utf8');
+header('Content-Type: text/javascript; charset=utf8');
// header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/');
header('Access-Control-Max-Age: 3628800');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');