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
<!DOCTYPE html>
<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="//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>
const fipsToState = {
"2": "ALASKA",
"28": "MISSISSIPPI",
"1": "ALABAMA",
"30": "MONTANA",
"5": "ARKANSAS",
"37": "NORTH CAROLINA",
"60": "AMERICAN SAMOA",
"38": "NORTH DAKOTA",
"4": "ARIZONA",
"31": "NEBRASKA",
"6": "CALIFORNIA",
"33": "NEW HAMPSHIRE",
"8": "COLORADO",
"34": "NEW JERSEY",
"9": "CONNECTICUT",
"35": "NEW MEXICO",
"11": "DISTRICT OF COLUMBIA",
"32": "NEVADA",
"10": "DELAWARE",
"36": "NEW YORK",
"12": "FLORIDA",
"39": "OHIO",
"13": "GEORGIA",
"40": "OKLAHOMA",
"66": "GUAM",
"41": "OREGON",
"15": "HAWAII",
"42": "PENNSYLVANIA",
"19": "IOWA",
"72": "PUERTO RICO",
"16": "IDAHO",
"44": "RHODE ISLAND",
"17": "ILLINOIS",
"45": "SOUTH CAROLINA",
"18": "INDIANA",
"46": "SOUTH DAKOTA",
"20": "KANSAS",
"47": "TENNESSEE",
"21": "KENTUCKY",
"48": "TEXAS",
"22": "LOUISIANA",
"49": "UTAH",
"25": "MASSACHUSETTS",
"51": "VIRGINIA",
"24": "MARYLAND",
"78": "VIRGIN ISLANDS",
"23": "MAINE",
"50": "VERMONT",
"26": "MICHIGAN",
"53": "WASHINGTON",
"27": "MINNESOTA",
"55": "WISCONSIN",
"29": "MISSOURI",
"54": "WEST VIRGINIA",
"56": "WYOMING"
}