---
title: "2 : Santé des terres, santé des sols et workflows spatiaux"
format: html
execute:
  echo: true
  warning: false
  message: false
---

![notebook 2](https://raw.githubusercontent.com/smugeit/ghana_wkshp_img/main/images/img.png)

# Notebook 2 : Santé des terres, santé des sols et workflows spatiaux

Ce notebook couvre **cinq sessions pratiques** :

- **Session 1 :** préparation de l’environnement, chargement des données LDSF et premières vérifications  
- **Session 2 :** conversion de tableaux avec coordonnées en objets spatiaux avec `sf`  
- **Session 3 :** cartographie statique avec `ggplot2` et cartographie interactive avec `leaflet`  
- **Session 4 :** jointure de jeux de données et exploration des relations entre santé des terres et santé des sols  
- **Session 5 :** opérations spatiales simples, export des données et pistes d’analyse complémentaire  

Tout au long du notebook, nous suivrons le même flux d’analyse :

**Charger les données > Inspecter > Filtrer > Cartographier > Joindre > Interpréter > Sauvegarder**

Ce notebook utilise deux jeux de données issus de cartes prédictives dérivées du LDSF dans la région de la Grande Muraille Verte. Il s’appuie sur les bases du Notebook 1 et introduit les notions essentielles pour travailler avec des coordonnées, des objets spatiaux, des cartes statiques et des cartes interactives dans R.

---

## Objectifs d’apprentissage

À la fin de ce notebook, vous devriez être capable de :

- charger et inspecter deux jeux de données sur la santé des terres et la santé des sols
- filtrer les données pour les six pays d’intérêt : Mali, Niger, Tchad, Burkina Faso, Mauritanie et Sénégal
- convertir un tableau avec des colonnes `lon` et `lat` en objet spatial `sf`
- comprendre ce qu’est un système de référence de coordonnées (CRS)
- créer des cartes statiques avec `ggplot2` et `geom_sf()`
- créer des cartes interactives avec `leaflet`
- joindre deux tableaux avec `inner_join()`
- explorer quelques relations simples entre couvert arboré, érosion et carbone organique du sol
- exporter des résultats spatiaux ou tabulaires

## Avant de commencer

1. Créez ou ouvrez votre dossier de travail pour l’atelier, par exemple : `atelier_ggw_senegal`.
2. Vérifiez que votre dossier contient une structure simple :

```text
atelier_ggw_senegal/
├── donnees/
├── scripts/
├── resultats/
└── figures/
```

3. Enregistrez ce notebook dans le dossier `scripts/` et renommez-le, par exemple :  
`Practical2_VotreNom.qmd`

4. Placez les fichiers de données fournis dans le dossier `donnees/` :

- `gmv_ldsf_ldlc.csv`
- `gmv_ldsf_sante_sol.csv`

5. Ouvrez le fichier `.qmd` dans RStudio et travaillez uniquement dans votre propre copie.
6. Enregistrez régulièrement votre travail avec **File > Save** ou `Ctrl + S`.

# Session 1 - Préparer l’environnement et charger les données

## 1. Packages et environnement de travail

Avant de commencer, assurez-vous que les packages nécessaires sont disponibles et que les données du tutoriel sont enregistrées dans les bons dossiers. Nous utiliserons des dossiers simples : `donnees/`, `resultats/` et `figures/`.

Nous utilisons les packages suivants. Installez ceux qui manquent avec `install.packages("nom_du_package")`.

```{r}
#| message: false
#| warning: false

library(sf)           # package principal pour les données spatiales dans R
library(leaflet)      # cartes web interactives
library(ggplot2)      # cartes statiques et graphiques
library(dplyr)        # manipulation des données
library(rnaturalearth)     # polygones des limites nationales
library(rnaturalearthdata) # données nécessaires pour rnaturalearth
library(RColorBrewer)      # palettes de couleurs
library(viridis)           # palettes perceptuellement uniformes

# Créer les dossiers de sortie s’ils n’existent pas encore
dir.create("resultats", showWarnings = FALSE)
dir.create("figures", showWarnings = FALSE)
```

---

## 2. Charger et explorer les données {#sec-load}

```{r}
table_sante_terres <- read.csv("donnees/gmv_ldsf_ldlc.csv")
table_sante_sols <- read.csv("donnees/gmv_ldsf_sante_sol.csv")
```

Commencez toujours par inspecter la structure et les premières lignes de vos données.

```{r}
# Dimensions : lignes × colonnes
dim(table_sante_terres)
dim(table_sante_sols)
```

```{r}
# Noms des colonnes et types de données
str(table_sante_terres)
```

**Vous pouvez aussi exécuter `str(table_sante_sols)`**

```{r}
# Statistiques descriptives rapides
summary(table_sante_terres)
```

**Vous pouvez aussi exécuter `summary(table_sante_sols)`**

Remarquez que les deux tableaux partagent les colonnes `pays`, `echantillon_id`, `lon` et `lat`. Ces colonnes indiquent la localisation géographique de chaque point d’échantillonnage et nous permettront de joindre les deux tableaux plus tard.

Combien de pays uniques contient le jeu de données ?

```{r}
sort(unique(table_sante_terres$pays))
```

Dans ce jeu de données, les noms de pays sont conservés en anglais dans les fichiers (`Chad`, `Mauritania`, `Senegal`) afin de faciliter la correspondance avec les limites administratives utilisées par `rnaturalearth`.

---

## 3. Filtrer les six pays d’intérêt {#sec-filter}

Pour ce tutoriel, nous nous concentrons sur six pays de la Grande Muraille Verte : **Mali**, **Niger**, **Tchad**, **Burkina Faso**, **Mauritanie** et **Sénégal**. Nous utilisons `filter()` de `dplyr` avec `%in%` pour garder uniquement les lignes dont la valeur de `pays` apparaît dans notre liste.

```{r}
pays_interet <- c("Mali", "Niger", "Chad", "Burkina Faso", "Mauritania", "Senegal")

sante_terres <- table_sante_terres |>
  filter(pays %in% pays_interet)

sante_sols <- table_sante_sols |>
  filter(pays %in% pays_interet)

# Combien de points d’échantillonnage par pays ?
sante_terres |> count(pays)
```

```{r}
sante_sols |> count(pays)
```

---

# Session 2 - Travailler avec des données spatiales dans R

## 4. Qu’est-ce qu’une donnée spatiale ? Introduction à `sf` {#sec-sf-intro}

Un tableau classique avec des colonnes `lon` et `lat` est une donnée *tabulaire* qui contient des coordonnées. Pour réaliser des opérations spatiales — mesurer des distances, superposer des couches, reprojeter — nous devons indiquer explicitement à R que ces nombres représentent des localisations à la surface de la Terre.

Le package `sf` (*Simple Features*) représente les données spatiales comme un tableau R classique avec une colonne supplémentaire appelée *geometry*. Cette colonne stocke la forme géographique de chaque ligne : point, ligne ou polygone. Les fonctions habituelles de `dplyr` fonctionnent avec les objets `sf`, auxquelles s’ajoutent des fonctions spatiales commençant souvent par `st_`.

### Convertir un tableau en objet spatial `sf`

```{r}
sante_terres_sf <- st_as_sf(
  sante_terres,
  coords = c("lon", "lat"),   # noms des colonnes pour x (longitude) et y (latitude)
  crs    = 4326               # code EPSG pour WGS 84 — le système GPS standard
)

sante_sols_sf <- st_as_sf(
  sante_sols,
  coords = c("lon", "lat"),
  crs    = 4326
)
```

Regardons le résultat :

```{r}
# La classe est maintenant à la fois "sf" et "data.frame"
class(sante_terres_sf)
```

```{r}
# head() affiche la colonne geometry à la fin
head(sante_terres_sf)
```

```{r}
# Type de géométrie — POINT signifie que chaque ligne est une localisation
st_geometry_type(sante_terres_sf) |> unique()
```

### Comprendre le système de référence de coordonnées (CRS)

Un CRS définit la manière dont les coordonnées se positionnent sur la surface courbe de la Terre. Il faut toujours vérifier le CRS de vos données avant de réaliser une opération spatiale.

```{r}
st_crs(sante_terres_sf)
```

**EPSG:4326** (aussi appelé WGS 84) utilise des *degrés de longitude et de latitude*. C’est le système utilisé par les GPS et par la plupart des services de cartographie web. Lorsque vous lisez des coordonnées depuis un fichier CSV, vous utiliserez presque toujours `crs = 4326`.

### Boîte englobante — où se situent les données ?

```{r}
st_bbox(sante_terres_sf)
```

Cela donne la longitude minimale et maximale (`xmin`, `xmax`) et la latitude minimale et maximale (`ymin`, `ymax`) du jeu de données. C’est utile pour définir l’emprise d’une carte.

---

# Session 3 - Cartographie statique et interactive

## 5. Cartes statiques avec `ggplot2` {#sec-ggplot}

`ggplot2` prend en charge les objets `sf` grâce à `geom_sf()`. En combinant cela avec des limites de pays provenant de `rnaturalearth`, nous pouvons créer une carte de base propre.

### Télécharger les limites des pays

```{r}
# Télécharger les polygones des pays africains à résolution moyenne
africa <- ne_countries(
  continent = "africa",
  scale      = "medium",
  returnclass = "sf"      # retourner un objet sf, et non un objet Spatial
)

# Garder uniquement les pays que nous analysons
pays_selectionnes <- africa |>
  filter(name %in% pays_interet)
```

### Une carte simple de points

```{r}
ggplot() +
  geom_sf(data = africa, fill = "grey95", colour = "grey60", linewidth = 0.3) +
  geom_sf(data = pays_selectionnes, fill = "lightyellow", colour = "grey40",
          linewidth = 0.5) +
  geom_sf(data = sante_terres_sf, colour = "firebrick", size = 1.5, alpha = 0.6) +
  coord_sf(xlim = c(-18, 25), ylim = c(8, 24)) +  # recadrer sur la région d’intérêt
  labs(
    title    = "Points d’échantillonnage LDSF — six pays de la GMV",
    subtitle = "Santé des terres et condition des paysages",
    x = "Longitude", y = "Latitude",
    caption  = "Données : LDSF / World Agroforestry (CIFOR-ICRAF)"
  ) +
  theme_bw()
```

```{r}
ggsave("figures/ggplot_points_echantillon.png", width = 6, height = 4, dpi = 300)
```

### Colorer les points selon une variable

Nous pouvons représenter une variable comme `couvert_arbre` avec l’esthétique de couleur. `scale_colour_viridis_c()` utilise une palette perceptuellement uniforme, également lisible pour les personnes daltoniennes.

```{r}
ggplot() +
  geom_sf(data = africa, fill = "grey95", colour = "grey70", linewidth = 0.3) +
  geom_sf(data = pays_selectionnes, fill = "lightyellow", colour = "grey40",
          linewidth = 0.5) +
  geom_sf(data = sante_terres_sf, aes(colour = couvert_arbre), size = 2, alpha = 0.8) +
  scale_colour_viridis_c(name = "Couvert arboré (%)", option = "D", direction = -1) +
  coord_sf(xlim = c(-18, 25), ylim = c(8, 24)) +
  labs(
    title   = "Couvert arboré aux points d’échantillonnage LDSF",
    x = "Longitude", y = "Latitude",
    caption = "Données : LDSF / World Agroforestry (CIFOR-ICRAF)"
  ) +
  theme_bw()
```

```{r}
ggsave("figures/ggplot_couvert_arbre.png", width = 6, height = 4, dpi = 300)
```

---

## 6. Cartes interactives avec `leaflet` {#sec-leaflet}

`leaflet` crée des cartes HTML interactives qui permettent de zoomer, se déplacer et cliquer sur les éléments. Contrairement à `ggplot2`, `leaflet` s’affiche dans le navigateur plutôt que comme une image statique, ce qui est idéal pour explorer les données ou les partager avec des parties prenantes.

### Une carte leaflet minimale

```{r}
leaflet(data = sante_terres_sf) |>
  addTiles() |>                          # ajoute le fond de carte OpenStreetMap par défaut
  addCircleMarkers(
    radius = 4,
    color  = "steelblue",
    stroke = FALSE,
    fillOpacity = 0.8
  )
```

> **Astuce** `addTiles()` sans argument utilise OpenStreetMap. Vous pouvez choisir un autre fond de carte ; voir la section @sec-basemaps.

### Ajouter des popups

Les popups apparaissent lorsque vous cliquez sur un point et peuvent afficher du contenu HTML.

```{r}
leaflet(data = sante_terres_sf) |>
  addTiles() |>
  addCircleMarkers(
    radius = 4,
    color  = "steelblue",
    stroke = FALSE,
    fillOpacity = 0.8,
    popup = ~paste0(
      "<b>Pays :</b> ", pays, "<br>",
      "<b>ID échantillon :</b> ", echantillon_id, "<br>",
      "<b>Couvert arboré :</b> ", couvert_arbre, "%<br>",
      "<b>Érosion :</b> ", erosion, "%<br>",
      "<b>Cultures :</b> ", cultures, "%"
    )
  )
```

### Points colorés selon une variable

Pour montrer le motif spatial d’une variable continue, nous associons les valeurs à des couleurs avec l’une des fonctions de couleur de `leaflet` :

| Fonction | Utilisation |
|---|---|
| `colorNumeric()` | Données continues avec un gradient progressif |
| `colorBin()` | Données continues divisées en classes |
| `colorQuantile()` | Lorsque vous voulez un nombre similaire de points par classe |
| `colorFactor()` | Données catégorielles |

```{r}
# Créer une palette de couleurs pour le carbone organique du sol
pal_cos <- colorNumeric(
  palette = "YlOrRd",
  domain  = sante_sols_sf$cos,
  na.color = "grey"
)

leaflet(data = sante_sols_sf) |>
  addProviderTiles("CartoDB.Positron") |>
  addCircleMarkers(
    radius      = 5,
    color       = ~pal_cos(cos),
    stroke      = FALSE,
    fillOpacity = 0.85,
    popup = ~paste0(
      "<b>Pays :</b> ", pays, "<br>",
      "<b>COS :</b> ", round(cos, 2), " g/kg<br>",
      "<b>Azote total :</b> ", round(nt, 2), " g/kg<br>",
      "<b>pH du sol :</b> ", round(pH_sol, 2)
    ),
    label = ~paste0("COS : ", round(cos, 1), " g/kg")
  ) |>
  addLegend(
    position = "bottomright",
    pal      = pal_cos,
    values   = ~cos,
    title    = "Carbone organique du sol<br>(g/kg)",
    opacity  = 0.8
  )
```

### Utiliser des classes pour des seuils plus lisibles

```{r}
pal_erosion <- colorBin(
  palette = "Reds",
  domain  = sante_terres_sf$erosion,
  bins    = c(0, 25, 50, 75, 100),
  reverse = TRUE   # rouge = érosion élevée
)

leaflet(data = sante_terres_sf) |>
  addProviderTiles("CartoDB.Positron") |>
  addCircleMarkers(
    radius      = 5,
    color       = ~pal_erosion(erosion),
    stroke      = FALSE,
    fillOpacity = 0.85,
    popup = ~paste0(
      "<b>Pays :</b> ", pays, "<br>",
      "<b>Érosion :</b> ", erosion, "%<br>",
      "<b>Couvert arboré :</b> ", couvert_arbre, "%"
    )
  ) |>
  addLegend(
    position = "bottomright",
    pal      = pal_erosion,
    values   = ~erosion,
    title    = "Érosion (%)",
    opacity  = 0.8
  )
```

### Contrôles de couches — activer et désactiver plusieurs données {#sec-layers}

`addLayersControl()` permet d’activer ou de désactiver des couches superposées. C’est utile lorsque l’on veut comparer plusieurs variables sur une même carte.

```{r}
pal_tree <- colorNumeric("Greens", domain = sante_terres_sf$couvert_arbre)
pal_crop  <- colorNumeric("Blues",  domain = sante_terres_sf$cultures)

leaflet() |>
  addProviderTiles("CartoDB.Positron", group = "CartoDB") |>
  addProviderTiles("OpenStreetMap",    group = "OpenStreetMap") |>

  # Couche 1 : couvert arboré
  addCircleMarkers(
    data        = sante_terres_sf,
    group       = "Couvert arboré",
    radius      = 5,
    color       = ~pal_tree(couvert_arbre),
    stroke      = FALSE,
    fillOpacity = 0.85,
    popup = ~paste0("<b>Couvert arboré :</b> ", couvert_arbre, "%")
  ) |>
  addLegend(
    position = "bottomleft",
    pal      = pal_tree,
    values   = sante_terres_sf$couvert_arbre,
    title    = "Couvert arboré (%)",
    group    = "Couvert arboré"
  ) |>

  # Couche 2 : cultures
  addCircleMarkers(
    data        = sante_terres_sf,
    group       = "Cultures",
    radius      = 5,
    color       = ~pal_crop(cultures),
    stroke      = FALSE,
    fillOpacity = 0.85,
    popup = ~paste0("<b>Cultures :</b> ", cultures, "%")
  ) |>
  addLegend(
    position = "bottomright",
    pal      = pal_crop,
    values   = sante_terres_sf$cultures,
    title    = "Cultures (%)",
    group    = "Cultures"
  ) |>

  addLayersControl(
    baseGroups    = c("CartoDB", "OpenStreetMap"),
    overlayGroups = c("Couvert arboré", "Cultures"),
    options       = layersControlOptions(collapsed = FALSE)
  )
```

### Fonds de carte alternatifs {#sec-basemaps}

`addProviderTiles()` donne accès à de nombreux fournisseurs de fonds de carte préconfigurés. Voici quelques options utiles pour le travail écologique :

```{r}
# Imagerie satellite
leaflet(data = sante_sols_sf) |>
  addProviderTiles("Esri.WorldImagery") |>
  addCircleMarkers(radius = 4, color = "yellow", stroke = FALSE, fillOpacity = 0.9,
                   label = ~pays)
```

Noms de fournisseurs courants :

| Fournisseur | Description |
|---|---|
| `"OpenStreetMap"` | Fond OSM standard |
| `"CartoDB.Positron"` | Fond clair et épuré |
| `"CartoDB.DarkMatter"` | Fond sombre |
| `"Esri.WorldImagery"` | Imagerie satellite |
| `"Esri.WorldTopoMap"` | Carte topographique |
| `"Stamen.Terrain"` | Terrain (nécessite une clé Stadia) |

---

# Session 4 - Joindre les données et explorer les relations

## 7. Joindre les deux jeux de données {#sec-join}

Les deux tableaux partagent `echantillon_id` et `pays`. Nous pouvons les combiner avec `inner_join()` afin de travailler avec les variables de santé des terres et de santé des sols dans un même objet.

```{r}
# Joindre d’abord comme tableaux classiques, puis convertir en sf
donnees_combinees <- inner_join(
  sante_terres,
  sante_sols |> select(echantillon_id, pays, cos, nt, pH_sol, sable, limon),
  by = c("echantillon_id", "pays")
)

nrow(sante_terres); nrow(sante_sols); nrow(donnees_combinees)
```

```{r}
# Convertir en sf
donnees_combinees_sf <- st_as_sf(donnees_combinees, coords = c("lon", "lat"), crs = 4326)

head(donnees_combinees_sf)
```

Nous pouvons maintenant explorer des relations simples, par exemple : le carbone organique du sol est-il associé au couvert arboré ?

```{r}
ggplot(donnees_combinees, aes(x = couvert_arbre, y = cos, colour = pays)) +
  geom_point(alpha = 0.6, size = 2) +
  geom_smooth(method = "lm", se = TRUE, linewidth = 1) +
  scale_colour_brewer(palette = "Set1", name = "Pays") +
  labs(
    title = "Carbone organique du sol et couvert arboré",
    x     = "Couvert arboré (%)",
    y     = "Carbone organique du sol (g/kg)"
  ) +
  theme_bw()
```

```{r}
ggplot(donnees_combinees, aes(x = sable, y = cos, colour = pays)) +
  geom_point(alpha = 0.6, size = 2) +
  geom_smooth(method = "lm", se = TRUE, linewidth = 1) +
  scale_colour_brewer(palette = "Set1", name = "Pays") +
  labs(
    title = "Carbone organique du sol et teneur en sable",
    x     = "Sable (%)",
    y     = "Carbone organique du sol (g/kg)"
  ) +
  theme_bw()
```

Nous pouvons aussi représenter les deux variables ensemble avec une popup de style bivarié :

```{r}
pal_cos2 <- colorNumeric("YlOrRd", domain = donnees_combinees_sf$cos, na.color = "grey80")

leaflet(data = donnees_combinees_sf) |>
  addProviderTiles("CartoDB.Positron") |>
  addCircleMarkers(
    radius      = ~scales::rescale(couvert_arbre, to = c(3, 10)),  # taille = couvert arboré
    color       = ~pal_cos2(cos),                                # couleur = COS
    stroke      = TRUE,
    weight      = 1,
    fillOpacity = 0.8,
    popup = ~paste0(
      "<b>Pays :</b> ", pays, "<br>",
      "<b>Couvert arboré :</b> ", couvert_arbre, "%<br>",
      "<b>COS :</b> ", round(cos, 2), " g/kg<br>",
      "<b>Érosion :</b> ", erosion, "%"
    )
  ) |>
  addLegend("bottomright", pal = pal_cos2, values = ~cos,
            title = "COS (g/kg)", opacity = 0.8)
```

> **Ce que vous voyez :** la *taille* de chaque cercle représente le couvert arboré, tandis que sa *couleur* représente le carbone organique du sol. Les points grands et foncés tendent donc à combiner un couvert arboré plus élevé et un COS plus élevé, ce qui est cohérent avec le rôle des arbres dans l’apport de matière organique au sol.

---

# Session 5 - Opérations spatiales, export et synthèse

## 8. Opérations spatiales avec `sf` {#sec-spatial-ops}

### Calculer des centroïdes par pays

```{r}
# Agréger les points au niveau des pays
by_pays <- sante_terres_sf |>
  group_by(pays) |>
  summarise(
    n_sites    = n(),
    mean_erosion   = mean(erosion, na.rm = TRUE),
    mean_couvert_arbre = mean(couvert_arbre, na.rm = TRUE),
    geometry   = st_union(geometry)   # unir tous les points en MULTIPOINT
  )

by_pays
```

```{r}
# Calculer le centroïde du nuage de points de chaque pays
centroides <- st_centroid(by_pays)

ggplot() +
  geom_sf(data = africa, fill = "grey95", colour = "grey70", linewidth = 0.3) +
  geom_sf(data = sante_terres_sf, colour = "grey60", size = 1, alpha = 0.5) +
  geom_sf(data = centroides, aes(size = n_sites, colour = mean_erosion), alpha = 0.9) +
  scale_colour_distiller("Érosion moyenne (%)", palette = "YlOrRd", direction = 1) +
  scale_size_continuous("Nombre de sites", range = c(4, 12)) +
  coord_sf(xlim = c(-18, 25), ylim = c(8, 24)) +
  labs(title = "Résumé des échantillons LDSF par pays") +
  theme_bw()
```

### Découper les points selon un polygone de pays

`st_filter()` (ou `st_intersection()`) conserve uniquement les éléments qui se trouvent à l’intérieur d’un polygone donné. C’est utile si votre nuage de points contient des coordonnées mal localisées.

```{r}
senegal_limite <- africa |> filter(name == "Senegal")

points_senegal <- st_filter(sante_terres_sf, senegal_limite)

nrow(sante_terres_sf |> filter(pays == "Senegal"))  # points étiquetés "Senegal"
nrow(points_senegal)                                    # points réellement situés dans la limite du Sénégal
```

---

## 9. Exporter les données spatiales {#sec-export}

`sf` peut lire et écrire de nombreux formats spatiaux. Le GeoPackage (`.gpkg`) est un format moderne, ouvert et stocké dans un seul fichier, souvent recommandé à la place du Shapefile.

```{r}
#| eval: false

# Écrire en GeoPackage
st_write(donnees_combinees_sf, "resultats/ldsf_combine.gpkg", delete_dsn = TRUE)

# Relire le fichier
ldsf_reloaded <- st_read("resultats/ldsf_combine.gpkg")
```

Pour exporter en CSV, en supprimant l’information spatiale :

```{r}
#| eval: false

# Réajouter les coordonnées comme colonnes avant l’export
donnees_combinees_coordonnees <- donnees_combinees_sf |>
  mutate(
    lon = st_coordinates(geometry)[, 1],
    lat = st_coordinates(geometry)[, 2]
  ) |>
  st_drop_geometry()

write.csv(donnees_combinees_coordonnees, "resultats/ldsf_combine_export.csv", row.names = FALSE)
```

---

## 10. Bonus : modéliser le carbone organique du sol {#sec-modelling}

> **Cette section est un bonus** — travaillez dessus si le temps le permet.

Maintenant que nous avons un jeu de données combiné (`donnees_combinees`), nous pouvons poser une question plus formelle : quelles variables du paysage et du sol permettent de prédire le carbone organique du sol (COS), en tenant compte du fait que nos observations proviennent de six pays différents ?

Les points sont regroupés par pays. Les points d’un même pays partagent des facteurs non mesurés — climat, histoire d’utilisation des terres, matériau parental du sol — qui peuvent les rendre plus semblables entre eux qu’avec les points d’autres pays. Ignorer cette structure peut conduire à surestimer notre confiance dans les effets estimés. Nous pouvons donc ajuster un **modèle linéaire mixte** avec `lmer` du package `lme4`, en ajoutant `pays` comme **interception aléatoire** : chaque pays reçoit son propre niveau de base de COS, tandis que les effets fixes sont estimés pour l’ensemble du jeu de données.

---

## 11. Récapitulatif {#sec-summary}

Dans ce notebook, vous avez appris à :

| Tâche | Fonction clé |
|---|---|
| Convertir un CSV avec coordonnées en données spatiales | `st_as_sf()` |
| Vérifier le système de référence de coordonnées | `st_crs()` |
| Obtenir l’emprise géographique d’un jeu de données | `st_bbox()` |
| Créer des cartes statiques | `geom_sf()` avec `ggplot2` |
| Construire des cartes interactives | `leaflet()` + `addCircleMarkers()` |
| Associer des valeurs à des couleurs | `colorNumeric()`, `colorBin()`, `colorFactor()` |
| Ajouter des popups et des labels | arguments `popup =`, `label =` |
| Activer/désactiver des couches interactivement | `addLayersControl()` |
| Joindre des données spatiales et non spatiales | `inner_join()` + `st_as_sf()` |
| Agréger des géométries | `st_union()`, `st_centroid()` |
| Filtrer des points selon un polygone | `st_filter()` |
| Exporter vers un GeoPackage | `st_write()` |

### À essayer ensuite

- Modifiez la palette de couleurs dans l’une des cartes `leaflet`. Explorez les palettes disponibles avec `RColorBrewer::display.brewer.all()`.
- Ajoutez une troisième couche superposée, par exemple le pH du sol, à la carte avec contrôle des couches dans la section @sec-layers.
- Filtrez les données pour un seul pays et créez une carte `leaflet` détaillée avec imagerie satellite.
- Essayez `colorQuantile()` au lieu de `colorNumeric()` pour le COS. En quoi la carte est-elle différente ?
- Exportez `donnees_combinees_sf` vers un GeoPackage et ouvrez-le dans QGIS.
