Géocodages simple et inversé avec Google Maps et OpenLayers

La localisation d’une adresse postale est utilisée dans de nombreux services en ligne. L’obtention des coordonnées géographiques depuis cette adresse s’appelle le géocodage (ou geocoding en anglais) et si on veut connaître une adresse à partir d’une latitude et d’une longitude, on parle de géocodage inversé. Bien évidemment, cette correspondance demande la mise en place d’une base de données spatiales conséquentes avec des mises à jour continues et seules quelques structures ont développé des API de géocodage. A l’échelle mondiale, on peut citer Bing Maps, MapQuest, Here et Google Maps et on note que pour chaque API, une clé est nécessaire. Dans ce post, on se sert de l’API de Google Maps seulement pour le géocodage et non pour la cartographie dynamique et les objets géographiques générés par OpenLayers (v4). Après la mise en place de la cartographie, on commencera par coder le géocodage simple où chaque géométrie des points localisés est ajoutée dans un vecteur. Puis, par le clic sur la carte, les coordonnées géographiques obtenues permettront d’afficher l’adresse correspondante.

1. Mise en place de la carte avec OpenLayers

Dans un premier temps, on s’occupe de l’ergonomie de la carte avec l’input pour saisir les adresses qui sont stockées en tant qu’objets dans la source du vecteur.

1.1. Style et affichage des composants de la carte

Pour l’affichage de la carte, on peut s’appuyer sur les différents tutoriels et démonstrations de webmapping avec l’API d’OpenLayers sur le site de Geomatick. L’idée, ici, est de se focaliser sur la barre de saisie d’adresse qui est placée en haut et au centre de la carte. Cela évite de superposer l’input avec les contrôles par défaut… mais libre à chacun de modifier le CSS pour choisir son emplacement. Aussi, on s’aide de Bootstrap pour former un groupe compact entre l’input et le bouton qui déclenche la fonction de localisation des adresses au clic. Pour la partie CSS et HTML, on a donc :

#bloc-input {
	text-overflow: ellipsis;
	width: 300px;
	position: absolute;
	top: 10px;
	left: 40%;
	z-index:9999;
}

...

<div id="map" class="map">
	<div id="bloc-input" class="input-group">
		<input id="adresse" type="text" class="form-control" placeholder="Saisir une adresse, une ville">			  
		<span class="input-group-btn">
			<button class="btn btn-success" type="button" onclick="geocodage(); return false;"><i class="fa fa-map-marker"></i></button>
		</span>
	</div>
</div>

Plusieurs remarques sur les lignes de code précédentes, le groupe « input-bouton » désigné par l’id « bloc-input » a un position absolue et un z-index élevé pour être visible sur une carte en plein écran associé au contrôle full-screen. Il est ensuite placé dans la div de la carte, l’id de l’input « adresse » nous servira a obtenir la valeur saisie et la fonction appelée geocodage( ) est placée dans la balise du bouton.

On peut passer à la partie concernant le code JavaScript.

1.2. Création du vecteur représentant les localisations

Les déclarations des couches de la carte et de l’objet de la carte Map sont réalisées. Ici, on choisit le fonds cartographique d’OpenStreetMap (OSM) puis le vecteur des localisations est créé. Pour rappel, dans OpenLayers, un vecteur est défini par un ou des objets géographiques (features). Dans cet exemple, chaque localisation de l’adresse est ajoutée à un seul et unique vecteur; c’est pourquoi on déclare la source du vecteur vide, c’est-à-dire sans objet. Le vecteur est ensuite ajouté à l’objet Map, ce qui nous donne :

var OSM = new ol.layer.Tile({
	source: new ol.source.OSM()
});
var styleVecteur = new ol.style.Style({
	image: new ol.style.Circle({
		radius: 6,
		fill: new ol.style.Fill({
			color: '#3399CC'
		}),
		stroke: new ol.style.Stroke({
			color: '#fff',
			width: 2
		})
	})
});
var sourceVecteur = new ol.source.Vector({});
var vecteur = new ol.layer.Vector({
	source: sourceVecteur,
	style: styleVecteur
});
var map = new ol.Map({
	layers: [OSM,vecteur],
	target: 'map',
	view: new ol.View({
		center: ol.proj.transform( [0, 48], 'EPSG:4326', 'EPSG:3857'),
		zoom: 3
	})
});

Le style du vecteur est simple, on peut se référer au tutoriel pour mesurer et afficher la distance entre deux points pour symboliser les localisations par des icons. Ensuite, le centre de la carte est transformé dans la projection WGS 84 (ESPG 4326) qui est plus simple à définir. On attaque maintenant la fonction de géocodage.

2. Géocodage simple

En théorie, l’URL de l’API geocoding de Google Maps nécessite une adresse et la clé de l’API qui peut être différente pour chaque API (place, geocode, etc) ou globale avec l’API Google Maps. Les valeurs retournées après l’interrogation du service peuvent être de format XML ou JSON, ici on choisit de traiter les données JSON. Pour plus de détails sur les valeurs retournées par l’API, on se réfère à la documentation de Google. La fonction de géocodage peut être séparée en deux étapes : une première concerne le traitement de la requête JSON puis on se concentre sur la géométrie du vecteur.

2.1. Réponse JSON de la requête de géocodage

Dans la fonction,

  • on récupère la valeur de l’input (cf. $1.1) grâce à son id;
var adresse = $("#adresse").val();
  • cette valeur est ensuite placée dans l’URL de l’API geocoding – si l’adresse n’est pas vide -;
var urlAdresse = 'https://maps.googleapis.com/maps/api/geocode/json?address='+adresse+'&key=votre clé de l'API';
  • on utilise la classe $.getJSON  de JQuery pour retourner les coordonnées géographiques si le statut de la requête est validé;
$.getJSON(urlAdresse, function(json){
	var status = json.status;
	if(status=="OK"){
		var latAdresse = json.results[0].geometry.location.lat;
		var lngAdresse = json.results[0].geometry.location.lng;
		//... (cf. $2.2)
	}else{
		alert('erreur : ' +status);
	}
});

Normalement, le statut doit être « OK », c’est-à-dire que l’API Google nous renvoie bien les valeurs mais certaines limitent peuvent provoquer une erreur.  On peut citer par exemple le code de statut : "OVER_QUERY_LIMIT" qui indique que le nombre de requêtes a été dépassé. Par défaut, Google permet une utilisation libre de son service jusqu’à 2500 requêtes par jour.

2.2. Ajout de la géométrie dans la couche vectorielle

Après la déclaration de la latitude latAdresse et de la longitude lngAdresse de l’adresse, ces coordonnées « reprojetées » définissent la géométrie de type point de l’objet géographique.

var coord = [lngAdresse,latAdresse];
var newcoord = ol.proj.transform(coord, 'EPSG:4326','EPSG:3857');
var locFeature = new ol.Feature({
	geometry: new ol.geom.Point(newcoord),
});

On ajoute l’objet à la source du vecteur puis on peut centrer la carte sur les coordonnées (figure 1).

sourceVecteur.addFeature(locFeature);
map.getView().setCenter(newcoord);

A ce stade, le code est fonctionnel pour le géocodage simple et on peut donc passer à la fonction de géocodage inverse.

Figure 1 : Exemple de géocodage de la ville de Rennes en Bretagne.

3. Géocodage inversé

Comme évoqué précédemment, l’idée est de cliquer sur la carte afin d’indiquer l’adresse dans l’input. Ainsi, on récupère la situation géographique :

map.on('click',function(evt){
	var position = evt.coordinate;
	var newposition = ol.proj.transform(position, 'EPSG:3857','EPSG:4326');
	var latitude = newposition[1];
	var longitude = newposition[0];
	...
});

La latitude et la longitude sont ensuite incorporées dans l’URL de l’API de Google puis on traite les réponses JSON :

var urlPosition = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='+latitude+','+longitude+'&key=votre clé de l'API';
$.getJSON(urlPosition, function(json){
	var status = json.status;
	if(status=="OK"){
		var formatted_address = json.results[0].formatted_address;
		$("#adresse").val(formatted_address);
		var locFeature = new ol.Feature({
			geometry: new ol.geom.Point(position),
		});
		sourceVecteur.addFeature(locFeature);
		map.getView().setCenter(position);
	}else{
		alert('erreur : ' +status);
	}
});

Ici, on renvoie la valeur de l’adresse formatée formatted_address dans l’input.

En conclusion, l’API de géocodage de Google Maps est couplée à OpenLayers afin de localiser les adresses saisies sur une cartographie dynamique. En sens inverse, à partir de la méthode du clic sur la carte d’OpenLayers, on affiche l’adresse issue de la réponse JSON des services de Google Maps. Un dépassement des quotas de requêtes de Google est la principale limite mais on peut toutefois la contourner en utilisant les autres services de Bing Maps par exemple. Cet article permet ainsi de garder les fonctionnalités Open Sources d’OpenLayers tout en y associant des services utiles.

Note : un plugin de géocodage nommé « geocoding » existe pour QGIS.

5 Comments

  1. Bonjour! Peux t-on faire la même chose pour un seul pays? mon problème est comment faire pour détourer le pays en question? Merci bien!
    Très bon tuto!!!

  2. Bonjour Tsinjo,
    Je te remercie pour ton message.
    Je ne suis pas certain d’avoir totalement compris ta question. Tu veux qu’à la recherche d’une adresse, les frontières du pays recherché soient surlignées?
    Si c’est le cas, dans la réponse JSON, on peut obtenir le sigle et le nom du pays par :
    json.results[0].address_components[5].long_name; (par exemple : United States) et json.results[0].address_components[5].short_name; (par exemple : US).
    Ensuite, il faut que cette variable corresponde à l’attribut d’un nouveau vecteur qui représente les limites des pays.

  3. Re : Je voulais savoir si on pouvais juste avoir la carte d’un pays mais pas toute la carte du monde et c’est dans ce pays qu’on effectuera les recherches. Je dirais qu’il s’agit d’un problème de fond cartographique. Merci de votre intérêt.

  4. La plupart des fonds cartographiques sont à l’échelle du globe mais il est possible par exemple d’importer les données d’OpenStreetMap d’un seul pays. Je te joins un lien vers un tutoriel qui permet de stocker la donnée dans PostGreSQL puis de la servir via GeoServer : http://workshops.boundlessgeo.com/tutorial-osm/
    Ensuite pour cibler les recherches de l’API Google, il est possible de définir une région prioritaire dans l’URL : https://developers.google.com/maps/documentation/geocoding/intro?hl=fr#RegionCodes

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *