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()));
} }
   
// Like d3.time.format, but faster. // Like d3.time.format, but faster.
function parseDate(d) { function parseDate(d) {
return new Date(2001, return new Date(d);
d.substring(0, 2) - 1,  
d.substring(2, 4),  
d.substring(4, 6),  
d.substring(6, 8));  
} }
   
window.filter = function(filters) { window.filter = function(filters) {
filters.forEach(function(d, i) { charts[i].filter(d); }); filters.forEach(function(d, i) { charts[i].filter(d); });
renderAll(); renderAll();
}; };
   
window.reset = function(i) { window.reset = function(i) {
charts[i].filter(null); charts[i].filter(null);
renderAll(); renderAll();
}; };
   
function flightList(div) { function flightList(div) {
var flightsByDate = nestByDate.entries(date.top(40)); var flightsByDate = nestByDate.entries(date.top(40));
   
div.each(function() { div.each(function() {
var date = d3.select(this).selectAll(".date") var date = d3.select(this).selectAll(".date")
.data(flightsByDate, function(d) { return d.key; }); .data(flightsByDate, function(d) { return d.key; });
   
date.enter().append("div") date.enter().append("div")
.attr("class", "date") .attr("class", "date")
.append("div") .append("div")
.attr("class", "day") .attr("class", "day")
.text(function(d) { return formatDate(d.values[0].date); }); .text(function(d) { return formatDate(d.values[0].date); });
   
date.exit().remove(); date.exit().remove();
   
var flight = date.order().selectAll(".flight") var flight = date.order().selectAll(".flight")
.data(function(d) { return d.values; }, function(d) { return d.index; }); .data(function(d) { return d.values; }, function(d) { return d.index; });
   
var flightEnter = flight.enter().append("div") var flightEnter = flight.enter().append("div")
.attr("class", "flight"); .attr("class", "flight");
   
flightEnter.append("div") flightEnter.append("div")
.attr("class", "time") .attr("class", "time")
.text(function(d) { return formatTime(d.date); }); .text(function(d) { return formatTime(d.date); });
   
flightEnter.append("div") flightEnter.append("div")
.attr("class", "origin") .attr("class", "origin")
.text(function(d) { return d.origin; }); .text(function(d) { return d.origin; });
   
flightEnter.append("div") flightEnter.append("div")
.attr("class", "destination") .attr("class", "destination")
.text(function(d) { return d.destination; }); .text(function(d) { return d.destination; });
   
flightEnter.append("div") flightEnter.append("div")
.attr("class", "distance") .attr("class", "distance")
.text(function(d) { return formatNumber(d.distance) + " mi."; }); .text(function(d) { return formatNumber(d.distance) + " mi."; });
   
flightEnter.append("div") flightEnter.append("div")
.attr("class", "delay") .attr("class", "delay")
.classed("early", function(d) { return d.delay < 0; }) .classed("early", function(d) { return d.delay < 0; })
.text(function(d) { return formatChange(d.delay) + " min."; }); .text(function(d) { return formatChange(d.delay) + " min."; });
   
flight.exit().remove(); flight.exit().remove();
   
flight.order(); flight.order();
}); });
} }
   
function barChart() { function barChart() {
if (!barChart.id) barChart.id = 0; if (!barChart.id) barChart.id = 0;
   
var margin = {top: 10, right: 10, bottom: 20, left: 10}, var margin = {top: 10, right: 10, bottom: 20, left: 10},
x, x,
y = d3.scale.linear().range([100, 0]), y = d3.scale.linear().range([100, 0]),
id = barChart.id++, id = barChart.id++,
axis = d3.svg.axis().orient("bottom"), axis = d3.svg.axis().orient("bottom"),
brush = d3.svg.brush(), brush = d3.svg.brush(),
brushDirty, brushDirty,
dimension, dimension,
group, group,
round; round;
   
function chart(div) { function chart(div) {
var width = x.range()[1], var width = x.range()[1],
height = y.range()[0]; height = y.range()[0];
   
y.domain([0, group.top(1)[0].value]); y.domain([0, group.top(1)[0].value]);
   
div.each(function() { div.each(function() {
var div = d3.select(this), var div = d3.select(this),
g = div.select("g"); g = div.select("g");
   
// Create the skeletal chart. // Create the skeletal chart.
if (g.empty()) { if (g.empty()) {
div.select(".title").append("a") div.select(".title").append("a")
.attr("href", "javascript:reset(" + id + ")") .attr("href", "javascript:reset(" + id + ")")
.attr("class", "reset") .attr("class", "reset")
.text("reset") .text("reset")
.style("display", "none"); .style("display", "none");
   
g = div.append("svg") g = div.append("svg")
.attr("width", width + margin.left + margin.right) .attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom) .attr("height", height + margin.top + margin.bottom)
.append("g") .append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
   
g.append("clipPath") g.append("clipPath")
.attr("id", "clip-" + id) .attr("id", "clip-" + id)
.append("rect") .append("rect")
.attr("width", width) .attr("width", width)
.attr("height", height); .attr("height", height);
   
g.selectAll(".bar") g.selectAll(".bar")
.data(["background", "foreground"]) .data(["background", "foreground"])
.enter().append("path") .enter().append("path")
.attr("class", function(d) { return d + " bar"; }) .attr("class", function(d) { return d + " bar"; })
.datum(group.all()); .datum(group.all());
   
g.selectAll(".foreground.bar") g.selectAll(".foreground.bar")
.attr("clip-path", "url(#clip-" + id + ")"); .attr("clip-path", "url(#clip-" + id + ")");
   
g.append("g") g.append("g")
.attr("class", "axis") .attr("class", "axis")
.attr("transform", "translate(0," + height + ")") .attr("transform", "translate(0," + height + ")")
.call(axis); .call(axis);
   
// Initialize the brush component with pretty resize handles. // Initialize the brush component with pretty resize handles.
var gBrush = g.append("g").attr("class", "brush").call(brush); var gBrush = g.append("g").attr("class", "brush").call(brush);
gBrush.selectAll("rect").attr("height", height); gBrush.selectAll("rect").attr("height", height);
gBrush.selectAll(".resize").append("path").attr("d", resizePath); gBrush.selectAll(".resize").append("path").attr("d", resizePath);
} }
   
// Only redraw the brush if set externally. // Only redraw the brush if set externally.
if (brushDirty) { if (brushDirty) {
brushDirty = false; brushDirty = false;
g.selectAll(".brush").call(brush); g.selectAll(".brush").call(brush);
div.select(".title a").style("display", brush.empty() ? "none" : null); div.select(".title a").style("display", brush.empty() ? "none" : null);
if (brush.empty()) { if (brush.empty()) {
g.selectAll("#clip-" + id + " rect") g.selectAll("#clip-" + id + " rect")
.attr("x", 0) .attr("x", 0)
.attr("width", width); .attr("width", width);
} else { } else {
var extent = brush.extent(); var extent = brush.extent();
g.selectAll("#clip-" + id + " rect") g.selectAll("#clip-" + id + " rect")
.attr("x", x(extent[0])) .attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0])); .attr("width", x(extent[1]) - x(extent[0]));
} }
} }
   
g.selectAll(".bar").attr("d", barPath); g.selectAll(".bar").attr("d", barPath);
}); });
   
function barPath(groups) { function barPath(groups) {
var path = [], var path = [],
i = -1, i = -1,
n = groups.length, n = groups.length,
d; d;
while (++i < n) { while (++i < n) {
d = groups[i]; d = groups[i];
path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height); path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
} }
return path.join(""); return path.join("");
} }
   
function resizePath(d) { function resizePath(d) {
var e = +(d == "e"), var e = +(d == "e"),
x = e ? 1 : -1, x = e ? 1 : -1,
y = height / 3; y = height / 3;
return "M" + (.5 * x) + "," + y return "M" + (.5 * x) + "," + y
+ "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6) + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
+ "V" + (2 * y - 6) + "V" + (2 * y - 6)
+ "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y) + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
+ "Z" + "Z"
+ "M" + (2.5 * x) + "," + (y + 8) + "M" + (2.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8) + "V" + (2 * y - 8)
+ "M" + (4.5 * x) + "," + (y + 8) + "M" + (4.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8); + "V" + (2 * y - 8);
} }
} }
   
brush.on("brushstart.chart", function() { brush.on("brushstart.chart", function() {
var div = d3.select(this.parentNode.parentNode.parentNode); var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", null); div.select(".title a").style("display", null);
}); });
   
brush.on("brush.chart", function() { brush.on("brush.chart", function() {
var g = d3.select(this.parentNode), var g = d3.select(this.parentNode),
extent = brush.extent(); extent = brush.extent();
if (round) g.select(".brush") if (round) g.select(".brush")
.call(brush.extent(extent = extent.map(round))) .call(brush.extent(extent = extent.map(round)))
.selectAll(".resize") .selectAll(".resize")
.style("display", null); .style("display", null);
g.select("#clip-" + id + " rect") g.select("#clip-" + id + " rect")
.attr("x", x(extent[0])) .attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0])); .attr("width", x(extent[1]) - x(extent[0]));
dimension.filterRange(extent); dimension.filterRange(extent);
}); });
   
brush.on("brushend.chart", function() { brush.on("brushend.chart", function() {
if (brush.empty()) { if (brush.empty()) {
var div = d3.select(this.parentNode.parentNode.parentNode); var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", "none"); div.select(".title a").style("display", "none");
div.select("#clip-" + id + " rect").attr("x", null).attr("width", "100%"); div.select("#clip-" + id + " rect").attr("x", null).attr("width", "100%");
dimension.filterAll(); dimension.filterAll();
} }
}); });
   
chart.margin = function(_) { chart.margin = function(_) {
if (!arguments.length) return margin; if (!arguments.length) return margin;
margin = _; margin = _;
return chart; return chart;
}; };
   
chart.x = function(_) { chart.x = function(_) {
if (!arguments.length) return x; if (!arguments.length) return x;
x = _; x = _;
axis.scale(x); axis.scale(x);
brush.x(x); brush.x(x);
return chart; return chart;
}; };
   
chart.y = function(_) { chart.y = function(_) {
if (!arguments.length) return y; if (!arguments.length) return y;
y = _; y = _;
return chart; return chart;
}; };
   
chart.dimension = function(_) { chart.dimension = function(_) {
if (!arguments.length) return dimension; if (!arguments.length) return dimension;
dimension = _; dimension = _;
return chart; return chart;
}; };
   
chart.filter = function(_) { chart.filter = function(_) {
if (_) { if (_) {
brush.extent(_); brush.extent(_);
dimension.filterRange(_); dimension.filterRange(_);
} else { } else {
brush.clear(); brush.clear();
dimension.filterAll(); dimension.filterAll();
} }
brushDirty = true; brushDirty = true;
return chart; return chart;
}; };
   
chart.group = function(_) { chart.group = function(_) {
if (!arguments.length) return group; if (!arguments.length) return group;
group = _; group = _;
return chart; return chart;
}; };
   
chart.round = function(_) { chart.round = function(_) {
if (!arguments.length) return round; if (!arguments.length) return round;
round = _; round = _;
return chart; return chart;
}; };
   
return d3.rebind(chart, brush, "on"); return d3.rebind(chart, brush, "on");
} }
}); });
   
</script> </script>
   
file:a/stop.php -> file:b/stop.php
<?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.
*/ */
include ('include/common.inc.php'); include ('include/common.inc.php');
if (isset($stopid)) { if (isset($stopid)) {
$stop = getStop($stopid); $stop = getStop($stopid);
} }
/* if ($stopcode != "" && $stop[5] != $stopcode) { /* if ($stopcode != "" && $stop[5] != $stopcode) {
$url = $APIurl . "/json/stopcodesearch?q=" . $stopcode; $url = $APIurl . "/json/stopcodesearch?q=" . $stopcode;
$stopsearch = json_decode(getPage($url)); $stopsearch = json_decode(getPage($url));
$stopid = $stopsearch[0][0]; $stopid = $stopsearch[0][0];
$url = $APIurl . "/json/stop?stop_id=" . $stopid; $url = $APIurl . "/json/stop?stop_id=" . $stopid;
$stop = json_decode(getPage($url)); $stop = json_decode(getPage($url));
} }
if (!startsWith($stop[5], "Wj") && strpos($stop[1], "Platform") === false) { if (!startsWith($stop[5], "Wj") && strpos($stop[1], "Platform") === false) {
// expand out to all platforms // expand out to all platforms
   
} */ } */
   
$stops = Array(); $stops = Array();
$stopPositions = Array(); $stopPositions = Array();
$stopNames = Array(); $stopNames = Array();
$tripStopNumbers = Array(); $tripStopNumbers = Array();
$allStopsTrips = Array(); $allStopsTrips = Array();
$fetchedTripSequences = Array(); $fetchedTripSequences = Array();
$stopLinks = ""; $stopLinks = "";
if (isset($stopids)) { if (isset($stopids)) {
foreach ($stopids as $sub_stopid) { foreach ($stopids as $sub_stopid) {
$stops[] = getStop($sub_stopid); $stops[] = getStop($sub_stopid);
} }
$stop = $stops[0]; $stop = $stops[0];
$stopid = $stops[0]["stop_id"]; $stopid = $stops[0]["stop_id"];
$stopLinks.= "Individual stop pages: <br>"; $stopLinks.= "Individual stop pages: <br>";
foreach ($stops as $key => $sub_stop) { foreach ($stops as $key => $sub_stop) {
   
$stopNames[$key] = $sub_stop["stop_name"]; $stopNames[$key] = $sub_stop["stop_name"];
$stopLinks.= '<a href="stop.php?stopid=' . $sub_stop["stop_id"] . '&amp;stopcode=' . $sub_stop["stop_code"] . '">' . $sub_stop["stop_name"] . '</a> '; $stopLinks.= '<a href="stop.php?stopid=' . $sub_stop["stop_id"] . '&amp;stopcode=' . $sub_stop["stop_code"] . '">' . $sub_stop["stop_name"] . '</a> ';
   
$stopPositions[$key] = Array( $stopPositions[$key] = Array(
$sub_stop["stop_lat"], $sub_stop["stop_lat"],
$sub_stop["stop_lon"] $sub_stop["stop_lon"]
); );
$trips = getStopTrips($sub_stop["stop_id"]); $trips = getStopTrips($sub_stop["stop_id"]);
$tripSequence = ""; $tripSequence = "";
foreach ($trips as $trip) { foreach ($trips as $trip) {
$tripSequence.= "{$trip['trip_id']},"; $tripSequence.= "{$trip['trip_id']},";
$tripStopNumbers[$trip['trip_id']][] = $key; $tripStopNumbers[$trip['trip_id']][] = $key;
} }
if (!in_array($tripSequence, $fetchedTripSequences)) { if (!in_array($tripSequence, $fetchedTripSequences)) {
// only fetch new trip sequences // only fetch new trip sequences
$fetchedTripSequences[] = $tripSequence; $fetchedTripSequences[] = $tripSequence;
$trips = getStopTripsWithTimes($sub_stop["stop_id"]); $trips = getStopTripsWithTimes($sub_stop["stop_id"]);
foreach ($trips as $trip) { foreach ($trips as $trip) {
if (!isset($allStopsTrips[$trip["trip_id"]])) if (!isset($allStopsTrips[$trip["trip_id"]]))
$allStopsTrips[$trip["trip_id"]] = $trip; $allStopsTrips[$trip["trip_id"]] = $trip;
} }
} }
//else { //else {
// echo "skipped sequence $tripSequence"; // echo "skipped sequence $tripSequence";
//} //}
} }
} }
if (sizeof($stops) > 0) { if (sizeof($stops) > 0) {
$stopDescParts = explode("<br>", $stop['stop_desc']); $stopDescParts = explode("<br>", $stop['stop_desc']);
include_header(trim(str_replace("Street: ", "", $stopDescParts[0])), "stop"); include_header(trim(str_replace("Street: ", "", $stopDescParts[0])), "stop");
} else { } else {
include_header($stop['stop_name'], "stop"); include_header($stop['stop_name'], "stop");
} }
/* $serviceAlerts = json_decode(getPage(curPageURL() . "/servicealerts_api.php?filter_class=stop&filter_id=".$stopid) , true); /* $serviceAlerts = json_decode(getPage(curPageURL() . "/servicealerts_api.php?filter_class=stop&filter_id=".$stopid) , true);
   
foreach($serviceAlerts['entities'] as $serviceAlert) { foreach($serviceAlerts['entities'] as $serviceAlert) {
echo '<div id="servicewarning">'.$serviceAlert['alert']['description']['translation'].'</div>'; echo '<div id="servicewarning">'.$serviceAlert['alert']['description']['translation'].'</div>';
} */ } */
   
echo '<div class="content-secondary">'; echo '<div class="content-secondary">';
echo $stopLinks; echo $stopLinks;
if (sizeof($stops) > 0) { if (sizeof($stops) > 0) {
trackEvent("View Stops", "View Combined Stops", $stop["stop_name"], $stop["stop_id"]); trackEvent("View Stops", "View Combined Stops", $stop["stop_name"], $stop["stop_id"]);
echo staticmap($stopPositions); echo staticmap($stopPositions);
} else { } else {
trackEvent("View Stops", "View Single Stop", $stop["stop_name"], $stop["stop_id"]); trackEvent("View Stops", "View Single Stop", $stop["stop_name"], $stop["stop_id"]);
echo staticmap(Array( echo staticmap(Array(
0 => Array( 0 => Array(
$stop["stop_lat"], $stop["stop_lat"],
$stop["stop_lon"] $stop["stop_lon"]
) )
)); ));
} }
   
timeSettings(); timeSettings();
   
echo '</div><div class="content-primary">'; echo '</div><div class="content-primary">';
echo ' <ul data-role="listview" data-inset="true">'; echo ' <ul data-role="listview" data-inset="true">';
if (sizeof($allStopsTrips) > 0) { if (sizeof($allStopsTrips) > 0) {
sktimesort($allStopsTrips, "arrival_time", true); sktimesort($allStopsTrips, "arrival_time", true);
$trips = $allStopsTrips; $trips = $allStopsTrips;
} else { } else {
$trips = getStopTripsWithTimes($stopid, "", "", "", (isset($filterIncludeRoutes) || isset($filterHasStop) ? "75" : "")); $trips = getStopTripsWithTimes($stopid, "", "", "", (isset($filterIncludeRoutes) || isset($filterHasStop) ? "75" : ""));
} }
   
echo "<div class='ui-header' style='overflow: visible; height: 2.5em'>"; echo "<div class='ui-header' style='overflow: visible; height: 2.5em'>";
// if we have too many trips, cut down to size. // if we have too many trips, cut down to size.
if (!isset($filterIncludeRoutes) && !isset($filterHasStop) && sizeof($trips) > 10) { if (!isset($filterIncludeRoutes) && !isset($filterHasStop) && sizeof($trips) > 10) {
$trips = array_splice($trips, 0, 10); $trips = array_splice($trips, 0, 10);
} }
   
// later/earlier button setup // later/earlier button setup
if (sizeof($trips) == 0) { if (sizeof($trips) == 0) {
$time = isset($_REQUEST['time']) ? strtotime($_REQUEST['time']) : time(); $time = isset($_REQUEST['time']) ? strtotime($_REQUEST['time']) : time();
$earlierTime = $time - (90 * 60); $earlierTime = $time - (90 * 60);
$laterTime = $time + (90 * 60); $laterTime = $time + (90 * 60);
} else { } else {
$tripsKeys = array_keys($trips); $tripsKeys = array_keys($trips);
$earlierTime = strtotime($trips[$tripsKeys[0]]['arrival_time']) - (90 * 60); $earlierTime = strtotime($trips[$tripsKeys[0]]['arrival_time']) - (90 * 60);
$laterTime = strtotime($trips[$tripsKeys[sizeof($trips) - 1]]['arrival_time']) - 60; $laterTime = strtotime($trips[$tripsKeys[sizeof($trips) - 1]]['arrival_time']) - 60;
} }
if (isset($stopids) && sizeof($stopids) > 0) { if (isset($stopids) && sizeof($stopids) > 0) {
$stopidurl = "stopids=" . implode(",", $stopids); $stopidurl = "stopids=" . implode(",", $stopids);
} else { } else {
$stopidurl = "stopid=$stopid"; $stopidurl = "stopid=$stopid";
} }
if (sizeof($trips) >= 10) { if (sizeof($trips) >= 10) {
echo '<a href="stop.php?' . $stopidurl . '&service_period=' . service_period() . '&time=' . date("H:i", $laterTime) . '" data-icon="arrow-r" class="ui-btn-right">Later Trips</a>'; echo '<a href="stop.php?' . $stopidurl . '&service_period=' . service_period() . '&time=' . date("H:i", $laterTime) . '" data-icon="arrow-r" class="ui-btn-right">Later Trips</a>';
} }
echo '<a href="stop.php?' . $stopidurl . '&service_period=' . service_period() . '&time=' . date("H:i", $earlierTime) . '" data-icon="arrow-l" class="ui-btn-left">Earlier Trips</a>'; echo '<a href="stop.php?' . $stopidurl . '&service_period=' . service_period() . '&time=' . date("H:i", $earlierTime) . '" data-icon="arrow-l" class="ui-btn-left">Earlier Trips</a>';
echo "</div>"; echo "</div>";
if (sizeof($trips) == 0) { if (sizeof($trips) == 0) {
echo "<li style='text-align: center;'>No trips in the near future.</li>"; echo "<li style='text-align: center;'>No trips in the near future.</li>";
} else { } else {
  if ($labs) {
  // ETA calculation
   
  $tripETA = Array();
  // max/min delay instead of stddev?
  $query = $query = "select 'lol', avg(timing_delta), stddev(timing_delta), count(*) from myway_timingdeltas where extract(hour from time) between ".date("H", $earlierTime)." and ".date("H", $laterTime);
  //select 'lol', stop_id,extract(hour from time), avg(timing_delta), stddev(timing_delta), count(*) from myway_timingdeltas where stop_id = '5501' group by stop_id, extract(hour from time) order by extract(hour from time)
  $query = $conn->prepare($query);
  $query->execute();
  if (!$query) {
  databaseError($conn->errorInfo());
  return Array();
  }
  $ETAparams = Array();
  foreach ($query->fetchAll() as $row) {
  $ETAparams[$row[0]] = Array("avg"=> $row[1], "stddev"=>floor($row[2]),"count"=>$row[3]);
  };
  //print_r($ETAparams);
  foreach ($trips as $trip) {
  $tripETA[$trip['trip_id']] = date("H:i",strtotime($trip['arrival_time']." - ".(floor($ETAparams['lol']['stddev']))." seconds"))." to ".
  date("H:i",strtotime($trip['arrival_time']." + ".(floor($ETAparams['lol']['stddev']))." seconds"));
  }
  //print_r($tripETA);
  }
foreach ($trips as $trip) { foreach ($trips as $trip) {
if ( if (
isset($filterHasStop) && (getTripHasStop($trip['trip_id'], $filterHasStop) == 1) isset($filterHasStop) && (getTripHasStop($trip['trip_id'], $filterHasStop) == 1)
|| (isset($filterIncludeRoutes) && in_array($trip["route_short_name"], $filterIncludeRoutes)) || (isset($filterIncludeRoutes) && in_array($trip["route_short_name"], $filterIncludeRoutes))
|| (!isset($filterIncludeRoutes) && !isset($filterHasStop)) || (!isset($filterIncludeRoutes) && !isset($filterHasStop))
) { ) {
echo '<li>'; echo '<li>';
   
$destination = getTripDestination($trip['trip_id']); $destination = getTripDestination($trip['trip_id']);
echo '<a href="trip.php?stopid=' . $stopid . '&amp;tripid=' . $trip['trip_id'] . '"><h3>' . $trip['route_short_name'] . " towards " . $destination['stop_name'] . "</h3><p>"; echo '<a href="trip.php?stopid=' . $stopid . '&amp;tripid=' . $trip['trip_id'] . '"><h3>' . $trip['route_short_name'] . " towards " . $destination['stop_name'] . "</h3><p>";
$viaPoints = viaPointNames($trip['trip_id'], $trip['stop_sequence']); $viaPoints = viaPointNames($trip['trip_id'], $trip['stop_sequence']);
  if ($labs) {
  echo '<br><span class="eta">ETA: ' . $tripETA[$trip['trip_id']] . '</span>';
  }
if ($viaPoints != "") if ($viaPoints != "")
echo '<br><span class="viaPoints">Via: ' . $viaPoints . '</span>'; echo '<br><span class="viaPoints">Via: ' . $viaPoints . '</span>';
if (sizeof($tripStopNumbers) > 0) { if (sizeof($tripStopNumbers) > 0) {
echo '<br><small>Boarding At: '; echo '<br><small>Boarding At: ';
if (sizeof($tripStopNumbers[$trip['trip_id']]) == sizeof($stopids)) { if (sizeof($tripStopNumbers[$trip['trip_id']]) == sizeof($stopids)) {
echo "All Stops"; echo "All Stops";
} else { } else {
foreach ($tripStopNumbers[$trip['trip_id']] as $key) { foreach ($tripStopNumbers[$trip['trip_id']] as $key) {
echo $stopNames[$key] . ', '; echo $stopNames[$key] . ', ';
} }
} }
echo '</small>'; echo '</small>';
} }
echo '</p>'; echo '</p>';
echo '<p class="ui-li-aside"><strong>' . $trip['arrival_time'] . '</strong></p>'; echo '<p class="ui-li-aside"><strong>' . $trip['arrival_time'] . '</strong></p>';
echo '</a></li>'; echo '</a></li>';
flush(); flush();
@ob_flush(); @ob_flush();
} }
} }
} }
echo '</ul>'; echo '</ul>';
echo '</div>'; echo '</div>';
include_footer(); include_footer();
?> ?>