Le nouveau rideau de fer
Un exemple de carte en 2,5D
Introduction
Ce document montre comment réaliser cette carte de discontinuités en 2,5D (fausse 3D) joliment mise en page avec R. Des versions antérieures de cette carte ont déjà été publiées, dans le Manuel de cartographie (Lambert, Zanin 2016), ou dans Mad Maps (Lambert, Zanin 2019) et ont fait l’objet de billets de blog (Lambert 2017). Ici, nous entendons prouver qu’il est possible de réaliser ce type de carte sans passer par un logiciel DAO (Lambert 2019). Les sources et les références sont précisées à la fin du document.
1 Préparation des données
1.1 Packages
Pour réaliser cette carte, nous nous appuyons sur 5 packages :
- eurostat (Lahti et al. 2017) pour les données,
- rnaturalEarth (South 2017) pour quelques couches d’habillages supplémentaires,
- sf (Pebesma 2018) pour gérer les objets spatiaux,
- mapsf (Giraud 2021) pour l’affichage des différentes couches composant la carte,
- scales (Wickham, Seidel 2020) pour réaliser le rééchelonnage d’une série statistique.
library("sf")
library("mapsf")
library("eurostat")
library("rnaturalearth")
library("scales")1.2 Données géométriques
Pour commencer, nous créons un fond de carte basé sur le système de découpage territoriale NUTS1. Afin de couvrir l’espace européen de manière exhaustive, nous construisons un maillage hybride et homogène combinant différentes versions et niveaux de découpage NUTS.
Le découpage NUTS3 (version 2016) n’est pas disponible pour les pays suivants : Autriche, Belgique, Suisse, Allemagne, Grèce, Pays-Bas, Turquie, Irlande, Islande et Norvège. Nous utiliserons donc le découpage NUTS2 pour ces territoires. Pour des raisons de disponibilité des données post-Brexit, nous l’utiliserons dans sa version 2013 pour le Royaume-Uni.
Récupération des découpages NUTS de 2016 et construction d’un fond de carte hybride NUTS2/3.
# NUTS 2016
nuts2016 <- get_eurostat_geospatial(
output_class = "sf",
resolution = "20",
nuts_level = "all",
year = "2016")
# Couche géographique des NUTS3 et NUTS2 pour 2016
nuts2016_3 <- nuts2016[nuts2016$LEVL_CODE == 3, ]
nuts2016_2 <- nuts2016[nuts2016$LEVL_CODE == 2, ]
# Liste des pays (code ISO) sans découpage NUTS3
N2 <- c("AT", "BE", "CH", "DE", "EL", "NL", "UK", "TR", "IE", "IS", "NO")
# Couche géographique NUTS2/3 en fonction de la liste de pays N2
nuts <- rbind(nuts2016_2[nuts2016_2$CNTR_CODE %in% N2, ],
nuts2016_3[!nuts2016_3$CNTR_CODE %in% N2, ])
# Suppression des départements d'Outre-mer français
nuts <- nuts[!nuts$id %in% c("FRY10", "FRY20", "FRY30", "FRY40", "FRY50"), ]
# Suppression des NUTS 2016 du Royaume-Uni
nuts <- nuts[nuts$CNTR_CODE != "UK", ]
# Sélection et renommage de colonnes
nuts <- nuts[,c("id","NUTS_NAME","geometry")]
colnames(nuts) <- c("id","name","geometry")Fusion avec le découpage NUTS2 2013 du Royaume-Uni.
# Récupération du découpage NUTS2 2013
nuts2013 <- get_eurostat_geospatial(
output_class = "sf",
resolution = "20",
nuts_level = "2",
year = "2013")
# Sélection des NUTS2 du Royaume-Uni
uk <- nuts2013[nuts2013$CNTR_CODE == "UK",]
# Sélection et renommage de colonnes
uk <- uk[,c("id","NUTS_NAME","geometry")]
colnames(uk) <- c("id","name","geometry")
# Fusion des NUTS2 2013 du Royaume-Uni avec le fond de carte NUTS2/3 2016
nuts <- rbind(nuts, uk)Le fond de carte (ou couche géographique) créé est donc une fusion des découpages territoriaux suivants :
1.3 Données statistiques
Grâce au package eurostat, nous récupérons des données de PIB par habitant en 2016.
# Récupération des données (PIB/hab)
var <- "nama_10r_3gdp"
gdpinh <- get_eurostat(var, time_format = "num")
# Sélection de l'unité de mesure et de la date
gdpinh <- gdpinh[gdpinh$unit == "EUR_HAB",]
gdpinh <- gdpinh[gdpinh$time == 2016, c("geo","values")]
colnames(gdpinh) <- c("id","GDPINH_2016")Les données pour le Royaume-Uni ne sont plus disponibles depuis le Brexit. Elles sont également manquantes pour quelques unités territoriales. Nous combinons donc les données issues d’eurostat avec des estimations issues de la base de données ESPON (fichier missing.csv, téléchargeable ici).
Nous combinons les deux jeux de données pour avoir une couverture exhaustive de l’espace à représenter.
# Import des données
missing <- read.csv("data/missing.csv")
# Fusion des deux tableaux
gdpinh <- rbind(gdpinh, missing)Pour des questions de reproductibilité, nous sauvegardons les données complétées dans le répertoire local data.
write.csv(gdpinh, "data/gdpinh.csv")Les estimations issues de la base de données ESPON (“missing”’“) ainsi que le tableau de données complété (”gdpinh”) sont récupérables à ce lien :
Nous effectuons ensuite une jointure entre les données et les géométries.
nuts <- merge(
x = nuts,
y = gdpinh,
by = "id",
all.x = TRUE)2 Modèle de mise en page
2.1 Couches d’habillage
Pour habiller la carte, nous utilisons des couches géographiques mises à disposition par le package rnaturalearth.
# Surface terrestre, à petite échelle
land <- ne_download(
scale = 110,
type = "land",
category = "physical",
returnclass = "sf")Il est possible d’afficher la couche d’habillage avec la fonction mf_map du package mapsf.
mf_map(land, border = NA, col = "#6eb1db")# Mers et océans, à petite échelle
ocean <- ne_download(
scale = 110,
type = "ocean",
category = "physical",
returnclass = "sf")mf_map(ocean, border = NA, col = "#6eb1db")Puis, nous créons une couche de graticules avec la fonction st_graticule du package sf.
graticule = st_graticule(
crs = st_crs(4326),
ndiscr = 100,
lon = seq(-180, 180, by = 2),
lat = seq(-90, 90, by = 1),
margin = 0.01)mf_map(graticule, col = "#6eb1db")2.2 Projection orthographique
Pour donner un effet de rotondité et permettre une représentation en 2,5D, on opte pour une projection orthographique centrée sur l’Afrique. Pour éviter tout problème dans l’opération de projection (bug, artefacts, etc.), nous définissons au préalable un rectangle qui servira à découper les différentes couches géographiques.
# Construction du rectangle (bounding box)
bb <- st_as_sfc(x = st_bbox(c(xmin = -50 , xmax = 70, ymin = 20, ymax = 80),
crs = st_crs(4326)))Nous pouvons ensuite découper toutes les couches d’habillage en fonction du rectangle préalablement créé. Pour que cette opération se passe bien, nous utilisons la fonction sf_use_s2 du package sf en paramétrant l’argument use_s2 en FALSE pour faire comme si les latitudes et longitudes étaient des coordonnées euclidiennes. Une fois les intersections effectuées, nous réexécutons cette fonction en paramétrant l’argument use_s2 en TRUE.
sf_use_s2(FALSE)
ocean <- st_intersection(ocean, bb)
ocean <- st_segmentize(ocean, 100)
land <- st_intersection(land, bb)
land <- st_segmentize(land, 100)
graticule <- st_intersection(graticule, bb)
sf_use_s2(TRUE)Nous projetons ensuite toutes les couches géographiques créées dans une projection orthographique centrée sur l’Afrique (10° de latitude nord et 15° de longitude).
# Définition de la projection en format "proj-strings" (PROJ.4)
ortho <- "+proj=ortho +lat_0=-10 +lon_0=15 +x_0=0 +y_0=0
+ellps=WGS84 +units=m +no_defs"
# Projection des couches géographiques
ocean <- st_transform(ocean, ortho)
land <- st_transform(land, ortho)
graticule <- st_transform(graticule, ortho)
nuts <- st_transform(nuts, ortho)Affichons l’ensemble des couches recadrées et projetées pour visualiser le résultat :
par(mar = c(0, 0, 0, 0), mfrow = c(2, 2))
mf_map(land, col = "#6eb1db", border = NA)
mf_title("land", bg = "#6eb1db")
mf_map(ocean, col = "#6eb1db", border = NA)
mf_title("ocean", bg = "#6eb1db")
mf_map(graticule, col = "#6eb1db", lwd = 1)
mf_title("graticule", bg = "#6eb1db")
mf_map(nuts, col = "#6eb1db", border = "white", lwd = 0.2)
mf_title("NUTS2/3", bg = "#6eb1db")2.3 Effet d’ombrage
On peut générer un effet d’ombrage en agrégeant les régions NUTS en un seul polygone, puis en affichant plusieurs fois ce polygone avec de la transparence et un léger décalage dans l’espace. Ci-dessous, un exemple sur la France métropolitaine :
# Union de l'ensemble des entités de la couche géographique "nuts"
fr <- st_union(nuts[substr(nuts$id, 1, 2) == "FR", ])
# Paramétrage des marges de la fenêtre graphique
par(mar = c(0, 0, 0, 0))
# Affichage multiple de la couche, en appliquant à chaque fois un léger décalage
mf_map(fr + c(5000,-5000), col = "#827e6c40", border = NA)
mf_map(fr + c(10000,-10000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(15000,-15000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(20000,-20000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(25000,-25000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr, col = "#6eb1db", border = "white", lwd = 0.1, add = TRUE)2.4 Fonction template()
A partir des différentes couches géographiques recadrées et projetées, nous allons à présent réaliser un modèle de mise en page cartographique.
Pour cela, nous définissons précisément l’emprise de la carte dans le système de coordonnées de la projection. Les coordonnées étant en mètres, nous utilisons un facteur 100 000 (variable k) qui nous permet de manipuler de petits chiffres avec un niveau de précision satisfaisant. Nous utiliserons également ce facteur k pour positionner les différents éléments d’habillage de la carte.
# Création de la variable k
k <- 100000
# Coordonnées de l'emprise * k
extent <- c(-20, 42, 24.5, 63) * k
# Construction de l'emprise (objet sfc)
bb <- st_as_sfc(x= st_bbox(c(xmin = extent[1],
xmax = extent[3],
ymin = extent[2],
ymax = extent[4]),
crs = st_crs(nuts)))On crée ensuite la fonction template() qui contiendra tous les éléments de la mise en page souhaitée. Ce modèle est construit à l’aide de fonctions du package mapsf.
# Création de la fonction
template = function(file) {
# Création d'un thème
theme <- mf_theme(x = "default",
bg = "#f2efe6",
fg = "#f2efe6",
mar = c(0, 0, 0, 0),
tab = TRUE,
pos = "left",
inner = FALSE,
line = 2,
cex = 1.9,
font = 3)
# Paramétrage de l'export de la carte en format png
mf_export(bb,
export = "png",
width = 2000,
filename = file,
res = 150,
theme = theme,
expandBB = c(-.02, 0, 0.05, 0))
# Affichage de la couche 'ocean'
mf_map(ocean, col = "#9acbe3", border = "#9acbe3", lwd = 5, add = TRUE)
# Affichage de la couche 'graticule'
mf_map(graticule, col = "#FFFFFF80", lwd = 1.5, lty = 3, add = TRUE)
# Affichage d'un effet d'ombrage pour la couche 'nuts'
ue <- st_union(nuts)
mf_map(ue + c(5000,-5000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(ue + c(10000,-10000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(ue + c(15000,-15000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(ue + c(20000,-20000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(ue + c(25000,-25000), col = "#827e6c40", border = NA, add = TRUE)
# Affichage de la couche 'nuts'
mf_map(nuts, col = "#dbccb6", border = "white", lwd = 0.3, add = TRUE)
# Affichage d'un bandeau bleu transparent sous le titre
rect(-22 * k,
41.3 * k,
28 * k,
41.3 * k + 250000,
border = NA, col = "#2369bd80")
# Affichage du titre
text(x = -21.5 * k,
y = 42.4 * k,
labels = "30 YEARS LATER, THE IRON CURTAIN IS STILL VISIBLE",
cex = 2.14,
pos = 4,
font = 2,
col = "#FFFFFF80")
# Affichage des sources
text(x = -21.75 * k,
y = 44.25 * k,
labels = "Map 100% designed in the R language by Nicolas Lambert, 2019.
Code source available here: https://github.com/neocarto/ironcurtain). Data sources: Eurostat & Natural Earth, 2019",
cex = 0.5,
pos = 4,
font = 1,
col = "#3f4654")
# Affichage d'une échelle
mf_scale(size = 700,
lwd = 0.6,
cex = 0.5,
col = "#3f4654",
pos = c(19 * k, y = 44 * k))
}La fonction d’export mf_export() est incluse dans la fonction template(). Le résultat de cette fonction est donc une sortie graphique au format png, directement enregistrée sur votre machine. Ainsi, l’utilisation de cette fonction doit obligatoirement être couplée avec la fonction dev.off()qui permet de fermer le périphérique graphique et de lancer l’enregistrement de son contenu dans un fichier.
template() permet de parfaitement contrôler les marges et le dimensionnement de la carte produite, quelle que soit la taille de sa fenêtre graphique.
Et voilà le résultat :-)
# Indiquez le nom du fichier png créé
# Utilisez dev.off() pour clôturer l'enregistrement du fichier
template("figures/fig1.png")
dev.off()