export function renderCaloriesVisualization() { fetch("../static/js/final_combined_with_all_data.json") .then((response) => response.json()) .then((data) => { const parseDate = d3.timeParse("%Y-%m-%d"); const formatYear = d3.timeFormat("%Y"); const formatMonth = d3.timeFormat("%Y-%m"); const members = ["Corentin", "Maya", "Anis", "Amira"]; data.forEach((d) => (d.date = parseDate(d.date))); const margin = { top: 50, right: 230, bottom: 150, left: 70 }; const width = 800 - margin.left - margin.right; const height = 500 - margin.top - margin.bottom; const svg = d3 .select("#calories-visualization") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); const years = [2022, 2023, 2024]; const prevYearBtn = document.getElementById("caloriesPrevYear"); const nextYearBtn = document.getElementById("caloriesNextYear"); const currentYearDisplay = document.getElementById("caloriesCurrentYear"); // Gestion des événements des boutons prevYearBtn.addEventListener("click", () => { const currentYearIndex = years.indexOf( parseInt(currentYearDisplay.textContent) ); if (currentYearIndex > 0) { const newYear = years[currentYearIndex - 1]; currentYearDisplay.textContent = newYear; updateVisualization(newYear); updateAnalysis("calories", newYear); // Activer/désactiver les boutons nextYearBtn.disabled = false; if (currentYearIndex - 1 === 0) { prevYearBtn.disabled = true; } } }); nextYearBtn.addEventListener("click", () => { const currentYearIndex = years.indexOf( parseInt(currentYearDisplay.textContent) ); if (currentYearIndex < years.length - 1) { const newYear = years[currentYearIndex + 1]; currentYearDisplay.textContent = newYear; updateVisualization(newYear); updateAnalysis("calories", newYear); // Activer/désactiver les boutons prevYearBtn.disabled = false; if (currentYearIndex + 1 === years.length - 1) { nextYearBtn.disabled = true; } } }); // Initialisation des boutons prevYearBtn.disabled = years.indexOf(parseInt(currentYearDisplay.textContent)) === 0; nextYearBtn.disabled = years.indexOf(parseInt(currentYearDisplay.textContent)) === years.length - 1; updateVisualization(2024); function updateVisualization(selectedYear) { const filteredData = data.filter( (d) => formatYear(d.date) === selectedYear.toString() ); if (filteredData.length === 0) { console.log(`Aucune donnée pour l'année ${selectedYear}`); return; } const groupedData = d3.groups(filteredData, (d) => formatMonth(d.date)); const aggregatedData = groupedData.map(([month, records]) => { const aggregated = { month }; members.forEach((member) => { aggregated[`Calories_${member}`] = d3.mean( records, (d) => d[`Calories_${member}`] || 0 ); }); return aggregated; }); svg.selectAll("*").remove(); svg .append("text") .attr("x", width / 2) .attr("y", margin.top - 60) .attr("text-anchor", "middle") .style("font-size", "16px") .style("font-weight", "bold") .text("Analyse des calories brûlées par mois"); const xScale = d3 .scaleBand() .domain(aggregatedData.map((d) => d.month)) .range([0, width]) .padding(0.2); const yScale = d3 .scaleLinear() .domain([ 0, d3.max(aggregatedData, (d) => Math.max(...members.map((member) => d[`Calories_${member}`])) ), ]) .nice() .range([height, 0]); const colorScale = d3 .scaleOrdinal() .domain(members) .range(["#1d38e3", "#0f7e06", "#d6bff4", "#7e09bd"]); svg .append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(xScale)) .selectAll("text") .attr("transform", "rotate(-45)") .style("text-anchor", "end"); svg .append("text") .attr("x", width / 2) .attr("y", height + 70) .attr("text-anchor", "middle") .style("font-size", "14px") .text("Mois"); svg.append("g").call(d3.axisLeft(yScale)); svg .append("text") .attr("transform", "rotate(-90)") .attr("x", -height / 2) .attr("y", -50) .attr("text-anchor", "middle") .style("font-size", "14px") .text("Calories brûlées"); // Ajout de la légende const legend = svg .append("g") .attr( "transform", `translate(${width / 2 - (members.length * 120) / 2}, ${ height + 100 })` ); // Positionnement members.forEach((member, i) => { const legendGroup = legend .append("g") .attr("transform", `translate(${i * 120}, 0)`); // Espacement horizontal // Rectangle coloré legendGroup .append("rect") .attr("width", 15) .attr("height", 15) .attr("fill", colorScale(member)) .style("opacity", 0.8); // Texte descriptif legendGroup .append("text") .attr("x", 20) // Position par rapport au rectangle .attr("y", 12) // Alignement vertical .style("font-size", "12px") .text(member); }); members.forEach((member) => { const lineData = aggregatedData.map((d) => ({ month: d.month, calories: d[`Calories_${member}`] || 0, })); const line = d3.line() .x((d) => xScale(d.month) + xScale.bandwidth() / 2) .y((d) => yScale(d.calories)); svg .append("path") .data([lineData]) .attr("fill", "none") .attr("stroke", colorScale(member)) // Couleur de la ligne .attr("stroke-width", 2) // Épaisseur de la ligne .attr("d", line); }); } }); }