Playing - Tremblements de terre depuis 1900

Animation et transition (D3JS & jquery)
d3js3.x jquery3.2.1
Sources :

Introduction

Cette démonstration, visible dans un nouvel onglet en cliquant sur l'image, représente tous les tremblements de terre depuis 1900 de magnitude supérieure à 6, soit environ 7500 tremblements de terre (ce qui explique le temps de chargement de la démonstration). Quelques informations :
  • Les boutons de contrôle à gauche permettent de lancer l'animation, de l'accélérer ou de la ralentir, de la mettre en pause ou de la recommencer.
  • Par défaut la vitesse de l'animation est de 150ms par mois. Elle peut être accélérée jusqu'à 0ms par mois. Dans ce cas c'est la capacité de l'ordinateur qui limite la vitesse de l'animation.
  • Chaque tremblement de terre apparaît comme un cercle coloré qui grossit jusqu'à un diamètre dépendant de sa magnitude. Cette animation dure 2 secondes.
  • Lorsque le tremblement de terre est de magnitude supérieure à 8, un texte contenant son emplacement apparaît pendant 10 secondes.
  • L'Antarctique, qui a vécu des tremblements de terre, n'est pas visible sur cette démonstration.
Elle met en oeuvre les mécanismes suivants :
  • Construction de la carte du monde en fonction de la résolution de l'écran.
  • Animation pour chaque tremblement de terre en fonction de sa magnitude (fonction transition avec D3JS).
  • Gestion de la vitesse de défilement des tremblements de terre (fonction setInterval en javascript).

Les données

Mise à part la carte, les données proviennent du site http://earthquake.usgs.gov. Il permet d'accéder à une page de recherche entièrement paramétrable. Pour collecter les données nécessaires à cette démonstration, il suffit de sélectionner le format de sortie geoJSON qui sera directement reconnu par D3JS. Le fichier utilisé pour notre exemple est from1901to2020.json.

Mise à échelle automatique de la carte

Dans cette démonstration, la carte s'affiche toujours en entier et centrée quelque soit la résolution. Pour cela, nous commençons par créer la projection et le path avec un scale de 1 et une translation à 0. Ces valeurs sont importantes car si elles ne sont pas utilisées le calcul qui va suivre risque d'être faussé.

var projection = d3.geo.mercator()
	.scale(1)
	.translate([0, 0]);

var path = d3.geo.path()
	.pointRadius(2)
	.projection(projection);

Dans la fonction drawWorldMap (voir code source), nous commençons par calculer les bounds de notre carte, puis nous en déduisons le scale et la translation en fonction de la largeur et de la hauteur souhaitée. Notez que le scale n'est que de 85% de la taille obtenue afin de laisser de la place pour les tremblements de terre sur les bords. Il suffit ensuite d'appliquer ces transformations à notre projection et d'ajouter chaque pays à un groupe précédemment créé.

function drawWorldMap() {
	var b  = path.bounds(countries),
		s = .85 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
		t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];

	projection
		.scale(s)
		.translate(t);
	
	cGroup.selectAll("path")
		.data(countries.features)
		.enter()
		.append("path")
		.attr("d", path);
}

Animation

L'animation de cette carte est gérée par l'objet monthAnimation. Il définit deux fonctions internes : start et stop. 5 autres fonctions sont définies sur l'objet lui-même, elles correspondent aux 5 boutons permettant de jouer ou de varier l'animation. La fonction start vérifie qu'une animation n'est pas déjà en cours (ligne 2) puis exécute la fonction javascript setInterval qui va tourner indéfiniment toutes les interval ms. Le contenu de cette fonction peut être visualisé dans le code source. Lorsque le dernier mois est atteint la fonction stop est appelée. Celle-ci arrête l'animation si elle existe (ligne 16) en utilisant la fonction javascript clearInterval.

var start = function() {
	if (timerId === null) {
		timerId = setInterval(function() {
			if (iMonth > monthInterval.length - 1) {
				anim.stop();
			}
			// Calcul du mois courant
			// Appel de la fonction transition pour jouer les tremblements de terre du mois courant
			// Mise à jour de la barre du graphique à gauche et du compteur de tremblements de terre
			iMonth++;
		}, interval);
	}
};

var stop = function() {
	if (timerId !== null) {
		clearInterval(timerId);
		timerId = null;
	}
};

Les 2 fonctions faster et lower font varier la variable interval. A chaque fois l'animation doit être arrêtée puis redémarrée pour prendre en compte ce nouvel interval. Les 3 autres permettent de démarrer, mettre en pause et redémarrer l'animation. Toutes sont assez simples à comprendre via le code source.

Transitions

Nos données en provenance de l'USGS ont été regroupées par mois. Nous avons donc une variable qui contient pour chaque mois la liste des tremblements qui ont eu lieu (voir l'initialisation de la variable earthquakeByMonth). L'objet réalisant l'animation appelle chaque mois la fonction transition en lui fournissant la liste des tremblements de terre correspondant. Celle-ci créé un cercle de rayon 1px dont la couleur correspond à la magnitude (voir fonction color). Il est positionné correctement sur la carte grâce à la fonction projection. Après création, nous débutons une transition de 2 secondes qui vise à modifier le rayon pour qu'il atteigne la valeur r (qui dépend encore une fois de la magnitude). Ensuite, le cercle est enlevé de la carte (remove). Notez que la transition concernant le texte et le trait pour les tremblements de terre de magnitude supérieure à 8 peut être consultée directement le code source.

function transition(earthquakeMonth) {
	for (var i = 0; i < earthquakeMonth.length; i++) {	
		var r = radius(earthquakeMonth[i].properties.mag);
		var p = projection([earthquakeMonth[i].geometry.coordinates[0], earthquakeMonth[i].geometry.coordinates[1]]);
		var circle = eqGroup.append("circle")
			.attr("r",1)
			.attr("id", "s" + earthquakeMonth[i].properties.id)
			.style("fill", color(earthquakeMonth[i].properties.mag))
			.style("opacity", 0.7)
			.style("z-index", Math.floor(10 - earthquakeMonth[i].properties.mag))
			.attr("transform", "translate(" + p + ")");
		
		circle.transition()
			.duration(2000)
			.attr("r", r)
			.remove();
		
		// Gestion des tremblements de terre de magnitude supérieure à 8
	}
}

Pour cette démonstration, le détail technique n'est pas complet. Il nous a néanmoins semblé suffisant si l'on se réfère également aux autres tutoriaux du site dans lesquels la plupart des concepts non détaillés ici sont présents. Evidemment, nous répondrons avec plaisir à toutes vos questions car ça n'a pas été facile pour nous aussi de faire fonctionner ce code. Par ailleurs, toute critique ou proposition d'amélioration est la bienvenue.

COMMENTAIRES