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.

5 réflexions au sujet de « Cartographie de flux avec R »

  1. 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 de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *