diff --git a/frontend/index.html b/frontend/index.html index 9e18fb39df7c8afcf8ff2bd0d98cdb19772ba4ad..f2e21bd920c4580a92b8a3a6c1616413fb8134e4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -248,35 +248,97 @@ </div> </section> - <section id="radial-distance-chart" class="details"> + <section id="radial-distance-chart-Anis" class="details"> <div class="container"> <div class="row align-items-center"> - <!-- Visualisation à gauche --> + <!-- Visualisation pour Anis --> <div class="col-md-6 order-md-1"> - <div id="chart" class="d-flex justify-content-center align-items-center"></div> + <div id="chart-Anis" class="d-flex justify-content-center align-items-center"></div> + </div> + <!-- Texte explicatif --> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse Radiale : Distance & Sommeil (Anis)</h3> + <p> + Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine pour <strong>Anis</strong>. + </p> + <ul> + <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> + <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> + <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> + </ul> + </div> + </div> + </div> + </section> + + <section id="radial-distance-chart-Maya" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation pour Maya --> + <div class="col-md-6 order-md-1"> + <div id="chart-Maya" class="d-flex justify-content-center align-items-center"></div> + </div> + <!-- Texte explicatif --> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse Radiale : Distance & Sommeil (Maya)</h3> + <p> + Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine pour <strong>Maya</strong>. + </p> + <ul> + <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> + <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> + <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> + </ul> + </div> + </div> + </div> + </section> + + <!-- Répétez pour Corentin et Amira --> + <section id="radial-distance-chart-Corentin" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation pour Corentin --> + <div class="col-md-6 order-md-1"> + <div id="chart-Corentin" class="d-flex justify-content-center align-items-center"></div> + </div> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse Radiale : Distance & Sommeil (Corentin)</h3> + <p> + Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine pour <strong>Corentin</strong>. + </p> + <ul> + <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> + <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> + <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> + </ul> + </div> + </div> + </div> + </section> + + <section id="radial-distance-chart-Amira" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation pour Amira --> + <div class="col-md-6 order-md-1"> + <div id="chart-Amira" class="d-flex justify-content-center align-items-center"></div> </div> - <!-- Texte à droite --> <div class="col-md-6 order-md-2" data-aos="fade-left"> - <h3>Analyse Radiale : Distance & Sommeil</h3> + <h3>Analyse Radiale : Distance & Sommeil (Amira)</h3> <p> - Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine. - Vous pouvez sélectionner une personne pour explorer ses données. + Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine pour <strong>Amira</strong>. </p> <ul> <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> </ul> - <select class="form-select" id="personDropdown"> - <option value="Distance_Anis">Anis</option> - <option value="Distance_Maya">Maya</option> - <option value="Distance_Corentin">Corentin</option> - <option value="Distance_Amira">Amira</option> - </select> </div> </div> </div> </section> + </main> diff --git a/static/js/main.js b/static/js/main.js index 22596e3a308a7aaddac12525455a2e20f55d617d..e8e875df6d85cdf7b07d8ea3c5f2830ed68b9f5c 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1321,10 +1321,10 @@ function renderRadialDistanceChart() { fetch("../static/js/final_combined_with_all_data.json") .then((response) => response.json()) .then((data) => { - const width = 600; - const height = 600; - const innerRadius = 50; - const outerRadius = Math.min(width, height) / 2 - 100; + const width = 300; + const height = 300; + const innerRadius = 30; + const outerRadius = Math.min(width, height) / 2 - 20; // Filter data const filteredData = data.filter((d) => { @@ -1342,7 +1342,7 @@ function renderRadialDistanceChart() { const processedData = Array.from(groupedData, ([week, records]) => { const aggregated = { week: week, - year: week.split("-")[0], // Extract the year from the week + year: week.split("-")[0], Distance_Anis: d3.sum(records, (d) => (d.Distance_Anis > 0 ? d.Distance_Anis : 0)), Distance_Maya: d3.sum(records, (d) => (d.Distance_Maya > 0 ? d.Distance_Maya : 0)), Distance_Corentin: d3.sum(records, (d) => (d.Distance_Corentin > 0 ? d.Distance_Corentin : 0)), @@ -1355,17 +1355,14 @@ function renderRadialDistanceChart() { return aggregated; }); - const dropdown = document.getElementById("personDropdown"); - dropdown.addEventListener("change", () => updateChart(dropdown.value)); + const users = ["Anis", "Maya", "Corentin", "Amira"]; + users.forEach((user) => { + const personKey = `Distance_${user}`; + const sleepKey = `Sleep_${user}`; - // Initial chart rendering - updateChart(dropdown.value); + d3.select(`#chart-${user}`).html(""); // Clear the previous chart - function updateChart(personKey) { - const sleepKey = personKey.replace("Distance", "Sleep"); - d3.select("#chart").html(""); // Clear the previous chart - - const svg = d3.select("#chart") + const svg = d3.select(`#chart-${user}`) .append("svg") .attr("width", width) .attr("height", height) @@ -1386,159 +1383,65 @@ function renderRadialDistanceChart() { .domain([0, d3.max(processedData, (d) => d[sleepKey])]) .range(["lightblue", "darkblue"]); - // Add year to the center - const uniqueYears = [...new Set(processedData.map((d) => d.year))]; - if (uniqueYears.length === 1) { - svg - .append("text") - .attr("text-anchor", "middle") - .attr("dy", "0.35em") - .style("font-size", "20px") - .text(uniqueYears[0]); - } - // Bars -svg.append("g") -.selectAll("path") -.data(processedData) -.join("path") -.attr("d", d3.arc() - .innerRadius(innerRadius) - .outerRadius((d) => d[personKey] > 0 ? y(d[personKey]) : y(20)) // Hauteur fixe (20 km) pour valeurs manquantes - .startAngle((d) => x(d.week)) - .endAngle((d) => x(d.week) + x.bandwidth()) - .padAngle(0.02) - .padRadius(innerRadius)) -.attr("fill", (d) => d[personKey] > 0 ? color(d[sleepKey]) : "#ccc") // Couleur grise pour valeurs manquantes -.append("title") -.text((d) => `${d.week}\nDistance: ${d[personKey] > 0 ? d[personKey].toFixed(2) : "N/A"}\nSleep: ${d[sleepKey] > 0 ? d[sleepKey].toFixed(2) + "h" : "N/A"}`); - - // Week Labels with grouped years -svg.append("g") -.selectAll("g") -.data(processedData) -.join("g") -.attr("transform", (d) => { - const midAngle = (x(d.week) + x.bandwidth() / 2) * 180 / Math.PI - 90; - const radius = outerRadius + 25; - return ` - rotate(${midAngle}) - translate(${radius},0) - `; -}) -.call((g) => { - g.append("text") - .text((d, i) => { - // Only show the year once for the first week of each year - if (i === 0 || d.year !== processedData[i - 1].year) { - return `${d.year} ${d.week.split("-")[1]}`; - } - return `${d.week.split("-")[1]}`; - }) - .attr("text-anchor", "middle"); -}); -// Legend -const defs = svg.append("defs"); -const gradient = defs.append("linearGradient") - .attr("id", "sleepGradient") - .attr("x1", "0%") - .attr("y1", "0%") - .attr("x2", "100%") - .attr("y2", "0%"); - -gradient.append("stop").attr("offset", "0%").attr("stop-color", "lightblue"); -gradient.append("stop").attr("offset", "100%").attr("stop-color", "darkblue"); - -const legend = svg.append("g") - .attr("transform", `translate(${-width / 2 + 30}, ${-height / 2 + 50})`); // Position à gauche - -// Title -legend.append("text") - .attr("y", 0) - .attr("x", 0) - .text("Durée de sommeil") - .attr("font-size", "12px") - .attr("text-anchor", "start") - .attr("font-weight", "bold"); - -// Gradient bar -legend.append("rect") - .attr("x", 0) - .attr("y", 15) // Space below the title - .attr("width", 100) // Longer gradient for better visibility - .attr("height", 10) - .style("fill", "url(#sleepGradient)"); - -// Min and Max values for sleep -legend.append("text") - .attr("x", -5) // Align with start of gradient - .attr("y", 35) - .text(`${d3.min(processedData, (d) => d[sleepKey]).toFixed(1)}h`) - .attr("font-size", "10px") - .attr("text-anchor", "start"); - -legend.append("text") - .attr("x", 105) // Align with end of gradient - .attr("y", 35) - .text(`${d3.max(processedData, (d) => d[sleepKey]).toFixed(1)}h`) - .attr("font-size", "10px") - .attr("text-anchor", "start"); - -// Gray bar for missing data -legend.append("rect") - .attr("x", 0) - .attr("y", 50) // Space below the gradient - .attr("width", 100) // Same width as gradient for consistency - .attr("height", 10) - .style("fill", "#ccc"); - -// Label for gray bar -legend.append("text") - .attr("x", 0) // Align with start of gray bar - .attr("y", 70) // Space below the gray bar - .text("Valeur manquante") - .attr("font-size", "10px") - .attr("text-anchor", "start"); - - - // Add radial distance circles -const distanceTicks = y.ticks(5); // Nombre de cercles (5 niveaux ici) -const circleGroup = svg.append("g") - .attr("class", "distance-circles"); - -// Dessiner les cercles -circleGroup.selectAll("circle") - .data(distanceTicks) - .join("circle") - .attr("r", (d) => y(d)) - .attr("fill", "none") - .attr("stroke", "#ccc") - .attr("stroke-dasharray", "4 2"); // Ligne pointillée (facultatif) - -// Ajouter les étiquettes pour les distances -circleGroup.selectAll("text") - .data(distanceTicks) - .join("text") - .attr("x", 0) - .attr("y", (d) => -y(d)) // Positionné au-dessus des cercles - .attr("dy", "-0.3em") - .attr("text-anchor", "middle") - .attr("font-size", "10px") - .text((d) => `${d.toFixed(0)} km`); // Format pour afficher les valeurs - } + svg.append("g") + .selectAll("path") + .data(processedData) + .join("path") + .attr("d", d3.arc() + .innerRadius(innerRadius) + .outerRadius((d) => d[personKey] > 0 ? y(d[personKey]) : y(10)) + .startAngle((d) => x(d.week)) + .endAngle((d) => x(d.week) + x.bandwidth()) + .padAngle(0.02) + .padRadius(innerRadius)) + .attr("fill", (d) => d[personKey] > 0 ? color(d[sleepKey]) : "#ccc") + .append("title") + .text((d) => `${d.week}\nDistance: ${d[personKey] > 0 ? d[personKey].toFixed(2) : "N/A"} km\nSommeil: ${d[sleepKey] > 0 ? d[sleepKey].toFixed(2) + "h" : "N/A"}`); + + // Add user label + svg.append("text") + .attr("text-anchor", "middle") + .attr("dy", "-1em") + .style("font-size", "14px") + .style("font-weight", "bold") + .text(user); + + // Radial circles + const distanceTicks = y.ticks(5); + const circleGroup = svg.append("g"); + + circleGroup.selectAll("circle") + .data(distanceTicks) + .join("circle") + .attr("r", (d) => y(d)) + .attr("fill", "none") + .attr("stroke", "#ccc") + .attr("stroke-dasharray", "4 2"); + + circleGroup.selectAll("text") + .data(distanceTicks) + .join("text") + .attr("x", 0) + .attr("y", (d) => -y(d)) + .attr("dy", "-0.3em") + .attr("text-anchor", "middle") + .style("font-size", "10px") + .text((d) => `${d.toFixed(0)} km`); + }); }) .catch((error) => console.error("Error loading data:", error)); - function getISOWeekNumber(date) { - const tempDate = new Date(date); - tempDate.setDate(tempDate.getDate() + 4 - (tempDate.getDay() || 7)); - const yearStart = new Date(tempDate.getFullYear(), 0, 1); - return Math.ceil(((tempDate - yearStart) / 86400000 + 1) / 7); - } - + function getISOWeekNumber(date) { + const tempDate = new Date(date); + tempDate.setDate(tempDate.getDate() + 4 - (tempDate.getDay() || 7)); + const yearStart = new Date(tempDate.getFullYear(), 0, 1); + return Math.ceil(((tempDate - yearStart) / 86400000 + 1) / 7); + } } + document.addEventListener("DOMContentLoaded", function () { renderStepsVisualization(); renderDistanceVisualization();