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