Skip to content
Snippets Groups Projects
main.js 20.1 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);
          });
        });
    }
    document.addEventListener("DOMContentLoaded", function () {
      renderStepsVisualization();
      renderDistanceVisualization();
      renderCaloriesVisualization();
    });