|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<link href="https://fonts.googleapis.com/css?family=Montserrat:400" rel="stylesheet"> |
|
<style> |
|
|
|
text { |
|
font-family: "Montserrat", sans-serif; |
|
font-size: 8px; |
|
} |
|
|
|
.polygons { |
|
fill: none; |
|
pointer-events: all; |
|
} |
|
|
|
path.domain { |
|
display: none; |
|
} |
|
|
|
.axis line { |
|
stroke: #ddd; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.dnf circle { |
|
fill: none; |
|
stroke-width: 1px; |
|
} |
|
|
|
.dnf text { |
|
opacity: 0; |
|
font-size: 8px; |
|
text-align: center; |
|
} |
|
|
|
.dimmed { |
|
opacity: 0.25; |
|
} |
|
|
|
.highlighted text { |
|
opacity: 1; |
|
} |
|
|
|
</style> |
|
<body> |
|
<svg width="750" height="1250"></svg> |
|
</body> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
const svg = d3.select("svg"), |
|
margin = {top: 20, right: 20, bottom: 20, left: 120}, |
|
width = +svg.attr("width") - margin.left - margin.right, |
|
height = +svg.attr("height") - margin.top - margin.bottom, |
|
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
const x = d3.scaleLinear().range([0, width]), |
|
y = d3.scalePoint().rangeRound([0, height]).padding(0.1), |
|
color = d3.scaleSequential(d3.interpolateMagma); |
|
|
|
const voronoi = d3.voronoi() |
|
.x(d => x(d.year)) |
|
.y(d => y(d.status)) |
|
.extent([[-5, -5], [width + 5, height + 5]]); |
|
|
|
d3.csv("dnf.csv", type, (error, data) => { |
|
if (error) return console.error(error); |
|
|
|
data = data.filter(d => d.status.indexOf('Laps') < 1); |
|
|
|
x.domain(d3.extent(data, d => d.year)); |
|
y.domain(d3.map(data, d => d.status).keys()); |
|
color.domain(d3.extent(data, d => d.count)); |
|
|
|
g.append("g") |
|
.attr("class", "axis x--axis") |
|
.call(d3.axisTop(x).tickFormat(d3.format(".0f")).tickSize(-height)); |
|
|
|
g.append("g") |
|
.attr("class", "axis y--axis") |
|
.call(d3.axisLeft(y)); |
|
|
|
const circle = g.selectAll(".dnf") |
|
.data(data) |
|
.enter().append("g") |
|
.attr("class", d => "dnf status-" + d.statusId + " year-" + d.year); |
|
|
|
circle.append("circle") |
|
.attr("cx", d => x(d.year)) |
|
.attr("cy", d => y(d.status)) |
|
.style("r", 3) |
|
.style("stroke", d => color(d.count)); |
|
|
|
circle.append("text") |
|
.attr("x", -margin.left + 10) |
|
.attr("y", d => y(d.status)) |
|
.attr("dy", "0.35em") |
|
.text(d => d.year + ": " + d.count + "x"); |
|
|
|
g.selectAll(".polygons") |
|
.data(voronoi.polygons(data)) |
|
.enter().append("path") |
|
.attr("class", "polygons") |
|
.attr("d", d => d ? "M" + d.join("L") + "Z" : null) |
|
.on("mouseover", mouseover) |
|
.on("mouseout", mouseout); |
|
|
|
}); |
|
|
|
function mouseover(d) { |
|
d3.selectAll(".dnf").classed("dimmed", true); |
|
d3.selectAll(".dnf.status-" + d.data.statusId).classed("dimmed", false); |
|
d3.selectAll(".dnf.year-" + d.data.year).classed("dimmed", false); |
|
d3.selectAll(".dnf.status-" + d.data.statusId + ".year-" + d.data.year).classed("highlighted", true); |
|
} |
|
|
|
function mouseout(d) { |
|
d3.selectAll(".dnf").classed("dimmed", false).classed("highlighted", false); |
|
} |
|
|
|
function type(d) { |
|
d.count = +d.count; |
|
return d; |
|
} |
|
</script> |