25

I want to use this formula with php. I have a database with some values of latitute and longitude saved.

I want to find, with a certain value of latitude and longitude in input, all the distances (in km) from this point with each point in the database. To do this, I used the formula on googlemaps api:

( 6371 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) )

Of course using that in php I replaced radians with deg2rad.The values 37,-122 are my values of input and lat,lng are my values in the database.

Below there is my code. The problem is that there is something wrong but I don't understand what. The value of distance is of course wrong.

//values of latitude and longitute in input (Rome - eur, IT)
$center_lat = "41.8350";
$center_lng =  "12.470";

//connection to database. it works
(..)

//to take each value in the database:
    $query = "SELECT * FROM Dati";
    $result = mysql_query($query);
    while ($row = @mysql_fetch_assoc($result)){
        $lat=$row['Lat']);
        $lng=$row['Lng']);
    $distance =( 6371 * acos((cos(deg2rad($center_lat)) ) * (cos(deg2rad($lat))) * (cos(deg2rad($lng) - deg2rad($center_lng)) )+ ((sin(deg2rad($center_lat))) * (sin(deg2rad($lat))))) );
    }

For values for example: $lat= 41.9133741000 $lng= 12.5203944000

I have the output of distance="4826.9341106926"

5 Answers5

63

The formula you used, seems to be the arccosine instead of the haversine formula. The haversine formula is indeed more appropriate to calculate the distance on a sphere, because it is less prone to rounding errors.

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}

P.S. I couldn't find an error in your code, so is it just a typo that you wrote $lat= 41.9133741000 $lat= 12.5203944000 ? Maybe you just calculated with $lat=12.5203944000 and $long=0 because you overwrote your $lat variable.

Edit:

Tested the code and it returned a correct result:

$center_lat = 41.8350;
$center_lng = 12.470;
$lat = 41.9133741000;
$lng = 12.5203944000;

// test with your arccosine formula
$distance =( 6371 * acos((cos(deg2rad($center_lat)) ) * (cos(deg2rad($lat))) * (cos(deg2rad($lng) - deg2rad($center_lng)) )+ ((sin(deg2rad($center_lat))) * (sin(deg2rad($lat))))) );
print($distance); // prints 9.662174538188

// test with my haversine formula
$distance = haversineGreatCircleDistance($center_lat, $center_lng, $lat, $lng, 6371);
print($distance); // prints 9.6621745381693
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • sorry, yes it's only a mistake in the test of the question, of course in the code I used $lat and $lng –  Feb 07 '13 at 14:37
  • I took the formula [here](https://developers.google.com/maps/articles/phpsqlsearch_v3?hl=it#findnearsql). with your function I have (with the same input above) the output of "4826934.1106926". why? –  Feb 07 '13 at 14:46
  • @user1938352 - Did you try to declare the variable as number instead of a string? Your example uses `$center_lat = "41.8350";` where it should be `$center_lat = 41.8350;`, maybe it parses the '.' as thousand separator depending on your locale settings. – martinstoeckli Feb 07 '13 at 14:59
  • if I use $center_lat = 41.8350; I have the same output. I tryed. I'm desperate! –  Feb 07 '13 at 15:03
  • @user1938352 - I made a test with your code and it returns the correct result. So you have to search for the error somewhere else, could you run the test code on your own server? – martinstoeckli Feb 07 '13 at 15:20
  • I really really don't know what is wrong in my code, but I took the old code without this part, put inside the part of haversineGreatCircleDistance and now works!thank you very much –  Feb 07 '13 at 17:24
  • 1
    Must note that this formula used is calculating in meters. To change to kilometers ax the last three digits for the earths radius.(6371) – 1011 1110 Feb 09 '16 at 03:01
  • 10
    I like how you take the earth radius as a parameter - very future proof. – TJ L Jul 14 '16 at 19:22
  • Not just future-proof, allows for usage of different units :) – Darvanen Feb 21 '18 at 01:09
  • May I know what is $center_lat ? – Nadia Nov 20 '19 at 08:03
  • @Nadia - It's the latitude of the center of the world . No it is copied from the OPs question and according to the comment it is the coordinate of Rom, but for the calculation it is just one of two points to calculate the distance between. – martinstoeckli Nov 20 '19 at 08:28
6
public function getDistanceBetweenTwoPoints($point1 , $point2){
    // array of lat-long i.e  $point1 = [lat,long]
    $earthRadius = 6371;  // earth radius in km
    $point1Lat = $point1[0];
    $point2Lat =$point2[0];
    $deltaLat = deg2rad($point2Lat - $point1Lat);
    $point1Long =$point1[1];
    $point2Long =$point2[1];
    $deltaLong = deg2rad($point2Long - $point1Long);
    $a = sin($deltaLat/2) * sin($deltaLat/2) + cos(deg2rad($point1Lat)) * cos(deg2rad($point2Lat)) * sin($deltaLong/2) * sin($deltaLong/2);
    $c = 2 * atan2(sqrt($a), sqrt(1-$a));

    $distance = $earthRadius * $c;
    return $distance;    // in km
}
Tayyab Hussain
  • 1,658
  • 1
  • 18
  • 21
4

from this link:

function getDistance($latitude1, $longitude1, $latitude2, $longitude2) {
    $earth_radius = 6371;

    $dLat = deg2rad($latitude2 - $latitude1);
    $dLon = deg2rad($longitude2 - $longitude1);

    $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);
    $c = 2 * asin(sqrt($a));
    $d = $earth_radius * $c;

    return $d;
}

As you can see there are many differences between this as your code. I don't know if you have either a different approach to the formula or maybe some step when converting to PHP went wrong, but the above formula should work.

Naryl
  • 1,878
  • 1
  • 10
  • 12
  • this give me the same output of my method, so I think the problem is in the values of input. but where is the mistake? –  Feb 07 '13 at 12:51
2

I making the class of haversine which having the static fuction getDistance having the four paramters and it return the distance from the bot location points

class HaverSign {
    
     public static function getDistance($latitude1, $longitude1, $latitude2, $longitude2) {
        $earth_radius = 6371;

        $dLat = deg2rad($latitude2 - $latitude1);
        $dLon = deg2rad($longitude2 - $longitude1);

        $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);
        $c = 2 * asin(sqrt($a));
        $d = $earth_radius * $c;

        return $d;
}
}

above class is stored at the root directory and root directory contains classes folder Call this by using the following way in any php page

include "../classes/HaverSign.php";
$haversign=new HaverSign();

$lat=18.5204;
$lon=73.8567;

$lat1=18.5404;
$lon1=73.8167;

$dist = $haversign->getDistance($lat,$lon,$lat1,$lon1);
echo $dist;

Output is as follow

4.7676529976827
dawg
  • 98,345
  • 23
  • 131
  • 206
Sandip Bhoi
  • 440
  • 3
  • 9
1

I calculate distances straight inside queries, using the following stored procedure:

CREATE FUNCTION GEODIST (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
    RETURNS DOUBLE
    DETERMINISTIC
        BEGIN
            DECLARE dist DOUBLE;
            SET dist =  round(acos(cos(radians(lat1))*cos(radians(lon1))*cos(radians(lat2))*cos(radians(lon2)) + cos(radians(lat1))*sin(radians(lon1))*cos(radians(lat2))*sin(radians(lon2)) + sin(radians(lat1))*sin(radians(lat2))) * 6378.8, 1);
            RETURN dist;
        END|

You just execute the above as an SQl statement from within phpMyAdmin to create the procedure. Just notice the ending |, so in your SQL input window, choose for the | sign as limiter.

Then in a query, call it like this:

$sql = "
SELECT `locations`.`name`, GEODIST(`locations`.`lat`, `locations`.`lon`, " . $lat_to_calculate . ", " . $lon_to_calculate . ") AS `distance`
FROM `locations` ";

I found this to be a lot faster than calculating it in PHP after the query has been run.

CyberBrain
  • 127
  • 3
  • By the way, this calculates the distance in meters, so you could divide it by 1000 or round it somehow to calculate KM's. – CyberBrain Feb 07 '13 at 12:07