2: google.maps.ClientGeocoder : latitude et longitude d'un point à partir de son adresse
Par Pascal MARTIN le lundi 4 février 2008, 08:00 - Développement Web - Lien permanent
L'article 1: Afficher une carte avec Google Maps nous a permis de voir comment afficher une carte en utilisant l'API Google Maps.
Mais l'exemple utilisé supposait que nous connaissions la latitude et la longitude du point sur lequel nous souhaitions centrer notre carte.
En réalité, en particulier lorsque l'on interagit avec un utilisateur, il est plus fréquent de connaître l'adresse d'un lieu, plutôt que ses coordonnées géographiques !
Par exemple, il est plus facile de se souvenir de "Place Bellecour, Lyon, France", que de "(45.756788, 4.831515)".
Heureusement, Google Maps fourni un service, le Geocoder, permettant d'obtenir les coordonnées géographiques d'un point à partir de son adresse.
La documentation de l'API de Geocoding est accessible sur le site de google.
Attention : Pour cet article, je présuppose que vous avez lu et compris la première partie : Afficher une carte avec Google Maps, et ne reviendrai pas sur les concepts qui y sont déjà présentés.
Afficher la carte en fonction d'une adresse
En première partie de cette série d'articles sur Google Maps, nous avons centré la carte sur la Place Bellecour, dont nous connaissions la latitude et la longitude :
- Latitude : 45.756788
- Longitude : 4.831515
Cela dit, ce n'est pas très "User Friendly" !
Google Maps fournit un service permettant de "traduire" une adresse en coordonnées géographiques.
Ce service est nommé "Geocoder".
Le principe de base est simple : vous lui fournissez une adresse en entrée, et il retourne la couple latitude/longitude correspondant.
Pour utiliser ce service, commencez- par instancier la classe google.maps.ClientGeocoder, et utilisez la méthode getLatLng.
Cette méthode réalise un appel en arrière-plan vers les serveurs Google, et appelle, une fois les coordonnées reçues, une méthode dont la référence lui a été passée en second paramètre. Celle-ci reçoit en argument le couple de coordonnées.
Par exemple, si la variable adresse contient l'adresse dont on veut charger les coordonnées :
var geocoder = new google.maps.ClientGeocoder(); geocoder.getLatLng(adresse, function (coord) { // Et centrage de la map sur les coordonnées renvoyées par Google : map.setCenter(coord, 15); });
Notre fonction appelée au chargement de la page peut donc être ré-écrite comme ceci :
var initMap = function () { var map = new google.maps.Map2(document.getElementById('map')); var adresse = 'place bellecour, lyon, france'; // Recherche des coordonnées d'un point dont on connait l'adresse : var geocoder = new google.maps.ClientGeocoder(); geocoder.getLatLng(adresse, function (coord) { // Et centrage de la map sur les coordonnées renvoyées par Google : map.setCenter(coord, 15); }); }; // initMap
Et voici le résultat :
Autrement dit, nous obtenons la même chose que ce que nous voyions au cours de l'article précédent, lorsque nous affichions la carte directement en fonction de la latitude et de la longitude du point nous intéressant.
Soyez précis dans les adresses que vous utilisez
Google Maps est un service fonctionnant à l'échelle du monde entier.
Dans le monde, il existe probablement plusieurs localisations portant le nom de celle qui vous intéresse !
Le Geocoder de Google Maps fait de son mieux : si vous lui passez une adresse qui correspond à plusieurs localisations, il aura tendance à renvoyer les coordonnées de celle qu'il considère comme étant "la meilleure".
Typiquement, cela peut signifier que si vous saisissez un nom correspondant à plusieurs villes, il pourrait avoir tendance à renvoyer les coordonnées de la plus grande - ou de la plus peuplée - ou de la plus importante géo-politiquement parlant - ou ...
Pour illustrer mes propos, voici un exemple :
Vous cherchez à afficher une carte centrée sur une ville nommée "Vienne".
Cette ville se trouve en Isère (département 38), en France.
Il existe plusieurs villes nommées "Vienne".
Vienne, capitale de l'Autriche
Pour commencer, lançons une recherche en appelant le Geocoder en ne lui passant que "Vienne" comme adresse.
Voici le résultat obtenu :
Bingo, la capitale de l'Autriche...
Une grande ville nommée "Vienne", importante d'un point de vue géo-politique, fort peuplée (1.6 million d'habitants, si j'en crois la page que Wikipedia consacre à Vienne.)
Mais pas la "Vienne" que nous recherchions...
Vienne, un département
Seconde tentative, précisons que nous cherchons une localisation en France, en appelant le Geocoder sur "Vienne, France".
Voici le résultat obtenu :
Cette fois, le service Google Maps nous renvoi vers le département de la Vienne.
Géo-politiquement parlant, ce département est plus important que la ville que nous cherchions ; sa superficie est plus grande ; il est plus peuplé ; et il est probablement plus connu...
Mais ce n'est pas la ville qui nous intéressait !
Vienne, une ville en Isère
Pour notre troisième essai, soyons encore plus précis : lançons une recherche sur "38, Vienne, France".
(Pour rappel, la ville que nous cherchons est en Isère, le département 38)
Et voici le résultat :
Cette fois-ci, le Geocoder nous a bien renvoyé le résultat attendu.
Enseignements à tirer de cet exemple
Quelle était le but de cet exemple ?
Vous montrer que :
- Le service de geocoding ne fait pas toujours ce que vous souhaiteriez
- Il peut donc être judicieux de faire valider par l'utilisateur ce que le GeoCoder a renvoyé ; typiquement, si un utilisateur saisit son adresse, et que vous voulez enregistrer les coordonnées correspondantes en Base de Données, pourquoi ne pas afficher une carte à l'utilisateur, lui demandant si la localisation proposée sur la carte est bien celle souhaitée - et lui demandant de préciser son adresse, dans le cas contraire
- Plus vous êtes précis, meilleures seront les chances que le service de geocoding trouve la "bonne localisation"
- Indiquez les libellés que vous connaissez
- Si vous savez à quel pays se rapporte l'adresse, précisez le
- Si vous pouvez fournir une indication supplémentaire - un code postal, par exemple - faites le !
Fournir quelques indications supplémentaires au GeoCoder
Pour améliorer vos chances d'obtenir la "bonne" adresse, vous pouvez passer quelques indications au service de Geocoding.
setBaseCountryCode
Pour commencer, vous pouvez indiquer au service de Geocoding que vous préférez recevoir des résultats se trouvant dans un pays donné.
Par exemple :
geocoder.setBaseCountryCode('fr');
Dans le cas où votre application n'est pas internationalisée, cela augmente les chances de renvoyer des résultats correspondant à ce que l'utilisateur souhaitait.
setViewport
L'API du GeoCoder permet d'aller plus loin que cela : il est possible d'indiquer au service que l'on souhaite qu'il accorde une priorité plus élevée aux localisation se trouvant dans une zone délimitée par deux points.
Prenons par exemple le cas d'une ville nommée Beaurepaire : il en existe quatre en France.
Les deux qui m'intéressent sont :
Par défaut, lorsque l'on effectue une requête sur "Beaurepaire, France" vers le service de Geocoding, sans lui donner plus de précision, celui-ci retourne :
Ce qui correspond à Beaurepaire en Isère.
Maintenant, considérons que l'utilisateur de notre application souhaite effectuer une recherche sur l'Ouest de la France (que ce soit parce qu'il habite dans la région, parce qu'il recherche une destination de vacances, ... tout dépendra de votre application) : la ville retournée par défaut ne lui conviendra certainement pas.
Le service de GeoCoding permet de définir une zone rectangulaire, délimitée par deux couples de coordonnées :
- Point constituant la limite sud-ouest de la zone,
- et coordonnées du nord-est de la zone.
Par exemple, pour délimiter une zone dans l'Ouest de la France :
var pointSW = new GLatLng(44, -3); var pointNE = new GLatLng(48, 2); geocoder.setViewport(new GLatLngBounds(pointSW, pointNE));
Dans sa recherche de coordonnées, le Geocoder aura tendance à privilégier les points situés dans cette zone.
Si nous relançons notre recherche sur "Beaurepaire, France", en utilisant la fonction setViewport comme présenté au-dessus, nous obtenons à présent :
Cette fois-ci, c'est Beaurepaire en Vendée qui a été renvoyé par le service de Geocoding.
Pour plus de clarté, j'ai ajouté l'affichage de la zone définie via setViewport, à l'aide du code suivant :
var cadre = new GPolyline([ new GLatLng(pointNE.lat(), pointSW.lng()), new GLatLng(pointNE.lat(), pointNE.lng()), new GLatLng(pointSW.lat(), pointNE.lng()), new GLatLng(pointSW.lat(), pointSW.lng()), new GLatLng(pointNE.lat(), pointSW.lng()) ], "#000000", 3); map.addOverlay(cadre);
Un point important est à noter : préciser un viewport est une indication seulement !
Autrement dit, le Geocoder est susceptible de ne pas suivre votre indication, et de retourner des coordonnées extérieures à la zone définie !
Sur l'exemple de Vienne, par exemple, ça ne fonctionnait pas : je me retrouvais toujours en Autriche... (Ce qui explique que, pour cette partie, j'ai utilisé une autre ville en exemple ^^)
Obtenir plusieurs localisations pour une adresse
Le service de GeoCoding permet d'aller plus loin : jusqu'à présent, nous l'avons utilisé pour obtenir une couple de coordonnées pour une adresse... Mais, pour une adresse donnée, il peut renvoyer plusieurs couples de coordonnées, correspondant toutes plus ou moins à la saisie.
Je m'explique : plus haut, nous avons travaillé avec l'exemple de "Beaurepaire, France", et nous avons constaté qu'il existait plusieurs "Beaurepaire" en France.
Mais la méthode getLatLng du Geocoder ne nous renvoyait toujours qu'une seule localisation : celle qui était considéré comme "la meilleure" par le service ; pas forcément celle que vous, ou votre utilisateur, considéreriez comme "la meilleure" !
Cette solution n'est pas forcément la plus adaptée au contexte de votre application : vous pourriez demander à l'utilisateur, parmi la liste des villes répondant à sa saisie, laquelle est celle qui l'intéresse réellement...
Pour répondre à ce besoin, le service de Geocoding fourni une autre méthode : getLocations.
Elle fonctionne de la même manière que getLatLng, mais renvoie des données pour une ou plusieurs localisations.
Par exemple, en l'appelant sur "Beaurepaire, France", la fonction de callback passée en second paramètre recevra non pas un simple couple de coordonnées, mais un objet de la forme suivante :
{
"name":"beaurepaire, france",
"Status":{
"code":200,
"request":"geocode"
},
"Placemark":[
{
"id":"p1",
"address":"Beaurepaire, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Rhône-Alpes",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Isère",
"Locality":{
"LocalityName":"Beaurepaire"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
5.05504,
45.338439,
0
]
}
},
{
"id":"p2",
"address":"Beaurepaire, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Pays de la Loire",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Vendée",
"Locality":{
"LocalityName":"Beaurepaire"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
-1.087891,
46.910971,
0
]
}
},
{
"id":"p3",
"address":"Beaurepaire-en-Bresse, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Bourgogne",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Saône-et-Loire",
"Locality":{
"LocalityName":"Beaurepaire-en-Bresse"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
5.386905,
46.667817,
0
]
}
},
{
"id":"p4",
"address":"Beaurepaire, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Picardie",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Oise",
"Locality":{
"LocalityName":"Beaurepaire"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
2.57048,
49.293437,
0
]
}
},
{
"id":"p5",
"address":"Beaurepaire, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Haute-Normandie",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Seine-Maritime",
"Locality":{
"LocalityName":"Beaurepaire"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
0.220527,
49.665643,
0
]
}
},
{
"id":"p6",
"address":"Beaurepaire-sur-Sambre, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Nord-Pas-de-Calais",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Nord",
"Locality":{
"LocalityName":"Beaurepaire-sur-Sambre"
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
3.793896,
50.062004,
0
]
}
},
{
"id":"p7",
"address":"Beaurepaire, Champrond-en-Gâtine, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Centre",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Eure-et-Loir",
"Locality":{
"LocalityName":"Champrond-en-Gâtine",
"DependentLocality":{
"DependentLocalityName":"Beaurepaire"
}
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
1.087505,
48.389163,
0
]
}
},
{
"id":"p8",
"address":"Beaurepaire, Laigny, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Picardie",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Aisne",
"Locality":{
"LocalityName":"Laigny",
"DependentLocality":{
"DependentLocalityName":"Beaurepaire"
}
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
3.86494,
49.85891,
0
]
}
},
{
"id":"p9",
"address":"Beaurepaire, Doullens, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Picardie",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Somme",
"Locality":{
"LocalityName":"Doullens",
"DependentLocality":{
"DependentLocalityName":"Beaurepaire"
}
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
2.395936,
50.16396,
0
]
}
},
{
"id":"p10",
"address":"Beaurepaire, Chauffayer, France",
"AddressDetails":{
"Country":{
"CountryNameCode":"FR",
"AdministrativeArea":{
"AdministrativeAreaName":"Provence-Alpes-Côte d'Azur",
"SubAdministrativeArea":{
"SubAdministrativeAreaName":"Hautes-Alpes",
"Locality":{
"LocalityName":"Chauffayer",
"DependentLocality":{
"DependentLocalityName":"Beaurepaire"
}
}
}
}
},
"Accuracy":4
},
"Point":{
"coordinates":[
6.02335,
44.739799,
0
]
}
}
]
}
Comme points intéressants, on peut noter :
- Une propriété "Status" indiquant si la requête a réussi,
- Une propriété "Placemark", contenant une liste de localisations correspondant à la saisie,
- avec, pour chaque Placemark :
- L'adresse : "address",
- Les composantes de l'adresse : "AdressDetails" (département, région, ...)
- Les coordonnées correspondantes : propriété "Point.coordinates"
Un exemple d'utilisation de cette fonctionnalité pourrait être :
geocoder.getLocations(adresse, function (coordonnees) { var nbrPlacemarks = coordonnees.Placemark.length; var iPlacemark; var placemark; for (iPlacemark=0 ; iPlacemark<nbrPlacemarks ; iPlacemark++) { placemark = coordonnees.Placemark[iPlacemark]; createMarkerOnPlacemark(map, placemark); } });
Cette portion de code fait appel au service de Geocoding, et, pour chaque localisation possible retournée, appelle la méthode suivante :
var createMarkerOnPlacemark = function (map, placemark) { var coord = placemark.Point.coordinates; var marker = new GMarker(new GLatLng(coord[1], coord[0])); GEvent.addListener(marker, "click", function() { var html = placemark.address; html += '<br />'; html += '( ' + placemark.Point.coordinates[1] + ' ; ' + placemark.Point.coordinates[0] + ' )'; html += '<p>'; if (placemark.AddressDetails.Country.AdministrativeArea) { var administrativeArea = placemark.AddressDetails.Country.AdministrativeArea; html += administrativeArea.AdministrativeAreaName; if (administrativeArea.SubAdministrativeArea) { var subAdministrativeArea = administrativeArea.SubAdministrativeArea; html += '<br />'; html += subAdministrativeArea.SubAdministrativeAreaName; if (subAdministrativeArea.Locality) { var locality = subAdministrativeArea.Locality; html += '<br />'; html += locality.LocalityName; if (locality.DependentLocality) { html += '<br />'; html += locality.DependentLocality.DependentLocalityName; } } } } html += '</p>'; marker.openInfoWindowHtml(html); }); // addListener map.addOverlay(marker); }; // createMarkerOnPlacemark
Cette méthode place un marqueur sur la carte, à l'emplacement de la localisation proposée par le Geocoder, et défini un gestionnaire d'événement associé au clic sur ledit marqueur : un clic sur celui-ci provoquera l'ouverture d'une fenêtre d'information, contenant quelques données sur la localisation en question.
(Nous verrons plus loin, dans un autre article, comment utiliser les marqueurs et les fenêtres d'information)
Et voici le résultat, pour une recherche sur "Beaurepaire, France" :
La même chose, après avoir cliqué sur un marqueur :
Et sur un autre marqueur :
Et pour finir, par curiosité, voici ce qu'il se passe si nous indiquons au service de Geocoding, via la méthode setViexport présentée plus haut, que nous voulons qu'il accorde une priorité plus élevée à une zone dans l'Ouest de la France :
On constate que certains points en dehors du viewport demandé ont disparu... Mais on a la preuve qu'utiliser la méthode setViewport revient à donner une indication, et seulement une indication, au service de Geocoding.
Utiliser le Geocoder côté serveur, via une requête HTTP
Jusqu'à présent, nous avons vu comment utiliser le Geocoder côté client, en Javascript.
Mais il est aussi possible de l'utiliser côté serveur, dans le langage que vous utilisez pour le reste de votre application.
Le principe est simple :
- Vous émettez une requête HTTP vers http://maps.google.com/maps/geo? en passant en paramètre
- votre clef d'API
- le format de données attendu en retour
- json
- xml / kml
- csv
- l'adresse que vous voulez localiser
Et le Geocoder vous renvoi la liste des localisations correspondantes, exactement comme vu juste au-dessus.
Par exemple, en PHP, en utilisant l'extension curl, vous écririez :
$localisation = "Place Bellecour, Lyon, France"; //$localisation = "Beaurepaire, France"; $localisationUrl = urlencode($localisation); $outputMode = 'xml'; // csv / xml / kml / json $apiKey = '...'; // TODO placer ici votre clef d'API $urlGeocoder = 'http://maps.google.com/maps/geo?key=' . $apiKey . '&output=' . $outputMode . '&q=' . $localisationUrl; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $urlGeocoder); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla 5/0'); // Pour avoir de l'UTF-8 en sortie... $resultGeocoder = curl_exec($ch); $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
Si le format demandé en sortie est 'csv', vous obtiendrez en réponse une chaîne composée de quatre champs, séparés par des virgules :
- code de statut,
- précision,
- latitude,
- et longitude
Si le format demandé est 'xml' ou 'kml', vous obtiendrez une réponse au format XML, contenant toutes les informations que nous avons déjà eu à disposition lorsque nous avons utilisé getLocations côté client.
Même chose si le format demandé est 'json', sauf que, cette fois-ci, les données seront regroupées au sein d'une chaîne de caractères en notation littérale d'objet Javascript.
Selon la situation, vous utiliserez quelque chose ressemblant à l'une des trois possibilités offertes par le code suivant :
if ($httpStatus === 200) { if ($outputMode === 'csv') { list($status, $accuracy, $latitude, $longitude) = explode(',', $resultGeocoder); if ($status === '200') { echo "Latitude : $latitude\n"; echo "Longitude : $longitude\n"; echo "Précision : $accuracy\n"; } } // csv else if ($outputMode === 'xml' || $outputMode === 'kml') { $xml = new SimpleXMLElement($resultGeocoder); //var_dump($xml); if ((string)$xml->Response->Status->code === '200') { foreach ($xml->Response->Placemark as $placemark) { echo "Adresse : {$placemark->address}\n"; $coordonnees = explode(',', $placemark->Point->coordinates); echo "Latitude : {$coordonnees[1]}\n"; echo "Longitude : {$coordonnees[0]}\n"; echo "Précision : {$placemark->AddressDetails['Accuracy']}\n"; } } } // xml / kml else if ($outputMode === 'json') { if (function_exists('json_decode')) { // La fonction json_decode n'est présente que depuis PHP 5.2 $data = json_decode($resultGeocoder); //var_dump($data); if ($data->Status->code === 200) { foreach ($data->Placemark as $placemark) { echo "Adresse : {$placemark->address}\n"; echo "Latitude : {$placemark->Point->coordinates[1]}\n"; echo "Longitude : {$placemark->Point->coordinates[0]}\n"; echo "Précision : {$placemark->AddressDetails->Accuracy}\n"; } } } } // json } // HTTP 200
La version 'csv' est très simple, du fait que le Geocoder ne renvoit, dans ce mode, que des données minimalistes.
Les version 'xml' et 'json' sont plus riches, et permettent d'obtenir plus de données - le choix entre l'une ou l'autre vous renvient, puisqu'elles présentent les mêmes fonctionnalités.
Quelques remarques
Pour terminer cet articles, j'aimerais juste ajouter une ou deux remarques.
Le service de Geocoding n'a pas toujours raison
Tout d'abord, pensez à vos utilisateurs : c'est pour eux que vous écrivez votre application, et, eux seuls peuvent dire si une localisation est correcte ou non.
Autrement dit, ne tenez pas pour vérité les données renvoyées par le service de Geocoding : il renvoi ce qu'il considère comme juste ; pas nécessairement ce qui l'est pour vos utilisateurs.
Vous devriez donc toujours offrir à vos utilisateur la possibilité de valider ou corriger les coordonnées correspondant à leur saisie, avant de les enregistrer et d'en tenir compte pour la suite de vos traitements.
N'effectuez pas plus de requêtes que nécessaire
Obtenir les coordonnées correspondant à une adresse revient à effectuer une requête HTTP. Cela prend du temps.
Autant que possible, donc, n'abusez pas de cette fonctionnalité : si vous en avez la possibilité, effectuez la requête auprès du service de geocoding une et une seule fois, et stockez les coordonnées en plus de l'adresse, de manière à accélérer l'affichage.
Ceci est d'autant plus important que le service de Geocoding ne vous permet pas d'effectuer un nombre illimité de requêtes.
Le prochain article nous permettra de voir quels sont les différents types de carte que Google-maps nous permet d'afficher, ainsi que les principaux contrôles que nous pouvons leur ajouter.
Navigation :
- Retour au sommaire : utilisation de l'API Google Maps.
- 1 : Afficher une carte avec Google Maps : Les premiers éléments à mettre en place pour intégrer une carte à une page Web.
- 2 : Obtenir les latitude et longitude d'un point à partir de son adresse : Utiliser le service de Geocoding proposé par Google Maps, pour "traduire" une adresse en un couple latitude/longitude.
- 3 : Types de cartes et contrôles : Les différents types de carte que le service Google Maps permet d'afficher, et les différents contrôles que vous pouvez leur ajouter.
- 4: Marqueurs et fenêtres d'informations : Afficher des marqueurs et des fenêtres d'informations sur vos cartes
MAJ le 02/03/2008 : Ajout de la partie Navigation + Lien vers la partie 3.
MAJ le 16/04/2008 : Mise à jour de la partie Navigation : ajout du lien vers la partie 4.
Pour être averti lors de la publication de nouvelles entrées, n'hésitez pas à vous abonner au flux RSS ou ATOM des articles de mon blog !










Commentaires
Bonjour
Vous pouvez visiter pour plus d'informations sur l'utilisation des GoogleMaps.
Amicalement
Salut,
article très complet et très clair, félicitations !
Je me posais la question pour une appli que je réalise actuellement s'il était possible de récupérer ville, region, dept et pays à partir d'une requête de géolocalisation mais en partant des coordonnées gps (lat, lng) ?
Grosso modo j'aimerai récupérai le nom de la ville la plus proche d'un point ajouté sur une carte pour pouvoir alimenter une base de données (ville, département, région, pays), en fait j'ai déjà cette base rempli avec tt ce qui concerne la France mais mon appli ne sera pas limité à ce pays.
Donc j'aimerai remplir ma base au fur et à mesure des points ajoutés via mes utilisateurs ...
si tu as une piste ou la connaissance d'un autre service de geolocalisation autre que google qui me permettrait de faire cette opération inverse je t'en remercie par avance ...
Tatane
Merci
Au risque de te décevoir, je ne me rappelle pas être jamais tombé sur cette fonctionnalité ; de manière générale, c'est plus souvent l'inverse que l'on rencontre : l'utilisateur saisi son adresse, on lui affiche la carte (via un coup de GeoCoding), et il peut affiner en plaçant un marqueur à l'endroit précis qu'il l'intéresse.
Alors que là, l'inverse... Je n'ai pas souvenir que l'API Google Maps fournisse cette fonctionnalité, en tout cas
Pourtant, j'imagine que Google a ce qu'il faut pour fournir cette fonctionnalité : elle semble inclue à la nouvelle version de Gears.
Cf : http://code.google.com/apis/gears/api_geolocation.html#address
Je n'ai eu le temps de tester à fond, mais à première vue, c'est pas si mal ; avec la portion de code suivante, il me sort que je suis à Lyon - ce qui est correct (il se trompe sur l'arrondissement, par contre ^^ ) :
var geo = google.gears.factory.create('beta.geolocation');Ce qui donne dans firebug, une fois déplié :function updatePosition(position) {
console.log(position);
}
function handleError(positionError) {
alert('Attempt to get location failed: ' + positionError.message);
}
geo.getCurrentPosition(updatePosition, handleError, {
enableHighAccuracy: true,
gearsRequestAddress: true,
gearsAddressLanguage: 'fr-FR'
});
Mais, comme je disais plus haut, ça passe par Gears... Avec la multitude de contraintes que cela implique... Contraintes qui font que ça ne répond pas à ton besoin de site "grand public".
Avec un peu de chance, qui sait, on retrouvera peut-être bientôt cette fonctionnalité dans l'API Maps !
(ou alors, elle y est déjà, et je ne l'ai pas vu en survolant la doc ^^ )
N'hésitez pas à re-poster pour nous tenir au courant du résultat de vos recherches : ça en intéresse sûrement d'autres !
(dont moi un jour futur, j'imagine
ok merci bcp pour la réponse, effectivement dans ce sens là c'est rare comme requete ...
j'ai lu un de tes articles sur google gear et effectivement ça risque de ne pas être enviseagable pour mon site ...
bah je trouverai bien une solution, pour le moment j'ai pris le pb à l'envers et je cherche des bases de données ou listing d'au moins pays/regions/dept, y'a qqs ressources intéressantes genre çà : http://164.214.2.53/gns/html/cntry_...
à suivre ...
merci pour le coup de pouce
Bonne continuation !
Et merci pour le lien ; j'ai bookmarqué, ça peut toujours servir ^^
Quoi qu'il en soit, j'ai testé l'API de Google Maps et il y a de gros soucis pour récupérer des données via XML car tantôt on a le tag tantôt le tag (cas d'une adresse belge), bref je n'ose imaginer aux autres cas de figure où c'est encore autre chose que ces deux termes...
Bonjour à tous, super tuto, je ne connaissais pas l'aspect serveur de l'api, merci pour le tuyau.
En rapport avec la question de Tatane, je vous conseille de faire un tour sur www.geonames.org qui propose des web services et autres friandises.
Google maps + geonames donne des résultats trés intéressants.
La bonne journée
Fred
Parfait c'est exactement ce qui me manquais ! clair et efficace, merci bcp !
Salut
C'est super comme aricle. Merci.
J'avais une question : J'ai une liste de points définis par la latitude et longitude comment savoir si tous mes points sont bien localisés dans un département (en d'autres termes comment repérés les points érronés ? )
Existe il une base de données des longitudes latitude min max par département ou quelque chose comme ça ?
Ou peut être une fonction de l'api google maps qui permet d'obtenir cette information (d'où ma question sur ce forum!)
Je ne peux pas verifier ça "manuellement" car j'ai des dizaines de milliers de points par département
Bonsoir,
Le plus simple que j'ai vu mis en place pour ce genre de cas (mais pas forcément le plus efficace ni le plus exact, c'est l'inconvénient de la simplicité) était au niveau de la vérification de validité de couples latitude/longitude pour des communes que je savais être dans un département, mais dont je n'étais pas sûr des latitudes et longitudes que j'avais à ma disposition, en particulier à cause de communes de même nom :
Mais cette méthode colle pour le cas où on sait que les communes sont dans un département, et où la question qui se pose est celle de l'exactitude des coordonnées que l'on a en base...
Après, je pense que cette méthode peut déjà vous permettre de dégrossir le travail : si vous avez un point situé à 300 km du centre de votre département, c'est fort probablement qu'il n'en fait pas parti ; même chose pour 100 km, d'ailleurs...
Mais le résultat sera approximatif... Ce qui signifie que si vous ne voulez pas avoir trop de faux positifs, il vous faudra garder un rayon de département suffisamment grand... Et plus il sera grand, plus vous aurez de faux négatifs... A vous de trouver le juste milieu, donc ^^
Pour une méthode qui ait une exactitude de 100%, je n'ai pas d'idée, désolé.
Note : le coefficient à appliquer n'est pas aussi simple que ça : ça dépend de la position de vos villes sur Terre, pour prendre en compte sa forme et tout ça... => google pour plus d'informations ^^
Vous tomberez par exemple sur http://www.movable-type.co.uk/scripts/latlong.html
Et pour un exemple, voyons voir ce qu'on pourrait avoir en Isère :
=> Le "centre" de l'Isère a pour coordonnées
45.3205410487805 ; 5.49489386866792Et pour les comparaisons, par exemple :
OK, ça marche dans ce cas qui va bien ; on peut sans aucun doute trouver des cas trop justes, soit trop loin soit pas assez, qui génèreront des faux positifs ou des faux négatifs (d'autant plus que l'Isère est un peu en longueur)...
Mais ça permet déjà de faire une première passe de "ménage", qui enlèvera avec un peu de chance le plus gros...
Quoi qu'il en soit, bon courage !
Bonjour,
Je trouve votre tuto très bien et je vous remercies car il est très explicatif. J'ai un pb que je n'arrive pas à résoudre. Il s'agit d'afficher au chargement un très grand nombre d'adresse. J'ai pensé qu'il me fallait convertir toutes ces adresses en coordonnées puis appliquer addGeoPoint, mais je n'arrive pas à trouver comment recuperer précisement la lat et la long (d'une addresse donnée) dans des variables séparement. SVP, pourriez vous m'aider?
Bonjour,
Je ne suis pas certain de comprendre votre question, mais si vous avez un grand nombre de point à afficher, et que, pour chaque point, vous ne connaissez que son adresse, vous allez probablement faire appel au service de GeoCoding un grand nombre de fois... A chaque affichage de page ?
Si oui, votre page va mettre une éternité à s'afficher (et vous vous ferez détester par Google ^^ ) ; à mon avis, il vaudrait mieux stocker, pour chaque point, en plus de son adresse, ses coordonnées (lat/lng), en faisant appel au service de Geocoding une fois pour toutes, à l'enregistrement du point, plutôt qu'à son affichage.
Deux avantages :
Quant à obtenir la latitude et la longitude "dans des variables séparées"... J'ai du mal à voir ce que vous voulez dire : pour chaque adresse, il vous faut effectuer un appel au service de Geocoding, en utilisant la classe
google.maps.ClientGeocoderet sa méthodegetLatLng, la fonction de callback passée en second paramètre recevra alors les informations des coordonnées correspondant à l'adresse recherchée, avec, dans ces informations, la latitude et la longitude ?bonjour Pascal,
Je n'ai pu vous répondre depuis la dernière fois car j'ai eu un problème avec ma connexion internet qui vient seulement de se rétablir.
Vous avez tout a fait raison quant à la méthode à employer. Je vais essayer d'être plus précis pour mon problème.
Je voudrais transformer un tableau contenant plusieurs adresses en un tableau contenant les geocoordonnées correspondants. Par exemple, j'ai le tableau addresses qui est de la forme suivante
et je voudrais obtenir le tableau geopoints de la forme suivante
et je n'arrive pas à mettre en place un code javascript qui me fait le travail.
Voici ce que j'avais pensé faire:
function conversion(){var geopoints = new array;
for (i=0; i< addresses.length;i++){
if (geocoder){
geocoder.getLatLng(addressses[i][0],function(point) {
if (!point){
alert("Location not found:" + adresses[i][0]);
}else {
marker= new GMarker(center);
var tpoint= marker.getPoint();
geopoints[i][0]=tpoint.lat();
geopoints[i][1]=tpoint.lng();
geopoints[i][2]=addresses[i][1];
geopoints[i][3]=addresses[i][2];
}
} )
}
}
return geopoints
}
puis ensuite appeler la fonction conversion:
, puis ensuite appliquer la fonction
showGeopoints(), mais aucun points ne s'affiche, il n'y a que le map. J'ai vraiment besoin de votre soutient car je dois livrer le client très bientot.Merci d'avance.Bonjour,
(je fais un peu rapide)
Une chose est à garder à l'esprit quand on utilise le geocoder (je ne sais pas si votre problème vient de là, mais ça se pourrait) : il travaille en asynchrone : vous lancez une requête, mais vous ne savez quand le résultat de cette requête arrivera -- et il n'y a que dans la fonction de callback passée en second paramètre à
getLatLngque vous le savez.Typiquement, le code se trouvant dans le source JS après l'appel à
getLatLngsera généralement exécuté avant que les coordonnées n'aient effectivement été obtenues !Dans votre exemple, ça signifie que au moment où le
return geopointss'exécute, de manière générale, aucune coordonnée n'aura encore été obtenue : les appels seront seulement en train de s'exécuter, en arrière-plan, de manière asynchrone.En somme, c'est dans la fonction de callback qu'il faudrait manipuler les coordonnées, et non en dehors.
Par exemple, avec quelque chose de ce type :
En espérant que cela vous aide à avancer,
Bon courage !
J'ai perdu un temps monstre à trouver les coordonnées d'une adresse (latitude/longitude ville)
Je conseille donc ce site à ceux que ça intéresse:
http://www.alsasys.com/web-agency/o...
Amicalement
Merci Pascal pour tes explications et aussi pour la rapidité de tes réponses, je vais essayer d'exploiter ca tout de suite.
Merci aussi à lod. Je pense que cela me sera aussi très utile.
Amicalement
lod > ça ira plus vite que de pondre un bout de code JS à chaque fois ^^ (et probablement plus vite que de passer par maps.google.com et de récupérer la latitude et la longitude dans le lien qu'on peut obtenir depuis la carte ^^ )
momo72 > pas de problème
Bon courage !
Bonjour,
Je commencerais d'abord par vous souhaiter une bonne et heureuse année 2009, ainsi qu'a tout les internautes qui consulte ce forum.
Je voudrais mettre en place un script qui permet à l'internaute d'avoir l'ensemble des magasins à partir d'une adresse de son choix et dans un rayon de son choix. Je me suis inspiré du tuto qui est à cette adresse:
http://code.google.com/support/bin/...
Il y a donc deux fichiers: l'un connecté à la base de données (phpsqlsearchgen.php) et l'autre connecté à google pour le geacodage (nxgooglemapsapi.php).
J'ai suivi scrupuleusement le tuto mais cela ne fonctionne pas.Après plusieurs tests et contrôles je pense que le pb vient des lignes contenant GDownload() ou parse() du fichier nxgooglemapsapi.php. Voici mes deux fichiers:
nxgooglemapsapi.php
@@<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://
www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Stote Locator</title> <script src="[http://maps.google.com/maps?file=api&v=2&key=ABQIAAAASxOW0sFRTtwHqMg4ukQLLhTHs9lI5o6Df0HCZVlYC_EU9t1n4hTvthF3c2nVcAxAbeABdUWC2QR7Zw]" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ var map; var geocoder;function load() { if (GBrowserIsCompatible()) { geocoder = new GClientGeocoder(); map = new GMap2(document.getElementById('map')); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl()); map.setCenter(new GLatLng(25, 1), 2); } }function searchLocations() { var address = document.getElementById('addressInput').value; geocoder.getLatLng(address, function(latlng) { if (!latlng) { alert(address + ' not found'); } else { searchLocationsNear(latlng); } }); }function searchLocationsNear(center) { var radius = document.getElementById('radiusSelect').value; var searchUrl = 'phpsqlsearchgen.php?lat=' + center.lat() + '&lng=' + center.lng() + '&radius=' + radius; __GDownloadUrl(searchUrl, function(data) { var xml = GXml.parse(data);__ ____ var markers = xml.documentElement.getElementsByTagName('marker');map.clearOverlays();
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute('name');var address = markersi.getAttribute('address');
var point = new GLatLng(parseFloat(markers[i].getAttribute('lat')), parseFloat(markers[i].getAttribute('lng'))); var marker = createMarker(point, name, address); map.addOverlay(marker); bounds.extend(point); } map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds)); }); } function createMarker(point, name, address) { var marker = new GMarker(point); var html = '<b>' + name + '</b> <br/>' + address; GEvent.addListener(marker, 'click', function() { marker.openInfoWindowHtml(html); }); return marker; } //]]> </script> </head><body onLoad="load()" onUnload="GUnload()"> Address: <input type="text" id="addressInput"/> Radius: <select id="radiusSelect"> <option value="25" selected>25</option> <option value="100">100</option> <option value="200">200</option> </select> <input type="button" onClick="searchLocations()" value="Search Locations"/> <br/> <br/><div style="width:950px; font-family:Arial,
sans-serif; font-size:11px; border:1px solid black">
<table align="center"> <tbody> <tr> <td align="center"> <div id="map" style="overflow: hidden; width:940px; height:500px"></div> </td> </tr> </tbody> </table></div>
</html>
@@
et phpsqlsearchgen.php
@@<?php
header("Content-type: text/xml");
require("phpsqlsearch_dbinfo.php");
function parseToXML($htmlStr)
{
$xmlStr=str_replace('<','<',$htmlStr);
$xmlStr=str_replace('>','>',$xmlStr);
$xmlStr=str_replace('"','"',$xmlStr);
$xmlStr=str_replace("'",''',$xmlStr);
$xmlStr=str_replace("&",'&',$xmlStr);
return $xmlStr;
}
// Get parameters from URL. les variables globales ont été remplies par l'internaute.
$center_lat = $_GET
Veulliez m'excuser,le deuxième fichier n'était pas complet. Mon application se trouve à l'adresse
http://gbsolutionsinc.net/ProjetMap...
Mon test consiste à rentrer l'adresse
6 allée de Luynes 37000 tours, avec le rayon 25 e je devrais en retour avoir 4 adresses avec marqueurs sur la carte, ce qui n'est pas le cas.
Peut etre pourriez vous me donner un coup de pouce, pour voir ce qui ne vas pas. Je pense que le pb se trouve au niveau de GDownload ou GXml.parse()
Merci d'avance.
phpsqlsearchgen.php
<?php
header("Content-type: text/xml");
require("phpsqlsearch_dbinfo.php");
function parseToXML($htmlStr)
{
$xmlStr=str_replace('<','<',$htmlStr);
$xmlStr=str_replace('>','>',$xmlStr);
$xmlStr=str_replace('"','"',$xmlStr);
$xmlStr=str_replace("'",''',$xmlStr);
$xmlStr=str_replace("&",'&',$xmlStr);
return $xmlStr;
}
// Get parameters from URL. les variables globales ont été remplies par l'internaute.
$center_lat = $_GET("lat");//j'ai mis des parenthèse car
$center_lng = $_GET("lng");// je n'arrive pas à vous
$radius = $_GET("radius"); // l'envoyer avec les crochets
// Opens a connection to a MySQL server
$connection=mysql_connect ($server, $username, $password);
if (!$connection) {
die('Not connected : ' . mysql_error());}
// Set the active MySQL database
$db_selected = mysql_select_db($database, $connection);
if (!$db_selected) {
die ('Can\'t use db : ' . mysql_error());}
// Select all the rows in suppliers_details table
$query = sprintf("SELECT suppliers_address , suppliers_latitude , suppliers_longitude , suppliers_city , ( 3959 * acos( cos( radians('%s') ) * cos( radians( suppliers_latitude ) ) * cos( radians( suppliers_longitude ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( suppliers_latitude ) ) ) ) AS distance FROM suppliers_details HAVING distance < '%s' ORDER BY distance LIMIT 0 , 20",
$result = mysql_query($query);
if (!$result) {
die('Invalid query: ' . mysql_error());}
// Start XML file, echo parent node
echo "<markers >\n";
while ($row = @mysql_fetch_assoc($result)){
echo "</markers> \n";
}
// End XML file
?>
Peut etre pourriez vous me donner un coup de pouce, pour voir ce qui ne vas pas. Je pense que le pb se trouve au niveau de GDownload ou GXml.parse()
Merci d'avance.
Momo
Bonjour,
Excellent tuto présentant des fonctionnalités que je ne pensais même pas.
De mon côté, j'ai mis en place sur plusieurs site des Google Maps par contre un problème se pose toujours: certaines adresses ne sont pas trouvées par le geolocaliseur mais trouvé via Google Map.
Avez-vous déjà rencontré ce problème?
Merci.
Note: j'ai déjà vérifié les accents et autres caractères pouvant poser problème.
Pour ceux qui passerait par là, le problème venait du fait que j'envoyais trop rapidement les requête à Google. Il est nécessaire d'attendre qq milli-secondes (100ms apparement) entre chaque requête.
Ah ouais !
Moi dès fois, j'ai des erreurs js aléatoire, du style a is null, peut être du à ça ?
Comment as tu fait pour temporiséer entre chaque ?
Sinsey > Voilà ma fonction. Je l'ai trouvé dans la doc de Google et je l'ai un peu arrangé
function getGeocode($adresse){
// Initialize delay in geocode speed
$delay = $lat = $lng = $alt = 0;
$base_url = "http://maps.google.com/maps/geo?out...";
$geocode_pending = true;
while ($geocode_pending) {
$request_url = $base_url . "&q=" . urlencode($adresse);
// Exécution de la requète et récupération du status
$xml = simplexml_load_file($request_url) or die("url not loading");
$status = $xml->Response->Status->code;
if (strcmp($status, "200") == 0) {
// Geocode trouvé
$geocode_pending = false;
$coordinates = $xml->Response->Placemark->Point->coordinates;
$coordinatesSplit = split(",", $coordinates);
// Format: Longitude, Latitude, Altitude
$alt = $coordinatesSplit(crochet ouvrant)2(crochet fermant);
$lat = $coordinatesSplit(crochet ouvrant)1(crochet fermant);
$lng = $coordinatesSplit(crochet ouvrant)0(crochet fermant);
} else if (strcmp($status, "620") == 0) {
// Demande trop rapide, refus de Google
$delay += 100000;
} else {
// Geocode introuveable
$geocode_pending = false;
//echo "Address " . $address . " failed to geocoded. ";
//echo "Received status " . $status . "<br>";
}
usleep($delay);
}
$geocode = array();
$geocode(crochet ouvrant)'latitude'(crochet fermant) = $lat;
$geocode(crochet ouvrant)'longitude'(crochet fermant) = $lng;
$geocode(crochet ouvrant)'altitude'(crochet fermant) = $alt;
$geocode(crochet ouvrant)'status'(crochet fermant) = $status;
return($geocode);
}
NOTE : les crochets ne marchaient pas, j'ai du feinter, à remplacer donc ...
merci pour tes explications! ca m'a vraiment beaucoup aidee!
et merci pour ceux qui ont laisser des commentaires instructif!
bonne continuation!
(je vais lire tes autres topic, tu m'inspire!!^^)
Depuis l'autre coté du pont wilson, je te remercie grandement pour ce tuto de qualité qui m'a appris à utiliser l'API google.
A bientôt sur ton blog
A +
salut all
tout d'abord et comme tout les autres un grand merci pour ce tuto acessible et sympa
cependant je n'ai pas trouver tout mon bonheur
je suis a la recherche d'une méthode pour centrer au mieux ma carte avec les coordonnées de plusieurs points
pour un seul point je centre sur ce dernier
mais dans le cas de plusieurs ...comment être sur de centrer sur tout les points ?
mis a part choisir les coordonnées les plus éloignées et les définir comme étant les coordonnées du cadre je ne vois rien d'autre.
merci d'avance si vous avez connaissance d'une fonction permettant de faire un zoom sur plusieurs point
j'espère avoir été assez clair a+
Tafou >Merci pour la fonction, désolé de ma réponse tardive, mais je n'ai pas été informé de ton post.
En fait, ici tu utilise la méthode XML, moi j'utilise le geocoder en javascript.
Y'a-t-il un équivalent pour le delay. voici ma fonction qui est donc envoyé pour chaque point à placer
geocoder.getLatLng("<?=$nomville.",".$departement?>", function (coord) {<? // Géocodage : Création du point correspondant aux coordonnées nous intéressant?>
alert(coord);
var marker = createMarker(coord,"<?=$labelicon?>"); <? chr(13);// Création d'un marqueur localisé sur ce point?>
map.addOverlay(marker); <? chr(13);// Ajout du marqueur à la carte?>
}); //End geocoder.getLatLng
Et la fonction qui créer le marqueur
@@var createMarker = function (coord, name) {
//Icon
var icon = new GIcon(G_DEFAULT_ICON);
icon.image="http://labs.google.com/ridefinder/i...";
icon.shadow = "http://labs.google.com/ridefinder/i...";
icon.iconSize = new GSize(12, 20);
icon.shadowSize = new GSize(22, 20);
//Marqueur
var marker = new GMarker(coord, icon);
var html = '<b>' + name + '</b>';
GEvent.addListener(marker, 'click',
function() {
marker.openInfoWindowHtml(html);
}
);
return marker;
}
Utilisation du Geocoder :
vous dites :
Ceci est d'autant plus important que le service de Geocoding ne vous permet pas d'effectuer un nombre illimité de requêtes.
J'ai fait une boucle pour récupérer mes marker et les afficher avec le Géocoder, et effectivement, il semble qu'au bout de quelques markers, il ne trouve pas le suivant.
Il m'a semblé aussi, que si je ne l'utilise pas pendant un certain temp, le même programme affiche bien tous les marker.
Est ce cela la limitation du Géocoder ?
Merci pour une réponse.
Pierre.
Bonjour,
déjà merci pour le tuto
en fait, j'ai le même souci que "Popote".
j'aimerais pouvoir centrer ma carte avec les coordonnées de plusieurs points.
Est-ce que quelqu'un aurait une solution?
Merci d'avance
Lio