Initiation à la cartographie

Cette page a pour but de vous initier à la cartographie avec R. Pour une formation plus complète, n’hésitez pas à consulter le manuel de T. Giraud et H. Pécout ici : Cartographie avec R.

1 Chargez les packages

Nous allons charger le package sf pour la manipulation des données spatiales, et les packages mapsf et tmap pour la cartographie.

library(sf)
library(mapsf)
library(tmap)

2 Importez les fichiers

Téléchargez la table des communes du Calvados COM14.rds ici : lien, et déposez-la dans le data de votre projet.

data <- readRDS("data/COM14.Rds")

Il s’agit d’une table au format sf, représentant les communes du Calvados. La table attributaire comprend les variables suivantes :

  • INSEE_COM : Code INSEE de la commune
  • NOM : Nom de la commune
  • TXLEPEN : Taux de vote en faveur de Le Pen au second tour des présidentielles de 2022
  • DENSPOP : Densité de population en 2022
  • TXCHOM : Taux de chômage en 2022

Téléchargez ensuite les terrains de tennis dans le département ici : lien

tennis <- read_sf("data/TENNIS.gpkg") %>% st_centroid()
Warning: st_centroid assumes attributes are constant over geometries

3 Quelques éléments de base

3.1 Le format sf

La manipulation des données spatiales dans R s’est nettement simplifiée depuis l’apparition du package sf en 2016. Les données spatiales sont de simples data.frame stockant la géométrie des objets (points, lignes ou polygones) dans une colonne dédiée *geometry* (de classe sfc pour simple feature column). Cette colonne stocke les coordonnées géographiques des points ou des noeuds, ce qui permet de cartographier les objets.

sf Source : Géomatique avec R

3.2 La sémiologie graphique

Il existe des règles de sémiologie graphique qu’il convient de respecter dans chacune de vos cartes. La manière de représenter l’information sur une carte dépend de la nature de la variable à cartographier. Ce schéma résume l’essentiel :

sémio Source : Cartographie avec R

Par exemple :

  • La variable Population des communes : sa moyenne a un sens, de même que sa somme. Il faut donc la représenter avec des symboles proportionnels
  • La variable Taux de chômage des communes : sa moyenne a un sens, mais pas sa somme. Il faut la représenter avec une carte choroplèthe (aplats de couleurs dans une gamme progressive)
  • La variable Type de communes : sa moyenne n’a pas de sens. Il faut la représenter avec des symboles de forme différente
  • Comment représenter la densité de population ?

Par ailleurs, une carte doit toujours comprendre les quatre éléments suivants (TOLE) :

  • Titre
  • Orientation
  • Légende
  • Echelle

4 Les packages mapsf et tmap

Il existe plusieurs packages dédiés à la cartographie, dont mapsf et tmap qui sont probablement les plus complets. Les syntaxes sont néanmoins différentes, et chacun a des points forts et des points faibles. Il est donc intéressant de savoir utiliser les deux.

4.1 mapsf

Ce package (vignettes ici) est basé sur la fonction centrale mf_map(), dont les arguments sont :

  • x : un objet sf
  • var : le nom de la variable à représenter
  • type : le type de représentation

Ainsi, pour afficher les communes du Calvados :

mf_map(x = data, border = "grey20")

Si on veut ajouter un autre objet spatial sur la carte, il faut répéter la fonction mf_map en ajoutant l’argument add = TRUE. On peut aussi facilement ajouter un titre avec mf_title :

mf_map(x = data, border = "grey20")
mf_map(x = tennis, pch = 20, cex = .5, col = "red", add= TRUE)
mf_title("Commune du Calvados")

On peut également ajouter une ombre à la carte pour faire joli, et penser à la mise en page en rajoutant l’échelle et l’orientation :

mf_shadow(x = data)
mf_map(x = data, border = "grey20", add = TRUE)
mf_title("Terrains de tennis dans le Calvados")
mf_map(x = tennis, pch = 20, cex = .5, col = "red", add= TRUE)
mf_scale(size = 20, lwd = 2, cex = 1)
mf_arrow()

4.2 tmap

Le package tmap fonctionne en double mode plot (carte statique classique) et view (carte interactive), ce qui est très pratique. Il faut donc commencer par fixer ce mode, qui est plot par défaut. Vous trouverez les vignettes du package ici.

tmap_mode("plot")

La syntaxe de tmap est additive, comme dans ggplot2. La logique est la suivante :

  • Identification de l’objet spatial à cartographier avec la fonction tm_shape() (i.e. l’objet sf étudié)
  • Identification de la nature de l’objet à cartographier (points, lignes, polygones) avec les fonctions (par exemple) tm_dots(), tm_lines() ou tm_polygons().

Pour représenter les communes du Calvados, on procède donc ainsi :

tm_shape(data) + tm_polygons()

Si on veut ajouter les terrains de tennis, il faut rajouter une phrase comprenant la paire de fonctions :

tm_shape(data) + tm_polygons() + 
  tm_shape(tennis) + tm_dots(fill = "red")

Maintenant, la même chose en mode interactif :

tmap_mode("view")
tm_shape(data) + tm_polygons() + 
  tm_shape(tennis) + tm_dots(fill = "red")

Allons maitnenant un peu plus loin dans le type de cartes et dans la mise en page.

5 Cartes choroplèthes

Nous allons cartographier le taux de chômage dans les communes du Calvados. Comme tous les taux, cette variable doit être représentée par des aplats de couleur progressifs (carte choroplèthe). Cela implique le choix d’une méthode de discrétisation, puisque les valeurs vont être représentées au sein de quelques classes seulement. La méthode la plus commune et passe-partout est la méthode Q6, regroupant les valeurs en 4 classes d’effectifs égaux + deux classes de valeurs extrêmes.

5.1 Avec mapsf

mf_shadow(data, col = "grey10")
mf_map(
  x = data, 
  var = "TXCHOM", 
  type = "choro",
  pal = "Red-Green",
  breaks = "q6",
  leg_pos = 'left',
  leg_title = "",
  add = TRUE
)
# On peut ajouter une annotation indiquant la ville de Caen
mf_annotation(data[366,],
              txt = "Caen", 
              halo = T, 
              s = 1.9)
mf_title("Taux de chômage en 2022 dans le Calvados")
mf_scale(size = 20)
mf_arrow()

5.2 Avec tmap

tmap_mode("plot") # Mode statique

tm_shape(data) + 
  tm_polygons(fill = "TXCHOM",
              fill.legend = tm_legend(title = "", frame = FALSE), 
              fill.scale = tm_scale_intervals(style = "quantile", 
                                              n = 6,
                                              values = "YlOrRd"),
              col = "grey70",
              lwd = 0.7) + 
  tm_scalebar(position = c("right", "bottom"),
              text.size = 0.6 ) + 
  tm_title("Taux de chômage en 2022 dans le Calvados",
    position = tm_pos_out("center", "top"),
    size = 1.2,
    fontface = "bold") +
  tm_layout(frame = FALSE, 
    legend.outside = TRUE)

# Liste complète des palettes : https://r-graph-gallery.com/38-rcolorbrewers-palettes.html
# ou RColorBrewer::display.brewer.all()

6 Cartes à symboles proportionnels

Nous souhaitons maintenant cartographier la population des communes. Il s’agit d’une variable quantitative de stock (un effectif), qu’il convient de cartographier à l’aide de cercles proportionnels.

6.1 Avec mapsf

mf_shadow(data, col = "grey10")
mf_map(x = data, add = TRUE)
mf_map(
  x = data, 
  var = "POPULATION", 
  type = "prop",
  leg_pos = 'left',
  add = TRUE, border = "grey80", 
  leg_title = "Nombre d'habitants", 
  val_max = 200000
)
# On peut ajouter une annotation indiquant la ville de Caen
mf_annotation(data[366,],
              txt = "Caen", 
              halo = T, 
              s = 1.9)
mf_title("Population communale en 2022 dans le Calvados")
mf_scale(size = 20)
mf_arrow()

6.2 Avec tmap

tm_shape(data) +
  tm_polygons(fill = "grey95", col = "grey60") +
  tm_symbols(
    size = "POPULATION",
    fill = "red",
    col = "black",
    lwd = 1,
    size.scale = tm_scale(values.scale = 2),
    size.legend = tm_legend(title = "Nombre d'habitants", frame = FALSE)
  ) +
  tm_scalebar(
    position = c("right", "BOTTOM"),
    text.size = 0.6) +
  tm_title("Population communale en 2022 dans le Calvados",
           position = tm_pos_out("center", "top"),
           size = 1.2,
           fontface = "bold") +
  tm_layout(
    frame = FALSE, 
    legend.outside = TRUE)

Il existe de multplies autres paramétrages possibles dans ces deux packages, qui sont présentées dans la documentation et les nombreuses vignettes associées. Il convient de les explorer en fonction de vos besoins sépcifiques…

6.3 Exercice

En vous aidant des codes ci-dessus, pouvez-vous cartographier la densité de population par commune dans le Calvados, avec les deux packages étudiés ?

7 Croiser des couches d’information géographique

Les géotraitements consistent à croiser des couches d’information pour produire de nouveux objets ou de nouvelles variables spatialisées. Ils consitutuent le coeur de la géomatique. On peut calculer des distances entre objets, des densités d’objets ponctuels dans des objets surfaciques, découper des géométries en fonction d’autres, réaliser des sélections et des jointures spatiales, etc. Tout cela est possible à l’aide des fonctions du package sf, listées ci-dessous :

ls("package:sf")
  [1] "%>%"                          "as_Spatial"                  
  [3] "dbDataType"                   "dbWriteTable"                
  [5] "FULL_bbox_"                   "gdal_addo"                   
  [7] "gdal_create"                  "gdal_crs"                    
  [9] "gdal_extract"                 "gdal_inv_geotransform"       
 [11] "gdal_metadata"                "gdal_polygonize"             
 [13] "gdal_rasterize"               "gdal_read"                   
 [15] "gdal_read_mdim"               "gdal_subdatasets"            
 [17] "gdal_utils"                   "gdal_write"                  
 [19] "gdal_write_mdim"              "get_key_pos"                 
 [21] "NA_agr_"                      "NA_bbox_"                    
 [23] "NA_crs_"                      "NA_m_range_"                 
 [25] "NA_z_range_"                  "plot_sf"                     
 [27] "rawToHex"                     "read_sf"                     
 [29] "sf_add_proj_units"            "sf_extSoftVersion"           
 [31] "sf_proj_info"                 "sf_proj_network"             
 [33] "sf_proj_pipelines"            "sf_proj_search_paths"        
 [35] "sf_project"                   "sf_use_s2"                   
 [37] "sf.colors"                    "st_agr"                      
 [39] "st_agr<-"                     "st_area"                     
 [41] "st_as_binary"                 "st_as_grob"                  
 [43] "st_as_s2"                     "st_as_sf"                    
 [45] "st_as_sfc"                    "st_as_text"                  
 [47] "st_axis_order"                "st_bbox"                     
 [49] "st_bind_cols"                 "st_boundary"                 
 [51] "st_break_antimeridian"        "st_buffer"                   
 [53] "st_can_transform"             "st_cast"                     
 [55] "st_centroid"                  "st_collection_extract"       
 [57] "st_combine"                   "st_concave_hull"             
 [59] "st_contains"                  "st_contains_properly"        
 [61] "st_convex_hull"               "st_coordinates"              
 [63] "st_covered_by"                "st_covers"                   
 [65] "st_crop"                      "st_crosses"                  
 [67] "st_crs"                       "st_crs<-"                    
 [69] "st_delete"                    "st_difference"               
 [71] "st_dimension"                 "st_disjoint"                 
 [73] "st_distance"                  "st_drivers"                  
 [75] "st_drop_geometry"             "st_equals"                   
 [77] "st_equals_exact"              "st_exterior_ring"            
 [79] "st_filter"                    "st_geometry"                 
 [81] "st_geometry_type"             "st_geometry<-"               
 [83] "st_geometrycollection"        "st_graticule"                
 [85] "st_inscribed_circle"          "st_interpolate_aw"           
 [87] "st_intersection"              "st_intersects"               
 [89] "st_is"                        "st_is_empty"                 
 [91] "st_is_full"                   "st_is_longlat"               
 [93] "st_is_simple"                 "st_is_valid"                 
 [95] "st_is_within_distance"        "st_jitter"                   
 [97] "st_join"                      "st_layers"                   
 [99] "st_length"                    "st_line_interpolate"         
[101] "st_line_merge"                "st_line_project"             
[103] "st_line_sample"               "st_linestring"               
[105] "st_m_range"                   "st_make_grid"                
[107] "st_make_valid"                "st_minimum_bounding_circle"  
[109] "st_minimum_rotated_rectangle" "st_multilinestring"          
[111] "st_multipoint"                "st_multipolygon"             
[113] "st_nearest_feature"           "st_nearest_points"           
[115] "st_node"                      "st_normalize"                
[117] "st_overlaps"                  "st_perimeter"                
[119] "st_point"                     "st_point_on_surface"         
[121] "st_polygon"                   "st_polygonize"               
[123] "st_precision"                 "st_precision<-"              
[125] "st_read"                      "st_read_db"                  
[127] "st_relate"                    "st_reverse"                  
[129] "st_sample"                    "st_segmentize"               
[131] "st_set_agr"                   "st_set_crs"                  
[133] "st_set_geometry"              "st_set_precision"            
[135] "st_sf"                        "st_sfc"                      
[137] "st_shift_longitude"           "st_simplify"                 
[139] "st_snap"                      "st_sym_difference"           
[141] "st_touches"                   "st_transform"                
[143] "st_triangulate"               "st_triangulate_constrained"  
[145] "st_union"                     "st_viewport"                 
[147] "st_voronoi"                   "st_within"                   
[149] "st_wrap_dateline"             "st_write"                    
[151] "st_write_db"                  "st_z_range"                  
[153] "st_zm"                        "vec_cast.sfc"                
[155] "vec_ptype2.sfc"               "write_sf"                    

Imaginons que nous souhaitions cartographier la densité de terrains de tennis par habitant (nombre de terrains pour 1000 habitants). Le géotraitement va ici consister à compter le nombre de terrains dans chaque commune. Il ne suffira ensuite que de diviser cet effectif par la population communale.

7.1 Compter des points dans des polygones

Pour cette tâche, il convient d’utiliser la fonction st_intersects, qui va identifier les index des points contenus dans chaque polygone, puis compter le nombre d’index avec la fonction length :

data$nb_tennis <- st_intersects(data, tennis) %>% lengths()

7.2 Calculer la densité et la cartographier

# Densité de terrain de tennis pour 1000 habitants
data$dens_tennis <- data$nb_tennis / data$POPULATION * 1000

7.3 Avec mapsf

mf_shadow(data, col = "grey10")
mf_map(
  x = data, 
  var = "dens_tennis", 
  type = "choro",
  breaks = "quantile",
  n = 5,
  pal = "Peach", 
  leg_pos = 'left',
  leg_title = "",
  add = TRUE
)
# On peut ajouter une annotation indiquant la ville de Caen
mf_annotation(data[366,],
              txt = "Caen", 
              halo = T, 
              s = 1.9)
mf_title("Densité de terrain de tennis pour 1000 habitants")
mf_scale(size = 20)
mf_arrow()

7.4 Avec tmap

tm_shape(data) + 
  tm_polygons(fill = "dens_tennis",                  
              fill.legend = tm_legend(title = "", frame = FALSE, 
                                      position = tm_pos_out()),   
              fill.scale = tm_scale_intervals(style = "fisher",
                                              n = 5,
                                              values = "YlOrRd"),
              col = "grey70",                        
              lwd = 0.7) + 
  tm_scalebar(position = c("right", "bottom"),
              text.size = 0.6) +
  tm_title("Densité de terrain de tennis pour 1000 habitants",
           position = tm_pos_out("center", "top"),
           size = 1.2,
           fontface = "bold") +
  tm_layout(frame = FALSE, 
            legend.outside = TRUE)

On aime beaucoup faire du tennis dans le nord-est du Calvados.