Testing out selecting neighboring geographic units. Uses TopoJSON’s topojson.neighbors(objects)
function.
Hover to highlight a county and it’s neighboring counties. This lets you see how a county compares to its immediate neighbors for a given variable, here the 2014 unemployment rate.
Data sources: Unemployment data from U.S. Bureau of Labor Statistics. Shapefile from U.S. Census Bureau, cleaned up with QGIS, simplified with mapshaper.org. Color palette from ColorBrewer
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Neighboring Geography Selection</title>
<style>
body {
font: 9px sans-serif;
}
.county {
stroke: slategrey;
stroke-width: 1px;
}
.bar {
stroke: lightgrey;
stroke-width: .5px;
}
.q-0 { fill: #f7f7f7; }
.q-1 { fill: #d9d9d9; }
.q-2 { fill: #bdbdbd; }
.q-3 { fill: #969696; }
.q-4 { fill: #636363; }
.q-5 { fill: #252525; }
.hovered {
webkit-transition: 250ms;
-moz-transition: 250ms;
-o-transition: 250ms;
transition: 250ms;
fill: red;
font-weight: bold;
}
.neighbor {
webkit-transition: 250ms;
-moz-transition: 250ms;
-o-transition: 250ms;
transition: 250ms;
fill: rgb(255, 129, 129);
font-weight: bold;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis path {
display: none;
}
svg {
position: absolute;
}
.map {
left: 300px;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script>
var margin = {top: 10, left: 70, bottom: 40, right: 20},
width = 960/2 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var projection = d3.geo.conicConformal()
.rotate([90, 0])
.center([.2, 44.75])
.parallels([29.5, 45.5])
.scale(6000)
.translate([
(width + margin.left + margin.right) / 2,
(height + margin.top + margin.bottom) / 2
])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var scale = {
x: d3.scale.linear().range([0, width]),
y: d3.scale.ordinal().rangeBands([height, 0], .1),
color: d3.scale.quantize()
.range(d3.range(6))
};
var axis = {
x: d3.svg.axis().scale(scale.x).orient("bottom"),
y: d3.svg.axis().scale(scale.y).orient("left")
};
var chart = d3.select("body").append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var map = d3.select("body").append("svg")
.attr("class", "map")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
chart.append("g")
.attr("class", "y axis");
d3.json("wi.json", ready)
function ready(error, wi) {
if (error) throw error;
var countyData = cleanData(wi);
scale.x.domain([0, d3.max(countyData, function(d) { return d.unemployment_rate; })]);
scale.y.domain(countyData.map(function(d) { return d.name; }));
scale.color.domain(d3.extent(countyData, function(d) { return d.unemployment_rate; }));
// Draw map
var counties = map.selectAll(".county").data(countyData)
.enter().append("path")
.attr("class", function(d) { return "county q-" + scale.color(d.unemployment_rate); })
.attr("d", path)
.on("mouseenter", mouseenter)
.on("mouseleave", mouseleave);
// Draw bar chart
var bars = chart.selectAll(".bar").data(countyData)
.enter().append("rect")
.attr("x", function(d) { return scale.x(0); })
.attr("y", function(d) { return scale.y(d.name); })
.attr("width", function(d) { return scale.x(d.unemployment_rate); })
.attr("height", function(d) { return scale.y.rangeBand(); })
.attr("class", function(d) { return "bar q-" + scale.color(d.unemployment_rate); })
.on("mouseenter", mouseenter)
.on("mouseleave", mouseleave);
var labels = chart.selectAll(".label").data(countyData)
.enter().append("text")
.attr("x", function(d) { return scale.x(d.unemployment_rate); })
.attr("y", function(d) { return scale.y(d.name); })
.attr("dx", 3)
.attr("dy", 7)
.style("text-anchor", "start")
.text(function(d) { return d.unemployment_rate; });
chart.select(".x.axis")
.call(axis.x)
.append("text")
.attr("x", width)
.attr("y", 30)
.style("text-anchor", "end")
.text("Unemployment Rate (%)");
chart.select(".y.axis")
.call(axis.y);
function mouseenter(hovered) {
counties
.classed("hovered", function(d) { return d === hovered; })
.classed("neighbor", function(d) { return hovered.neighbors.indexOf(d) !== -1; });
bars
.classed("hovered", function(d) { return d === hovered; })
.classed("neighbor", function(d) { return hovered.neighbors.indexOf(d) !== -1; });
labels
.classed("hovered", function(d) { return d === hovered; })
.classed("neighbor", function(d) { return hovered.neighbors.indexOf(d) !== -1; });
chart.select(".y.axis").selectAll("g")
.classed("hovered", function(name) { return name === hovered.name; })
.classed("neighbor", function(name) {
return hovered.neighbors
.filter(function(d) { return d.name === name; }).length > 0;
});
}
function mouseleave() {
counties
.classed("hovered", false)
.classed("neighbor", false);
bars
.classed("hovered", false)
.classed("neighbor", false);
labels
.classed("hovered", false)
.classed("neighbor", false);
chart.select(".y.axis").selectAll("g")
.classed("hovered", false)
.classed("neighbor", false);
}
}
function cleanData(wi) {
var features = topojson.feature(wi, wi.objects.wi).features,
neighbors = topojson.neighbors(wi.objects.wi.geometries),
countyData = features
.map(function(d,i) {
d.neighbors = features.filter(function(d1, i1) {
return neighbors[i].indexOf(i1) !== -1;
});
d.unemployment_rate = +d.properties["wi-data__2"];
d.name = d.properties.NAME;
return d;
})
.sort(function(a, b) {
return b.unemployment_rate - a.unemployment_rate;
});
return countyData;
}
d3.select(self.frameElement)
.style("width", 800 + "px")
.style("height", (height + margin.top + margin.bottom) + "px");
</script>
</body>
</html>