Cartes du monde avec R : projections, graticules et palettes

Tutoriels R
Data viz
Mercator, Equal Earth et les autres : comprendre les projections cartographiques et les manier avec ggplot2 et sf.
Auteur·rice

Thelma Panaïotis

Date de publication

15 septembre 2026

Mots clés

cartographie, projections, sf, rnaturalearth, ggplot2, R

Cartes du monde avec R : projections, graticules et palettes

Mercator, Equal Earth et les autres : comprendre les projections cartographiques et les manier avec ggplot2.

Pourquoi la projection choisie change tout

Vous avez peut-être vu passer l’info : le 4 septembre 2026, l’Assemblée générale des Nations unies a adopté une résolution portée par le Togo encourageant les États, écoles et organisations internationales à délaisser la projection de Mercator au profit de représentations plus fidèles aux superficies réelles, comme la projection Equal Earth. Ce n’est pas la première fois qu’un grand acteur prend ses distances avec Mercator : Google Maps l’a fait dès 2018 pour ses vues à l’échelle mondiale, en basculant sur un globe 3D plutôt qu’une carte plate. Le choix d’une projection cartographique change la façon dont on perçoit la taille réelle des pays.

Et pour cause, aplatir une sphère sans la déformer est mathématiquement impossible. Chaque projection fait donc un choix, elle préserve certaines propriétés au prix d’en déformer d’autres :

  1. les surfaces (un pays deux fois plus grand qu’un autre en réalité doit apparaître deux fois plus grand sur la carte) ;
  2. les angles et les formes (un contour de pays ne doit pas être distordu) ;
  3. les distances (la longueur d’un segment sur la carte doit refléter la distance réelle).

Aucune projection ne préserve les trois à la fois. C’est là que Mercator et Equal Earth prennent des chemins différents.

Mercator : des angles parfaits, des surfaces qui explosent

La projection de Mercator, créée en 1569 pour la navigation maritime, préserve les angles : une ligne droite sur la carte correspond à un cap constant au compas, très utile en mer. Mais cette préservation des angles a un coût élevé sur les surfaces, qui grandissent démesurément à mesure qu’on s’éloigne de l’équateur.

Carte du monde en projection Mercator, sans l'Antarctique. Le Groenland, tout au nord, apparaît presque aussi grand que l'Afrique alors qu'il est en réalité bien plus petit : les régions proches des pôles sont fortement dilatées par rapport aux régions équatoriales.
Figure 1: Le monde en projection Mercator.

Avec cette projection, le Groenland paraît aussi grand que l’Afrique, alors qu’en réalité l’Afrique fait environ 14 fois sa taille. C’est le reproche le plus connu fait à cette projection, elle donne une importance visuelle disproportionnée aux régions proches des pôles, au détriment des régions équatoriales.

Pourquoi l’Antarctique est souvent absent sur une carte Mercator

L’échelle de Mercator diverge à mesure qu’on approche des pôles. L’Antarctique, qui s’étend jusqu’à -90° de latitude, devient démesurément et inutilisablement grand sous cette projection. C’est pourquoi ce continent est traité à part avec cette projection. Le plus souvent, il est tout simplement exclu des données avant de générer la figure.

Equal Earth : des surfaces fidèles, des formes qui bougent un peu

La projection Equal Earth (Šavrič et al. 2019) part du problème inverse : elle préserve les proportions de surface entre les pays. Le Groenland y retrouve une taille comparable à sa réalité, au prix de formes et d’angles légèrement déformés loin de l’équateur.

Le même monde que la carte précédente, mais en projection Equal Earth : le globe prend une forme elliptique sur fond bleu clair, l'Afrique retrouve une taille bien plus importante que le Groenland, à l'inverse de la carte en Mercator.
Figure 2: Le même monde en projection Equal Earth.

L’Afrique retrouve ainsi sa vraie surface, tandis que le Groenland rétrécit en conséquence. Rien n’a changé dans les données, uniquement la façon de les projeter sur un plan.

Pourquoi Equal Earth et pas une autre projection équivalente ?

Equal Earth n’est pas la première projection à préserver les surfaces : Mollweide (1805) ou Gall-Peters (1855) le font aussi. Elle a été conçue spécifiquement pour donner des formes de continents plus naturelles à l’œil que ses prédécesseurs, tout en gardant cette propriété. C’est ce compromis esthétique qui explique son adoption récente par plusieurs médias et organisations.

Mais comment on fait ça concrètement, avec R ?

Faire des cartes du monde avec R

Pour ce tutoriel, on utilise trois packages qui se combinent naturellement :

  • sf (simple features) pour manipuler des données géographiques (points, lignes, polygones) ;
  • rnaturalearth pour récupérer directement les contours des pays du monde ;
  • ggplot2, qu’on ne présente plus, avec sa fonction geom_sf() dédiée aux objets spatiaux.

Récupérer les contours du monde

rnaturalearth télécharge et met en cache des données géographiques prêtes à l’emploi. Pas besoin de chercher un shapefile sur internet.

world <- ne_countries(scale = "medium", returnclass = "sf")

L’argument scale contrôle le niveau de détail des contours ("small", "medium" ou "large"). Pour une carte du monde entière, "medium" suffit largement : "large" alourdit inutilement le fichier sans gain visuel à cette échelle.

world est un objet sf : un tableau de données classique, avec une colonne spéciale geometry qui contient les polygones de chaque pays.

world |> 
  select(name, continent, pop_est, geometry) |> 
  head()
Simple feature collection with 6 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -73.36621 ymin: -22.40205 xmax: 109.4449 ymax: 41.9062
Geodetic CRS:  WGS 84
       name     continent  pop_est                       geometry
1  Zimbabwe        Africa 14645468 MULTIPOLYGON (((31.28789 -2...
2    Zambia        Africa 17861030 MULTIPOLYGON (((30.39609 -1...
3     Yemen          Asia 29161922 MULTIPOLYGON (((53.08564 16...
4   Vietnam          Asia 96462106 MULTIPOLYGON (((104.064 10....
5 Venezuela South America 28515829 MULTIPOLYGON (((-60.82119 9...
6   Vatican        Europe      825 MULTIPOLYGON (((12.43916 41...

Une première carte

Avec un objet sf, on peut utiliser la fonction geom_sf() qui comprend nativement la colonne geometry.

ggplot(world) +
  geom_sf(fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(expand = FALSE) +
  theme_void() +
  theme(panel.background = element_rect(fill = "#E4EFF6", color = NA))
Carte du monde en projection plate carrée : les pays sont affichés directement en longitude et latitude, dans un rectangle. Les continents sont étirés horizontalement près des pôles, notamment le Groenland et l'Antarctique.
Figure 3: Carte du monde en projection par défaut (WGS84, non projetée)

Ici on utilise panel.background dans theme() pour colorer les océans, cela fonctionne car cette carte est sans projection. Pour les autres cas, nous verrons plus bas comment faire.

Par défaut, geom_sf() affiche les coordonnées géographiques (longitude, latitude) telles quelles, sans appliquer de projection. C’est ce qu’on appelle la projection plate carrée : chaque degré de longitude et chaque degré de latitude occupent le même espace sur l’écran. Simple, mais ce n’est pas neutre pour autant : les distorsions de surface aux pôles y sont déjà bien visibles.

Changer de projection

C’est coord_sf() qui gère la projection, via son argument crs (coordinate reference system, le système de référence des coordonnées). Pour Equal Earth, il n’existe pas encore de code EPSG largement adopté : on passe par une chaîne PROJ, qui décrit directement les paramètres de la projection.

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = world, fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE) +
  theme_void()
Carte du monde en projection Equal Earth obtenue avec coord_sf(), forme elliptique sur fond bleu clair représentant les océans, proportions de surface entre pays fidèles à la réalité.
Figure 4: Carte du monde en projection Equal Earth
ImportantEPSG ou PROJ, comment choisir ?

Utilisez un code EPSG dès qu’il existe pour votre projection, comme "EPSG:3857" pour Mercator : c’est plus lisible et moins sujet aux erreurs. Passez par une chaîne PROJ (comme +proj=eqearth ou +proj=robin) quand la projection n’a pas de code EPSG standard ou que vous avez besoin de paramètres spécifiques (méridien central personnalisé par exemple).

Autre exemple de chaîne PROJ courante : Robinson, une projection qui ne préserve ni les surfaces ni les angles à la perfection, mais cherche un compromis visuel entre les deux. Elle a longtemps servi de référence pour les cartes du monde généralistes : la National Geographic Society l’a utilisée de 1988 à 1998, avant de lui préférer la projection Winkel Tripel, qui réduit encore un peu plus la déformation près des pôles.

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = world, fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(crs = "+proj=robin", expand = FALSE) +
  theme_void()
Carte du monde en projection Robinson, forme ovale légèrement aplatie aux pôles, sur fond bleu clair. Compromis visuel entre préservation des surfaces et des angles, sans en privilégier aucune à l'extrême.
Figure 5: Carte du monde en projection Robinson

Les touches finales qui font la différence

Un fond qui épouse la projection

Sans fond du tout, une carte projetée devient vite perturbante à lire : impossible de savoir où s’arrête la Terre et où commence le vide.

ggplot(world) +
  geom_sf(fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE) +
  theme_void()
Carte du monde en projection Equal Earth sans aucun fond : les pays flottent sur un arrière-plan blanc uni, sans repère visuel pour situer le contour elliptique de la projection ni distinguer la terre de l'espace vide autour.
Figure 6: Sans fond, difficile de dire où s’arrête le globe.

Le contour elliptique de la projection Equal Earth se devine à peine. Sur toutes les autres cartes de cet article, l’arrière-plan bleu clair comble ce vide en suivant la forme réelle de la projection. On pourrait croire que theme(panel.background = ...) suffit, mais ce n’est pas le cas : ce réglage colorie toujours un rectangle, quelle que soit la projection, puisqu’il agit sur le panel ggplot2 et non sur la carte elle-même.

La solution consiste à tracer le fond comme une géométrie à part entière, projetée dans le même système que les pays : un rectangle couvrant tout le globe en longitude/latitude (-180° à 180°, -90° à 90°), mais construit comme un contour tracé point par point plutôt que 4 coins isolés, pour que la reprojection le courbe correctement plutôt que de garder des côtés droits.

globe <- st_sfc(
  st_polygon(list(cbind(
    c(seq(-180, 180, 1), rep(180, 181), seq(180, -180, -1), rep(-180, 181)),
    c(rep(-90, 361), seq(-90, 90, 1), rep(90, 361), seq(90, -90, -1))
  ))),
  crs = "EPSG:4326" # sytème longitude / latitude
)

Il suffit ensuite de tracer globe en premier, avant les pays.

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = world, fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE) +
  theme_void()

La même carte en projection Equal Earth que précédemment, mais avec un fond bleu clair en forme d'ellipse qui épouse exactement le contour de la projection, dessiné derrière les pays plutôt qu'en rectangle.

Carte en projection Equal Earth, avec les océans colorés pour voir la limite du globe.

coord_sf() projette globe exactement comme il projette world : le rectangle en lat/lon devient l’ellipse, le cercle ou les lobes propres à chaque projection, sans code supplémentaire. Le découpage en pas de 1° (plutôt que 4 coins) est ce qui permet cette courbure : sf ne courbe que les segments qu’on lui fournit, pas les lignes entre deux points éloignés.

Des couleurs sélectionnées avec soin

Une carte muette (juste des contours) est rarement le but final. On veut le plus souvent représenter une variable par pays : c’est la carte choroplèthe. Prenons un exemple qui n’a besoin d’aucune source externe : le nombre de voisins frontaliers de chaque pays, calculé directement à partir des géométries de world.

voisins <- st_touches(world)
world$n_voisins <- lengths(voisins)

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = world, aes(fill = n_voisins), color = "darkgray", linewidth = 0.1) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE) +
  scale_fill_viridis_c(name = "Voisins") +
  theme_void()
Carte choroplèthe du monde en projection Equal Earth, chaque pays coloré selon son nombre de voisins terrestres avec une palette viridis allant du jaune clair (peu ou pas de voisins, notamment les îles) au violet foncé (beaucoup de voisins, comme la Chine ou la Russie).
Figure 7: Nombre de pays voisins, par pays (projection Equal Earth)

st_touches() renvoie, pour chaque pays de world, la liste des autres pays qui partagent au moins un point de frontière avec lui ; lengths() compte simplement la taille de chaque sous-liste. Pas de jointure ni de fichier externe : toute l’information est déjà dans la géométrie des pays.

Qui a le plus de voisins ?

La Chine arrive en tête avec 17 voisins terrestres, suivie par la Russie avec 14. À l’autre extrême, les îles ressortent en clair : 0 voisin, ce que st_touches() calcule correctement sans traitement particulier. À prendre comme illustration plutôt que comme référence géopolitique : le tracé des frontières disputées varie selon les jeux de données, rnaturalearth fait ses propres choix à ce sujet.

Le choix de la palette n’est pas un détail cosmétique : mal choisie, elle peut inverser la lecture des données ou les rendre illisibles pour une partie des lecteurs, comme le détaille mon article une palette de couleurs, ça ne se choisit pas au hasard. Ici, le nombre de voisins est une variable numérique avec un ordre naturel, ce qui appelle une palette séquentielle comme viridis car perceptuellement uniforme et lisible pour les personnes daltoniennes.

Un graticule qui ne se cache pas derrière le fond

Le graticule, c’est la grille de méridiens et de parallèles en filigrane sur la plupart des cartes. coord_sf() peut en calculer un automatiquement via le thème (panel.grid), mais panel.grid se dessine toujours derrière les couches geom_sf() : sur les cartes de cet article, il existe bien mais reste caché sous le fond opaque globe.

La solution consiste à ne pas dépendre du thème du tout, et à tracer le graticule comme une géométrie à part entière avec st_graticule(), au même titre que world ou globe : son ordre d’apparition dans le code contrôle alors réellement ce qui se superpose à quoi.

graticule <- st_graticule(
    lon = seq(-180, 180, 20), 
    lat = seq(-80, 80, 20),
    crs = "EPSG:4326" # sytème longitude / latitude
  )

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = graticule, color = "grey75", linewidth = 0.2) +
  geom_sf(data = world, fill = "#6BAED6", color = "white", linewidth = 0.1) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE) +
  theme_void()
Carte du monde en projection Equal Earth avec une grille de méridiens et parallèles gris clair visible sur l'océan, tracée par-dessus le fond bleu clair mais sous les pays, sans aucune étiquette.
Figure 8: Graticule tracé explicitement avec st_graticule(), entre le fond et les pays

st_graticule() calcule les lignes en EPSG:4326 selon les intervalles fournis via lon et lat. Trois couches empilées dans l’ordre voulu : globe, graticule, world. Ainsi la grille reste visible sur l’océan sans passer devant les pays.

head(graticule)
Simple feature collection with 6 features and 10 fields
Attribute-geometry relationships: constant (3), NAs (7)
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: -180 ymin: -89.91 xmax: -80 ymax: 89.91
Geodetic CRS:  WGS 84 (CRS84)
  degree type   degree_label                       geometry x_start y_start
1   -180    E   "180"*degree LINESTRING (-180 -89.91, -1...    -180  -89.91
2   -160    E "160"*degree*W LINESTRING (-160 -89.91, -1...    -160  -89.91
3   -140    E "140"*degree*W LINESTRING (-140 -89.91, -1...    -140  -89.91
4   -120    E "120"*degree*W LINESTRING (-120 -89.91, -1...    -120  -89.91
5   -100    E "100"*degree*W LINESTRING (-100 -89.91, -1...    -100  -89.91
6    -80    E " 80"*degree*W LINESTRING (-80 -89.91, -80...     -80  -89.91
  x_end y_end angle_start angle_end plot12
1  -180 89.91          90        90   TRUE
2  -160 89.91          90        90   TRUE
3  -140 89.91          90        90   TRUE
4  -120 89.91          90        90   TRUE
5  -100 89.91          90        90   TRUE
6   -80 89.91          90        90   TRUE

Chaque ligne du graticule porte, en plus de sa géométrie, une colonne degree_label et des colonnes x_start/y_start qui donnent un point d’ancrage sur la ligne. Ce sont ces colonnes qu’on va exploiter dans la section suivante pour annoter les axes.

Annoter les axes

Une fois le graticule visible, reste la question des labels. st_graticule() calcule directement des étiquettes prêtes à l’emploi : chaque ligne porte une colonne degree_label déjà formatée avec point cardinal ("120"*degree*W…) en syntaxe plotmath, plus des colonnes x_start/y_start qui donnent une position d’ancrage.

Le graticule repasse ici à un intervalle de 60° plutôt que 20° : à 20°, chaque ligne porterait une étiquette, soit 18 labels par axe, ce qui serait illisible à cette taille de carte.

graticule <- st_graticule(lon = seq(-180, 180, 60), lat = seq(-80, 80, 20), crs = "EPSG:4326")

# Labels de parallèles (type "N"), ancrés sur le bord gauche (méridien -180°) :
# chaque parallèle porte un label côté ouest, qu'on récupère via x_start/y_start
labels_lat <- graticule |>
  filter(type == "N") |>
  st_drop_geometry() |>
  st_as_sf(coords = c("x_start", "y_start"), crs = "EPSG:4326")

# Labels de méridiens (type "E"), replacés sur le bord bas (parallèle -80°)
# plutôt qu'à leur point d'ancrage d'origine, pour aligner tous les labels
# de longitude sur une même ligne en bas de carte
labels_lon <- graticule |>
  filter(type == "E", degree != -180) |>
  st_drop_geometry() |>
  mutate(y_start = -80) |>
  st_as_sf(coords = c("x_start", "y_start"), crs = "EPSG:4326")

ggplot() +
  geom_sf(data = globe, fill = "#E4EFF6", color = NA) +
  geom_sf(data = graticule, color = "grey75", linewidth = 0.2) +
  geom_sf(data = world, fill = "#6BAED6", color = "white", linewidth = 0.1) +
  # nudge_x/nudge_y décalent les labels hors des pays plutôt que dessus
  geom_sf_text(data = labels_lat, aes(label = degree_label), parse = TRUE, size = 2.5, color = "grey40", hjust = 1, nudge_x = -600000) +
  geom_sf_text(data = labels_lon, aes(label = degree_label), parse = TRUE, size = 2.5, color = "grey40", vjust = 1, nudge_y = -600000) +
  coord_sf(crs = "+proj=eqearth", expand = FALSE, clip = "off") +
  theme_void() +
  theme(plot.margin = margin(t = 5, r = 15, b = 15, l = 35, unit = "pt"))
Même carte du monde en projection Equal Earth avec graticule, cette fois annotée : les degrés de latitude sont indiqués sur le bord gauche et les degrés de longitude, avec points cardinaux N/S/E/W, en bas de la carte, en petit texte gris.
Figure 9: Graticule annoté : parallèles à gauche, méridiens en bas

Pour transformer les coordonnées x_start/y_start en un nouveau point à afficher, st_drop_geometry() retire d’abord la géométrie existante (la ligne), pour ne garder qu’un tableau classique ; st_as_sf() en recrée ensuite une, cette fois ponctuelle, à partir des colonnes x_start/y_start désignées via coords. Sans ce passage par un tableau intermédiaire, sf chercherait à combiner la géométrie de ligne déjà présente avec la nouvelle géométrie de point, ce qui échouerait.

Le point d’ancrage du méridien -180° est exclu de labels_lon : sans ce filtre, il tomberait au même endroit projeté que le premier label de labels_lat (80°S), dans le coin bas-gauche de la carte, et les deux labels se superposeraient. parse = TRUE dans geom_sf_text() interprète degree_label comme une expression plotmath pour afficher correctement ° et les points cardinaux, et coord_sf(clip = "off") + plot.margin évitent que hjust/vjust/nudge_x/nudge_y ne poussent les labels hors du cadre de tracé.

Deux détails à ajuster pour le français

degree_label est généré en anglais ("120"*degree*W plutôt que O) : il faut substituer W par O, ou reconstruire le label soi-même depuis la colonne degree (paste0(abs(degree), "°")) pour une version sans point cardinal, plus compacte mais moins explicite sur le sens est/ouest.

Quelle projection pour quel usage ?

ImportantPas de projection universellement “meilleure”

Mercator reste pertinente pour la navigation ou le web (elle préserve les angles à tout niveau de zoom), Equal Earth pour comparer des surfaces entre pays, Robinson pour un rendu généraliste sans enjeu de précision. Le vrai réflexe : identifier la propriété (surface, angle, distance) qui compte pour le message, puis choisir la projection en conséquence.

Envie d’aller plus loin en cartographie avec R ? Dans le prochain article, on sort des sentiers battus avec trois projections plus exotiques : vue satellite, vue polaire et écorce d’orange, avec tous les pièges qu’il a fallu déjouer pour les faire fonctionner.

Les références

Šavrič, Bojan, Tom Patterson, et Bernhard Jenny. 2019. « The Equal Earth Map Projection ». International Journal of Geographical Information Science 33 (3): 454‑65. https://doi.org/10.1080/13658816.2018.1504949.