Public
Edited
Mar 12, 2024
1 star
Insert cell
Insert cell
population = FileAttachment("population.json").json()
.then((data) =>
data
.slice(1) // removes a header line
.map(([p, state, county]) => ({
state,
fips: `${state}${county}`,
population: +p
}))
)
Insert cell
us = FileAttachment("counties-albers-10m.json").json()
Insert cell
nation = topojson.feature(us, us.objects.nation)
Insert cell
statemap = new Map(topojson.feature(us, us.objects.states).features.map(d => [d.id, d]))
Insert cell
countymap = new Map(topojson.feature(us, us.objects.counties).features.map(d => [d.id, d]))
Insert cell
// Join the geographic shapes and the population data.
data = population.map((d) => ({
...d,
county: countymap.get(d.fips),
state: statemap.get(d.state)
}))
.filter(d => d.county)
.sort((a, b) => d3.descending(a.population, b.population));
Insert cell
statemesh = topojson.mesh(us, us.objects.states, (a, b) => a !== b)
Insert cell
// Construct a path generator.
path = d3.geoPath()
Insert cell
// Construct the radius scale.
radius = d3.scaleSqrt(d3.extent(data, d => d.population), [1, 40])
Insert cell
centroid = {
const path = d3.geoPath();
return feature => path.centroid(feature)
}
Insert cell
width
Insert cell
height = 610
Insert cell
format = d3.format(",.0f")
Insert cell
presentChart = {

const zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed)
// Create the SVG container. Its dimensions correspond to the bounding-box
// of the pre-projected US shapefile.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", 610)
.attr("viewBox", [0, 0, 975, 610])
.on("click", reset)

const g = svg.append("g")

// Create the cartographic background layers.
const nationBoundary = g.append("path")
.datum(topojson.feature(us, us.objects.nation))
.attr("fill", "#ddd")
.attr("d", path)

const stateBoundary = g.append("path")
.datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", "1px")
.attr("stroke-linejoin", "round")
.attr("d", path)

// Add a circle for each county, with a title (tooltip)
const circles = g.append("g")
.attr("fill", "teal")
.attr("fill-opacity", 0.5)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
.selectAll()
.data(data)
.join("circle")
.attr("transform", d => `translate(${centroid(d.county)})`)
.attr("r", d => radius(d.population))
.attr("class", "circles")

const label = g.append("text").attr("class", "label")

circles
.on("mouseover", function(e, d){

d3.select(this)
.transition()
.duration(200)
.style("opacity", 1)
.style("stroke", "white")
.style("stroke-width", "6px")

const labelText = `${d.county.properties.name} county: \n ${format(d.population)}`

label
.attr("display", null)
.attr("font-size", 15)
.attr("font-weight", "bold")
.style("opacity", 1)
.attr("transform", `translate(${centroid(d.county)})`)
.attr("dx", 9)
.attr("dy", -7)
.text(d => labelText)

})
.on("mouseleave", function(e, d){

d3.select(this)
.transition()
.duration(200)
.style("stroke", "white")
.style("stroke-width", "1px")

label
.attr("display", "none")

})

// Create the legend
const legend = svg.append("g")
.attr("fill", "#777")
.attr("transform", "translate(915,608)")
.attr("text-anchor", "middle")
.style("font", "10px sans-serif")
.selectAll()
.data(radius.ticks(4).slice(1))
.join("g")

legend.append("circle")
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("cy", d => -radius(d))
.attr("r", radius)

legend.append("text")
.attr("y", d => -2 * radius(d))
.attr("dy", "1.3em")
.text(radius.tickFormat(4, "s"))

svg.call(zoom)

function reset() {
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width / 2, height / 2])
)
}

function zoomed(event){
const {transform} = event
g.attr("transform", transform)
g.attr("stroke-width", 1/transform.k)

d3
.selectAll(".circles")
.attr("r", d => radius(d.population)/transform.k)
d3
.selectAll(".label")
.attr("font-size", Math.floor(15/transform.k))
}

return svg.node()
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more