Géocoder en Python est très facile, quelques lignes de code suffisent une fois que l'on connait les détails des services offerts par les principaux sites de géocodage/géolocalisation gratuit : Google, Yahoo, Geonames, Mediawiki et autres.
Principes
Prenons, par exemple, le service de géocodage de Google. Les Google Maps API Services (code.google.com/intl/en/apis/maps/documentation/geocoding) indiquent la manière d'interroger le service web et quels sont les résultats que l'on peut obtenir avec leur format (json, cvs, xml et kml).
L'adresse interrogée est : "Place de l'Université 1, 1348 Louvain-la-Neuve" (en Belgique).
L'interrogation se fait par l’URL suivante :
maps.google.com/maps/geo?q=Place%20de%20l%27Universit%C3%A9%2C%201%2C%201348%2C%20Louvain-la-Neuve&output=json&api_key=
L'important est le terme output= qui permet de préciser le format désiré de réponse, json, csv, xml ou kml.
Commençons par le résultat au format json (format par défaut):
{
"name": "Place de l'Université 1, 1348 Louvain-la-Neuve",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "Place de l'Université, 1348 Louvain-La-Neuve, Belgique",
"AddressDetails": {
"Accuracy" : 6, "Country" : { "AdministrativeArea" : { "AdministrativeAreaName" : "Région Wallonne", "SubAdministrativeArea" : { "Locality" : { "DependentLocality" : { "DependentLocalityName" : "Louvain-la-Neuve", "PostalCode" : { "PostalCodeNumber" : "1348" }, "Thoroughfare" : { "ThoroughfareName" : "Place de l'Université" } }, "LocalityName" : "Louvain-La-Neuve" }, "SubAdministrativeAreaName" : "Brabant Wallon" } }, "CountryName" : "Belgique", "CountryNameCode" : "BE" } }, "ExtendedData": { "LatLonBox": { "north": 50.6731662, "south": 50.6668709, "east": 4.6184483, "west": 4.6121531 } }, "Point": { "coordinates": [ 4.6151754, 50.6701607, 0 ]
}
} ]
}
Les principaux éléments obtenus sont :
- Les coordonnées (coordinates) longitude, latidude en projection WGS84 (degrés décimaux)
- le statut de la réponse: le code 200 indique qu'il n'y a pas eu d'erreur (voir code.google.com/intl/en/apis/maps/documentation/geocoding/#StatusCodes);
- le degré de confiance du géocodage: Accuracy:6 indique que la géolocalisation est précise au niveau de la rue ou de la place et non de l'adresse proprement dite (n°1) qui aurait été 8, voir code.google.com/intl/en/apis/maps/documentation/geocoding/#GeocodingAccuracy);
- et d'autres éléments moins intéressants dans le cas qui nous concerne.
La réponse au format csv :
200,6,50.6701607,4.6151754
Beaucoup plus courte, sans explication, mais les données obtenues sont maintenant connues. Les résultats au format xml ou kml sont équivalents à celui de json, à la différence que le choix kml provoque le téléchargement du fichier kml.
Python seul
Maintenant, avec Python, la démarche est très simple
Pour le format csv:
import urllib
def geocode(addr, api_key=''):
url = "http://maps.google.com/maps/geo?q=%s&output=csv&api_key=%s" % (urllib.quote(addr), urllib.quote(api_key))
coord = urllib.urlopen(url).read().split(',')
coortext = 'lat: %s,long: %s' % (coord[2],coord[3])
return coortext
coordonnees = geocode("Place de l'Université 1, 1348 Louvain-la-Neuve")
print coordonnees
lat: 50.6701607 long: 4.6151754
Pour le format json (d'après gdallaire.net/blog/) :
Les versions de Python supérieures à la 2.5.x possèdent le module/bibliothèque json dans la distribution standard. Pour les autres, il suffit d'installer simplejson
import urllib
import json ou import simplejson as json (suivant les cas)
def geocode(addr, api_key=''):
url = "http://maps.google.com/maps/geo?q=%s&output=json&api_key=%s" % (urllib.quote(addr), urllib.quote(api_key))
data = urllib.urlopen(url).read()
return json.loads(data)['Placemark'][0]['Point']['coordinates']
data = geocode("Place de l'Université 1, 1348 Louvain-la-Neuve")
print "lat:%s long:%s" % (data[1], data[0])
lat: 50.6701607 long:4.6151754
Traiter le résultat xml est tout aussi facile. Google, grand utilisateur et promoteur de Python, propose son propre module pour le faire gmaps-samples.googlecode.com/svn/trunk/articles-kmlgeocode/geocoding_for_kml.py.
Il est évident que la procédure doit être améliorée pour traiter les erreurs (échec du géocodage,...) ou pour interroger d'autres sites de géolocalisation. Ce dernier point est traité par www.paolocorti.net/2009/10/14/geocoding-with-geopy/ qui illustre les divers formats qui peuvent être reçus.
Heureusement, des développeurs Python ont fait le travail pour nous avec des bibliothèques comme Googlemaps (limité à Google) ou Geopy (plusieurs services).
Leur syntaxe est équivalente (en théorie avec Google, il faudrait renseigner votre clé api, mais en pratique elle n'est pas toujours nécessaire, ce qui n'est pas le cas avec Yahoo ou autre).
Python avec le module geopy
from geopy import geocoders
g=geocoders.Google('éventuellement votre clé api Google')
place, (lat, lng) = g.geocode("Place de l'Université 1, 1348, Louvain-la-Neuve")
print "%s: %.5f, %.5f" % (place, lat, lng)
Place de l'Université, 1348 Ottignies-Louvain-la-Neuve, Belgium: 50.67016, 4.61518
Geopy permet d'interroger de la même manière les autres services de geocodage (ici le Yahoo Geocoding Web Service):
y = geocoders.Yahoo('votre clé api yahoo')
place, (lat, lng) = y.geocode("Place de l'Université 1, 1348, Louvain-la-Neuve")
print "%s: %.5f, %.5f" % (place, lat, lng)
Place De l'Université 1, 1348 Louvain-la-Neuve, Belgium, BE: 50.66990, 4.61566
puis Geonames et autres, voir code.google.com/p/geopy/wiki/GettingStarted
La bibliothèque permet aussi d'obtenir éventuellement plusieurs géolocalisations par demande ou de calculer des distances avec 2 algorithmes:
from geopy import distance
_, lln = g.geocode('Louvain-la-Neuve')
_, Paris = g.geocode('Paris')#avec l'algorithme GreatCircleDistance (fr.wikipedia.org/wiki/Distance_du_grand_cercle)
distance.distance = distance.GreatCircleDistance
distance.distance(lln, Paris).km
256.58343152506433#avec l'algorithme de Vincenty (en.wikipedia.org/wiki/Vincenty%27s_formulae)
distance.VincentyDistance.ELLIPSOID = 'wgs-84'
distance.distance(lln, Paris).km
256.58343152506433
Puis de manipuler ces distances:
d = distance.distance
_, Lille = g.geocode('Lille')
(d(lln, Lille) + d(Lille,Paris)).km
310.65819253714375
Geopy permet d'autres traitements comme le traitement des fichiers gpx ou le géocodage inverse (le "Reverse geocoding" permet d'obtenir l'adresse la plus proche d'une localisation), mais nous allons l'appliquer avec la bibliothèque suivante.
Python avec le module googlemaps
from googlemaps import GoogleMaps
gmaps = GoogleMaps()
addresse = "Place de l'Université 1, 1348 Ottignies-Louvain-la-Neuve"
lat, lng = gmaps.address_to_latlng(addresse)
print lat, lng
50.6701607 4.6151754
Pour le "Reverse geocoding", du fait de sa conception, il faut un niveau de précision (Accuracy) de 8 ou 9 sinon des surprises peuvent arriver ( puisque Google va chercher les adresses précises qu'il connait aux alentours du point choisi). Alors, au hasard (puisque le degré de l'exemple précédent n'est que de 6):
addresse = "Boulevard Malesherbes,2,Paris"
lat, lng = gmaps.address_to_latlng(addresse)
print lat, lng
48.8796507 2.3137858
destination = gmaps.latlng_to_address(48.8796507,2.3137858)
print destination
2 Boulevard Malesherbes, 75008 Paris, France
Il permet aussi de réaliser des recherches selon la terminologie Google:
local = gmaps.local_search('cafe near ' + destination)
print local
avec une floppée de résultats...
et la recherche de trajets:
depart = gmaps.latlng_to_address(48.8796507,2.3137858)
destination= gmaps.latlng_to_address(48.8712762,2.3161377)
print destination
2 Rue de Miromesnil, 75008 Paris, France
directions = gmaps.directions(depart, destination)
print directions['Directions']['Distance']['meters']
994
print directions['Directions']['Duration']['seconds']
215
for step in directions['Directions']['Routes'][0]['Steps']:
print step['descriptionHtml']
Head <b>northeast</b> on <b>Boulevard Malesherbes</b>
Turn <b>right</b> to stay on <b>Boulevard Malesherbes</b>
Slight <b>right</b> at <b>Rue de Miromesnil</b> <div class="google_note">Destination will be on the left</div>
Conclusions
Les possibilités sont donc presque infinies. Rien n'empêche d'ouvrir un fichier de liste d'adresses (formats txt, csv ou même Microsoft@Excel avec l'une des bibliothèques de lecture appropriée), de passer les adresses à la moulinette avec Python, puis d'enregistrer les résultats dans un autre fichier (voir par exemple canadianamp.ca/2008/12/05/simple-geocoding-with-python-and-urllib/ , www.ninemoreminutes.com/2009/12/google-maps-with-python-and-kml/ , code.google.com/intl/fr/apis/kml/articles/csvtokml.html et beaucoup d'autres).
Ces procédures, intimement liées aux apis des sites de géolocalisation (puisqu'ils ne font que "parser" les réponses envoyées par les serveurs) sont toujours à la merci d'un changement de politique ou de format de la part du site interrogé. Il y a aussi des limites aux demandes que vous pouvez faire (même avec un api). Mais c'est aussi vrai avec toutes les applications de géocodage/géolocalisation, propriétaires ou libres qui utilisent ces services. Les solutions Python permettent donc de sérieuses économies vis-à-vis de l'achat d'un logiciel payant. De plus, le temps de réaction des développeurs Python est très rapide.
Un nouveau module en cours de développement, Pymaps, permet même d'afficher directement les données géolocalisées sur Google Maps après traitement avec Geopy ou autres.
La démarche suivante serait le changement de projection des résultats obtenus (en WGS84) vers des projections propres à chaque région ou pays. Ce sera le sujet d'un autre article.
Tous les traitements ont été effectués sur Mac OS X avec Python 2.5.4
Site officiel : simplejson
Site officiel : geopy
Site officiel : googlemaps
Site officiel : pymaps