Tracking COVID-19 in NYC
With the world going through the COVID-19 pandemic, and given that I’m in NYC, which is the epicenter of the outbreak here in the U.S., I thought it would be a fun exercise to track the number of cases and deaths in NYC. There are a lot of great visualizations out there, and I’ve been checking the COVID-19 Dashboard by JHU CSSE, but I wanted an easier, faster way to just see the information for NYC.
I found a data source from New York Times that gets updated daily on GitHub (https://github.com/nytimes/covid-19-data). From this data, I used D3.js (which I learned in my course CSE 6242 Data Analytics and Visualization at Georgia Tech) to create a simple interactive plot that tracks the number of confirmed COVID-19 cases and deaths in NYC. Check it out here!
Here’s the code, written with D3.js v5:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>COVID-19 in NYC</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font: 10px "Helvetica Neue", Helvetica, Arial, sans-serif;
position: relative;
}
text {
font-family: arial;
font-size: 12px;
}
path.line {
fill: none;
stroke: red;
stroke-width: 2px;
}
.overlay {
fill: none;
pointer-events: all;
}
.axis path,
.axis line {
fill: none;
stroke: slategray;
shape-rendering: crispEdges;
}
.focus circle {
fill: none;
stroke: black;
}
.tooltip {
width: 94px;
padding: 4px 10px;
border: 1px solid #aaa;
border-radius: 4px;
box-shadow: 2px 2px 4px rgba(0,0,0,0.3);
position: absolute;
background-color: white;
font-size: 14px;
pointer-events: none;
-webkit-transition: all 0.25s;
-moz-transition: all 0.25s;
-ms-transition: all 0.25s;
-o-transition: all 0.25s;
transition: all 0.25s;
}
.tooltip div {
margin: 3px 0;
}
.tooltip-date, .tooltip-likes {
font-weight: bold;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.6;
shape-rendering: crispEdges
}
.grid path {
stroke-width: 0;
</style>
</head>
<body>
<script type="text/javascript">
var parseDate = d3.timeParse("%Y-%m-%d"),
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(","),
dateFormatter = d3.timeFormat("%m/%d/%y");
var margin = {left: 50, right: 20, top: 20, bottom: 50 };
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var max = 0;
var xNudge = 50;
var yNudge = 20;
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("display", "none");
//Function for converting CSV values from strings to Dates and numbers
var rowConverter = function(d) {
return {
date: parseDate(d.date),
county: d.county,
cases: parseFloat(d.cases),
deaths: parseFloat(d.deaths)
};
}
d3.dsv(",", "https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv", rowConverter).then(function(data) {
dataset = data.filter(function(d) {
return d["county"] === "New York City"
});
max = d3.max(dataset, function(d) { return d.cases; });
minDate = d3.min(dataset, function(d) {return d.date; });
maxDate = d3.max(dataset, function(d) { return d.date; });
var y = d3.scaleLinear()
.domain([1,max])
.range([height,0]);
var x = d3.scaleTime()
.domain([minDate,maxDate])
.range([0,width]);
var yAxis = d3.axisLeft(y);
var xAxis = d3.axisBottom(x);
var line = d3.line()
.x(function(d){ return x(d.date); })
.y(function(d){ return y(d.cases); })
var line2 = d3.line()
.x(function(d){ return x(d.date); })
.y(function(d){ return y(d.deaths); })
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(8)
}
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(5)
}
var svg = d3.select("body").append("svg").attr("id","svg").attr("height","100%").attr("width","100%");
var chartGroup = svg.append("g").attr("class","chartGroup").attr("transform","translate("+xNudge+","+yNudge+")");
chartGroup.append("path")
.style("stroke","blue")
.style("fill","none")
.style("stroke-width","2px")
.attr("d",function(d){ return line(dataset); })
chartGroup.append("path")
.style("stroke","red")
.style("fill","none")
.style("stroke-width","2px")
.attr("d",function(d){ return line2(dataset); })
var focus = chartGroup.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 5);
focus.append("path")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
var tooltipDate = tooltip.append("div")
.attr("class", "tooltip-date");
var tooltipCases = tooltip.append("div");
tooltipCases.append("span")
.attr("class", "tooltip-title")
.text("Cases: ");
var tooltipDeaths = tooltip.append("div");
tooltipDeaths.append("span")
.attr("class", "tooltip-title")
.text("Deaths: ");
var tooltipCasesValue = tooltipCases.append("span")
.attr("class", "tooltip-cases");
var tooltipDeathsValue = tooltipDeaths.append("span")
.attr("class", "tooltip-deaths");
chartGroup.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); tooltip.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); tooltip.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(dataset, x0, 1),
d0 = dataset[i - 1],
d1 = dataset[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.cases) + ")");
tooltip.attr("style", "left:" + (x(d.date) + 64) + "px;top:" + y(d.cases) + "px;");
tooltip.select(".tooltip-date").text(dateFormatter(d.date));
tooltip.select(".tooltip-cases").text(formatValue(d.cases));
tooltip.select(".tooltip-deaths").text(formatValue(d.deaths));
}
chartGroup.append("g")
.attr("class","axis x")
.attr("transform","translate(0,"+height+")")
.call(xAxis);
chartGroup.append("g")
.attr("class","axis y")
.call(yAxis);
//Create title
chartGroup.append("text")
.attr("x", (width / 2))
.attr("y", 16)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.text("COVID-19 Confirmed Cases and Deaths in NYC");
chartGroup.append("g")
.attr("class","grid")
.attr("transform","translate(0," + height + ")")
.style("stroke-dasharray",("3,3"))
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
)
chartGroup.append("g")
.attr("class","grid")
.style("stroke-dasharray",("3,3"))
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
)
});
</script>
</body>
</html>