Suite à des discussions sur le géocodage par le géocodeur de l'IGN, nous avons trouvé intéressant d'implémenter son support dans la bibliothèque Python Geopy. Le géocodage, pour rappel, c'est le fait d'associer des adresses à des coordonnées géographiques.
Nous avons pour le moment ignoré le support des codes EPSG. Il est en effet possible de retourner les coordonnées transformées en coordonnées locales (EPSG 2154, ...) mais comme les objets retournés par Geopy lors du géocodage contiennent une référence à latitude, longitude, cela ne nous paraissait pas très pertinent pour rester générique. Ce support pourra toujours être ajouté ultérieurement.
Nous avons rencontrés quelques cas qui ne semblent pas fonctionner ou bien nous avons loupé des informations dans la documentation officielle http://api.ign.fr/tech-docs-js/fr/developpeur/search.html
Enregistrez-vous auprès de l'IGN¶
Vous devez pour vos tests, vous enregistrer sur le site http://api.ign.fr pour créer une clé pour un usage "développeurs". Il vous faut créer une clé avec le support de OpenLS (c'est un standard OGC pour le géocodage). Cela pourra être une clé "Sig" ou "Web" qui permettent d'accéder aux trois fonctionnalités de géocodage qu'offrent l'API. Celle-ci permet une recherche adresse, une recherche par parcelles (sous réserve que ces parcelles soient des parcelles avec cadastre vecteur) ou par toponymes. Vous avez aussi la fonctionnalité de reverse geocoding qui consiste à faire l'opposer du géocodage c'est à dire d'associer une adresse à une position. Typiquement, cal répond à une question vous êtes livreur et souhaitez confirmer que vous êtes dans la bonne rue où livrer. Depuis votre mobile, vous envoyez vos coordonnées et vous connaissez votre rue.
Selon, votre usage, attention de souscrire à la "bonne" licence. Pour cela, allez voir les conditions d'utilisation sur http://professionnels.ign.fr/api-web#tab-3
Installer Geopy¶
Pour essayer le géocodeur de l'IGN, vous devez installer GeoPy (version 1.7) avec
pip install geopy
Configurer les variables d'environnement¶
Après installation, nous allons vous montrer quelques exemples d'opérations de géocodage
Pour éviter de coder "en dur", c'est à dire dans un fichier contenant du code, nous allons utiliser des variables d'environnement pour par exemple éviter de laisser trainer des informations confidentielles sur un gestionnaire de version type SVN, Git ou Mercurial
Pour vous authentifier, vous avez deux choix (qui s'excluent):
- en passant par ce qu'on appel le
refereravec la clé d'API - avec un couple nom utilisateur / mot de passe avec la clé d'API.
Selon votre clé et votre système d'exploitation, l'exportation des variables d'environnement varie aussi. Voir le récapitulatif ci-dessous.
Linux / Mac
# Clé Sig
export IGNFRANCE_KEY='votre_cle'
export IGNFRANCE_USERNAME='votre_nom_utilisateur'
export IGNFRANCE_PASSWORD='votre_mot_de_passe'
unset IGNFRANCE_REFERER
ou
# Clé Web
export IGNFRANCE_KEY='votre_cle'
export IGNFRANCE_REFERER='localhost' # En dev, c'st localhost mais cela peut changer quand vous avez une clé "pro"
unset IGNFRANCE_USERNAME
unset IGNFRANCE_PASSWORD
Windows
# Clé Sig
set IGNFRANCE_KEY='votre_cle'
set IGNFRANCE_USERNAME='votre_nom_utilisateur'
set IGNFRANCE_PASSWORD='votre_mot_de_passe'
setx IGNFRANCE_REFERER ""
ou
# Clé Web
set IGNFRANCE_KEY='votre_cle'
set IGNFRANCE_REFERER='localhost' # En dev, c'st localhost mais cela peut changer quand vous avez une clé "pro"
setx IGNFRANCE_USERNAME ""
setx IGNFRANCE_PASSWORD ""
# Pour la compatibilité Python 2/3
from __future__ import absolute_import, division, print_function
from builtins import (bytes, str, open, super, range,
zip, round, input, int, pow, object)
from geopy.geocoders.ignfrance import IGNFrance
import os
# Si on veut le mode debug qui permet d'afficher les urls appelées ainsi que le contenu XML qui est envoyé et reçu,
# on décommente les 4 lignes ci-dessous
#import logging
#logger = logging.getLogger('geopy')
#logger.addHandler(logging.StreamHandler())
#logger.setLevel('DEBUG')
# Partie authentification qui nécessite d'avoir enregistré les variables d'environnement avant d'avoir lancé le "notebook"
ign = IGNFrance(
api_key=os.environ.get('IGNFRANCE_KEY'),
username=os.environ.get('IGNFRANCE_USERNAME'),
password=os.environ.get('IGNFRANCE_PASSWORD'),
referer=os.environ.get('IGNFRANCE_REFERER'),
timeout=20 # Set to 20 seconds
)
# Appel au cadastre pour avoir les coordonnées d'une parcelle
cadastre_call = ign.geocode('44109000EX0114', 'CadastralParcel', maximum_responses=10, exactly_one=True)
print (cadastre_call.raw)
# Adresse avec retour de type freeform
adress_call_with_freeform = ign.geocode(
query="8 rue Général Buat, Nantes",
query_type="StreetAddress",
is_freeform=True,
exactly_one=False,
maximum_responses=5
)
print (adress_call_with_freeform)
for adr in adress_call_with_freeform:
print (adr.raw)
# Adresse avec retour "classique"
adress_call = ign.geocode('8 rue Général Buat, Nantes', 'StreetAddress',
maximum_responses=10, exactly_one=False)
for poi in adress_call:
print (poi.raw)
# Cas PositionOfInterest en réalité les toponymes
position_of_interest_call_attrib_filtering = ign.geocode(
'Les Molettes',
'PositionOfInterest',
maximum_responses=10,
filtering='<Place type="Departement">38</Place>',
exactly_one=False
)
for poi in position_of_interest_call_attrib_filtering:
print (poi.raw)
# Filtrage des résultats en fonction d'une enveloppe
lat_min, lng_min, lat_max, lng_max = 45.00, 5, 46, 6.40
spatial_filtering_envelope = """
<gml:envelope>
<gml:pos>{lat_min} {lng_min}</gml:pos>
<gml:pos>{lat_max} {lng_max}</gml:pos>
</gml:envelope>
""".format(
lat_min=lat_min,
lng_min=lng_min,
lat_max=lat_max,
lng_max=lng_max
)
position_of_interest_call_spatial_filtering_envelope = ign.geocode(
'Les Molettes',
'PositionOfInterest',
maximum_responses=10,
filtering=spatial_filtering_envelope,
exactly_one=False
)
for poi in position_of_interest_call_spatial_filtering_envelope:
print (poi.raw)
# Reverse géocodage "Normal" c'est à dire sans filtre
point_call = ign.reverse(
query='47.229554,-1.541519',
exactly_one=True
)
print (point_call.raw)
# Il existe la possibilité de retourner à la fois des adresses et des toponymes via des "preferences
point_call_preference = ign.reverse('47.229554,-1.541519',
reverse_geocode_preference=['StreetAddress', 'PositionOfInterest'])
print (point_call_preference[0].raw)
# Reverse geocodage en spécifiant le centre et le rayon de recherche en mètres
spatial_filtering_radius = """
<gml:CircleByCenterPoint>
<gml:pos>{coord}</gml:pos>
<gml:radius>{radius}</gml:radius>
</gml:CircleByCenterPoint>
""".format(coord='48.8033333 2.3241667', radius='50')
point_call_radius = ign.reverse('48.8033333,2.3241667',
maximum_responses=10,
filtering=spatial_filtering_radius)
print (len(point_call_radius))
# Test avec les mêmes coordonnées, sans filtre spatial
point_call_no_radius = ign.reverse('48.8033333,2.3241667',
maximum_responses=10)
print (len(point_call_no_radius))
# Filtrage avec un polygone
# Ne marche pas ou bien on a loupé quelque chose dans la documentation
# http://api.ign.fr/tech-docs-js/fr/developpeur/search.html#Recherche_avec_une_contrainte_polygonale
spatial_filtering_polygon = """
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:pos>48.8033 2.3241</gml:pos>
<gml:pos>48.8033 2.3242</gml:pos>
<gml:pos>48.8032 2.3242</gml:pos>
<gml:pos>48.8032 2.3241</gml:pos>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
"""
point_call_polygon = ign.reverse('48.8033333,2.3241667',
maximum_responses=10, filtering=spatial_filtering_polygon)
print (point_call_polygon)
Exemple avec des données tabulaires¶
On termine avec un exemple plus complet qui utilise des bibliothèques de haut niveau comme Pandas pour manipuler un CSV Il permet d'utiliser aussi en entrée des données Excel et d'écrire en Excel
Selon votre besoin et vos contraintes, le module CSV de Python peut faire l'affaire.
Si vous devez utiliser Excel, il est possible de travailler avec Tablib ou bien le couple xlrd/xlwt
Pour LibreOffice, il est possible d'utiliser une bibliothèque comme Unotools qui s'interface avec LibreOffice/OpenOffice via Python
Vous devez installer Pandas et Numpy avec
pip install numpy pandas
import pandas as pd
import numpy as np
import urllib
# Les données viennent de http://www.ign.fr/institut/adresses-lign
df = pd.read_csv(
'ign_adresse_a_geocoder.tsv',
sep='\t',
encoding='utf-8'
)
columns_names = list(df.columns.values)
print (columns_names)
# [u'NOM', u'ADRESSE', u'CP_VILLE', u'TEL']
# Ajout de nouvelles "colonnes"
df.insert(len(columns_names), "R_ADRESS", np.nan)
df.insert(len(columns_names) + 1, "LONGITUDE", np.nan)
df.insert(len(columns_names) + 2, "LATITUDE", np.nan)
df.insert(len(columns_names) + 3, "ACCURACY", np.nan)
df.insert(len(columns_names) + 4, "QUALITE", np.nan)
df.insert(len(columns_names) + 5, "MATCH_TYPE", np.nan)
# Appel au service de géocodage et mise à jour des colonnes avec les résultats
for index, row in df.iterrows():
url_encoded_address = (row['ADRESSE'] + ', ' + row['CP_VILLE']).encode('ascii', errors='xmlcharrefreplace') # Pour éviter des erreurs sur les accents
#print url_encoded_address
results = ign.geocode(
url_encoded_address,
query_type="StreetAddress",
exactly_one=True
)
df.loc[index, 'R_ADRESS'] = results.address
df.loc[index, 'LONGITUDE'] = results.longitude
df.loc[index, 'LATITUDE'] = results.latitude
df.loc[index, 'ACCURACY'] = results.raw['accuracy']
df.loc[index, 'QUALITE'] = results.raw['qualite']
df.loc[index, 'MATCH_TYPE'] = results.raw['match_type']
# Write results
df.to_csv('adresses_avec_geocodage.txt', sep='\t', encoding='utf-8', index=False)
df
Les résultats dans adresses_avec_geocodage.txt sont très bons mais aussi quelquefois très mauvais. On les identifie assez vite car le résultat est alors avec une précision "à la ville" City
Voici quelques cas où le résultat est à la ville et ce n'est pas toujours la bonne ville d'ailleurs.
# Ville où code postal et adresses ne correspondent pas en fait à une commune (ville nouvelle)
# Voir https://fr.wikipedia.org/wiki/Marne-la-Vall%C3%A9e
adress_call = ign.geocode('6/8, avenue Blaise Pascal, 77455 MARNE-LA-VALLÉE CEDEX 2', 'StreetAddress',
maximum_responses=10, exactly_one=True)
print (adress_call.address)
adress_call = ign.geocode('6/8, avenue Blaise Pascal, Champs sur Marne', 'StreetAddress',
maximum_responses=10, exactly_one=True)
print (adress_call.address)
# Juste un édifice mais sans rue donc un peu normal mais gagnerait à utiliser des noms de bâtiments
# Pour éviter des erreurs sur les accents
address = 'Château des Barres, 45290 NOGENT-SUR-VERNISSON'.encode('ascii', errors='xmlcharrefreplace')
adress_call = ign.geocode(address, maximum_responses=10, exactly_one=True)
print (adress_call.address)
# Correspondance la meilleure (similarité de chaîne du genre calcul de la distance de Levenshtein)
# Malheureusement, le nom de la commune est incomplet donc c'est faux.
# Le calculateur semble ignorer l'indication que fournit le code postal dans la chaîne envoyée
address = "6, avenue de l'Europe - BP 42116, 31521 RAMONVILLE CEDEX".encode('ascii', errors='xmlcharrefreplace')
adress_call = ign.geocode(address, maximum_responses=10, exactly_one=True)
print (adress_call.address)
address = "6, avenue de l'Europe - BP 42116, 31521 RAMONVILLE-SAINT-AGNE CEDEX".encode('ascii', errors='xmlcharrefreplace')
adress_call = ign.geocode(address, maximum_responses=10, exactly_one=True)
print (adress_call.address)
Récupération du code¶
Pour cela, suivez l'URL suivante https://gist.github.com/ThomasG77/6b0525707674b1bbb343
Support du géocodeur en dehors de Python¶
Le support du géocodage est plus limité mais existe aussi côté PHP.
Vous pouvez aussi utiliser CURL, un utilitaire en ligne de commande même s'il vous restera toujours à gérer le XML retourné.
Par ailleurs, des exemples sont aussi disponibles sur le site officiel de l'IGN lorsque vous voulez géocoder directement côté client (en JavaScript ou Flash) mais sans avoir besoin de garder le résultat du géocodage en mémoire http://api.ign.fr/tech-docs-js/examples/
Aide possible
Si vous avez des besoins liés au géocodage ou que certaines fonctions sont manquantes, nous pouvons les implémenter. N'hésitez pas à nous contacter.
Notre partenaire spécialisé dans la formation et l’accompagnement à la migration vers les logiciels libres.
Notre partenaire études, statistiques et observatoires
Notre coopérative d'activité, elle nous accompagne dans la création et la gestion de notre activité profesionnelle
Commentaires