Taking advantage of the forceSimulation-based nature of the cartogram to switch to a bubble chart using the same data.
Demographics from http://library.duke.edu/data/collections/popest
forked from emeeks's block: Cartogram to Bubble Chart
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 to Bubble Chart</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]
var notcarto = true
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() {
if (notcarto) {
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))
.force("x", d3.forceX(d => d.cx))
.force("y", d3.forceY(d => d.cy))
.alpha(1)
.restart()
}
else {
var positionScale = d3.scaleCluster().domain(thisKeyValues).range([200,400,600,800,1000])
force
.force("x", d3.forceX(d => positionScale(d[selectedValue])))
.force("y", d3.forceY(400))
.alpha(1)
.restart()
}
notcarto = !notcarto
}
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