2  Harmonisation des données SAPM

Dans cette section, nous passons en revue plusieurs jeux de données ayant documenté les aires protégées à Madagascar à différentes périodes. Ces données, issues de sources officielles, ont été produites et mises à jour par différentes institutions. Nous les harmonisons en appliquant un ensemble de traitements standardisés :

Ces opérations visent à rendre comparables des jeux de données produits à des moments différents et selon des logiques institutionnelles variées. Elles permettent la construction d’une série temporelle cohérente, indispensable à toute analyse historique ou comparative sur les dynamiques de conservation à Madagascar.

Ces opérations sont réalisées avec R, avec une série de package appropriés.

Code
# On charge les librairies nécessaires
library(tidyverse) # Aide à la manipulation de données
library(sf) # Pour traiter des données spatiales (vectorielles)
library(tmap) # Pour faire de jolies cartes
library(gt) # Pour faire de jolis tableaux

# Specify the source repository 
data_dir <- "sources/"
# Specify CRS
my_crs <- 29702

# Cleans PA name columns only (e.g. NOM, SHORT_NAME, etc.)
# You must specify which columns to clean
clean_pa_names_cols <- function(df, name_cols) {
  df %>%
    mutate(across(
      all_of(name_cols),
      ~ .x %>%
        str_replace_all("[\r\n\t]", " ") %>%  # replace line breaks, carriage returns, tabs with space
        str_squish() %>%                      # collapse multiple spaces into one
        str_trim()                           # remove leading/trailing whitespace
    ))
}

2.1 ANGAP 2002

Avant la création du SAPM, l’ANGAP était la seule entité chargée de gérer les aires protégées, et donc leurs données. L’ANGAP avait dans ce cadre transmis à l’institut national de géographie malgache (FTM) pour la mise à jour de la base de donnée topographique (BD 500).

Code
# Load and preprocess the shapefile
angap_2002 <- st_read(paste0(data_dir, "ap_angap BD 500/ap_angap.shp"), 
quiet = TRUE) %>%
  st_set_crs(my_crs) %>%  # Laborde Magasiaraka CRS
  clean_pa_names_cols(name_cols = "NOM") %>%
  mutate(has_info = ifelse(is.na(CLASSEMENT), 
                           "Attributs absents", 
                           "Attributs présents"),
         has_name = case_when(is.na(NOM) ~ "Pas de nom",
                              NOM == "Hors AP" ~ "Hors AP",
                              .default = "Nommé"))

# Separate datasets: "Pas de nom", "Hors AP", and "Nommé"
angap_pas_de_nom <- angap_2002 %>% filter(has_name == "Pas de nom")
angap_hors_ap <- angap_2002 %>% filter(has_name == "Hors AP")
angap_named <- angap_2002 %>% filter(has_name == "Nommé")

# Set tmap to interactive mode
tmap_mode("view")

# Create the map with three layers
tm_shape(angap_named) +
  tm_fill(col = "has_info", 
          id = "NOM",
          popup.vars = names(st_drop_geometry(angap_named))) +
  tm_borders(col = "black", lwd = 0.5) +  # Default borders for named features
tm_shape(angap_pas_de_nom) +
  tm_fill(col = "has_info", 
          id = "NOM",
          popup.vars = names(st_drop_geometry(angap_pas_de_nom)),
          legend.show = FALSE) +
  tm_borders(col = "red", lwd = 3) +  # Thick red borders for "Pas de nom"
tm_shape(angap_hors_ap) +
  tm_fill(col = "has_info", 
          id = "NOM",
          popup.vars = names(st_drop_geometry(angap_hors_ap)),
          legend.show = FALSE) +
  tm_borders(col = "blue", lwd = 3) + # Thick blue borders for "Hors AP"
  tm_add_legend(type = "fill", 
                labels = c("Pas de nom", "Hors AP"), 
                col = c("red", "blue"), 
                lwd = c(3, 3, 0.5), 
                title = "Contour")

Le champ “classement” de ce jeu de données fait référence à des décrets parus jusqu’en 1998, mais il fait aussi référence à 11 entités qui n’ont pas de métadonnées les caractérisant.

Parmi celles-ci, 6 n’ont pas de nom :

  • Le Parc National d’Ankarafantsika, né en 2002 de la fusion de la réserve naturelle intégrale n°15 d’Ankarafantsika et de la réserve - forestière d’Anktarafantsika.
  • Mantadia, dont les limites ont été étendues en 2002.
  • Tsingy de Namoroka, créé en 1927 et devenu parc national en 2002.

Le jeu de données n’inclut pas de métadonnées explicitant sa date, mais il nous a été remis avec l’explication qu’il s’agissait d’un jeu datant de 2002. Cette assertion est cohérente avec le fait que le jeu de donnée inclut l’extension d’Ankarafantsika opérée en 2002, mais pas les création [Compléter].

On effectue donc les opération suivantes : identification des polygones sans nom qui font visiblement partie d’une AP, regroupement des polygones simples en multipolygones s’ils ont les mêmes attributs, et suppression des polygones explicitement référencés comme “Hors AP”.

Code
angap_2002 <- angap_2002 %>%
  select(APBON = APBON_, APBON_ID, NOM, TYPE_AP, CLASSEMENT, IUCN) %>%
  mutate(NOM = case_when( # Rename the patches visibly included in PAs
    APBON_ID %in% c(100006, 100005) ~ "Ranomafana",
    APBON_ID %in% c(100002, 100003, 100004) ~ "Masoala",
    APBON == 27 ~ "Baie de Baly",
    .default = NOM)) %>%
  group_by(across(NOM:IUCN)) %>%
  summarize(geometry = st_union(geometry)) %>%
  ungroup() %>%
  filter(NOM != "Hors AP") # Discard the explicitely labelled as not PA

write_rds(angap_2002, "data/no_id/angap_2002.rds")

tm_shape(angap_2002) +
  tm_polygons()

Le résultat est présenté dans la carte ci-dessus.

2.2 SAPM 2010

Le site REBIOMA qui mettait en ligne les données du SAPM ne fonctionne plus depuis 2019 d’après les sauvegardes régulières effectuées par Internet Archive. Fort heureusement, une copie des données datées du 10/12/2010 a été sauvegardée par Ghislain Vieilledent et publié en accompagnement d’une publication (Vieilledent et al. 2020).

Code
sapm_2010 <- st_read(paste0(data_dir,"Vieilledent_sapm_20101220/AP-NAP_38s.shp"), 
                     quiet = TRUE, options = "ENCODING=WINDOWS-1252") %>% 
    st_transform(my_crs) %>% 
    clean_pa_names_cols(name_cols = "NOM")

write_rds(sapm_2010, "data/no_id/sapm_2010.rds")

tm_shape(sapm_2010) +
  tm_borders(col = "purple") +
  tmap_options(check.and.fix = TRUE)

2.3 Evolution du SAPM 2002-2011

Ce jeu de données a été publié en ligne par un consultant du SAPM, avec la référence suivante : Evolution of the Madagascar Protected Area System (SAPM), Tom Allnut, https://hub.arcgis.com/content/4218737646234c7cab1c7a20e2c2489d/about

It is provided as a .mpk, which is in fact a zip including shapefiles. We convert to Zip and open the shapefiles.

Code
# Specify the directory path
dir_path <- paste0(data_dir, "sapm_evolution_2012/commondata/data1")

# List all .shp files in the directory
shapefile_paths <- list.files(path = dir_path, 
                              pattern = "\\.shp$", 
                              full.names = TRUE, 
                              recursive = TRUE)

# Extract the filenames without the full path
shapefile_names <- basename(shapefile_paths)

# Filter shapefiles with a date in the filename (e.g., "2008", "2009", etc.)
date_shapefiles <- shapefile_paths[str_detect(shapefile_names, "20[0-9]{2}")]

# Load and bind the shapefiles
sapm_evol_2001_2011 <- date_shapefiles %>%
  map(~ st_read(.x, quiet = TRUE) %>% # Read each shapefile
        clean_pa_names_cols(name_cols = "NOM")) %>% # and clean PA name
  reduce(bind_rows) %>%
  mutate(YEAR = str_extract(YEAR, "\\d{4}")) %>% # Extract the 4-digit year
  st_transform(crs = my_crs) # On passe les données en laborde malgache

sapm_evol_2001_2011 %>%
  filter(YEAR == 2002) %>%
  tm_shape() +
  tm_borders(col = "blue") + 
  tm_shape(angap_2002) +
  tm_fill(col = "red", alpha = 0.5) + 
  tmap_options(check.and.fix = TRUE)
Code
# Save the data to a specified path
write_rds(sapm_evol_2001_2011, "data/no_id/sapm_evol_2001_2011.rds")

La superposition des limites extraites de cette série temporelle avec celles issues de la base ANGAP 2002 permet de visualiser les premiers recouvrements et écarts.

2.4 SAPM 2017

Ce jeu de données nous a été remis par la FAPBM. Il s’agit visiblement des données SAPM mises à jour en mars 2017.

L’un des points du périmètre de l’aire protégée Andrafiamena Andavakoera (index 32, sommet numéro 841) est localisé de manière aberrante, formant un papillon et entraînant des erreurs topologiques. La fonction st_make_valid retire ce point aberrant.

Code
# Load and transform data
sapm_2017 <- st_read(paste0(data_dir, "sapm_201703017_om_surface/sapm_201703017_om_surface.shp"),
                     options = "ENCODING=WINDOWS-1252") %>%
  st_transform(my_crs) %>%
  st_make_valid() %>%
  clean_pa_names_cols(name_cols = c("SHORT_NAME", "FULL_NAME"))
options:        ENCODING=WINDOWS-1252 
Reading layer `sapm_201703017_om_surface' from data source 
  `C:\Users\fbede\Documents\Statistiques\conservation-dynamic-madagascar\sources\sapm_201703017_om_surface\sapm_201703017_om_surface.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 123 features and 23 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 58336.43 ymin: 57373.5 xmax: 833924.5 ymax: 1562970
Projected CRS: laborde
Code
# Save the data
write_rds(sapm_2017, "data/no_id/sapm_2017.rds")

tm_shape(sapm_2017) +
  tm_polygons(col = "CATEG_IUCN")

2.5 Vahatra 2017

Code
load("sources/AP_Vahatra.rds")
AP_Vahatra %>%
  st_transform(my_crs) %>%
  clean_pa_names_cols(name_cols = c("nom", "full_name", "nom_wdpa")) %>%
  write_rds("data/no_id/vahatra.rds")

dans certaines entités.

2.6 SAPM 2024

Ce jeu de données constitue l’état le plus récent de la base SAPM. Il inclut de nouvelles aires protégées récemment créées ainsi que des ajustements sur les périmètres existants.

Code
# Load shapefile
sapm_2024 <- st_read(paste0(data_dir, "Shape AP mai2023/dernier version shape_ap_28042023.shp"), 
                   quiet = TRUE)

# Check for invalid geometries
invalid_geometries <- sapm_2024 %>%
  st_zm(drop = TRUE, what = "ZM") %>%   # <- remove Z and M before anything else
  rownames_to_column(var = "index") %>%
  rowwise() %>%
  mutate(is_valid = st_is_valid(geometry)) %>%
  filter(!is_valid) %>%
  select(Index = index, Nom = SHORT_NAME, Source = path)

# Create a folder to save the images
output_dir <- "invalid_geometry_plots"
dir.create(output_dir, showWarnings = FALSE)

# Loop through invalid geometries and save the plots
for (i in seq_len(nrow(invalid_geometries))) {
  geometry_index <- invalid_geometries$Index[i]
  
  # Generate a file name
  plot_file <- file.path(output_dir, paste0("geometry_", geometry_index, ".png"))
  
  # Save the plot
  png(plot_file)
  plot(st_geometry(sapm_2024[geometry_index, ]))
  dev.off()
}

# Add image file paths to the invalid geometries table
invalid_geometries <- invalid_geometries %>%
  mutate(
    Geometrie = paste0(output_dir, "/geometry_", Index, ".png")
  )

# Display the table with gt
if (nrow(invalid_geometries) == 0) {
  message("All geometries are valid.")
} else {
  invalid_geometries %>%
    st_drop_geometry() %>%
    gt() %>%
    text_transform(
      locations = cells_body(columns = Geometrie),
      fn = function(x) {
        local_image(filename = x, height = 100)  # Embed local image with specified height
      }
    ) %>%
    tab_header(
      title = "Géométries invalides",
      subtitle = "Liste des géométries invalides"
    )
}
Géométries invalides
Liste des géométries invalides
Index Nom Source Geometrie
3 CM7B NA
8 Mangiho NA
12 IVOHIBORO D:\ASA AP\FOREST SHAPE.shp
22 Behara Tranomaro D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
34 Torotorofotsy D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
47 Andrafiamena Andavakoera D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
50 Menabe Antimena D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
61 Analalava D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
65 Makirovana Tsihomanaomby D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
68 Pointe à Larrée D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
70 Antrema D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
107 Pic d'Ivohibe D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
108 Ranomafana D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
112 Zahamena D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
121 Bemanevika D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
131 COMATSA Nord D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
133 Nord-Ifotaka D:\SHAPE AP\Shapfile vaovao\sapm_201703017_dd\sapm_201703017_dd.shp
Code
# Now try to make this valid

sapm_2024_valid <- sapm_2024 %>%
  st_make_valid()

# Identify invalid geometries
invalid_geometries <- sapm_2024_valid %>%
  filter(!st_is_valid(geometry))

#  Print the names (or identifiers) of the invalid geometries
# Replace 'NOM' with the actual column containing the name or identifier
if (nrow(invalid_geometries) > 0) {
  cat("Invalid geometries found:\n")
  print(invalid_geometries$SHORT_NAME)  # Adjust this to match your column name
} else {
  cat("All geometries are valid.\n")
}
Invalid geometries found:
[1] "CM7B"                     "Mangiho"                 
[3] "IVOHIBORO"                "Andrafiamena Andavakoera"
Code
# Step 5: Filter out invalid geometries for now to plot the valid ones
sapm_2024_valid_clean <- sapm_2024_valid %>%
  filter(st_is_valid(geometry)) %>%
  clean_pa_names_cols(name_cols = c("Name", "SHORT_NAME")) %>%
  st_transform(my_crs)

write_rds(sapm_2024_valid_clean, "data/no_id/sapm_2024.rds")

# Step 6: Plot the valid geometries using tmap
tm_shape(sapm_2024_valid_clean) +
  tm_fill(col = "blue", alpha = 0.5) + 
  tm_shape(sapm_2017) +
  tm_fill(col = "red", alpha = 0.5)
Vieilledent, Ghislain, Marie Nourtier, Clovis Grinand, Miguel Pedrono, Alison Clausen, Tsiky Rabetrano, Jean-Roger Rakotoarijaona, et al. 2020. “It’s Not Just Poverty: Unregulated Global Market and Bad Governance Explain Unceasing Deforestation in Western Madagascar.” BioRxiv, 2020–07.