class Graphic { constructor(root, raw) { this.root = root; this.data = raw; const total = this.data .reduce((memo, curr) => memo + curr.new, 0) .toLocaleString(); this.root .append("h2") .classed("graphic-title bold", true) .text("COVID-19 deaths in Canada"); this.root .append("div") .classed("intro", true) .html( `

Since January 2020, there have been ${total} deaths in Canada attributed to COVID-19

The graphic below shows every death reported each day since the beginning of the pandemic.

Each represents one death.

` ); this.container = this.root.append("div").classed("graphic-container", true); console.log(this.data); } update() { function dateSlashToText(date) { const [month, day, year] = date.split("/"); const monthArray = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; return `${monthArray[month - 1]}${month === "5" ? "" : "."} ${year}`; } const row = this.container .selectAll("div") .data(this.data) .join("div") .classed("row", true) .classed("even", (d) => +d.dateString.split("/")[0] % 2 === 0) .on("mouseover", (e, d, i, arr) => { const num = e.target.querySelector(".number"); if (num) num.style.display = "flex"; }) .on("mouseout", (e, d) => { const num = e.target.querySelector(".number"); if (num) num.style.display = "none"; }); this.container.append("div").classed("padding", true); row .selectAll(".point") .data((d) => new Array(Math.max(0, d.new))) .join("div") .classed("point", true); row .append("div") .classed("date label bold", true) .classed( "first", (d) => d.dateString.split("/")[0] === "1" && d.dateString.split("/")[2] === "2020" ) .classed("visible", (d) => d.dateString.split("/")[1] === "1") .text((d) => dateSlashToText(d.dateString)); row .append("div") .classed("deaths label bold", true) .classed( "first", (d) => d.dateString.split("/")[0] === "1" && d.dateString.split("/")[2] === "2020" ) .classed("visible", (d) => d.dateString.split("/")[1] === "1") .text((d) => { const [M, D, Y] = d.dateString.split("/"); const month = this.data .filter( (day) => day.dateString.split("/")[0] === M && day.dateString.split("/")[2] === Y ) .map((day) => day.new); return month.reduce((memo, curr) => memo + curr, 0).toLocaleString(); }); const num = row .append("div") .classed("number bold", true) .html((d) => `${d.new} (${d.dateString})`) .style("display", "none"); return this; } } d3.json( "https://beta.ctvnews.ca/content/dam/common/exceltojson/COVID-19-Canada-New.txt" ).then((raw) => { function SerialDateToJSDate(serialDate, offsetUTC) { return new Date(Date.UTC(0, 0, serialDate, offsetUTC)); } console.log(raw); const pad = []; for (let i = 43831; i < 43855; i++) { pad.push({ Date: i, Can_Death: 0 }); } const data = [...pad, raw] .flat() .filter((d) => d.Date !== "") .map((d, i, arr) => { return { date: SerialDateToJSDate( d.Date, -(24 - 1 - new Date().getTimezoneOffset() / 60) ), deaths: +d.Can_Death, new: i === 0 ? 0 : +d.Can_Death - +arr[i - 1].Can_Death, get dateString() { const [D, M, Y] = [ this.date.getDate(), this.date.getMonth(), this.date.getFullYear(), ]; const stringDate = `${+M + 1}/${D}/${Y}`; return stringDate; }, }; }); new Graphic(d3.select(".ctv-graphic"), data).update(); });