Connor Ingram U.S. County Interactive Population Visualization (2013)
Data from https://en.wikipedia.org/wiki/List_of_United_States_counties_and_county_equivalents and corresponding sources.
The data contains: """ The International Committee for Information Technology Standards (INCITS) code The entity name The state or federal district The population as of July 1, 2013, as estimated by United States Census Bureau """ I use https://d3js.org/us-10m.v1.json for the map visualization, which contains the INCITS code for each county. The population data is bound on creation of the path elements.
Im using the null projection (d3.geoPath() with no projection specified). Since the input geometries are pre-projected, another projection would slow down performace significantly. I originally tested projections with different topologies such as geoMercator(), but had trouble finding a small enough geojson that would run smoothly enough at the county-level.
xxxxxxxxxx
<html>
<head>
<title>US County Populations - D3 Visualization</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<style>
.state-border{
fill: none;
stroke-width: 1.5px;
}
.county-border {
fill:none;
stroke-width: 0.5px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 80px;
height: 95px;
padding: 2px;
border: 0px;
border-radius: 4px;
font: 12px sans-serif;
background: white;
pointer-events: none;
}
.title {
text-align: center;
font: 20px sans-serif;
fill: steelblue;
}
.text {
font: 12px sans-serif;
pointer-events: none;
}
.label {
font: 12px sans-serif;
text-align: center;
font-weight: bold;
pointer-events: none;
text-anchor: middle;
}
.group-border {
fill: none;
stroke: lightsteelblue;
stroke-width: 2px;
}
body {
margin: 0px;
}
</style>
</head>
<body>
<script>
var margin = {top: 10, right: 10, bottom: 10, left: 10};
var width = 960 - margin.top - margin.bottom;
var height = 500 - margin.left - margin.right;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var group = svg.append("g");
svg.append("rect")
.attr("class", "group-border")
.attr("width", width)
.attr("height", height);
function scale (scaleFactor) {
return d3.geoTransform({
point: function(x, y) {
this.stream.point(x * scaleFactor, y * scaleFactor);
}
});
}
var path = d3.geoPath().projection(scale(0.8));
var features = group.append("g");
var zoom = d3.zoom()
.scaleExtent([0,100])
.on("zoom", zoomed);
group.call(zoom);
var url = "https://d3js.org/us-10m.v1.json";
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
d3.queue()
.defer(d3.json, url)
.await(ready);
function ready(err, us) {
if (err) console.log(err);
console.log(us);
d3.csv("us-county-names.csv", function(data) {
data.forEach(function(d) {
d.Pop = +d.Pop.replace(/,\s?/g, "");
});
var maxPop = d3.max(data, function(d) { return d.Pop; });
var minPop = d3.min(data, function(d) { return d.Pop; });
console.log(maxPop);
console.log(minPop);
var colorscale = d3.scaleLog()
.domain([minPop, maxPop])
.range([0,1]);
// population color legend
var legendX = width - 100;
var legendY = height - 145;
var legendBg = svg.append("rect")
.attr("class", "legendBg")
.attr("rx", 5)
.attr("ry", 5)
.attr("x", legendX)
.attr("y", legendY - 20)
.attr("fill", "lightsteelblue")
.style("opacity", .9)
.attr("width", 95)
.attr("height", 160);
var legend = svg.selectAll(".legend")
.data(d3.scaleSequential(d3.interpolateGnBu).ticks(5).reverse())
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + (legendX + 8) + "," + ((legendY + 8)+ i * 20) + ")"; });
legend.append("rect")
.attr("width", 20)
.attr("height", 20)
.style("fill", d3.scaleSequential(d3.interpolateGnBu));
legend.append("text")
.attr("class", "text")
.attr("x", 26)
.attr("y", 10)
.attr("dy", ".35em")
.text(function(d, i) {
return Math.round(d * maxPop);
});
svg.append("text")
.attr("class", "label")
.attr("x", legendX + 48)
.attr("y", legendY)
.text("Population");
// end population color legend
// Toggle Borders button
svg.append("rect")
.attr("class", "border-btn")
.attr("width", 95)
.attr("height", 25)
.attr("x", legendX)
.attr("y", legendY - 50)
.attr("rx", 5)
.attr("ry", 5)
.attr("fill", "lightsteelblue")
.on("mouseover", function () {
d3.select(this).attr("fill", "orange");
})
.on("mouseout", function() {
d3.select(this).attr("fill", "lightsteelblue")
})
.on("click", function () {
var stroke = d3.selectAll(".county-border").attr("stroke");
if (stroke == "#fff") {
d3.selectAll(".county-border").attr("stroke", "none");
}
else {
d3.selectAll(".county-border").attr("stroke", "#fff");
}
});
svg.append("text")
.attr("class", "label")
.attr("x", legendX + 48)
.attr("y", legendY - 33)
.text("Toggle borders");
// end Toggle Borders button
features.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter()
.append("path")
.attr("class", "county")
.attr("d", path)
.attr("fill", function(d) {
try {
var pop = data.filter( function(e) { return +e.INCITS == +d.id; })[0].Pop;
d.Pop = pop;
var state = data.filter( function(e) { return +e.INCITS == +d.id; })[0].State;
d.State = state;
d.Color = d3.interpolateGnBu(colorscale(d.Pop));
return d.Color;
}
catch (error) {
console.log("Error - county not found");
return "none";
}
})
.on("mouseover", function(d) {
d3.select(this).attr("fill", "orange");
div.transition(200).style("opacity", .9);
try {
var cnty = data.filter( function(e) { return +e.INCITS == +d.id; })[0].County;
div.html(cnty + ",<br><b>" + d.State + "<br><br>Population:</b><br>" + d.Pop)
.style("left", (d3.event.pageX + 10) + "px")
.style("top", (d3.event.pageY + 10) + "px");
}
catch (error) {
div.transition(200).style("opacity", 0);
}
})
.on("mouseout", function(d) {
d3.select(this).attr("fill", d.Color);
div.transition(200).style("opacity", 0);
});
features.append("path") // improved performace by not redering overlapping strokes
.datum(topojson.mesh(us, us.objects.counties, function (a,b) { return a !== b && !(a.id / 1000 ^ b.id / 1000); }))
.attr("class", "county-border")
.attr("stroke", "#fff")
.attr("d", path);
features.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "state-border")
.attr("stroke", "#fff")
.attr("d", path);
features.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", 20)
.text("U.S. County Populations");
});
}
function zoomed () {
features.attr("transform", d3.event.transform);
d3.selectAll(".state-border").style("stroke-width", 1.5 / d3.event.transform.k + "px");
d3.selectAll(".county-border").style("stroke-width", 0.5 / d3.event.transform.k + "px");
}
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js
https://d3js.org/topojson.v1.min.js