|
<!DOCTYPE html> |
|
<html> |
|
<meta charset="utf-8"> |
|
<title>Top 10 ATP players results in 2015</title> |
|
<style> |
|
|
|
body { |
|
margin: auto; |
|
} |
|
|
|
#circle circle { |
|
fill: none; |
|
pointer-events: all; |
|
} |
|
|
|
.group path { |
|
fill-opacity: .9; |
|
} |
|
|
|
path.chord { |
|
stroke: #000; |
|
stroke-width: .25px; |
|
} |
|
|
|
#circle:hover path.fade { |
|
display: none; |
|
} |
|
|
|
svg { |
|
font: 12px sans-serif; |
|
margin: 1em; |
|
} |
|
|
|
</style> |
|
<header> |
|
|
|
</header> |
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> |
|
<body> |
|
<!-- Table to hold the chord and text --> |
|
<table border="0" cellpadding="10" style="overflow-y: scroll;"> |
|
<tr> |
|
<td><div id="chord_container"></div></td> |
|
<td><div id="text_container"> |
|
<h1 style="text-align: center">Top 10 ATP players' results in 2015</h1> |
|
<span style="min-width: 300px; float: right; margin: 1em; font-family: 'PT Sans',sans-serif"> |
|
<p>The chart indicates the results of the top 10 ATP players against each other during 2015. |
|
<p>Hover over the circle rim to see number of matches for that player. Hover over an arc to see the results between two players. |
|
<p>The thickness of links between players encodes the relative frequency of matches between the players: thicker links represent more matches. |
|
<p>Links are directed: for example, Djokovic beat Murray 6 times, but Murray only beat Djokovic once. Links are given the colour of the player who wins most often. |
|
<p>Built with <a href="http://d3js.org/">d3.js</a>. Thanks to <a href="http://bost.ocks.org/mike/">Mike Bostock</a> for sample code.</span> |
|
<p>Data sourced from <a href="http://www.tennis-data.co.uk/">tennis-data.co.uk</a>.</span> |
|
</div></td> |
|
</tr> |
|
</table> |
|
|
|
<script> |
|
|
|
var width = (window.innerWidth < 480 ? window.innerWidth : 480), |
|
height = width, |
|
outerRadius = Math.min(width, height) / 2 - 10, |
|
innerRadius = outerRadius - 24; |
|
|
|
var formatPercent = d3.format(".1%"); |
|
|
|
//var fill = d3.scale.category10(); |
|
|
|
function fill(n) { |
|
var colors = ['#8dd3c7', |
|
'#ffffb3', |
|
'#bebada', |
|
'#fb8072', |
|
'#80b1d3', |
|
'#fdb462', |
|
'#b3de69', |
|
'#fccde5', |
|
'#d9d9d9', |
|
'#bc80bd'] |
|
return colors[n % colors.length]; |
|
} |
|
|
|
var arc = d3.svg.arc() |
|
.innerRadius(innerRadius) |
|
.outerRadius(outerRadius); |
|
|
|
var layout = d3.layout.chord() |
|
.padding(.04) |
|
.sortSubgroups(d3.descending) |
|
.sortChords(d3.ascending); |
|
|
|
var path = d3.svg.chord() |
|
.radius(innerRadius); |
|
|
|
var svg = d3.select("#chord_container").append("svg") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.append("g") |
|
.attr("id", "circle") |
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); |
|
|
|
svg.append("circle") |
|
.attr("r", outerRadius); |
|
|
|
d3.csv("players.csv", function(players) { |
|
d3.json("atpresults.json", function(matrix) { |
|
|
|
// Compute the chord layout. |
|
layout.matrix(matrix); |
|
|
|
// Add a group per player. |
|
var group = svg.selectAll(".group") |
|
.data(layout.groups) |
|
.enter().append("g") |
|
.attr("class", "group") |
|
.on("mouseover", mouseover); |
|
|
|
// Add a mouseover title. |
|
group.append("title").text(function(d, i) { |
|
return players[i].name + ": " + parseInt(d.value+0.5) + " Top 10 wins"; |
|
}); |
|
|
|
// Add the group arc. |
|
var groupPath = group.append("path") |
|
.attr("id", function(d, i) { return "group" + i; }) |
|
.attr("d", arc) |
|
.style("fill", function(d, i) { return fill(d.index); }); |
|
|
|
// Add a text label. |
|
var groupText = group.append("text") |
|
.attr("x", 6) |
|
.attr("dy", 15); |
|
|
|
groupText.append("textPath") |
|
.attr("xlink:href", function(d, i) { return "#group" + i; }) |
|
.text(function(d, i) { console.log(d); return players[i].name; }); |
|
|
|
// Remove the labels that don't fit. :( |
|
groupText.filter(function(d, i) { return groupPath[0][i].getTotalLength() / 2 - 6 < this.getComputedTextLength(); }) |
|
.remove(); |
|
|
|
// Add the chords. |
|
var chord = svg.selectAll(".chord") |
|
.data(layout.chords) |
|
.enter().append("path") |
|
.attr("class", "chord") |
|
.style("fill", function(d) { return fill(d.source.index); }) |
|
.attr("d", path); |
|
|
|
// Add an elaborate mouseover title for each chord. |
|
chord.append("title").text(function(d) { |
|
return players[d.source.index].name |
|
+ " beat " + players[d.target.index].name |
|
+ " " + d.source.value |
|
+ " time(s)" |
|
+ "\n" + players[d.target.index].name |
|
+ " beat " + players[d.source.index].name |
|
+ " " + d.target.value |
|
+ " time(s)"; |
|
}); |
|
|
|
function mouseover(d, i) { |
|
chord.classed("fade", function(p) { |
|
return p.source.index != i |
|
&& p.target.index != i; |
|
}); |
|
} |
|
}); |
|
}); |
|
d3.select(self.frameElement).style("height", "600px"); |
|
</script> |