An attempt to procedurally draw the amazing data visualization of African-American demographics in 1890 by W. E. B. Du Bois based on census data. Rural/Small Town/Suburban/Urban is just a threshold for pop density of less than 10, 10 to 100, 100 to 1000 and 1000 plus people per square mile from census land area of counties.
Celebrate Black History
xxxxxxxxxx
<head>
<title>W E B Du Bois Data Visualization Remade</title>
</head>
<meta charset="utf-8">
<style>
svg {
height: 300px;
width: 300px;
display: inline-block;
}
body {
background: #e6d7c8;
}
.rural {
fill: #d9384a;
stroke: #a7212f;
background: #d9384a;
border: 1px solid #a7212f;
}
.urban {
fill: #3e6454;
stroke: #4f5e51;
background: #3e6454;
border: 1px solid #4f5e51;
}
.suburban {
fill: #5a74b0;
stroke: #47527a;
background: #5a74b0;
border: 1px solid #47527a;
}
.smalltown {
fill: #f4b32a;
stroke: #c9a15a;
background: #f4b32a;
border: 1px solid #c9a15a;
}
.final {
stroke: none;
}
.legend {
height: 200px;
width: 200px;
padding: 40px;
display: inline-block;
vertical-align: top;
}
.legend > div {
line-height: 40px;
}
.legend-item {
display: inline-block;
width: 30px;
height: 30px;
margin-right: 15px;
}
path {
stroke-linejoin: round;
stroke: none;
}
#viz > div {
display: inline-block;
width: 400px;
height: 100px;
}
</style>
<body>
<div id="viz">
<div class="legend">
<div><div class="legend-item urban"></div>Urban</div>
<div><div class="legend-item suburban"></div>Suburban</div>
<div><div class="legend-item smalltown"></div>Small Town</div>
<div><div class="legend-item rural"></div>Rural</div>
</div>
<div><a href="https://www.loc.gov/pictures/resource/ppmsca.33873/?co=anedub" target="_blank">Based on a data visualization of African-American demographics in 1890 by W. E. B. Du Bois</a>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js" type="text/JavaScript"></script>
<script src="states.js" type="text/JavaScript"></script>
<script src="fipsToState.js" type="text/JavaScript"></script>
<script>
["white", "hispanic", "black", "asian", "nativeamerican", "hawaiipacificisland", "multiracial"].forEach(race => {
const div = d3.select("body").append("div")
div.append("h1")
.html(race)
.style("text-align", "center")
Object.keys(fipsToState).forEach(fip => {
const svg = div
.append("svg")
.attr("class", "f" + fip)
.attr("width", 300)
.attr("height", 300)
drawState(svg, fip)
})
function drawState(svg, selectedStateID) {
const selectedState = states.filter(d => d.id === selectedStateID)[0]
if (!selectedState) {
return
}
const ruralPopulation = selectedState.rural ? selectedState.rural[race] : 0
const urbanPopulation = selectedState.urban ? selectedState.urban[race] : 0
const suburbanPopulation = selectedState.suburban ? selectedState.suburban[race] : 0
const smallTownPopulation = selectedState.smalltown ? selectedState.smalltown[race] : 0
const piScale = d3.scaleLinear().domain([0,20]).range([0,Math.PI * 2])
let newData = []
const duboisData = [
{ type: "rural", value: ruralPopulation },
{ type: "smalltown", value: smallTownPopulation },
{ type: "suburban", value: suburbanPopulation },
{ type: "urban", value: urbanPopulation }
]
.sort((a,b) => a.value - b.value)
const max = duboisData[3].value
const popScale = d3.scaleLinear().domain([0, max]).range([0,100])
const ruralData = d3.range(popScale(ruralPopulation))
const urbanData = d3.range(popScale(urbanPopulation))
const suburbanData = d3.range(popScale(suburbanPopulation))
const smallTownData = d3.range(popScale(smallTownPopulation))
svg
.append("g")
.attr("transform", `translate(${100 - popScale(duboisData[2].value)},50)`)
.attr("class", "dubois")
svg
.append("text")
.text(fipsToState[selectedStateID])
.style("text-anchor", "middle")
.attr("x", 100)
.attr("y", 30)
const topArea = d3.area()
.x(d => d)
.y0(0)
.y1(10)
svg.select("g.dubois")
.append("path")
.attr("class", duboisData[2].type)
.attr("transform", "translate(0,0)")
.attr("d", topArea(d3.range(popScale(duboisData[2].value))))
const secondArea = d3.area()
.x0(d => popScale(duboisData[0].value) - d)
.x1(d => popScale(duboisData[0].value) - d + 10)
.y0(d => d)
.y1(d => d)
const secondOffset = popScale(duboisData[2].value) - popScale(duboisData[0].value) - 10
svg.select("g.dubois")
.append("path")
.attr("class", duboisData[0].type)
.attr("transform", `translate(${secondOffset},10)`)
.attr("d", secondArea(d3.range(popScale(duboisData[0].value))))
const thirdOffset = secondOffset
const thirdOffsetY = 10 + popScale(duboisData[0].value)
const thirdArea = d3.area()
.x0(d => d)
.x1(d => d + 10)
.y0(d => d)
.y1(d => d)
svg.select("g.dubois")
.append("path")
.attr("class", duboisData[1].type)
.attr("transform", `translate(${thirdOffset},${thirdOffsetY})`)
.attr("d", thirdArea(d3.range(popScale(duboisData[1].value))))
let fourthOffsetY = thirdOffsetY + popScale(duboisData[1].value)
const connectorAmount = Math.min(50, popScale(duboisData[3].value))
let fourthOffset = thirdOffset + popScale(duboisData[1].value) - connectorAmount
const radialConnectorArea = d3.area()
.x0(d => connectorAmount - d)
.x1(d => connectorAmount - d + 10)
.y0(d => d)
.y1(d => d)
svg.select("g.dubois")
.append("path")
.attr("class", "final " + duboisData[3].type)
.attr("transform", `translate(${fourthOffset},${fourthOffsetY})`)
.attr("d", radialConnectorArea(d3.range(connectorAmount)))
const spiralData = Math.max(popScale(duboisData[3].value) - 50, 0) + 6
const fifthOffsetY = fourthOffsetY + spiralData / 2 + connectorAmount + 3
const fifthOffset = fourthOffset + connectorAmount - 5
const radialArea = d3.radialArea()
.angle(d => -piScale(d%20) - 0.57)
.innerRadius(d => d >= spiralData - 6 ? spiralData - d : spiralData - d - 8)
.outerRadius(d => spiralData - d)
.curve(d3.curveBasis)
svg.select("g.dubois")
.append("path")
.attr("class", "final " + duboisData[3].type)
.attr("transform", `translate(${fifthOffset},${fifthOffsetY})`)
.attr("d", radialArea(d3.range(spiralData)))
}
})
</script>
https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js