Created
December 22, 2011 08:27
-
-
Save cpudney/1509502 to your computer and use it in GitHub Desktop.
D3 Lap Chart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"lapCount": 58, | |
"laps": [ | |
{ | |
"name": "Sebastian Vettel", | |
"placing": [1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], | |
"pitstops": [9], | |
"mechanical": [25] | |
}, | |
{ | |
"name": "Mark Webber", | |
"placing": [2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 9], | |
"pitstops": [10, 32, 56] | |
}, | |
{ | |
"name": "Fernando Alonso", | |
"placing": [3, 18, 18, 18, 18, 15, 13, 13, 15, 13, 10, 10, 10, 9, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], | |
"pitstops": [8], | |
"accident": [0] | |
}, | |
{ | |
"name": "Jenson Button", | |
"placing": [4, 6, 6, 6, 6, 6, 19, 19, 12, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], | |
"pitstops": [6, 34], | |
"accident": [0] | |
}, | |
{ | |
"name": "Felipe Massa", | |
"placing": [5, 2, 2, 2, 2, 2, 3, 3, 9, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], | |
"pitstops": [8] | |
}, | |
{ | |
"name": "Nico Rosberg", | |
"placing": [6, 5, 5, 5, 5, 5, 5, 5, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5], | |
"pitstops": [8, 33] | |
}, | |
{ | |
"name": "Michael Schumacher", | |
"placing": [7], | |
"accident": [0] | |
}, | |
{ | |
"name": "Rubens Barrichello", | |
"placing": [8, 9, 9, 9, 9, 9, 8, 7, 10, 8, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8], | |
"pitstops": [8, 31], | |
"accident": [55] | |
}, | |
{ | |
"name": "Robert Kubica", | |
"placing": [9, 4, 4, 4, 4, 4, 4, 4, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], | |
"pitstops": [8] | |
}, | |
{ | |
"name": "Adrian Sutil", | |
"placing": [10, 8, 8, 8, 8, 8, 7, 9, 5, 2], | |
"mechanical": [9] | |
}, | |
{ | |
"name": "Lewis Hamilton", | |
"placing": [11, 7, 7, 7, 7, 7, 6, 6, 11, 11, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6], | |
"pitstops": [8, 34], | |
"accident": [55] | |
}, | |
{ | |
"name": "Sebastien Buemi", | |
"placing": [12], | |
"accident": [0] | |
}, | |
{ | |
"name": "Vitantonio Liuzzi", | |
"placing": [13, 12, 12, 12, 12, 12, 11, 11, 3, 9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7], | |
"pitstops": [9] | |
}, | |
{ | |
"name": "Pedro De La Rosa", | |
"placing": [14, 11, 11, 11, 11, 11, 10, 10, 13, 12, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 12, 12], | |
"pitstops": [8] | |
}, | |
{ | |
"name": "Nico Hulkenberg", | |
"placing": [15], | |
"accident": [0] | |
}, | |
{ | |
"name": "Kamui Kobayashi", | |
"placing": [16], | |
"accident": [0] | |
}, | |
{ | |
"name": "Jaime Alguersuari", | |
"placing": [17, 13, 13, 13, 13, 13, 12, 12, 4, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 11, 11], | |
"pitstops": [9, 27] | |
}, | |
{ | |
"name": "Vitaly Petrov", | |
"placing": [18, 10, 10, 10, 10, 10, 9, 8, 14, 14], | |
"pitstops": [8], | |
"accident": [9] | |
}, | |
{ | |
"name": "Heikki Kovalainen", | |
"placing": [19, 15, 15, 15, 14, 14, 14, 15, 18, 18, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13], | |
"pitstops": [8] | |
}, | |
{ | |
"name": "Jarno Trulli", | |
"placing": [20, 20, 20, 20, 20, 17, 15, 14, 17, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10], | |
"pitstops": [1, 8, 29], | |
"mechanical": [0] | |
}, | |
{ | |
"name": "Bruno Senna", | |
"placing": [21, 14, 14, 14, 15], | |
"mechanical": [4] | |
}, | |
{ | |
"name": "Karun Chandock", | |
"placing": [22, 16, 16, 16, 16, 19, 18, 18, 19, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14], | |
"pitstops": [8, 46] | |
}, | |
{ | |
"name": "Timo Glock", | |
"placing": [23, 17, 17, 17, 17, 16, 16, 16, 8, 17, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14], | |
"pitstops": [8, 9], | |
"mechanical": [41] | |
}, | |
{ | |
"name": "Luca di Grassi", | |
"placing": [24, 19, 19, 19, 19, 18, 17, 17, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 17, 16], | |
"pitstops": [10, 25], | |
"mechanical": [26] | |
} | |
], | |
"lapped": [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 17, 16, 16, 16, 16, 16, 16, 16,15, 15, 15, 14, 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13], | |
"safety": [1, 2, 3, 4] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | |
"http://www.w3.org/TR/html4/loose.dtd"> | |
<html> | |
<head> | |
<title>Formula 1 Lap Chart</title> | |
<meta http-equiv="X-UA-Compatible" content="chrome=1"> | |
<link href="style.css" media="screen" rel="stylesheet" type="text/css"/> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.7.0"></script> | |
</head> | |
<body> | |
<span class="title">Australian Formula 1 Grand Prix, 2010 | |
<a href="http://creativecommons.org/licenses/by-sa/3.0/" target="_blank"><img align="top" | |
alt="Creative Commons License" | |
src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png"/></a> | |
</span> | |
<span class="attrib">By <a href="http://vislives.com/" target="_blank">Chris Pudney</a></span> | |
<div id="chart"></div> | |
<span class="legend">M = mechanical failure | P = pit stop | X = accident | <span | |
class="safety">safety-car deployed</span> | <span class="lapped">lapped</span></span> | |
<script src="lap-chart.js" type="text/javascript"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Dimensions. | |
const DIMENSIONS = getWindowDimensions(); | |
const WIDTH = DIMENSIONS.width; | |
const HEIGHT = DIMENSIONS.height - 100; | |
// Insets. | |
const INSETS = {'left': 150, 'right': 150, 'top': 30, 'bottom': 30}; | |
// Padding. | |
const PADDING = {'left': 20, 'right': 20, 'top': 15, 'bottom': 15}; | |
// Tick-mark length. | |
const TICK_MARK_LENGTH = 8; | |
// Marker radius. | |
const MARKER_RADIUS = 12; | |
// Scales. | |
const SCALES = {}; | |
// Opacity of dimmed objects. | |
var DIMMED_OPACITY = 0.3; | |
var HIGHLIGHT_OPACITY = 1.0; | |
// Visualize when document has loaded. | |
// | |
window.onload = function() { | |
// Load data. | |
d3.json("2010au.json", function(data) { | |
// Check integrity. | |
integrityCheck(data); | |
// Sort laps on finishing order. | |
data.laps.sort(function(a, b) { | |
var aLaps = a.placing.length; | |
var bLaps = b.placing.length; | |
return aLaps == bLaps ? a.placing[aLaps - 1] - b.placing[bLaps - 1] : bLaps - aLaps; | |
}); | |
// Process lap markers.. | |
data.pitstops = processLapMarkers(data, "pitstops"); | |
data.mechanical = processLapMarkers(data, "mechanical"); | |
data.accident = processLapMarkers(data, "accident"); | |
// Visualize the data. | |
visualize(data); | |
}); | |
}; | |
// Check data. | |
// | |
// data: the data to check. | |
// | |
function integrityCheck(data) { | |
var laps = data.laps; | |
var lapCount = data.lapCount; | |
// Check lap data. | |
checkLaps(laps, lapCount); | |
// Check lapped data. | |
checkLapped(data.lapped, lapCount, laps.length); | |
// Check safety car data. | |
checkSafetyCar(data.safety, lapCount); | |
} | |
// Check lap data. | |
// | |
// laps: the lap data. | |
// lapCount: number of laps. | |
// | |
function checkLaps(laps, lapCount) { | |
for (var j = 0; | |
j < laps.length; | |
j++) { | |
// Has name? | |
var name = laps[j].name; | |
if (name == undefined || name.length == 0) { | |
alert("Warning: invalid name for element " + j); | |
} | |
// Has placings? | |
var places = laps[j].placing; | |
if (places == undefined) { | |
alert("Warning: missing placings for element " + j + " (" + name + ")"); | |
} | |
else if (places.length == 0 || places.length > lapCount + 1) { | |
alert("Warning: invalid number of placings (" + places.length + ") for element " + j + | |
" (" + name + ") - expected between 1 and " + (lapCount - 1)); | |
} | |
// Check markers. | |
var maxLaps = places.length; | |
checkMarker(laps[j].pitstops, "pitstop", maxLaps, j, name); | |
checkMarker(laps[j].mechanical, "mechanical", maxLaps, j, name); | |
checkMarker(laps[j].accident, "accident", maxLaps, j, name); | |
} | |
for (var i = 0; | |
i < lapCount; | |
i++) { | |
var positions = []; | |
for (j = 0; | |
j < laps.length; | |
j++) { | |
places = laps[j].placing; | |
if (places.length > i) { | |
// Valid placing? | |
var placing = places[i]; | |
if (isNaN(placing) || placing < 1 || placing % 1 != 0) { | |
alert("Warning: invalid placing '" + placing + "' for " + laps[j].name) | |
} | |
else { | |
var count = positions[placing]; | |
positions[placing] = isNaN(count) ? 1 : count + 1 | |
} | |
} | |
} | |
// Check for duplicate/missing positions. | |
for (j = 1; | |
j < positions.length; | |
j++) { | |
count = positions[j]; | |
if (count != 1) { | |
alert("Warning: data inconsistent: lap " + i + ", position " + j + ", count " + count); | |
} | |
} | |
} | |
} | |
// Check integrity of marker data. | |
// | |
// marker: marker data. | |
// name: driver name. | |
// type: text description of marker. | |
// max: maximum allowed lap value of marker. | |
// index: index of driver in list. | |
// | |
function checkMarker(marker, type, max, index, name) { | |
if (marker != undefined) { | |
// Check marker. | |
for (var i = 0; | |
i < marker.length; | |
i++) { | |
var stop = marker[i]; | |
if (isNaN(stop) || stop < 0 || stop >= max || stop % 1 != 0) { | |
alert("Warning: invalid " + type + " (" + stop + ") for element " + index + " (" + name + ")"); | |
} | |
} | |
} | |
} | |
// Check lapped data. | |
// | |
// lapped: the lapped data. | |
// lapCount: number of laps. | |
// driverCount: number of drivers. | |
// | |
function checkLapped(lapped, lapCount, driverCount) { | |
if (lapped != undefined) { | |
var lappedLength = lapped.length; | |
if (lappedLength != lapCount) { | |
alert("Lapped array length (" + lappedLength + ") incorrect - expected length " + lapCount); | |
} | |
for (var j = 1; | |
j < lappedLength; | |
j++) { | |
// Valid position. | |
var position = lapped[j]; | |
if (isNaN(position) || position % 1 != 0 || position < -1 || position > driverCount) { | |
alert("Invalid lapped position: element " + j + " (" + position | |
+ "); expected integer between -1 and " + driverCount); | |
} | |
} | |
} | |
} | |
// Check safety car data. | |
// | |
// safety: safety car data. | |
// lapCount: number of laps. | |
// | |
function checkSafetyCar(safety, lapCount) { | |
if (safety != undefined) { | |
for (var i = 0; | |
i < safety.length; | |
i++) { | |
// Valid lap? | |
var lap = safety[i]; | |
if (isNaN(lap) || lap < 0 || lap % 1 != 0 || lap > lapCount) { | |
alert("Invalid safety car lap: element " + i + " (" + lap | |
+ "); expected integer between 0 and " + lapCount); | |
} | |
} | |
} | |
} | |
// Process lap markers. | |
// | |
// data: lap data. | |
// key: marker key. | |
// | |
function processLapMarkers(data, key) { | |
var markers = []; | |
var p = 0; | |
for (var i = 0; | |
i < data.laps.length; | |
i++) { | |
var lapData = data.laps[i]; | |
var laps = lapData[key]; | |
if (laps != undefined) { | |
for (var j = 0; | |
j < laps.length; | |
j++) { | |
var lap = laps[j]; | |
var marker = {}; | |
marker.start = lapData.placing[0]; | |
marker.lap = lap; | |
marker.placing = lapData.placing[lap]; | |
marker.name = lapData.name; | |
markers[p++] = marker; | |
} | |
} | |
} | |
return markers; | |
} | |
// Create the visualization. | |
// | |
// data the lap data object. | |
// | |
function visualize(data) { | |
// Configure scales. | |
configureScales(data); | |
// Root panel. | |
var vis = d3.select('#chart') | |
.append('svg:svg') | |
.attr('width', WIDTH) | |
.attr('height', HEIGHT); | |
// Add safety car element. | |
addSafetyElement(vis, data.safety); | |
// Add lapped element. | |
addLappedElement(vis, data.lapped); | |
// Lap tick-lines. | |
addLapTickLines(vis, data.lapCount); | |
// Lap labels. | |
addLapLabels(vis, data.lapCount, SCALES.y.range()[0] - PADDING.bottom, '0.0em', 'top'); | |
addLapLabels(vis, data.lapCount, SCALES.y.range()[1] + PADDING.top, '0.35em', 'bottom'); | |
// Add placings poly-lines. | |
addPlacingsLines(vis, data.laps); | |
// Add name labels. | |
addDriverLabels(vis, data.laps, 'pole', SCALES.x(0) - PADDING.right, 'end') | |
.attr('y', function (d) { | |
return SCALES.y(d.placing[0] - 1); | |
}); | |
addDriverLabels(vis, data.laps, 'flag', SCALES.x(data.lapCount) + PADDING.left, 'start') | |
.attr('y', function (d, i) { | |
return SCALES.y(i); | |
}); | |
// Add markers. | |
addMarkers(vis, data.pitstops, "pitstop", "P"); | |
addMarkers(vis, data.mechanical, "mechanical", "M"); | |
addMarkers(vis, data.accident, "accident", "X"); | |
} | |
// Configure the scales. | |
// | |
// data: data set. | |
// | |
function configureScales(data) { | |
SCALES.x = d3.scale.linear() | |
.domain([0, data.lapCount]) | |
.range([INSETS.left, WIDTH - INSETS.right]); | |
SCALES.y = d3.scale.linear() | |
.domain([0, data.laps.length - 1]) | |
.range([INSETS.top, HEIGHT - INSETS.bottom]); | |
SCALES.clr = d3.scale.category20(); | |
} | |
// Highlight driver. | |
// | |
// vis: the data visualization root. | |
// index: index of driver to highlight. | |
// | |
function highlight(vis, name) { | |
// Dim others. | |
vis.selectAll('polyline') | |
.style('opacity', function(d) { | |
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY; | |
}); | |
vis.selectAll('circle') | |
.style('opacity', function(d) { | |
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY; | |
}); | |
vis.selectAll('text.label') | |
.style('opacity', function(d) { | |
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY; | |
}); | |
} | |
// Remove highlights. | |
// | |
// vis: the data visualization root. | |
// | |
function unhighlight(vis) { | |
// Reset opacity. | |
vis.selectAll('polyline') | |
.style('opacity', HIGHLIGHT_OPACITY); | |
vis.selectAll('circle') | |
.style('opacity', HIGHLIGHT_OPACITY); | |
vis.selectAll('text.label') | |
.style('opacity', HIGHLIGHT_OPACITY); | |
} | |
// Add safety car laps (rectanle elements). | |
// | |
// vis: the data visualization root. | |
// data: safety car laps. | |
// | |
function addSafetyElement(vis, data) { | |
if (data != undefined) { | |
var y = SCALES.y.range()[0]; | |
var height = SCALES.y.range()[1] - y; | |
var width = SCALES.x(1) - SCALES.x(0); | |
vis.selectAll('rect.safety') | |
.data(data) | |
.enter() | |
.append('svg:rect') | |
.attr('class', 'safety') | |
.attr('x', function(d) { | |
return SCALES.x(d - 0.5); | |
}) | |
.attr('y', function() { | |
return y; | |
}) | |
.attr('height', function() { | |
return height; | |
}) | |
.attr('width', function() { | |
return width; | |
}); | |
} | |
} | |
// Add lapped rectangle elements. | |
// | |
// vis: the data visualization root. | |
// data: the lapped data. | |
// | |
function addLappedElement(vis, data) { | |
if (data != undefined) { | |
var width = SCALES.x(1) - SCALES.x(0); | |
vis.selectAll('rect.lapped') | |
.data(data) | |
.enter() | |
.append('svg:rect') | |
.attr('class', 'lapped') | |
.attr('x', function(d, i) { | |
return SCALES.x(i + 0.5); | |
}) | |
.attr('y', function(d) { | |
return SCALES.y(d > 0 ? d - 1.5 : 0); | |
}) | |
.attr('height', function(d) { | |
return d > 0 ? SCALES.y.range()[1] - SCALES.y(d - 1.5) : 0; | |
}) | |
.attr('width', function(d) { | |
return d > 0 ? width : 0; | |
}); | |
} | |
} | |
// Add lap tick-lines. | |
// | |
// vis: the data visualization root. | |
// lapCount: number of laps. | |
// | |
function addLapTickLines(vis, lapCount) { | |
vis.selectAll('line.tickLine') | |
.data(SCALES.x.ticks(lapCount)) | |
.enter().append('svg:line') | |
.attr('class', 'tickLine') | |
.attr('x1', function(d) { | |
return SCALES.x(d + 0.5); | |
}) | |
.attr('x2', function(d) { | |
return SCALES.x(d + 0.5); | |
}) | |
.attr('y1', SCALES.y.range()[0] - TICK_MARK_LENGTH) | |
.attr('y2', SCALES.y.range()[1] + TICK_MARK_LENGTH) | |
.attr('visibility', function(d) { | |
return d <= lapCount ? 'visible' : 'hidden' | |
}); | |
} | |
// Add lap labels. | |
// | |
// vis: the data visualization root. | |
// data: lap data. | |
// y: y position of labels. | |
// dy: y offset. | |
// cssClass: CSS class id. | |
// | |
function addLapLabels(vis, data, y, dy, cssClass) { | |
vis.selectAll('text.lap.' + cssClass) | |
.data(SCALES.x.ticks(data)) | |
.enter().append('svg:text') | |
.attr('class', 'lap ' + cssClass) | |
.attr('x', function(d) { | |
return SCALES.x(d); | |
}) | |
.attr('y', y) | |
.attr('dy', dy) | |
.attr('text-anchor', 'middle') | |
.text(function(d, i) { | |
return i > 0 ? i : ''; | |
}); | |
} | |
// Add placings polyline elements. | |
// | |
// vis: the visualization root. | |
// laps: lap data. | |
// | |
function addPlacingsLines(vis, laps) { | |
vis.selectAll('polyline.placing') | |
.data(laps) | |
.enter() | |
.append('svg:polyline') | |
.attr('class', 'placing') | |
.attr('points', function(d) { | |
var points = []; | |
for (var i = 0; | |
i < d.placing.length; | |
i++) { | |
points[i] = SCALES.x(i) + ',' + SCALES.y(d.placing[i] - 1); | |
} | |
if (points.length > 0) | |
points.push(SCALES.x(i - 0.5) + ',' + SCALES.y(d.placing[i - 1] - 1)); | |
return points.join(' '); | |
}) | |
.style('stroke', function(d) { | |
return SCALES.clr(d.placing[0]); | |
}) | |
.on('mouseover', function(d) { | |
highlight(vis, d.name); | |
}) | |
.on('mouseout', function() { | |
unhighlight(vis); | |
}); | |
} | |
// Add driver name labels. | |
// | |
// vis: the data visualization root. | |
// laps: the lap data. | |
// cssClass: CSS class id. | |
// textAnchor: text-anchor value. | |
// | |
function addDriverLabels(vis, laps, cssClass, x, textAnchor) { | |
return vis.selectAll('text.label.' + cssClass) | |
.data(laps) | |
.enter() | |
.append('svg:text') | |
.attr('class', 'label ' + cssClass) | |
.attr('x', x) | |
.attr('dy', '0.35em') | |
.attr('text-anchor', textAnchor) | |
.text(function(d) { | |
return d.name; | |
}) | |
.style('fill', function(d) { | |
return SCALES.clr(d.placing[0]); | |
}) | |
.on('mouseover', function(d) { | |
highlight(vis, d.name); | |
}) | |
.on('mouseout', function() { | |
unhighlight(vis); | |
}); | |
} | |
// Add markers. | |
// | |
// vis: the visualization root. | |
// data: marker data. | |
// class: marker sub-class. | |
// label: marker label. | |
// | |
function addMarkers(vis, data, cssClass, label) { | |
label = label || "P"; | |
// Place circle glyph. | |
vis.selectAll("circle.marker." + cssClass) | |
.data(data) | |
.enter() | |
.append("svg:circle") | |
.attr("class", "marker " + cssClass) | |
.attr("cx", function(d) { | |
return SCALES.x(d.lap); | |
}) | |
.attr("cy", function(d) { | |
return SCALES.y(d.placing - 1); | |
}) | |
.attr("r", MARKER_RADIUS) | |
.style("fill", function(d) { | |
return SCALES.clr(d.start); | |
}) | |
.on('mouseover', function(d) { | |
highlight(vis, d.name); | |
}) | |
.on('mouseout', function() { | |
unhighlight(vis); | |
}); | |
// Place text. | |
vis.selectAll("text.label.marker" + cssClass) | |
.data(data) | |
.enter() | |
.append("svg:text") | |
.attr("class", "label marker" + cssClass) | |
.attr("x", function(d) { | |
return SCALES.x(d.lap); | |
}) | |
.attr("y", function(d) { | |
return SCALES.y(d.placing - 1); | |
}) | |
.attr("dy", "0.35em") | |
.attr("text-anchor", "middle") | |
.text(label) | |
.on('mouseover', function(d) { | |
highlight(vis, d.name); | |
}) | |
.on('mouseout', function() { | |
unhighlight(vis); | |
}); | |
} | |
// Gets the window dimensions. | |
// | |
function getWindowDimensions() { | |
var width = 630; | |
var height = 460; | |
if (document.body && document.body.offsetWidth) { | |
width = document.body.offsetWidth; | |
height = document.body.offsetHeight; | |
} | |
if (document.compatMode == 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth) { | |
width = document.documentElement.offsetWidth; | |
height = document.documentElement.offsetHeight; | |
} | |
if (window.innerWidth && window.innerHeight) { | |
width = window.innerWidth; | |
height = window.innerHeight; | |
} | |
return {'width': width, 'height': height}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
padding: 0; | |
background-color: #000000; | |
font-family: sans-serif; | |
font-size: 12px; | |
color: #666666; | |
overflow: hidden; | |
} | |
a:link { | |
color: #ccccff; | |
} | |
a:visited { | |
color: #cccccc; | |
} | |
a:active { | |
color: #ffccff; | |
} | |
a.hover { | |
color: #ffcccc; | |
} | |
.title { | |
float: left; | |
font-size: 14px; | |
font-weight: bold; | |
color: #ffffff; | |
} | |
.attrib { | |
float: right; | |
font-size: 14px; | |
color: #ffffff; | |
} | |
text.lap { | |
fill: #999999; | |
} | |
text.label { | |
fill: #000000; | |
} | |
text.label.marker { | |
font-weight: bold; | |
} | |
polyline.placing { | |
fill: none; | |
stroke-width: 5; | |
} | |
line.tickLine { | |
stroke: #999999; | |
} | |
.lapped { | |
background-color: #333333; | |
stroke: #333333; | |
fill: #333333; | |
} | |
.safety{ | |
background-color: #330000; | |
stroke: #330000; | |
fill: #330000; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment