<!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. |