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 et htmlwidgets 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 !)

map <- leaflet()

map

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

library(leaflet)

map <- leaflet() %>%
          addTiles()

map

La carte est désormais interactive et on peut effectuer des zooms ou se déplacer.

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 :

coordonnnées de la place de la gare de Sucy 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 leafletcouvre 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 longitude
  • lat = pour la latitude
  • zoom = pour le degré d’aggrandissement de la carte de 1 pour le Monde entier à 20 pour une vision ulra locale
map <- leaflet() %>% 
          addTiles() %>%
          setView(lat = 48.77141, lng=2.50870, zoom = 17)

map

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.

map <- leaflet() %>% 
          addTiles() %>%
          setView(lat = 48.77141, lng=2.50870, zoom = 12)

map

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.

map <- leaflet() %>% 
            addProviderTiles('Esri.WorldTopoMap') %>%
          setView(lat = 48.77141, lng=2.50870, zoom = 12)
map

4.1.5 Affichage d’un point avec addMarkers()

L’usage le plus fréquent de leafletconsiste à ajouter des éléments de localisation ponctuelle appelés markerset de rendre ces objets ponctuels interactifs avec l’ouverture de fenêtres popupslorsqu’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() :

map <- leaflet() %>% 
            addProviderTiles('Esri.WorldTopoMap') %>%
            setView(lat = 48.77141, lng=2.50870, zoom = 12) %>% 
            addMarkers(lat = 48.77141, lng=2.50870)
map

On constate que le marqueur donne bien la position choisi mais n’est pas interactif. Il faut ajouter plus de paramètres pour assurer l’interactivité.

4.1.6 Ajout d’un labelou 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 popupqui 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 …

source("myproject/pgm/base_functions.R")

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

On peut superposer plusieurs cartes de polygones à condition que certaines soient composée uniquement de lignes de contour (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 htmltoolset 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 :

# Fonction de création de la carte leaflet
base_map_leaflet <- function(base = base,    # Choix de la base de donnée
                             soc = selj,     # Choix de la variable sociologique
                             geo = seli,     # Choix de la zone géographique d'étude
                             ...,            # Autres paramètres facultatifs 
                             )