[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 !