Introduction
Fin 2019, un virus est apparu en Chine. Début 2020 il s'est très vite propagé sur la planète pour devenir une pandémie. Dans ce tutoriel nous détaillerons plusieurs des éléments qui nous ont permis de
construire ce dashboard sur le Coronavirus (ou COVID-19). Nous avons utilisé Leaflet pour faire la carte et D3JS pour les différents graphiques ainsi que pour manipuler les données. Celles-ci proviennent
de Github (John Hopkins CSSE) et sont mises à jour quotidiennement. Voici
les différents sujets traités dans ce tutoriel :
- Utilisation d'un générateur de grille responsive
- Scrollbar spécifique pour diminuer sa largeur et la fondre dans le style
- Solution pour le problème habituel des CORS
- Utilisation de flyTo de Leaflet pour se déplacer sur la carte
- Création de fenêtres (dialog) qu'on peut déplacer et fermer
- Un choix de couleurs responsable quand on parle d'être humain
Bien sûr la section commentaire est là si vous voulez discuter d'autres aspects de ce dashboard.
Génération d'une grille responsive
Pour générer une grille facilement et sans importer d'autres librairies, nous avons utilisé le site https://vue-grid-generator.netlify.com/.
Ce site permet très facilement de produire une grille adaptée (choix du nombre de colonnes, de lignes et définition précise de leurs tailles) et de récupérer le CSS associé. Pour notre dashboard voici le HTML complet
d'origine.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<style type="text/css">
html {
background-color: #222;
color: #bdbdbd;
}
.container {
display: grid;
width: 100%;
height: 100%;
grid-template-areas: "header header header header"
"map confirmed-table death-table recovered-table"
"map confirmed-chart death-chart recovered-chart"
"credit confirmed-chart death-chart recovered-chart";
grid-template-columns: 3fr 1fr 1fr 1fr;
grid-template-rows: 80px 4fr 1fr 1fr;
}
.container > div {
background-color: #333;
border: 1px solid #363636;
margin: 5px;
}
.header { grid-area: header; }
.map { grid-area: map; }
.confirmed-table { grid-area: confirmed-table; }
.death-table { grid-area: death-table; }
.recovered-table { grid-area: recovered-table; }
.confirmed-chart { grid-area: confirmed-chart; }
.death-chart { grid-area: death-chart; }
.recovered-chart { grid-area: recovered-chart; }
.credit { grid-area: credit; }
</style>
</head>
<body>
<div class="container">
<div class="header">header</div>
<div class="map">map</div>
<div class="confirmed-table">confirmed-table</div>
<div class="death-table" id="death-div">death-table</div>
<div class="recovered-table">recovered-table</div>
<div class="confirmed-chart">confirmed-chart</div>
<div class="death-chart">death-chart</div>
<div class="recovered-chart">recovered-chart</div>
<div class="credit">credit</div>
</div>
</body>
</html>
Ces quelques lignes de CSS permettent d'obtenir une grille avec des blocs de tailles différentes, un padding entre les blocs et sur les côtés. L'ensemble s'adapte (au chargement) à la taille de l'écran.
Paramétrage des scrollbars
Poursuivons sur la partie CSS pour présenter la mise en oeuvre d'une scrollbar sur les différentes tables (cas confirmés, décès et cas guéris). Nous voulions à la fois réduire la largeur des scrollbars et les fondre
dans le reste du dashboard. Il nous a suffi de définir le CSS suivant et d'utiliser la classe scroll
sur les différents tableaux.
.scroll {
overflow-y: auto;
}
.scroll::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #ccc;
}
.scroll::-webkit-scrollbar {
width: 6px;
background-color: #ccc;
}
.scroll::-webkit-scrollbar-thumb {
background-color: #222;
}
Néanmoins, ce n'est pas suffisant. Une scrollbar apparait uniquement si la hauteur du composant ne permet pas d'afficher tout son contenu. Pour cela il faut que cette hauteur soit fixe. Comme nous voulons conserver
le côté dynamique du dashboard nous avons été obligés de fixer la hauteur après chargement de la page avec le code javascript suivant qui permet de figer la hauteur des éléments possédants une scrollbar à la hauteur qu'ils possèdent
avant qu'on leur ajoute le contenu. On diminue également leur taille de 50px pour laisser un petit peu plus de place aux courbes en dessous.
d3.selectAll(".table-info")
.style("height", (document.getElementById("death-div").offsetHeight - 50) + "px");
Cross-origin resource sharing (CORS)
On rencontre fréquemment une erreur CORS policy lorsqu'on essaie de charger depuis une page des ressources qui proviennent d'une autre adresse que celle du serveur qui fournit la page. Pour palier à ce problème et charger
nos fichiers CSV directement depuis Github nous avons utilisé le site https://cors-anywhere.herokuapp.com. Il suffit de préfixer nos URLs avec ce site pour
que le problème disparaisse. Cela fonctionne car le site fournit les bons en-têtes pour que les requêtes soient acceptées.
var promises = [];
promises.push(d3.csv('https://cors-anywhere.herokuapp.com/https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv'));
promises.push(d3.csv('https://cors-anywhere.herokuapp.com/https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Deaths.csv'));
promises.push(d3.csv('https://cors-anywhere.herokuapp.com/https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Recovered.csv'));
Promise.all(promises).then(function(values) {
// Traitement des données
});
Leaflet flyTo
Lorsque l'on clique sur un pays dans l'un des trois tableaux du dashboard, la carte se positionne automatiquement aux coordonnées de ce pays. Nous réalisons cette animation en utilisant la fonction flyTo
de Leaflet.
Dans notre cas on vide le svg qui contient tous les cercles bleus car leurs positions va changer. On en profite également pour afficher les informations sur le pays sélectionné.
tr.on("click", function(d) {
var d = dataByCountry[this.rowIndex];
d3.select("#map-svg").remove();
map.flyTo([d.lat, d.lng]);
info.update(getConfirmedFromLatLng(d.lat, d.lng));
});
Création des fenêtres (dialog)
Pour réaliser les fenêtres qui s'affichent quand on clique sur les cercles bleus nous avons utilisé les trois tutoriels suivants de w3schools :
L'ensemble produit pas mal de code et nous avons utilisé D3JS pour l'insertion des éléments HTML de manière dynamique mais le dashboard fonctionne sans autre librairie. Pour que plusieurs fenêtres fonctionnent en même
temps il faut s'assurer de disposer d'
id
uniques. A cette fin nous utilisons la latitude et la longitude du pays auxquelles on supprime les signes négatifs et les points.
let idDialog = "dialog" + countryData["Lat"].replace("-", "").replace(".", "") + countryData["Long"].replace("-", "").replace(".", "");
Le choix des couleurs
Lorsque nous avons publié la première version de ce dashboard sur Twitter nous utilisions le rouge pour représenter les cas confirmés. C'est un choix qui peut sembler naturel mais cette couleur est aussi anxiogène, le rouge est la
couleur du sang et du danger. Quand on présente des informations de santé sur les êtres humains il est préférable d'utiliser une couleur plus neutre, c'est pour cette raison que nous avons changé pour utiliser du bleu.
COMMENTAIRES
Spring Time
le source code complet svp
ericfrigot
Bonjour, il vous suffit d'aller sur la page https://www.datavis.fr/play... et de faire clic droit avec la souris "Voir le code source de la page" (exemple sous Chrome). Attention il y a pas de javascript.
Spring Time
Merci beaucoup.
User
Bonjour, j'ai essayé de reproduire votre modèle en suivant toutes les consignes, mais j'obtiens l'erreur suivant : Uncaught TypeError: Cannot read property 'offsetHeight' of null. Pourriez-vous m'aider à éclaircir ce mystère ?
ericfrigot
Bonjour, effectivement il y avait une erreur dans le code présenté sur la page, il manquait la déclaration de l'id "death-div", je l'ai ajouté dans la partie "Génération d'une grille responsive". Par sécurité et si vous voulez aller plus loin n'hésitez pas à ouvrir la page du dashboard et à regarder le code source.