## Installer les packages nécessaires
pkg <- c("pastclim", "sf", "lwgeom", "terra")
pkg_missing <- !(pkg %in% installed.packages())
install.packages(pkg[pkg_missing])Le dernier maximum glaciaire, dont le pic de froid est daté d’environ 21 ka BP, correspond à une extension extrême des calottes de glace et à un niveau des mers minimal.
On cherche ici à réaliser trois cartes permettant de visualiser les surfaces émergées et l’englacement au cours de cette période : une carte du monde, une carte de l’Arctique et une carte de l’Europe. Le tout avec R (R Core Team 2026) et des données ouvertes.
Pour cela, il va être nécessaire d’utiliser des projections cartographiques adéquates en fonction des zones d’intérêt et de choisir des teintes hypsométriques adaptées pour la visualisation.
Les données d’élévation utilisées sont issues du modèle d’élévation global ETOPO (NOAA National Centers for Environmental Information 2022). La base de données GLACIMONTIS (Lima et al. 2025) fournit les limites des calottes glaciaires et des glaciers de montagne au cours du dernier maximum glaciaire. Il s’agit d’une compilation de l’ensemble des reconstructions publiées (Lima et al. 2026). Enfin, on va avoir recourt aux données issues de la reconstruction paléoclimatique CHELSA (Karger et al. 2020, 2023), afin de disposer de valeurs de précipitations annuelles au cours du dernier maximum glaciaire.
Quatre packages R sont nécessaires : sf (Pebesma et Bivand 2023; Pebesma 2018) et lwgeom (Pebesma 2026) pour le traitement des données vectorielles, terra (Hijmans et al. 2026) pour le traitement des données raster, ainsi que pastclim (Leonardi et al. 2023) pour l’accès aux données paléoclimatiques.
Préparation des données
Élévation et bathymétrie
Les données d’élévation sont téléchargeables sur le site web des National Centers for Environmental Information (NCEI). Afin de garder un temps de calcul raisonnable, on utilise ici les données avec une résolution spatiale de 60 secondes d’arc (soit environ 2 km à l’équateur).
## Télécharger les données d'élévation depuis le site du NOAA
url <- "https://www.ngdc.noaa.gov/mgg/global/relief/ETOPO2022/data/60s/60s_bed_elev_gtif/ETOPO_2022_v1_60s_N90W180_bed.tif"
download.file(url, destfile = "ETOPO_2022_v1_60s_N90W180_bed.tif")
## Lire les données
etopo <- terra::rast("ETOPO_2022_v1_60s_N90W180_bed.tif")
## Réduire la résolution spatiale
## (optionnel : permet de faciliter les calculs par la suite)
etopo_small <- terra::aggregate(etopo, fact = 2)Ces données utilisent un système de coordonnées géographiques (WGS 84, voir terra::crs(etopo)) qui localise des points sur une surface sphérique à l’aide d’unités angulaires (latitude et longitude, exprimées en degrées). Il est donc indispensable de projeter les données au préalable, afin de les représenter dans un plan en utilisant des unités linéaires (mètres).
Le choix de la projection cartographique est un compromis (toute projection entraîne des distorsions et aucune ne préserve à la fois aires, distances et angles) qui dépend de l’objectif fixé (voir Royé 2026 pour un guide pratique des principales projections avec R).
La projection Equal Earth (ESRI:54035) a été définie par Bojan Šavrič, Tom Patterson et Bernhard Jenny (2018) afin de préserver les surfaces relatives des continents :
We created it to provide a visually pleasing alternative to the Gall-Peters projection, which some schools and organizations have adopted out of concern for fairness-they need a world map showing continents and countries at their true sizes relative to each other. — Šavrič et al. (2019)
Il s’agit d’une projection équivalente (elle conserve les aires), elle est donc particulièrement adaptée à une carte globale où l’impact visuel d’une région doit refléter son aire (Royé 2026).
## Projection du raster
etopo_54035 <- terra::project(etopo_small, "ESRI:54035")Les données raster sont par nature des matrices rectangulaires. Pour obtenir les bords arrondis caractéristiques de la projection Equal Earth, il est donc nécessaire de masquer les cellules hors des limites de la projection.
## Définir de l'étendue souhaitée en précisant la latitude et la longitude
## (ensemble du globe)
rect <- matrix(
data = c(-180, -90, -180, 90, 180, 90, 180, -90, -180, -90),
ncol = 2,
byrow = TRUE
)
## Créer un polygone rectangulaire correspondant à l'étendue,
## puis ajouter des sommets au polygone le long des arrêtes,
## enfin, spécifier le système de coordonnées géographiques
bbox_lon_lat <- sf::st_polygon(list(rect)) |>
sf::st_segmentize(dfMaxLength = 1) |> # Nécessite le package lwgeom
sf::st_sfc() |>
sf::st_set_crs(4326) # WGS 84
## Projeter le polygone dans le système de coordonnées Equal Earth (ESRI:54035)
bbox_equal_earth <- sf::st_transform(bbox_lon_lat, crs = "ESRI:54035")
## Masquer les cellules hors de l'emprise de la projection
etopo_54035_mask <- terra::mask(etopo_54035, terra::vect(bbox_equal_earth))Il ne reste plus qu’à abaisser le niveau de la mer par rapport à l’actuel, qui se trouve 120 mètres plus bas au cours du dernier maximum glaciaire.
## Abaisser le niveau de la mer
etopo_120 <- etopo_54035_mask + 120
## Extraire les altitudes (surfaces émergées)
## (les élévations négatives sont transformées en valeurs manquantes)
earth_54035 <- terra::classify(
x = etopo_120,
rcl = matrix(c(-Inf, 0, NA), nrow = 1, byrow = TRUE)
)
## Extraire les profondeurs (surfaces immergées)
## (les élévations positives sont transformées en valeurs manquantes)
water_54035 <- terra::classify(
x = etopo_120,
rcl = matrix(c(0, Inf, NA), nrow = 1, byrow = TRUE),
include.lowest = FALSE
)Calottes glaciaires et glaciers de montagne
La base de données GLACIMONTIS est archivée sur Zenodo.
## Télécharger l'archive
url <- "https://zenodo.org/records/15600659/files/GLACIMONTIS_GDB.zip"
download.file(url, destfile = "GLACIMONTIS_GDB.zip")
## Décompresser la base de données
utils::unzip("GLACIMONTIS_GDB.zip", exdir = ".")Cette base de données est distribuée au format Geodatabase, dont on peut extraire les deux couches d’intérêt : les calottes glaciaires et les glaciers de montagne. Deux jeux de données sont disponibles pour les glaciers : un ensemble (Empirically Reconstructed Paleoglaciers) regroupant toutes les reconstructions et un ensemble de données validées et prêtes à l’emploi (Filtered Reconstructed Paleoglaciers). Ce dernier ensemble fusionne les reconstructions afin de créer des masques glaciaires généralisés (Lima et al. 2026).
## Lire les données
## Paléoglaciers
paleoglaciers <- sf::st_read(
dsn = "GLACIMONTIS.gdb",
layer = "FilteredReconstructedPaleoglaciers",
quiet = TRUE
)
## Calottes glaciaires
icesheets <- sf::st_read(
dsn = "GLACIMONTIS.gdb",
layer = "LGMReconstructedIceSheets",
quiet = TRUE
)La base de données GLACIMONTIS est une compilation de sources hétérogènes, il est donc préférable de vérifier que toutes les géométries sont valides et de nettoyer les données le cas échéant.
## Vérifier la géométrie des calottes glaciaires
sf::st_is_valid(icesheets, NA_on_exception = FALSE, reason = TRUE)[1] "Loop 5: Edge 18 has duplicate vertex with edge 23"
[2] "Valid Geometry"
[3] "Valid Geometry"
[4] "Edge 34057 crosses edge 34060"
[5] "Valid Geometry"
[6] "Valid Geometry"
Il est possible de réparer automatiquement les géométries invalides (sommets dupliqués, auto-intersections…) avec la fonction st_make_valid(), mais l’approche est un peu brutale et peut parfois aboutir à des résultats inattendus (il serait préférable d’inspecter et d’éditer les données manuellement). Par commodité, on se contente ici d’ignorer les géométries invalides.
## Vérifier la géométrie des glaciers
sf::st_is_valid(paleoglaciers, NA_on_exception = FALSE, reason = TRUE)Error:
! IllegalArgumentException: Invalid number of points in LinearRing found 2 - must be 0 or >= 3
Dans le cas des glaciers, le message d’erreur retourné par st_is_valid() indique que la géométrie d’au moins un polygone est corrompue (un polygone ne peux pas avoir moins de trois sommets). Ce problème est plus complexe, car l’ensemble des données est regroupé au sein d’une unique entité multi-parties contenant les différents polygones. Il est donc nécessaire d’identifier, puis de supprimer les polygones en question au sein de l’entité.
## Extraire la colonne de géométrie et selectionner l'unique première entité
poly_geom <- sf::st_geometry(paleoglaciers)[[1]]
## Extraire la matrice de coordonnées de tous les sommets des polygones
poly_coords <- sf::st_coordinates(poly_geom)
## Compter le nombre de sommets par polygone
## (pour chaque sommet, la colonne L2 contient l'identifiant du polygone)
poly_points <- table(poly_coords[, "L2"])
## Identifier les polygones avec moins de 3 sommets
poly_corrupt <- which(poly_points < 3)
## Supprimer les polygones avec moins de 3 sommets
poly_clean <- poly_geom[-poly_corrupt]
## Reconstruire une entité MUTLIPOLYGON avec le CRS initial
glaciers <- poly_clean |>
sf::st_multipolygon() |>
sf::st_sfc() |>
sf::st_set_crs(sf::st_crs(paleoglaciers)) |>
sf::st_as_sf()Il reste ensuite à projeter les données dans le système de coordonnées Equal Earth (voir sf::st_crs(glaciers) et sf::st_crs(icesheets)).
## Reprojection des données vectorielles (Equal Earth)
glaciers_54035 <- sf::st_transform(glaciers, crs = "ESRI:54035")
ice_54035 <- sf::st_transform(icesheets, crs = "ESRI:54035")Précipitations
Les données paléoclimatiques sont accessibles via le portail de données PaleoClim (Brown et al. 2018). On utilise ici les données avec une résolution spatiale de 2,5 minutes d’arc (soit environ 5 km à l’équateur).
## Définir le répertoire de destination des données
pastclim::set_data_path(ask = FALSE)
## Télécharger les données PaleoClim
pastclim::download_dataset(
dataset = "paleoclim_1.0_2.5m",
bio_variables = "bio12" # Précipitations annuelles
)
## Extraire les précipitations annuelles
climate <- pastclim::region_slice(
time_bp = -21000, # Dernier maximum glaciaire
bio_variables = "bio12",
dataset = "paleoclim_1.0_2.5m"
)À nouveau, il est nécessaire de projeter les données (voir terra::crs(climate)). On utilise les données d’élévation comme référence : les données projetées auront ainsi la même résolution, la même emprise géographique et le même système de coordonnées (Equal Earth).
## Projeter le raster
climate_54035 <- terra::project(climate, etopo_54035)
## Masquer les cellules hors de l'emprise de la projection
paleoclim_54035 <- terra::mask(climate_54035, terra::vect(bbox_equal_earth))Visualisation
Monde
Pour visualiser les données d’élévation, on réalise une carte hypsométrique : on utilise des aplats de couleur pour indiquer les différentes tranches d’altitude. Pour réaliser ce découpage, on commence par explorer la distribution des données à l’aide d’un histogramme (l’argument breaks de la fonction hist() permet de spécifier le nombre de classes souhaitées). Les classes de l’histogramme peuvent ensuite être utilisées pour discrétiser les données d’élévation.
## Distribution des valeurs d'élévation
## (10 classes de même amplitude)
world_hist <- terra::hist(x = earth_54035, breaks = 10, plot = FALSE)
## Extraire les bornes des classes d'élévation issues de l'histogramme
world_breaks <- world_hist$breaksEnfin, on associe une couleur à chaque classe d’élévation en utilisant une palette adaptée (ici une palette classique : du vert pour les basses terres, au brun et au gris pour les hautes terres et les sommets).
## Palette de couleurs (vert -> brun -> gris)
imhof_palette <- c("#73b273", "#9ad49a", "#f1ecaa",
"#eec68d", "#e09b81", "#dbdbdb")
## Créer une fonction d'interpolation continue
imhof_ramp <- colorRamp(imhof_palette)
## Interpoler les couleurs pour chaque classe d'élévation
world_rgb <- imhof_ramp(world_breaks / max(world_breaks) - min(world_breaks))
world_colors <- rgb(world_rgb, maxColorValue = 255)Il ne reste plus qu’à visualiser l’ensemble des données (en précisant les bornes des classes d’élévations et les couleurs associés ; figure 1).
## Visualiser les terres émergées
terra::plot(
x = earth_54035,
breaks = world_breaks, # Bornes des classes d'élévation
col = world_colors, # Palette de couleur
colNA = "white",
legend = FALSE,
axes = FALSE,
maxcell = 4000000
)
## Ajouter les océans
terra::plot(
x = water_54035,
col = "#c6dbef",
legend = FALSE,
add = TRUE
)
## Ajouter les glaciers
terra::polys(
x = glaciers_54035,
col = "white",
border = NA
)
## Ajouter les calottes glaciaires
terra::polys(
x = ice_54035,
col = "white",
border = "#00000040"
)
## Ajouter un cadre
terra::polys(x = terra::vect(bbox_equal_earth))Si la figure 1 apparaît satisfaisante au premier abord, elle pose en réalite un problème de sémiologie graphique. L’échelle de couleur appliquée aux données d’élévation (vert pour les élévations les plus faibles, brun à gris pour les valeurs les plus élevées) peut en effet donner l’impression que de grandes surfaces du globe sont couvertes de végétation et que les reliefs sont, au contraire, complètement dénudés. C’est un problème fréquent en cartographie qui conduit le lecteur à confondre l’hypsométrie avec la couverture du sol.
Pour y remédier, une approche possible est d’utiliser les données de précipitation pour colorer les terres émergées (les précipitations sont alors utilisées comme proxy du couvert végétal ; figure 2 (a)) et d’y ajouter des ombrages calculés à partir des valeurs d’élévation pour marquer le relief (figure 2 (b)). Cette technique, dite teintes hypsométriques croisées (cross-blended hypsometric tints), est présentée par Tom Patterson sur son blog (Patterson s. d.) et discutée dans la revue Cartographic Perspectives (Patterson et Jenny 2011).
Le calcul des ombrages nécessite de connaître deux caractéristiques du terrain : la déclivité (argument v = "slope" de la fonction terrain()) et l’orientation (v = "aspect") des pentes (voir Royé 2022 pour plus de détails, ou l’utilisation du package rayshader).
## Facteur d'exagération verticale pour accentuer la visualisation
z <- 40
## Calcul des caractéristiques du terrain
slope_54035 <- terra::terrain(earth_54035 * z, v = "slope", unit = "radians")
aspect_54035 <- terra::terrain(earth_54035 * z, v = "aspect", unit = "radians")
## Calcul des ombrages
## (mutlidirectionnel : 225, 270, 315 et 360 degrés)
hillshade_54035 <- terra::shade(
slope = slope_54035,
aspect = aspect_54035,
angle = 45, # Angle d'élévation de la source de lumière
direction = c(225, 270, 315, 360) # Azimut de la source de lumière
)
hillshade_54035 <- Reduce(mean, hillshade_54035)On obtient ainsi une représentation nettement plus vraissemblable des conditions du dernier maximum glaciaire (figure 2 (c)).
maxcell de la fonction plot() du package terra permet de spécifier le nombre maximum de cellules à afficher. La valeur par défaut (500000) permet un affichage rapide du graphique, mais il peut être nécessaire d’augmenter cette valeur pour un meilleur rendu.## Palette de couleurs pour les précipitations
climate_ramp <- c("#FFE5AD", "#B4A022", "#61790A", "#245231", "#003B47")
## Interpoler 255 couleurs
climate_color <- colorRampPalette(climate_ramp)(255)
## Visualiser les précipitations annuelles
terra::plot(
x = paleoclim_54035,
col = climate_color,
legend = FALSE,
axes = FALSE,
maxcell = 4000000 # Meilleur rendu
)
terra::polys(x = terra::vect(bbox_equal_earth))
## Visualiser les ombrages
terra::plot(
x = hillshade_54035,
col = grey(1:100 / 100),
legend = FALSE,
axes = FALSE,
maxcell = 4000000
)
terra::polys(x = terra::vect(bbox_equal_earth))
## Combiner précipitations et ombrages
terra::plot(
x = hillshade_54035,
col = grey(1:100 / 100),
legend = FALSE,
axes = FALSE,
maxcell = 4000000
)
terra::plot(
x = paleoclim_54035,
col = adjustcolor(climate_color, alpha.f = 0.5),
legend = FALSE,
add = TRUE,
maxcell = 4000000
)
## Océans + glaciers + calottes glaciaires
terra::plot(x = water_54035, col = "#c6dbef", legend = FALSE, add = TRUE)
terra::polys(x = glaciers_54035, col = "white", border = NA)
terra::polys(x = ice_54035, col = "white", border = "#00000040")
terra::polys(x = terra::vect(bbox_equal_earth)) # CadreArctique
Afin de visualiser l’Arctique, on utilise la projection projection NSIDC Sea Ice Polar Stereographic (EPSG:3413). Il s’agit d’une projection projection azimutale polaire.
Dans le cas des données raster, il est possible de spécifier la projection cible de la fonction project() du package terra soit à l’aide d’un code EPSG ("EPSG:3413"), soit à l’aide d’un objet dont les caractéristiques (résolution, emprise et système de coordonnées) sont celles que l’on souhaite obtenir. Ici on commence par créer un squelette en coordonnées géographiques (lattitude, longitude ; WGS 84) afin de définir une emprise qui couvre toute la surface du globe au Nord du 45e parallèle. On projete ensuite le squelette dans le système de coordonnées cartographique souhaité (NSIDC Sea Ice Polar Stereographic) avant de l’utiliser pour reprojeter les données (élévations, ombrages et précipitations).
## Créer un squelette pour la projection
## L'emprise est définie en précisant la latitude et la longitude
template_4326 <- terra::rast(
nrows = 135,
ncols = 1080,
xmin = -180,
xmax = 180,
ymin = 45, # 45e parallèle nord
ymax = 90,
crs = "EPSG:4326" # WGS 84
)
## Projeter le squelette (NSIDC Sea Ice Polar Stereographic)
template_3414 <- terra::project(template_4326, "EPSG:3413")
## Reprojection des données raster
hillshade_3413 <- terra::project(hillshade_54035, template_3414)
paleoclim_3413 <- terra::project(paleoclim_54035, template_3414)
water_3413 <- terra::project(water_54035, template_3414)
## Reprojection des données vectorielles
glaciers_3413 <- sf::st_transform(glaciers_54035, crs = "EPSG:3413")
## Supprimer tout ce qui se trouve au Sud du 45e parallèle au préalable
bbox_45N <- c(xmin = -180, xmax = 180, ymin = 45, ymax = 90) |>
sf::st_bbox(crs = "EPSG:4326") |>
sf::st_transform(crs = "ESRI:54035")
ice_3413 <- ice_54035 |>
sf::st_crop(bbox_45N) |>
sf::st_transform(crs = "EPSG:3413")On peut ensuite visualiser les données (figure 3).
## Précipitations et ombrages
terra::plot(
x = hillshade_3413,
col = grey(1:100 / 100),
legend = FALSE,
axes = FALSE,
box = TRUE,
maxcell = 4000000
)
terra::plot(
x = paleoclim_3413,
col = adjustcolor(climate_color, alpha.f = 0.5),
legend = FALSE,
add = TRUE,
maxcell = 4000000
)
## Océans + glaciers + calottes glaciaires
terra::plot(x = water_3413, col = "#c6dbef", legend = FALSE, add = TRUE)
terra::polys(x = glaciers_3413, col = "white", border = NA)
terra::polys(x = ice_3413, col = "white", border = "#00000040")Europe
Pour l’Europe, on applique la même procédure en utilisant la projection Lambert Azimuthal Equal Area centrée sur l’Europe1 (LAEA Europe, EPSG:3035).
## Créer un squelette pour la projection
template_3035 <- terra::rast(
nrows = 1800,
ncols = 2000,
xmin = 2400000,
xmax = 5400000,
ymin = 1400000,
ymax = 4100000,
crs = "EPSG:3035" # LAEA Europe
)
## Reprojection des données raster
hillshade_3035 <- terra::project(hillshade_54035, template_3035)
paleoclim_3035 <- terra::project(paleoclim_54035, template_3035)
water_3035 <- terra::project(water_54035, template_3035)
## Reprojection des données vectorielles
glaciers_3035 <- sf::st_transform(glaciers_54035, crs = "EPSG:3035")
ice_3035 <- sf::st_transform(ice_54035, crs = "EPSG:3035")On peut ensuite visualiser les données (figure 4).
## Précipitations et ombrages
terra::plot(
x = hillshade_3035,
col = grey(1:100 / 100),
legend = FALSE,
axes = FALSE,
box = TRUE,
maxcell = 4000000
)
terra::plot(
x = paleoclim_3035,
col = adjustcolor(climate_color, alpha.f = 0.5),
legend = FALSE,
add = TRUE,
maxcell = 4000000
)
## Océans + glaciers + calottes glaciaires
terra::plot(x = water_3035, col = "#c6dbef", legend = FALSE, add = TRUE)
terra::polys(x = glaciers_3035, col = "white", border = NA)
terra::polys(x = ice_3035, col = "white", border = "#00000040")
## Barre d'échelle
terra::sbar(
d = 500000,
xy = "topleft",
type = "bar",
divs = 4,
cex = 0.8,
below = "km",
scaleby = 1000
)Session
R version 4.6.1 (2026-06-24)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.4 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
locale:
[1] LC_CTYPE=fr_FR.UTF-8 LC_NUMERIC=C
[3] LC_TIME=fr_FR.UTF-8 LC_COLLATE=fr_FR.UTF-8
[5] LC_MONETARY=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8
[7] LC_PAPER=fr_FR.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Paris
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] s2_1.1.11 digest_0.6.39 codetools_0.2-20 fastmap_1.2.0
[5] xfun_0.60 e1071_1.7-17 KernSmooth_2.23-27 knitr_1.51
[9] htmltools_0.5.9 rmarkdown_2.31 wk_0.9.5 classInt_0.4-11
[13] sf_1.1-2 cli_3.6.6 grid_4.6.1 DBI_1.3.0
[17] terra_1.9-46 proxy_0.4-29 class_7.3-24 compiler_4.6.1
[21] rstudioapi_0.19.0 tools_4.6.1 evaluate_1.0.5 Rcpp_1.1.2
[25] yaml_2.3.12 otel_0.2.0 rlang_1.3.0 jsonlite_2.0.0
[29] htmlwidgets_1.6.4 units_1.0-1
Les références
Notes de bas de page
Système de référence spatiale officiel en Europe.↩︎
Réutilisation
Citation
@online{frerebeau2026,
author = {Frerebeau, N.},
title = {Le monde au cours du dernier maximum glaciaire},
date = {2026-08-31},
url = {https://carnets.archeosciences-bordeaux.fr/datascience/20260901-late-glacial-maximum/},
langid = {fr-FR}
}





