Cette carte a été optimisé pour l'usage qui en est fait dans ce tutoriel, merci de consulter le tutoriel Map - Optimisation pour plus d'information.
Nous avons réalisé un nouveau tutoriel plus avancé pour les cartes choroplètes, il permet notamment de mieux gérer les couleurs : D3JS - Map Avancée. N'hésitez pas à le parcourir après avoir lu celui-ci.
Cet exemple est un complément du précédent et vise surtout à introduire les cartes choroplèthes. Ces cartes permettent la représentation d'un thème donnée (ici la densité de population française par département) grâce à des couleurs. D3JS facilite particulièrement la création de telles cartes. C'est aussi l'occasion de faire une carte plus propre avec un tooltip plus lisible, la possibilité de choisir un jeu de couleur qui a été travaillé et une échelle associée aux couleurs.
Le code est strictement identique à celui de l'exemple précédent pour la création de la carte (il faut juste
ajouter l'attribut class
avec pour valeur Blues
sur le SVG pour initier les couleurs
et passer la width
de 550 à 700 pour afficher la légende de l'échelle).
const width = 700, height = 550;
const path = d3.geoPath();
const projection = d3.geoConicConformal() // Lambert-93
.center([2.454071, 46.279229]) // Center on France
.scale(2600)
.translate([width / 2 - 50, height / 2]);
path.projection(projection);
const svg = d3.select('#map').append("svg")
.attr("id", "svg")
.attr("width", width)
.attr("height", height)
.attr("class", "Blues");
const deps = svg.append("g");
Cette carte présente la répartition de la population française. Elle est définit par département dans un fichier qui peut-être récupéré via firebug dans l'onglet réseau ou directement sur ce lien : population.csv.
Pour charger nos données (le JSON et le CSV) nous utilisons les promesses qui ont été introduites avec D3JS v5. Les promesses permettent de gérer facilement des requêtes asynchrones.
Ici elles nous permettent de synchroniser le chargement de nos deux fichiers. Le fichier CSV est parsé comme on avait précédement parsé du JSON mais ici on utilise la méthode d3.csv()
(notez que le séparateur utilisé doit être la virgule). La création des path ne varie quasiment pas, on ajoute seulement un id à chaque path, ce qui nous permettra de le retrouver
ultérieurement. Notez que l'id doit commencer par une lettre. Il est ici composé d'un 'd' concaténé avec le code du département que l'on trouve dans notre fichier geoJSON.
var promises = [];
promises.push(d3.json('/tutorials/d3js/map-population/departments.json'));
promises.push(d3.csv("/tutorials/d3js/map-population/population.csv"));
Promise.all(promises).then(function(values) {
const geojson = values[0]; // Récupération de la première promesse : le contenu du fichier JSON
const csv = values[1]; // Récupération de la deuxième promesse : le contenu du fichier csv
var features = deps
.selectAll("path")
.data(geojson.features)
.enter()
.append("path")
.attr('id', d => "d" + d.properties.CODE_DEPT)
.attr("d", path);
// Ici on insèrera tout le code qui va suivre dans cette partie.
Nous commençons par définir la fonction qui nous permettra d'obtenir la couleur de notre département. Pour cela, nous utilisons
une projection discontinue d3.scaleQuantile()
entre 0 et le max de notre population vers le range
de 1 à 9. Cela implique que nous aurons 9 variations de couleurs pour représenter la population de chaque département.
var quantile = d3.scaleQuantile()
.domain([0, d3.max(csv, e => +e.POP)])
.range(d3.range(9));
Comme nous l'avons vu dans le premier tutoriel, nous ajoutons ensuite un groupe qui contientra nos rectangle de différentes couleurs.
Ce groupe est décalé via la méthode translate
pour être positionné correctement.
var legend = svg.append('g')
.attr('transform', 'translate(525, 150)')
.attr('id', 'legend');
Nous ajoutons ensuite 9 rectangles aux dimensions de 20px par 20px et nous décalons chaque rectangle de 20px sur l'axe vertical.
La coloration se fait simplement avec la ligne qui ajoute l'attribut class
à chaque rectangle. Nous reviendrons sur sa
définition à la fin du chapitre, l'attribut CSS aura pour valeur q1-9, q2-9... .
legend.selectAll('.colorbar')
.data(d3.range(9))
.enter().append('svg:rect')
.attr('y', d => d * 20 + 'px')
.attr('height', '20px')
.attr('width', '20px')
.attr('x', '0px')
.attr("class", d => "q" + d + "-9")
Ici, nous attaquons une partie qui nous sera fort utile dans les chapitres suivant : la construction d'un axe gradué. Cela se fait
en deux étapes : la définition du domaine et la définition de l'axe. Le domaine utilisé (scale
) représente encore une
projection d'un nombre compris entre 0 et le max de notre population. Le range
de destination en revanche tient compte
de la hauteur de nos rectangle (20px) et de leur nombre (9) pour que l'échelle soit de la même taille que l'ensemble de nos rectangles.
var legendScale = d3.scaleLinear()
.domain([0, d3.max(csv, e => +e.POP)])
.range([0, 9 * 20]);
Nous définissons ensuite l'axe qui utilise le domaine précédement définit. L'axe est orienté sur la droite (et du coup automatiquement
vertical, les autres valeurs possible sont axisLeft
toujours vertical et axisBottom
ou axisTop
pour un axe horizontal). ticks
indique le nombre de tiret que nous souhaitons (D3JS fait au mieux). L'ensemble est décalé
pour se placer correctement à droite des rectangles.
var legendAxis = svg.append("g")
.attr('transform', 'translate(550, 150)')
.call(d3.axisRight(legendScale).ticks(6));
Pour chaque entrée de notre CSV, nous faisons comme pour la succession de rectangle et ajoutons l'attribut class
pour définir notre couleur. Celle-ci utilise la fonction quantile
que nous avons précédemment définie. Le code
gérant l'affichage du tooltip n'a pas beaucoup varié, par contre le CSS a été un peu amélioré pour plus de lisibilité.
csv.forEach(function(e,i) {
d3.select("#d" + e.CODE_DEPT)
.attr("class", d => "department q" + quantile(+e.POP) + "-9")
.on("mouseover", function(event, d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("<b>Région : </b>" + e.NOM_REGION + "<br>"
+ "<b>Département : </b>" + e.NOM_DEPT + "<br>"
+ "<b>Population : </b>" + e.POP + "<br>")
.style("left", (event.pageX + 30) + "px")
.style("top", (event.pageY - 30) + "px");
})
.on("mouseout", function(event, d) {
div.style("opacity", 0);
div.html("")
.style("left", "-500px")
.style("top", "-500px");
});
});
Finalement on rajoute un listener sur notre liste de choix de couleurs pour rafraichir l'ensemble.
d3.select("select").on("change", function() {
d3.selectAll("svg").attr("class", this.value);
});
Le jeu de couleur utilisé pour ce graphique est connu sous le nom de Color Brewer. Ici, c'est la version CSS qui est utilisée. Le selecteur permet de choisir une couleur associée à une value comme 'Blues' ensuite et comme nous l'avons vu nous ajoutons une classe à chaque département du type 'q4-9' ce qui nous construit la class '.Blues .q4-9' et signifie que ce département aura pour couleur la 4ième variation sur une échelle de 9 du bleu.
Vous trouverez ci-dessous le code complet CSS associé à cette visualisation.
#svg {
display: block;
margin: auto;
}
/** La souris change pour un curseur sur un département et le contour est noir avec une épaisseur assez fine */
.department {
cursor: pointer;
stroke-linecap: round;
stroke-linejoin: round;
stroke: black;
stroke-width: .5px;
}
/** Au survol d'un département le contour devient plus épais */
.department:hover {
stroke-width: 2px;
}
/** Le tooltip est défini avec des propriétés statiques qui conviennent à nos données */
div.tooltip {
position: absolute;
opacity:0.8;
z-index:1000;
text-align:left;
border-radius:4px;
-moz-border-radius:4px;
-webkit-border-radius:4px;
padding:8px;
color:#fff;
background-color:#000;
font: 12px sans-serif;
max-width: 300px;
height: 60px;
}
Suite à la remarque d'une internaute, nous rajoutons un lien vers le github suivant : https://github.com/gregoiredavid/france-geojson. Il permet de récupérer au format geoJSON les communes, départements et régions au niveau de la France entière ainsi que les communes et départements spécifiques à chaque région ou département.
Si votre fichier CSV possède plusieurs données, vous pouvez vouloir mettre à jour automatiquement la carte en choisissant la colonne à afficher. En supposant que vous disposez d'une liste de choix comme pour les couleurs dans notre exemple, le code nécessaire est assez court.
d3.select("#data").on("change", function() {
selectedData = this.value;
quantile = d3.scale.scaleQuantile
.domain([0, d3.max(csv, e => +e[selectedData])])
.range(d3.range(9));
legendScale.domain([0, d3.max(csv, e => +e[selectedData])]);
legendAxis.call(d3.axisRight(legendScale).ticks(6));
csv.forEach(function(e,i) {
d3.select("#d" + e.CODE_DEPT)
.attr("class", d => "department q" + quantile(+e[selectedData]) + "-9");
});
});
La variable selectedData
est déclarée juste avant la lecture du CSV et initialisé à 'POP', c'est-à-dire la colonne population
de notre fichier CSV. La 1ère ligne de ce code récupère la valeur sélectionnée par l'utilisateur, elle correspond à la colonne qu'il a choisi.
Ensuite nous mettons à jour notre projection pour tenir compte du nouvel intervalle de données. Le domaine de la légende est lui aussi recalculé
et un simple appel à la fonction call
permet de rafraichir les libellés. Enfin pour chaque entrée du CSV on met à jour nos départements.
Ce code doit être inclu dans la fonction qui charge le CSV.
VOUS POURRIEZ AIMER
COMMENTAIRES
PlG_BENIS
Merci pour ce travail!
PierreV
Bonjour,
Je reviens vers vous pour des questions d'optimisation de la taille des fichiers.
Je travaille sur le fichier des communes françaises qui est très volumineux à charger, j'ai donc voulu remplacer le format GEOJSON par du TOPOJSON.
La taille du fichier a été grandement réduite (divisée par 3 environ), seulement je connais mal ce nouveau format et je n'arrive pas à adapter votre code. Malgré des tutoriels sur internet je ne réussis pas à y connecter le json et les données.
Sauriez vous comment faire ?
Merci,
Pierre
ericfrigot
Bonjour, normalement ce n'est pas très différent. Est-ce que vous avez le moyen de me mettre à disposition votre fichier TOPOJSON ? Eric
PierreV
Bonjour,
Vous devriez y avoir accès par le lien suivant :
https://gist.github.com/Pie...
Merci, Pierre
Greg Hor
Bonjour, super tutoriel! J ai à nouveau beaucoup appris ;).
J'ai adapté la carte des populations à d'autres données départementales. J aimerai mettre cette carte sur mon blog (hébergé sur blogger) pour en faire profiter mes amis. Si j ai bien compris, il faut dans un premier temps que mes ressources (le .json des departements français ainsi que le .csv contenant les données) soient stockés dans une adresse accessible sur le web. J ai naïvement tenté de uploader le "data.csv" dans un repertoire github pour l appeler dans le script via d3.csv("https://github.com/.../data...") . Visiblement sans succès. Après quelques recherches sur Google, j ai l impression que le problème résulte d un conflit same-origin policy ,
qui m empêche d aller lire les données stockées dans une url extérieure.
Sachant que je n ai pas de serveur, y a t il un moyen simple d associer les données au script afin qu il soit fonctionnel en ligne? Dans une réponse postée dans le tuto précédent, vous aviez évoqué la possibilité d inclure les données .json directement dans le script via la déclaration d 'une variable. Est-il possible de faire la même chose avec des données .csv ? Cordialement, Grégoire
ericfrigot
Bonjour, c'est bien sûr possible. Voici ce que ça donne : http://www.datavis.fr/d3js/...
Pour information, j'ai construit l'objet de la façon suivante : Utilisation de Firefox avec Firebug, ajout d'un point d'arrêt juste après le chargement du fichier CSV, ajout d'un espion : JSON.stringify(csv). Ensuite il suffit de copier le contenu de la chaine de caractère et ça correspond à l'objet à déclarer. Eric
marooned
Bonjour, je suis débutant sur JS et j'essaie de suivre ton tuto, j'avoue c'est bien explicite, mais je n'utilise pas de server et je voulais avoir la version dans la quelle les fichiers .json et .CSV sont stockés dans des variables. tu as mis le lien : https://www.datavis.fr/d3js..., mais il ne marche pas, est-il possible de récupérer le code source?
marooned
Super, je viens de réussir à faire le tuto d'après. le tout avec Angular 7. Merci pour votre coup de main et pour la qualité de vos tutos.
ps : j'ai transformé le fichier csv en json sur le net.
ericfrigot
Bonjour, le lien est de nouveau actif, j'ai dû recréer le fichier car je l'avais supprimé chez moi. Pour information tout est dans le fichier, même la gestion de la couleur qui utilise un tableau de couleurs contrairement à cette page. Pour information je me suis appuyé sur un autre tutoriel pour gérer la couleur: https://www.datavis.fr/inde...
Greg Hor
Bonjour Eric, cela fonctionne très bien, merci beaucoup pour le tuyau! Grégoire
PierreV
Bonjour, je vous remercie pour votre tutoriel très détaillé qui m'a bien aidé pour réaliser ma première carte interactive avec d3.
Je souhaiterais compléter le résultat en ajoutant la possibilité avec une liste déroulante (comme pour le choix des couleurs) d'afficher différents jeux de données autres que la population. J'ai rajouté un second menu déroulant ainsi que des colonnes au csv mais je n'arrive pas à faire le lien entre les deux et à afficher différentes données. Auriez-vous des conseils pour réaliser cela ? Merci.
ericfrigot
Bonjour, je viens de mettre à jour le tutoriel avec une partie traitant de ce problème. En espérant que cela puisse vous être utile.
PierreV
Merci beaucoup, c'est exactement ce que je recherchais.
En y ajoutant l'outil de zoom de votre exemple à l'échelle communale, j'obtiens un résultat vraiment très complet.
Jiji
Bonjour, y-a t-il une limitation dans le nombre de "parties" colorées? En effet, j'ai réalisé ce tuto avec l'ensemble des communes d'Ile de France soit 1281 communes et "seules" environ 200 communes ont été colorées.
Merci d'avance pour votre retour.
ericfrigot
Bonjour, il n'y a d'autre limitation que celle de votre navigateur (sa capacité à traiter l'information). J'ai réalisé la même carte pour toutes les communes de France, ce qui fait plus de 36500 polygones (attention, c'est assez lourd, 11Mo à télécharger et un gros traitement pour le navigateur) : http://www.datavis.fr/index..., vous pouvez même zoomer avec votre molette. Votre problème peut avoir plusieurs origines, soit vos couleurs sont trop faibles et apparaissent presque blanches (raison du tutoriel sur les prix Nobel) soit il vous manque des données pour les associer à chaque commune. Si votre code est visible en ligne, vous pouvez me donner le lien, je jetterais un œil.
Jiji
Bonjour, je comprends tout à fait. Ce n'est donc pas une "limitation" qui empêchent la colorisation de mes communes car votre carte "lourde" s'affiche sans problèmes. Malheureusement, je travaille uniquement en local pour le moment.
Personnellement, je pense que c'est mon fichier csv que j'ai créé sur excel qui en est la cause. En effet, seules les 237 premières communes sont colorisées.
Merci pour votre disponibilité et votre réponse.
Sylia
Est-il possible d'avoir le découpage par région et non pas par département?
ericfrigot
Bonjour, je viens de rajouter un paragraphe sur les autres données geoJSON que l'on peut trouver sur Internet pour la France.
Sylia
Merci de votre réponse!
Ina
Bonjour,
j'ai une question j'ai suivit le tuto du début à la fin mais ça marche pas et j'ai pas d'erreur non plus. J'ai été même récuperer le fichier json mais ça marche. Est ce que je dois ajouter autre chose qui n'est pas dit ici. Je vous remercie de votre aide
ericfrigot
Bonjour. Pas évident pour moi de savoir ce qui ne marche pas dans votre code. Du coup, j'ai repris ce tutoriel et le précédent pour voir si j'avais oublié quelque chose. Le résultat est visible sur cette page : http://www.datavis.fr/d3js/... (je me suis contenté de reprendre les différentes portions de code et de récupérer le CSS de cette page). Vous pouvez récupérer tout le code source et avec ça vous aurez votre exemple qui fonctionne. Par ailleurs, si vous me donnez le lien de votre site ou un lien pour télécharger vos sources je pourrais vous dire ce qui ne fonctionne pas dans votre code. Au plaisir de vous recroisez par ici.
Ina
je vous remercie de votre votre réponse, c'était bien le problème de CSS, mais par contre j'arrive pas à changer la couleur j'ai téléchargé le fichier colorbrewer mais la couleur ne change pas, je travaille en local je ne sais pas trop comment je vais faire pour que vous puissiez voir mon code . Je travaille sur un projet actuellement donc je veux me référer sur votre exemple pour le faire. j'ai vu que vous afficher la population, j'aimerai faire pareil sauf que pour moi c'est sur une certaine nombre d'année(5 ans) pouvez vous me donnez une idée pour cette partie. Je vous remercie d'avance
ericfrigot
Bonjour. Je vous répond un peu tardivement mais si vous avez toujours des soucis pour colorer votre carte, je vous invite à zipper votre travail dans un fichier que vous déposerez sur un service d'hébergement en ligne, ainsi vous pourrez me fournir le lien. Par ailleurs pour représenter plusieurs années vous pouvez jetez un oeil à cet exemple : http://bl.ocks.org/darrenja.... La partie importante du code est celle qui commence par d3.selectAll("input").on("change", function change(). A partir de cette ligne on récupère l'année sélectionnée et on change la colonne de notre fichier pour remplir la couleur de chaque path (chaque département pour nous). Par rapport à ce tutoriel, li faut redéfinir la class et non le path. Si j'ai du temps je mettrais à jour l'exemple que j'avais réalisé pour vous.
Guest
Bonjour, ce tuto est vraiment bien fait merci ! Serait-il possible d'avoir la structure du geoJson associé ? (d3js/map-population/departments.json)
ericfrigot
Bonjour et merci pour ce commentaire.
Les spécifications complètes du GeoJSON sont détaillées à cette URL : http://geojson.org/geojson-...
Seule la partie properties varie et ici elle contient les informations concernant le département.
N'hésitez-pas si vous avez besoin d'autres détails.