Cartographie épidémiologique avec R : Étude de cas sur une épidémie de méningite au Sénégal

Author

Christian DARI

Published

December 12, 2025

Introduction

Contexte

La géomatique épidémiologique est une discipline essentielle en santé publique qui combine les outils de la géomatique (Systèmes d’Information Géographique - SIG) avec les méthodes épidémiologiques. Elle permet de visualiser, analyser et interpréter la distribution spatiale des maladies, facilitant ainsi la prise de décision en santé publique.

Dans le cadre de ce webinaire organisé en partenariat avec R Users Community, nous présentons une initiation pratique à la création de cartes épidémiologiques avec R, en reproduisant une analyse réalisée initialement avec QGIS.

Présentation du cas d’étude

Nous travaillerons sur un jeu de données fictif simulant une épidémie de méningite au Sénégal. L’objectif est de démontrer comment R peut remplacer ou compléter les logiciels SIG traditionnels pour la production de cartes épidémiologiques professionnelles.

Figure : Carte de référence produite avec QGIS montrant la répartition des cas et décès de méningite au Sénégal (données fictives).

Objectifs pédagogiques

  1. Maîtriser le chargement et la manipulation de données géospatiales avec R
  2. Apprendre à créer des cartes thématiques épidémiologiques
  3. Reproduire une carte professionnelle initialement créée avec QGIS
  4. Automatiser la production cartographique pour des analyses reproductibles

Méthodologie

Environnement logiciel et packages

Nous utiliserons R version 4.5.2 pour la cartographie et Positron comme IDE avec les packages suivants :

# Chargement des packages
library(sf)
library(readxl)
library(ggplot2)
library(ggspatial)
library(cowplot)
library(scales)
library(dplyr)
library(questionr)

Structure des données

Données géographiques

  • Couche vectorielle des départements sénégalais (format Shapefile)
  • Couche vectorielle des pays d’Afrique
  • Système de coordonnées : WGS 84 / UTM zone 31N (EPSG:32631)

Données épidémiologiques

  • Cas déclarés par département
  • Décès par département
  • Population départementale (pour calcul des taux)

Analyse et production cartographique

1. Chargement et préparation des données

# 1.1. Chargement des données géographiques
departements <- st_read("DATA/SN/ADMIN2.SHP", quiet = TRUE)

# 1.2. Chargement des données épidémiologiques
BD <- read_xlsx("Exo jointure.xlsx")
BD <- rename(BD, NAME = `Département`)

# 1.3. Calcul du taux de décès pour 100 000 habitants
BD$TD <- BD$Décès / BD$Population * 100000

# 1.4. Vérification du système de coordonnées
cat("Système de coordonnées initial :", st_crs(departements)$input, "\n")
Système de coordonnées initial : NA 
# Définition et transformation du système de coordonnées
st_crs(departements) <- 4326 # Attribution du système WGS84
departements <- st_transform(departements, crs = 4326)
departements <- st_make_valid(departements, ) # Correction de géométries
cat("Système de coordonnées défini :", st_crs(departements)$input, "\n")
Système de coordonnées défini : EPSG:4326 
# 1.5. Jointure des données géographiques et épidémiologiques
departements <- left_join(departements, BD, by = "NAME")

# Vérification
cat("Nombre de départements :", nrow(departements), "\n")
Nombre de départements : 27 
cat("Variables disponibles :", paste(names(departements), collapse = ", "))
Variables disponibles : NAME, ISO_CTRY, LVLID, CODE1, LVLID1, N° d'ordre, Cas, Décès, Population, TD, geometry

2. Calcul des centroïdes et classification des données

# 2.1. Calcul des centroïdes pour la représentation des décès
departements_centroides <- st_centroid(departements)

# 2.2. Classification des cas en catégories
departements$categorie <- cut(
  departements$Cas,
  breaks = c(-1, 1, 40, 80, 100, 202),
  labels = c("0", "1 - 40", "40 - 80", "80 - 100", "100 - 202"),
  include.lowest = TRUE
)

# 2.3. Définition de la palette de couleurs
couleurs <- c(
  "0" = "white",
  "1 - 40" = "#f7b9b9ff",
  "40 - 80" = "#f66b6bff",
  "80 - 100" = "#fc3a3aff",
  "100 - 202" = "#CC0000"
)

# Résumé statistique
cat("Répartition des catégories de cas :\n")
Répartition des catégories de cas :
print(freq(departements$categorie))
           n    % val%
0          3 11.1 11.1
1 - 40    12 44.4 44.4
40 - 80    4 14.8 14.8
80 - 100   1  3.7  3.7
100 - 202  7 25.9 25.9

3. Production des cartes élémentaires

3.1. Carte des cas par département

carte_cas <- ggplot() +
  # Polygones des départements colorés selon les cas
  geom_sf(
    data = departements,
    aes(fill = categorie),
    color = "black",
    linewidth = 0.3
  ) +
  # Étiquettes des départements
  geom_sf_text(
    data = departements,
    aes(label = NAME),
    size = 2.8,
    fontface = "bold.italic",
    check_overlap = TRUE
  ) +
  # Palette de couleurs
  scale_fill_manual(
    values = couleurs,
    name = "Fourchette de cas",
    na.value = "gray90"
  ) +
  # Échelle graphique
  annotation_scale(
    location = "br",
    width_hint = 0.3,
    style = "bar",
    line_width = 1
  ) +
  # Flèche du Nord
  annotation_north_arrow(
    location = "tl",
    which_north = "true",
    style = north_arrow_fancy_orienteering(
      fill = c("black", "white"),
      line_col = "black"
    ),
    height = unit(1.5, "cm"),
    width = unit(1.5, "cm")
  ) +
  # Mise en page
  coord_sf(expand = FALSE) +
  labs(
    title = "Répartition spatiale des cas de méningite",
    caption = "Source :R 4.5.2 \nProjection : WGS 84 / UTM zone 31N \nAuteur : Christian DARI"
  ) +
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    panel.grid = element_line(color = "gray90", linewidth = 0.2),
    legend.position = "right",
    legend.title = element_text(face = "bold", size = 10),
    legend.text = element_text(size = 9),
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40")
  )

carte_cas + panel_border(linetype = 1, size = 3, colour = "black")

3.2. Carte des décès par département (cercles proportionnels)

## Représentation des décès de méningite
carte_Décès <- ggplot() +
  geom_sf(data = departements, col = "black", fill = "white") +
  # Points de densité de décès (cercles proportionnels au centre des départements)
  geom_sf(
    data = departements_centroides,
    aes(size = Décès),
    color = "#04a5f5f5",
    fill = "#04a5f5f5",
    shape = 20,
    stroke = 1.5
  ) +

  # Échelle de taille pour les cercles de décès
  scale_size_continuous(
    name = "Décès",
    range = c(6, 30), # Ajustez la taille min et max des cercles
    breaks = c(5, 10, 15, 20, 25),
    limits = c(1, NA), # Pour inclure toutes les valeurs
    guide = guide_legend(
      order = 1,
      override.aes = list(color = "#04a5f5f5", fill = "#04a5f5f5")
    )
  ) +

  # Grille de coordonnées
  coord_sf(expand = FALSE) +

  # Thème
  theme_minimal() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    panel.grid.major = element_line(color = "gray80", size = 0.3),
    axis.text = element_text(size = 7),
    legend.position = "right",
    legend.title = element_text(face = "bold", size = 10),
    legend.text = element_text(size = 9),
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5)
  ) +

  labs(
    title = "Répartition des décès de méningite au sénégal",
    caption = "Source :R 4.5.2 \nProjection : WGS 84 / UTM zone 31N \nAuteur : Christian DARI"
  )

carte_Décès + panel_border(linetype = 1, size = 3, colour = "black")

4. Carte synthèse combinée

carte_principale <- ggplot() +
  # 1. Polygones des départements (cas)
  geom_sf(
    data = departements,
    aes(fill = categorie),
    color = "black",
    linewidth = 0.8,
    alpha = 0.9
  ) +
  # 2. Cercles proportionnels (décès)
  geom_sf(
    data = departements_centroides,
    aes(size = Décès),
    color = "#0a85c2ff",
    fill = "#097fb9ff",
    alpha = 0.6,
    shape = 21,
    stroke = 1
  ) +
  # 3. Étiquettes des départements
  geom_sf_text(
    data = departements,
    aes(label = NAME),
    size = 3.2,
    fontface = "bold.italic",
    color = "black",
    check_overlap = TRUE
  ) +
  # 4. Échelles et légendes
  scale_fill_manual(
    values = couleurs,
    name = "Fourchette de cas",
    guide = guide_legend(override.aes = list(size = 4))
  ) +
  scale_size_continuous(
    name = "Nombre de décès",
    range = c(6, 23), # Ajustez la taille min et max des cercles
    breaks = c(5, 10, 25),
    limits = c(1, NA), # Pour inclure tous les valeurs
    guide = guide_legend(
      override.aes = list(fill = "#097fb9ff", color = "#0a85c2ff")
    )
  ) +
  # 5. Éléments cartographiques
  annotation_scale(
    location = "bl",
    width_hint = 0.1,
    style = "bar",
    line_width = 1.3,
    text_cex = 0.8,
    text_face = "bold",
    height = unit(0.25, "cm"),
    pad_y = unit(0.38, "cm"),
    pad_x = unit(0.5, "cm")
  ) +
  annotation_north_arrow(
    location = "tl",
    which_north = "true",
    style = north_arrow_fancy_orienteering(
      fill = c("black", "white"),
      line_col = "black",
      line_width = 3
    ),
    height = unit(2, "cm"),
    width = unit(2, "cm"),
    pad_x = unit(0.5, "cm"),
    pad_y = unit(0.5, "cm")
  ) +
  # 6. Mise en page
  coord_sf(
    expand = TRUE,
    crs = 4326, # WGS84 = DMS
    datum = 4326,
    label_graticule = "NESW", # Affiche N,E,S,W sur toutes les bordures
    label_axes = list(
      bottom = "E",
      left = "N",
      top = "N",
      right = "E"
    )
  ) +
  labs(
    title = "ÉPIDÉMIE DE MÉNINGITE AU SÉNÉGAL"
  ) +
  theme_minimal() +
  theme(
    panel.border = element_rect(
      fill = NA,
      color = c("black", "white"), # Bordure alternée
      linewidth = 8,
      linetype = 8 # Ligne pointillée
    ),
    axis.title = element_blank(),
    axis.text = element_text(face = "bold", size = 8, colour = "black"),
    panel.background = element_rect(
      fill = "white",
      color = "black",
      linewidth = 1
    ),
    panel.grid = element_line(color = "gray90", linewidth = 0.3),
    legend.position = "right",
    legend.box = "vertical",
    legend.spacing = unit(0.3, "cm"),
    legend.title = element_text(face = "bold", size = 11),
    legend.text = element_text(size = 10),
    plot.title = element_text(
      face = "bold",
      size = 16,
      hjust = 0.5,
      margin = margin(b = 10)
    )
  )

print(carte_principale)

4.2. Ajout des limitrophes du Sénégal

ggdraw(carte_principale) +
  draw_label(
    "Mali",
    x = 0.75,
    y = 0.4,
    fontface = "bold.italic",
    size = 12,
    angle = 0
  ) +
  draw_label(
    "Mauritanie",
    x = 0.51,
    y = 0.76,
    fontface = "bold.italic",
    size = 12,
    angle = -35
  ) +
  draw_label(
    "Gambie",
    x = 0.25,
    y = 0.33,
    fontface = "bold.italic",
    size = 12,
    angle = 12
  ) +
  draw_label(
    "Guinée Bisseau",
    x = 0.35,
    y = 0.17,
    fontface = "bold.italic",
    size = 12,
    angle = 8
  ) +
  draw_label(
    "Guinée",
    x = 0.55,
    y = 0.17,
    fontface = "bold.italic",
    size = 12,
    angle = 0
  ) +
  draw_label(
    "Océan Atlantique",
    x = 0.1,
    y = 0.4,
    fontface = "bold.italic",
    size = 12,
    angle = 120,
    color = "royalblue"
  )

5. Carton de localisation (carte de l’Afrique)

# 5.1. Chargement des données de l'Afrique de l'Ouest
pays_afrique <- st_read(
  "C:/Users/hp/Documents/Analyse spatiale/DATA/Afrique/Pays d'Afrique.shp",
  quiet = TRUE
)

# 5.2. Création du carton de localisation
carton_localisation <- ggplot() +
  geom_sf(
    data = pays_afrique,
    fill = "white",
    color = "gray15",
    linewidth = 0.8
  ) +
  geom_sf(
    data = pays_afrique[pays_afrique$NAME == "Senegal", ],
    fill = "lightblue",
    color = "black",
    linewidth = 0.8
  ) +
  geom_sf_text(
    data = pays_afrique,
    aes(label = NAME_FR),
    size = 4,
    fontface = "bold",
    color = "darkblue"
  ) +
  coord_sf(
    xlim = c(-18, -7),
    ylim = c(9, 18),
    expand = TRUE
  ) +
  labs(title = "Localisation du Sénégal") +
  theme_void() +
  theme(
    panel.border = element_rect(color = "black", fill = NA, linewidth = 1.2),
    plot.title = element_text(
      size = 10,
      face = "bold",
      hjust = 0.5,
      margin = margin(b = 5)
    )
  )

carton_localisation <- ggdraw(carton_localisation) +
  draw_label(
    "Mali",
    x = 0.7,
    y = 0.6,
    fontface = "bold",
    color = "darkblue",
    size = 10
  ) +
  draw_label(
    "Mauritanie",
    x = 0.5,
    y = 0.8,
    fontface = "bold",
    color = "darkblue",
    size = 10
  )
print(carton_localisation)

5.3. Assemblage final

carte_finale <- ggdraw(carte_principale) +
  draw_plot(
    carton_localisation,
    x = 0.57, # Position horizontale (gauche)
    y = 0.62, # Position verticale (haut)
    width = 0.3, # Largeur du carton
    height = 0.28 # Hauteur du carton
  ) +
  draw_label(
    "Réalisé avec R 4.5.2 \nPackages : sf, ggplot2, ggspatial\nPar : Christian DARI \nContact : +229 01 65588427 \nEmail : christiandari995@gmail.com",
    x = 0.85,
    y = 0.09,
    size = 7,
    hjust = 0,
    fontface = "bold.italic",
    color = "gray20"
  )

print(carte_finale)

ggdraw(carte_finale) +
  draw_label(
    "Mali",
    x = 0.75,
    y = 0.4,
    fontface = "bold.italic",
    size = 12,
    angle = 0
  ) +
  draw_label(
    "Mauritanie",
    x = 0.51,
    y = 0.87,
    fontface = "bold.italic",
    size = 12,
    angle = -35
  ) +
  draw_label(
    "Gambie",
    x = 0.25,
    y = 0.28,
    fontface = "bold.italic",
    size = 12,
    angle = 12
  ) +
  draw_label(
    "Guinée Bisseau",
    x = 0.35,
    y = 0.1,
    fontface = "bold.italic",
    size = 12,
    angle = 8
  ) +
  draw_label(
    "Guinée",
    x = 0.55,
    y = 0.1,
    fontface = "bold.italic",
    size = 12,
    angle = 0
  ) +
  draw_label(
    "Océan Atlantique",
    x = 0.1,
    y = 0.4,
    fontface = "bold.italic",
    size = 12,
    angle = 120,
    color = "royalblue"
  )

6. Sauvegarde des résultats

# Sauvegarde en haute résolution (PNG)
ggsave(
  filename = "carte_meningite_senegal_HD.png",
  plot = carte_finale,
  width = 16,
  height = 12,
  dpi = 300,
  bg = "white"
)

# Sauvegarde en format vectoriel (PDF)
ggsave(
  filename = "Output/carte_meningite_senegal.pdf",
  plot = carte_finale,
  width = 14,
  height = 10,
  device = "pdf"
)

# Sauvegarde en format éditable (SVG)
ggsave(
  filename = "Output/carte_meningite_senegal.svg",
  plot = carte_finale,
  width = 14,
  height = 10,
  device = "svg"
)

Avantages de l’approche R pour la cartographie épidémiologique

Reproducibilité

  • Script reproductible à 100%
  • Versionnage possible avec Git
  • Automatisation des mises à jour

Flexibilité analytique

  • Intégration directe des analyses statistiques
  • Possibilité de modélisation spatiale avancée
  • Génération de rapports automatisés

Interopérabilité

  • Compatibilité avec divers formats de données
  • Export vers multiples formats (PNG, PDF, SVG, HTML)
  • Intégration dans des pipelines d’analyseLimitations et perspectives

Limitations actuelles

  • Courbe d’apprentissage initiale plus raide que les SIG traditionnels
  • Gestion de très grandes bases de données géospatiales
  • Interface graphique limitée pour les manipulations spatiales complexes

Améliorations potentielles

  1. Automatisation complète du processus
  2. Dashboard interactif avec Shiny
  3. Intégration de données temporelles pour l’analyse spatio-temporelle
  4. Modélisation des facteurs de risque environnementaux

Conclusion

Au cours de ce webinaire, nous avons démontré la capacité de R à produire des cartes épidémiologiques professionnelles comparables à celles générées par des logiciels SIG dédiés comme QGIS. L’approche par programmation offre des avantages significatifs en termes de reproductibilité, automatisation et intégration dans des workflows d’analyse complets.

La géomatique épidémiologique avec R représente une compétence précieuse pour les épidémiologistes, les biostatisticiens et les professionnels de santé publique, permettant de transformer des données brutes en informations spatiales actionnables pour la prise de décision.

Recommandations

Pour les débutants en cartographie avec R, nous recommandons :

  1. Commencer par des projets simples avant des analyses complexes
  2. Documenter rigoureusement chaque étape du code
  3. Utiliser des systèmes de coordonnées appropriés aux échelles d’analyse
  4. Valider les résultats avec des logiciels SIG traditionnels
  5. Participer aux communautés R spécialisées
Nous organisons des formations et des coachings pour améliorer les compétences des étudiants et des chercheurs. Contactez-nous pour plus de renseignements.