[PHP/SQL] Géolocalisation et distance entre des villes

J'ai besoin pour un développement spécifique mais aussi pour Agenda Culturel de pouvoir lister dans un script PHP les villes à proximité d'une ville donnée. Concrètement, cela signifie afficher les villes situées à l'intérieur d'un périmètre d'une distance de X kilomètres de la ville de référence.

J'ai longuement cherché sur le web, et je n'ai trouvé que peu d'infos à ce sujet. Je vous livre ici le résultat de mes recherches et de mes multiples tests, de l'intégration des coordonnées de latitude/longitude à l'aide des API de géolocalisation google map et yahoo map jusqu'à la requête sql finale qui inclue la formule de calcul de distance à partir des coordonnées géolocalisées.

geolocalisation-proximite-php-sql

Une base de donnée fiable des villes associées à leurs coordonnées de latitude/longitude

En premier lieu, il s'agit d'avoir une base de données de villes. Il en existe de nombreuses sur le net, celle-ci intègre même les coordonnées de latitude et de longitude.

Dans mon cas, je possédais déjà une base de données de villes françaises mais sans ces coordonnées. La meilleure méthode pour y intégrer la latitude et la longitude de chaque ville est d'utiliser l'API de Yahoo ou de google.

Ces deux API permettent de récupérer un flux XML correspondant à la recherche (ex : Amiens, France) contenant la latitude et la longitude.

Par exemple, voici le résultat de la recherche de la ville d'Amiens avec l'API de Yahoo :

<ResultSet xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="zip">
<Latitude>49.896309</Latitude>
<Longitude>2.279720</Longitude>
<City>80000 Amiens</City>
<State>France</State>
<Country>FR</Country>
</Result>
</ResultSet>

Il n'y a donc plus qu'à faire une boucle sql sur la table des villes en parsant le flux de yahoo ou google correspondant (en indiquant la ville, le code postal et le pays) et intégrer à l'enregistrement de la ville la latitude et la longitude. Un vrai jeu d'enfant avec simplexml en php5 ! Cependant, même si ces API sont puissantes, il se peut que parfois la ville ne soit pas trouvée, que la ville retournée ne soit pas la bonne ou que plusieurs résultats soient retournés. Pour plus d'informations, et pour choisir entre les API de Yahoo et de Google, je vous invite à lire cet article sur la géolocalisation via yahoo et google map très bien fait et qui m'a bien aidé.

Vous avez à présent une table contenant les villes associées à leurs coordonnées géographique de latitude et de longitude.


Il ne reste plus qu'à exploiter cela dans une requête SQL et afficher le tout en PHP.

J'ai d'abord tenté une conversion des données latitude/longitude en points de coordonnées lambert 2 étendue (norme française) grâce à la fonction geos2lambert() disponible sur google code mais son exploitation s'est révélé compliquée et imprécise (ou alors c'est moi qui suis vraiment nul en maths !).

Je suis donc reparti sur les données de latitude/longitude. Grâce à une formule mathématique complexe trouvée sur le net je suis arrivé au résultat suivant.

Je reçois en variable GET les coordonnées de ma ville de référence :

$latitude=$_GET[lat];
$longitude=$_GET[lon];

La fameuse formule utilisant les colonnes lat et lon de ma table sql et les données de latitude et longitude de ma ville de référence :

$formule="(6366*acos(cos(radians($latitude))*cos(radians(`lat`))*cos(radians(`lon`) -radians($longitude))+sin(radians($latitude))*sin(radians(`lat`))))";

Et la requête SQL prête à être exécutée :

$sql="SELECT ville,$formule AS dist FROM ville WHERE $formule<='$_GET[distance]' ORDER by dist ASC";

  • le SELECT $formule AS dist permet de classer les villes de la plus proche à la plus éloignée avec ORDER by dist ASC
  • le WHERE $formule<='$_GET[distance]' permet de définir un rayon en km ($_GET[distance]) à partir de la ville de référence

Je vous laisse coder l'éxécution de la requête sql et l'affichage des données en php, ce n'est pas vraiment le plus dur !

En guise de conclusion...

Il faut savoir que ce calcul et le résultat ne sont pas précis pour plusieurs raisons :

  • Le calcul ne tient pas compte de l'altitude, qui, dans certains endroits peut influer sur les distances
  • La formule a été simplifiée au maximum par souci de simplicité et d'efficacité.
  • La distance est "à vol d'oiseau" et non pas une distance routière
  • La localisation latitude/longitude d'une ville repose sur un point (généralement la mairie de la ville en question), donc la distance ne commence pas à la sortie de la ville mais du centre de la ville. Ainsi, par exemple, pour Paris cela ne sert à rien de vouloir localiser les villes à 5km...

Cependant, malgré l'imprécision cela fonctionne, car l'intérêt premier est d'afficher les villes proches et non de concurrencer google map ! Il convient juste de ne pas forcément afficher les distances en km qui sont souvent fausses (à quelques kilomètres près).

L'utilisation de cette technique qui est finalement assez simple à mettre en place peut être très utile sur du web local afin d'élargir les recherches sur une ville aux villes à proximité (dans le cas où aucune réponse n'est disponible pour la ville concernée par exemple) ou proposer d'autres villes ou contenus (ou publicités géolocalisées !) à l'internautes en rapport avec sa requête.

Si vous avez des questions sur la mise en place d'une recherche de proximité ou si cet article vous a aidé, n'hésitez pas, les commentaires sont fait pour ça !

Newsletter

Recevez nos dernières actualités, offres et conseils en vous inscrivant à notre newsletter.

Commentaires (56)

charles
  • 1. charles | Jeu 05 Nov 2015

Impeccable ! Merci beaucoup.

Dimitri
  • 2. Dimitri (site web) | Sam 04 Avr 2015

$sql="SELECT ville,$formule AS dist FROM ville WHERE $formule<='$_GET[distance]' ORDER by dist ASC";

Attention aux injections sql...

sonia

Ca fait plaisir de trouver des outils qui fonctionne! merci !

george
  • 4. george (site web) | Mar 06 Mai 2014

Salut, merci pour cette mise a jour ;), j ai deja dev une appli qui permet une geolocalisation gratuite de portable. Je vais ouvrir mon champs d expertise grace a toi!

Vince
  • 5. Vince | Ven 04 Avr 2014

Top ! Merci bcp, j'ai gagné un peu pres 2 ans de dév :))

tom_over
  • 6. tom_over | Mar 21 Mai 2013

Bonjour,
J'utilise depuis 2 ans votre requête sql pour définir périmètre sur googlemap et je rencontre un problème depuis quelques mois. Certainement suite à une mise à jour de SQL :

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))*cos(radians(`cm_membergooglemaps_lat`))*cos(radians(`cm_membergooglemaps_lng`' at line 1

Avez-vous rencontré le même problème ? Avez-vous une astuce pour contourner le problème ?
Merci pour votre aide

oam
  • 7. oam | Jeu 16 Mai 2013

6366 = rayon sphère terrestre

Beta test
  • 8. Beta test | Lun 04 Mars 2013

pour les distances routiere, il est possible de le faire via l'api de google mais cela deviens compliqué et lourd a gérer.
Voici l'astuce.
tu fais la recherche de ta ville (la meme que celle ici plus haut par exemeple.) on peut conciderer que chaque ville est située a un parcour plus long par la route qu'a vole d'oiseau. il te reste a faire (apres avoir recu la reponse de ton calcul) un passage de chaque ville/village dans l'appli googlemaps pour savoir la distance entre ces 2 points tu pourra meme proposer a tes internautes la distance a pied ou en bus.

Mais attention, comme dis plus haut c'est lourd. A la campagne ca va encore mais une recherche sur les grandes villes prend des plombes

Nicolas
  • 9. Nicolas | Sam 29 Déc 2012

Ouaah !!
Un grand merci pour ce tuto avec en plus des explications très claires.
Super !

Webbax
  • 10. Webbax (site web) | Jeu 13 Déc 2012

Hello,

Un très grand merci pour cette formule, je viens de tester ça sur mon projet... ça fonctionne nickel. On dira ce qu'on veut... mais sans formule... ben on est mal pris ;)

Bonne continuation !

wiloooo
  • 11. wiloooo (site web) | Jeu 25 Oct 2012

Je crois qu'il y a une erreur dans la démonstration :-/ non ?

Prdo
  • 12. Prdo | Jeu 04 Oct 2012

Bonjour,
Depuis plusieurs jours je recherche un système de ce genre mais plus complet. Je voudrais que les distance soit prise à échelle routière.
Exemple : J'envoie Amiens, j'ai toutes les villes se situant à 10 kilomètre d'Amiens par voie routière et non d'oiseau qui sorte.
Quelqu'un aurait quelque chose qui va dans ce sens ?
Je vous remercie

evets
  • 13. evets | Dim 30 Sept 2012

salut
Moi je comprends pas... Cela fonctionne en local mais pas sur mon site distant ? :-(

tom_over
  • 14. tom_over | Mar 18 Sept 2012

Bonjour,

Est-il possible d'afficher la distance qui nous sépare ?

Car la variable 'dist' est virtuel donc introuvable dans la base de données...

Si quelqu'un a une solution à me proposer car je bloque.. :( Merciii

PizzaToy
  • 15. PizzaToy (site web) | Mar 28 Août 2012

Salut,
c'est la méthode que j'utilise depuis 2009. Aucun problème !

ALain
  • 16. ALain | Lun 23 Juil 2012

Bonjour,

Hélas, le lien vers le résultat n'existe plus. Quelqu'un en aurait une copie ?

Et que signifie le 6366 ?

thom
  • 17. thom | Mer 09 Mai 2012

Merci pour ce billet et cette fameuse formule, a ce sujet que représente la valeur 6366 ?

Antoine
  • 18. Antoine (site web) | Sam 28 Avr 2012

Un grand MERCI! Ca fait plaisir des explications comme celle-ci :-)

momo
  • 19. momo | Dim 18 Mars 2012

Bonjour,

J'ai un fichier avec une liste de 700 000 clients dont j'ai les coordonnées de latitude et de longitude et un autre fichier avec une liste de 40 clubs dont j'ai aussi les coordonnées de longitude et latitude.

Or mon but est de choisir pour chaque client le magasin le plus près, donc celui dont la distance est minimale.

Or j'ai essayé de faire ça sur excel : Feuil1 = liste des clients, Feuil2= liste des clubs, en appliquant la formule de calcul des distances sur la Feuil1, puis en tirant cette formule sur 40 colonnes, mais lorsque cela tire pour les 700 000 clients, excel bug systématiquement.

Mon but est de faire la même chose sur Access mais je sais pas comment faire. En effet, sur Access qui est plus adapté aux gros volumes de tables ça ne plantera pas.

Merci de votre aide

franck
  • 20. franck (site web) | Ven 20 Jan 2012

Dav ,tuas effectivement une erreur dans ton code au niveau de la requette

"$ville_base = mysql_query(« SELECT nomville,$formule AS dist FROM table_communes WHERE $formule mysql_query(« SELECT
par => mysql_query("SELECT

le reste semble marcher si:

Les champ de ta table correspondent à:
contenant la latitude=> latitude
contenant la longitude=> longitude


et si le nom de ta table est bien => table_communes

Chris
  • 21. Chris | Lun 09 Jan 2012

Pour ceux qui aurais comme moi un soucis ... ( dav ) !!

pour moi comme ca, ca marche sinon 'lon' et 'lat' valent '0' et la dist = 5111.5757527 !!!

$formule="(6366*acos(cos(radians($latitude))*cos(radians(lat))*cos(radians(lon) -radians($longitude))+sin(radians($latitude))*sin(radians(lat))))";

$query="SELECT nom,lat AS lat, lon AS lon, $formule AS dist FROM villes WHERE $formule<='3' ORDER by dist ASC";

theptitprince
  • 22. theptitprince | Mar 03 Jan 2012

Re-bonjour...
Il semblerait simplement que les coordonnées soient erronées ;)

theptitprince
  • 23. theptitprince | Mar 03 Jan 2012

Bonjour.
J'ai une petite erreur étrange... Quand on fait une recherche par frejus... Et ba SQL veut plus rien savoir!

Tony Samson
  • 24. Tony Samson (site web) | Ven 16 Déc 2011

@loko : tout simplement dans le cas où (comme dans mon cas à l'époque) vous avez déjà une base de donnée de villes qui est utilisée. Dans le cas d'un nouveau site, c'est clair, c'ets plus simple d'utiliser une base de villes existante.

Loolou
  • 25. Loolou | Ven 16 Déc 2011

Merci mille fois, j'avais justement besoin d'une telle formule pour un projet perso.
Merci.

loko
  • 26. loko | Jeu 15 Déc 2011

interessant mais je comprends pas:
pourquoi se fatiguer à faire appel à l'api de yahoo ou de google pour collecter les latitude/longitude puisque le lien http://www.galichon.com/codesgeo fournit déjà une base de donnees (à jour -2011: et depuis la position des villes n'a pas bcp changé...) avec ces infos. ou alors c'est qu'elles ne sont pas fiables ??

dav
  • 27. dav | Mer 30 Nov 2011

Bonjour,

j'ai testé le code mais je crois que j'ai une erreur. je n'arrive pas à afficher les données voici mon code :


$latitude='49.89444351196299';
$longitude='2.2933330535889005';

$formule="(6366*acos(cos(radians($latitude))*cos(radians('latitude'))*cos(radians('longitude') -radians($longitude))+sin(radians($latitude))*sin(radians('latitude'))))";



$ville_base = mysql_query("SELECT nomville,$formule AS dist FROM table_communes WHERE $formule<='50' ORDER by dist ASC ");
while($row = mysql_fetch_array($ville_base))
{
$nomville= $row['nomville'];


echo $nomville.'';

}

Merci de votre aide

Kalagan
  • 28. Kalagan (site web) | Mar 11 Oct 2011

Merci pour la formule et pour la requête SQL, c'est exactement ce que je cherchais :)

azzabi
  • 29. azzabi | Jeu 06 Oct 2011

merci c vraiment interessant

Mansion JC
  • 30. Mansion JC | Mer 15 Juin 2011

Bonjour

Très interessant ce tuto.
Si je liste les commerces situés dans un rayon de 10 kms autour de A, comment faire pour calculer la distance de chaque commerce par rapport à A comme l'a fait PizzaToy
sur son site ?
Merci pour votre réponse

Web Apps
  • 31. Web Apps (site web) | Ven 15 Avr 2011

Un petit souci cependant... l'API de Google Maps limite le nombre de requêtes par jour.

Tony Samson
  • 32. Tony Samson (site web) | Mar 22 Mars 2011

@guillaume : La distance renvoyée est en kilomètres.

Didier
  • 33. Didier (site web) | Mer 02 Mars 2011

Simple, efficace...Que de temps gagné !
Merci

Veebee
  • 34. Veebee | Ven 11 Fév 2011

Bonjour et merci pour cet article clair et très utile

Guillaume
  • 35. Guillaume | Dim 06 Fév 2011

Merci pour ton article, je cherche a faire exactement la meme chose.
Par contre, dans ta formule, la distance renvoyée entre les deux points GPS latitude et longitude est en quelle unité ??
Merci d'avance.

AkaiKen
  • 36. AkaiKen | Mar 01 Fév 2011

Bonjour,
j'ai utilisé votre article pour faire afficher des commerces dans un rayon de 'tant de km', exactement comme vous l'avez posté, et ça marche très bien, merci :)

J'ai cependant une question : est-ce possible, connaissant un point (latitude et longitude), la longitude d'un deuxième point, et la distance entre les deux, de sortir la latitude du second point ? (idem pour sa longitude si l'on connait sa latitude)
J'ai essayé de sortir ces infos de la formule que vous proposiez, mais je tombe sur une hideuse suite de racines carrées de 1 + cos(truc) au carré, et je crains que le serveur ne prenne trop de temps pour calculer ça.
Auriez-vous une idée ?
Merci d'avance !

Tony Samson
  • 37. Tony Samson (site web) | Ven 07 Jan 2011

La colonne "dist" n'existe pas dans la table. Dans la requete cela permet de classer les villes de la plus proche à la plus éloignée avec ORDER by dist ASC

tom_over
  • 38. tom_over | Mer 29 Déc 2010

Merci pour ton article.. comme Thomas ça fait pas mal de temps que je cherche à faire ce genre de requête afin d'optimiser la géolocalisation..

par contre juste une question... je ne comprend pas ta structure de ta table "ville"
il y a quoi dans ta ligne "dist" ? ?

Thomas
  • 39. Thomas (site web) | Lun 06 Déc 2010

MERCIIIII !!!!!!!

5h que je tente de faire un tri dans le même style (sauf que les coordonnées sont tirées via la fonction de géolocalisation) et ça marche du tonnerre !

Il est possible d'afficher des distances routières en passant par google direction, cependant c'est assez limité en termes de volume de requête.

encore merci pour cet article

rmgl
  • 40. rmgl | Jeu 21 Oct 2010

bonsoir, l'explicatif est intéressant, cependant j'ajouterai, non un bémol mais une demande d'explication plus générale:
pour quelle raison latitude, longitude d'un même point varient selon Maps, tyre, GPS, Google etc. ?
Car le GPS nous positionne avec 5 mètres d'écart, donc une précision importante.

NAS

Yes ! intéressant tout ça ! Si en plus tu nous donnais un petit fichier SQL avec la table des villes de france :) ce serait le top ;-)
mais déjà merci :)

Adrien
  • 42. Adrien (site web) | Sam 24 Avr 2010

Merci pour ton tuto bien sympa :)

Par contre, juste pour info, dans mon cas la formule ne fonctionnais pas après plusieurs tentatives, du coup j'ai trouvé ceci, qui semble fonctionner pour moi :

http://code.google.com/intl/fr/apis/maps/articles/geospatial.html#geospatial

A savoir que c'est en miles, donc x1.6 et quelques ^^

Merci encore :)

drhouse
  • 43. drhouse | Jeu 22 Avr 2010

Article sympa
Merci bcp

Sun Location
  • 44. Sun Location (site web) | Mar 09 Mars 2010

Super cet article, bien simpa en tout cas !
merci :)

Jerem
  • 45. Jerem | Lun 18 Jan 2010

Salut Mec, bon article ! Du vrai tuto, comme on les aime ;)
Continue +++

PizzaToy
  • 46. PizzaToy (site web) | Ven 30 Oct 2009

Bonjour,

un grand merci pour la formule et le code, c'est vrai ça marche du tonnerre !
L'intérêt d'une telle application est multiple. On s'approche du logique de géomarketing en délimitant une zone de chalandise. On peut y adapter un curseur pour faire varier le rayon de notre recherche. Un des points intéressant est que, par exemple, une ville se trouvant limitrophe de plusieurs départements renverra un balayage complet de sa zone en une seule recherche; alors qu'un simple moteur par départemnt obligera de relancer la procédure.
Pour ma part j'ai ajouté un Autocompleter et une carte inréractive, tout ça avec 3 bases de données : une pour les départements et les préfectures, une autre pour les villes et positions (35000 points), et les adresses des pizzerias affiliées ou pas au concept www.pizzatoy.com.

Encore merci

fabien tanguy
  • 47. fabien tanguy | Dim 13 Sept 2009

la formule exacte a un nom celle d'haversine.
merci pour cet article très clair et très enrichissant

Portekoi
  • 48. Portekoi | Jeu 27 Août 2009

Merci à toi pour cet article fort intéressant :)

Tony Samson
  • 49. Tony Samson (site web) | Ven 31 Juil 2009

Formule plus complexe en tenant compte que la terre est ronde (donc plus précis) :
http://fr.wikipedia.org/wiki/Distance_du_grand_cercle

Tony Samson
  • 50. Tony Samson (site web) | Jeu 30 Juil 2009

Voilà le lien de démo est corrigé.
@topheur : pour ce qui est de la formule plus complexe je ne saurais dire où je l'avais trouvé... désolé.

Return
  • 51. Return | Mar 28 Juil 2009

Bonjour,

Article très bien réalisé. Merci pour le partage.

Une petite remarque : le lien ne marche plus :)

Ptigob
  • 52. Ptigob | Mar 21 Juil 2009

Cool c'est la formule qu'il me fallait merci msieur :)

Titcoatch
  • 53. Titcoatch | Mar 19 Mai 2009

Merci c'était parfait pour ce que je voulais faire et ça marche du tonnerre.

topheur
  • 54. topheur | Mer 08 Avr 2009

Trop bon.

Merci beaucoup pour cette article.

Question : tu dis dans ton article que tu utilises un calcul simpifié. Sais tu ou trouver la version non simplifié ?

philgood
  • 55. philgood | Mar 24 Mars 2009

Très interessant, très bien rédigé

merci pour ce partage

Antoine
  • 56. Antoine (site web) | Mer 11 Mars 2009

merci pour cet article :)

Voir plus de commentaires

Ajouter un commentaire