Skip to content
Snippets Groups Projects
main.js 39 KiB
Newer Older
  • Learn to ignore specific revisions
  • RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    /**
    * Template Name: Bootslander
    * Template URL: https://bootstrapmade.com/bootslander-free-bootstrap-landing-page-template/
    * Updated: Mar 17 2024 with Bootstrap v5.3.3
    * Author: BootstrapMade.com
    * License: https://bootstrapmade.com/license/
    */
    
    (function() {
      "use strict";
    
      /**
       * Easy selector helper function
       */
      const select = (el, all = false) => {
        el = el.trim()
        if (all) {
          return [...document.querySelectorAll(el)]
        } else {
          return document.querySelector(el)
        }
      }
    
      /**
       * Easy event listener function
       */
      const on = (type, el, listener, all = false) => {
        let selectEl = select(el, all)
        if (selectEl) {
          if (all) {
            selectEl.forEach(e => e.addEventListener(type, listener))
          } else {
            selectEl.addEventListener(type, listener)
          }
        }
      }
    
      /**
       * Easy on scroll event listener 
       */
      const onscroll = (el, listener) => {
        el.addEventListener('scroll', listener)
      }
    
      /**
       * Navbar links active state on scroll
       */
      let navbarlinks = select('#navbar .scrollto', true)
      const navbarlinksActive = () => {
        let position = window.scrollY + 200
        navbarlinks.forEach(navbarlink => {
          if (!navbarlink.hash) return
          let section = select(navbarlink.hash)
          if (!section) return
          if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
            navbarlink.classList.add('active')
          } else {
            navbarlink.classList.remove('active')
          }
        })
      }
      window.addEventListener('load', navbarlinksActive)
      onscroll(document, navbarlinksActive)
    
      /**
       * Scrolls to an element with header offset
       */
      const scrollto = (el) => {
        let header = select('#header')
        let offset = header.offsetHeight
    
        if (!header.classList.contains('header-scrolled')) {
          offset -= 20
        }
    
        let elementPos = select(el).offsetTop
        window.scrollTo({
          top: elementPos - offset,
          behavior: 'smooth'
        })
      }
    
      /**
       * Toggle .header-scrolled class to #header when page is scrolled
       */
      let selectHeader = select('#header')
      if (selectHeader) {
        const headerScrolled = () => {
          if (window.scrollY > 100) {
            selectHeader.classList.add('header-scrolled')
          } else {
            selectHeader.classList.remove('header-scrolled')
          }
        }
        window.addEventListener('load', headerScrolled)
        onscroll(document, headerScrolled)
      }
    
      /**
       * Back to top button
       */
      let backtotop = select('.back-to-top')
      if (backtotop) {
        const toggleBacktotop = () => {
          if (window.scrollY > 100) {
            backtotop.classList.add('active')
          } else {
            backtotop.classList.remove('active')
          }
        }
        window.addEventListener('load', toggleBacktotop)
        onscroll(document, toggleBacktotop)
      }
    
      /**
       * Mobile nav toggle
       */
      on('click', '.mobile-nav-toggle', function(e) {
        select('#navbar').classList.toggle('navbar-mobile')
        this.classList.toggle('bi-list')
        this.classList.toggle('bi-x')
      })
    
      /**
       * Mobile nav dropdowns activate
       */
      on('click', '.navbar .dropdown > a', function(e) {
        if (select('#navbar').classList.contains('navbar-mobile')) {
          e.preventDefault()
          this.nextElementSibling.classList.toggle('dropdown-active')
        }
      }, true)
    
      /**
       * Scrool with ofset on links with a class name .scrollto
       */
      on('click', '.scrollto', function(e) {
        if (select(this.hash)) {
          e.preventDefault()
    
          let navbar = select('#navbar')
          if (navbar.classList.contains('navbar-mobile')) {
            navbar.classList.remove('navbar-mobile')
            let navbarToggle = select('.mobile-nav-toggle')
            navbarToggle.classList.toggle('bi-list')
            navbarToggle.classList.toggle('bi-x')
          }
          scrollto(this.hash)
        }
      }, true)
    
      /**
       * Scroll with ofset on page load with hash links in the url
       */
      window.addEventListener('load', () => {
        if (window.location.hash) {
          if (select(window.location.hash)) {
            scrollto(window.location.hash)
          }
        }
      });
    
      /**
       * Preloader
       */
      let preloader = select('#preloader');
      if (preloader) {
        window.addEventListener('load', () => {
          preloader.remove()
        });
      }
    
      /**
       * Initiate glightbox
       */
      const glightbox = GLightbox({
        selector: '.glightbox'
      });
    
      /**
       * Initiate gallery lightbox 
       */
      const galleryLightbox = GLightbox({
        selector: '.gallery-lightbox'
      });
    
      /**
       * Testimonials slider
       */
      new Swiper('.testimonials-slider', {
        speed: 600,
        loop: true,
        autoplay: {
          delay: 5000,
          disableOnInteraction: false
        },
        slidesPerView: 'auto',
        pagination: {
          el: '.swiper-pagination',
          type: 'bullets',
          clickable: true
        }
      });
    
      /**
       * Animation on scroll
       */
      window.addEventListener('load', () => {
        AOS.init({
          duration: 1000,
          easing: 'ease-in-out',
          once: true,
          mirror: false
        })
      });
    
      /**
       * Initiate Pure Counter 
       */
      new PureCounter();
    
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    })();
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    // VISU 1
    
    function renderStepsVisualization() {
      fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter 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 members = ["Corentin", "Maya", "Anis", "Amira"];
    
              data.forEach(d => d.date = parseDate(d.date));
    
              const margin = { top: 50, right: 230, bottom: 150, left: 70 };
              const width = 800 - margin.left - margin.right;
              const height = 500 - margin.top - margin.bottom;
    
              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})`);
    
              const years = [2021, 2022, 2023, 2024];
              const sliderContainer = d3.select("#slider-container");
    
              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)]);
    
              updateVisualization(2024);
    
              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);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                      });
    
                      return aggregated;
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                  });
    
                  svg.selectAll("*").remove();
    
    
                  const xScale = d3.scaleBand()
                      .domain(aggregatedData.map(d => d.month))
                      .range([0, width])
                      .padding(0.2);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
                  const yScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Steps_${member}`])))]).nice()
                      .range([height, 0]);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
                  const colorMap = {
                      "Maya": "#0f7e06",
                      "Corentin": "#1d38e3",
                      "Anis": "#d6bff4",
                      "Amira": "#7e09bd"
                  };
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
                  svg.append("g")
    
                      .attr("transform", `translate(0, ${height})`)
                      .call(d3.axisBottom(xScale))
                      .selectAll("text")
                      .attr("transform", "rotate(-45)")
                      .style("text-anchor", "end");
    
                  svg.append("g").call(d3.axisLeft(yScale));
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", height + 70)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Mois");
    
                  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");
    
                  members.forEach((member) => {
                      const lineData = aggregatedData.map(d => {
                          let steps = d[`Steps_${member}`] === -1.0 ? 0 : d[`Steps_${member}`];
                          return { month: d.month, steps };
    
                      const line = d3.line()
                          .x(d => xScale(d.month) + xScale.bandwidth() / 2)
                          .y(d => yScale(d.steps))
                          .defined(d => d.steps !== 0);
    
                      svg.append("path")
                          .data([lineData])
                          .attr("class", `line-${member}`)
                          .attr("d", line)
                          .attr("fill", "none")
                          .attr("stroke", colorMap[member])
                          .attr("stroke-width", 2);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                  });
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
              rangeSlider.on("input", function () {
                  const selectedYear = years[this.value];
                  yearDisplay.text(selectedYear);
                  updateVisualization(selectedYear);
              });
          });
    }
    
    // VISU 2
    function renderDistanceVisualization() {
      fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter 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 members = ["Corentin", "Maya", "Anis", "Amira"];
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
              data.forEach(d => d.date = parseDate(d.date));
    
              const margin = { top: 50, right: 230, bottom: 150, left: 70 };
              const width = 800 - margin.left - margin.right;
              const height = 500 - margin.top - margin.bottom;
    
              const svg = d3.select("#distance-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})`);
    
              const years = [2023, 2024];
              const sliderContainer = d3.select("#distance-slider-container");
    
              const rangeSlider = sliderContainer.append("input")
                  .attr("type", "range")
                  .attr("min", 0)
                  .attr("max", years.length - 1)
                  .attr("value", years.indexOf(2024))
                  .style("width", "60%");
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
              const yearDisplay = sliderContainer.append("span")
                  .text(years[years.indexOf(2024)]);
    
              const tooltip = d3.select("body").append("div")
                  .attr("class", "tooltip")
                  .style("opacity", 0);
    
              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[`Distance_${member}`] = d3.mean(records, d => {
                              const distance = d[`Distance_${member}`];
                              return (distance !== -1) ? distance : undefined;
                          }) || 0;
    
                          aggregated[`Calories_${member}`] = d3.mean(records, d => {
                              const calories = d[`Calories_${member}`];
                              return (calories !== -1) ? calories : undefined;
                          }) || 0;
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                      });
    
                      return aggregated;
    
                  svg.selectAll("*").remove();
    
                  const bubbleSizeScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Distance_${member}`])) )])
                      .range([2, 20]);
    
                  const xScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Distance_${member}`])) )])
                      .range([0, width]);
    
                  const yScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Calories_${member}`])) )])
                      .range([height, 0]);
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", margin.top - 60)
                      .attr("text-anchor", "middle")
                      .style("font-size", "16px")
                      .style("font-weight", "bold")
                      .text("Comparaison des calories brûlées vs distance parcourue");
    
                  svg.append("g")
                      .attr("transform", `translate(0, ${height})`)
                      .call(d3.axisBottom(xScale));
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", height + 70)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Distance (KM)");
    
                  svg.append("g").call(d3.axisLeft(yScale));
    
                  svg.append("text")
                      .attr("transform", "rotate(-90)")
                      .attr("x", -height / 2)
                      .attr("y", -50)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Calories brûlées");
    
                  members.forEach((member) => {
                      svg.selectAll(`.bubble-${member}`)
                          .data(aggregatedData)
                          .enter()
                          .append("circle")
                          .attr("cx", d => xScale(d[`Distance_${member}`]))
                          .attr("cy", d => yScale(d[`Calories_${member}`]))
                          .attr("r", d => bubbleSizeScale(d[`Distance_${member}`]))
                          .attr("fill", d => {
                              return (d[`Distance_${member}`] === 0 || d[`Calories_${member}`] === 0) ? "gray" : colorMap[member];
                          })
                          .style("opacity", 0.7)
                          .on("mouseover", function(event, d) {
                              tooltip.transition().duration(200).style("opacity", .9);
                              tooltip.html(`Mois : ${d.month}<br>Distance : ${d[`Distance_${member}`]} km<br>Calories : ${d[`Calories_${member}`]} cal`)
                                  .style("left", (event.pageX + 5) + "px")
                                  .style("top", (event.pageY - 28) + "px");
                          })
                          .on("mouseout", function() {
                              tooltip.transition().duration(500).style("opacity", 0);
                          });
    
              updateVisualization(years[1]);
    
              rangeSlider.on("input", function() {
                  const selectedYear = years[this.value];
                  yearDisplay.text(selectedYear);
                  updateVisualization(selectedYear);
              });
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
          });
    
    }
    
    //VISU 3
    function renderCaloriesVisualization() {
      fetch('../static/js/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));
    
          const margin = { top: 50, right: 230, bottom: 150, left: 70 };
          const width = 800 - margin.left - margin.right;
          const height = 500 - margin.top - margin.bottom;
    
          const svg = d3.select("#calories-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})`);
    
          const years = [2022, 2023, 2024];
          const sliderContainer = d3.select("#calories-slider-container");
    
          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)]);
    
          updateVisualization(2024);
    
          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[`Calories_${member}`] = d3.mean(records, d => d[`Calories_${member}`] || 0);
              });
              return aggregated;
            });
    
            svg.selectAll("*").remove();
    
            svg.append("text")
              .attr("x", width / 2)
              .attr("y", margin.top - 60)
              .attr("text-anchor", "middle")
              .style("font-size", "16px")
              .style("font-weight", "bold")
              .text("Analyse des calories brûlées 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[`Calories_${member}`])))]).nice()
              .range([height, 0]);
    
            const colorScale = d3.scaleOrdinal()
              .domain(members)
              .range(["#1d38e3", "#0f7e06", "#d6bff4", "#7e09bd"]);
    
            svg.append("g")
              .attr("transform", `translate(0, ${height})`)
              .call(d3.axisBottom(xScale))
              .selectAll("text")
              .attr("transform", "rotate(-45)")
              .style("text-anchor", "end");
    
            svg.append("text")
              .attr("x", width / 2)
              .attr("y", height + 70)
              .attr("text-anchor", "middle")
              .style("font-size", "14px")
              .text("Mois");
    
            svg.append("g").call(d3.axisLeft(yScale));
    
            svg.append("text")
              .attr("transform", "rotate(-90)")
              .attr("x", -height / 2)
              .attr("y", -50)
              .attr("text-anchor", "middle")
              .style("font-size", "14px")
              .text("Calories brûlées");
    
            members.forEach(member => {
              const areaData = aggregatedData.map(d => ({
                month: d.month,
                calories: d[`Calories_${member}`] || 0
              }));
    
              const area = d3.area()
                .x(d => xScale(d.month) + xScale.bandwidth() / 2)
                .y0(yScale(0))
                .y1(d => yScale(d.calories));
    
              svg.append("path")
                .data([areaData])
                .attr("fill", colorScale(member))
                .attr("opacity", 0.8)
                .attr("d", area);
            });
          }
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
          rangeSlider.on("input", function () {
            const selectedYear = years[this.value];
            yearDisplay.text(selectedYear);
            updateVisualization(selectedYear);
          });
        });
    }
    
    // Visu4
    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: 50, right: 230, bottom: 150, left: 70 };
              const width = 800 - margin.left - margin.right;
              const height = 500 - margin.top - margin.bottom;
    
              const svg = d3.select("#sleep-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})`);
    
              const years = [2021, 2022, 2023, 2024];
              const sliderContainer = d3.select("#sleep-slider-container");
    
              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)]);
    
              const tooltip = d3.select("body").append("div")
                  .attr("class", "tooltip")
                  .style("opacity", 0);
    
              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}`] || 0);
                      });
                      return aggregated;
                  });
    
                  svg.selectAll("*").remove();
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", margin.top - 60)
                      .attr("text-anchor", "middle")
                      .style("font-size", "16px")
                      .style("font-weight", "bold")
                      .text("Analyse des heures de sommeil 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[`Sleep_${member}`])))]).nice()
                      .range([height, 0]);
    
                  svg.append("g")
                      .attr("transform", `translate(0, ${height})`)
                      .call(d3.axisBottom(xScale))
                      .selectAll("text")
                      .attr("transform", "rotate(-45)")
                      .style("text-anchor", "end");
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", height + 70)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Mois");
    
                  svg.append("g").call(d3.axisLeft(yScale));
    
                  svg.append("text")
                      .attr("transform", "rotate(-90)")
                      .attr("x", -height / 2)
                      .attr("y", -50)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Sommeil moyen (heures)");
    
                  members.forEach((member, i) => {
                      svg.selectAll(`.bar-sleep-${member}`)
                          .data(aggregatedData)
                          .enter()
                          .append("rect")
                          .attr("x", d => xScale(d.month) + i * (xScale.bandwidth() / members.length))
                          .attr("y", d => yScale(d[`Sleep_${member}`]))
                          .attr("width", xScale.bandwidth() / members.length)
                          .attr("height", d => height - yScale(d[`Sleep_${member}`]))
                          .attr("fill", colorMap[member])
                          .on("mouseover", function (event, d) {
                              tooltip.transition().duration(200).style("opacity", 0.9);
                              tooltip.html(`Mois : ${d.month}<br>Sommeil : ${d[`Sleep_${member}`].toFixed(2)} heures`)
                                  .style("left", (event.pageX + 5) + "px")
                                  .style("top", (event.pageY - 28) + "px");
                          })
                          .on("mouseout", function () {
                              tooltip.transition().duration(500).style("opacity", 0);
                          });
                  });
              }
    
              updateVisualization(years[3]);
    
              rangeSlider.on("input", function () {
                  const selectedYear = years[this.value];
                  yearDisplay.text(selectedYear);
                  updateVisualization(selectedYear);
              });
          });
    }
    
    
    
    
    
    // Visu 5
    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")
            .style("position", "absolute")
            .style("visibility", "hidden")
            .style("background", "#fff")
            .style("border", "1px solid #ccc")
            .style("padding", "5px")
            .style("border-radius", "4px")
            .style("font-size", "12px");
    
          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("Steps");
    
          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("Sleep (hours)");
    
          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);
    
          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}: Steps: ${d.steps}, Sleep: ${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++;
                }
              }, 1000);
            } 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));
    }
    // Visu 6
    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;
    
          // Filter data
          const filteredData = data.filter((d) => {
            const date = new Date(d.date);
            return date >= new Date("2023-10-01") && date <= new Date("2024-12-31");
          });
    
          // Group data by ISO week
          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]) => {
            const aggregated = {
              week: week,
              year: week.split("-")[0], // Extract the year from the week
              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)),
              Distance_Amira: d3.sum(records, (d) => (d.Distance_Amira > 0 ? d.Distance_Amira : 0)),
              Sleep_Anis: d3.mean(records, (d) => (d.Sleep_Anis > 0 ? d.Sleep_Anis : 0)),
              Sleep_Maya: d3.mean(records, (d) => (d.Sleep_Maya > 0 ? d.Sleep_Maya : 0)),
              Sleep_Corentin: d3.mean(records, (d) => (d.Sleep_Corentin > 0 ? d.Sleep_Corentin : 0)),
              Sleep_Amira: d3.mean(records, (d) => (d.Sleep_Amira > 0 ? d.Sleep_Amira : 0)),
            };
            return aggregated;
          });
    
          const dropdown = document.getElementById("personDropdown");
          dropdown.addEventListener("change", () => updateChart(dropdown.value));
    
          // Initial chart rendering
          updateChart(dropdown.value);
    
          function updateChart(personKey) {
            const sleepKey = personKey.replace("Distance", "Sleep");
            d3.select("#chart").html(""); // Clear the previous chart
    
            const svg = d3.select("#chart")
              .append("svg")
              .attr("width", width)
              .attr("height", height)
              .attr("viewBox", [-width / 2, -height / 2, width, height])