4  Création d’une base réglementaire à partir de CNLEGIS

4.1 Objet

Le processus de création, modification ou disparition des aires protégées passe par des lois, décrets et arrêtés officels. Ces éléments réglementaires sont dispersés dans divers textes juridiques, que nous allons ici collecter et consolider à partir de la base du Centre National d’Informatoin et de Documentation Legislative et Juridique (CNLEGIS). Cette instance centralise les textes réglementaires à Madagascar et propose un moteur de recherche permettant de trouver les documents pertinents en fonction de recherches full-text.

4.2 Extraction des textes pertinents

La base CNLEGIS ne propose pas d’API, donc l’extraction a dû être réalisée manuellement. Nous avons simplement entré la requête “aire protégée”, ce qui a retourné 131 résultats. Nous avons extrait les 14 pages web correspondantes. Le code ci-dessous extrait l’information pertinente, la nettoie et la restitue sous forme de tableaux.

Code
library(rvest)
library(tidyverse)
library(sf)

# Specify the source repository 
data_dir <- "sources/"
# Directory containing the HTML files
input_dir <- paste0(data_dir, "Décrets/CNLEGIS")


# Columns to select
keep_columns <- c(
  "DATE(S)", "DATE TEXTES", "TEXTE(S)", "TYPE", "NUM TEXTE", "NUM", "OBJET",
  "OBJET MG", "NUM JO", "DATE JO", "DATE JO FR", "PAGE JO", "ETAT", "NOTE(S)",
  "NOTES MG", "DOC PDF FR", "DOC PDF MG", "MINISTERE(S)", "id", "HTML FR", 
  "HTML MG")
keep_types <- c(
  "Constitution", "Loi constitutionnelle", "Loi organique", "Loi",
  "Ordonnance", "Décret", "Arrêté", "Circulaire", "Décision",
  "Instruction", "Note", "Délibération", "Arrêt", "Avis", "Palmarès",
  "Procès verbal", "Exposé des motifs de la loi", "Jugement", "Déclaration")

# Function to clean column names
clean_column_names <- function(names) {
  names %>%
    str_replace_all("\\(|\\)", "") %>%  # Remove parentheses
    str_replace_all(" ", "_") %>%      # Replace spaces with underscores
    str_to_lower()                     # Convert to lowercase
}



# Function to extract the desired table from a single HTML file
extract_table <- function(file_path) {
  # Read the HTML file
  html <- read_html(file_path)
  
  # Extract the table
  table <- html %>%
    html_node("table#Lst_docs") %>%
    html_table(fill = TRUE)
  
  
  # Select the relevant columns
  selected_columns <- table %>%
    select(all_of(keep_columns)) %>%
    filter(TYPE %in% keep_types)
  
   colnames(selected_columns) <- clean_column_names(colnames(selected_columns))

      # Ensure all columns are converted to text
   selected_columns<- selected_columns %>%
     mutate(across(everything(), as.character))
   
  return(selected_columns)
}

# Get a list of all HTML files in the directory
html_files <- list.files(input_dir, pattern = "\\.html$", full.names = TRUE)

# Apply the extraction function to all files
all_results <- map_dfr(html_files, extract_table) %>%
  mutate(date_txt = dmy(date_textes), # Convertit les dates
         date_jo = dmy(date_jo)) %>%
  relocate(date_txt, date_jo, .before = everything()) # place les dates au début

all_results |>
  select(-html_fr, -html_mg, -objet_mg, -textes, -num, -doc_pdf_fr, -doc_pdf_mg) |> 
   DT::datatable(height = 10)

4.3 Classification par type de réglement

Pour mieux organiser les données extraites, les textes sont classifiés selon le type de transformation qu’elles opèrent (création, modification, prorogation, etc.) à l’aide de règles de typologie.

Code
# Add typology columns
class_results <- all_results %>%
  mutate(
    creation_definitive = str_detect(
      textes, regex("(?<!en )(création|crétion de l'aire protégée)", 
                    ignore_case = TRUE)),
    modifiant = str_detect(textes, regex("modifiant", ignore_case = TRUE)),
    prorogation = str_detect(textes, regex("prorog", ignore_case = TRUE)),
    delegation_gestion = str_detect(
      textes, regex("délégation de gestion", ignore_case = TRUE)),
    mise_protection_temporaire = str_detect(
      textes, regex("protection temporaire", ignore_case = TRUE)),

    nomination = str_detect(textes, regex("nomination", ignore_case = TRUE))) %>%
mutate(
    total_true = rowSums(select(., creation_definitive:nomination), na.rm = TRUE),
    concatenated_true = pmap_chr(
      select(., creation_definitive:nomination),
      ~ paste(names(which(c(...))), collapse = ", ")
    ),
    .before = everything()
  ) %>%
  filter(!(total_true == 0)) %>%
  filter(!(total_true == 1 & nomination)) %>% # On enlève le copil Sydney
  filter(!(str_detect(textes, "fonctionnement du Comité de Pilotage"))) %>%
  filter(!(str_detect(textes, "fonctionnement de la Commission"))) %>%
  filter(!(str_detect(textes, "organisation du Comité")))

arr_creation_pre2008 <- class_results %>%
  filter(year(date_jo) < 2008 & (date_jo) > 2008 &
           (mise_protection_temporaire | creation_definitive)) %>%
  arrange(date_jo)

Deux exceptions méritent d’être relevées :

Ces deux textes concernent une liste très étendue de sites potentiels, qui ne sont pas nommément listés dans le texte. On a juste une carte (peu précise) et une somme de zones concernées (en nombre et en surface).

4.4 Appariement des noms d’aires protégées entre CNLEGIS et SAPM

On va avoir besoin de rapprocher ces textes juridiques des données spatiales SAPM et WDPA. On doit le faire à partir des noms des aires protégées, mais ceux-ci ne sont pas normalisés. Une première étape essentielle consiste à extraire ces noms et à les nettoyer avant de procéder à la recherche de correspondances avec les bases spatiales.

Code
class_results <- class_results %>%
  mutate(
    pa = str_extract(
      textes, regex("[\"«](.*?)[\"»,]|nommée\\s*(.*?)[»,]", ignore_case = TRUE)
    ),
    pa = ifelse(
      is.na(pa),
      str_extract(
        textes, regex("nommée\\s*(.*?)[\"]", ignore_case = TRUE)
      ),
      pa
    ),
    pa = ifelse(
      str_detect(textes, "respectivement"),
      str_extract(
        textes,
        regex("respectivement\\s+(.*?)(?=\\.|N°|ETAT)", ignore_case = TRUE)
      ),
      pa
    ),
    pa = str_replace(pa, "\"Complexe des Aires", "Complexe des Aires"), 
    pa = str_remove(pa, "^nommée\\s*"),  # Supprime "nommée"
    pa = str_remove(pa, "^respectivement\\s*"),  # Supprime "nommée"
    pa = str_remove(pa, "(?<=\")[^\"«]*$"),  # Supprime tout après le 2e guillemet
    pa = str_remove_all(pa, "[\"«»]"),  # Supprime les quotes
    pa = str_trim(pa),  # Supprime les espaces en trop
    pa = str_remove(pa, ",$"),
    pa = str_remove(pa, "^'"),
    .before = textes
  )

Une fois les noms des aires protégées concernées extraites de chaque texte, on les compare aux noms de la base SAPM. On le fait tout d’abord automatiquement avec un algorithme d’appariement approximatif (“fuzzy matching”), qui tient compte des variations d’orthographe ou des dénominations multiple.

Code
library(stringdist)
library(fuzzyjoin)


sapm_2017 <- read_rds("data/sapm_2017.rds")



sapm_2017 %>%
  pluck("SHORT_NAME")
  [1] "Ambararata Londa"             "Ambatotsirongorongo"         
  [3] "Ambohidray"                   "Ambohijanahary\n"            
  [5] "Angavo"                       "Behara Tranomaro"            
  [7] "Bemarivo"                     "Bongolava"                   
  [9] "Kasijy"                       "Maningozy"                   
 [11] "Ranobe bay"                   "Ranobe PK32"                 
 [13] "Sud-Ouest Ifotaky"            "Tampoketsa d'Analamaitso\n"  
 [15] "Vohidefo"                     "Mahavavy Kinkony"            
 [17] "Mangoky Ihotry"               "Tsitongambarika"             
 [19] "Torotorofotsy"                "Beanka"                      
 [21] "Sahafina"                     "Velondriake"                 
 [23] "Ambodivahibe"                 "Ankeniheny-Zahamena"         
 [25] "Ambositra-Vondrozo"           "Tampolo"                     
 [27] "Bombetoka Beloboka"           "Ambondrobe"                  
 [29] "Lac Alaotra"                  "Rivière Nosivolo"            
 [31] "Allée des Baobabs"            "Andrafiamena Andavakoera"    
 [33] "Anjozorobe-Angavo"            "Loky Manambato"              
 [35] "Menabe Antimena"              "Maromizaha"                  
 [37] "Tsinjoriake"                  "Nosy Antsoha"                
 [39] "Ampanangandehibe-Behasina"    "Analabe Betanatanana"        
 [41] "Mahialambo"                   "Mangabe-Ranomena-Sahasarotra"
 [43] "Agnakatrika"                  "Agnalazaha"                  
 [45] "Ampasindava"                  "Analalava"                   
 [47] "Ankarabolava"                 "Alandraza Analavelo"         
 [49] "Galoko-Kalobinono"            "Makirovana Tsihomanaomby"    
 [51] "Ibity"                        "Oronjia"                     
 [53] "Pointe à Larrée"              "Vohidava-Betsimalaho"        
 [55] "Antrema"                      "Ambatovaky"                  
 [57] "Ambohitantely"                "Analamazaotra"               
 [59] "Analamerana"                  "Andohahela"                  
 [61] "Andranomena"                  "Andringitra\n"               
 [63] "Anjanaharibe-Sud"             "Ankarafantsika"              
 [65] "Ankarana"                     "Baie de Baly"                
 [67] "Bemaraha"                     "Betampona"                   
 [69] "Bezà-Mahafaly"                "Bora"                        
 [71] "Cap Sainte Marie"             "Sahamalaza-Iles Radama"      
 [73] "Isalo"                        "Kalambatritra"               
 [75] "Kirindy Mité"                 "Lokobe\n"                    
 [77] "Mananara Nord\n"              "Mangerivola"                 
 [79] "Manombo"                      "Manongarivo"                 
 [81] "Mantadia"                     "Marojejy"                    
 [83] "Marolambo"                    "Marotandrano"                
 [85] "Masoala"                      "Befotaka-Midongy du Sud"     
 [87] "Mikea"                        "Montagne d'Ambre"            
 [89] "Nosy Hara"                    "Nosy Mangabe"                
 [91] "Nosy Tanikely"                "Nosy Ve Androka"             
 [93] "Pic d'Ivohibe"                "Ranomafana"                  
 [95] "Tsaratanàna"                  "Tsimanampesotse"             
 [97] "Namoroka"                     "Zahamena"                    
 [99] "Zombitse-Vohibasia"           "Ambatoatsinanana"            
[101] "Petriky"                      "Mandena"                     
[103] "Itremo"                       "Montagne des Français"       
[105] "Tsimembo Manambolomaty"       "Mandrozo"                    
[107] "Bemanevika"                   "Mahimborondro"               
[109] "Manjakatompo Ankaratra"       "Andreba"                     
[111] "Ankarea"                      "Ankivonjy"                   
[113] "Makira"                       "Soariake"                    
[115] "Amoron'i Onilahy"             "Ankodida"                    
[117] "COMATSA Nord"                 "COMATSA Sud"                 
[119] "Nord-Ifotaka"                 "Iles Barren"                 
[121] "Ambatofotsy"                  "Ampotaka-Ankorabe"           
[123] "Analalava"                   
Code
class_results %>%
  pluck("pa")
  [1] "Ankafobe"                                                                                                                                          
  [2] "Ivohiboro"                                                                                                                                         
  [3] "Tsinjoarivo Ambalaomby"                                                                                                                            
  [4] "Ankafobe"                                                                                                                                          
  [5] "Anjajavy"                                                                                                                                          
  [6] "Oronjia"                                                                                                                                           
  [7] "Anjajavy"                                                                                                                                          
  [8] "Foret Naturelle de Tsitongambarika"                                                                                                                
  [9] "Complexe Zones Humides Mahavavy Kinkony"                                                                                                           
 [10] "Ambodivahibe"                                                                                                                                      
 [11] "Maromizaha"                                                                                                                                        
 [12] "Nosy Antsoha"                                                                                                                                      
 [13] "Ambohitr' Antsingy Montagne des Français"                                                                                                          
 [14] "Paysage Harmonieux Protégée Bemanevika"                                                                                                            
 [15] "Sakara"                                                                                                                                            
 [16] "Anjajavy"                                                                                                                                          
 [17] "Behara Tranomaro, Sud-Ouest Ifotaky, Torotorofotsy, Ambohidray, Angavo, Ranobe Bay, Vohidefo, Bombetoka Belobaka, Ranobe PK 32 et Ambararata Londa"
 [18] "Ambodivahibe"                                                                                                                                      
 [19] "Maromizaha"                                                                                                                                        
 [20] "Nord-Ifotaka"                                                                                                                                      
 [21] "Amoron'i Onilahy"                                                                                                                                  
 [22] "Ambatotsirongorongo"                                                                                                                               
 [23] "Ankarabolava"                                                                                                                                      
 [24] "Agnakatrika"                                                                                                                                       
 [25] "Ampanangandehibe-Behasina"                                                                                                                         
 [26] "Ampanangandehibe-Behasina"                                                                                                                         
 [27] "Analalava"                                                                                                                                         
 [28] "Corridor Ankeniheny-Zahamena"                                                                                                                      
 [29] "Corridor Forestier Ambositra-Vondrozo"                                                                                                             
 [30] "Lac Alaotra"                                                                                                                                       
 [31] "Rivière Nosivolo"                                                                                                                                  
 [32] "Loky Manambato"                                                                                                                                    
 [33] "Menabe Antimena"                                                                                                                                   
 [34] "Complexe Anjozorobe-Angavo"                                                                                                                        
 [35] "Massif d'Ibity"                                                                                                                                    
 [36] "Réserve Spéciale Pointe à Larrée"                                                                                                                  
 [37] "Vohidava-Betsimilaho"                                                                                                                              
 [38] "Ambohitr'Antsingy Montagne des Français"                                                                                                           
 [39] "Tsinjoriake"                                                                                                                                       
 [40] "Mahialambo"                                                                                                                                        
 [41] "Analabe Betanatanana"                                                                                                                              
 [42] "Allée des Baobabs"                                                                                                                                 
 [43] "Andrafiamena Andavakoera"                                                                                                                          
 [44] "Nosy Antsoha"                                                                                                                                      
 [45] "Velondriake"                                                                                                                                       
 [46] "Ampasindava"                                                                                                                                       
 [47] "Galoko-Kalobinono"                                                                                                                                 
 [48] "Agnalazaha"                                                                                                                                        
 [49] "Makirovana Tsihomanaomby"                                                                                                                          
 [50] "Analalava"                                                                                                                                         
 [51] "Forêt Sacrée Alandraza Anavelo"                                                                                                                    
 [52] "Forêt Naturelle de Petriky"                                                                                                                        
 [53] "Ambatoatsinanana"                                                                                                                                  
 [54] "Ankodida"                                                                                                                                          
 [55] "Mandena"                                                                                                                                           
 [56] "Complexe des Aires Protégées Ambohimirahavavy"                                                                                                     
 [57] "Complexe Zones Humides Mangoky Ihotry"                                                                                                             
 [58] "Ankarea"                                                                                                                                           
 [59] "Mangabe-Ranomena-Sahasarotra"                                                                                                                      
 [60] "Beanka"                                                                                                                                            
 [61] "Sahafina"                                                                                                                                          
 [62] "Marolambo"                                                                                                                                         
 [63] "Site Bioculturel d'Antrema"                                                                                                                        
 [64] "Mandrozo"                                                                                                                                          
 [65] "Marolambo"                                                                                                                                         
 [66] "Nosy Ve Androka"                                                                                                                                   
 [67] "Ankivonjy"                                                                                                                                         
 [68] "Complexe Tsimembo Manambolomaty"                                                                                                                   
 [69] "Complexe Zones Humides Mahavavy Kinkony"                                                                                                           
 [70] "Soariake"                                                                                                                                          
 [71] "Ambatofotsy"                                                                                                                                       
 [72] "Ampotaka-Ankarabe"                                                                                                                                 
 [73] "Corridor Ankeniheny-Zahamena"                                                                                                                      
 [74] "Corridor Forestier Ambositra-Vondrozo"                                                                                                             
 [75] "Iles Barren"                                                                                                                                       
 [76] "Loky Manambato"                                                                                                                                    
 [77] "Complexe Mahavavy Kinkony"                                                                                                                         
 [78] "Menabe Antimena"                                                                                                                                   
 [79] "Corridor Marojejy Anjanaharibe Tsaratanana"                                                                                                        
 [80] "Nouvelle Aire Protégée Manjakatompo-Ankaratra"                                                                                                     
 [81] "Corridor Ankeniheny Zahamena"                                                                                                                      
 [82] "Corridor Forestier Ambositra Vondrozo"                                                                                                             
 [83] "Makira"                                                                                                                                            
 [84] "Complexe de zones humides de la Mangoky"                                                                                                           
 [85] "Complexe Manambolomaty"                                                                                                                            
 [86] "Massif d'Ibity"                                                                                                                                    
 [87] "Forêt de Tsitongambarika"                                                                                                                          
 [88] "Alan' i Vohibe"                                                                                                                                    
 [89] "Ranobe PK 32"                                                                                                                                      
 [90] "Allée des Baobab"                                                                                                                                  
 [91] "Nord-lfotaky"                                                                                                                                      
 [92] "Tampolo"                                                                                                                                           
 [93] "Ambatotsirongorongo"                                                                                                                               
 [94] "Mandena"                                                                                                                                           
 [95] "Kodida"                                                                                                                                            
 [96] "Ambato Atsinanana (Sainte Luce)"                                                                                                                   
 [97] "Couloir forestier d’Anjozorobe-Angavo"                                                                                                             
 [98] "Complexe Mikea"                                                                                                                                    
 [99] "Complexe Mahavavy-Kinkony"                                                                                                                         
[100] "Amoron'i Onilahy"                                                                                                                                  
[101] "Lac Alaotra"                                                                                                                                       
[102] "Complexe Andreba"                                                                                                                                  
[103] "Makira"                                                                                                                                            
[104] "Corridor Forestier Ankeniheny-Zahamena"                                                                                                            
[105] "Couloir forestier d’Anjozorobe-Angavo"                                                                                                             
[106] "Corridor Forestier Bongolava"                                                                                                                      
[107] "Montagne des Français"                                                                                                                             
[108] "Corridor Forestier Fandriana- Vondrozo"                                                                                                            
[109] "Forêt d'Analalava"                                                                                                                                 
[110] "Mandena"                                                                                                                                           
[111] "Ambato-Antsinanana (Sainte-Luce)"                                                                                                                  
[112] "Menabe Central"                                                                                                                                    
[113] "Nord-Ifotaky"                                                                                                                                      
[114] "Kodida"                                                                                                                                            
[115] "Corridor Forestier Ankeniheny-Zahamena"                                                                                                            
[116] "Makira"                                                                                                                                            
[117] "Couloir Forestier d'Anjozorobe-Angavo"                                                                                                             
[118] "Massif d'Itremo"                                                                                                                                   
Code
# Harmonization for comparison (without modifying original data)
sapm_clean <- sapm_2017 %>%
  mutate(cleaned_SHORT_NAME = str_trim(str_to_lower(SHORT_NAME)))

class_clean <- class_results %>%
  mutate(cleaned_pa = str_trim(str_to_lower(pa)))

# Split cells containing multiple names (both ", " and " et " as separators)
split_class_clean <- class_clean %>%
  mutate(cleaned_pa_split = str_split(cleaned_pa, ",\\s*|\\s+et\\s+")) %>%  # Split on commas or " et "
  unnest(cleaned_pa_split) %>%
  rename(single_cleaned_pa = cleaned_pa_split)  # Rename for clarity

# Function to compute proportional string distance
match_pa_to_sapm <- function(pa_name, sapm_names) {
  # Calculate proportional distances
  distances <- stringdist(
    a = pa_name,
    b = sapm_names,
    method = "lv"  # Levenshtein distance
  ) / pmax(nchar(pa_name), nchar(sapm_names))
  
  # Find the best match
  best_match_idx <- which.min(distances)
  list(
    closest_match = sapm_names[best_match_idx],
    match_distance = distances[best_match_idx]
  )
}

# Find closest matches for each split name
conversion_table <- split_class_clean %>%
  rowwise() %>%
  mutate(
    match_result = list(match_pa_to_sapm(single_cleaned_pa, sapm_clean$cleaned_SHORT_NAME)),
    closest_match = sapm_clean$SHORT_NAME[which.min(
      stringdist(single_cleaned_pa, sapm_clean$cleaned_SHORT_NAME) / 
        pmax(nchar(single_cleaned_pa), nchar(sapm_clean$cleaned_SHORT_NAME))
    )],
    match_distance = min(
      stringdist(single_cleaned_pa, sapm_clean$cleaned_SHORT_NAME) /
        pmax(nchar(single_cleaned_pa), nchar(sapm_clean$cleaned_SHORT_NAME))
    )
  ) %>%
  ungroup() %>%
  select(original_pa = single_cleaned_pa, closest_match, match_distance)


writexl::write_xlsx(conversion_table, "conversion_table.xlsx")

Cette base est envoyée vers excel et on effectue une vérification manuelle pour avoir une liste complète des rapprochements.