Chapitre 4 Cartes dynamiques (CG)
Linking to GEOS 3.8.1, GDAL 3.1.4, PROJ 6.3.1
Introduction
Les cartes interactives se sont multipliées depuis quelques années sur les différents types d’écran (ordinateur, tablette, smartphone, …), le plus souvent sous la forme de figurés ponctuels (les fameuses “épingles” de Google) mais aussi désormais de plus en plus de grappes de points, de polygones ou de lignes. Le logiciel Leaflet
a joué un rôle décisif dans cette révolution cartographique en raison de son caractère libre, de sa polyvalence en terme de langage (R, Python, …) et bien évidemment de ses qualités intrinsèques. Le package Leaflet
est par ailleurs particulèrement adapté au couplage avec les applications interactives de type Shiny
et plus généralement les outils de crétion de tableaux de bords (dashboard). Pour Elena Salette, c’es est clairement le logiciel de référence
Je dirais que
leaflet
est LA star des packages de cartes interactifs. On le voit partout, et même si on lui reproche parfois sa lenteur d’affichage, il est ultra complet, et la doc est vraiment bien faite : https://rstudio.github.io/leaflet/. Ce package utilise la librairie Javascript open source du même nom qui est largement utilisée (cf. la partie “Trusted by the best” sur https://leafletjs.com/ ). Source : Elena Salette, ThinkR, 25-08-2020
Il n’est évidemment pas possible de donner en quelques heures une formation complète à Leaflet mais il est essentiel pour des étudiants se destinant au métier de data analyst ou de data scientist d’en maîtriser les bases, ce qui est l’objet du présent cours.
4.1 Une carte élémentaire
OBJECTIFS : Ce premier cours propose de fournir les bases élémentaires du logiciel Leaflet. Il est très largement inspiré d’un article d’Elena Salette publié sur l’excellent site de formation ThinkR et intitulé Cartographie interactive : comment visualiser mes données spatiales de manière dynamique avec leaflet ?
Notre objectif très limité sera de construire une carte interactive utilisant le fonds de carte OpenStreetMap que l’on pourra zoomer en avant ou en arrière. La carte comportera la localisation de la place de la gare à Sucy-en-Brie avec une “épingle” de localisation comportant une photographie de la gare et un petit texte de promotion de celle-ci.
4.1.1 Lancement avec leaflet()
Nous allons avoir besoin des packages suivants :
leaflet
puisque c’est l’objet même du cours !dplyr
afin de pouvoir construire des programmes utilisant des pipes%>%
sf
pour charger des fonds de carte de différents types (points, lignes polygones)htmltools
ethtmlwidgets
pour ajouter des popups interactifs sur notre carte
Pour vérifier que le package leaflet est bien installé, nous créons une première carte (vide !)
Et il n’y a … RIEN ! si ce n’est un bouton de zoom
4.1.2 Remplissage avec addTiles()
On ajoute sur ce fond de carte vide des “tuiles” cartographiques qui sont des images se modifiant selon l’échelle pour apporter plus ou moins de détails. Par défaut, le fonds de carte de référence est le fonds OpenStreetMap
4.1.3 Calage avec setView()
Nous allons ensuite choisir un point de référence, par exemple la place de la gare à Sucy-en-Brie. Pour trouver les coordonnées de latitude et longitude, la solution la plus simple est d’utiliser Google Maps puis de zoomer sur la zone d’étude et enfin d’effectuer un click droit avec la souris sur le point dont on cherche les coordonnées pour obtenir dans un popup les coordonnées recherchées :
On peut alors procéder à une double opération de centrage de notre carte et de définition d’une échelle d’observation afin que la carte produite par
leaflet
couvre bien la zone qui nous intéresse. Cette double opération est réalisée à l’aide de la fonction setView()
assortie des trois paramètre suivants :
lng =
pour la longitudelat =
pour la latitudezoom =
pour le degré d’aggrandissement de la carte de 1 pour le Monde entier à 20 pour une vision ulra locale
Une fois qu’on a vérifié le centrage avec un zoom fort (ici 17), on peut refaire la carte en utilisant un zoom plus faible, par exemple un zoom de 12 permettant de visualiser toute la commune de Sucy et les communes voisines.
4.1.4 Personalisation avec addProviderTiles()
Les tuiles OpenStreetMap qui servent de fonds de carte par défaut peuvent être remplacés par des tuiles personalisées fournies par des producteurs publics ou privés. On peut obtenir la liste des tuiles disponibles en tapant providers
dans la console de R studio et les tester une par une. Mais il est souvent plus simple et plus rapide d’aller visualiser les tuiles disponibles sur ce site web où l’on peut centrer le monde sur sa zone d’étude et voir ce que donnent les différentes familles de tuiles.
A titre d’exemple, les tuiles Stamen.Watercolor
donnent une touche pastel artistique à la carte :
map <- leaflet() %>%
addProviderTiles('Stamen.Watercolor') %>%
setView(lat = 48.77141, lng=2.50870, zoom = 12)
map
Tandis que la couche Esri.WorldTopoMap
fournit une imagerie précise mais de couleurs plus neutre que les tuiles OpenStreetMap
, ce qui sera intéressant si on superspose des marqueurs de couleur vive.
4.1.5 Affichage d’un point avec addMarkers()
L’usage le plus fréquent de leaflet
consiste à ajouter des éléments de localisation ponctuelle appelés markers
et de rendre ces objets ponctuels interactifs avec l’ouverture de fenêtres popups
lorsqu’on clique dessus avec la souris. On va donc voir pas à pas comment construire de telles cartes interactives en partant du cas le plus simple (marqueur unique) pour aller vers les cas plus complexes (ensemble de marqueurs de taille, couleur et formes différentes).
Nous allons commencer par indiquer l’emplacement de la place de la gare de Sucy-en-Brie sur notre carte précédente à l’aide de la fonction addMarkers()
:
4.1.6 Ajout d’un label
ou d’un popup
On peut définir deux comportements d’un marker selon que la souris ne fait que passer dessus (label
) ou selon que l’utilisateur effectue un click sur marker et déclenche l’ouverture d’une fenêtre (popup
). Dans sa version la plus simple, l’interactivité consiste à ajouter une chaîne de caractère à ces deux paramètres.
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77141, lng=2.50870, zoom = 12) %>%
addMarkers(lat = 48.77141, lng=2.50870,
# En passant la souris
label = "GARE DE SUCY-BONNEUIL",
# En cliquant sur l'icone
popup = "La gare RER A de Sucy Bonneuil est bien reliée aux communes
environnantes par un réseau de bus partant dans toutes les directions")
map
4.1.7 Amélioration du popup
Mais on peut faire beaucoup mieux, notamment pour la fenêtre popup
qui peut prendre la forme d’une mini-page web dont on fixe le contenu en html avec la fonction paste0()
et les dimensions avec le sous-paramètre popupOptions()
.
# Préparation de la fenêtre Popup
my_popup = paste0(
"<b> LA GARE DE SUCY",
"</b><br/><img src=https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Gare_Sucy-en-Brie.jpg/1200px-Gare_Sucy-en-Brie.jpg width='200px'><br/>",
"La gare RER A de Sucy Bonneuil est bien reliée aux communes
environnantes par un réseau de bus partant dans toutes les directions.")
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77141, lng=2.50870, zoom = 12) %>%
addMarkers(lat = 48.77141, lng=2.50870,
# En passant la souris
label = "GARE DE SUCY-BONNEUIL",
# En cliquant sur l'icone
popup = my_popup,
# Quelques options de la popup
popupOptions =
list(maxHeight = 150, maxWidth = 200))
map
4.1.8 Prolongements
Et voila, le tour est joué. Il faut maintenant réfléchir à la façon de construire une carte comportant un ensemble d’épingles similaires avec des couleurs ou des formes différentes, des messages différents, des photographies variées … Il ne sera alors évidemment pas possible d’ajouter une commande addMarkers() pour chaque épingle si la carte en comporte des centaines.
Si vous avez bien compris ce cours, vous pourrez trouver des réponses en lisant de façon autonome le reste de l’article dont nous nous somme inspiré : Cartographie interactive : comment visualiser mes données spatiales de manière dynamique avec leaflet ?
4.2 Une carte statistique
OBJECTIFS : Ce second cours propose un objectif plus ambitieux de création d’une carte interactive à décrivant les données du recensement de 2017 à l’échelon des IRIS. Il suppose acquise les connaissances données en option Data Mining du master MECI, notamment le modèle de donnée permettant de stocker des informations sociales et spatiales qui est accessible sur github en cliquant ici
On va prendre l’exemple d’ une carte de distribution des ouvriers à l’échelon des IRIS dans quatre communes du Val de Marne au moment du recensement de population de 2017. La carte devra être interactive et permettre de visualiser les données INSEE tout en pouvant zoomer et repérer le tracé topographique des rues.
4.2.1 Préparation des données
Cette section s’appuie sur un cours concernant les modèles de données et les fonctions associées. On se limitera donc à expliquer les fichiers obtenus sans plus de détail.
On charge tout d’abord une bibliothèque de fonctions …
Puis une base de donnée structurée d’où l’on extrait les communes retenues à l’aide de leur code INSEE
csp <- readRDS("myproject/data/VDM_RP2017_CS1_IRIS_POP.rds")
csp <-base_select_geo(base = csp, sel=c("94011","94071","94055","94068"))
On extrait de la base le nombre d’ouvrier en effectif brut et en part de la population de l’IRIS.
csp<-base_norm_geo(csp)
mapiris <- inner_join(csp$tab,csp$geo) %>% filter(j =="6") %>% st_as_sf()
kable(head(mapiris))
i | j | Xij | Xi | Pj_i | lab_i | i2 | lab_i2 | geometry | var_i | var_i2 | lng | lat |
---|---|---|---|---|---|---|---|---|---|---|---|---|
940110101 | 6 | 51 | 440 | 0.1159091 | Zone d’Activite | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.490373 48… | quartier IRIS | Communes | 2.490429 | 48.77414 |
940110102 | 6 | 250 | 2393 | 0.1044714 | Centre Ancien | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.485309 48… | quartier IRIS | Communes | 2.481459 | 48.77566 |
940110103 | 6 | 192 | 1674 | 0.1146953 | Haut Bonneuil | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.478069 48… | quartier IRIS | Communes | 2.477026 | 48.77223 |
940110104 | 6 | 206 | 1265 | 0.1628458 | Cite Fabien | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.485309 48… | quartier IRIS | Communes | 2.488028 | 48.77105 |
940110105 | 6 | 278 | 1781 | 0.1560921 | Saint-Exupery | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.485309 48… | quartier IRIS | Communes | 2.482796 | 48.77102 |
940110106 | 6 | 231 | 1431 | 0.1614256 | Salvador Allende | 94011 | Bonneuil-sur-Marne | MULTIPOLYGON (((2.478069 48… | quartier IRIS | Communes | 2.480329 | 48.76829 |
On agrège le fichier par communes afin de pouvoir superposer leur contour sur la carte des IRIS.
csp2<-base_agreg_geo(csp)
csp2<-base_norm_geo(csp2)
mapcom <- inner_join(csp2$tab,csp2$geo) %>% filter(j =="6")%>% st_as_sf()
kable(head(mapcom))
i | j | Xij | Xi | Pj_i | lab_i | lat | lng | geometry |
---|---|---|---|---|---|---|---|---|
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
94011 | 6 | 1926 | 107224 | 0.0179624 | Bonneuil-sur-Marne | 48.77062 | 2.482723 | POLYGON ((2.476012 48.76572… |
4.2.2 Contours des iris et communes avec addPolygons()
La fonction leaflet de base pour tracer des polygones est addPolygons()
qui est l’équivalent de addMarkers()
que l’on a vu précédemment. Mais la différence importante est que l’on peut désormais fournir un fichier sf aux fonctions addPolygons
et addMarkers
puis accéder aux variables contenues dans ce fichier en utilisant un tilde ’~’suivi du nom de la variable.
On peut par exemple construire une carte des communes avec un label donnant le nom
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77, lng=2.53, zoom = 12) %>%
addPolygons(data = mapcom,
color = c("red","orange","green","blue"),
label = ~lab_i)
map
fill =
) dont on peut régler l’épaisseur (weight=
) ou la couleur (color =
). Les labels ou popup ne pourront a priori concerner également qu’une seule couche.
# Ajout de la variable couleur
mapiris$comcolor<-as.factor(mapiris$lab_i2)
levels(mapiris$comcolor)<-c("red","orange","green","blue")
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77, lng=2.53, zoom = 12) %>%
addPolygons(data = mapiris,
fill = TRUE,
color = ~comcolor,
label = ~lab_i,
weight = 1) %>%
addPolygons(data = mapcom,
fill = FALSE,
color = "black",
weight = 2)
map
4.2.3 Cartes choroplèthes avec addPolygon()
et colorBin()
De la même manière que nous avons affiché la couleur des communes dans chaque IRIS, nous pouvons proposer une carte choroplèthe du % de personnes habitant dans des HLM et ajouter un popup donnant la valeur de l’indicateur si l’on clique. La seule difficulté est de préparer une palette de couleur à l’aide de l’une des fonction colorNumeric()
, colorBin()
, colorQuantile()
ou colorFactor()
.
Voyons un exemple sur la variable Pj_i
qui réprésente ici la part des ouvriers(j
) dans la population de plus de 18 ans de chaque IRIS(i
) exprimée en pourcentage
# Choix de la variable
mapiris$myvar <-round(100*mapiris$Pj_i,1)
# Choix des classes
mycut<-c(0, 3,6, 9,12,100)
# Choix de la palette (c'est une fonction !)
mypal <- colorBin('RdYlBu',
mapiris$myvar,
bins=mycut,
rev=TRUE)
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77, lng=2.53, zoom = 12) %>%
addPolygons(data = mapiris,
fillColor = ~mypal(myvar),
fillOpacity = 0.5,
color = "white",
label = ~myvar,
# popup = mypop,
weight = 1) %>%
addLegend(data = mapiris,
pal = mypal,
title = "% ouvriers",
values =~myvar,
position = 'topright') %>%
addPolygons(data = mapcom,
fill = FALSE,
color = "black",
weight = 2)
map
4.2.4 Cartes de stock avec addCircleMarkers()
Notre carte est intéressante mais elle ne permet pas de voir quelle est la population ouvrière parmi les plus de 18 ans. En effet, certains IRIS sont très étendus alors qu’ils regroupent peu de population et d’autres sont très petits mais comportent beaucoup d’ouvrier. Ce qui est logique puisque les ouvriers sont plus souvent présents dans les zones denses d’habitat HLM.
Nous allons donc superposer sur la carte précédente le nombre de personnes de plus de 18 ans catégorisées comme ouvrier. Puisqu’il s’agit d’un stock, nous devrons utiliser un figuré ponctuel avec une surface proportionnelle au nombre d’habitants des logements sociaux.
# Choix de la variable
mapiris$myvar <-round(100*mapiris$Pj_i,1)
# Choix des classes
mycut<-c(0, 3,6, 9,12,100)
# Choix de la palette (c'est une fonction !)
mypal <- colorBin('RdYlBu',
mapiris$myvar,
bins=mycut,
rev=TRUE)
# Calcul du diamètre des cercles
mapiris$myradius <-8*sqrt(mapiris$Xij/max(mapiris$Xij))
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77, lng=2.53, zoom = 12) %>%
addPolygons(data = mapiris,
fillColor = ~mypal(myvar),
fillOpacity = 0.5,
color = "white",
label = ~myvar,
# popup = mypop,
weight = 1) %>%
addLegend(data = mapiris,
pal = mypal,
title = "% ouvriers",
values =~myvar,
position = 'topright') %>%
addCircleMarkers(data=mapiris,
lat = ~lat,
lng = ~lng,
radius = ~myradius,
stroke = FALSE,
label = ~myradius,
fillColor = "gray50",
fillOpacity = 0.5)%>%
addPolygons(data = mapcom,
fill = FALSE,
color = "black",
weight = 2)
map
4.2.5 Finition avec un popup
et highlightOptions()
Et pour terminer notre belle carte, nous allons ajouter une fenêtre popup apportant à l’utilisateur tous les renseignements sur chaque IRIS. Pour cela nous allons devoir construire chaque fenêtre popup au format HTML préalablement à l’affichage des cartes en utilisant des outils issus des packaghes htmltools
et htmlwidgets
. On supprime les labels des deux couches, l’utilisateur ayant désormais juste à cliquer sur un Iris pour obtenir tous les renseignements dans une seule fenêtre
On ajoute au passage un paramètre highlighOptions()
pour que les iris au dessus desquels passe la souris soient mis en valeur par un trait de contour spécifique. On verra ainsi mieux à quelle zone s’applique le popup de renseignement.
# Choix de la variable
mapiris$myvar <-round(100*mapiris$Pj_i,1)
# Choix des classes
mycut<-c(0, 3,6, 9,12,100)
# Choix de la palette (c'est une fonction !)
mypal <- colorBin('RdYlBu',
mapiris$myvar,
bins=mycut,
rev=TRUE)
# Calcul du diamètre des cercles
mapiris$myradius <-8*sqrt(mapiris$Xij/max(mapiris$Xij))
# Préparation des popups
mypopups <- lapply(seq(nrow(mapiris)), function(i) {
paste0( paste("Commune : ",mapiris$lab_i2[i]), '<br>',
paste("Iris : " ,mapiris$lab_i[i]), '<br>',
paste("Population > 18 ans : " ,mapiris$Xi[i]), '<br>',
paste("Ouvrier > 18 ans : " ,mapiris$Xij[i]), '<br>',
paste("% ouvriers :", mapiris$myvar[i])
)
})
mypopups<-lapply(mypopups, htmltools::HTML)
# Réalisation de la carte
map <- leaflet() %>%
addProviderTiles('Esri.WorldTopoMap') %>%
setView(lat = 48.77, lng=2.53, zoom = 12) %>%
addPolygons(data = mapiris,
fillColor = ~mypal(myvar),
fillOpacity = 0.5,
color = "white",
popup = mypopups,
weight = 1,
highlightOptions = highlightOptions(weight = 3, color = 'green')) %>%
addLegend(data = mapiris,
pal = mypal,
title = "% ouvriers",
values =~myvar,
position = 'topright') %>%
addCircleMarkers(data=mapiris,
lat = ~lat,
lng = ~lng,
radius = ~myradius,
stroke = FALSE,
label = ~myradius,
fillColor = "gray50",
fillOpacity = 0.5)%>%
addPolygons(data = mapcom,
fill = FALSE,
color = "black",
weight = 2)
map
4.2.6 Prolongements
Le résultat est bien joli, mais il impose une série d’interventions manuelles qui empêchent d’automatiser la production de cartes équivalentes sur les cadres, les employés, les artisans … Ou bien sur un autre ensemble d’étude.
La mission d’un data analyste expérimenté sera donc de reprendre ce programme et de l’automatiser sous la forme d’une fonction paramétrique qui crée une carte leaflet à partir d’un jeu minimal de trois paramètres :