Playing - Dashboard sur le Coronavirus

Construire rapidement un dashboard, utiliser la fonctionnalité flyTo de Leaflet, résoudre les problèmes CORS, le tout sans utiliser d'autres libraries
d3js6.x
Sources :

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.