Public
Edited
May 21, 2024
Fork of Covid19
Insert cell
Insert cell
Type Markdown, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
WHO-COVID-19-global-data.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
vaccination-data.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
vaccination-metadata.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tree = {

const width = 1000;
const marginTop = 10;
const marginRight = 10;
const marginBottom = 10;
const marginLeft = 40;

const root = d3.hierarchy(World);
const dx = 10;
const dy = (width - marginRight - marginLeft) / (1 + root.height);
const tree = d3.tree().nodeSize([dx, dy]);
const diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x);

const svg = d3.create("svg")
.attr("width", width)
.attr("height", dx)
.attr("viewBox", [-marginLeft, -marginTop, width, dx])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif; user-select: none;");

const gLink = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5);

const gNode = svg.append("g")
.attr("cursor", "pointer")
.attr("pointer-events", "all");

function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
path.unshift(current);
return path;
}

function handleMouseOver(event, d) {
const path = getAncestors(d);
const nodeIds = path.map(ancestor => ancestor.id);

const highlightedNodes = gNode.selectAll('g')
.filter(node => nodeIds.includes(node.id));

highlightedNodes.select('circle')
.attr('fill', 'orange');

highlightedNodes.select('text')
.attr("font-weight", "bold")
.attr("fill", "light blue");

gLink.selectAll('path')
.filter(link => nodeIds.includes(link.target.id))
.attr('stroke', 'red')
.attr('stroke-width', 3);

// Tooltip content
let tooltipContent = `<strong>${d.data.name}</strong>`;
if (d.data.Sum_of_New_cases !== undefined || d.data.Sum_of_New_deaths !== undefined) {
tooltipContent += `<br>Total Cases: ${d.data.Sum_of_New_cases || 'N/A'}`;
tooltipContent += `<br>Total Deaths: ${d.data.Sum_of_New_deaths || 'N/A'}`;
}

tooltip.html(tooltipContent)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY + 10}px`)
.style("visibility", "visible");
}




function handleMouseOut(event, d) {
gNode.selectAll('g')
.select('circle')
.attr('fill', d => d._children ? "#555" : "#999");

gNode.selectAll('g')
.select('text')
.attr("font-weight", "normal")
.attr("fill", "lightblue");

gLink.selectAll('path')
.attr('stroke', '#555')
.attr('stroke-width', 1.5);

tooltip.style("visibility", "hidden");
}




function update(event, source) {
const duration = event?.altKey ? 2500 : 250;
const nodes = root.descendants().reverse();
const links = root.links();

tree(root);

let left = root;
let right = root;
root.eachBefore(node => {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
});

const height = right.x - left.x + marginTop + marginBottom;

const transition = svg.transition()
.duration(duration)
.attr("height", height)
.attr("viewBox", [-marginLeft, left.x - marginTop, width, height])
.tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));

const node = gNode.selectAll("g")
.data(nodes, d => d.id);

const nodeEnter = node.enter().append("g")
.attr("transform", d => `translate(${source.y0},${source.x0})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
.on("click", (event, d) => {
d.children = d.children ? null : d._children;
update(event, d);
})
.on('mouseover', handleMouseOver)
.on('mouseout', handleMouseOut);

nodeEnter.append("circle")
.attr("r", 2.5)
.attr("fill", d => d._children ? "#555" : "#999")
.attr("stroke-width", 10);

nodeEnter.append("text")
.attr("dy", "0.31em")
.attr("x", d => d._children ? -6 : 6)
.attr("text-anchor", d => d._children ? "end" : "start")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.attr("stroke", "white");

const nodeUpdate = node.merge(nodeEnter).transition(transition)
.attr("transform", d => `translate(${d.y},${d.x})`)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);

const nodeExit = node.exit().transition(transition).remove()
.attr("transform", d => `translate(${source.y},${source.x})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);

const link = gLink.selectAll("path")
.data(links, d => d.target.id);

const linkEnter = link.enter().append("path")
.attr("d", d => {
const o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});

link.merge(linkEnter).transition(transition)
.attr("d", diagonal);

link.exit().transition(transition).remove()
.attr("d", d => {
const o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
});

root.eachBefore(d => {
d.x0 = d.x;
d.y0 = d.y;
});
}

root.x0 = dy / 2;
root.y0 = 0;
root.descendants().forEach((d, i) => {
d.id = i;
d._children = d.children;
if (d.depth && d.data.name.length !== 7) d.children = null;
});

update(null, root);

return svg.node();

}
Insert cell
Insert cell
Insert cell
WorldMap=
{
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("text-align", "center")
.style("padding", "8px")
.style("background", "yellow")
.style("border", "1px solid #000")
.style("border-radius", "4px")
.style("pointer-events", "none");

const colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, d3.max(transformedVData, d => d.TOTAL_VACCINATIONS_PER100)]);
const projection = d3.geoMercator().fitSize([width, height], world);
const path = d3.geoPath().projection(projection);


svg.selectAll("path")
.data(world.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", d => {
const country = transformedVData.find(c => c.id === d.id);
return country ? colorScale(country.TOTAL_VACCINATIONS_PER100) : "#ccc";
})
.attr("stroke", "#000")
.attr("stroke-width", 0.5)
.on("mouseover", (event, d) => {
d3.select(event.currentTarget).style("fill", "#f77");

const country = transformedVData.find(c => c.id === d.id);
tooltip.style("opacity", .9)
.html(country ? country.COUNTRY + ": " + country.TOTAL_VACCINATIONS_PER100 : "No Data")
.style("left", (event.pageX) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", (event, d) => {
tooltip.style("opacity", 0);

const country = transformedVData.find(c => c.id === d.id);
d3.select(event.currentTarget).style("fill", country ? colorScale(country.TOTAL_VACCINATIONS_PER100) : "#ccc");
});

return svg.node();
}

Insert cell
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