From eba518c49710a01d7ef5ced1eb8e2f1c735f4e7d Mon Sep 17 00:00:00 2001 From: p2312048 <maya.boudjebbour@etu.univ-lyon1.fr> Date: Thu, 2 Jan 2025 00:23:48 +0100 Subject: [PATCH] Update visualisation sommeil --- Visu/index.html | 48 ++++++++++--------- Visu/script.js | 121 ++++++++++++++++++++++++++++-------------------- Visu/style.css | 53 ++++++++++++++++++--- 3 files changed, 142 insertions(+), 80 deletions(-) diff --git a/Visu/index.html b/Visu/index.html index 79b452d..5ca6ba6 100644 --- a/Visu/index.html +++ b/Visu/index.html @@ -8,30 +8,34 @@ </head> <body> <div class="container"> - <h1>Comparaison des Cycles de Sommeil</h1> - - <!-- Sélecteur d'année --> - <label for="yearSelect">Choisissez l'année:</label> - <select id="yearSelect"> - <option value="2024">2024</option> - <option value="2021">2021</option> - <option value="2022">2022</option> - <option value="2023">2023</option> - <!-- Vous pouvez ajouter d'autres années si nécessaire --> - </select> - - <!-- Sélecteur de membre --> - <label for="memberSelect">Choisissez un membre:</label> - <select id="memberSelect"> - <option value="all">Tous les membres</option> - <option value="Anis">Anis</option> - <option value="Maya">Maya</option> - <option value="Corentin">Corentin</option> - <option value="Amira">Amira</option> - </select> + <!-- Conteneur de la partie sélecteurs à gauche --> + <div class="selectors"> + <!-- Sélecteur d'année sous forme de liste déroulante --> + <label for="yearSelect">Choisissez l'année:</label> + <select id="yearSelect"> + <option value="2024">2024</option> + <option value="2023">2023</option> + <option value="2022">2022</option> + <option value="2021">2021</option> + + </select> + + <!-- Sélecteur de membre --> + <label for="memberSelect">Choisissez un membre:</label> + <select id="memberSelect"> + <option value="all">Tous les membres</option> + <option value="Anis">Anis</option> + <option value="Maya">Maya</option> + <option value="Corentin">Corentin</option> + <option value="Amira">Amira</option> + </select> + </div> <!-- Espace pour le graphique --> - <svg id="chart"></svg> + <svg id="chart"> + <!-- Titre du graphique à placer dans le SVG --> + <text id="chartTitle" x="50%" y="30" text-anchor="middle" font-size="20" fill="#333">Comparaison des Cycles de Sommeil</text> + </svg> </div> <script src="https://d3js.org/d3.v7.min.js"></script> diff --git a/Visu/script.js b/Visu/script.js index cd5a5c1..bc7f010 100644 --- a/Visu/script.js +++ b/Visu/script.js @@ -7,7 +7,7 @@ d3.json("sleep_combined_clean.json").then(function(data) { // Parsing des dates et tri des données par mois data.forEach(d => { d.date = d3.timeParse("%Y-%m-%d")(d.date); - d.month = d3.timeMonth(d.date); // Utilisation de d3.timeMonth pour grouper par mois + d.month = d3.timeMonth(d.date); }); // Création du groupe pour chaque membre @@ -18,70 +18,81 @@ d3.json("sleep_combined_clean.json").then(function(data) { const monthsForYear = getMonthsForYear(selectedYear); // Filtrage des données par mois - var groupedData = d3.group(data, d => d.month); // Utilisation de d3.group pour le groupement par mois + var groupedData = d3.group(data, d => d.month); // Transformation des données en format plus approprié pour la visualisation var nestedData = monthsForYear.map(month => { - const valuesForMonth = groupedData.get(month) || []; // Données filtrées pour le mois + const valuesForMonth = groupedData.get(month) || []; + const avgMonthSleep = members.map(member => { + const memberData = valuesForMonth.filter(d => d[member] !== null); + return { + member: member, + value: memberData.length > 0 ? d3.mean(memberData, v => v[member]) : 0 + }; + }); return { month: month, - values: members.map(member => { - const memberData = valuesForMonth.filter(d => d[member] !== null); - const avgValue = memberData.length > 0 ? d3.mean(memberData, v => v[member]) : 0; - return { - member: member, - value: avgValue // Valeur calculée pour ce membre - }; - }) + values: avgMonthSleep }; }); // Dimensions du graphique var width = 800, height = 400; - var margin = {top: 20, right: 30, bottom: 60, left: 60}; // Ajuster les marges pour les axes + var margin = { top: 20, right: 120, bottom: 60, left: 60 }; - // Création de l'échelle des X et Y + // Création des échelles var x = d3.scaleBand() - .domain(monthsForYear) // Utiliser les mois de l'année sélectionnée + .domain(monthsForYear) .range([margin.left, width - margin.right]) .padding(0.1); - + var y = d3.scaleLinear() .domain([0, d3.max(nestedData, d => d3.max(d.values, v => v.value))]) .nice() .range([height - margin.bottom, margin.top]); - - // Création de l'échelle des couleurs pour chaque membre + var color = d3.scaleOrdinal() .domain(members) - .range(["#ff6347", "#4682b4", "#32cd32", "#ff1493"]); // Couleurs différentes pour chaque membre + .range(["#ff6347", "#4682b4", "#32cd32", "#ff1493"]); - // Création du SVG pour le graphique + // Création du SVG var svg = d3.select("#chart") .attr("width", width) .attr("height", height); // Nettoyer le SVG avant de redessiner - svg.selectAll("*").remove(); + svg.selectAll("*").remove(); - // Ajouter l'axe X + // Ajouter les axes svg.append("g") - .attr("transform", "translate(0," + (height - margin.bottom) + ")") - .call(d3.axisBottom(x).tickFormat(d3.timeFormat("%B"))) // Affichage des mois + .attr("transform", `translate(0, ${height - margin.bottom})`) + .call(d3.axisBottom(x).tickFormat(d3.timeFormat("%B"))) .selectAll("text") .style("text-anchor", "middle") - .style("font-size", "12px") - .attr("transform", "rotate(-45)") - .attr("dx", "-0.8em") - .attr("dy", "0.15em"); + .style("font-size", "12px"); - // Ajouter l'axe Y svg.append("g") - .attr("transform", "translate(" + margin.left + ",0)") + .attr("transform", `translate(${margin.left}, 0)`) .call(d3.axisLeft(y)); - // Créer le tooltip + // Ajouter les titres des axes + svg.append("text") + .attr("x", width / 2) + .attr("y", height - 5) + .attr("text-anchor", "middle") + .style("font-size", "16px") + .text("Mois"); + + svg.append("text") + .attr("transform", "rotate(-90)") + .attr("x", -height / 2) + .attr("y", 20) + .attr("text-anchor", "middle") + .style("font-size", "16px") + .text("Heures de sommeil"); + + // Création du tooltip var tooltip = d3.select("body").append("div") .attr("class", "tooltip") .style("position", "absolute") @@ -92,58 +103,66 @@ d3.json("sleep_combined_clean.json").then(function(data) { .style("border-radius", "5px") .style("pointer-events", "none"); - // Dessiner les barres pour chaque mois et chaque membre + // Dessiner les barres svg.append("g") .selectAll("g") .data(nestedData) .enter().append("g") - .attr("transform", d => "translate(" + x(d.month) + ",0)") + .attr("transform", d => `translate(${x(d.month)}, 0)`) .selectAll("rect") - .data(d => { - if (selectedMember === "all") { - return d.values; // Affiche tous les membres - } else { - return d.values.filter(v => v.member === selectedMember); // Affiche seulement le membre sélectionné - } - }) + .data(d => selectedMember === "all" ? d.values : d.values.filter(v => v.member === selectedMember)) .enter().append("rect") - .attr("x", (d, i) => i * (x.bandwidth() / 4)) // Chaque membre reçoit une portion de la bande + .attr("x", (d, i) => i * (x.bandwidth() / members.length)) .attr("y", d => y(d.value)) - .attr("width", x.bandwidth() / 4) // Largeur des barres + .attr("width", x.bandwidth() / members.length) .attr("height", d => height - margin.bottom - y(d.value)) .attr("fill", d => color(d.member)) - .append("title") - .text(d => d.member + ": " + d.value.toFixed(2) + " heures") .on("mouseover", function(event, d) { - // Afficher le tooltip lors du survol tooltip.style("visibility", "visible") .text(d.member + ": " + d.value.toFixed(2) + " heures"); }) .on("mousemove", function(event) { - // Déplacer le tooltip avec la souris tooltip.style("top", (event.pageY + 5) + "px") .style("left", (event.pageX + 5) + "px"); }) .on("mouseout", function() { - // Cacher le tooltip lorsqu'on arrête de survoler tooltip.style("visibility", "hidden"); }); + + // Ajouter la légende + var legend = svg.append("g") + .attr("transform", `translate(${width - margin.right + 20}, ${margin.top})`); + + members.forEach((member, i) => { + var legendRow = legend.append("g") + .attr("transform", `translate(0, ${i * 20})`); + + legendRow.append("rect") + .attr("width", 15) + .attr("height", 15) + .attr("fill", color(member)); + + legendRow.append("text") + .attr("x", 20) + .attr("y", 12) + .style("font-size", "12px") + .text(member); + }); } - // Initialiser avec 2024 et tous les membres + // Initialisation updateChart(2024, "all"); - // Mettre à jour le graphique lorsque l'utilisateur change l'année ou le membre + // Gestion des changements dans les sélections d3.select("#yearSelect").on("change", function() { const selectedYear = this.value; const selectedMember = d3.select("#memberSelect").node().value; - updateChart(selectedYear, selectedMember); // Redessiner le graphique avec la nouvelle année + updateChart(selectedYear, selectedMember); }); - // Mettre à jour le graphique lorsque l'utilisateur change la sélection du membre d3.select("#memberSelect").on("change", function() { const selectedMember = this.value; const selectedYear = d3.select("#yearSelect").node().value; - updateChart(selectedYear, selectedMember); // Redessiner le graphique avec le membre sélectionné + updateChart(selectedYear, selectedMember); }); }); diff --git a/Visu/style.css b/Visu/style.css index 7823d15..f6629cd 100644 --- a/Visu/style.css +++ b/Visu/style.css @@ -3,23 +3,63 @@ body { margin: 0; padding: 0; background-color: #f5f5f5; + display: flex; + justify-content: center; + align-items: flex-start; + height: 100vh; } .container { + display: flex; + justify-content: space-between; width: 80%; - margin: 0 auto; - padding-top: 20px; - text-align: center; + padding: 20px; + margin-top: 20px; + align-items: flex-start; } -h1 { - font-size: 24px; +.selectors { + width: 200px; + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 10px; + background-color: #ffffff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +label { + margin: 10px 0 5px; + font-size: 16px; +} + +select { + width: 100%; + padding: 8px; margin-bottom: 20px; + font-size: 16px; + border-radius: 4px; + border: 1px solid #ccc; +} + +#chart-container { + display: flex; + flex-direction: column; + align-items: center; /* Centrer tout le contenu à l'intérieur du conteneur */ + width: 60%; +} + +#mainTitle { + font-size: 24px; + margin-bottom: 20px; /* Espacement entre le titre et le graphique */ + text-align: center; /* Centrer le titre */ } #chart { - width: 100%; height: 500px; + width: 70%; + background-color: #ffffff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .tooltip { @@ -32,4 +72,3 @@ h1 { pointer-events: none; font-size: 14px; } - -- GitLab