{
const width = 750,
height = 500,
margin = { top: 60, right: 220, bottom: 100, left: 80 };
const svg = d3.create("svg").attr("width", width).attr("height", height);
console.log("Slider value:", monthSlider1);
if (!scatterData || scatterData.length === 0) {
console.error("scatterData is empty:", scatterData);
return "No data available for plotting.";
}
const xScale = d3
.scaleLinear()
.domain([0, d3.max(scatterData, (d) => d.totalPrecipitation)])
.range([margin.left, width - margin.right]);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(scatterData, (d) => d.totalWildfires)])
.range([height - margin.bottom, margin.top]);
const seasonNames = ["Winter", "Spring", "Summer", "Fall"];
const seasonColors = ["#1f77b4", "#2ca02c", "#d62728", "#ff7f0e"];
const colorScale = d3.scaleOrdinal().domain(seasonNames).range(seasonColors);
const sizeScale = d3
.scaleSqrt()
.domain([0, d3.max(scatterData, (d) => d.avgWindSpeed)])
.range([3, 16]);
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale));
svg
.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale));
// **Graph Title**
svg
.append("text")
.attr("x", width / 2)
.attr("y", margin.top - 30)
.attr("text-anchor", "middle")
.attr("font-size", "18px")
.attr("font-weight", "bold")
.text("Total Precipitation vs. Wildfires (Monthly)");
// **X-Axis Label**
svg
.append("text")
.attr("x", (width - margin.right + margin.left) / 2) // Centering fix
.attr("y", height - 40) // Adjusted for spacing
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Total Precipitation (mm)");
// **Y-Axis Label**
svg
.append("text")
.attr("x", -height / 2)
.attr("y", margin.left / 3)
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Number of Wildfires");
// **Tooltip**
const tooltip = d3
.select("body")
.append("div")
.style("position", "absolute")
.style("background", "white")
.style("border", "1px solid #ccc")
.style("padding", "5px")
.style("display", "none");
const scatterGroup = svg.append("g");
function updateScatterByMonth(selectedMonth) {
// **Show all months up to the selected value**
const filteredData = scatterData.filter((d) => d.month <= selectedMonth);
console.log(`Filtering for months up to ${selectedMonth}`, filteredData);
scatterGroup.selectAll("circle").remove(); // **Clear old dots**
scatterGroup
.selectAll("circle")
.data(filteredData, (d) => d.month)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.totalPrecipitation))
.attr("cy", (d) => yScale(d.totalWildfires))
.attr("r", (d) => sizeScale(d.avgWindSpeed)) // **Updated smaller size**
.attr("fill", (d) => colorScale(d.season))
.attr("opacity", 0.8)
.on("mouseover", (event, d) => {
tooltip
.style("display", "block")
.html(
`Month: ${d.month}<br>
Precipitation: ${d.totalPrecipitation} mm<br>
Wildfires: ${d.totalWildfires}<br>
Wind Speed: ${d.avgWindSpeed}<br>
Season: ${d.season}`
)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 20}px`);
})
.on("mouseout", () => tooltip.style("display", "none"));
}
// **Listen to Slider Changes in ObservableHQ**
(async () => {
for await (const value of Generators.input(viewof monthSlider1)) {
console.log("Slider changed to:", value);
updateScatterByMonth(value);
}
})();
updateScatterByMonth(1); // **Initial Call to Render Month 1**
// **Legend for Seasons**
const legend = svg
.append("g")
.attr("transform", `translate(${width - 160}, ${margin.top})`);
// **Title for Season Legend**
legend
.append("text")
.attr("x", 0)
.attr("y", -15) // **Ensure it's visible**
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Season");
seasonNames.forEach((season, i) => {
legend
.append("circle")
.attr("cx", 0)
.attr("cy", i * 25)
.attr("r", 6)
.attr("fill", colorScale(season));
legend
.append("text")
.attr("x", 12)
.attr("y", i * 25 + 5)
.text(season)
.attr("font-size", "14px")
.attr("alignment-baseline", "middle");
});
// **Legend for Wind Speed (Bubble Size)**
const windSpeedLegend = svg
.append("g")
.attr("transform", `translate(${width - 160}, ${margin.top + 150})`);
// **Fix: Wind Speed Title Fully Visible**
windSpeedLegend
.append("text")
.attr("x", 0)
.attr("y", -30) // **Fixed cutoff issue**
.attr("font-size", "16px")
.attr("font-weight", "bold")
.text("Average Wind Speed");
const windSpeeds = [0, 2, 4, 6, 8];
const legendSpacing = 45; // **Slightly increased spacing**
windSpeeds.forEach((speed, i) => {
windSpeedLegend
.append("circle")
.attr("cx", 30) // Keep circles aligned
.attr("cy", i * legendSpacing)
.attr("r", sizeScale(speed)) // **Updated smaller size**
.attr("fill", "black")
.attr("opacity", 0.5);
windSpeedLegend
.append("text")
.attr("x", 75)
.attr("y", i * legendSpacing + 5)
.text(speed)
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("alignment-baseline", "middle");
});
return svg.node();
}