function createVaccinationChart(data, title) {
const width = 1000;
const height = 500;
const marginTop = 50;
const marginRight = 400;
const marginBottom = 80;
const marginLeft = 60;
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 12px sans-serif;");
svg.append("text")
.attr("x", width / 2)
.attr("y", marginTop / 2)
.attr("text-anchor", "middle")
.style("font-size", "18px")
.style("font-weight", "bold")
.text(`Persons Vaccinated Against Seasonal Influenza by ${title} (2019-2023)`);
const years = ["2019-20", "2020-21", "2021-22", "2022-23"];
const x = d3.scalePoint()
.domain(years)
.range([marginLeft, width - marginRight])
.padding(0.5);
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - marginBottom, marginTop]);
// Define line generator
const line = d3.line()
.x(d => x(d.year))
.y(d => y(d.value));
// Process data for line generation
const processedData = data.map(d => {
return {
group: d.group,
values: years.map(year => ({
year,
value: d[year]
}))
};
});
// Create color scale
const color = d3.scaleOrdinal()
.domain(processedData.map(d => d.group))
.range(d3.schemeCategory10);
// Add X-axis
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x));
// Add bold X-axis title in the middle
svg.append("text")
.attr("x", width / 2)
.attr("y", height - marginBottom + 40)
.attr("text-anchor", "middle")
.style("font-weight", "bold")
.text("Year");
// Add Y-axis
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y).tickFormat(d => d + "%"));
// Add bold Y-axis title in the middle
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -(height - marginBottom + marginTop) / 2)
.attr("y", marginLeft - 40)
.attr("text-anchor", "middle")
.style("font-weight", "bold")
.text("Percentage Vaccinated");
// Add target line (70%)
svg.append("line")
.attr("x1", marginLeft)
.attr("y1", y(70))
.attr("x2", width - marginRight)
.attr("y2", y(70))
.attr("stroke", "gray")
.attr("stroke-width", 1)
.attr("stroke-dasharray", "5,5");
// Add target label
svg.append("text")
.attr("x", width - marginRight + 10)
.attr("y", y(70) + 4)
.attr("fill", "gray")
.text("Target: 70%");
// Create groups for lines and points
const groups = svg.append("g")
.selectAll("path")
.data(processedData)
.join("g");
// Add lines
const lines = groups.append("path")
.attr("fill", "none")
.attr("stroke", d => color(d.group))
.attr("stroke-width", 2)
.attr("d", d => line(d.values))
.attr("data-group", d => d.group);
// Add points
const points = groups.selectAll("circle")
.data(d => d.values.map(v => ({...v, group: d.group})))
.join("circle")
.attr("cx", d => x(d.year))
.attr("cy", d => y(d.value))
.attr("r", 5)
.attr("fill", d => color(d.group))
.attr("stroke", "white")
.attr("stroke-width", 1)
.attr("data-group", d => d.group);
// Create tooltip div
const tooltip = d3.select("body").append("div")
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("border-radius", "4px")
.style("padding", "10px")
.style("box-shadow", "0 2px 4px rgba(0,0,0,0.1)")
.style("font-size", "12px")
.style("visibility", "hidden");
// Add interaction
points
.on("mouseover", function(event, d) {
// Show tooltip
tooltip
.style("visibility", "visible")
.html(`<strong>Period:</strong> ${d.year}<br>
<strong>Group:</strong> ${d.group}<br>
<strong>Percentage:</strong> ${d.value}%`)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
// Highlight the current group
const currentGroup = d.group;
lines.style("opacity", l => l.group === currentGroup ? 1 : 0.2);
points.style("opacity", p => p.group === currentGroup ? 1 : 0.2);
})
.on("mouseout", function() {
// Hide tooltip
tooltip.style("visibility", "hidden");
// Restore all lines and points
lines.style("opacity", 1);
points.style("opacity", 1);
});
// Move legend to bottom right
const legendX = width - marginRight + 20;
const legendY = height - marginBottom - (processedData.length * 20) - 10;
const legend = svg.append("g")
.attr("transform", `translate(${legendX}, ${legendY})`);
processedData.forEach((d, i) => {
const legendRow = legend.append("g")
.attr("transform", `translate(0, ${i * 20})`);
legendRow.append("rect")
.attr("width", 12)
.attr("height", 12)
.attr("fill", color(d.group));
legendRow.append("text")
.attr("x", 20)
.attr("y", 10)
.text(d.group);
});
return svg.node();
}