** Version 2 includes county-by-county line maps, removable with a click. **
Based on Mike Bostock's coropleth, I used a interpolator instead. Data for unemployment by year is from the Bureau of Labor Statistics. I'm planning to modularize this design and make it slimmer in order to be able to punch in other county-based data, so more to come.
Built with blockbuilder.org. Legend plug-in from Susie Lu (http://d3-legend.susielu.com/).
forked from KingOfCramers's block: Unemployment in the United States by County
xxxxxxxxxx
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
<style>
text {
font-family: "Raleway"
}
#start{
display: block;
position: absolute;
top: 300px;
left: 400px;
width: 100px;
color: white;
background-color: green;
border: none;
padding: 10px;
font-size: 15px;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
#tooltip{
display: block;
position: absolute;
top: 300px;
left: 400px;
max-width: 150px;
z-index: 1000;
background-color: white;
padding: 5px;
font-family: Raleway;
font-size: 11px;
opacity: 0;
}
#tooltipSVG {
position: absolute;
z-index: 1000;
background-color: white;
opacity: 0;
}
.active {
stroke-width: 1px;
stroke: white;
}
.inactive {
stroke: black;
stroke-width: .25px;
}
.showing {
display: block;
}
.notShowing {
display: none;
}
#tooltip span.positive {
color: red;
}
#tooltip span.negative {
color: green;
}
#title {
font-family: Raleway;
position: absolute;
display: block;
margin: 20px;
fill: white;
font-size: 35px;
box-shadow: 2px solid black;
}
#miniPath {
fill: none;
stroke: black;
}
.states{
fill: none;
stroke: lightgrey;
stroke-width: 1px;
}
text.label {
fill: white;
}
g.legendLinear{
background-color: white;
}
circle.cities {
fill: red;
stroke: red;
}
</style>
<body>
</body>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<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://d3js.org/topojson.v2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.4/d3-legend.js"></script>
<script>
var width = 960;
var height = 600;
var margin = {top: 0, bottom: 0, left: 0, right: 0}
var path = d3.geoPath(); // Geopath generator
var startYear = 2007;
var unemployment = new Map();
var year = startYear;
var color = d3.scaleSequential()
.domain([1.0,30.0])
.interpolator(d3.interpolateInferno);
d3.queue()
.defer(d3.json, "https://d3js.org/us-10m.v1.json")
.defer(d3.csv, "unemploymentEdited.csv", function(d){
unemployment.set(d.county, [+d.rate2007,+d.rate2008,+d.rate2009,+d.rate2010,+d.rate2011,+d.rate2012,+d.rate2013,+d.rate2014,+d.rate2015,+d.rate2016,d.area])
})
.await(ready)
function ready(error, us) {
// ZOOM, SVG, VARS
var zoomExtent = d3.zoom().scaleExtent([1, 20]);
function zoom() {
g.attr("transform", d3.event.transform)
}
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("background-color","lightgrey")
.call(zoomExtent
.on("zoom", zoom))
// DEFINE LINECHART FOR TOOLTIP
var miniMargin = {top: 10, bottom: 20, left: 15, right: 10}
var miniWidth = 200
var miniHeight = 100
var parseMiniYear = d3.timeParse("%Y")
function generateLine(countyId){
let newData = unemployment.get(countyId).slice(0,10)
let countyRange = d3.extent(newData)
let countyXScale = d3.scaleTime()
.domain([parseMiniYear(startYear), parseMiniYear(startYear + newData.length)])
.range([15, miniWidth - miniMargin.right - miniMargin.left])
let countyYScale = d3.scaleLinear()
.domain(countyRange)
.nice()
.range([miniHeight - miniMargin.top - miniMargin.bottom, miniMargin.top])
let miniXAxis = d3.axisBottom().scale(countyXScale).ticks(5)
let miniYAxis = d3.axisLeft().scale(countyYScale).ticks(4)
d3.select("#miniXGroup").call(miniXAxis)
d3.select("#miniYGroup").call(miniYAxis)
let lineGen = d3.line()
.x((d,i) => countyXScale(parseMiniYear(i + startYear)))
.y((d) => countyYScale(d))
.curve(d3.curveMonotoneX)
var countySelect = d3.select("#miniPath")
.attr("d", lineGen(newData))
countySelect.exit().remove();
}
// TOOLTIP
var tooltipContainer = d3.select("body")
.append("div")
var tooltip = tooltipContainer
.append("div")
.attr("id","tooltip")
.attr("pointer-events","none")
var tooltipSVG = tooltipContainer
.append("svg")
.attr("id","tooltipSVG")
.attr("width", 200)
.attr("height", 100)
.attr("pointer-events","none")
var miniG = tooltipSVG.append("g").attr("id","miniG")
.attr("transform",`translate(${miniMargin.left},${miniMargin.top})`)
miniG.append("path").attr("id","miniPath")
miniG.append("g").attr("id","miniXGroup")
.attr("transform",`translate(0,${miniHeight - miniMargin.top - miniMargin.bottom})`)
miniG.append("g").attr("id","miniYGroup")
.attr("transform",`translate(${miniMargin.left},0)`)
var g = svg.append("g")
.attr("class", "counties")
.attr("transform",`translate(8,${margin.top})`)
.on("mouseleave", function(){
d3.select("#tooltip")
.transition()
.style("opacity",0)
d3.select("#tooltipSVG")
.transition()
.style("opacity",0)
})
.on("mouseover", function(){
d3.select("#tooltip")
.transition()
.style("opacity",1)
d3.select("#tooltipSVG")
.transition()
.style("opacity",1)
})
// LEGEND
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", `translate(${width-420},${height-90})`);
var legendLinear = d3.legendColor()
.shapeWidth(35)
.shapePadding(3)
.cells(10)
.orient('horizontal')
.scale(color);
svg.select(".legendLinear")
.call(legendLinear);
d3.select("text.label").text("1.0%")
// PATHS
var paths = g.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("fill", function(d){
return unemployment.get(d.id) ?
color(unemployment.get(d.id)[0]) : "white";
})
.attr("d", path)
.attr("class","inactive")
.on("mouseover",function(d){
var countyName = unemployment.get(d.id)[10]
d3.select(this)
.attr("class","active")
.raise();
d3.selectAll(".states").raise();
d3.select("#tooltip").html(function(){
let isLastYear = d3.select("#year").text() == 2016;
let unemplThatYear = unemployment.get(d.id)[year - startYear]
if(year != 2007 || isLastYear){
var rate = unemployment.get(d.id)[year-startYear] - unemployment.get(d.id)[year-2008] >= 0 ?
"positive" : "negative";
if(isLastYear){
let tempYear = 2016;
var tempRate = unemployment.get(d.id)[tempYear-startYear] - unemployment.get(d.id)[tempYear-2008] >= 0 ?
"positive" : "negative";
let tempUnemplThatYear = unemployment.get(d.id)[tempYear - startYear]
return `${countyName}: ${tempUnemplThatYear}% <span class=${rate}>(${(unemployment.get(d.id)[tempYear-startYear] - unemployment.get(d.id)[2016-2008]).toFixed(2)})</span>`
}
return `${countyName}: ${unemplThatYear}% <span class=${rate}>(${(unemployment.get(d.id)[year-startYear] - unemployment.get(d.id)[year-2008]).toFixed(2)})</span>`
}
else {
return `${countyName}: ${unemplThatYear}%`
}
})
generateLine(d3.select(this).data()[0].id)
})
.on("mousemove", function(d,event){
let tooltipHeight = $("#tooltip").height();
d3.select("#tooltip").style("left", () => d3.event.x + 15 + "px")
d3.select("#tooltip").style("top", () => d3.event.y - tooltipHeight - 20 + "px")
d3.select("#tooltipSVG").style("top", () => d3.event.y - 5 + "px")
d3.select("#tooltipSVG").style("left", () => d3.event.x + 15 + "px")
})
.on("mouseleave",function(){
d3.select(this)
.attr("class","inactive")
})
.on("click", function(){
if(d3.select("#tooltipSVG").attr("class") == "showing"){
d3.select("#tooltipSVG").attr("class", "notShowing")
}
else{
d3.select("#tooltipSVG").attr("class","showing")
}
})
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "states")
.attr("d", path);
// YEAR TEXT
svg.append("text")
.text(startYear)
.attr("x",width - 205)
.attr("y",56)
.attr("id","year")
.style("font-size", "45px")
.style("fill","white")
.style("opacity",0)
// START BUTTON
d3.select("body")
.append("button")
.attr("type","button")
.text("Start")
.attr("id","start")
d3.select("#start").on("click", function(){
d3.select(this).transition().style("opacity",0)
d3.select("#title").transition().style("opacity",0).remove();
d3.select("#year").transition().style("opacity",1)
var runAnimation = setInterval(wrapper, 2500);
function wrapper(){
if(year<2016){
year++
recolor(year);
}
else{
clearInterval(runAnimation)
year = startYear;
d3.select("#start")
.attr("z-index",-1)
.text("Restart")
.style("background-color","orange")
.transition().style("opacity",1)
}
}
})
// RECOLORING CODE
function recolor(x){
d3.selectAll("g.counties path")
.transition()
.duration(500)
.attr("fill", function(d){
return unemployment.get(d.id) ? color(unemployment.get(d.id)[x-startYear]) : "white";
})
.on("end", function(d){
$("#year").text(year)
if(document.getElementsByClassName("active")[0]){
let countyTag = d3.select(".active").data()[0].id;
var rate = unemployment.get(countyTag)[x-startYear] - unemployment.get(countyTag)[x-(startYear+1)] >= 0 ?
"positive" : "negative"
$("#tooltip").html(unemployment.get(countyTag)[10] + ": " + unemployment.get(countyTag)[x-startYear] + "% " +`<span class=${rate}>(` + (unemployment.get(countyTag)[x-startYear] - unemployment.get(countyTag)[x-(startYear+1)]).toFixed(2) +")</span>")
}
})
}
// APPEND THE TITLE
var title = d3.select("svg")
.append("text")
.text("Unemployment in the United States")
.attr("id","title")
.attr("transform",`translate(200,260)`)
}
</script>
https://code.jquery.com/jquery-3.2.1.slim.min.js
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js
https://d3js.org/topojson.v2.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.4/d3-legend.js