////// Date chart function drawDateChart(parent, fullData, cumulative, animated, fullMaxData=null) { let data = fullData.tracking; let maxData = fullMaxData ? fullMaxData.tracking : null; let container = d3.select(parent) container.selectAll('*').remove() let svg = container.append('svg') .attr('width', '100%') .attr('height', '100%') //.attr('viewBox', `0 0 ${chart.width} 400`).attr('preserveAspectRatio', "xMaxYMid meet") let chart = { width: Number(svg.style('width').split('px')[0]), height: Number(svg.style('height').split('px')[0]), pad: 20, numDays: fullMaxData ? Math.max(data.length, maxData.length) : data.length } let bar = { width: (chart.width - chart.pad*4)/chart.numDays, pad: 1//Math.floor(chart.width/375) } let axisLayer = svg.append('g').attr('class', 'axis-layer') .attr('transform', `translate(${chart.pad/2}, ${-chart.pad})`) let maxLayer = svg.append('g').attr('class', 'max-layer') .attr('transform', `translate(${chart.pad}, ${-chart.pad})`) let chartLayer = svg.append('g').attr('class', 'chart-layer') .attr('transform', `translate(${chart.pad}, ${-chart.pad})`) let dateLayer = svg.append('g').attr('class', 'date-layer') .attr('transform', `translate(${chart.pad}, ${-chart.pad})`) let maxCountCumulative = 0; (maxData ? maxData : data).forEach(day => { maxCountCumulative = Math.max(day.total, maxCountCumulative) }) let maxCountNew = 0; (maxData ? maxData : data).forEach(day => { maxCountNew = Math.max(day.new, maxCountNew) }) let heightScale = d3.scaleLinear() .domain([0, Math.max(1, cumulative ? maxCountCumulative : maxCountNew)]) .range([0, chart.height - chart.pad*2 - 50]) let axisArray = [] let max = cumulative ? maxCountCumulative : maxCountNew; for (let i = 0; i < 5; i++) { axisArray.push(i*Math.ceil(max/5)) } let axis = axisLayer.selectAll('line').data(axisArray) let bars = chartLayer.selectAll('rect').data(data) if (animated) { axis.enter().append('line') .attr('x1', (d,i) => 0) .attr('y1', (d,i) => chart.height - heightScale(d) + 0.5) .attr('x2', chart.width - chart.pad) .attr('y2', (d,i) => chart.height - heightScale(d) + 0.5) .attr('shape-rendering', 'crispEdges') .attr('stroke', (d,i) => i === 0 ? '#000' : '#d3d3d3') let axisText = axisLayer.selectAll('text').data(axisArray) axisText.enter().append('text') .attr('x', (d,i) => chart.width - chart.pad ) .attr('font-family', `'CTVSans-Regular','CTV Sans',Arial`) .attr('font-size', 12) .attr('fill','#333333') .attr('text-anchor', 'end') .text(d => d.toLocaleString()) .attr('y', (d,i) => chart.height - heightScale(d) + 0.5 - 5) bars.enter() .append('rect') .attr('x', (d,i) => i*bar.width) .attr('y', (d,i) => chart.height ) .attr('height', 0) .attr('width', bar.width - bar.pad) .attr('fill', (d, i, N) => i === N.length-1 ? '#F4BB3F': '#F4BB3F') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(data, d.cases.length, d.cumulative, d.date)) .transition().duration(1500) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) } else { axis.enter().append('line') .attr('x1', (d,i) => 0) .attr('y1', (d,i) => chart.height - heightScale(d) + 0.5) .attr('x2', chart.width - chart.pad) .attr('y2', (d,i) => chart.height - heightScale(d) + 0.5) .attr('shape-rendering', 'crispEdges') .attr('stroke', (d,i) => i === 0 ? '#000' : '#d3d3d3') let axisText = axisLayer.selectAll('text').data(axisArray) axisText.enter().append('text') .attr('x', (d,i) => chart.width - chart.pad) .attr('font-family', `'CTVSans-Regular','CTV Sans',Arial`) .attr('font-size', 12) .attr('fill','#333333') .attr('text-anchor', 'end') .text(d => d.toLocaleString()) .attr('y', (d,i) => chart.height - heightScale(d) + 0.5 - 5) bars.enter() .append('rect') .attr('x', (d,i) => i*bar.width) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) .attr('width', bar.width - bar.pad) .attr('fill', (d, i, N) => i === N.length-1 ? '#F4BB3F': '#F4BB3F') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(data, d.cases.length, d.cumulative, d.date)) } if (maxData) { let maxBars = maxLayer.selectAll('rect').data(maxData) if (animated) { maxBars.enter() .append('rect') .attr('x', (d,i) => i*bar.width) .attr('y', (d,i) => chart.height) .attr('height', 0) .attr('width', bar.width - bar.pad) .attr('fill', '#f3f3f3') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(d.cases.length, d.cumulative, d.date)) .transition().duration(1500) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) } else { maxBars.enter() .append('rect') .attr('x', (d,i) => i*bar.width) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) .attr('width', bar.width - bar.pad) .attr('fill', '#f3f3f3') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(d.cases.length, d.cumulative, d.date)) } } let textCumulative = svg.append('text') .attr('class', 'text-cumulative') .text('Cumulative') .attr('x', 10) .attr('y', 25) .attr('fill', cumulative ? '#333' : '#d3d3d3') .attr('font-family', cumulative ? `'CTVSans-Bold','CTV Sans',Arial` : `'CTVSans-Regular','CTV Sans',Arial`) .on('click', d => { updateDateChart(parent, fullData, true, fullMaxData) chartSettings.date.cumulative = true; }) let textNew = svg.append('text') .attr('class', 'text-new') .text('New') .attr('x', textCumulative.node().getComputedTextLength() + 10 + 20) .attr('y', 25) .attr('fill', cumulative ? '#d3d3d3' : '#333') .attr('font-family', cumulative ? `'CTVSans-Regular','CTV Sans',Arial` : `'CTVSans-Bold','CTV Sans',Arial`) .on('click', d => { updateDateChart(parent, fullData, false, fullMaxData) chartSettings.cumulative = false; }) //console.log(chart.width) let name = fullData.properties ? fullData.properties.PRENAME : 'Canada'; let nameSize = Math.min(26, chart.width/name.length) //console.log(name) let textName = svg.append('text') .attr('class', 'text-name') .text(name) .attr('x', 10) .attr('y', 65 - (26 - nameSize)) .attr('fill', '#333') .attr('font-size', nameSize) .attr('font-family', `'CTVSans-Bold','CTV Sans',Arial`) let subTextName = svg.append('text') .attr('class', 'subText-name') .text(fullData.properties ? "Reported cases in the province" : "Click map to view provincial cases") .attr('x', 10) .attr('y', 85 - (26 - nameSize)) .attr('fill', '#939393') .attr('font-size', 14) .attr('font-family', `'CTVSans-Regular','CTV Sans',Arial`) let dateText = dateLayer.selectAll('text').data(data) dateText.enter().append('text') .attr('y', chart.height + chart.pad - 5) .attr('x', (d, i) => i*bar.width + bar.width/2) .attr('font-size', 11) .attr('font-family', `'CTVSans-Regular','CTV Sans',Arial`) .attr('text-anchor', 'middle') .text((d, i) => { let month = String(d.date.getMonth()+1).padStart(2, '0'); let day = String(d.date.getDate()).padStart(2, '0'); let date = `${month}/${day}` return i%10 === 0 ? date : '' }) let dateTicks = dateLayer.selectAll('line').data(data) dateTicks.enter().append('line') .attr('y1', chart.height + chart.pad - 15) .attr('x1', (d, i) => i*bar.width + bar.width/2 - 0.5) .attr('y2', chart.height) .attr('x2', (d, i) => i*bar.width + bar.width/2 - 0.5) .attr('stroke', 'black') .attr('opacity', (d, i) => i%10 === 0 ? 1 : 0) } function updateDateChart(parent, fullData, cumulative, fullMaxData=null) { let data = fullData.tracking; console.log(data) let maxData = fullMaxData ? fullMaxData.tracking : null; let container = d3.select(parent) let svg = container.select('svg') //console.log(fullData, data) let chart = { width: Number(svg.style('width').split('px')[0]), height: Number(svg.style('height').split('px')[0]), pad: 20, numDays: fullMaxData ? Math.max(data.length, maxData.length) : data.length } let bar = { width: (chart.width - chart.pad*4)/chart.numDays, pad: 1//Math.floor(chart.width/375) } let axisLayer = svg.select('.axis-layer') let maxLayer = svg.select('.max-layer') let chartLayer = svg.select('.chart-layer') let maxCountCumulative = 0; (maxData ? maxData : data).forEach(day => { maxCountCumulative = Math.max(day.total, maxCountCumulative) }) let maxCountNew = 0; (maxData ? maxData : data).forEach(day => { maxCountNew = Math.max(day.new, maxCountNew) }) let heightScale = d3.scaleLinear() .domain([0, Math.max(1, cumulative ? maxCountCumulative : maxCountNew)]) .range([0, chart.height - chart.pad*2 - 50]) let axisArray = [] let max = cumulative ? maxCountCumulative : maxCountNew; for (let i = 0; i < 5; i++) { axisArray.push(i*Math.ceil(max/5)) } let axis = axisLayer.selectAll('line').data(axisArray) axis .transition().duration(1000) .attr('y1', (d,i) => chart.height - heightScale(d) + 0.5) .attr('y2', (d,i) => chart.height - heightScale(d) + 0.5) let axisText = axisLayer.selectAll('text').data(axisArray) axisText .text(d => d.toLocaleString()) .transition().duration(1000) .attr('y', (d,i) => chart.height - heightScale(d) + 0.5 - 5) if (maxData) { let maxBars = maxLayer.selectAll('rect').data(maxData) maxBars .attr('x', (d,i) => i*bar.width) .attr('width', bar.width - bar.pad) .attr('fill', '#f3f3f3') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(d.cases.length, d.cumulative, d.date)) .transition().duration(1500) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) } let bars = chartLayer.selectAll('rect').data(data) bars.exit() .attr('opacity', 1) .transition().duration(500) .attr('opacity', 0) .remove() bars .attr('x', (d,i) => i*bar.width) .attr('width', bar.width - bar.pad) .attr('fill', (d, i, N) => i === N.length-1 ? '#F4BB3F': '#F4BB3F') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') //.on('click', d => console.log(d.cases.length, d.cumulative, d.date)) .transition().duration(1500) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) bars.enter().append('rect') .attr('x', (d,i) => i*bar.width) .attr('width', bar.width - bar.pad) .attr('fill', (d, i, N) => i === N.length-1 ? '#F4BB3F': '#F4BB3F') .attr('rx', 0) .attr('shape-rendering', 'crispEdges') .attr('y', (d,i) => chart.height - heightScale(0)) .attr('height', d => heightScale(0)) //.on('click', d => console.log(d.cases.length, d.cumulative, d.date)) .transition().duration(1500) .attr('y', (d,i) => chart.height - heightScale(cumulative ? d.total : d.new)) .attr('height', d => heightScale(cumulative ? d.total : d.new)) let textCumulative = svg.select('.text-cumulative') .attr('font-family', `'CTVSans-Bold','CTV Sans',Arial`) .attr('fill', cumulative ? '#333' : '#d3d3d3') .on('click', d => { updateDateChart(parent, fullData, true, fullMaxData) chartSettings.date.cumulative = true; }) let textNew = svg.select('.text-new') .attr('font-family', `'CTVSans-Bold','CTV Sans',Arial`) .attr('fill', cumulative ? '#d3d3d3' : '#333') .on('click', d => { updateDateChart(parent, fullData, false, fullMaxData) chartSettings.date.cumulative = false; }) let textName = svg.select('.text-name') .text(fullData.properties ? fullData.properties.PRENAME : "Canada") }