geodist

Dependency-free, ultra fast calculation of geodesic distances.

Marc-Antoine Dupuis

2025-11-25

La géodésie

The shortest path between two points on the earth, customarily treated as an ellipsoid of revolution, is called a geodesic.1

But : un package léger, sans dépendance, pour calculer des distances géodésiques très rapidement.

Est deux fois plus rapides que ses concurrents (du marché gratuit) : SF et Geosphere2

Publié en 2018, fondé notamment sur l’algorithme de Karney (2013).

La conccurence

Avantages de Geodist : rapide, gratuit, flexible, virtuellement aucune limite d’adresses, pouvoir tout faire soi-même, compatibilité avec les milliers de packages R.

Inconvénients : devoir tout faire « soi-même », ne calcule pas les distances routières (Google Maps API), ou les niveaux de terrain (QGIS, Mapbox).

Outil Accessibilité Communauté d'utilisateurs Popularité en sciences sociales Compatibilité avec d'autres outils Transparence et réplicabilité Adaptabilité et flexibilité
sf, geosphere, geodist (packages R) Gratuit, open-source. Large communauté Élevée Excellente Maximale Très élevée
QGIS (Système d'Information Géographique) Gratuit, open-source Logiciel SIG le plus populaire Croissante Excellente Élevée Très élevée
Google Maps API (Application Programming Interface) Gratuit jusqu’à 5000 évènements. 1-10$ / 1000 évènements. Très large. Modérée Bonne Limitée Modérée
Mapbox Gratuit, mais abonnements payants, jusqu’à + 6000$. Large Croissante Excellente Moyenne Très élevée

Geodist en sciences sociales

Exemple pour calculer les distances moyennes entre les principales communautés autochtones du Québec et les services de santé les plus proches. Erreurs possibles : IA a récupéré les adresses.

# ===================================================
# CODE COMBINÉ : CALCUL DE DISTANCES + VISUALISATION
# ===================================================

# Charger les librairies
library(dplyr)
library(tidyr)
library(geodist)
library(ggplot2)

# ===== PARTIE 1 : CALCUL DES DISTANCES =====

# Lire le fichier CSV
donnees <- read.csv("/Users/marcantoinedupuis/Library/CloudStorage/GoogleDrive-2946762@gmail.com/Mon disque/Université/ULAVAL/Outils Numériques/outil_presentation/donnes_clsc.csv", 
                    stringsAsFactors = FALSE)

# Fonction pour séparer les coordonnées
separer_coordonnees <- function(coord_string) {
  coords <- strsplit(gsub(" ", "", coord_string), ",")[[1]]
  return(data.frame(lat = as.numeric(coords[1]), 
                    lon = as.numeric(coords[2])))
}

# Traiter les coordonnées autochtones
coords_autochtones <- do.call(rbind, lapply(donnees$localisation_autochtone, 
                                            separer_coordonnees))
colnames(coords_autochtones) <- c("lat_autochtone", "lon_autochtone")

# Traiter les coordonnées des centres de santé
coords_sante <- do.call(rbind, lapply(donnees$localisation_sante, 
                                      separer_coordonnees))
colnames(coords_sante) <- c("lat_sante", "lon_sante")

# Combiner les données
donnees_completes <- cbind(donnees, coords_autochtones, coords_sante)

# Calculer la distance pour chaque ligne
calcul_distances <- function(data) {
  resultats <- data.frame()

  for (i in 1:nrow(data)) {
    origine <- c(data$lon_autochtone[i], data$lat_autochtone[i])
    destination <- c(data$lon_sante[i], data$lat_sante[i])

    distance_geodesique <- geodist::geodist(
      x = matrix(origine, ncol = 2),
      y = matrix(destination, ncol = 2),
      measure = "haversine"
    ) / 1000

    distance_routiere_approx <- distance_geodesique * 1.35

    resultats <- rbind(resultats, data.frame(
      communaute = data$nom_communaute[i],
      centre_sante = data$institution_sante_proche[i],
      distance_geodesique_km = round(distance_geodesique, 2),
      distance_routiere_approx_km = round(distance_routiere_approx, 2)
    ))
  }

  return(resultats)
}

# Calculer toutes les distances
resultats_distances <- calcul_distances(donnees_completes)

# Afficher les résultats

cat("===== DISTANCES ENTRE COMMUNAUTÉS ET CENTRES DE SANTÉ =====\n\n")
===== DISTANCES ENTRE COMMUNAUTÉS ET CENTRES DE SANTÉ =====
for (i in 1:nrow(resultats_distances)) {
  cat(sprintf("Communauté: %s\n", resultats_distances$communaute[i]))
  cat(sprintf("Centre de santé: %s\n", resultats_distances$centre_sante[i]))
  cat(sprintf("Distance routière approximative: %.2f km\n\n", 
              resultats_distances$distance_routiere_approx_km[i]))
}
Communauté: Odanak
Centre de santé: Centre de Santé d'Odanak
Distance routière approximative: 0.09 km

Communauté: Wôlinak
Centre de santé: Centre de Santé de Wôlinak
Distance routière approximative: 0.09 km

Communauté: Wolf Lake
Centre de santé: CLSC de Témiscaming-Kipawa (Point de service)
Distance routière approximative: 33.73 km

Communauté: Kebaowek First Nation
Centre de santé: Centre de Santé de Kebaowek
Distance routière approximative: 0.14 km

Communauté: Communauté anicinape de Kitcisakik
Centre de santé: Poste de soins infirmiers Kitcisakik
Distance routière approximative: 0.03 km

Communauté: Kitigan Zibi Anishinabeg
Centre de santé: Centre de santé Wanaki
Distance routière approximative: 0.24 km

Communauté: La Nation Anishnabe du Lac-Simon
Centre de santé: Centre de Santé Mino-Tehewin
Distance routière approximative: 0.08 km

Communauté: Conseil de la Première Nation Abitibiwinni
Centre de santé: Centre de Santé de Pikogan
Distance routière approximative: 0.24 km

Communauté: Algonquins of Barriere Lake
Centre de santé: Centre de santé de Lac-Rapide
Distance routière approximative: 0.02 km

Communauté: Timiskaming First Nation
Centre de santé: Centre de Santé Timiskaming
Distance routière approximative: 0.05 km

Communauté: Long Point First Nation
Centre de santé: Centre de santé de Winneway
Distance routière approximative: 0.07 km

Communauté: Atikamekw de Manawan
Centre de santé: Centre de Santé Masko-Siwin
Distance routière approximative: 0.09 km

Communauté: Bande des Atikamekw d'Opitciwan
Centre de santé: Centre de Santé d'Opitciwan
Distance routière approximative: 0.05 km

Communauté: Conseil des Atikamekw de Wemotaci
Centre de santé: Centre de Santé de Wemotaci
Distance routière approximative: 0.09 km

Communauté: La Nation Crie de Chisasibi
Centre de santé: CMC Chisasibi
Distance routière approximative: 104.61 km

Communauté: La Nation Crie d'Eastmain
Centre de santé: CMC Eastmain
Distance routière approximative: 143.22 km

Communauté: La Nation Crie de Mistissini
Centre de santé: CMC Mistissini
Distance routière approximative: 0.79 km

Communauté: La Nation Crie de Nemaska
Centre de santé: CMC Nemaska
Distance routière approximative: 0.09 km

Communauté: Oujé-Bougoumou Cree Nation
Centre de santé: CMC Oujé-Bougoumou
Distance routière approximative: 39.57 km

Communauté: Les Cris de la Première Nation de Waskaganish
Centre de santé: CMC Waskaganish
Distance routière approximative: 0.14 km

Communauté: Waswanipi
Centre de santé: CMC Waswanipi
Distance routière approximative: 3.28 km

Communauté: La Nation Crie de Wemindji
Centre de santé: CMC Wemindji
Distance routière approximative: 1.07 km

Communauté: La Première Nation de Whapmagoostui
Centre de santé: CMC Whapmagoostui
Distance routière approximative: 1.49 km

Communauté: Bande des Innus de Pessamit
Centre de santé: Centre de Santé de Pessamit
Distance routière approximative: 21.52 km

Communauté: Bande Innue Essipit
Centre de santé: Centre de santé communautaire d'Essipit
Distance routière approximative: 75.28 km

Communauté: Première Nation des Pekuakamiulnuatsh
Centre de santé: Centre de Santé de Mashteuiatsh
Distance routière approximative: 37.90 km

Communauté: Bande de la Nation Innu Matimekush-Lac John
Centre de santé: Centre de santé de Matimekush
Distance routière approximative: 10.05 km

Communauté: Bande des Innus de Ekuanitshit
Centre de santé: Centre de santé d'Ekuanitshit
Distance routière approximative: 0.15 km

Communauté: Première Nation des Innus de Nutashkuan
Centre de santé: Centre de santé de Nutashkuan
Distance routière approximative: 3.51 km

Communauté: Bande des Montagnais de Pakua Shipi
Centre de santé: Centre de santé de Pakua Shipi
Distance routière approximative: 374.93 km

Communauté: Montagnais de Unamen Shipu
Centre de santé: Centre de santé d'Unamen Shipu
Distance routière approximative: 48.57 km

Communauté: Innu Takuaikan Uashat mak Mani-Utenam
Centre de santé: Centre de santé Uashat mak Mani-Utenam
Distance routière approximative: 0.06 km

Communauté: La Nation Micmac de Gespeg
Centre de santé: CLSC de Gaspé (Services de santé Gespeg)
Distance routière approximative: 2.86 km

Communauté: Micmacs of Gesgapegiag
Centre de santé: Gesgapegiag Health and Community Services
Distance routière approximative: 0.06 km

Communauté: Listuguj Mi'gmaq Government
Centre de santé: Listuguj Community Health Services
Distance routière approximative: 11.04 km

Communauté: Nation Naskapi de Kawawachikamach
Centre de santé: CLSC Naskapi
Distance routière approximative: 8.27 km

Communauté: Première Nation Wolastoqiyik Wahsipekuk
Centre de santé: CLSC de Rivière-du-Loup (Siège administratif)
Distance routière approximative: 17.79 km

Communauté: Nation Wendat
Centre de santé: Centre de santé Marie-Paule-Sioui-Vincent
Distance routière approximative: 4.29 km

Communauté: Mohawks of Kahnawá:ke
Centre de santé: Centre Hospitalier Kateri Memorial
Distance routière approximative: 1.10 km

Communauté: Mohawks of Kanesatake
Centre de santé: Centre de santé de Kanesatake
Distance routière approximative: 0.09 km

Communauté: Akulivik
Centre de santé: CLSC d'Akulivik
Distance routière approximative: 0.04 km

Communauté: Aupaluk
Centre de santé: CLSC d'Aupaluk
Distance routière approximative: 0.12 km

Communauté: Chisasibi
Centre de santé: CMC Chisasibi
Distance routière approximative: 104.61 km

Communauté: Inukjuak
Centre de santé: CLSC d'Inukjuak
Distance routière approximative: 0.30 km

Communauté: Ivujivik
Centre de santé: CLSC d'Ivujivik
Distance routière approximative: 0.03 km

Communauté: Kangiqsualujjuaq
Centre de santé: CLSC de Kangiqsualujjuaq
Distance routière approximative: 0.15 km

Communauté: Kangiqsujuaq
Centre de santé: CLSC de Kangiqsujuaq
Distance routière approximative: 0.16 km

Communauté: Kangirsuk
Centre de santé: CLSC de Kangirsuk
Distance routière approximative: 0.10 km

Communauté: Killiniq
Centre de santé: N/A
Distance routière approximative: 0.00 km

Communauté: Kuujjuaq
Centre de santé: Centre de santé Ungava Tulattavik
Distance routière approximative: 5.24 km

Communauté: Kuujjuarapik
Centre de santé: CLSC de Kuujjuaraapik
Distance routière approximative: 0.06 km

Communauté: Puvirnituq
Centre de santé: Centre de santé Inuulitsivik
Distance routière approximative: 0.08 km

Communauté: Quaqtaq
Centre de santé: CLSC de Quaqtaq
Distance routière approximative: 0.06 km

Communauté: Salluit
Centre de santé: CLSC de Salluit
Distance routière approximative: 0.06 km

Communauté: Tasiujaq
Centre de santé: CLSC de Tasiujaq
Distance routière approximative: 0.06 km

Communauté: Umiujaq
Centre de santé: CLSC d'Umiujaq
Distance routière approximative: 0.05 km
# Sauvegarder les résultats dans un fichier CSV
write.csv(resultats_distances, 
          "distances_communautes_centres_sante.csv", 
          row.names = FALSE)

cat("Les résultats ont été sauvegardés dans 'distances_communautes_centres_sante.csv'\n")
Les résultats ont été sauvegardés dans 'distances_communautes_centres_sante.csv'
# Afficher un résumé statistique
cat("\n===== STATISTIQUES DES DISTANCES =====\n")

===== STATISTIQUES DES DISTANCES =====
cat(sprintf("Distance moyenne: %.2f km\n", 
            mean(resultats_distances$distance_routiere_approx_km, na.rm = TRUE)))
Distance moyenne: 18.89 km
cat(sprintf("Distance médiane: %.2f km\n", 
            median(resultats_distances$distance_routiere_approx_km, na.rm = TRUE)))
Distance médiane: 0.15 km
cat(sprintf("Distance minimale: %.2f km\n", 
            min(resultats_distances$distance_routiere_approx_km, na.rm = TRUE)))
Distance minimale: 0.00 km
cat(sprintf("Distance maximale: %.2f km\n", 
            max(resultats_distances$distance_routiere_approx_km, na.rm = TRUE)))
Distance maximale: 374.93 km
# ===== PARTIE 2 : ATTRIBUTION DES FAMILLES ET VISUALISATION =====

# Fonction pour attribuer les familles autochtones
get_famille <- function(comm) {
  if (grepl("Odanak|Wôlinak", comm, ignore.case=TRUE)) return("Abénakis")
  else if (grepl("Algonquins|Wolf Lake|Kebaowek|Kitcisakik|Kitigan Zibi|Lac-Simon|Barriere Lake|Timiskaming|Winneway|Abitibiwinni|Long Point", comm, ignore.case=TRUE)) return("Algonquins")
  else if (grepl("Atikamekw", comm, ignore.case=TRUE)) return("Atikamekw")
  else if (grepl("^La Nation Crie|Cree Nation|Cris|Waswanipi|Waskaganish|Mistissini|Nemaska|Wemindji|Chisasibi|Eastmain|Oujé-Bougoumou", comm, ignore.case=TRUE)) return("Cris")
  else if (grepl("Innu|Innue|Matimekush|Ekuanitshit|Nutashkuan|Pekuakamiulnuatsh|Uashat|Pakua Shipi|Unamen Shipu|Essipit|Pessamit", comm, ignore.case=TRUE)) return("Innus")
  else if (grepl("Micmac|Mi'gmaq|Gespeg|Gesgapegiag|Listuguj", comm, ignore.case=TRUE)) return("Micmacs")
  else if (grepl("Naskapi", comm, ignore.case=TRUE)) return("Naskapis")
  else if (grepl("Wolastoqiyik", comm, ignore.case=TRUE)) return("Wolastoqiyik")
  else if (grepl("Wendat", comm, ignore.case=TRUE)) return("Wendat")
  else if (grepl("Mohawks|Kahnawá:ke|Kanesatake", comm, ignore.case=TRUE)) return("Mohawks")
  else if (grepl("Akulivik|Aupaluk|Inukjuak|Ivujivik|Kangiqsualujjuaq|Kangiqsujuaq|Kangirsuk|Killiniq|Kuujjuaq|Kuujjuarapik|Puvirnituq|Quaqtaq|Salluit|Tasiujaq|Umiujaq|Ungava", comm, ignore.case=TRUE)) return("Inuit")
  else return("Autre")
}

# Attribuer les familles aux résultats
resultats_distances$famille <- sapply(resultats_distances$communaute, get_famille)

# Filtrer les familles demandées
familles_demandes <- c("Abénakis","Algonquins","Atikamekw","Cris","Innus",
                       "Micmacs","Naskapis","Wolastoqiyik","Wendat","Mohawks","Inuit")
df_filtre <- resultats_distances[resultats_distances$famille %in% familles_demandes,]

# Calculer les statistiques par famille
stats <- df_filtre %>%
  group_by(famille) %>%
  summarise(
    moyenne = mean(distance_routiere_approx_km, na.rm = TRUE),
    ecart_type = sd(distance_routiere_approx_km, na.rm = TRUE),
    n = n()
  ) %>%
  mutate(famille_n = paste0(famille, " (n = ", n, ")")) %>%
  arrange(desc(moyenne))

# Afficher les statistiques
cat("\n\n===== STATISTIQUES PAR FAMILLE AUTOCHTONE =====\n")


===== STATISTIQUES PAR FAMILLE AUTOCHTONE =====
print(stats[, c("famille_n", "moyenne", "ecart_type", "n")])
# A tibble: 11 × 4
   famille_n            moyenne ecart_type     n
   <chr>                  <dbl>      <dbl> <int>
 1 Innus (n = 9)        63.6      120.         9
 2 Cris (n = 9)         44.2       57.5        9
 3 Wolastoqiyik (n = 1) 17.8       NA          1
 4 Naskapis (n = 1)      8.27      NA          1
 5 Micmacs (n = 3)       4.65       5.71       3
 6 Wendat (n = 1)        4.29      NA          1
 7 Algonquins (n = 9)    3.84      11.2        9
 8 Mohawks (n = 2)       0.595      0.714      2
 9 Inuit (n = 15)        0.434      1.33      15
10 Abénakis (n = 2)      0.09       0          2
11 Atikamekw (n = 3)     0.0767     0.0231     3
# Générer le graphique automatiquement
cat("\n===== GÉNÉRATION DU GRAPHIQUE =====\n")

===== GÉNÉRATION DU GRAPHIQUE =====
graphique <- ggplot(stats, aes(x = reorder(famille_n, moyenne), y = moyenne)) +
  geom_bar(stat = "identity", fill = "skyblue", width = 0.7) +
  geom_errorbar(aes(ymin = moyenne - ecart_type, ymax = moyenne + ecart_type),
                width = 0.25, color = "darkblue", size = 1.2) +
  labs(
    title = "Distance routière moyenne d'un centre de santé par famille autochtone",
    x = "Famille autochtone (nombre de communautés)",
    y = "Distance routière moyenne (km)"
  ) +
  theme_minimal(base_size = 11) +
  coord_flip()


Le graphique a été sauvegardé dans 'graphique_distances_familles.png'