2013-02-07 41 views
16

Tôi muốn sử dụng công thức này với php. Tôi có một cơ sở dữ liệu với một số giá trị của vĩ độ và kinh độ được lưu.Công thức Haversine với php

Tôi muốn tìm, với giá trị vĩ độ và kinh độ nhất định trong đầu vào, tất cả khoảng cách (tính bằng km) từ điểm này với mỗi điểm trong cơ sở dữ liệu. Để làm điều này, tôi đã sử dụng công thức trên GoogleMaps api:

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

Tất nhiên sử dụng trong php Tôi thay radian với deg2rad .Công giá trị 37, -122 là những giá trị của tôi về đầu vào và lat, lng là những giá trị của tôi trong kho dữ liệu.

Dưới đây là mã của tôi. Vấn đề là có điều gì đó sai trái nhưng tôi không hiểu điều gì. Giá trị của khoảng cách là tất nhiên sai.

//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)))))); 
    } 

Đối với giá trị ví dụ: $ lat = 41,9133741000 $ lng = 12,5203944000

Tôi có đầu ra của khoảng cách = "4.826,9341106926"

+0

Kiểm tra dấu ngoặc bạn –

Trả lời

43

Công thức bạn đã sử dụng, có vẻ là arccosine thay vì công thức haversine. Công thức haversine thực sự là thích hợp hơn để tính toán khoảng cách trên một hình cầu, bởi vì nó không dễ bị làm tròn lỗi với các điểm phản đối.

/** 
* 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. Tôi không thể tìm thấy một lỗi trong mã của bạn, do đó, nó chỉ là một lỗi đánh máy mà bạn đã viết $lat= 41.9133741000 $lat= 12.5203944000? Có lẽ bạn chỉ cần tính với $ lat = 12.5203944000 và $ long = 0 vì bạn ghi đè biến $ lat của mình.

Edit:

Tested mã và nó trở lại một kết quả chính xác:

$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 
+0

xin lỗi, có nó chỉ là một sai lầm trong các thử nghiệm của câu hỏi, tất nhiên trong mã tôi đã sử dụng $ lat và $ lng –

+0

tôi lấy công thức [ở đây] (https://developers.google.com/maps/articles/ phpsqlsearch_v3? hl = it # findnearsql). với chức năng của bạn tôi có (với cùng một đầu vào ở trên) đầu ra của "4826934.1106926". tại sao? –

+0

@ user1938352 - Bạn có cố gắng khai báo biến này dưới dạng số thay vì chuỗi không? Ví dụ của bạn sử dụng '$ center_lat =" 41.8350 ";' nơi nó nên là '$ center_lat = 41.8350;', có thể nó phân tích cú pháp '.' dưới dạng dấu phân tách nghìn tùy thuộc vào cài đặt ngôn ngữ của bạn. – martinstoeckli

1

từ 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; 
} 

Như bạn có thể thấy có nhiều điểm khác biệt giữa mã này. Tôi không biết nếu bạn có một cách tiếp cận khác với công thức hoặc có thể một số bước khi chuyển đổi sang PHP đã sai, nhưng công thức trên sẽ hoạt động.

+0

đưa cái này tôi cùng một đầu ra của phương pháp của tôi, vì vậy tôi nghĩ rằng vấn đề là trong các giá trị của đầu vào. nhưng đâu là sai lầm? –

1

tôi tính toán khoảng cách thẳng bên trong truy vấn, sử dụng thủ tục lưu trữ sau đây:

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| 

Bạn chỉ cần thực hiện ở trên như một câu lệnh SQL từ bên trong phpMyAdmin để tạo ra các thủ tục. Chỉ cần thông báo kết thúc |, vì vậy trong cửa sổ nhập liệu SQL của bạn, hãy chọn cho | ký hiệu là giới hạn.

Sau đó, trong một truy vấn, hãy gọi nó như thế này:

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

Tôi thấy điều này xảy ra nhanh hơn rất nhiều so với tính toán nó trong PHP sau khi truy vấn đã được chạy.

+0

Nhân tiện, điều này tính toán khoảng cách tính bằng mét, vì vậy bạn có thể chia nó theo 1000 hoặc làm tròn nó bằng cách nào đó để tính toán KM. – CyberBrain

2
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 
} 
0

tôi làm cho lớp của haversign mà có fuction tĩnh getDistance có bốn paramters và nó trở lại khoảng cách từ điểm vị trí bot

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; 
} 
} 

trên lớp được lưu trữ tại thư mục gốc và thư mục gốc chứa thư mục lớp Gọi này bằng cách sử dụng các cách sau đây trong bất kỳ trang php

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 là như sau

4.7676529976827 
Các vấn đề liên quan