views:

1576

answers:

6

Hello!

In PHP, I have the following code for calculating the distance between two locations:

<?php
function distance($lat1, $long1, $lat2, $long2) {
    // DEGREE TO RADIAN
    $latitude1 = $lat1/180*pi();
    $longitude1 = $long1/180*pi();
    $latitude2 = $lat2/180*pi();
    $longitude2 = $long2/180*pi();
    // FORMULA: e = ARCCOS ( SIN(Latitude1) * SIN(Latitude2) + COS(Latitude1) * COS(Latitude2) * COS(Longitude2-Longitude1) ) * EARTH_RADIUS
    $distance = acos(sin($latitude1)*sin($latitude2)+cos($latitude1)*cos($latitude2)*cos($longitude2-$longitude1))*6371;
    return $distance;
}
echo distance(9.9921962, 53.5534074, 9.1807688, 48.7771056); // Hamburg, DE - Stuttgart, DE
?>

But now, I want to select locations close to a given location via PHP from my MySQL database:

  • The user enters his hometown
  • My script gets the latitude/longitude values via the Google API
  • In my database, I have about 200 locations with a field for the latitude value and a field for the longitude value
  • I need a code for PHP and MySQL to select the 10 locations which are closest to the user's hometown

I hope you can help me. Thanks in advance!

A: 

There are MySQL functions available for doing exactly this.

http://dev.mysql.com/doc/refman/5.0/en/gis-introduction.html

Frank Farmer
Thanks but I can't use MySQL's geospatial features.
A: 

MySQL has the ability to index rows geospatially. You might not need to do this math by yourself (you can just ask MySQL to compute the distance between two geo objects and sort by that..).

See: http://forums.mysql.com/read.php?23,159205,159205

Assaf Lavie
Thanks but I can't use MySQL's geospatial features.
A: 

Why don't you use MySQL's geospatial features...? No, just kidding.

If the 200 records are actual places like towns, etc then as an alternative could you use GeoNames' API?

The following webservice will provide the 10 closest locations to the lat and lng provided:

http://ws.geonames.org/findNearby?lat=47.3&amp;lng=9

Source: http://www.geonames.org/export/web-services.html#findNearbyPlaceName

Full list: http://www.geonames.org/export/ws-overview.html

mr-euro
Thanks! I have the data in my database so I needn't use GeoNames' API.
Yes I understand, but you could match those 200 records with the responses from Geonames and from that have the 10 closest matches. This is again assuming that your 200 records are somehow known places and not some random positions.
mr-euro
+1  A: 

That's the Haversine formula. You can translate the PHP directly into SQL so you can query the database spatially (the alternative being to pull every record out of the DB and run the data through PHP). MySQL provides all the maths functions that you need.

I did this for a commercial website which provided post/zipcode based distance lookups, so its certainly possible without specific GIS functions.

nOw2
Thanks, it works fine! :) I didn't know that MySQL has all the math functions.
+1  A: 

MySQL Great Circle Distance (Haversine formula) does exactly what you need.

With only 200 records however you may as well just load them all and check them with code. The data set is really way too small to be worrying too much about database vs code or any other such optimizations.

Calculating distance between zip codes in PHP has a couple of PHP implementations of this algorithm.

Geo Proximity Search is pretty much the exact same problem you have.

cletus
Thanks, as nOw2 said, it's the Haversine formula. I have the PHP implemention yet, see the code in my question.
+1  A: 

Maybe something like

SELECT field1, field2, ...,
    ACOS(SIN(latitude / 180 * PI()) * SIN(:1) + COS(latitude / 180 * PI()) * COS(:2) * COS(:2 - longtidude)) * 6371 AS distance
    ORDER BY distance ASC;

or

SELECT field1, field2, ...,
    ACOS(SIN(RADIANS(latitude)) * SIN(:1) + COS(RADIANS(latitude)) * COS(:2) * COS(:2 - longtidude)) * 6371 AS distance
    ORDER BY distance ASC;

(directly translated from the PHP code)

:1 and :2 is $lat2/180*pi() and $long2/180*pi() respectively.

itsbth
Thanks, it's the same as nOw2's answer, isn't it? You write "$lat2/180*pi()". Why not just "RADIANS($lat2)"? You used it in the rest of the query, so why not here, too?
:1 and :2 comes from PHP, which doesn't have RADIANS. http://us2.php.net/deg2rad could be used though.
itsbth