|
<!DOCTYPE html> |
|
|
|
<head> |
|
<meta charset="utf-8"> |
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=yes" /> |
|
</head> |
|
|
|
<body> |
|
<svg width="960" height="600" stroke-linejoin="round" stroke-linecap="round"> |
|
<defs></defs> |
|
<rect></rect> |
|
<g class="counties"></g> |
|
<g class="states"></g> |
|
</svg> |
|
|
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> |
|
<script src="https://unpkg.com/[email protected]"></script> |
|
<script> |
|
var svg = d3.select('svg'); |
|
|
|
// background invisible rect to help with resetting transformations |
|
d3.select('rect') |
|
.attr('width', svg.attr('width')) |
|
.attr('height', svg.attr('height')) |
|
.style('fill', 'none') |
|
.style('pointer-events', 'all') |
|
.on('click', reset); |
|
|
|
var gCounties = d3.select('g.counties'), |
|
gStates = d3.select('g.states'), |
|
defs = svg.select('defs'), |
|
activeState = d3.select(null), |
|
originalStrokeWidth = 0.5, |
|
transitionDuration = 750, |
|
unemployment = d3.map(), |
|
path = d3.geoPath(); |
|
|
|
var colorScale = d3.scaleThreshold() |
|
.domain(d3.range(2, 10)) // input bounds |
|
.range(d3.schemeRdPu[9]); // output bounds |
|
|
|
var blurScale = d3.scaleThreshold() |
|
.domain(d3.range(1, 10)) // input bounds |
|
.range(d3.range(0, 4.5, 0.5).reverse()); // output bounds |
|
|
|
d3.queue() |
|
.defer(d3.json, 'https://unpkg.com/us-atlas@1/us/10m.json') |
|
.defer(d3.tsv, 'unemployment.tsv', function(d) { |
|
unemployment.set(d.id, +d.rate); |
|
}) |
|
.await(ready); |
|
|
|
function ready(error, us) { |
|
if (error) throw error; |
|
|
|
// generate filter defs for every unique range value in the blurScale |
|
defs.selectAll('filter') |
|
.data(blurScale.range()) |
|
.enter().append('filter') |
|
.attr('id', function(blurRangeValue) { |
|
return 'blur_' + blurRangeValue; |
|
}) |
|
.append('feGaussianBlur') |
|
.attr('stdDeviation', function(blurRangeValue) { |
|
return blurRangeValue; |
|
}); |
|
|
|
gCounties.selectAll('path') |
|
.data(topojson.feature(us, us.objects.counties).features) |
|
.enter().append('path') |
|
.style('stroke', 'black') |
|
.style('stroke-width', originalStrokeWidth) |
|
.style('fill', function(countyGeoJson) { |
|
countyGeoJson.rate = unemployment.get(countyGeoJson.id); |
|
return colorScale(countyGeoJson.rate); |
|
}) |
|
.style('filter', function(d) { |
|
return 'url(#blur_' + blurScale(d.rate) + ')'; |
|
}) |
|
.style('cursor', 'pointer') |
|
.attr('d', path) |
|
.on('click', reset) |
|
.append('title') |
|
.text(function(countyGeoJson) { |
|
return countyGeoJson.rate + '% unemployment'; |
|
}); |
|
|
|
gStates.selectAll('path') |
|
.data(topojson.feature(us, us.objects.states).features) |
|
.enter().append('path') |
|
.style('stroke', 'transparent') |
|
.style('fill', 'transparent') |
|
.style('cursor', 'pointer') |
|
.attr('d', path) |
|
.on('click', clicked); |
|
} |
|
|
|
function clicked(stateGeoJson) { |
|
if (activeState.node() === this) { |
|
return reset(); |
|
} |
|
|
|
activeState.classed('active', false); |
|
activeState = d3.select(this).classed('active', true); |
|
|
|
var width = svg.attr('width'), |
|
height = svg.attr('height'), |
|
bounds = path.bounds(stateGeoJson), |
|
dx = bounds[1][0] - bounds[0][0], |
|
dy = bounds[1][1] - bounds[0][1], |
|
x = (bounds[0][0] + bounds[1][0]) / 2, |
|
y = (bounds[0][1] + bounds[1][1]) / 2, |
|
scale = .9 / Math.max(dx / width, dy / height), |
|
translate = [width / 2 - scale * x, height / 2 - scale * y]; |
|
|
|
gCounties |
|
.transition() |
|
.duration(transitionDuration) |
|
.attr('transform', 'translate(' + translate + ')scale(' + scale + ')') |
|
.selectAll('path') |
|
.style('stroke-width', originalStrokeWidth / scale + 'px') |
|
|
|
gStates |
|
.attr('transform', 'translate(' + translate + ')scale(' + scale + ')') |
|
.selectAll('path') |
|
// set to none to allow for county-level tooltips |
|
.style('stroke', 'none') |
|
.style('fill', 'none'); |
|
} |
|
|
|
function reset() { |
|
activeState.classed('active', false); |
|
activeState = d3.select(null); |
|
|
|
gCounties |
|
.transition() |
|
.duration(transitionDuration) |
|
.attr('transform', '') |
|
.selectAll('path') |
|
.style('stroke-width', originalStrokeWidth + 'px'); |
|
|
|
gStates |
|
.attr('transform', '') |
|
.selectAll('path') |
|
// set to transparent to allow for clicks at the continental view |
|
.style('stroke', 'transparent') |
|
.style('fill', 'transparent'); |
|
} |
|
</script> |
|
|
|
</body> |