Built with blockbuilder.org
Last active
October 8, 2018 05:55
-
-
Save jwilber/7efe98bd384a95a466b8975a930408e4 to your computer and use it in GitHub Desktop.
one hundred people
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
license: mit |
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> | |
<html lang="en"> | |
<head> | |
<link href="https://fonts.googleapis.com/css?family=PT+Sans|PT+Serif|Roboto" rel="stylesheet"> | |
<link href="../codecss/prism.css" rel="stylesheet" /> | |
<meta charset="UTF-8"> | |
<title>One Hundred People</title> | |
<style type="text/css"> | |
body { | |
background-color: white; | |
font-family: 'consolas', serif; | |
} | |
#container { | |
max-width: 1000px; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 5px; | |
padding: 1px 10px 10px 10px; | |
background-color: white; | |
box-shadow: 1px 1px 1px 1px #fff; | |
} | |
h1 { | |
font-weight: 300; | |
color: darkslategray; | |
font-family: "consolas", sans-serif; | |
font-size: 56px; | |
margin-top: 10px; | |
margin-bottom: 0px; | |
} | |
h2 { | |
font-weight: 300; | |
color: darkslategray; | |
font-family: "PT Sans", sans-serif; | |
font-size: 32px; | |
margin-bottom: 20px; | |
margin-top: -10px; | |
} | |
p { | |
font-size: 18px; | |
margin-top: 4px; | |
margin-bottom: 8px; | |
color: #333; | |
} | |
a { | |
color: darkslategray; | |
} | |
a:hover { | |
color: darkslategray; | |
} | |
.footer { | |
font-size: 14px; | |
margin-top: 0px; | |
} | |
#svganchor { | |
margin: auto; | |
} | |
.criterionList { | |
font-size: 12px; | |
color: #222; | |
text-transform: uppercase; | |
cursor: pointer; | |
} | |
.chartTitle { | |
font-family: "PT Sans", sans-serif; | |
font-weight: 700; | |
font-size: 22px; | |
color: #444; | |
} | |
.chartSubTitle { | |
font-family: "PT Sans", sans-serif; | |
font-size: 18px; | |
color: #444; | |
} | |
.nodesData { | |
font-family: 'PT Serif', serif; | |
font-size: 14px; | |
} | |
.nodesDataSpan { | |
font-family: 'PT Serif', serif; | |
font-size: 18px; | |
font-weight: 700; | |
} | |
.preciseNumberTitle { | |
font-family: "PT Sans", sans-serif; | |
font-size: 12px; | |
color: #444; | |
} | |
.nodesDataPrecise { | |
font-family: 'PT Serif', serif; | |
font-size: 12px; | |
color: #444; | |
} | |
svg { | |
background-color: white; | |
} | |
</style> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
</head> | |
<body> | |
<div id="container"> | |
<h1>One Hundred People</h1> | |
<div id="svganchor"></div> | |
<br> | |
</div> | |
<script type="text/javascript"> | |
var totalData = [{ | |
criterion: "Sex", | |
description: "Distribution of males and females", | |
groups: [{ | |
name: "Male", | |
value: 50.43 | |
}, { | |
name: "Female", | |
value: 49.57 | |
}] | |
}, { | |
criterion: "Body weight", | |
description: "People above a weight considered healthy", | |
groups: [{ | |
name: "Overweight", | |
value: 21.88 | |
}, { | |
name: "Not overweight", | |
value: 78.12 | |
}] | |
},{ | |
criterion: "Undernourishment", | |
description: "People with sufficient quantity or quality of nourishment", | |
groups: [{ | |
name: "Undernourished", | |
value: 10.00 | |
}, { | |
name: "Not Undernourished", | |
value: 90.00 | |
}] | |
},{ | |
criterion: "Age", | |
description: "Distribution by age", | |
groups: [{ | |
name: "Below 14 years of age", | |
value: 26.30 | |
}, { | |
name: "Between 15 and 64 years", | |
value: 65.90 | |
}, { | |
name: "65 years and over", | |
value: 7.80 | |
}] | |
},{ | |
criterion: "Internet access", | |
description: "People with an internet connection", | |
groups: [{ | |
name: "With connection", | |
value: 47.31 | |
}, { | |
name: "Without connection", | |
value: 52.69 | |
}] | |
},{ | |
criterion: "Homosexuality", | |
description: "Distribution by sexual orientation", | |
groups: [{ | |
name: "Heterosexual", | |
value: 97.4 | |
}, { | |
name: "Homosexual or Bisexual", | |
value: 2.6 | |
}] | |
},{ | |
criterion: "Mobile phone", | |
description: "People using mobile phones", | |
groups: [{ | |
name: "Using a mobile", | |
value: 63.90 | |
}, { | |
name: "Not using a mobile", | |
value: 36.10 | |
}] | |
},{ | |
criterion: "Electricity", | |
description: "People with access to electricity", | |
groups: [{ | |
name: "Access", | |
value: 84.58 | |
}, { | |
name: "No access", | |
value: 15.42 | |
}] | |
},{ | |
criterion: "Water", | |
description: "People with access to clean water", | |
groups: [{ | |
name: "No access", | |
value: 8.39 | |
}, { | |
name: "Access", | |
value: 91.61 | |
}] | |
},{ | |
criterion: "Unemployment", | |
description: "Unemployment rate", | |
groups: [{ | |
name: "Unemployed", | |
value: 5.93 | |
}, { | |
name: "Employed", | |
value: 94.07 | |
}] | |
},{ | |
criterion: "Rule of the road", | |
description: "Side of the road in bidirectional traffic", | |
groups: [{ | |
name: "Drive on the right side", | |
value: 66.00 | |
}, { | |
name: "Drive on the left side", | |
value: 34.00 | |
}] | |
},{ | |
criterion: "Income", | |
description: "Dollars per day", | |
groups: [{ | |
name: "Less than $2", | |
value: 71.00 | |
}, { | |
name: "Between $10 and $20", | |
value: 13.00 | |
}, { | |
name: "More than $20", | |
value: 16.00 | |
}] | |
},{ | |
criterion: "Religion", | |
description: "Distribution by religion", | |
groups: [{ | |
name: "Christian", | |
value: 31.50 | |
}, { | |
name: "Muslim", | |
value: 22.32 | |
}, { | |
name: "Atheist", | |
value: 15.35 | |
}, { | |
name: "Hindu", | |
value: 13.95 | |
}, { | |
name: "Other", | |
value: 16.88 | |
}] | |
},{ | |
criterion: "Continent", | |
description: "People from different continents", | |
groups: [{ | |
name: "Africa", | |
value: 14.5 | |
}, { | |
name: "Europe", | |
value: 11.4 | |
}, { | |
name: "Americas", | |
value: 13.2 | |
},{ | |
name: "Asia", | |
value: 60.3 | |
}, { | |
name: "Oceania", | |
value: 0.6 | |
}] | |
},{ | |
criterion: "Literacy", | |
description: "People able to read and write", | |
groups: [{ | |
name: "Literate", | |
value: 83.7 | |
}, { | |
name: "Illiterate", | |
value: 16.3 | |
}] | |
},{ | |
criterion: "Handedness", | |
description: "Preference to use one hand rather than the other", | |
groups: [{ | |
name: "Left-handedness", | |
value: 8.00 | |
}, { | |
name: "Right-handedness", | |
value: 92.00 | |
}] | |
},{ | |
criterion: "Blood type", | |
description: "ABO blood group system", | |
groups: [{ | |
name: "A", | |
value: 33.90 | |
}, { | |
name: "B", | |
value: 16.20 | |
}, { | |
name: "AB", | |
value: 5.10 | |
}, { | |
name: "O", | |
value: 44.80 | |
}] | |
},{ | |
criterion: "Sanitation", | |
description: "People with access to basic sanitation, like flush toilets and treated sewage", | |
groups: [{ | |
name: "Access", | |
value: 67.52 | |
}, { | |
name: "No access", | |
value: 32.48 | |
}] | |
}]; | |
var headPath = "M251.249,127.907c17.7,0,32.781-6.232,45.254-18.7c12.467-12.467,18.699-27.554,18.699-45.253 c0-17.705-6.232-32.783-18.699-45.255C284.029,6.233,268.948,0,251.249,0c-17.705,0-32.79,6.23-45.254,18.699 c-12.465,12.469-18.699,27.55-18.699,45.255c0,17.703,6.23,32.789,18.699,45.253C218.462,121.671,233.549,127.907,251.249,127.907 z"; | |
var bodyPath = "M381.438,153.029c-10.663-10.657-23.599-15.987-38.827-15.987H159.889c-15.23,0-28.171,5.327-38.831,15.987 c-10.657,10.66-15.987,23.604-15.987,38.831v118.776c0,7.611,2.663,14.079,7.993,19.407s11.803,7.994,19.414,7.994 c7.614,0,14.087-2.666,19.417-7.994c5.327-5.328,7.994-11.796,7.994-19.407V210.134h18.271v260.379 c0,8.754,3.144,16.275,9.423,22.559c6.28,6.276,13.796,9.418,22.554,9.418s16.278-3.142,22.557-9.418 c6.28-6.283,9.42-13.802,9.42-22.559V338.038h18.27V470.52c0,8.75,3.141,16.275,9.421,22.552 c6.279,6.283,13.802,9.425,22.556,9.425c8.76,0,16.279-3.142,22.559-9.425c6.283-6.283,9.418-13.798,9.418-22.552V210.134h18.274 v100.495c0,7.618,2.665,14.086,7.994,19.411c5.328,5.331,11.799,7.994,19.41,7.994c7.61,0,14.093-2.663,19.417-7.994 c5.328-5.325,7.994-11.793,7.994-19.411V191.86C397.43,176.63,392.095,163.689,381.438,153.029z"; | |
var criterionListData = totalData.map(d => d.criterion); | |
var divList = d3.select("#controler"); | |
var criterionList = divList.selectAll(".criterionList") | |
.data(criterionListData) | |
.enter() | |
.append("p") | |
.attr("class", "criterionList"); | |
criterionList.text(d => d); | |
var width = 850, height = 500; | |
var svg = d3.select("#svganchor") | |
.append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var padding = [10, 10, 10, 10]; | |
var xScale = d3.scalePoint() | |
.range([padding[3], width - padding[1]]) | |
.padding(0.5); | |
var barScale = d3.scaleLinear() | |
.range([padding[3], width - padding[1]]) | |
.domain([0,100]); | |
var colorScale = d3.scaleOrdinal() | |
.range(['#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e']); | |
var dataNodes = d3.range(100).map(() => ({id: "", xPosition:""})); | |
var simulation = d3.forceSimulation() | |
.force("collide", d3.forceCollide(12)) | |
.force("x", d3.forceX(d => d.xPosition).strength(0.2)) | |
.force("y", d3.forceY(height / 2)) | |
.velocityDecay(0.7); | |
var chartTitle = svg.append("text") | |
.attr("class", "chartTitle") | |
.attr("x", width / 2) | |
.attr("y", 20) | |
.attr("text-anchor", "middle"); | |
var chartSubTitle = svg.append("text") | |
.attr("class", "chartSubTitle") | |
.attr("x", width / 2) | |
.attr("y", 44) | |
.attr("text-anchor", "middle"); | |
var preciseNumberTitle = svg.append("text") | |
.attr("class", "preciseNumberTitle") | |
.attr("y", 424) | |
.attr("x", padding[3]) | |
.text("Precise numbers:"); | |
draw("Sex"); | |
function draw(criterion){ | |
chartTitle.text(criterion); | |
var filteredData = totalData.filter(d => d.criterion === criterion); | |
chartSubTitle.text(filteredData[0].description); | |
xScale.domain(filteredData[0].groups.map(d => d.name)); | |
colorScale.domain(filteredData[0].groups.map(d => d.name)); | |
var counterData = 0; | |
filteredData[0].groups.forEach(d=>{ | |
var rounded = Math.round(d.value); | |
for(var i = counterData; i < rounded + counterData; i++){ | |
dataNodes[i].id = d.name; | |
dataNodes[i].xPosition = xScale(d.name); | |
}; | |
counterData += rounded; | |
}); | |
var nodes = svg.selectAll(".g") | |
.data(dataNodes) | |
.enter() | |
.append("g"); | |
nodes.attr("fill", d => colorScale(d.id)); | |
nodes.append("path") | |
.attr("d", headPath) | |
.attr("transform", "translate(-12,0) scale(0.045,0.045)"); | |
nodes.append("path") | |
.attr("d", bodyPath) | |
.attr("transform", "translate(-12,0) scale(0.045,0.045)"); | |
var nodesData = svg.selectAll(".nodesData") | |
.data(filteredData[0].groups, d => d.name) | |
.enter() | |
.append("text") | |
.attr("class", "nodesData") | |
.attr("text-anchor", "middle") | |
.attr("y", 100) | |
.attr("x", d => xScale(d.name)) | |
.text(d => d.name + ": ") | |
.append("tspan") | |
.attr("class", "nodesDataSpan") | |
.text(d => Math.round(d.value)); | |
var nodesDataPrecise = svg.selectAll(".nodesDataPrecise") | |
.data(filteredData[0].groups, d => d.name) | |
.enter() | |
.append("text") | |
.attr("class", "nodesDataPrecise") | |
.attr("text-anchor", "middle") | |
.attr("y", 440) | |
.attr("x", d => xScale(d.name)) | |
.text(d => d.name + ": " + d.value + "%"); | |
dataNodes.forEach(d =>{ | |
d.x = width / 2; | |
d.y = height / 2; | |
}); | |
simulation.nodes(dataNodes) | |
.on("tick", tick); | |
var bars = svg.selectAll(".bars") | |
.data(filteredData[0].groups); | |
var barsEnter = bars.enter() | |
.append("rect") | |
.attr("class", "bars") | |
.attr("y", 470) | |
.attr("x", (d,i) => i ? barScale(filteredData[0].groups[i-1].value) : barScale(0)) | |
.attr("height", 20) | |
.attr("width", d => barScale(d.value) - padding[3]) | |
.attr("fill", d => colorScale(d.name)); | |
var polylines = svg.selectAll(".polylines") | |
.data(filteredData[0].groups) | |
.enter() | |
.append("polyline") | |
.attr("class", "polylines") | |
.attr("points", d => "" + xScale(d.name) + ",470 " | |
+ xScale(d.name) + ",446") | |
.attr("stroke-width", 1) | |
.attr("stroke", "darkslategray"); | |
function tick(){ | |
nodes.attr('transform', (d) => { | |
if (d.y < 120) { | |
d.y = 120 | |
}; | |
if (d.y > 380) { | |
d.y = 380 | |
}; | |
return 'translate(' + (d.x) + ',' + (d.y) + ')'; | |
}); | |
}; | |
function redraw(criterion){ | |
chartTitle.text(criterion); | |
var filteredData = totalData.filter(d => d.criterion === criterion); | |
chartSubTitle.text(filteredData[0].description); | |
xScale.domain(filteredData[0].groups.map(d => d.name)); | |
colorScale.domain(filteredData[0].groups.map(d => d.name)); | |
var counterData = 0; | |
filteredData[0].groups.forEach(d=>{ | |
var rounded = Math.round(d.value); | |
for(var i = counterData; i < rounded + counterData; i++){ | |
dataNodes[i].id = d.name; | |
dataNodes[i].xPosition = xScale(d.name); | |
}; | |
counterData += rounded; | |
}); | |
nodes.transition() | |
.duration(500) | |
.attr("fill", d => colorScale(d.id)); | |
var newNodesData = svg.selectAll(".nodesData") | |
.data(filteredData[0].groups, d => d.name + d.value); | |
var newNodesDataExit = newNodesData.exit() | |
.remove(); | |
var newNodesDataEnter = newNodesData.enter() | |
.append("text") | |
.attr("class", "nodesData") | |
.attr("text-anchor", "middle") | |
.attr("y", 100) | |
.attr("x", d => xScale(d.name)) | |
.text(d => d.name + ": ") | |
.append("tspan") | |
.attr("class", "nodesDataSpan") | |
.text(d => Math.round(d.value)); | |
var newNodesDataPrecise = svg.selectAll(".nodesDataPrecise") | |
.data(filteredData[0].groups, d => d.name + d.value); | |
newNodesDataPrecise.exit() | |
.transition() | |
.delay(750) | |
.duration(10) | |
.remove(); | |
newNodesDataPrecise.enter() | |
.append("text") | |
.attr("class", "nodesDataPrecise") | |
.attr("text-anchor", "middle") | |
.attr("y", 440) | |
.attr("x", d => xScale(d.name)) | |
.transition() | |
.delay(750) | |
.duration(10) | |
.text(d => d.name + ": " + d.value + "%"); | |
var bars = svg.selectAll(".bars") | |
.data(filteredData[0].groups); | |
var barExit = bars.exit() | |
.transition() | |
.delay(750) | |
.duration(500) | |
.attr("x", width - padding[1]) | |
.attr("width", 0) | |
.remove(); | |
var barsEnter = bars.enter() | |
.append("rect") | |
.attr("class", "bars") | |
.attr("x", width - padding[1]) | |
.merge(bars) | |
.attr("y", 470) | |
.attr("height", 20); | |
barsEnter.transition() | |
.delay(750) | |
.duration(500) | |
.attr("x", (d,i) =>{ | |
if(i === 0){ | |
return barScale(0); | |
} else { | |
var counter = 0; | |
for(var j = 0; j < i; j++){ | |
counter += filteredData[0].groups[j].value; | |
}; | |
return barScale(counter); | |
} | |
}) | |
.attr("width", d => barScale(d.value) - padding[3]) | |
.attr("fill", d => colorScale(d.name)); | |
var polylines = svg.selectAll(".polylines") | |
.data(filteredData[0].groups); | |
var polylinesExit = polylines.exit() | |
.transition() | |
.delay(750) | |
.duration(500) | |
.attr("points", "" + width + ",470 " | |
+ width + ",458 " + width + ",458 " | |
+ width + ",446") | |
.remove(); | |
var polylinesEnter = polylines.enter() | |
.append("polyline") | |
.attr("class", "polylines") | |
.attr("points", "" + width + ",470 " | |
+ width + ",458 " + width + ",458 " | |
+ width + ",446") | |
.merge(polylines) | |
.transition() | |
.delay(750) | |
.duration(500) | |
.attr("points", (d,i) =>{ | |
if(i === 0){ | |
return "" + (barScale(0) + (barScale(filteredData[0].groups[0].value))/2) + ",470 " + (barScale(0) + (barScale(filteredData[0].groups[0].value))/2) + ",458 " | |
+ xScale(d.name) + ",458 " + xScale(d.name) + ",446"; | |
} else { | |
var counter = 0; | |
for(var j = 0; j < i; j++){ | |
counter += filteredData[0].groups[j].value; | |
}; | |
var thisWidth = barScale(counter); | |
return "" + (thisWidth + (barScale(d.value) - padding[3])/2) + ",470 " + (thisWidth + (barScale(d.value) - padding[3])/2) + ",458 " + xScale(d.name) + ",458 " + xScale(d.name) + ",446"; | |
} | |
}) | |
.attr("stroke-width", 1) | |
.attr("fill", "none") | |
.attr("stroke", "darkslategray"); | |
setTimeout(function(){ | |
simulation.nodes(dataNodes); | |
simulation.alpha(0.8).restart(); | |
}, 750) | |
//end of redraw | |
}; | |
d3.selectAll(".criterionList").on("click", function(d){ | |
redraw(d); | |
}); | |
//end of draw | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment