Cartographie de flux avec R

La cartographie automatisée de flux n’est pas une chose facile à réaliser, et ce quelque soit le logiciel utilisé.
L’objectif de ce billet est de proposer un exemple de cartographie de flux avec R.

[MAJ] J’ai déplacé les données et ajouté le code sur GitHub. [/MAJ]

Les données utilisées sont des données de migration résidentielles issues de l’Insee et un fond de carte des départements français diffusé par l’IGN. Le tout est à télécharger ici sous forme de RData.

Cet exemple s’appuie sur le package igraph dédié à l’analyse et à la représentation de graphs. Les fonctionnalités de représentation de graphs orientés sont mobilisés pour représenter les migrations résidentielles entre les 6 départements les plus attractifs (hors Ile de France) entre 2003 et 2008.

# chargement des packages nécessaires
library(igraph)
library(maptools)

# chargement des données 
# téléchargeables ici : http://wukan.ums-riate.fr/flow/dep.RData
load("dep.RData")

# extraction des coordonnées des centroides des departements
centres <- cbind(depGeom@data[,"CODE_DEPT"],
                 as.data.frame(coordinates(depGeom)))
names(centres)<-c("id","X","Y")

# création de la variable d'attractivité des départements :
# le nombre d'arrivées
attract <- aggregate(depMig$fij, by = list(depMig$dest), sum)
names(attract) <- c("id", "nbarrivee")

# Sélection flux entre les 6 départements recevant le plus de 
# migrations résidentielles à l'exeption des département 
# de l'IDF, de l'étranger (99) et des DOM (97)
# liste des départements à exclure
listExcl <- c("75", "92", "77", "78", "93", "94", "95", "97", "99")
# exclusion de ces départements
mig <- depMig[!depMig$dest %in% listExcl & 
                !depMig$orig %in% listExcl,]
# liste des départements à sélectionner 
listDep <- attract[order(attract$nbarrivee[!attract$id %in% listExcl], 
                         decreasing = T),][1:6,"id"]
# sélection de ces départements
mig <- mig[mig$orig %in% listDep & mig$dest %in% listDep, ]

# transformation des flux sélectionnés en graph
g <- data.frame(mig[,c("orig", "dest")])
g <- as.matrix(g)
g <- graph.edgelist(g, directed=TRUE)

# création de la liste des noeuds (vertex)
vert <- data.frame(id = V(g)$name)

# affectation des positions des coordonnées des centroides aux noeuds
coords <- centres[match(vert$id, table = centres$id), ]
coords <- as.matrix(coords[,2:3])
g$layout <- coords

# ajout d'un attribut  aux noeuds
V(g)$weights <- attract[match(vert$id, table = attract$i), "nbarrivee"]

# ajout d'un attribut d'épaisseur aux liens (edge)
x <- get.edgelist(g)
E(g)$fij <- mig[match(x = paste(x[,1],x[,2],sep="_"), 
                      table = paste(mig$orig,mig$dest,sep="_")),"fij"]

# affichage du fond de carte
plot(depGeom, col="pink")
# affichage du graph
#png("carte1.png", width = 415, height = 450)
par(mar=c(0,0,0,0),oma = c(0,0,0,0) )
plot.igraph(g, add=T, rescale = F, 
            vertex.size = (V(g)$weights)*100,
            edge.curved = 0.4, 
            edge.width =  E(g)$fij/250,
            edge.color = "red")
#dev.off()

carte1
Cette première représentation pose un problème : la taille des pointes de flèches est la même pour toutes les flèches, le paramètre edge.arrow.size n’accepte pas de vecteur en entrée.
Une solution est de faire autant de plots que de flèche et de les superposer (solution proposée ici).

# png("toto.png", width = 415, height = 450)
par(mar=c(0,0,0,0),oma = c(0,0,0,0) )
# Affichage de la carte des départements
plot(depGeom, col="black", bg = "grey", border = "#ffffff00")

# Titre et légende
mtext(text = " Migrations résidentielles majeures entre les \n départements les plus attractifs (hors IDF)\n",
      side = 3,adj=0, cex=1.5, line = -4.5)
mtext(text = "Source : Insee, migrations résidentielles 2003-2008 ",
      side = 1,adj=1,cex=0.8, line = -2)
mtext(text = " T. Giraud, 2014",side = 1,adj=0,cex=0.8, line = -2)

# ajout des flèches une par une
for (e in seq_len(ecount(g))) {
  graph2 = delete.edges(g, E(g)[(1:ecount(g))[-e]])
  plot(graph2, 
       vertex.size = (V(g)$weights)*250,
       edge.curved = 0.5,
       edge.width=E(graph2)$fij / 170,
       # nous limitons la taille des plus grosses flèches à 1.75 
       edge.arrow.size = if(E(graph2)$fij / 700 >= 1.75){
         1.75
         } else {
           E(graph2)$fij/700
           },
       edge.color = "red",
       vertex.label=NA, 
       add=TRUE, 
       rescale = F
  )
}
#dev.off()

carte2

Si le résultat est quand même très satisfaisant pour une cartographie automatique de flux, quelques problèmes subsistent :

  • La pointe des flèches est moche (surtout pour les grandes flèches et on ne peut pas la changer.
  • Le départ et les arrivées de flux ne “visent” pas les centres des cercles.
  • L’ajout d’une belle légende concernant à la fois les cercles et les flux n’est vraiment pas évident.
  • L’adaptation des tailles de cercles, des épaisseurs de flux et des tailles de pointes de flèches n’est pas gérée.

OpenEdition vous propose de citer ce billet de la manière suivante :
Timothée Giraud (11 décembre 2014). Cartographie de flux avec R. R Géomatique. Consulté le 9 décembre 2024 à l’adresse https://doi.org/10.58079/tp39


7 réflexions sur « Cartographie de flux avec R »

  1. Bonjour,
    merci beaucoup pour vos travaux partagés,
    dans le cadre de mon stage je suis censé travailler sur l’analyse de l’accessibilité aux aménités urbaines,
    pour l’instant je cherche une manière qui me permettrait de cartographier ( carte isochrone) la distance à la ressource la plus proche (D) depuis les centroides des communes (O),
    Avec sf:: st_distance( x,y) j’ai réussi à avoir une matrice d’adjacence des distances, par la suite j’ai pu calculer la distance minimum pour chaque ligne ( ressource la plus proche pour chaque commune)
    je cherche un moyen qui me permettrait de cartographie mes résultats de manière à monter la variation de l’accessibilité ( proximité à la ressource) une carte isochrone je me disais ,
    si c’est possible, pouvez-vous m’indiquer, s’il vous plait, quel est ce moyen s’il existe de passer du calcul de distances ( matrice OD ) à la cartographie de celles-ci ?
    dans un second temps, j’espère pouvoir le faire sur réseau routier ( accessibilité sur réseau ). Pour cela avez-vous, s’il vous plait, des pistes, des travaux , des méthodes ou même des conseils qui pourront m’aider ?
    merci d’avance pour vos réponses
    bien cordialement

  2. bonjour,
    pourriez vous svp remettre en ligne les fichiers rdata
    merci

    ps/ merci pour votre article sur les flux!!

    1. Bonjour,
      merci beaucoup pour tous ces travaux partagés.
      Plutôt que de créer la couche point à partir de centroides de la couche département, peut-on charger directement une couche polygone puis une couche point (à supposer que nos points ne soient pas des centroides) ?
      Est-ce possible de cartographier des flux dont l’origine ou la destination serait toujours la même ?
      Merci

      1. On peut charger directement des centroides, si la couche depGeom est une couche points cette instruction fonctionne aussi:
        centres <- cbind(depGeom@data[,"CODE_DEPT"], as.data.frame(coordinates(depGeom)))
        Le dataframe depMig contient la table des origines/destinations, si les origines ou les destination sont toujours les mêmes alors vous cartographierez des flux dont l’origine ou la destination sont toujours les mêmes.

        1. Je pensais que l’instruction était différente selon si on chargeait un shape point polygone ou ligne. Mais je viens de découvrir la notion de slot et je comprends que c’est cet élément @ qui permet de voir ce qui les compose et différencie leur géomatrie. Bien évidemment pour les origines destinations…!
          Merci beaucoup

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.