<html lang="fr">

<head>
  <meta charset="utf-8">
  <title>Exemple de graphique et tableau</title>
  <meta name="description" content="Exemple de graphique et tableau">
  <link href="style.css" rel="stylesheet">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>

<body>
  <div class="container">
    <div class="row title">
      <header class="masthead mb-auto">
        <div class="inner">
          <h1 class="masthead-brand" id="title"></h1>
        </div>
      </header>
    </div>
    <div class="row graph-and-table">
      <div class="reps0 col-sm-12">
        <div id="reps0">
        </div>
      </div>
      <div class="table-probs col-sm-offset-0 col-sm-12">
        <table id="table-probs" class="probs" media="print">
          <caption id="table-title">Prévisions sur une période de 6 heures</caption>
          <caption id="table-footer">*Veuillez noter que ces données proviennent de modèles météorologiques non expertisées.</caption>
          <thead>
            <tr>
              <th>Débutant le</th>
              <th>≥0.2 mm</th>
              <th>≥1.0 mm</th>
              <th>≥2.5 mm</th>
              <th>≥5.0 mm</th>
              <th>Temp.</th>
              <th>Vents</th>
            </tr>
          </thead>
          <tbody>
          </tbody>
        </table>
      </div>
    </div>
    <div class="col-sm-offset-0 col-sm-3">
      <div class="class-sm-offset-1 col-sm-12">
        <p></p>
      </div>
      <div class="class-sm-offset-1 col-sm-12">
        <p></p>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
  <script src="https://cdn.plot.ly/plotly-2.0.0.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
  <script src="main.js"></script>
</body>

</html>
html,
 body {
   height: 100%;
   background-color: rgb(255, 255, 255);
 }
 h1 {
   text-align: center;
 }
 body {
   display: -ms-flexbox;
   display: -webkit-box;
   display: flex;
   -ms-flex-pack: center;
   -webkit-box-pack: center;
   justify-content: center;
   color: #fff;
   text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5);
   -webkit-print-color-adjust: exact;
 }
 #reps0 {
   display: flex;
 }
 #table-title {
   padding-top: .75rem;
   padding-bottom: .75rem;
   color: #6c757d;
   text-align: center;
   caption-side: top;
 }
 .probs {
   border: 1px solid #C0C0C0;
   border-collapse: collapse;
   padding: 5px;
   margin: auto;
 }
 .probs th {
   border: 1px solid #C0C0C0;
   padding: 5px;
   background: #F0F0F0;
   background: #797979;
   color: white;
   text-align: center;
 }
 .probs td {
   border: 1px solid #C0C0C0;
   text-align: center;
   padding: 5px;
 }
  #nan {
    background-color: #797979;
  }
  h1#title.masthead-brand {
    text-align: center;
  }
  div.row.title {
    margin: auto;
    display: inline;
  }

.low1 {
   background-color: #d8f3dc;
   color: black;
 }
 
 .low2 {
   background-color: #B7E4C7;
   color: black;
 }
 
 .low3 {
   background-color: #95D5B2;
   color: black;
 }
 
 .medium1 {
   background-color: #74C69D;
   color: black;
 }
 
 .medium2 {
   background-color: #52B788;
   color: black;
 }
 
 .medium3 {
   background-color: #40916C;
   color: white;
 }
 
.high1 {
   background-color: #2D6A4F;
   color: white;
 }
 
.high2 {
   background-color: #245741;
   color: white;
 }
 
 .high3 {
   background-color: #1B4332;
   color: white;
 }
.high4 {
  background-color: #081C15;
  color: white;
}
const parser = new DOMParser();
const [coordX, coordY] = [45.472, -73.750]; //Aéroport de Montréal Pierre-Elliott Trudeau International (CYUL)
const layer02 = 'REPS.DIAG.6_PRMM.ERGE0.2'; //Probabilités de précipitations >= 0.2mm
const layer1 = 'REPS.DIAG.6_PRMM.ERGE1'; //Probabilités de précipitations >= 1mm
const layer25 = 'REPS.DIAG.6_PRMM.ERGE2.5'; //Probabilités de précipitations >= 2.5mm
const layer5 = 'REPS.DIAG.6_PRMM.ERGE5'; //Probabilités de précipitations >= 5mm
const layerTemp = 'RDPS.ETA_TT'; //Température
const layerWindDir = 'RDPS.ETA_WD'; //Direction du vent
const layerWindSp = 'RDPS.ETA_WSPD'; //Module du vent
const server = 'https://geo.weather.gc.ca/geomet?service=WMS&version='; //Serveur de GeoMet du SMC
const getCapab = server + '1.3.0&request=GetCapabilities&layers='; //Lien vers le GetCapabilities
let [version, request, info_format] = ['1.3.0', 'GetFeatureInfo', 'application/json']; //Version, request and format for the information
let [minx, miny, maxx, maxy] = [coordX - 0.25, coordY - 0.25, coordX + 0.25, coordY + 0.25]; //bbox around CYVP airport
const colorArray = ["low0","low1","low2","low3","medium1","medium2","medium3","high1","high2","high3","high4"];
getCapabilities().then(function(response) {
  let startTime = response[0]; //Initialise la date de début
  let endTime = response[1]; //Initialise la date de fin
    let interval = response[2].match(/\d+/)[0]; //RegEx pour avoir le nombre d'heures d'intervalles: Par exemple: PT6H -> 6
    startDate = new Date(startTime); //Objet Date pour la date de début
    endDate = new Date(endTime); //Objet Date pour la date de fin
    var dif = (Math.abs(endDate - startDate) / 36e5) / interval; //Différence entre la date de début et celle de fin selon l'intervalle
    let headers = [];
    headers.push(interval, startDate, dif); //Pousse les informations nécéssaires pour la préparation des requêtes
    headers = prepareRequests(headers, server); //Prépare les requêtes. Headers[0] contient les dates et le reste du tableau, les URLs
    let begin = startTime.split('T')[0]; //Titre de la page
    document.getElementById("title").innerHTML = "Pr&eacute;visions CYUL du " + begin; //Titre du DOM
    let adjustedUTC = adjustUTC(headers[0]); //Ajuste les heures en fonctions du fuseau horaire de l'utilisateur
    let promises = []; //Tableau pour les Promises
    /*
     * La boucle for envoie de manière asynchrone toutes les requêtes contenues dans "headers"
     * Comme nous avons besoin du résultat de toutes les requêtes avant de tracer le graphique et remplir le tableau,
     * l'utilisation du Promise.all est élémentaire
     */
    for (let i = 1; i < headers.length; ++i) {
      promises.push(sendRequests(headers, i)); //Pousse dans la tableau des Promise, la promesse du résultat de la requête
    }
    Promise.all(promises)
      .then((results) => {
        traceGraph(adjustedUTC, results); //Trace the graph
        createTable(adjustedUTC, results); //Populate table
      }).catch(err => { //Catch failed fetch
        console.log(err);
      });
  }).catch(err => {
    console.log(err);
  });

async function getCapabilities() {
  let response = await fetch(getCapab + layer02);
  let data = await response.text().then((data) =>
    parser
      .parseFromString(data, "text/xml")
      .getElementsByTagName("Dimension")[0]
      .innerHTML.split("/")
  );
  return [data[0], data[1], data[2]];
}
function prepareRequests(headers, server) {
  const interval = headers[0];
  let startDate = headers[1];
  let dif = headers[2];
  let timesteps = [];
  timesteps.push(startDate.toISOString().replace('.000', '')); //Remove trailing zeroes for compatibility with time= parameter
  let increment = startDate;
  for (let i = 0; i < dif; ++i) {
    increment.setHours(increment.getHours() + Number(interval)); //Set the hour according to the intervals supported
    timesteps.push(increment.toISOString().replace('.000', '')); //Add to timesteps array
  }
  let url02 = server +
    version + '&request=' + request + '&layers=' + layer02 + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layer02 + '&x=1&y=1';
  let url1 = server +
    version + '&request=' + request + '&layers=' + layer1 + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layer1 + '&x=1&y=1';
  let url25 = server +
    version + '&request=' + request + '&layers=' + layer25 + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layer25 + '&x=1&y=1';
  let url5 = server +
    version + '&request=' + request + '&layers=' + layer5 + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layer5 + '&x=1&y=1';
  let urlTemp = server +
    version + '&request=' + request + '&layers=' + layerTemp + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layerTemp + '&x=1&y=1';
  let urlWd = server +
    version + '&request=' + request + '&layers=' + layerWindDir + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layerWindDir + '&x=1&y=1';
  let urlWspd = server +
    version + '&request=' + request + '&layers=' + layerWindSp + '&crs=EPSG:4326&bbox=' +
    minx + ',' + miny + ',' + maxx + ',' + maxy + '&exceptions=xml&width=10&height=10&INFO_FORMAT=' +
    info_format + '&query_layers=' + layerWindSp + '&x=1&y=1';
  let info = [];
  info.push(timesteps, url02, url1, url25, url5, urlTemp, urlWd, urlWspd);
  return info;
}
function adjustUTC(time) {
  let adjustedDate = [];
  let dateTime;
  for (let i = 0; i < time.length; ++i) {
    dateTime = new Date(time[i]);
    adjustedDate.push(dateTime);
  }
  return adjustedDate;
}
async function sendRequests(headers, nb) {
  let responses = [];
  let datesMod = [];
  let url = headers[nb].concat("&time=" + headers[0][0]);
  for (let k = 0; k < headers[0].length; ++k) {
    datesMod.push(headers[0][k]);
  }
  for (let i = 0; i < datesMod.length; ++i) {
    try {
      if (i != 0) {
        url = url.replace(/[^=]*$/.exec(url), datesMod[i]);
      }
      let response = await fetch(url);
      let data = await response.json();
      responses.push(data.features[0].properties.value);
    } catch (e) {
      console.log(e);
    }
  }
  return responses;
}
  function traceGraph(xaxis, yaxis) {
  let date = [];
  let tempRounded = [];
  let windKm = [];
  let lastDate = new Date(xaxis[xaxis.length-1]);
  lastDate.setHours(lastDate.getHours() + 6);
  lastDate = lastDate.toLocaleString('fr-CA');
  lastDate = lastDate.substring(0, lastDate.indexOf('m'));
  lastDate = lastDate.replace(' h ', 'h');

  for (let i = 0; i < xaxis.length; ++i) {
    date[i] = xaxis[i].toLocaleString('fr-CA');
    date[i] = date[i].substring(0, date[i].indexOf('m'));
    date[i] = date[i].replace(' h ', 'h');
  }
  date.push(lastDate);
  for (let j = 0; j < yaxis[4].length; ++j) {
    tempRounded[j] = Number(Math.round(yaxis[4][j]));
  }
  for (let k = 0; k < yaxis[6].length; ++k) {
    if (!isNaN(Math.round(yaxis[6][k]))) {
      windKm[k] = Number(Math.round(yaxis[6][k] * 3.6));
    }
  }
  for (let l = 0; l < yaxis.length; ++l) {
    yaxis[l].push(0);
  }
  var trace1 = {
    x: date,
    y: yaxis[0],
    mode: 'lines+markers',
    name: '≥ 0.5mm',
    line: {
      color: 'rgb(144, 224, 239)',
      width: 3,
      shape: 'hv'
    },
    fill: 'tozeroy',
  };
  var trace2 = {
    x: date,
    y: yaxis[1],
    mode: 'lines+markers',
    name: '≥ 1.0mm',
    line: {
      color: 'rgb(72, 202, 228)',
      width: 3,
      shape: 'hv'
    },
    fill: 'tozeroy',
  }
  var trace3 = {
    x: date,
    y: yaxis[2],
    mode: 'lines+markers',
    name: '≥ 2.5mm',
    line: {
      color: 'rgb(0, 150, 199)',
      width: 3,
      shape: 'hv'
    },
    fill: 'tozeroy',
  }
  var trace4 = {
    x: date,
    y: yaxis[3],
    mode: 'lines+markers',
    name: '≥ 5.0mm',
    line: {
      color: 'rgb(2, 62, 138)',
      width: 3,
      shape: 'hv'
    },
    fill: 'tozeroy',
  }
  var layout = {
    title: 'Probabilités de précipitations',
    automargin: true,
    showlegend: true,
    showlegend: true,
    legend: {
      "orientation": "h"
    },
    xaxis: {
      showticklabels: true,
      tickangle: 0,
      nticks: 3,
      tickfont: {
        family: 'Old Standard TT, serif',
        size: 12,
        color: 'black'
      },
    },
    yaxis: {
      title: 'Probabilités (%)',
      range: [0, 100]
    }
  };
  
var data = [trace1, trace2, trace3, trace4 /*trace5, trace6*/];
  Plotly.newPlot('reps0', data, layout);
}
function createTable(date, data) {
  for (let z = 0; z < data.length; ++z) {
    data[z].pop();
  }
  let table = document.querySelector("#table-probs");
  let row;
  for (let k = 0; k < date.length; ++k) {
    date[k] = date[k].toLocaleString('fr-CA');
    date[k] = date[k].substring(0, date[k].indexOf('m'));
    date[k] = date[k].replace(' h ', 'h');
  }
  for (let i = 0; i < date.length; ++i) {
    row = table.insertRow(-1);
    let cell1 = row.insertCell(0);
    let cell2 = row.insertCell(1);
    let cell3 = row.insertCell(-1);
    let cell4 = row.insertCell(-1);
    let cell5 = row.insertCell(-1);
    let cell6 = row.insertCell(-1);
    let cell7 = row.insertCell(-1);
    if (!isNaN(Math.round(data[4][i]))) {
      cell6.innerHTML = Math.round(data[4][i]) + " °C";
    } else {
      cell6.setAttribute("id", "nan");
    }
    if (!isNaN(Math.round(data[5][i])) && !isNaN(Math.round(data[6][i]))) {
      cell7.innerHTML = Math.round(data[6][i] * 3.6) + " km/h " + degToCompass(data[5][i]);
    } else {
      cell7.setAttribute("id", "nan");
    }
    cell1.innerHTML = date[i];
    cell2.innerHTML = data[0][i] + " %";
    cell3.innerHTML = data[1][i] + " %";
    cell4.innerHTML = data[2][i] + " %";
    cell5.innerHTML = data[3][i] + " %";
    for (let j = 0; j < 4; ++j) {
      switch(j) {
        case 0:
          let val = data[0][i] / 10;
          cell2.classList.add(colorArray[Math.round(val % 11)]);
          break;
        case 1:
          let val1 = data[1][i] / 10;
          cell3.classList.add(colorArray[Math.round(val1 % 11)]);
          break;
        case 2:
          let val2 = data[2][i] / 10;
          cell4.classList.add(colorArray[Math.round(val2 % 11)]);
          break;
        case 3:
          let val3 = data[3][i] / 10;
          cell5.classList.add(colorArray[Math.round(val3 % 11)]);
          break;
      }
    }
  }
}

function degToCompass(num) {
  let val = Number((num / 45));
  let arr = ["N", "NE", "E", "SE", "S", "SO", "O", "NO"];
  if (Math.round((val % 8)) == 8) {
    return "Variables";
  }
  return arr[Math.round((val % 8))];
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.