4  Données en mailles

Une approche courante consiste à diviser le territoires en mailles, carrées ou en forme d’alvéoles d’abeilles (hexagones), et à calculer des indicateurs pour chacune de ces mailles.

4.1 Constitution d’un maillage

On montre ci-dessous comment cette approche fonctionne. La première étape consiste à dessiner un carré autour des aires protégées malgaches, puis à subdiviser ce grand carré en un damier de formes hexagonales. Enfin, on ne garde que les hexagones qui se trouvent dans les frontières terrestres de Madagascar.

Code
library(tidyverse)
library(tmap)
library(sf)
library(mapme.biodiversity)
library(tidygeocoder) # pour obtenir les coordo GPS d'un point à partir de son nom
library(maptiles) # Pour télécharger des fonds de carte


# Surface des hexagones en km2
taille_hex <- 5

# Ce qui suit jusqu'à la commande "save" ne s'execute que si le résultat n'a pas
# déjà été généré lors d'une exécution précédente.
if (file.exists("data/grille_mada_donnees_raster.rds")) {
  load("data/grille_mada_donnees_raster.rds")
} else {
  
  # Création d'un maillage du territoire émergé --------------------------------
  
  # On crée un cadre autour des aires protégées du pays
  cadre_autour_mada = st_as_sf(st_as_sfc(st_bbox(aires_prot_mada)))
  
  # Cellules de 5km de rayon
  surface_cellule <- taille_hex * (1e+6)
  taille_cellule <- 2 * sqrt(surface_cellule / ((3 * sqrt(3) / 2))) * sqrt(3) / 2
  grille_mada <- st_make_grid(x = cadre_autour_mada,
                              cellsize = taille_cellule,
                              square = FALSE)
  # On découpe la grille pour ne garder que les terres émergées
  cellules_emergees <- st_intersects(contour_mada, grille_mada) %>%
    unlist()
  grille_mada <- grille_mada[sort(cellules_emergees)] %>%
    st_sf()
} 

Le maillage produit est trop fin pour être visible à l’échelle du pays, mais on peut l’observer en zoomant sur une zone spécifique.

Code
sf_use_s2(TRUE) 

# On compte le nombre d'hexagones
n_hex <- nrow(grille_mada)
# Carte pour visualiser le résultat ------------------------------------------

# A supprimer après validation, l'échappement ne fonctionne pas. 
# if (file.exists("data/carte_zoom.rds")) {
  # load("data/carte_zoom.rds")
  # load("data/elements_carte_zoom.rds")
# } else {

## Carte de droite : zoom sur une zone spécifique-----------------------------
# On part d'un dataframe contenant une adresse
nom_centre_zoom <- "Maroantsetra"
zoom_centre <- data.frame(address = nom_centre_zoom) %>%
  geocode(address, method = "osm") %>% # on retrouve sa localisation xy
  select(long, lat) %>% # on ne garde que le xy
  as.numeric() %>% # qu'on passe en format numérique attendu par st_point
  st_point() %>% # On le spécifie en point
  st_sfc(crs = "EPSG:4326") 

# On crée une boîte de 100km 
zoom_boite <- zoom_centre %>% # On repart du centre
  st_buffer(dist = 50000) %>% # On crée un cercle de 50km de rayon
  st_make_grid(n = 1) 

# On filtre les alvéoles pour ne garder que celles qui sont dans le zoom
grille_zoom <- st_intersection(grille_mada, zoom_boite)

# On télécharge un fond de carte pour la carte de droite
fond_carte_zoom <- get_tiles(zoom_boite, provider = "Stamen.Terrain", 
                             zoom = 10, crop = TRUE) 

save(n_hex, nom_centre_zoom, zoom_centre, zoom_boite, grille_zoom, 
     fond_carte_zoom, file = "data/elements_carte_zoom.rds")

# On était restés en mode interactif, on repasse en mode statique pour les 
# cartes
tmap_mode("plot")

# On génère la carte de droite
carte_zoom <- tm_shape(fond_carte_zoom) + 
  tm_rgb() +
  tm_shape(grille_zoom) +
  tm_borders() +
  tm_shape(zoom_boite) + 
  tm_borders(col = "red") +
  tm_layout(frame = FALSE,
            main.title = paste("Zoom sur la zone de", nom_centre_zoom),
            main.title.size = 1) +
  tm_credits(get_credit("Stamen.Toner"),
             bg.color = "white",
             align = "right",
             position = c("right", "BOTTOM"))
save(carte_zoom, zoom_boite, file = "data/carte_zoom.rds")
# }


## Carte de gauche : simple à réaliser mais hexagones non visibles -------------
load("data/contour_mada.rds")
carte_grille <- tm_shape(contour_mada) +
  tm_polygons(col = "grey") + 
  tm_shape(zoom_boite) +
  tm_borders(col = "red") +
  tm_layout(frame = FALSE) +
  tm_layout(main.title = paste("Découpage en", n_hex,
                               "hexagones de", taille_hex, "km2"),
            main.title.size = 1)

# Assemblage des deux cartes ---------------------------------------------------
tmap_arrange(carte_grille, carte_zoom, ncol = 2) 

4.2 Récupération des données pour le maillage

On va ensuite utiliser le package mapme.biodiversity pour calculer, pour chaque hexagones, une série d’indicateurs : temps de parcours jusqu’à la ville (définie comme toute localité de 5000 habitants) la plus proche en 2015, teneur du sol en argile et couvert forestier par année).

Code
if (file.exists("data/grille_mada_donnees_raster.rds")) {
  load("data/grille_mada_donnees_raster.rds")
} else {
  
  # Traitement des données satellitaires avec {mapme.bidiversity}---------------
  
  # Constitution d'un portefeuille (voir la documentation)
  grille_mada <- init_portfolio(x = grille_mada, 
                                years = 2000:2020,
                                outdir = "data_s3/mapme",
                                cores = 24,
                                add_resources = TRUE,
                                verbose = TRUE)
  
  # Acquisition des données satellitaires requises (rasters) ------------------- 
  # Données d'accessibilité de Nelson et al. (2018)
  grille_mada <-  get_resources(x = grille_mada, resource = "nelson_et_al",  
                                range_traveltime = "5k_110mio")
  # Données de qualité des sols (uniquement teneur )
  grille_mada <-  get_resources(x = grille_mada,
                                resources = "soilgrids",  layers = "clay", 
                                depths = "5-15cm", stats = "mean")
  # Données sur le couvert forestier de Global Forest Watch
  grille_mada <- get_resources(x = grille_mada, 
                               resources = c("gfw_treecover", "gfw_lossyear", 
                                             "gfw_emissions"))
  # Modèle numérique de terrain SRTM de la NASA
  grille_mada <- get_resources(x = grille_mada, resource = "nasa_srtm")
  # Données de feux
  grille_mada <- get_resources(x = grille_mada, resource = "nasa_firms",
                               instrument = "MODIS")
  
  # Calcul des indicateurs -----------------------------------------------------
  
  # Indicateurs d'accessibilité
  grille_mada <- calc_indicators(x = grille_mada,
                                 "traveltime",  stats_accessibility = "mean",
                                 engine = "extract")
  # Indicateurs de sols
  
  grille_mada <- calc_indicators(x = grille_mada,
                                 "soilproperties", stats_soil = "mean", 
                                 engine = "extract")
 
   # Indicateurs de couvert forestier
  grille_mada <- calc_indicators(x = grille_mada,
                                 indicators = "treecover_area_and_emissions", 
                                 min_cover = 30, min_size = 1)
  # Indicateurs de relief de terrain
  grille_mada <- calc_indicators(x = grille_mada,
                               indicators = c("tri", "elevation"),
                               stats_tri = "mean", stats_elevation = "mean")
  # Indicateurs d'incendies
  grille_mada <- calc_indicators(x = grille_mada,
                                 "active_fire_counts")
  grille_mada <- calc_indicators(x = grille_mada,
                                 "active_fire_properties")
  
  # Sauvegarde du résultat
  save(grille_mada, file = "data_s3/grille_mada_donnees_raster.rds")
}

# Assemblage des deux cartes ---------------------------------------------------
tmap_arrange(carte_grille, carte_zoom, ncol = 2) 

On peut représenter sous forme de cartes et d’histogrammes les différentes valeurs des indicateurs générés à partir des données satellitaires.

Code
if (file.exists("data/carte_mailles.rds")) {
  load("data/carte_mailles.rds")
} else {
  grille_mada_summary <- grille_mada %>%
    # On met à plat les données de distance
    unnest(cols = c(traveltime, soilproperties, tri, elevation),
           names_repair = "universal") %>%
    select(-distance, -layer, -depth, -stat,  -active_fire_counts, 
           -active_fire_properties) %>%
    rename(distance_minutes_5k_110mio = minutes_mean, mean_clay_5_15cm = mean) 
  
  grille_mada_summary <- grille_mada_summary %>%
    unnest(cols = treecover_area_and_emissions) %>%
    pivot_wider(names_from = "years", values_from = c("treecover", "emissions")) %>%
    mutate(var_treecover = (treecover_2020 - treecover_2000)/treecover_2000,
           sum_emissions = rowSums(across(starts_with("emission")), na.rm = T)) %>%
    rename(init_treecover_2000 = treecover_2000) %>% # pour le garder
    select(-starts_with("treecover"), -starts_with("emission")) %>%
    rename(treecover_2000 = init_treecover_2000) %>%
    relocate(geometry, .after = last_col())
  
  carte_acces <- tm_shape(grille_mada_summary) +
    tm_fill("distance_minutes_5k_110mio",
            title = "Distance ville (>5K hab)",
            palette = "Oranges",
            style = "fisher",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6,
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_sol <- tm_shape(grille_mada_summary) +
    tm_fill("mean_clay_5_15cm",
            title = "Sol argileux (5-15cm prof)",
            palette = "YlOrBr",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_TRI <- tm_shape(grille_mada_summary) +
    tm_fill("tri_mean",
            title = c("Terrain accidenté (TRI)"),
            palette = "Blues",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6,
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_alt <- tm_shape(grille_mada_summary) +
    tm_fill("elevation_mean",
            title = "Altitude",
            palette = "Purples",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6,
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_cover <- graph_alt <- tm_shape(grille_mada_summary) +
    tm_fill("treecover_2000",
            title = "Couvert arboré en 2000",
            palette = "Greens",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6,
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_loss <- graph_alt <- tm_shape(grille_mada_summary) +
    tm_fill("var_treecover",
            title = "Perte couvert (2000-2020)",
            palette = "Reds",
            n = 8,
            legend.hist = TRUE) +
    tm_layout(legend.outside = TRUE,
              # legend.title.size = 0.8,
              # legend.text.size = 0.6,
              legend.hist.width = 1,
              legend.hist.height = 1)
  
  carte_mailles <- tmap_arrange(carte_acces, carte_sol, 
                                carte_alt, carte_TRI, 
                                carte_cover, carte_loss,
                                ncol = 2, nrow = 3) %>%
    tmap_save("data/carte_mailles.png")
}

carte_mailles

Les cartes et histogrammes ci-dessus illustrent la distribution des variables spatiales calculées par hexagones.

4.3 Croisement des données d’aires protégées et satellitaires

On peut maintenant associer les données d’aires protégées aux hexagones afin de les croiser avec les indicateurs issus des données satellitaires déjà calculés pour ces hexagones.

Code
if (file.exists("data/grille_mada_AP.rds")) {
  load("data/grille_mada_AP.rds")
} else {
  # On charge les données d'aires protégées élaborées au chapitre précédent
  load("data/ch3_AP_Vahatra.rds")
  
  # On prépare ces données pour les joindre avec celles en mailles
  aires_prot_mada <- AP_Vahatra %>%
    st_make_valid() %>% # Corrige les erreurs topo dans certains polygones
    mutate(AP_ligne = row_number()) %>% # Intègre le numéro de ligne dans un champ
    mutate(an_creation = year(date_creation)) # passe les dates en années
  
  # Le code suivant va asocier les hexagones aux aires protégées en se référant
  # aux AP par leur rang dans la table des AP. On voudra plutôt leur identifiant, 
  # alors on crée une table d'équivalence rang/identifiant 
  aires_prot_mada_rang_id <- aires_prot_mada %>%
    st_drop_geometry() %>% # Enlève l'information spatiale
    select(AP_ligne, nom)
  
  
  # On sélectionne des infos additionnelles qu'on va inclure dans les données
  info_vahatra_a_inclure <- aires_prot_mada %>%
    st_drop_geometry() %>% # Enlève l'information spatiale
    select(AP_ligne, nom, an_creation, cat_iucn = cat__iucn, 
           gestionnaire = gest_2) # On ne garde que les variables d'intérêt
  
  # Pour chaque hexagone, on va maintenant identifier s'ils touchent ("intersect")
  # ou s'ils sont strictiement inclus dans ("within") une aire protégé
  grille_mada_AP <- grille_mada %>%
    mutate(AP_ligne = st_intersects(., aires_prot_mada), # liste des n° de lignes d'AP qui recoupent
           AP_ligne = map(AP_ligne, 1), # On extrait le 1° élément de la liste (toutes n'ont qu'1 élément)
           AP_ligne = as.integer(as.character(AP_ligne))) %>%  # formattage en numérique
    left_join(aires_prot_mada_rang_id, by = "AP_ligne") %>% # récupère l'id de l'AP
    rename(AP_touche = nom) %>% # on renomme pour différentier
    mutate(AP_ligne = st_within(., aires_prot_mada),
           AP_ligne = map(AP_ligne, 1),
           AP_ligne = as.integer(as.character(AP_ligne))) %>%
    left_join(aires_prot_mada_rang_id, by = "AP_ligne") %>%
    rename(AP_inclus = nom) %>%
    select(-AP_ligne) 
  
  grille_mada_AP <- grille_mada_AP %>%
    st_sf() %>%
    mutate(position_ap = ifelse(is.na(AP_touche), "Extérieur",
                                ifelse(!is.na(AP_inclus), "Intérieur",
                                       "Frontière")),
           ref_AP = ifelse(position_ap == "Intérieur", AP_inclus, 
                           ifelse(position_ap == "Frontière", AP_touche, NA))) %>%
    left_join(info_vahatra_a_inclure, by = c("ref_AP" = "nom")) %>%
    relocate(geometry, .after = last_col()) 
  
  grille_mada_AP <- grille_mada_AP %>%
    # On met à plat les données de distance
    unnest(cols = c(traveltime, soilproperties, tri, elevation),
           names_repair = "universal") %>%
    select(-distance, -layer, -depth, -stat,  -active_fire_counts, 
           -active_fire_properties) %>%
    rename(distance_minutes_5k_110mio = minutes_mean, mean_clay_5_15cm = mean)
  save(grille_mada_AP, file = "data/grille_mada_AP.rds")
}

# Une vue après classification
tm_shape(grille_mada_AP) +
  tm_fill(col = "position_ap", title = "par rapport aux aires protégées") +
  tm_layout(main.title = "Localisation des hexagones",
            # NB : position en minuscules pour laisser un espace avec la marge
            main.title.position = c("center", "top"),
            main.title.size = 1,
            legend.position = c("left", "top"),
            legend.outside = FALSE)

La suite de l’analyse au chapitre Chapter 8