Beginnings of ETA calc based on actual data
Beginnings of ETA calc based on actual data

<?php <?php
   
/* /*
* Copyright 2010,2011 Alexander Sadleir * Copyright 2010,2011 Alexander Sadleir
   
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
   
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
   
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
if (isset($_REQUEST['firstLetter'])) { if (isset($_REQUEST['firstLetter'])) {
$firstLetter = filter_var($_REQUEST['firstLetter'], FILTER_SANITIZE_STRING); $firstLetter = filter_var($_REQUEST['firstLetter'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['bysuburbs'])) { if (isset($_REQUEST['bysuburbs'])) {
$bysuburbs = true; $bysuburbs = true;
} }
if (isset($_REQUEST['bynumber'])) { if (isset($_REQUEST['bynumber'])) {
$bynumber = true; $bynumber = true;
} }
if (isset($_REQUEST['allstops'])) { if (isset($_REQUEST['allstops'])) {
$allstops = true; $allstops = true;
} }
if (isset($_REQUEST['nearby'])) { if (isset($_REQUEST['nearby'])) {
$nearby = true; $nearby = true;
  }
  if (isset($_REQUEST['labs'])) {
  $labs = true;
} }
if (isset($_REQUEST['suburb'])) { if (isset($_REQUEST['suburb'])) {
$suburb = $_REQUEST['suburb']; $suburb = $_REQUEST['suburb'];
} }
if (isset($_REQUEST['pageKey'])) { if (isset($_REQUEST['pageKey'])) {
$pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT); $pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT);
} }
if (isset($_REQUEST['lat'])) { if (isset($_REQUEST['lat'])) {
$lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); $lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} }
if (isset($_REQUEST['lon'])) { if (isset($_REQUEST['lon'])) {
$lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); $lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
} }
if (isset($_REQUEST['radius'])) { if (isset($_REQUEST['radius'])) {
$max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT); $max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT);
} }
if (isset($_REQUEST['numberSeries'])) { if (isset($_REQUEST['numberSeries'])) {
$numberSeries = filter_var($_REQUEST['numberSeries'], FILTER_SANITIZE_NUMBER_INT); $numberSeries = filter_var($_REQUEST['numberSeries'], FILTER_SANITIZE_NUMBER_INT);
} }
if (isset($_REQUEST['routeDestination'])) { if (isset($_REQUEST['routeDestination'])) {
$routeDestination = urldecode(filter_var($_REQUEST['routeDestination'], FILTER_SANITIZE_ENCODED)); $routeDestination = urldecode(filter_var($_REQUEST['routeDestination'], FILTER_SANITIZE_ENCODED));
} }
if (isset($_REQUEST['routename'])) { if (isset($_REQUEST['routename'])) {
$routename = urldecode(filter_var($_REQUEST['routename'], FILTER_SANITIZE_ENCODED)); $routename = urldecode(filter_var($_REQUEST['routename'], FILTER_SANITIZE_ENCODED));
} }
if (isset($_REQUEST['stopcode'])) { if (isset($_REQUEST['stopcode'])) {
$stopcode = filter_var($_REQUEST['stopcode'], FILTER_SANITIZE_STRING); $stopcode = filter_var($_REQUEST['stopcode'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['stopids'])) { if (isset($_REQUEST['stopids'])) {
$stopids = explode(",", filter_var($_REQUEST['stopids'], FILTER_SANITIZE_STRING)); $stopids = explode(",", filter_var($_REQUEST['stopids'], FILTER_SANITIZE_STRING));
} }
if (isset($_REQUEST['filterIncludeRoutes'])) { if (isset($_REQUEST['filterIncludeRoutes'])) {
$filterIncludeRoutes = explode(",", filter_var($_REQUEST['filterIncludeRoutes'], FILTER_SANITIZE_STRING)); $filterIncludeRoutes = explode(",", filter_var($_REQUEST['filterIncludeRoutes'], FILTER_SANITIZE_STRING));
} }
if (isset($_REQUEST['filterHasStop'])) { if (isset($_REQUEST['filterHasStop'])) {
$filterHasStop = filter_var($_REQUEST['filterHasStop'], FILTER_SANITIZE_STRING); $filterHasStop = filter_var($_REQUEST['filterHasStop'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['tripid'])) { if (isset($_REQUEST['tripid'])) {
$tripid = filter_var($_REQUEST['tripid'], FILTER_SANITIZE_STRING); $tripid = filter_var($_REQUEST['tripid'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['routeid'])) { if (isset($_REQUEST['routeid'])) {
$routeid = filter_var($_REQUEST['routeid'], FILTER_SANITIZE_STRING); $routeid = filter_var($_REQUEST['routeid'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['directionid'])) { if (isset($_REQUEST['directionid'])) {
$directionid = filter_var($_REQUEST['directionid'], FILTER_SANITIZE_STRING); $directionid = filter_var($_REQUEST['directionid'], FILTER_SANITIZE_STRING);
} }
if (isset($_REQUEST['stopid'])) { if (isset($_REQUEST['stopid'])) {
$stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT); $stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT);
} }
if (isset($_REQUEST['geolocate'])) { if (isset($_REQUEST['geolocate'])) {
$geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); $geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL);
} }
   
<?php <?php
   
setlocale(LC_CTYPE, 'C'); setlocale(LC_CTYPE, 'C');
// source: http://stackoverflow.com/questions/81934/easy-way-to-export-a-sql-table-without-access-to-the-server-or-phpmyadmin#81951 // source: http://stackoverflow.com/questions/81934/easy-way-to-export-a-sql-table-without-access-to-the-server-or-phpmyadmin#81951
include ('../include/common.inc.php'); include ('../include/common.inc.php');
$query = $conn->prepare(' $query = $conn->prepare('
SELECT * from myway_timingdeltas' SELECT * from myway_timingdeltas'
, array(PDO::ATTR_CURSOR => PDO::FETCH_ORI_NEXT)); , array(PDO::ATTR_CURSOR => PDO::FETCH_ORI_NEXT));
$query->execute(); $query->execute();
$errors = $conn->errorInfo(); $errors = $conn->errorInfo();
if ($errors[2] != "") { if ($errors[2] != "") {
die("Export terminated, db error" . print_r($errors, true)); die("Export terminated, db error" . print_r($errors, true));
} }
   
$headers = Array("date", "delay", "distance", "origin", "destination"); $headers = Array("date", "delay", "distance", "origin", "destination");
   
$fp = fopen('php://output', 'w'); $fp = fopen('php://output', 'w');
if ($fp && $query) { if ($fp && $query) {
//header('Content-Type: text/csv'); //header('Content-Type: text/csv');
header('Pragma: no-cache'); header('Pragma: no-cache');
header('Expires: 0'); header('Expires: 0');
fputcsv($fp, $headers); fputcsv($fp, $headers);
while ($r = $query->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) { while ($r = $query->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) {
$row = Array(); $row = Array();
foreach ($headers as $i => $fieldName) { foreach ($headers as $i => $fieldName) {
switch ($fieldName) { switch ($fieldName) {
case "date": case "date":
$row[] = date("dm",strtotime($r['date'])).date("Hi",strtotime($r['time'])); $row[] = date("r",strtotime($r['date']." ".$r['time']));
break; break;
case "delay": case "delay":
$row[] = $r['timing_delta']; $row[] = $r['timing_delta'];
break; break;
case "distance": case "distance":
$row[] = $r['stop_sequence']; $row[] = $r['stop_sequence'];
break; break;
case "origin": case "origin":
$row[] = $r['myway_stop']; $row[] = $r['myway_stop'];
break; break;
case "destination": case "destination":
$row[] = $r['route_name']; $row[] = $r['route_name'];
break; break;
default: default:
break; break;
} }
} }
fputcsv($fp, array_values($row)); fputcsv($fp, array_values($row));
} }
die; die;
} }
?> ?>
   
   
<!DOCTYPE html> <!DOCTYPE html>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Tesseract</title> <title>Tesseract</title>
<style> <style>
   
   
#charts { #charts {
padding: 10px 0; padding: 10px 0;
} }
   
.chart { .chart {
display: inline-block; display: inline-block;
height: 151px; height: 151px;
margin-bottom: 20px; margin-bottom: 20px;
} }
   
.reset { .reset {
padding-left: 1em; padding-left: 1em;
font-size: smaller; font-size: smaller;
color: #ccc; color: #ccc;
} }
   
.background.bar { .background.bar {
fill: #ccc; fill: #ccc;
} }
   
.foreground.bar { .foreground.bar {
fill: steelblue; fill: steelblue;
} }
   
.axis path, .axis line { .axis path, .axis line {
fill: none; fill: none;
stroke: #000; stroke: #000;
shape-rendering: crispEdges; shape-rendering: crispEdges;
} }
   
.axis text { .axis text {
font: 10px sans-serif; font: 10px sans-serif;
} }
   
.brush rect.extent { .brush rect.extent {
fill: steelblue; fill: steelblue;
fill-opacity: .125; fill-opacity: .125;
} }
   
.brush .resize path { .brush .resize path {
fill: #eee; fill: #eee;
stroke: #666; stroke: #666;
} }
   
#hour-chart { #hour-chart {
width: 260px; width: 260px;
} }
   
#delay-chart { #delay-chart {
width: 230px; width: 230px;
} }
   
#distance-chart { #distance-chart {
width: 420px; width: 420px;
} }
   
#date-chart { #date-chart {
width: 920px; width: 920px;
} }
   
#flight-list { #flight-list {
min-height: 1024px; min-height: 1024px;
} }
   
#flight-list .date, #flight-list .date,
#flight-list .day { #flight-list .day {
margin-bottom: .4em; margin-bottom: .4em;
} }
   
#flight-list .flight { #flight-list .flight {
line-height: 1.5em; line-height: 1.5em;
background: #eee; background: #eee;
width: 640px; width: 640px;
margin-bottom: 1px; margin-bottom: 1px;
} }
   
#flight-list .time { #flight-list .time {
color: #999; color: #999;
} }
   
#flight-list .flight div { #flight-list .flight div {
display: inline-block; display: inline-block;
width: 100px; width: 100px;
} }
   
#flight-list div.distance, #flight-list div.distance,
#flight-list div.delay { #flight-list div.delay {
width: 160px; width: 160px;
padding-right: 10px; padding-right: 10px;
text-align: right; text-align: right;
} }
   
#flight-list .early { #flight-list .early {
color: green; color: green;
} }
   
aside { aside {
position: absolute; position: absolute;
left: 740px; left: 740px;
font-size: smaller; font-size: smaller;
width: 220px; width: 220px;
} }
   
</style> </style>
   
   
<div id="charts"> <div id="charts">
<div id="hour-chart" class="chart"> <div id="hour-chart" class="chart">
<div class="title">Time of Day</div> <div class="title">Time of Day</div>
</div> </div>
<div id="delay-chart" class="chart"> <div id="delay-chart" class="chart">
<div class="title">Arrival Delay (min.)</div> <div class="title">Arrival Delay (min.)</div>
</div> </div>
<div id="distance-chart" class="chart"> <div id="distance-chart" class="chart">
<div class="title">Distance (mi.)</div> <div class="title">Distance (mi.)</div>
</div> </div>
<div id="date-chart" class="chart"> <div id="date-chart" class="chart">
<div class="title">Date</div> <div class="title">Date</div>
</div> </div>
</div> </div>
   
<aside id="totals"><span id="active">-</span> of <span id="total">-</span> flights selected.</aside> <aside id="totals"><span id="active">-</span> of <span id="total">-</span> flights selected.</aside>
   
<div id="lists"> <div id="lists">
<div id="flight-list" class="list"></div> <div id="flight-list" class="list"></div>
</div> </div>
   
   
</div> </div>
   
   
<script src="../js/tesseract/tesseract.min.js"></script> <script src="../js/tesseract/tesseract.min.js"></script>
<script src="../js/d3/d3.v2.min.js"></script> <script src="../js/d3/d3.v2.min.js"></script>
<script> <script>
   
d3.csv("busdelay.csv.php", function(flights) { d3.csv("busdelay.csv.php", function(flights) {
   
// Various formatters. // Various formatters.
var formatNumber = d3.format(",d"), var formatNumber = d3.format(",d"),
formatChange = d3.format("+,d"), formatChange = d3.format("+,d"),
formatDate = d3.time.format("%B %d, %Y"), formatDate = d3.time.format("%B %d, %Y"),
formatTime = d3.time.format("%I:%M %p"); formatTime = d3.time.format("%I:%M %p");
   
// A nest operator, for grouping the flight list. // A nest operator, for grouping the flight list.
var nestByDate = d3.nest() var nestByDate = d3.nest()
.key(function(d) { return d3.time.day(d.date); }); .key(function(d) { return d3.time.day(d.date); });
   
// A little coercion, since the CSV is untyped. // A little coercion, since the CSV is untyped.
flights.forEach(function(d, i) { flights.forEach(function(d, i) {
d.index = i; d.index = i;
d.date = parseDate(d.date); d.date = parseDate(d.date);
d.delay = +d.delay; d.delay = +d.delay;
d.distance = +d.distance; d.distance = +d.distance;
}); });
   
// Create the tesseract and relevant dimensions and groups. // Create the tesseract and relevant dimensions and groups.
flight = tesseract(flights), flight = tesseract(flights),
all = flight.groupAll(), all = flight.groupAll(),
date = flight.dimension(function(d) { return d3.time.day(d.date); }), date = flight.dimension(function(d) { return d3.time.day(d.date); }),
dates = date.group(), dates = date.group(),
hour = flight.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }), hour = flight.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }),
hours = hour.group(Math.floor), hours = hour.group(Math.floor),
delay = flight.dimension(function(d) { return Math.max(-60, Math.min(149, d.delay)); }), //delay = flight.dimension(function(d) { return Math.max(-60, Math.min(149, d.delay)); }),
  delay = flight.dimension(function(d) { return d.delay; }),
delays = delay.group(function(d) { return Math.floor(d / 10) * 10; }), delays = delay.group(function(d) { return Math.floor(d / 10) * 10; }),
distance = flight.dimension(function(d) { return Math.min(90, d.distance); }), distance = flight.dimension(function(d) { return Math.min(60, d.distance); }),
distances = distance.group(function(d) { return Math.floor(d / 50) * 50; }); distances = distance.group(function(d) { return Math.floor(d / 50) * 50; });
   
var charts = [ var charts = [
   
barChart() barChart()
.dimension(hour) .dimension(hour)
.group(hours) .group(hours)
.x(d3.scale.linear() .x(d3.scale.linear()
.domain([0, 24]) .domain([0, 24])
.rangeRound([0, 10 * 24])), .rangeRound([0, 10 * 24])),
   
barChart() barChart()
.dimension(delay) .dimension(delay)
.group(delays) .group(delays)
.x(d3.scale.linear() .x(d3.scale.linear()
.domain([-60, 150]) .domain([-650, 650])
.rangeRound([0, 10 * 21])), .rangeRound([0, 10 * 21])),
   
barChart() barChart()
.dimension(distance) .dimension(distance)
.group(distances) .group(distances)
.x(d3.scale.linear() .x(d3.scale.linear()
.domain([0, 90]) .domain([0, 60])
.rangeRound([0, 10 * 40])), .rangeRound([0, 10 * 40])),
   
barChart() barChart()
.dimension(date) .dimension(date)
.group(dates) .group(dates)
.round(d3.time.day.round) .round(d3.time.day.round)
.x(d3.time.scale() .x(d3.time.scale()
.domain([new Date(2001, 0, 1), new Date(2001, 3, 1)]) .domain([new Date(2011, 4, 1), new Date(2012, 1, 4)])
.rangeRound([0, 10 * 90])) .rangeRound([0, 10 * 90]))
.filter([new Date(2001, 1, 1), new Date(2001, 2, 1)]) .filter([new Date(2011, 4, 4), new Date(2012, 4, 4)])
   
]; ];
   
// Given our array of charts, which we assume are in the same order as the // Given our array of charts, which we assume are in the same order as the
// .chart elements in the DOM, bind the charts to the DOM and render them. // .chart elements in the DOM, bind the charts to the DOM and render them.
// We also listen to the chart's brush events to update the display. // We also listen to the chart's brush events to update the display.
var chart = d3.selectAll(".chart") var chart = d3.selectAll(".chart")
.data(charts) .data(charts)
.each(function(chart) { chart.on("brush", renderAll).on("brushend", renderAll); }); .each(function(chart) { chart.on("brush", renderAll).on("brushend", renderAll); });
   
// Render the initial lists. // Render the initial lists.
var list = d3.selectAll(".list") var list = d3.selectAll(".list")
.data([flightList]); .data([flightList]);
   
// Render the total. // Render the total.
d3.selectAll("#total") d3.selectAll("#total")
.text(formatNumber(flight.size())); .text(formatNumber(flight.size()));
   
renderAll(); renderAll();
   
// Renders the specified chart or list. // Renders the specified chart or list.
function render(method) { function render(method) {
d3.select(this).call(method); d3.select(this).call(method);
} }
   
// Whenever the brush moves, re-rendering everything. // Whenever the brush moves, re-rendering everything.
function renderAll() { function renderAll() {
chart.each(render); chart.each(render);
list.each(render); list.each(render);
d3.select("#active").text(formatNumber(all.value())); d3.select("#active").text(formatNumber(all.value()));
} }