|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="d3-patterngrid.js"></script> |
|
<style> |
|
.coin { |
|
stroke: black; |
|
} |
|
|
|
text { |
|
font-family: monospace; |
|
} |
|
|
|
.pie-segment { |
|
stroke: black; |
|
fill: white |
|
} |
|
|
|
.name-label, .fraction-label { |
|
font-size: 10px; |
|
} |
|
|
|
.name-label, .heading { |
|
font-weight: bold; |
|
} |
|
|
|
.sub-heading { |
|
font-size: 12px; |
|
} |
|
|
|
</style> |
|
</head> |
|
|
|
<body> |
|
<script> |
|
var width = 960, |
|
height = 500; |
|
|
|
var imageURL = "http://i.imgur.com/diLMHJQ.png"; |
|
var dataURL = "https://raw.githubusercontent.com/tlfrd/pay-ratios/master/data/over140k.json"; |
|
|
|
var imageMode = true; |
|
|
|
var initialPosition = { x: 90, y: 95 }; |
|
|
|
var circleSize = { width: 75, height: 75 }; |
|
|
|
var spacing = { |
|
h: 30, |
|
v: 70 |
|
}; |
|
|
|
var labelPositionTop = -48, |
|
labelPositionBottom = 55; |
|
|
|
var gridLength = 8; |
|
|
|
var path = d3.arc() |
|
.outerRadius(circleSize.width / 2) |
|
.innerRadius(0) |
|
.startAngle(0); |
|
|
|
var generateArc = function(fraction) { |
|
return path({endAngle: Math.PI * 2 * fraction}) |
|
} |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height) |
|
|
|
var defs = svg.append("defs"); |
|
|
|
var coinPattern = patternGrid.circleLayout() |
|
.config({ |
|
image: imageURL, |
|
radius: circleSize.width, |
|
padding: [spacing.h, spacing.v], |
|
margin: [initialPosition.y, initialPosition.x], |
|
id: "money" |
|
}); |
|
|
|
// Load data |
|
|
|
var angleGrid = []; |
|
|
|
d3.json(dataURL, function(error, data) { |
|
|
|
var over140 = data.number_over_140k; |
|
|
|
// Filter out universities where number of women is not provided |
|
over140 = over140.filter(function(a) { |
|
return a.women !== "-"; |
|
}) |
|
|
|
over140.forEach(function(a) { |
|
var angle = a.women / a.over || 0; |
|
|
|
var angleObj = { |
|
name: a.name, |
|
angle: 1 - angle, |
|
women: a.women, |
|
total: a.over |
|
}; |
|
angleGrid.push(angleObj); |
|
}); |
|
|
|
// Sort the grid |
|
angleGrid.sort(function(a, b) { |
|
if (a.angle < b.angle) return -1; |
|
if (a.angle > b.angle) return 1; |
|
if (a.angle === b.angle) return 0; |
|
}); |
|
|
|
// Assign positions |
|
angleGrid.forEach(function(a, i) { |
|
a.x = i % gridLength; |
|
a.y = Math.floor(i / gridLength); |
|
}); |
|
|
|
var circles = svg.append("g") |
|
.selectAll("circle") |
|
.data(angleGrid) |
|
.enter().append("circle") |
|
.attr("class", "coin") |
|
.attr("cx", function(d) { |
|
return initialPosition.x + (circleSize.width + spacing.h) * d.x; |
|
}) |
|
.attr("cy", function(d) { |
|
return initialPosition.y + (circleSize.height + spacing.v) * d.y; |
|
}) |
|
.attr("r", circleSize.width / 2) |
|
.attr("fill", "url(#money)"); |
|
|
|
var arcs = svg.append("g") |
|
.selectAll("path") |
|
.data(angleGrid.filter(d => d.angle)) |
|
.enter().append("path") |
|
.attr("transform", function(d) { |
|
var xPos = initialPosition.x + (circleSize.width + spacing.h) * d.x; |
|
var yPos = initialPosition.y + (circleSize.height + spacing.v) * d.y; |
|
return "translate(" + xPos + ", " + yPos + ")"; |
|
}) |
|
.attr("class", "pie-segment") |
|
.attr("d", d => generateArc(d.angle)); |
|
|
|
|
|
var namelabels = svg.append("g") |
|
.selectAll("text") |
|
.data(angleGrid) |
|
.enter().append("text") |
|
.attr("class", "name-label") |
|
.attr("x", function(d) { |
|
return initialPosition.x + (circleSize.width + spacing.h) * d.x; |
|
}) |
|
.attr("y", function(d) { |
|
return initialPosition.y + (circleSize.height + spacing.v) * d.y; |
|
}) |
|
.attr("dy", labelPositionTop) |
|
.attr("text-anchor", "middle") |
|
.text(d => d.name); |
|
|
|
var fractionclabels = svg.append("g") |
|
.selectAll("text") |
|
.data(angleGrid) |
|
.enter().append("text") |
|
.attr("class", "fraction-label") |
|
.attr("x", function(d) { |
|
return initialPosition.x + (circleSize.width + spacing.h) * d.x; |
|
}) |
|
.attr("y", function(d) { |
|
return initialPosition.y + (circleSize.height + spacing.v) * d.y; |
|
}) |
|
.attr("dy", labelPositionBottom) |
|
.attr("text-anchor", "middle") |
|
.text(d => d.women + " / " + d.total); |
|
}); |
|
|
|
var legend = svg.append("g") |
|
.attr("class", "legend") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "translate(" + [width - 100, height - 60] + ")"); |
|
|
|
legend.append("text") |
|
.attr("class", "heading") |
|
.attr("dy", -20) |
|
.text("London Universities"); |
|
|
|
legend.append("text") |
|
.attr("class", "sub-heading") |
|
.text("No. of Women / Total earning over 140k"); |
|
|
|
svg.on("click", changeStyle); |
|
|
|
function changeStyle() { |
|
if (imageMode) { |
|
imageMode = false; |
|
svg.selectAll(".coin") |
|
.style("fill", "white"); |
|
svg.selectAll(".pie-segment") |
|
.style("fill", "black"); |
|
} else { |
|
imageMode = true; |
|
svg.selectAll(".coin") |
|
.style("fill", "url(#money)"); |
|
svg.selectAll(".pie-segment") |
|
.style("fill", "white"); |
|
} |
|
} |
|
|
|
</script> |
|
</body> |