fetch('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)); // Dimensions et marges const margin = { top: 50, right: 230, bottom: 150, left: 70 }; const width = 1000 - margin.left - margin.right; const height = 500 - margin.top - margin.bottom; // Créer le conteneur SVG principal const svg = d3.select("#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})`); // Créer un conteneur pour le graphique détaillé const detailContainer = d3.select("#visualization") .append("div") .attr("id", "detail-container") .style("margin-top", "20px"); // Définir les années et la plage d'affichage const years = [2021, 2022, 2023, 2024]; // Créer le slider const sliderContainer = d3.select("#visualization") .append("div") .attr("id", "slider-container") .style("display", "flex") .style("align-items", "center") .style("justify-content", "space-between"); const rangeSlider = sliderContainer.append("input") .attr("type", "range") .attr("min", 0) .attr("max", years.length - 1) .attr("value", years.indexOf(2024)) .style("width", "60%"); const yearDisplay = sliderContainer.append("span") .text(years[years.indexOf(2024)]); // Afficher 2024 par défaut // Initialisation de la visualisation avec l'année 2024 updateVisualization(2024); // Fonction de mise à jour de la visualisation 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[`Steps_${member}`] = d3.mean(records, d => d[`Steps_${member}`] || 0); }); return aggregated; }); svg.selectAll("*").remove(); svg.append("text") .attr("x", width / 2) // Centrer horizontalement .attr("y", margin.top - 60) // Positionner légèrement au-dessus du graphique .attr("text-anchor", "middle") // Centrer le texte .style("font-size", "16px") // Taille de police .style("font-weight", "bold") // Gras .text("Analyse du nombre de pas 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[`Steps_${member}`])))]).nice() .range([height, 0]); const colorMap = { "Maya": "#0f7e06", "Corentin": "#1d38e3", "Anis": "#d6bff4", "Amira": "#7e09bd" }; //const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(members); // Axe X svg.append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(xScale)) .selectAll("text") .attr("transform", "rotate(-45)") .style("text-anchor", "end"); // Légende de l'axe X svg.append("text") .attr("x", width / 2) .attr("y", height + 70) .attr("text-anchor", "middle") .style("font-size", "14px") .text("Mois"); // Axe Y svg.append("g").call(d3.axisLeft(yScale)); // Légende de l'axe Y svg.append("text") .attr("transform", "rotate(-90)") .attr("x", -height / 2) .attr("y", -50) .attr("text-anchor", "middle") .style("font-size", "14px") .text("Nombre de pas"); // Légende des membres (rectangles colorés) const legend = svg.append("g") .attr("transform", `translate(${width + 10}, 20)`); members.forEach((member, i) => { legend.append("rect") .attr("x", 0) .attr("y", i * 20) .attr("width", 15) .attr("height", 15) .attr("fill", colorMap[member]); legend.append("text") .attr("x", 20) .attr("y", i * 20 + 12) .text(member) .style("font-size", "12px") .attr("text-anchor", "start"); }); // Lignes pour chaque membre members.forEach((member, i) => { const lineData = aggregatedData.map(d => { let steps = d[`Steps_${member}`] === -1.0 ? 0 : d[`Steps_${member}`]; // Remplacer -1.0 par 0 return { month: d.month, steps: steps }; }); // Vérifier si toutes les données d'un mois sont à 0 (indiquant qu'il n'y a pas de données pour ce mois) const isAllZero = lineData.every(d => d.steps === 0); const line = d3.line() .x(d => xScale(d.month) + xScale.bandwidth() / 2) .y(d => yScale(d.steps)) .defined(d => d.steps !== 0); // Ne pas tracer pour les valeurs égales à 0 (données manquantes) svg.append("path") .data([lineData]) .attr("class", `line-${member}`) .attr("d", line) .attr("fill", "none") .attr("stroke", colorMap[member]) .attr("stroke-width", 2) .style("stroke-dasharray", isAllZero ? "5,5" : "none") // Ajouter des pointillés si toutes les données sont manquantes }); } // Initialisation de la visualisation avec 2024 updateVisualization(years[3]); // Mettre à jour la visualisation lorsque le slider est déplacé rangeSlider.on("input", function() { const selectedYear = years[this.value]; yearDisplay.text(selectedYear); updateVisualization(selectedYear); }); });