do we still post code in blockquotes?
// Load the data using XMLHttpRequest
const req = new XMLHttpRequest();
req.open('GET', 'https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json', true);
req.send();
req.onreadystatechange = function() {
if (req.readyState === XMLHttpRequest.DONE) {
if (req.status === 200) {
const dataset = JSON.parse(req.responseText);
console.log(dataset);
console.log("First Item in dataset", dataset[0]);
draw(dataset);
}
}
};
// Create Tooltip
const tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.attr("id", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid black")
.style("padding", "5px")
.style("border-radius", "4px")
.style("pointer-events", "none");
function draw(dataset) {
console.log("Dataset structure:", dataset); // This should show an array of objects
console.log("1st item in dataset:", dataset[0]); // This should show an object with keys like Time, Place, etc.
const w = 1400;
const h = 700;
const padding = 50;
// Define the time parser for yScale (mm:ss format)
const timeParser = d3.timeParse("%M:%S");
// Define xScale to work directly with year values
const xScale = d3.scaleLinear()
.domain([d3.min(dataset, d => d.Year), d3.max(dataset, d => d.Year)])
.range([padding, w - padding]);
const yScale = d3.scaleTime()
.domain([
d3.min(dataset, (d) => timeParser(d.Time)), // Parse "mm:ss" strings to Date objects
d3.max(dataset, (d) => timeParser(d.Time))
])
.range([h - padding, padding]);
// Create SVG
const svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "graph")
// Add Title and Subtitle
svg.append("text")
.text("Doping in Professional Bicycle Racing")
.attr('text-anchor', 'middle')
.attr('x', w / 2)
.attr('y', 20 + padding)
.style('font-size', '40px')
.attr('id', 'title');
svg.append("text")
.text("35 Fastest times up Alpe d'Huez")
.attr('text-anchor', 'middle')
.attr('x', w / 2)
.attr('y', 70 + padding)
.style('font-size', '30px')
.attr('id', 'subTitle');
const xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%Y"));
//const yAxis = d3.axisLeft(yScale);
const yAxis = d3.axisLeft(yScale)
.tickFormat(d3.timeFormat("%M:%S")); // Format ticks as "mm:ss"
svg.append("g")
.attr("transform", `translate(0, ${h - padding})`)
.attr('id', 'x-axis')
.call(xAxis);
svg.append("g")
.attr("transform", `translate(${padding}, 0)`)
.attr("id", "y-axis")
.call(yAxis);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.Year))
.attr("cy", (d) => yScale(timeParser(d.Time)))
.attr("r", 5)
.attr("class", "dot")
.attr("data-xvalue", (d) => d.Year)
.attr("data-yvalue", (d) => d.Time)
.style("fill", (d) => d.Doping ? "red" : "blue")
.on("mouseover", function(event, d) {
console.log("Hovered data:", d);
tooltip.style("opacity", 1) // Make tooltip visible
.html(`
<strong>${d.Name}</strong>: ${d.Nationality}<br/>
<strong>Year:</strong> ${d.Year}<br/>
<strong>Time:</strong> ${d.Time}<br/>
${d.Doping ? `<strong>Doping:</strong> ${d.Doping}` : "No Doping"}
`)
.attr("data-year", d.Year)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 28}px`);
})
.on("mouseout", function() {
tooltip.style("opacity", 0); // Hide tooltip
});
// Add Legend
svg.append("text")
.attr('x', w - padding)
.attr('y', h - padding + 30)
.text('Legend');
// Legend for Doping Status
const legend = svg.append("g")
.attr("id", "legend")
.attr("transform", `translate(${w - 200},${h - 200})`);
legend.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 20)
.attr("height", 20)
.attr("fill", "red");
legend.append("text")
.attr("x", 30)
.attr("y", 15)
.text("Doping Allegations");
legend.append("rect")
.attr("x", 0)
.attr("y", 30)
.attr("width", 20)
.attr("height", 20)
.attr("fill", "blue");
legend.append("text")
.attr("x", 30)
.attr("y", 45)
.text("No Doping Allegations");
}