Published
Edited
Aug 11, 2020
Fork of New Cases
Insert cell
Insert cell
chart = {
const root = d3.create("div")
.attr("class", "root");
const svg = root.append("svg")
.attr("viewBox", [0, 0, width, height]);
const tooltip = root
.append("div")
.attr("class", "cooltip");
svg.append("g")
.call(yAxis);
// svg.append("path")
// .datum(avg)
// .attr("fill", color)
// .attr("opacity", 0.15)
// .attr("d", area);
svg.append("g")
.attr("fill", "#D0B3BC")
.attr("opacity", 1)
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", (d, i) => x(i+2))
.attr("y", d => y(d.new_deaths))
.attr("height", d => y(0) - y(d.new_deaths))
.attr("width", x.bandwidth())
.attr("data-date", d => d.date)
.on('mouseover', function (d) {
this.classList.add('hovered')
d3.select(this)
.attr("fill", "#8B5566")
.raise()
})
.on('mouseout', function() {
this.classList.remove('hovered');
d3.select(this)
.attr("fill", "#D0B3BC")
.lower();
tooltip.style('display', 'none');
});
svg.on('mousemove', function() {
const rootBounds = root.node().getBoundingClientRect();
const mouseX = d3.event.pageX - rootBounds.left;
const displayWidth = rootBounds.right - rootBounds.left
const transformRatio = width / displayWidth;
const heightTransformRatio = height / (rootBounds.bottom - rootBounds.top);
console.log(heightTransformRatio);
let eachBand = x.step();
let i = Math.floor((mouseX * transformRatio - margin.left)/eachBand-2);
let a = avg[i];
let d = data[i];
tooltip
.style('top', (y(a.new_deaths)/heightTransformRatio-100) + 'px')
.style('left', (mouseX + 90 <= displayWidth ? (mouseX + 10) : (mouseX - 90)) + 'px')
.style('display', 'block')
let node = tooltip.node();
node.innerHTML = "";
node.appendChild(getTooltipContents(d));
});
svg.append("path")
.datum(avg)
.attr("fill", "none")
.attr("stroke", color)
.attr("stroke-width", 3)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line);
svg.append("g")
.call(xAxis)
.select('.domain').attr("opacity", 0)
.select('.tick:first-of-type text').remove()
.selectAll('.tick text')
.attr('font-size', 15)
.attr('font-family', 'Helvetica')
.attr('fill', 'gray');
window.addEventListener("resize", () => resized(width, root, svg));
setTimeout(() => resized(width, root, svg), 12);
return root.node();
}
Insert cell
function resized(width, root, svg) {
let measuredWidth = root.node().getBoundingClientRect().width;
if (measuredWidth == 0) {
setTimeout(() => resized(width, root, svg), 12);
return;
}
let scale = width / measuredWidth;
svg.selectAll("line").attr("stroke-width", scale + "px");
svg.selectAll("text").attr("transform", "scale(" + scale + " " + scale + ")");
}
Insert cell
getTooltipContents = d => {
return html` <div class = "container">
<div class = "date"> ${d.date.toLocaleDateString("en-US")} </div>
<div class = "desc"> New deaths: </div>
<div class = "num"> ${d.new_deaths}</div>
</div> `;
}
Insert cell
html`
<style>
.cooltip {
background: white;
border: 1px solid lightgrey;
padding: 10px;
position: absolute;
font-family: Helvetica, Arial, sans-serif;
}
.date {
font-weight: 700;
font-size: 14px;
}
.desc {
font-size: 10px;
text-transform: uppercase;
color: grey;
}
.num {
font-size: 14px;
}
</style>
`
Insert cell
area = d3.area()
.defined(d => !isNaN(d.new_deaths))
.curve(curve)
.x((d, i) => x(i+2)+3)
.y0(y(0))
.y1(d => y(d.new_deaths))
Insert cell
curve = d3.curveLinear
Insert cell
line = d3.line()
.defined(d => !isNaN(d.new_deaths))
.x((d, i) => x(i+2)+3)
.y(d => y(d.new_deaths))
Insert cell
avg = data.map(rollingAvg).filter(d => d != null)
Insert cell
data = Object.assign(await d3.csv("https://raw.githubusercontent.com/prayaggordy/D3-covid-map/master/data/md_statewide.csv", d3.autoType)).map(({date, new_deaths}) => ({date, new_deaths: +new_deaths}))
Insert cell
function rollingAvg (b, index, array) {
let count = 0;
for (let i = 0; i < 7; ++i) {
if (index - i < 0)
return {...b, new_deaths: count/i}
count += array[index - i].new_deaths;
};
return {...b, new_deaths: count/7};
};
Insert cell
xDate = d3.scaleUtc()
.domain(d3.extent(data, d => d.date))
.range([margin.left, width - margin.right])
Insert cell
x = d3.scaleBand()
.domain(d3.range(data.length+2))
.range([margin.left, width - margin.right])
.padding(0.1)
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.new_deaths)+6]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xDate).ticks(width / 120).tickSize(0))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisRight(y)
.ticks(height / 50, data.format)
.tickSize(0))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick:not(:first-of-type) line")
.attr("stroke-dasharray", "2,2")
.attr("stroke-opacity", d => d === 1 ? null : 0.25)
.attr("x2", width - margin.left - margin.right))
.call(g => g.selectAll(".tick text")
.attr("x", 4)
.attr("dy", -4))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 26)
.attr("text-anchor", "start")
.text(" " + "deaths"))
.call(g => g.select(".tick:first-of-type text").remove())
Insert cell
color = "#724654"
Insert cell
height = 200
Insert cell
width = 550
Insert cell
margin = ({top: 20, right: 10, bottom: 20, left: 0})
Insert cell
d3 = require("d3@5")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more