Skip to content
Snippets Groups Projects
sleepActivityVisual.js 7.73 KiB
Newer Older
=AZIZI Anis's avatar
=AZIZI Anis committed
export function renderSleepActivityVisualization() {
    fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
      .then((response) => response.json())
      .then((data) => {
        const svg = d3
          .select("#sleep-activity-visualization")
          .append("svg")
          .attr("width", 700)
          .attr("height", 300);
  
        const margin = { top: 20, right: 150, bottom: 50, left: 50 };
        const width = +svg.attr("width") - margin.left - margin.right;
        const height = +svg.attr("height") - margin.top - margin.bottom;
  
        const g = svg
          .append("g")
          .attr("transform", `translate(${margin.left},${margin.top})`);
  
        const tooltip = d3
          .select("body")
          .append("div")
          .attr("class", "tooltip-sleepactivity");
  
        const colorMap = {
          Maya: "#0f7e06",
          Corentin: "#1d38e3",
          Anis: "#d6bff4",
          Amira: "#7e09bd",
        };
  
        const getISOWeekNumber = (date) => {
          const tempDate = new Date(date);
          tempDate.setHours(0, 0, 0, 0);
          tempDate.setDate(tempDate.getDate() + 4 - (tempDate.getDay() || 7));
          const yearStart = new Date(tempDate.getFullYear(), 0, 1);
          return Math.ceil(((tempDate - yearStart) / 86400000 + 1) / 7);
        };
  
        const filteredData = data.filter((d) => {
          const date = new Date(d.date);
          return date >= new Date("2023-10-01") && date <= new Date("2024-12-31");
        });
  
        const groupedData = d3.group(filteredData, (d) => {
          const date = new Date(d.date);
          const weekNumber = getISOWeekNumber(date);
          return `${date.getFullYear()}-W${weekNumber}`;
        });
  
        const processedData = Array.from(groupedData, ([week, records]) => {
          return records
            .map((d) =>
              [
                {
                  name: "Anis",
                  steps: d.Steps_Anis,
                  sleep: d.Sleep_Anis,
                  calories: d.Calories_Anis,
                },
                {
                  name: "Maya",
                  steps: d.Steps_Maya,
                  sleep: d.Sleep_Maya,
                  calories: d.Calories_Maya,
                },
                {
                  name: "Corentin",
                  steps: d.Steps_Corentin,
                  sleep: d.Sleep_Corentin,
                  calories: d.Calories_Corentin,
                },
                {
                  name: "Amira",
                  steps: d.Steps_Amira,
                  sleep: d.Sleep_Amira,
                  calories: d.Calories_Amira,
                },
              ].filter((d) => d.steps > 0 && d.sleep > 0)
            )
            .flat();
        });
  
        const x = d3
          .scaleLinear()
          .domain([0, Math.ceil(d3.max(processedData.flat(), (d) => d.steps))])
          .range([0, width]);
  
        const y = d3.scaleLinear().domain([0, 18]).range([height, 0]);
  
        const radius = d3
          .scaleSqrt()
          .domain([0, Math.ceil(d3.max(processedData.flat(), (d) => d.calories))])
          .range([3, 15]);
  
        g.append("g")
          .attr("transform", `translate(0,${height})`)
          .call(d3.axisBottom(x).ticks(10))
          .append("text")
          .attr("fill", "black")
          .attr("x", width / 2)
          .attr("y", 40)
          .attr("text-anchor", "middle")
          .text("Nombres de pas");
  
        g.append("g")
          .call(d3.axisLeft(y))
          .append("text")
          .attr("fill", "black")
          .attr("transform", "rotate(-90)")
          .attr("x", -height / 2)
          .attr("y", -40)
          .attr("text-anchor", "middle")
          .text("Nombres d'heurs de sommeil");
  
        const legend = svg
          .append("g")
          .attr("transform", `translate(${width + 20}, 50)`);
  
        legend
          .selectAll("rect")
          .data(Object.keys(colorMap))
          .enter()
          .append("rect")
          .attr("x", 0)
          .attr("y", (d, i) => i * 20)
          .attr("width", 15)
          .attr("height", 15)
          .attr("fill", (d) => colorMap[d]);
  
        legend
          .selectAll("text")
          .data(Object.keys(colorMap))
          .enter()
          .append("text")
          .attr("x", 20)
          .attr("y", (d, i) => i * 20 + 12)
          .text((d) => d);
        // Ajout de la légende pour la taille des bulles
  
        legend
          .append("text")
          .attr("x", 0)
          .attr("y", Object.keys(colorMap).length * 20 + 50)
          .text("calories brûlées");
  
        legend
          .append("circle")
          .attr("cx", 3)
          .attr("cy", Object.keys(colorMap).length * 20 + 70)
          .attr("r", radius(10)) // Taille de la bulle pour 10 calories (ajustez si nécessaire)
          .attr("fill", "#ccc")
          .attr("opacity", 0.7);
  
        legend
          .append("text")
          .attr("x", 30)
          .attr("y", Object.keys(colorMap).length * 20 + 75)
          .text("3 calories");
  
        legend
          .append("circle")
          .attr("cx", 10)
          .attr("cy", Object.keys(colorMap).length * 20 + 100)
          .attr("r", radius(100)) // Taille de la bulle pour 100 calories
          .attr("fill", "#ccc")
          .attr("opacity", 0.7);
  
        legend
          .append("text")
          .attr("x", 30)
          .attr("y", Object.keys(colorMap).length * 20 + 105)
          .text("600 calories");
  
        const slider = document.getElementById("date-slider");
        const playButton = document.getElementById("play-button");
        let playing = false;
        let interval;
  
        slider.max = processedData.length - 1;
  
        const update = (index) => {
          const currentData = processedData[index];
          const weekLabel = Array.from(groupedData.keys())[index];
  
          document.getElementById("date-label").textContent = weekLabel;
  
          g.selectAll("circle").remove();
  
          g.selectAll("circle")
            .data(currentData)
            .enter()
            .append("circle")
            .attr("cx", (d) => x(d.steps))
            .attr("cy", (d) => y(d.sleep))
            .attr("r", (d) => radius(d.calories))
            .attr("fill", (d) => colorMap[d.name])
            .attr("opacity", 0.7)
            .on("mouseover", (event, d) => {
              tooltip
                .style("visibility", "visible")
                .text(
                  `${d.name}: Pas: ${d.steps}, Sommeil: ${d.sleep}, Calories: ${d.calories}`
                );
            })
            .on("mousemove", (event) => {
              tooltip
                .style("top", `${event.pageY - 10}px`)
                .style("left", `${event.pageX + 10}px`);
            })
            .on("mouseout", () => {
              tooltip.style("visibility", "hidden");
            });
        };
  
        playButton.addEventListener("click", () => {
          if (!playing) {
            playing = true;
            playButton.textContent = "Pause";
            let index = 0;
            interval = setInterval(() => {
              if (index >= processedData.length) {
                clearInterval(interval);
                playButton.textContent = "Play";
                playing = false;
              } else {
                slider.value = index;
                update(index);
                index++;
              }
            }, 800);
          } else {
            clearInterval(interval);
            playButton.textContent = "Play";
            playing = false;
          }
        });
  
        slider.addEventListener("input", (event) => update(+event.target.value));
  
        update(0);
      })
      .catch((error) => console.error("Error loading data:", error));
  }