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

[submodule "js/flotr2"] [submodule "js/flotr2"]
path = js/flotr2 path = js/flotr2
url = https://github.com/HumbleSoftware/Flotr2.git url = https://github.com/HumbleSoftware/Flotr2.git
[submodule "js/FlashCanvas"] [submodule "js/FlashCanvas"]
path = js/FlashCanvas path = js/FlashCanvas
url = https://github.com/timcameronryan/FlashCanvas url = https://github.com/timcameronryan/FlashCanvas
[submodule "lib/amon-php"] [submodule "lib/amon-php"]
path = lib/amon-php path = lib/amon-php
url = https://github.com/martinrusev/amon-php.git url = https://github.com/martinrusev/amon-php.git
[submodule "js/yepnope"] [submodule "js/yepnope"]
path = js/yepnope path = js/yepnope
url = https://github.com/SlexAxton/yepnope.js.git url = https://github.com/SlexAxton/yepnope.js.git
  [submodule "javascripts/tesseract"]
  path = javascripts/tesseract
  url = https://github.com/square/tesseract.git
  [submodule "javascripts/d3"]
  path = javascripts/d3
  url = https://github.com/mbostock/d3.git
<?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
 
  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
  include ('../include/common.inc.php');
  $query = $conn->prepare('
  SELECT * from myway_timingdeltas'
  , array(PDO::ATTR_CURSOR => PDO::FETCH_ORI_NEXT));
  $query->execute();
  $errors = $conn->errorInfo();
  if ($errors[2] != "") {
  die("Export terminated, db error" . print_r($errors, true));
  }
 
  $headers = Array("date", "delay", "distance", "origin", "destination");
 
  $fp = fopen('php://output', 'w');
  if ($fp && $query) {
  //header('Content-Type: text/csv');
  header('Pragma: no-cache');
  header('Expires: 0');
  fputcsv($fp, $headers);
  while ($r = $query->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) {
  $row = Array();
  foreach ($headers as $i => $fieldName) {
  switch ($fieldName) {
  case "date":
  $row[] = date("r",strtotime($r['date']." ".$r['time']));
  break;
  case "delay":
  $row[] = $r['timing_delta'];
  break;
  case "distance":
  $row[] = $r['stop_sequence'];
  break;
  case "origin":
  $row[] = $r['myway_stop'];
  break;
  case "destination":
  $row[] = $r['route_name'];
  break;
  default:
  break;
  }
  }
  fputcsv($fp, array_values($row));
  }
  die;
  }
  ?>
 
 
file:b/labs/busdelay.php (new)
  <!DOCTYPE html>
  <meta charset="utf-8">
  <title>Tesseract</title>
  <style>
 
 
  #charts {
  padding: 10px 0;
  }
 
  .chart {
  display: inline-block;
  height: 151px;
  margin-bottom: 20px;
  }
 
  .reset {
  padding-left: 1em;
  font-size: smaller;
  color: #ccc;
  }
 
  .background.bar {
  fill: #ccc;
  }
 
  .foreground.bar {
  fill: steelblue;
  }
 
  .axis path, .axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
  }
 
  .axis text {
  font: 10px sans-serif;
  }
 
  .brush rect.extent {
  fill: steelblue;
  fill-opacity: .125;
  }
 
  .brush .resize path {
  fill: #eee;
  stroke: #666;
  }
 
  #hour-chart {
  width: 260px;
  }
 
  #delay-chart {
  width: 230px;
  }
 
  #distance-chart {
  width: 420px;
  }
 
  #date-chart {
  width: 920px;
  }
 
  #flight-list {
  min-height: 1024px;
  }
 
  #flight-list .date,
  #flight-list .day {
  margin-bottom: .4em;
  }
 
  #flight-list .flight {
  line-height: 1.5em;
  background: #eee;
  width: 640px;
  margin-bottom: 1px;
  }
 
  #flight-list .time {
  color: #999;
  }
 
  #flight-list .flight div {
  display: inline-block;
  width: 100px;
  }
 
  #flight-list div.distance,
  #flight-list div.delay {
  width: 160px;
  padding-right: 10px;
  text-align: right;
  }
 
  #flight-list .early {
  color: green;
  }
 
  aside {
  position: absolute;
  left: 740px;
  font-size: smaller;
  width: 220px;
  }
 
  </style>
 
 
  <div id="charts">
  <div id="hour-chart" class="chart">
  <div class="title">Time of Day</div>
  </div>
  <div id="delay-chart" class="chart">
  <div class="title">Arrival Delay (min.)</div>
  </div>
  <div id="distance-chart" class="chart">
  <div class="title">Distance (mi.)</div>
  </div>
  <div id="date-chart" class="chart">
  <div class="title">Date</div>
  </div>
  </div>
 
  <aside id="totals"><span id="active">-</span> of <span id="total">-</span> flights selected.</aside>
 
  <div id="lists">
  <div id="flight-list" class="list"></div>
  </div>
 
 
  </div>
 
 
  <script src="../js/tesseract/tesseract.min.js"></script>
  <script src="../js/d3/d3.v2.min.js"></script>
  <script>
 
  d3.csv("busdelay.csv.php", function(flights) {
 
  // Various formatters.
  var formatNumber = d3.format(",d"),
  formatChange = d3.format("+,d"),
  formatDate = d3.time.format("%B %d, %Y"),
  formatTime = d3.time.format("%I:%M %p");
 
  // A nest operator, for grouping the flight list.
  var nestByDate = d3.nest()
  .key(function(d) { return d3.time.day(d.date); });
 
  // A little coercion, since the CSV is untyped.
  flights.forEach(function(d, i) {
  d.index = i;
  d.date = parseDate(d.date);
  d.delay = +d.delay;
  d.distance = +d.distance;
  });
 
  // Create the tesseract and relevant dimensions and groups.
  flight = tesseract(flights),
  all = flight.groupAll(),
  date = flight.dimension(function(d) { return d3.time.day(d.date); }),
  dates = date.group(),
  hour = flight.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }),
  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 d.delay; }),
  delays = delay.group(function(d) { return Math.floor(d / 10) * 10; }),
  distance = flight.dimension(function(d) { return Math.min(60, d.distance); }),
  distances = distance.group(function(d) { return Math.floor(d / 50) * 50; });
 
  var charts = [
 
  barChart()
  .dimension(hour)
  .group(hours)
  .x(d3.scale.linear()
  .domain([0, 24])
  .rangeRound([0, 10 * 24])),
 
  barChart()
  .dimension(delay)
  .group(delays)
  .x(d3.scale.linear()
  .domain([-650, 650])
  .rangeRound([0, 10 * 21])),
 
  barChart()
  .dimension(distance)
  .group(distances)
  .x(d3.scale.linear()
  .domain([0, 60])
  .rangeRound([0, 10 * 40])),
 
  barChart()
  .dimension(date)
  .group(dates)
  .round(d3.time.day.round)
  .x(d3.time.scale()
  .domain([new Date(2011, 4, 1), new Date(2012, 1, 4)])
  .rangeRound([0, 10 * 90]))
  .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
  // .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.
  var chart = d3.selectAll(".chart")
  .data(charts)
  .each(function(chart) { chart.on("brush", renderAll).on("brushend", renderAll); });
 
  // Render the initial lists.
  var list = d3.selectAll(".list")
  .data([flightList]);
 
  // Render the total.
  d3.selectAll("#total")
  .text(formatNumber(flight.size()));
 
  renderAll();
 
  // Renders the specified chart or list.
  function render(method) {
  d3.select(this).call(method);
  }
 
  // Whenever the brush moves, re-rendering everything.
  function renderAll() {
  chart.each(render);
  list.each(render);
  d3.select("#active").text(formatNumber(all.value()));
  }
 
  // Like d3.time.format, but faster.
  function parseDate(d) {
  return new Date(d);
  }
 
  window.filter = function(filters) {
  filters.forEach(function(d, i) { charts[i].filter(d); });
  renderAll();
  };
 
  window.reset = function(i) {
  charts[i].filter(null);
  renderAll();
  };
 
  function flightList(div) {
  var flightsByDate = nestByDate.entries(date.top(40));
 
  div.each(function() {
  var date = d3.select(this).selectAll(".date")
  .data(flightsByDate, function(d) { return d.key; });
 
  date.enter().append("div&