Skip to content
Snippets Groups Projects
sleepVisual.js 9.86 KiB
Newer Older
  • Learn to ignore specific revisions
  • =AZIZI Anis's avatar
    =AZIZI Anis committed
    export function renderSleepVisualization() {
        fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
          .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 formatDay = d3.timeFormat("%d");
            const members = ["Corentin", "Maya", "Anis", "Amira"];
            data.forEach((d) => (d.date = parseDate(d.date)));
      
            const margin = { top: 30, right: 30, bottom: 50, left: 50 };
            const fullWidth = 600;
            const fullHeight = 400;
            const width = (fullWidth - margin.left - margin.right) / 2;
            const height = (fullHeight - margin.top - margin.bottom) / 2;
      
            const svg = d3
              .select("#sleep-visualization")
              .append("svg")
              .attr("width", fullWidth)
    
    =AZIZI Anis's avatar
    =AZIZI Anis committed
              .attr("height", fullHeight*1.1);
    
    =AZIZI Anis's avatar
    =AZIZI Anis committed
      
            const tooltip = d3
              .select("body")
              .append("div")
              .attr("class", "tooltip-sleep")
              .style("opacity", 0);
      
            const modal = d3.select("#modal");
            modal.select(".close").on("click", () => {
              modal.style("display", "none");
              d3.select("#detail-visualization").selectAll("*").remove();
            });
      
            const prevYearBtn = document.getElementById("sleepPrevYear");
            const nextYearBtn = document.getElementById("sleepNextYear");
            const currentYearDisplay = document.getElementById("sleepCurrentYear");
            const years = [2021, 2022, 2023, 2024];
      
            prevYearBtn.addEventListener("click", () => {
              const currentYearIndex = years.indexOf(
                parseInt(currentYearDisplay.textContent)
              );
              if (currentYearIndex > 0) {
                const newYear = years[currentYearIndex - 1];
                currentYearDisplay.textContent = newYear;
                updateVisualization(newYear);
              }
            });
      
            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);
              }
            });
      
            function updateVisualization(selectedYear) {
              const filteredData = data.filter(
                (d) => formatYear(d.date) === selectedYear.toString()
              );
              const groupedData = d3.groups(filteredData, (d) => formatMonth(d.date));
      
              const colorMap = {
                Maya: "#0f7e06",
                Corentin: "#1d38e3",
                Anis: "#d6bff4",
                Amira: "#7e09bd",
              };
          
      
              const aggregatedData = groupedData.map(([month, records]) => {
                const aggregated = { month };
                members.forEach((member) => {
                  aggregated[`Sleep_${member}`] = d3.mean(
                    records,
                    (d) => d[`Sleep_${member}`] || -1
                  );
                });
                return aggregated;
              });
      
              svg.selectAll("g.member-group").remove();
      
              members.forEach((member, memberIndex) => {
                const row = Math.floor(memberIndex / 2);
                const col = memberIndex % 2;
              
                // Création d'un groupe pour chaque membre
                const memberGroup = svg
                  .append("g")
                  .attr("class", "member-group")
                  .attr(
                    "transform",
                    `translate(${margin.left + col * (width + margin.right)}, ${
                      margin.top + row * (height + margin.bottom)
                    })`
                  );
              
                // Données spécifiques au membre
                const memberData = aggregatedData.map((d) => ({
                  month: d.month,
                  sleep: d[`Sleep_${member}`],
                }));
              
                // Échelles X et Y
                const xScale = d3
                  .scaleBand()
                  .domain(memberData.map((d) => d.month)) // Domain = mois
                  .range([0, width]) // Plage sur l'axe X
                  .padding(0.2);
              
                const yScale = d3
                  .scaleLinear()
                  .domain([
                    0,
                    d3.max(memberData, (d) => (d.sleep === -1 ? 10 : d.sleep)), // Si -1, max à 10
                  ])
                  .nice()
                  .range([height, 0]);
              
                // Ajout de l'axe X
                memberGroup
                  .append("g")
                  .attr("transform", `translate(0, ${height})`) // Place l'axe X en bas du graphique
                  .call(
                    d3.axisBottom(xScale).tickFormat((d) => d.split("-")[1]) // Affiche uniquement le mois
                  )
                  .selectAll("text")
                  .attr("transform", "rotate(-45)") // Rotation pour éviter les chevauchements
                  .style("text-anchor", "end")
                  .style("font-size", "10px");
              
                // Ajout de l'axe Y
                memberGroup.append("g").call(d3.axisLeft(yScale));
              
                // Titre du sous-graphe
                memberGroup
                  .append("text")
                  .attr("x", width / 2)
                  .attr("y", -10)
                  .attr("text-anchor", "middle")
                  .style("font-size", "14px")
                  .style("font-weight", "bold")
                  .text(`Sommeil de ${member}`);
              
                // Ajout des barres
                memberGroup
                  .selectAll(".bar-sleep")
                  .data(memberData)
                  .enter()
                  .append("rect")
                  .attr("x", (d) => xScale(d.month))
                  .attr("y", (d) => (d.sleep === -1 ? yScale(10) : yScale(d.sleep)))
                  .attr("width", xScale.bandwidth())
                  .attr("height", (d) =>
                    d.sleep === -1 ? height - yScale(10) : height - yScale(d.sleep)
                  )
                  .attr("fill", (d) => (d.sleep === -1 ? "#D3D3D3" : colorMap[member]))
                  .on("mouseover", function (event, d) {
                    tooltip.transition().duration(200).style("opacity", 1);
                    tooltip.html(`
                      <div style="text-align: center;">
                        <strong>Mois :</strong> ${d.month}<br>
                        <strong>Sommeil moyen :</strong> ${
                          d.sleep === -1 ? "Pas de données" : d.sleep.toFixed(2)
                        } heures
                      </div>
                    `);
                  })
                  .on("mousemove", function (event) {
                    tooltip
                      .style("left", event.pageX + 15 + "px")
                      .style("top", event.pageY + 15 + "px");
                  })
                  .on("mouseout", function () {
                    tooltip.transition().duration(500).style("opacity", 0);
                  })
                  .on("click", (event, d) => {
                    showDetailChart(d.month, member, selectedYear, colorMap[member]);
                  });
              });
              
      
              function showDetailChart(month, member, year, memberColor) {
                modal.style("display", "block");
                const detailContainer = d3.select("#detail-visualization");
                detailContainer.selectAll("*").remove();
      
                const filteredData = data.filter(
                  (d) =>
                    formatYear(d.date) === year.toString() &&
                    formatMonth(d.date) === month
                );
      
                const dailyData = d3
                  .groups(filteredData, (d) => formatDay(d.date))
                  .map(([day, records]) => ({
                    day: day,
                    value: d3.mean(records, (d) => d[`Sleep_${member}`] || 0),
                  }));
      
                const detailSvg = detailContainer
                  .append("svg")
                  .attr("width", 600)
                  .attr("height", 400)
                  .append("g")
                  .attr("transform", "translate(50, 50)");
      
                const xScale = d3
                  .scaleBand()
                  .domain(dailyData.map((d) => d.day))
                  .range([0, 500])
                  .padding(0.1);
      
                const yScale = d3
                  .scaleLinear()
                  .domain([0, d3.max(dailyData, (d) => d.value)])
                  .nice()
                  .range([300, 0]);
      
                detailSvg
                  .append("g")
                  .attr("transform", "translate(0, 300)")
                  .call(d3.axisBottom(xScale));
      
                detailSvg.append("g").call(d3.axisLeft(yScale));
      
                detailSvg
                  .append("text")
                  .attr("x", 250)
                  .attr("y", -20)
                  .attr("text-anchor", "middle")
                  .style("font-size", "16px")
                  .text(`${member} - Sommeil du mois de ${month} (${year})`);
      
                detailSvg
                  .append("text")
                  .attr("x", 250)
                  .attr("y", 340)
                  .attr("text-anchor", "middle")
                  .style("font-size", "12px")
                  .text("Jours du mois");
      
                detailSvg
                  .append("text")
                  .attr("transform", "rotate(-90)")
                  .attr("x", -200)
                  .attr("y", -40)
                  .attr("text-anchor", "middle")
                  .style("font-size", "12px")
                  .text("Heures de sommeil moyen");
      
                detailSvg
                  .selectAll(".bar")
                  .data(dailyData)
                  .enter()
                  .append("rect")
                  .attr("x", (d) => xScale(d.day))
                  .attr("y", (d) => (d.value < 0 ? yScale(d3.max(dailyData, (d) => d.value)) : yScale(d.value)))
                  .attr("width", xScale.bandwidth())
                  .attr("height", (d) =>
                    d.value < 0 ? 300 - yScale(d3.max(dailyData, (d) => d.value)) : 300 - yScale(d.value)
                  )
                  .attr("fill", (d) => (d.value < 0 ? "lightgrey" : memberColor));
              }
            }
      
            updateVisualization(2024);
          });
      }