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

Dernière mise à jour le 21/03/2020
d3js5.x leaflet1.6.0

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 :

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">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));
});

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.

comments powered by Disqus