Avant toute chose, merci à Nantes/Nantes-Métropole et surtout aux équipes qui en ont bavé pour tenir les promesses de J.M. Ayrault (c'est bien beau de faire des promesses politiques, mais s'il n'y a pas derrière des équipes qui tiennent la route pour mener les projets à bien, on ne va nul part). Merci bien entendu aussi à LiberTIC et en particulier à Claire pour le formidable boulot accompli ;)

Je crois que j'ai fait le tour des remerciements (non ce billet n'est pas sponsorisé...).

Je disais donc qu'un jeu de données avait immédiatement attiré l'attention des contributeur : le fichier des adresses postales de Nantes Métropole. Un fichier de 12 Mo pour des adresses postales... on s'est demandé ce qu'il pouvait bien pouvoir y avoir à l'intérieur. C'est somme tout assez simple et évident : les coordonnées GPS de toutes les boîtes aux lettres de l'agglomération Nantaise et les adresses associées.

Outre optimiser la tournée des facteurs, ces données vont permettre aux contributeurs OSM de vérifier la couverture de Nantes Métropole en croisant les données de ce fichier et celles déjà sur OSM (Open Street Map pour ceux qui se demandent bien la signification de cet acronyme depuis le début du billet... et si vous voulez en savoir plus sur ce super projet, c'est par ici).

Une histoire de bbox

Le fichier Adresses_nm.csv contient donc les coordonnées GPS de toutes les boîtes aux lettres de la métropole nantaise et plus précisément :

  • l'adresse (ex : 3 Rue des Quinze Sillons)
  • la commune associée (ex : ST-SEBASTIEN)
  • le mot directeur, c-à-d le mot le plus différenciateur de l'adresse (ex : Quinze)
  • le numéro seul (ex : 3)
  • le code rivoli de la voie (ex : 1176)
  • le code postal (ex : 44230)
  • et la longitude et latitude.

Dans un premier temps, nous allons utiliser ces données pour définir une bounding box (boîte contenante) pour chacune des communes de l'agglomération. L'idée est très simple : on parcourt toutes les entrées du fichier CSV en distribuant les coordonnées GPS par commune et en ne conservant que le min et le max des latitudes et longitudes.

Quelques lignes de python :

import csv
reader = csv.DictReader(open('Adresses_nm.csv', 'rb'), delimiter=';')
bbox_communes = {}
for row in reader:
	commune   = row["NOMCOM"]
	latitude  = float(row["LAT_WGS84"].replace(",", "."))
	longitude = float(row["LON_WGS84"].replace(",", "."))
	if not bbox_communes.has_key(commune):
		bbox_communes[commune] = {
			"minlat": latitude,
			"maxlat": latitude,
			"maxlon": latitude,
			"minlon": latitude}
	else:
		if latitude > bbox_communes[commune]["maxlat"]:
			bbox_communes[commune]["maxlat"] = latitude
		if latitude < bbox_communes[commune]["minlat"]:
			bbox_communes[commune]["minlat"] = latitude
		if latitude > bbox_communes[commune]["maxlon"]:
			bbox_communes[commune]["maxlon"] = longitude
		if latitude < bbox_communes[commune]["minlon"]:
			bbox_communes[commune]["minlon"] = longitude

... et voici les bbox :

Compter les communes

Dans un second temps nous allons vérifier que toutes les communes qui sont présentes dans le fichier sont bien déclarées dans OSM, et plus précisément déclarées dans les bbox calculées. Le plus simple pour ce faire est d'utiliser l'XAPI, soit pour Rezé par exemple, une requête de ce style : http://open.mapquestapi.com/xapi/api/0.6/*%5Bplace=*%5D%5Bbbox=-1.585649046124,47.152330025829,-1.521063232296,47.196722167190%5D. Ceci nous permet de nous rendre compte qu'OSM est très précise puisqu'en plus de Rezé la base de données contient de nombreux lieux-dits (ou quartiers) : Trentemoult, Le Port Au Blé, Saint Paul, La Houssais...

Encore une fois OSM est plus précis que les données officielles ;)

Trêve de digression, nous allons nous concentrer sur les communes uniquement et bien sûr automatiser le processus :

import urllib2
from lxml import etree
import sys
for k in bbox_communes.keys():
	sys.stdout.write(u"%s..." % unicode(k, "ISO8859"))
	maxlon = bbox_communes[k]["maxlon"]
	minlon = bbox_communes[k]["minlon"]
	maxlat = bbox_communes[k]["maxlat"]
	minlat = bbox_communes[k]["minlat"]
	xapiurl = "http://open.mapquestapi.com/xapi/api/0.6/node[place=*][bbox=%f,%f,%f,%f]" % (minlon,minlat,maxlon,maxlat)
	try:
		xml = urllib2.urlopen(xapiurl).read()
		dom = etree.XML(xml)
		for node in dom.iter(tag="node"):
			values = unicode("/".join([t.get("v") for t in node.iter(tag="tag")]))
			sys.stdout.write(u"%s=%s" % (node.get("uid"), values))
	except urllib2.HTTPError:
		sys.stdout.write("error querying (%s)" % (xapiurl))
	sys.stdout.write("\n")

Résultat, il va falloir homogénéiser un peu les tags utilisés pour les communes :)

Communeplace ?uid
REZEtown58255
ORVAULT # #
BOUGUENAIS # #
SAINT-AIGNAN-GRANDLIEU # #
SAINT-JEAN-DE-BOISEAUvillage58255
INDRE ? ?
ST-HERBLAIN ? ?
COUERON ? ?
SAUTRONvillage58255
SAINTE-LUCE-SUR-LOIRE ? ?
VERTOU # #
LA-MONTAGNE # #
BRAINSvillage58255
BOUAYE ? ?
LES-SORINIERES ? ?
NANTEScity212207
ST-SEBASTIEN # #
MAUVES-SUR-LOIREvillage58255
SAINT-LEGER-LES-VIGNESvillage58255
LA-CHAPELLE-SUR-ERDRE ? ?
THOUARE-SUR-LOIRE ? ?
BASSE-GOULAINE ? ?
CARQUEFOUtown58255
LE-PELLERIN ? ?

Prochaine étape, vérifier les noms des rues... et ensuite insertion des adresses dans la base ;)