function sortByKey(data, targetArray, dataKey, newKey, fn=d=>d) { data.forEach(row => { let keyName = fn(row[dataKey]); let fullDataObj = targetArray.find(place => place[newKey] === keyName); if (fullDataObj) { fullDataObj.rawData.push(row) } else { let newObj = { rawData: [] } newObj[newKey] = keyName newObj.rawData.push(row) targetArray.push(newObj) } }) return targetArray } async function getMonthlyData() { //let myData = await d3.csv('./data/deaths2018.csv') let myData = await d3.csv('/polopoly_fs/1.4937798!/httpFile/file.txt') myData = myData.filter(row => Number(row.REF_DATE) > 1999) //console.log(myData) //let myPop = await d3.csv('./data/pop-estimates.csv') let myPop = await d3.csv('/polopoly_fs/1.4937834!/httpFile/file.txt') //console.log(myPop) let regionArray = [] let regionSort = sortByKey(myData, regionArray, 'GEO', 'region', d => d.split(',')[0]) regionSort.forEach(region => { region.yearArray = []; let yearSort = sortByKey(region.rawData, region.yearArray, 'REF_DATE', 'year') //yearSort = yearSort.filter(row => row.Characteristics === 'Number of deaths') //yearSort = yearSort.filter(row => row['Month of death'] !== 'Total, month of death') yearSort.forEach(year => { year.monthArray = [] let monthSort = sortByKey(year.rawData, year.monthArray, 'Month of death', 'month', d => d.split(',')[1].trim()) year.monthArray.splice(0,1) year.monthArray.forEach((month, i) => { month.deaths = Number(month.rawData.find(row => row.Characteristics === 'Number of deaths').VALUE); let quarterArray = myPop.filter(row => Number(row['Year']) === Number(year.year)) let quarter = quarterArray[Math.floor(i/3)] month.population = Number(quarter[region.region]) month.deathsPer1000 = month.deaths/(month.population/1000) let quarter2020 = myPop.filter(row => Number(row['Year']) === 2020)[0] population2020 = Number(quarter2020[region.region]) month.population2020 = population2020; month.deathsAdjusted2020 = month.deathsPer1000*(population2020/1000) }) }) }) let fullData = regionArray.filter(region => region.region !== 'Unknown province or territory of residence' && region.region !== 'Northwest Territories including Nunavut') //console.log(fullData) return fullData } async function getData() { let myNewData = null; //let newWeekly = await d3.csv('./data/new-weekly-data.csv') let newWeekly = await d3.csv('/polopoly_fs/1.4937818!/httpFile/file.txt') let weeklyRegionArray = []; sortByKey(newWeekly, weeklyRegionArray, 'GEO', 'region', d => d.split(',')[0]) weeklyRegionArray.forEach(region => { region.yearArray = []; let yearSort = sortByKey(region.rawData, region.yearArray, 'REF_DATE', 'year') }) //let weeklyCovid = await d3.csv('./data/weekly-deaths-mar31.csv') let weeklyCovid = await d3.csv('/polopoly_fs/1.4937822!/httpFile/file.txt') return [myNewData, weeklyRegionArray, weeklyCovid]; } window.onload = async function() { //First charts (weekly) let [newData, newWeekly, weeklyCovid] = await getData() document.querySelectorAll('.covid-new-sub-chart').forEach((div, i) => { createNewDeathChart(newWeekly, div, newWeekly[i].region) }) let monthlyData = null //Second charts (monthly) /* monthlyData = await this.getMonthlyData() createRawDeathChart(monthlyData, '.covid-death-main-chart', 'Canada') document.querySelectorAll('.covid-death-sub-chart').forEach((div, i) => { createRawDeathChart(monthlyData, div, monthlyData[i+1].region) }) */ if (!monthlyData) { document.querySelector('.covid-death-container').remove() } } function createRawDeathChart(data, parent, region, newData=null) { let metric = 'deathsAdjusted2020' let regionData = data.find(place => place.region === region); //console.log(regionData) let container = d3.select(parent) let width = Number(container.style('width').split('px')[0]); let height = Number(container.style('height').split('px')[0]); let [top, right, bottom, left] = [50, 20, 20, 10 + width/8]; let svg = container.append('svg').attr('width', width).attr('height', height) let chartLayer = svg.append('g').attr('class', 'chart-layer') let max = 0; regionData.yearArray.forEach(year => { max = Math.max(max, d3.max(year.monthArray, d => d[metric])) }) let averageArray = Array.apply(null, Array(12)).map(() => []) regionData.yearArray.forEach(year => { year.monthArray.forEach((month, i) => { averageArray[i].push(month[metric]) }) }) averageLine = [] averageArray.forEach(array => { averageLine.push(d3.mean(array)) }) let yScale = d3.scaleLinear() .domain([0, (max<40?40:max<1000?1000:max<2000?2000:max<5000?5000:max<12000?12000:max<40000?40000:max*1.25)]) .range([height - bottom, top]) //.nice() let xScale = d3.scaleLinear() .domain([0, regionData.yearArray[0].monthArray.length-1]) .range([left, width - right]) let area = d3.area().x((d, i) => xScale(i)).y0(yScale(0)).y1(d => yScale(d)) let line = d3.line().x((d, i) => xScale(i)).y(d => yScale(d[metric])) let lineAvg = d3.line().x((d, i) => xScale(i)).y(d => yScale(d)) let areaAvg = d3.area().x((d, i) => xScale(i)).y0(yScale(0)).y1(d => yScale(d)) let lineNew = d3.line().x((d, i) => xScale(i)).y(d => yScale(d.deaths)) let areaNew = d3.area().x((d, i) => xScale(i)).y0(yScale(0)).y1(d => yScale(d.deaths)) chartLayer.append('g').attr('class', 'y-axis') .attr('transform', `translate(${left})`) .call(d3.axisLeft(yScale) .ticks(4) .tickSize(-width + right + left) ) .attr('opacity', 1) .attr('shape-rendering', 'crispEdges') regionData.yearArray.forEach((year, i) => { chartLayer.append('path').datum(year.monthArray) .attr('d', line) .attr('fill', 'none') .attr('stroke', '#f2f2f2') .attr('stroke-width', 4) .attr('stroke-linecap', 'round') .attr('opacity', 1) //.attr('opacity', 0).transition().duration(500).delay(1000 + i*1000).attr('opacity', 1).transition().duration(500).delay(500).attr('opacity', 0.1) /* chartLayer.append('text') .text(year.year) .attr('x', width/2).attr('y', height*0.25) .attr('class', 'chart-year') .attr('font-size', Math.round(height/10)).attr('font-family', 'sans-serif') .attr('text-anchor', 'middle').attr('font-weight', 'bold') .attr('fill', '#aaa').attr('opacity', 1) //.attr('opacity', 0).transition().duration(500).delay(1000 + i*1000).attr('opacity', 1).transition().duration(500).delay(500).attr('opacity', 0) */ }) if (newData) { newArray = []; newData.forEach(row => { newArray.push({ year: row.Year, month: row.Month, deaths: Number(row[regionData.region]) }) }) } // New Area /* chartLayer.append('path').datum(newArray) .attr('d', areaNew) .attr('fill', '#ffe7a6') // New Line let newLine = chartLayer.append('path').datum(newArray) .attr('d', lineNew) .attr('fill', 'none') .attr('stroke', '#ffa400') .attr('stroke-width', 6) .attr('stroke-linecap', 'butt') .attr('opacity', 1) */ //Average area /* chartLayer.append('path').datum(averageLine) .attr('d', areaAvg) .attr('fill', '#cccccc77') */ let avgLine = chartLayer.append('path').datum(averageLine) .attr('d', lineAvg) .attr('fill', 'none') .attr('stroke', '#2c2c2c') .attr('stroke-width', 4) .attr('stroke-linecap', 'butt') .attr('stroke-dasharray', Math.round(width/45)) .attr('opacity', 1) chartLayer.append('rect').attr('width', left).attr('height', height).attr('fill','#fff') let title = svg.append('text').text(regionData.region) .attr('class', 'ctvsans bold') .attr('y', 25) .attr('x', left/3) .attr('font-size', Math.round(16 + width/100)) if (parent === '.covid-death-main-chart') { title.text('Deaths per month in Canada (2000-2018)') if (newData) { newLine.attr('stroke-width', 7) } avgLine.attr('stroke-width', 5) let monthArray = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] chartLayer.append('g').attr('class', 'x-axis ctvsans') .attr('transform', `translate(0, ${height-bottom})`) .call(d3.axisBottom(xScale) .tickFormat((d, i) => monthArray[i]) ) .attr('opacity', 0.8) .attr('font-size', 12) svg.append('text').text('Deaths per month') .attr('class', 'ctvsans') .attr('y', 26) .attr('x', 0) .attr('font-size', 16) .attr('text-anchor', 'middle') .attr('fill', '#444') .attr('transform', `rotate(-90) translate(${-height/2},${-15})`) svg.append('text').text('(adjusted to 2020 population equivalent)') .attr('class', 'ctvsans') .attr('y', 26) .attr('x', 0) .attr('dy', 16) .attr('font-size', 13) .attr('text-anchor', 'middle') .attr('fill', '#888') .attr('transform', `rotate(-90) translate(${-height/2},${-15})`) } else { let shortMonthArray = ['Jan', 'Dec'] let xScale2 = d3.scaleLinear() .domain([0, 1]) .range([left, width - right]) chartLayer.append('g').attr('class', 'x-axis ctvsans') .attr('transform', `translate(0, ${height-bottom})`) .call(d3.axisBottom(xScale2) .tickFormat((d, i) => shortMonthArray[i]) .ticks(1) ) .attr('opacity', 0.8) .attr('font-size', 12) } chartLayer.append('g').attr('class', 'y-axis ctvsans') .attr('transform', `translate(${left-3})`) .call(d3.axisLeft(yScale) .ticks(4) .tickSize(0) ) .attr('opacity', 0.8) .attr('font-size', 12) } function createNewDeathChart(data, parent, region, weeklyCovid=null) { let regionData = data.find(place => place.region === region); let container = d3.select(parent) let width = Number(container.style('width').split('px')[0]); let height = Number(container.style('height').split('px')[0]); let [top, right, bottom, left] = [50, 20, 20, 10 + width/8]; let svg = container.append('svg').attr('width', width).attr('height', height) let chartLayer = svg.append('g').attr('class', 'chart-layer') let max = 0; regionData.yearArray.forEach(year => { max = Math.max(max, d3.max(year.rawData, d => Number(d['VALUE']))) }) let yScale = d3.scaleLinear() .domain([0, (max<50?50:max<250?250:max<500?500:max<1000?1000:max<2500?2500:max*1.25)]) .range([height - bottom, top]) //.nice() let xScale = d3.scaleLinear() .domain([0, regionData.yearArray[0].rawData.length-1]) .range([left, width - right]) //let area = d3.area().x((d, i) => xScale(i)).y0(yScale(0)).y1(d => yScale(d)) let line = d3.line() .x((d, i) => xScale(i)) .y(d => yScale(Number(d['VALUE']))) .defined(d => d['STATUS'] !== 'F') chartLayer.append('g').attr('class', 'y-axis ctvsans') .attr('transform', `translate(${left})`) .call(d3.axisLeft(yScale) .ticks(4) .tickSize(-width + right + left) ) .attr('opacity', 1) .attr('shape-rendering', 'crispEdges') let monthArray = ['Jan', 'Feb', 'Mar', ''] let xScale2 = d3.scaleLinear() .domain([0, 3]) .range([left, width - right]) chartLayer.append('g').attr('class', 'x-axis ctvsans') .attr('transform', `translate(0, ${height-bottom})`) .call(d3.axisBottom(xScale2) .tickFormat((d, i) => monthArray[i]) .ticks(3) ) .attr('opacity', 0.8) .attr('font-size', 12) regionData.yearArray.forEach((year, i) => { chartLayer.append('path').datum(year.rawData) .attr('d', line) .attr('fill', 'none') .attr('stroke', year.year === '2019' ? '#2c2c2c' : year.year === '2020' ? '#ffa400' : '#f2f2f2') .attr('stroke-width', year.year === '2020' ? 4 : 3) .attr('stroke-linecap', 'round') .attr('stroke-linejoin', 'round') //.attr('stroke-dasharray', year.year === '2019' ? `${Math.round(width/25)} 5` : 'none') .attr('opacity', 1) }) let lineCovid = d3.line() .x((d, i) => xScale(i)) .y(d => yScale(Number(d[regionData.region]))) .defined(d => d[regionData.region] > 0) if (weeklyCovid) { chartLayer.append('path').datum(weeklyCovid) .attr('d', lineCovid) .attr('fill', 'none') .attr('stroke', 'blue') .attr('stroke-width', 4) .attr('stroke-linecap', 'round') .attr('stroke-linejoin', 'round') //.attr('stroke-dasharray', year.year === '2019' ? `${Math.round(width/25)} 5` : 'none') .attr('opacity', 1) } let total2020 = 0; let stop = null; regionData.yearArray.find(year => year.year === '2020').rawData.forEach((week, i) => { total2020 += Number(week.VALUE); if (!stop) { if (week.STATUS === "F") { stop = i; } } }) let total2019 = 0; regionData.yearArray.find(year => year.year === '2019').rawData.forEach((week, i) => { if (!stop || i < stop) { total2019 += Number(week.VALUE) } }) let diff = total2020 - total2019; let title = svg.append('text').html(`${regionData.region} (${Math.sign(diff) > 0 ? '+' : ''}${diff.toLocaleString()})`) .attr('class', 'ctvsans bold') .attr('y', 25) .attr('x', left/3) .attr('font-size', Math.round(14 + width/100)) }