Des adresses à la carte : géocoder avec R et Google Maps

Date 22 janvier 2013

Comment faire pour représenter des données dont on connaît l’adresse ? Si on veut les cartographier, il faut géocoder ces adresses, c’est-à-dire en obtenir les latitude et longitude. De nombreux sites existent pour cela, comme GetLatLon, simple et efficace pour une utilisation ponctuelle. D’autres outils permettent d’automatiser le processus et ainsi de traiter un grand nombre d’adresses.

Pour R, il existe la fonction geocode, du module dismo[1]. Elle permet de facilement trouver ces informations, pour peu qu’on dispose d’une adresse précise. Par exemple, si on veut connaître les coordonnées du désormais trop célèbre village de Bugarach, il suffit de rentrer la requête suivante :

library(dismo) 
bugarach.latlon <- geocode(“Bugarach, France”, oneRecord=TRUE)

L’option oneRecord permet de ne retenir que le premier résultat trouvé. Si on l’omet, on peut obtenir une pluralité de résultats, à trier ensuite manuellement.

Les deux services mentionnés ci-dessus utilisent les données de Google Maps. Ils ne sont donc qu’une manière rapide de chercher dans cette base de données – et s’ils sont gratuits et efficaces, c’est parce que l’accès à cette base de données l’est elle-même.

Plus précisément, le travail qu’ils effectuent en coulisses consiste à se connecter à l’API de Google Maps, et d’y adresser une requête en bonne et due forme, puis en traduire la réponse sous une forme simple. Une API (Application Programming Interface, interface de programmation) est, dans ce contexte, l’équivalent informatique d’un guichet : elle a ses conditions d’ouverture et d’accès (est-ce qu’il faut s’identifier, combien de requêtes sont autorisées sur une période de temps donnée, à quels tarifs, etc.), et ses “formulaires”, c’est-à-dire la forme que doivent prendre les requêtes, et celle selon laquelle sont renvoyées les réponses. Bref, c'est la manière de communiquer avec une boîte noire[2].

Il existe plusieurs autres bases de données cartographiques, et une multitude de services de géocodage associés ; voir ce blog pour un comparatif des différents services gratuits. On présente ici quelques lignes de code R qui permettent d’interroger directement l’API de Google Maps avec R, et d'en exploiter (un peu) plus complètement les possibilités.

Un exemple : les lieux de culte à Paris

Dans l’exemple qui suit, on cherche à cartographier les lieux de culte à Paris. Par souci de simplicité, on part de données disponibles sur Wikipédia : une page y recense environ 250 édifices religieux à Paris. Il y a certainement bien plus de lieux de culte ; cette liste n’est probablement pas exhaustive, ni particulièrement claire. Elle a cependant le mérite de comporter des informations sur les grandes religions monothéistes, et se prête bien à la cartographie, donc elle nous suffira pour aujourd’hui.

Concernant l’adresse, l’information y est relativement précise, mais pas parfaite : on a un nom, parfois une rue, mais pas l’adresse exacte. Quoi qu’il en soit, la première étape est de récupérer ces données par un petit programme, et d’en faire un fichier importable sous R[3]. Ce fichier est disponible ici, mais pour plus de facilité, on l'importera directement avec R dans un instant.

C’est de ces données imparfaites qu’on part. Mais Google Maps a l’avantage d’être assez précis, même à partir de données approximatives.

Interroger Google Maps par son API

On va donc demander nos informations à l’API Geocode de Google Maps, qui est gratuite jusqu'à un certain seuil, et sur laquelle on peut facilement multiplier les requêtes : jusqu’à 2500 par jour, sur un même ordinateur - la fonction geocode du package dismo, utilisant le même service, est elle aussi limitée de cette façon. Pour travailler avec plus de données, il faudrait soit payer, soit utiliser un autre moteur de recherche (Yahoo permet 50.000 requêtes gratuites par jour, Bing 10.000 ; OpenStreetMap 86.400).

Une API, comme on l’a dit, est une sorte de guichet. Les informations échangées ont ainsi l’air barbare, mais se comprennent, comme les formulaires administratifs, en lisant patiemment les modes d’emploi. Ici, l’API s’utilise simplement en demandant l’ouverture d’une page web. “L’adresse” de cette page est en réalité le contenu de la requête, formulée selon une syntaxe spécifique. On lit dans la documentation que celle-ci est de la forme suivante :

http://maps.googleapis.com/maps/api/geocode/xml?address=LIEU&sensor=false

où on remplace LIEU par le lieu dont on veut déterminer les coordonnées (soit ce qui est dans la troisième colonne du tableau, dans l’image ci-dessus). Il n’y a pas besoin de beaucoup nettoyer ces noms de lieux, par exemple enlever les accents n’est pas nécessaire. Comme tout est à Paris, pour plus de précision on ajoutera également le nom de la ville à la fin de l’adresse, et celle du pays (et nous épargner ainsi le tri parmi les villes homonymes du Texas ou de l’Illinois).

# Lecture du fichier de données
lieux.culte <- read.csv("http://jourdan.ens.fr/~eollion/documents/culte.csv")

# On crée une variable vide, qui va contenir les requêtes
requetes <- NULL

# Traitement de chacune des adresses
for(i in 1:nrow(lieux.culte)) {

  # Lit l'adresse, et remplace les espaces par "+" (syntaxe de l'API)
  lieu <- lieux.culte[i, "ADRESSE"]
  lieu <- gsub("\\s+", "+", lieu)

  # Construction de la requête
  requetes[i] <- paste("http://maps.googleapis.com/maps/api/geocode/xml?address=",
                       lieu,
                       ",+Paris,+France",
                       "&sensor=false",
                       sep="") 

}

En deux mots, cette partie du programme: crée une variable (requetes), copie grâce une boucle (for) la variable ADRESSE de chaque ligne du tableau lieux.culte tiré de Wikipédia, en la mettant en forme pour l’API, c’est à dire en remplaçant les espaces par des +. On peut le vérifier en examinant le vecteur requetes obtenu :

[1] "http://maps.googleapis.com/maps/api/geocode/xml?address=Pagode+de+Vincennes,+bois+de+Vincennes,+Paris,+France&sensor=false"
[2] "http://maps.googleapis.com/maps/api/geocode/xml?address=Temple+bouddhique,+dalle+des+Olympiades,+Paris,+France&sensor=false"
[3] "http://maps.googleapis.com/maps/api/geocode/xml?address=Temple+taoiste,+rue+du+Disque,+Paris,+France&sensor=false"

Collecter les données géographiques pour chaque adresse

Si on ouvre l’une de ces "adresses" dans un navigateur, le site nous renvoie une page (en réalité un fichier XML), qui contient plusieurs informations, dont les coordonnées lat/lon. Le but maintenant est de les extraire de chacune de ces pages. C’est encore une fois assez simple, puisque les données sont structurées clairement, et toujours de la même façon. C’est ce que fait le code suivant :

# Module nécessaire à l'exploration des résultats
library(XML)

# Variable dans laquelle on va stocker les résultats
lieux.culte.latlon <- NULL

# Traitement de chacune des requêtes
# - ATTENTION : prend environ 10 minutes ici
for(i in 1:length(requetes)) {

  # Envoie la requête, reçoit la réponse, analyse sa structure
  reponse.xml <- readLines(requetes[i])
  reponse <- xmlTreeParse(reponse.xml, useInternalNodes=TRUE)

  # Traitement des résultats ; on ne prend que
  # la première réponse (result[1] dans ce qui suit)

  # On initialise comme NA
  latitude <- NA
  # Si la valeur existe, remplace NA (sinon reste NA)
  latitude <- xmlValue(reponse[["//result[1]//geometry/location/lat"]])

  # Longitude : idem
  longitude <- NA 
  longitude <- xmlValue(reponse[["//result[1]//geometry/location/lng"]]) 

  # Champ adresse de la réponse : idem
  adresse.recue <- NA 
  adresse.recue <- xmlValue(reponse[["//result[1]//formatted_address"]]) 

  # Mise des résultats ensemble, sous forme de dataframe
  latlon.etc <- cbind(lieux.culte[i,], requetes[i], latitude, longitude, adresse.recue)
  lieux.culte.latlon <- rbind(lieux.culte.latlon, latlon.etc)

  # Pause de 3 secondes - courtoisie envers le serveur 
  Sys.sleep(3)

}

Pas besoin de tout comprendre pour s’en servir, mais voici quelques explications tout de même. On crée tout d’abord un objet vide (lieux.culte.latlon), puis on traite une à une les requêtes préparées précédemment (boucle for). La réponse à chacune de ces requêtes est un fichier XML (proche cousin du HTML), dont on analyse la structure en arbre (xmlTreeParse), qui est alors stockée dans l’objet reponse.

On navigue ensuite, pour récupérer les informations qui nous intéressent, grâce à la syntaxe XPath (un billet bientôt sur ce sujet, mais de nombreux tutoriels sont disponibles, par exemple ici). Une petite précaution (initialiser à NA les variables de résultats) assure que, dans les cas où la donnée recherchée par le chemin XPath spécifié n’existe pas, la valeur indiquée soit NA (ce qui permet de repérer les erreurs facilement).

Enfin, on colle ces informations et d’autres dans une variable (latlon.etc) pour chaque ligne, qu’il ajoute à l’objet lieux.culte.latlon pour chaque nouvelle valeur de i. Pour terminer, Sys.sleep est une commande qui permet de faire prendre une pause au script (ici de 3 secondes). C’est très conseillé, afin de ne pas trop solliciter le serveur - par courtoisie, et parce que sinon ce dernier pourrait vous refuser l’accès.

Le résultat est visible dans l’objet lieux.culte.latlon : à côté de l’adresse, vous avez maintenant les coordonnées. La variable adresse permet ici de vérifier que les données correspondent bien spécifiquement au lieu demandé. En effet, dans le cas inverse, ce champ indique seulement Paris (il faut alors recoder à la main, ou préciser quelque chose dans l’adresse).

On a visualisé les données à l'aide d'une rapide carte. Le premier résultat (voir image en haut de cette page) est qu'il y a des églises un peu partout dans Paris, ce qui n'est pas révolutionnaire. Plus précisément, un zoom sur la zone de la Bastille montre l'alignement de synagogues, certaines dans le Marais, et d’autres à proximité. En revanche, le cas de l'Islam est très mal représenté ici, mais c’est dû à un effet de sources, à savoir qu'on est parti des édifices religieux, et pas des simples lieux de culte ; or la distinction est particulièrement importante dans le cas de cette religion, comme on le verra dans un prochain billet.

Lieux de culte près de Bastille

Pour aller plus loin

On vient, dans cet exemple d’utilisation de l’API de Google Maps, de réinventer la fonction geocode de dismo mentionnée plus haut, tout en contrôlant mieux les résultats obtenus.

On a ce faisant utilisé une API de type REST, qui est un standard très répandu. Or les autres services que Google, comme OpenStreetMap, MapQuest, Bing, Nokia, etc., disposent rarement d’une fonction directement disponible sous R, mais offrent tous des API, dont souvent l'une de ce type.

La syntaxe est alors extrêmement proche de celle qu'on a employée ici. Il est alors possible d'utiliser, par exemple, le projet Nominatim de la fondation Open Street Map, dont la limite d'utilisation est d'une requête par seconde (soit 86.400 par jour - ce dernier fait par ailleurs partie d'un projet plus général de mise à disposition gratuite de données géographiques. Les requêtes se font de la même manière, comme l'explique cette page de documentation, et l'extraction des données ne suppose qu'un léger ajustement[4]. Essayez donc !


  1. Merci à Baptiste Coulmont pour l’information.  ↩
  2. Le terme d’API est en réalité utilisé, en informatique, de façon un peu plus large ; toutefois l’analogie du guichet et de la boîte noire reste globalement correcte.  ↩
  3. Voir les tutoriels efficaces de F. Guillem sur son blog
  4. Remplacer xmlValue(reponse[["//result[1]//geometry/location/lat"]]) par xmlGetAttr(reponse[["place[1]"]], "lat") pour la latitude, et un équivalent pour la longitude.

Imprimer ce billet Imprimer ce billet

2 commentaires pour  “Des adresses à la carte : géocoder avec R et Google Maps”

  1. Editerna a dit le 21/01/2014 à 13:53 :

    Les explications du billet sont intéressantes, on voit mieux ce qui se cache derrière les fonctions faisant appel aux APIs. Sinon, il y a aussi la fonction geocode du package ggmap qui est vraiment pratique, mais le nombre de requêtes est limité, de mémoire, à 2000 par ip, par jour.

    Répondre
  2. [...] Examinon-les en repartant de l’exemple des édifices religieux à Paris, traité dans un précédent billet. Cette fois, on s’intéresse au culte [...]

    Répondre

Laisser un commentaire

XHTML: Vous pouvez utilisez ces balises :<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <embed style="" type="" id="" height="" width="" src="" object="" allowfullscreen="" allowscriptaccess="" cachebusting="" bgcolor="" quality="" flashvars=""> <iframe width="" height="" frameborder="" scrolling="" marginheight="" marginwidth="" src=""> <object style="" height="" width="" param="" embed=""> <param name="" value="">