-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
9,237 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>COVID-19 statistics by US state - Witulski</title> | ||
<style> /* set the CSS */ | ||
|
||
body { | ||
font-family: Arial; | ||
padding: 50px 5%; | ||
} | ||
|
||
svg { | ||
display: block; | ||
margin: 0 auto; | ||
} | ||
|
||
path { | ||
stroke: steelblue; | ||
stroke-width: 2; | ||
fill: none; | ||
} | ||
|
||
.axis path, | ||
.axis line { | ||
fill: none; | ||
stroke: lightgrey; | ||
stroke-width: 1; | ||
shape-rendering: crispEdges; | ||
} | ||
|
||
.legend { | ||
font-size: 16px; | ||
font-weight: bold; | ||
text-anchor: middle; | ||
opacity: 0.4; | ||
} | ||
|
||
.title { | ||
font-size: 16px; | ||
font-weight: bold; | ||
} | ||
|
||
.tooltip { | ||
line-height: 1; | ||
padding: 12px; | ||
background: grey; | ||
color: white; | ||
position: absolute; | ||
text-align: left; | ||
font: 14px sans-serif; | ||
font-weight: bold; | ||
} | ||
|
||
</style> | ||
<body> | ||
|
||
<center> | ||
<h1>COVID-19 seven day averages of new cases by US states</h1> | ||
<p><a href="index.html">See cases</a> | <a href="deaths.html">See deaths</a> | <a href="scaled_averages.html">See seven day averages scaled by population</a></p> | ||
<p>Click state names to show their graphs | Developed by <a href="http://cjwit.github.io">Christopher Witulski</a> | Last updated on <span id="date">June 26, 2020</span></p> | ||
</center> | ||
|
||
<!-- load the d3.js library --> | ||
<script src="src/d3.min.js"></script> | ||
|
||
<script> | ||
|
||
// Set the dimensions of the canvas / graph | ||
var margin = {top: 30, right: 100, bottom: 320, left: 100}, | ||
width = 800 - margin.left - margin.right, | ||
height = 800 - margin.top - margin.bottom; | ||
|
||
// Set the ranges | ||
// Change Y to scale.linear() for linear scale | ||
var x = d3.scale.linear().range([0, width]); | ||
var y = d3.scale.linear().range([height, 0]); | ||
|
||
// Define the axes | ||
var xAxis = d3.svg.axis() | ||
.scale(x) | ||
.orient("bottom") | ||
.ticks(5); | ||
|
||
var yAxis = d3.svg.axis() | ||
.scale(y) | ||
.orient("left") | ||
.tickSize(-width, 0, 0) | ||
.tickFormat(function(d) {return y.tickFormat(5,d3.format(",d"))(d)}) | ||
.ticks(5); | ||
|
||
// Define the line | ||
var dataline = d3.svg.line() | ||
.x(function(d,i) { return x(i); }) | ||
.y(function(d) { return y(d); }); | ||
|
||
// Adds the svg canvas | ||
var svg = d3.select("body") | ||
.append("svg") | ||
.attr("width", width + margin.left + margin.right) | ||
.attr("height", height + margin.top + margin.bottom) | ||
.append("g") | ||
.attr("transform", | ||
"translate(" + margin.left + "," + margin.top + ")"); | ||
|
||
var tooltip = d3.select("body") | ||
.append("div") | ||
.attr("class", "tooltip") | ||
.style("opacity", 0); | ||
|
||
// Get the data | ||
d3.csv("formattedCovidAverages.csv") | ||
.row(function(d) { | ||
var cases = Object.keys(d).map(function(key) { | ||
if (!isNaN(d[key])) { | ||
return d[key]; | ||
} | ||
}) | ||
return {state: d.state, cases: cases }; | ||
}) | ||
.get(function(error, data) { | ||
|
||
// Scale the range of the data | ||
// FIXME: get max value, not registering if statement | ||
x.domain([0, data[0].cases.length]); | ||
y.domain([0, d3.max(data, function(d) { | ||
// var maxCases = 0; | ||
// for (var i = 0; i < d.cases.length - 1; i++) { | ||
// if (d.cases[i] > maxCases) { | ||
// maxCases = d.cases[i]; | ||
// // console.log(d.state, d.cases[i], maxCases); | ||
// } | ||
// } | ||
// console.log(maxCases); | ||
return 10000; | ||
})]); | ||
|
||
// Nest the entries by symbol | ||
var dataNest = d3.nest() | ||
.key(function(d) { | ||
return d.state; | ||
}) | ||
.entries(data); | ||
|
||
// Add the X Axis | ||
svg.append("g") | ||
.attr("class", "x axis") | ||
.attr("transform", "translate(0," + height + ")") | ||
.call(xAxis); | ||
|
||
// Add the Y Axis | ||
svg.append("g") | ||
.attr("class", "y axis") | ||
.call(yAxis) | ||
|
||
svg.append('text') | ||
.attr('x', 10) | ||
.attr('y', -10) | ||
.attr('class', 'title') | ||
.text('Seven day rolling average') | ||
|
||
var color = d3.scale.category10(); // set the color scale | ||
|
||
// Loop through each symbol / key | ||
dataNest.forEach(function(d, i) { | ||
|
||
// sending just the list of numbers to the line() | ||
svg.append("path") | ||
.attr("class", "line") | ||
.style("stroke", function() { | ||
return d.color = color(d.key); | ||
}) | ||
.attr("id", 'tag'+d.key.replace(/\s+/g, '')) | ||
.style('opacity', 0.4) | ||
.attr("d", dataline(d.values[0].cases)) | ||
.on("mouseover", function() { | ||
|
||
// color label on mouseover | ||
d3.select('#label'+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style("opacity", 1) | ||
|
||
// color line on mouseover | ||
d3.select("#tag"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style("opacity", 1) | ||
.style('stroke-width', 8); | ||
|
||
// build tooltip | ||
// console.log('in path', d.key, d.values[0].cases) | ||
tooltip.transition() | ||
.duration(100) | ||
.style("opacity", .9); | ||
|
||
tooltip.html(function() { | ||
var casesArray = d.values[0].cases; | ||
var maxcases = 0; | ||
for (i = 0; i < casesArray.length; i++) { | ||
if (Number(casesArray[i]) > maxcases) { | ||
maxcases = Number(casesArray[i]) | ||
} | ||
} | ||
// FIXME: why is the last case undefined? | ||
return d.key + ", max: " + Math.round(maxcases) + ", current: " + Math.round(Number(casesArray[casesArray.length - 2])); | ||
}) | ||
.style('left', (d3.event.pageX - 50) + 'px') | ||
.style('top', (d3.event.pageY - 80) + "px"); | ||
}) | ||
.on("mouseout", function() { | ||
tooltip.transition() | ||
.duration(100) | ||
.style("opacity", 0); | ||
|
||
if (!d.active) { | ||
d3.select('#label'+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style("opacity", .4) | ||
d3.select("#tag"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style('opacity', 0.4) | ||
.style('stroke-width', 2); | ||
} | ||
}); | ||
|
||
// Make space for the legend | ||
// calculate individually based on i to place instead of working through this | ||
legendWidthSpacing = width/4 | ||
d.active = false | ||
|
||
// Build legend | ||
svg.append("text") | ||
.attr("x", (legendWidthSpacing/2 + Math.floor(i/14) * legendWidthSpacing)) | ||
.attr("y", height + 40 + (i%14 * 20)) | ||
.attr("class", "legend") | ||
.attr("id", 'label'+d.key.replace(/\s+/g, '')) | ||
.style("fill", function() {return d.color = color(d.key); }) | ||
.on("mouseover", function() { | ||
if (!d.active) { | ||
d3.select("#tag"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style('opacity', 1) | ||
.style('stroke-width', 4); | ||
} | ||
}) | ||
.on("mouseout", function() { | ||
if (!d.active) { | ||
d3.select("#tag"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style('opacity', 0.1) | ||
.style('stroke-width', 2); | ||
} | ||
}) | ||
.on("click", function () { | ||
var newOpacity = d.active ? 0.4 : 1; | ||
var newStrokeWidth = d.active ? 2 : 4; | ||
d3.select("#label"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style('opacity', newOpacity); | ||
d3.select("#tag"+d.key.replace(/\s+/g, '')) | ||
.transition().duration(200) | ||
.style('opacity', newOpacity) | ||
.style('stroke-width', newStrokeWidth); | ||
d.active = !d.active | ||
}) | ||
.text(d.key); | ||
}); | ||
|
||
}); | ||
|
||
</script> | ||
|
||
<p> | ||
This chart tracks the seven day average of cases of COVID-19 across the USA. The data comes from the New York Times (see <a href = 'https://www.nytimes.com/article/coronavirus-county-data-us.html?action=click&module=Spotlight&pgtype=Homepage'>here for more information</a> and <a href = 'https://github.com/nytimes/covid-19-data'>here for the GitHub data</a>). The idea for the chart comes from <a href = "https://ourworldindata.org/grapher/covid-confirmed-cases-since-100th-case">this similar one from Our World in Data</a>. | ||
</p> | ||
<p> | ||
I wouldn't depend on this for much, it's mostly a chance for me to see a graph that I wish were easier to find while checking to see if I remember how to use D3.js. | ||
</p> | ||
<script data-goatcounter="https://witulski.goatcounter.com/count" | ||
async src="//gc.zgo.at/count.js"></script> | ||
</body> |
Oops, something went wrong.