Cartograms distort geography to encode data. In this case, we're looking at the demography of US counties laid out using the constraints available d3's forceSimulation. Counties are positioned based on their centroid but sized based on demography, so more populated counties of that demographic will deform the position of nearby counties.
Demographics from http://library.duke.edu/data/collections/popest
xxxxxxxxxx
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Force-Based Cartogram</title>
<style>
body {
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<svg
width="1200"
height="1000"
version="1.1" id="Ebene_1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" >
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/d3-scale-cluster@1.1.7/dist/d3-scale-cluster.min.js"></script>
<script src="keys.js"></script>
<script>
var colors = [
"#d0d9c4",
"#dfa3ad",
"#d0915b",
"#a9573f",
"#844e5e"
]
var selectedValue = "tot_pop"
var selectedIndex = 0
var sizeRange = [1.5,50]
d3.csv("county_demo.csv", cartogram)
function cartogram(data) {
var dataKeys = Object.keys(keys)
data.forEach(d => {
dataKeys.forEach(k => {
d[k] = parseInt(d[k])
})
})
var thisKeyValues = data.map(d => d[selectedValue])
var extent = d3.extent(thisKeyValues)
var scale = d3.scaleLinear().domain(extent).range(sizeRange)
var colorScale = d3.scaleCluster().domain(thisKeyValues).range(colors)
var force = d3.forceSimulation()
.force("collision", d3.forceCollide(d => scale(d[selectedValue])).iterations(2))
.force("x", d3.forceX(d => d.cx))
.force("y", d3.forceY(d => d.cy))
.nodes(data)
.alphaMin(0.25)
.on("tick", updateCartogram)
.on("end", resetCartogram)
d3.select("svg")
.selectAll("circle.node")
.data(data)
.enter()
.append("circle")
.attr("class", "node")
.style("stroke-width", 2)
d3.select("svg").append("text")
.style("text-anchor", "middle")
.attr("class", "title")
.text("Total Population")
.style("font-size", "36px")
.attr("x", 600)
.attr("y", 50)
redrawNodes()
function updateCartogram() {
d3.selectAll("circle.node")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
}
function resetCartogram() {
selectedIndex = selectedIndex === dataKeys.length ? 0 : selectedIndex + 1
selectedValue = dataKeys[selectedIndex]
d3.select("text.title")
.text(keys[selectedValue])
thisKeyValues = data.map(d => d[selectedValue])
extent = d3.extent(thisKeyValues)
scale = d3.scaleLinear().domain(extent).range(sizeRange)
colorScale = d3.scaleCluster().domain(thisKeyValues).range(colors)
redrawNodes()
force.force("collision", d3.forceCollide(d => scale(d[selectedValue])).iterations(2))
.alpha(.75)
.restart()
}
function redrawNodes() {
d3.selectAll("circle.node")
.transition()
.duration(500)
.style("fill", d => colorScale(d[selectedValue]))
.style("stroke", d => d3.hsl(colorScale(d[selectedValue])).darker())
.attr("r", d => scale(d[selectedValue]))
}
}
</script>
https://d3js.org/d3.v4.min.js
https://unpkg.com/d3-scale-cluster@1.1.7/dist/d3-scale-cluster.min.js