Playing - Trafic des stations du métro de Paris

Trafic entrant dans chaque station du métro parisien (Leaflet & D3JS)
d3js3.x leaflet0.7.3
Sources :

Les données

La principale difficultée a été de récupérer les données et surtout de les transformer pour qu'elles soient faciles à manipuler. Ces données ont toutes été récupérées sur le site data.ratp.fr et en voici la liste :

Les lignes de métro ont été réalisé à la main. A partir de toutes ces données, nous avons rassemblé l'ensemble des informations en deux fichiers CSV. Le premier contient la liste des stations avec son nom, sa ville, son identifiant, sa position géographique, sa couleur, son trafic entrant et ses correspondances. Le second contient la liste des lignes avec pour chacune d'elle, son numéro, sa couleur et la liste ordonnée des identifiants de ses stations.

A l'aide d'un programme informatique, nous avons converti ces deux fichiers en fichiers geoJSON afin qu'ils soient facilement manipulables. C'est un point important car plus les données sont structurées et reconnues par la librairie D3JS plus le code pour les mettre en place est facile et concis. Ces deux fichiers contiennent une FeatureCollection.

Voici un extrait du fichier stations.json. Le premier élément présenté ici correspond à la station dont le trafic entrant est le plus important. Le fichier est trié dans le sens du plus gros trafic vers le plus petit, ainsi les plus gros cercles (chaque station est représentée par un cercle dont le diamètre correspond au trafic entrant) seront en-dessous des plus petits. Le type géométrique du format geoJSON utilisé est Point. Les autres propriétés permettent de renseigner la fenêtre d'information affichée en haut à droite.

{
 "type":"Feature",
 "properties":{
	"ID":"1842",
	"STATION":"GARE DU NORD",
	"CITY":"Paris",
	"QUARTER":"10",
	"TRAFIC":"49977513",
	"LINES":"4-5",
	"COLORS":"#BB4D98-#DE8B53"
 },
 "geometry":{
	"type":"Point",
	"coordinates":[
	   2.35470307836603,
	   48.8799654432891
	]
 }
}

L'autre fichier lines.json contient la liste des lignes ordonnée par numéro. Le type géométrique du format geoJSON est ici LineString qui contient les coordonnées ordonnées de chaque station de la ligne.

{
"type": "Feature",
"properties": {
	"LINE": "3bis",
	"COLOR": "#9A9940"
},
"geometry": {
	"type": "LineString",
	"coordinates": [
			[2.40638582344912, 48.8770699912197],
			[2.40485763553065, 48.871951099492],
			[2.40135199013901, 48.868519874537],
			[2.39846223866212, 48.8648347782704]
	]
}
}

A partir de ces deux fichiers, il faut à peine une quinzaine de ligne pour générer le plan du métro parisien. Mais comme nous souhaitons afficher celui-ci en superposition de la carte de Paris, le code est plus complexe car il faut gérer les zooms et déplacements que l'utilisateur peut réaliser.

Création de la carte et projection

Notre choix s'est porté sur les Tiles Stamen et la librairie Leaflet qui présentent l'avantage d'être en noir et blanc avec des variations très faibles. Le plan du métro ressort alors très bien au-dessus de la carte.

var stamen = new L.StamenTileLayer("toner-lite");

var myLayer = L.tileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png', {
	attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors, CC-BY-SA',
	subdomains: 'abcd',
	minZoom: 0,
	maxZoom: 20
});

var map = new L.Map("map", {
	center: new L.LatLng(48.853, 2.333),
	zoom: 13,
	layers: [stamen, myLayer],
});

Nous ajoutons le SVG à l'overlayPane de la map et créons les deux groupes pour nos lignes et nos stations. La classe leaflet-zoom-hide permet de rendre plus élégant le zoom en faisant disparaitre les groupes le temps qu'ils soient projettés à nouveau sur la carte.

var svg = d3.select(map.getPanes().overlayPane).append("svg");
var linesGroup = svg.append("g").attr("class", "leaflet-zoom-hide");
var stationsGroup = svg.append("g").attr("class", "leaflet-zoom-hide");

Contrairement aux tutoriaux de création de cartes visibles dans la section D3JS, nous n'utilisons pas une projection standard mais précisons nous-même la façon dont elle fonctionne. Pour cela nous définissons la fonction de projection projectPoint (située à la fin du code de la page plein écran) qui converti une latitude longitude en un point exprimé en pixel. C'est la librairie Leaflet qui assure cette projection.

var transform = d3.geo.transform({point: projectPoint});
var path = d3.geo.path().projection(transform);

function projectPoint(x, y) {
	var point = map.latLngToLayerPoint(new L.LatLng(y, x));
	this.stream.point(point.x, point.y);
}

Chargement des fichiers geoJSON

Il est possible de charger plusieurs fichiers geoJSON pour qu'ils soient tous mis en mémoire au moment voulu. Pour cela, il faut utiliser la fonction queue de D3JS qui attend le chargement de fichiers passés en paramètre de la fonction defer avant d'exécuter la fonction ready.

queue()
	.defer(d3.json, "stations.json")
	.defer(d3.json, "lines.json")
	.await(ready);

Le traitement des fichiers

Dans cette partie sont présentés quelques éléments important du traitement des données, pour le reste n'hésitez pas à voir le code source ou à poser des questions en bas de cette page.

Style fill & opacity des stations

L'opacité et la couleur de remplissage des cercles de chaque station utilisent la propriété COLORS du fichier stations.json. Cette propriété possède une liste de couleurs séparées par des tirets. S'il n'y a qu'une couleur alors nous remplissons le cercle avec cette couleur et lui associons une opacité de 1, sinon le cercle est gris et légèrement transparent.

.style("fill", function(d) { return (d.properties.COLORS.indexOf('-') > 0 ? "#B8B8B8" : d.properties.COLORS); })
.style("opacity", function(d) { return (d.properties.COLORS.indexOf('-') > 0 ? "0.7" : "1"); })

z-index des stations

Le z-index est fonction du trafic entrant de la station, ainsi les cercles les plus gros se retrouvent en-dessous de plus petits.

.style("z-index", function(d){ return Math.floor(50 - (d.properties.TRAFIC / 1000000)); })

mouseover & mouseout sur les stations

Chaque station du fichier geoJSON contient la liste des lignes et les couleurs correspondantes (les deux propriétés sont ordonnées de la même façon). Lorsque nous construisons les lignes nous leur associons l'id suivant : 'l' + d.properties.LINE. Ainsi lorsque l'utilisateur passe la souris sur une station nous somme capable de sélectionner toutes les lignes de cette station pour les colorées en noir. De la même façon nous pouvons lorsque l'utilisateur sors de la station remettre la couleur d'origine. Le code est assez simple car le fichier geoJSON a été pensé dans ce but.

.on("mouseover", function(d) {
		// Remplissage du DIV d'information
			  
		var lines = d.properties.LINES.split("-");
		for (i = 0; i < lines.length; ++i) {
			d3.selectAll("#l" + lines[i]).style("stroke", "black");
		}
	})
.on("mouseout", function(d) {
	var lines = d.properties.LINES.split("-");
	var colors = d.properties.COLORS.split("-");
	for (i = 0; i < lines.length; ++i) {
		d3.selectAll("#l" + lines[i]).style("stroke", colors[i]);
	}
}
Quelques améliorations resteraient à effectuer :
  • La superposition de certains tronçons de lignes pose problème lors de la sélection, ceux-ci ne sont pas visible. Une idée serait de changer le z-index en fonction de la sélection
  • Le SVG représente un rectangle dont les coins s'arrêtent précisément à la position des stations permettant de dessiner un rectangle les contenant toutes. Comme une station est représentée par un cercle qui possède un certain rayon, une partie de ce cercle n'est pas visible pour ces stations car il est en dehors de la taille calculé du SVG (voir par exemple la station Créteil-Pointe du Lac)

En réponse au commentaire de Bilouette

Bilouette, qui a posté un commentaire, voudrait pouvoir mettre certaines lignes du métro en pointillé, ce qui peut-être utile pour une ligne dont la construction n'est pas encore achevée. Comme il l'indique il faut effectivement utiliser la propriété stroke-dasharray et c'est très simple à mettre en oeuvre :

featureLine = linesGroup.selectAll("lines")
	.data(lines.features)
	.enter()
	.append("path")
	.attr('class', 'line')
	.attr('stroke-dasharray', function(d) { return (d.properties.LINE === '12' ? "5,5" : "0,0"); })
	.attr('id', function(d) { return 'l' + d.properties.LINE; })
	.attr("d", path)
	.style("stroke", function(d) { return d.properties.COLOR; });

Il suffit donc d'ajouter cet attribut et de faire en sorte qu'il dépende de nos données. Ici nous avons simplement mis des pointillés si le numéro de la ligne est 12 mais on peut modifier le fichier JSON pour ajouter une nouvelle propriété. Notez qu'en utilisant "0,0" comme valeur cela revient à une ligne sans pointillé. Si une partie seulement de la ligne de métro doit être en pointillé le plus simple est surement de créer deux lignes différentes et de trouver un moyen pour que la sélection de la ligne inclue les deux.

Le résultat est visible ici : Lignes pointillées.

COMMENTAIRES

disqus_QsMIlucvQa


Bonjour,
J'ai exactement fait ce que vous mettez dans le tutoriel.
Je
peux voir la carte mais les lignes ne marchent pas. On ne voit pas les
lignes et les couleurs. Dans la console s'est écrit "ReferenceError:
stationsGroup is not defined" je ne comprends pas du tout pourquoi.
Merci beaucoup pour votre aide.

ericfrigot


Bonjour,
stationsGroup est créé au début du traitement javascript, il s'agit d'un groupe qui doit contenir la liste des stations de métro sous forme de cercle. Avez-vous bien étudié le code source après avoir cliqué sur l'image en début de tutoriel ? Dans ce tutoriel tout le code javascript n'est pas présenté, il faut en partie étudié le code source. En tout cas votre erreur indique que la variable stationsGroup n'existe pas et vous pouvez très facilement le vérifier en faisant une recherche dans votre fichier source. Par ailleurs si vous voulez que je regarde avec vous il faudrait que vous déposiez vos source sur un site d'hébergement sur Internet afin que je puisse y avoir accès. Eric

f6


Bonjour,
Merci beaucoup pour ce superbe exemple !
J'ai vu dans les commentaires que certaines personnes parlaient d'ajouter à la carte les RER et Trams. Ca m'interesse beaucoup ! Serait-il possible de partager ça ?
Merci d'avance
Francis

ericfrigot


Bonjour Francis,
Merci pour les compliments. Concernant le fichier avec les lignes RER et Trams, je vous invite à poser directement la question à Adam BILLE, juste en-dessous. Pour ma part j'avais construit ce fichier pour les lignes du métro avec un petit programme à partir des données de la RATP (voir sur le site data.ratp.fr).
Eric

Adam BILLE


Bonjour,

Permettez-moi de vous féliciter pour votre site internet. J'ai trouvé cette carte si intéressante que j'ai ajouté sur ma carte les RER, les métros et les tramways. Cependant, je souhaiterais pouvoir choisir lesquels afficher sur ma carte. Par exemple, trois checkbox : une pour les RER, une pour les metros et pour les tram.

Il faut savoir que pour mon projet, j'ai désactivé la fonction qui montre l'affluence des stations. Je n'ai gardé que les lignes sur votre fond stamen.

Est-ce réalisable? Qu'en pensez-vous?

Je vous remercie d'avance pour votre éventuel retour.

Cordialement,

A.B

ericfrigot


Bonjour et merci pour votre compliment. Je vous propose d'intégrer directement votre problématique sur ce site. Si vous pouvez me faire parvenir vos sources j'ajouterais un tutoriel dédié à la variation des sources de données. Pouvez-vous me mettre votre code avec tous les fichiers à disposition sur internet ? Eric

Adam BILLE


Bonjour Eric, comment vous transmettre le code et les autres fichiers? Avez-vous une adresse mail? Il faut savoir que je travaille en local pour l'instant.

Concernant le code, je me suis très grandement inspiré du vôtre en supprimant les lignes de code correspondantes au trafic.
J'ai seulement alimenté le fichier json avec les autres lignes. Mon objectif est d'avoir la possibilité d'afficher les lignes que je souhaite voir non plus par type de transport (contrairement à ce que j'ai dit précédemment). Au final : une checkbox pour chaque ligne existante (16 lignes de métro, 5 RER et 9 tram). Bien évidemment, pour des raisons pratiques, un exemple simple suffirait et à nous de nous inspirer en réalisant ce qui nous intéresse.

Sachez que je suis très content de votre aide et vous remercie infiniment pour votre intérêt.

ericfrigot


Bonjour,
Vous pouvez m'envoyer vos fichiers à l'adresse suivante : eric.frigot arobase gmail.com. Je trouve votre problématique intéressante et j'en ferais un tutoriel à part. Si je comprends bien vous voulez afficher soit les lignes de métro soit les lignes RER soit les lignes du tram. Eric

Adam BILLE


Bonjour,

Je vous remercie. Je vous envoie cela dès que j'ai un petit moment : Je suis toujours en train d'ajouter des lignes.
Oui c'est exactement cela mis à part que "soit les lignes de métro soit les lignes RER soit les lignes du tram" peut prêter à confusion. L'objectif est d'afficher les lignes que l'on veut. Par exemple, je veux pouvoir afficher la ligne 5 du métro, la ligne A du RER et le T3B en cochant simplement leurs cases.
A ce propos, pour gagner du temps comment avez-vous fait pour créer votre fichier json des stations?. Je possède toutes ces informations dans un fichier excel que j'ai créé mais trouve ça long de le faire à la main : Avez-vous utiliser un logiciel? Un site internet?

Je vous remercie infiniment pour votre aide très généreuse. Votre site et votre aide sont vraiment géniales.

ericfrigot


Bonjour, dans ce cas je devrais être en mesure de vous faire un tutoriel sur le sujet sans avoir besoin de vos fichiers. Pour créer mes fichiers JSON des stations j'ai simplement écrit un programme en JAVA qui parcourt un CSV ou n'importe quel format et écrit dans un fichier en respectant le format geoJSON. Mais il reste une part de travail manuel. Eric

Adam BILLE


Bonjour, je vous remercie pour votre infinie gentillesse. Pour ma part j'ai trouvé une solution pour "non programmeur" : Elle consiste à concaténer sous excel les différents morceaux d'une info type json. Puis je copie ces informations sur un vérificateur de fichier json sur internet, et il met en forme automatiquement les informations.
Par ailleurs, je trouve que ce tutoriel sur la variation de données sera super utile à beaucoup de gens. Vous n'avez même pas besoin de le réaliser sur toutes les lignes, il suffira de le faire sur une ligne et nous nous occuperons de faire le reste.

Merci encore!

ericfrigot


Bonjour, je travaille sur ce tutoriel en ce moment : http://www.datavis.fr/playi.... Il permet de sélectionner les markers à afficher avec des checkbox. Vous pouvez regarder le code source, le code qui vous intéresse commence par var command = L.control({position: 'topright'}); et continue sur environ 30 lignes. N'hésitez pas si ce n'est pas clair.

ericfrigot


Et la version dédiée à ce tutoriel : http://datavis.fr/playing/m.... Il ne vous reste plus qu'à lister les lignes pour faire autant de checkbox, vous pourrez ensuite les sélectionner avec d3.select("#l" + d.properties.LINE).

Adam BILLE


Bonjour Eric,

Je me permets de revenir vers vous après m'avoir donné le temps de trouver la solution seul mais je n'arrive pas à placer correctement la commande d3.select("#l" + d.properties.LINE).

Voici où je pensais le placer :

this._div.innerHTML = '<h4>Informations</h4>'

+ '<form><input id="displayTraffic" type="checkbox" checked="">A</form>'
+ '<form><input id="displayTraffic" type="checkbox" checked="">B</form>'
+ '<form><input id="displayTraffic" type="checkbox" checked="">C</form>'
+ '<form><input id="displayTraffic" type="checkbox" checked="">D</form>'
+ '<form><input id="displayTraffic" type="checkbox" checked="">E</form>';

document.getElementById("displayTraffic").addEventListener("click", handleTrafficDisplay, false);
};

info.addTo(map);

document.getElementById("displayTraffic").addEventListener("click", handleTrafficDisplay, false);

function handleTrafficDisplay() {
if (this.checked) {
d3.select("#l" + d.properties.LINE).style("display", null);
} else {
d3.select("#l" + d.properties.LINE).style("display", "none");
}
}

Le problème dans mon code est qu'il n'y a pas de comparaison entre la ligne de la écrite à côté de la checkbox et la commande d3.select. Je ne sais pas comment réaliser cette comparaison : Par exemple, si la checkbox correspond à "A", afficher la ligne "A" dans le fichier lines.json.

Je vous remercie énormément pour votre éventuelle aide sur ce dernier point. Merci encore pour votre tuto sur la variation de donnée.

ericfrigot


Bonjour, en fait il faut que vos id soient différents sur les input. Par exemple id="displayTrafficA"... et ensuite vous faite d3.select('#displayTraffic' + d.properties.LINE). Pour faciliter la mise en oeuvre vous pouvez itérer sur la liste des lignes avec un simple for et ajouter votre input et le listener associé.

Adam BILLE


Waw, c'est vraiment trop bien. C'est exactement ce que je recherchais. Un énorme merci à vous et grand bravo pour votre excellent travail. Je suis actuellement en train d'étudier votre code afin que cela convienne à ce que je veux faire.

Je vous avoue que je n'est pas encore réussi à comprendre comment ajouter mes différentes checkbox et les commander mais je préfère comprendre puis vous poser mes questions.

Encore bravo ET merci pour ces tutos formidables qui aideront bien des gens dans leurs projets.

bilouette


Bonjour Eric,

Je me permets de vous féliciter pour votre site internet. J'ai ajouté les lignes de transilien et les projets du grand Paris.

Cependant, je souhaiterais avoir la possibilité de mettre certaines lignes en pointillés. Je pense que la commande "stroke-dasharray" serait utile mais comment choisir une ligne en particulier à mettre en pointillés?

De plus, est-il possible de remplacer les petits "ronds" représentant les stations de métro par le symbole M du métro (https://upload.wikimedia.or....

Je vous remercie d'avance pour votre éventuel retour. Je trouve votre site réellement fait et m'a permis de connaitre le javascript.

Merci encore!

ericfrigot


Bonjour et merci pour vos compliments, ça fait toujours plaisir.
J'ai mis à jour cette page pour vous répondre. Autant les pointillés c'est facile autant pour l'inclusion d'une image c'est bien plus difficile car on ne peut pas mettre d'image au niveau d'un path de manière correcte. Je travaille encore dessus mais je pense qu'il faut remplacer les path représentant les stations par des circles et il sera alors facile d'y ajouter une image. Je reviens vers vous dès que j'ai quelque chose de propre.
Eric

bilouette


Bonjour Eric,

Je vous remercie infiniment pour votre aide. Je ne m'attendais pas à avoir un paragraphe pour mon problème :-), je suis très honoré et je vous en remercie.
J'ai préféré suivre la deuxième méthode, celle qui consiste en ajoutant la propriété dans le fichier .json, elle donne de meilleurs résultats.
Concernant les images, je ne veux surtout pas prendre trop de votre temps si cela est compliqué. Je pensais qu'il suffisait de rajouter l'image grâce aux coordonnées de la station (je pense que vous signifiez peut être cela en parlant du path avec projection).

Mais, surtout ne vous embêtez pas avec ma question si elle nécessite du temps.

En tout cas, je vous remercie énormément pour votre aide. Cette carte est une vraie source d'inspiration et est unique dans son genre. Dommage qu'elle ne fonctionne pas sous internet explorer.

Merci encore et bravo!

Jiji


Bonjour,

Je me permets de vous poser cette question car je souhaite diminuer l'épaisseur des lignes mais je ne trouve pas comment.
J'ai tenté d'ajouter la ligne suivante
".style("stroke-width","3");" comme propriétés de featureLine = linesGroup.selectAll("lines").

Est-ce la bonne solution ? Sinon comment faire?

Merci énormément d'avance.

ericfrigot


Bonjour Jiji, en fait il faut modifier le style CSS qui se trouve au début du fichier fullscreen :
.line {
stroke-width: 5;
fill: none;
z-index: 1;
}

Il vous suffit de mettre 3 à la place de 5 et ça marchera. C'est vrai qu'avec D3JS on peut définir le style directement en Javascript mais aussi au niveau CSS directement.
Eric

Jiji


Bonjour Eric,

Je souhaiterais savoir s'il était possible d’insérer une image comme un logo avec d3 sur la carte openstreetmap. Je pensais l'ajouter dans le style css de la page mais je ne vois pas trop comment. Le but est que le logo reste toujours à la même taille.

Je vous remercie d'avance pour votre retour. Votre site est vraiment très utile.

ericfrigot


Bonjour Jiji,
D3 ne sera pas utile pour insérer un logo, il suffit d'ajouter l'HTML suivant :
<div id="rightbox" class="box right-box"><img src="logo.jpg"></img></div>
Et le CSS suivant :
.right-box {
position: absolute;
right: 15px;
top: 15px;
}
Le résultat est visible ici : http://www.datavis.fr/playi...

Jiji


Je vous remercie énormément. Mon problème venait du fait que j'ai mal placé ma balise div. En revanche, je trouve que vous êtes bien sympathique de nous aider. C'est super de votre part.

Merci beaucoup.

Jiji


Bonsoir Eric,

Cela marche parfaitement. Merci beaucoup. Votre site est une vraie source d'inspiration pour mes projets personnels.

Merci encore!

Anna


Bonjour,

Je souhaite faire à peu près la même chose dans mon projet, cependant je n'arrive pas à avoir la même chose que vous en utilisant vos données. Est-il possible d'avoir le code complet car la il me dit qu'il y a des erreurs au niveau du script.

Merci d'avance

ericfrigot


Bonjour,
Je ne suis pas sûr mais vous avez bien cliqué sur l'image pour voir le résultat en plein écran ? A partir de là il vous suffit de faire un clic droit pour récupérer tout le code HTML + JS et si vous avez téléchargé les deux fichiers JSON ça devrait fonctionner.