Tính khoảng cách giữa hai điểm kinh độ vĩ độ? (Công thức Haversine)


906

Làm cách nào để tính khoảng cách giữa hai điểm được chỉ định bởi vĩ độ và kinh độ?

Để làm rõ, tôi muốn khoảng cách bằng km; các điểm sử dụng hệ thống WGS84 và tôi muốn hiểu tính chính xác tương đối của các phương pháp tiếp cận có sẵn.


Để có độ chính xác cao hơn - xem stackoverflow.com/questions/1420045/
Kẻ

3
Lưu ý rằng bạn không thể áp dụng công thức Haversine trên ellipsoid của cuộc cách mạng như WGS 84. Bạn chỉ có thể áp dụng phương pháp này trên một hình cầu có bán kính.
Mike T

3
Hầu hết các câu trả lời ở đây đều sử dụng lượng giác hình cầu đơn giản, do đó, kết quả khá thô so với khoảng cách ellipsoid WGS84 được sử dụng trong hệ thống GPS. Một số câu trả lời đề cập đến công thức của Vincenty cho ellipsoids, nhưng thuật toán đó được thiết kế để sử dụng trên máy tính bàn thời đại của những năm 1960 và nó có vấn đề về độ ổn định và độ chính xác; chúng tôi có phần cứng và phần mềm tốt hơn bây giờ. Vui lòng xem GeographicLib để biết thư viện chất lượng cao với các triển khai bằng nhiều ngôn ngữ khác nhau.
PM 2Ring

@MikeT - đúng mặc dù nhiều câu trả lời ở đây có vẻ hữu ích trong khoảng cách nhỏ : Nếu bạn mất lat / long từ WGS 84 và áp dụng Haversine như thể đó là những điểm trên một hình cầu, bạn không nhận được câu trả lời mà lỗi chỉ là do yếu tố làm phẳng trái đất, vậy có lẽ trong vòng 1% công thức chính xác hơn? Với lời cảnh báo rằng đây là những khoảng cách nhỏ, hãy nói trong một thị trấn duy nhất.
ToolmakerSteve

1
Đối với các dạng tấm này: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Universal Windows Platform / Xamarin iOS / Xamarin Android, xem stackoverflow.com/a/54296314/2736742
A. Morel

Câu trả lời:


1147

Liên kết này có thể hữu ích cho bạn, vì nó chi tiết việc sử dụng công thức Haversine để tính khoảng cách.

Trích đoạn:

Tập lệnh này [trong Javascript] tính toán khoảng cách vòng tròn lớn giữa hai điểm - nghĩa là khoảng cách ngắn nhất trên bề mặt trái đất - sử dụng công thức 'Haversine'.

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

51
Liệu tính toán / phương pháp này cho Trái đất là một hình cầu (không phải là một hình cầu hoàn hảo)? Câu hỏi ban đầu yêu cầu khoảng cách giữa các điểm trên quả địa cầu WGS84. Không chắc chắn có bao nhiêu lỗi leo lên bằng cách sử dụng một hình cầu hoàn hảo, nhưng tôi nghi ngờ nó có thể khá nhiều tùy thuộc vào vị trí của các điểm trên quả địa cầu, do đó, sự khác biệt đáng để lưu tâm.
redcalx

15
Công thức Haversine không giải thích Trái đất là một hình cầu, do đó bạn sẽ gặp một số lỗi do thực tế đó. Nó không thể được đảm bảo chính xác đến hơn 0,5%. Điều đó có thể hoặc không thể là một mức độ lỗi chấp nhận được mặc dù.
Brandon

24
Có bất kỳ lý do để sử dụng Math.atan2(Math.sqrt(a), Math.sqrt(1-a))thay vì Math.asin(Math.sqrt(h)), đó sẽ là việc thực hiện trực tiếp công thức mà bài viết Wikipedia sử dụng? Có hiệu quả hơn và / hoặc ổn định hơn về số lượng?
musiphil

16
@UsmanMutawakil Vâng, 38 dặm bạn nhận được là khoảng cách trên đường. Thuật toán này tính toán khoảng cách đường thẳng trên bề mặt trái đất. Google Maps có một công cụ khoảng cách (phía dưới bên trái, "Phòng thí nghiệm") hoạt động tương tự, sử dụng công cụ đó để so sánh.
Pascal

4
@ Forte_201092: Bởi vì điều đó là không cần thiết - như (sin(x))²bằng(sin(-x))²
Jean Hominal

359

Tôi cần tính toán rất nhiều khoảng cách giữa các điểm cho dự án của mình, vì vậy tôi đã tiếp tục và cố gắng tối ưu hóa mã, tôi đã tìm thấy ở đây. Trung bình trong các trình duyệt khác nhau, triển khai mới của tôi chạy nhanh hơn 2 lần so với câu trả lời được đánh giá cao nhất.

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

Bạn có thể chơi với jsPerf của tôi và xem kết quả tại đây .

Gần đây tôi cần phải làm tương tự trong python, vì vậy đây là một triển khai python :

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

Và vì lợi ích của sự hoàn chỉnh: Haversine trên wiki.


13
@AngularM và rất có khả năng google sẽ tính toán khoảng cách nếu bạn sẽ đi một số đường và không phải là một đường thẳng.
Salvador Dali

3
Google tính toán khoảng cách lái xe, cái này tính toán "như con quạ bay"
sở thích

4
@Ouadie và nó sẽ cải thiện tốc độ? Rất có thể là không, nhưng tôi sẽ kết thúc với rất nhiều 'công cụ của bạn không hoạt động' cho những người sao chép nó trong các trình duyệt cũ
Salvador Dali

4
Vâng, nhưng những gì không // 2 * R; R = 6371 kmđại diện cho? và các phương pháp hiện tại cung cấp câu trả lời trong km hoặc dặm? cần tài liệu tốt hơn. Cảm ơn
Khalil Khalaf

20
@KhalilKhalaf bạn đang đùa hay đang cố gắng troll đây? km là viết tắt của km. Bạn nghĩ R là viết tắt của từ gì (đặc biệt nếu chúng ta nói về một shpere)? Đoán xem đơn vị nào sẽ là câu trả lời nếu bạn đã nhìn thấy km. Bạn đang tìm loại tài liệu nào ở đây: có đúng 4 dòng ở đó.
Salvador Dali

69

Đây là một triển khai C #:

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}

14
Bạn đang sử dụng bán kính xích đạo, nhưng bạn nên sử dụng bán kính trung bình là 6371 km
Philippe Leybaert

7
Không nên như vậy double dlon = Radians(lon2 - lon1);double dlat = Radians(lat2 - lat1);
Chris Marisic

Tôi đồng ý với Chris Marisic. Tôi đã sử dụng mã ban đầu và các tính toán đã sai. Tôi đã thêm lệnh gọi để chuyển đổi đồng bằng sang radian và nó hoạt động bình thường. Tôi đã gửi một bản chỉnh sửa và đang chờ nó được xem xét ngang hàng.
Bryan Bedard

Tôi đã gửi một chỉnh sửa khác vì lat1 & lat2 cũng cần phải được chuyển đổi thành radian. Tôi cũng đã sửa đổi công thức cho phép gán cho phù hợp với công thức và mã được tìm thấy ở đây: Movable-type.co.uk/scripts/latlong.html
Bryan Bedard

hiện RADIUSnhu cầu giá trị là 6371 như trong câu trả lời khác?
Chris Hayes

66

Đây là một triển khai java của công thức Haversine.

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

Lưu ý rằng ở đây chúng tôi đang làm tròn câu trả lời cho km gần nhất.


2
Nếu chúng ta muốn tính khoảng cách giữa hai điểm tính bằng mét, thì cách nào chính xác hơn? Để sử dụng 6371000như bán kính trái đất? (trung bình bán kính trái đất là 6371000 mét) hoặc chuyển đổi từ km sang mét từ chức năng của bạn?
Micro

nếu bạn muốn dặm, nhiều kết quả bởi0.621371
lasec0203

42

Cảm ơn rất nhiều cho tất cả điều này. Tôi đã sử dụng mã sau đây trong ứng dụng iPhone Objective-C của mình:

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

Vĩ độ và Kinh độ là số thập phân. Tôi đã không sử dụng min () cho cuộc gọi asin () vì khoảng cách mà tôi đang sử dụng quá nhỏ đến mức họ không yêu cầu.

Nó đã đưa ra câu trả lời không chính xác cho đến khi tôi chuyển các giá trị bằng Radian - bây giờ nó khá giống với các giá trị thu được từ ứng dụng Bản đồ của Apple :-)

Cập nhật thêm:

Nếu bạn đang sử dụng iOS4 trở lên thì Apple cung cấp một số phương pháp để thực hiện việc này để có thể đạt được chức năng tương tự với:

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}


Tôi nghĩ rằng dấu ngoặc đơn xung quanh pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))là không chính xác. Xóa những cái đó và kết quả khớp với những gì tôi nhận được khi tôi sử dụng các triển khai khác trên trang này hoặc triển khai công thức Haversine từ Wikipedia từ đầu.
zanedp

Sử dụng tọa độ (40.7127837, -74.0059413) cho NYC và (34.052234, -118.243685) cho LA, với ()khoảng đó, tôi nhận được 3869,75. Không có chúng, tôi nhận được 3935,75, gần như những gì một tìm kiếm trên web xuất hiện.
zanedp

40

Đây là một hàm PHP đơn giản sẽ đưa ra một xấp xỉ rất hợp lý (dưới tỷ lệ lỗi +/- 1%).

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

Như đã nói trước; trái đất KHÔNG phải là một hình cầu. Nó giống như một quả bóng chày cũ, cũ mà Mark McGwire đã quyết định tập luyện cùng - nó đầy vết lõm và va đập. Các tính toán đơn giản hơn (như thế này) coi nó như một quả cầu.

Các phương pháp khác nhau có thể chính xác hơn hoặc ít hơn tùy theo vị trí của bạn trên hình bầu dục không đều này VÀ điểm của bạn cách nhau bao xa (càng gần thì biên độ sai số tuyệt đối càng nhỏ). Kỳ vọng của bạn càng chính xác, toán học càng phức tạp.

Để biết thêm thông tin: khoảng cách địa lý wikipedia


4
Điều này hoạt động hoàn hảo! Tôi vừa thêm $ distance_miles = $ km * 0.621371; và đó là tất cả những gì cần thiết cho khoảng cách tương đối trong dặm! Cảm ơn Tony.

31

Tôi đăng ở đây ví dụ làm việc của tôi.

Liệt kê tất cả các điểm trong bảng có khoảng cách giữa một điểm được chỉ định (chúng tôi sử dụng một điểm ngẫu nhiên - lat: 45.20327, dài: 23.7806) dưới 50 KM, với vĩ độ và kinh độ, trong MySQL (các trường của bảng là tọa độ_lat và tọa độ_long):

Liệt kê tất cả có DISTANCE <50, tính bằng Kilômét (được coi là bán kính Trái đất 6371 KM):

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta 
FROM obiective 
WHERE coord_lat<>'' 
    AND coord_long<>'' 
HAVING distanta<50 
ORDER BY distanta desc

Ví dụ trên đã được thử nghiệm trong MySQL 5.0.95 và 5.5.16 (Linux).


Tôi nghĩ rằng một cách tiếp cận tốt có thể là lọc trước kết quả bằng cách sử dụng phép gần đúng, vì vậy công thức nặng chỉ được áp dụng cho một số trường hợp. Đặc biệt hữu ích nếu bạn có điều kiện khác. Tôi đang sử dụng cái này cho aprox ban đầu: stackoverflow.com/questions/1253499/ cấp
Pato

28

Trong các câu trả lời khác, việc thực hiện trong mất tích

Tính khoảng cách giữa hai điểm khá đơn giản với distmhàm từ geospheregói:

distm(p1, p2, fun = distHaversine)

Ở đâu:

p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid 

Vì trái đất không phải là hình cầu hoàn hảo, công thức Vincenty cho ellipsoids có lẽ là cách tốt nhất để tính khoảng cách. Vì vậy, trong geospheregói bạn sử dụng sau đó:

distm(p1, p2, fun = distVincentyEllipsoid)

Dĩ nhiên bạn không nhất thiết phải sử dụng geospheregói, bạn cũng có thể tính khoảng cách trong cơ sở Rvới một hàm:

hav.dist <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}

Để chắc chắn rằng tôi rõ về những gì bạn đã nói: Mã bạn đưa ra ở cuối bài: Đó có phải là một công thức của Vincenty không? Theo như bạn biết, nó sẽ đưa ra câu trả lời giống như gọi Vincenty trong không gian địa lý? [Tôi không có không gian địa lý hoặc thư viện khác; chỉ cần tìm một số mã để đưa vào một ứng dụng đa nền tảng. Tất nhiên tôi sẽ xác minh một số trường hợp thử nghiệm đối với một máy tính tốt đã biết.]
ToolmakerSteve

1
@ToolmakerSteve chức năng ở cuối câu trả lời của tôi là triển khai phương pháp Haversine
Jaap

Xin chào @Jaap Tôi có thể hỏi đơn vị đo lường cho công thức là gì? Có phải trong mét?
Jackson

11

Haversine chắc chắn là một công thức tốt cho hầu hết các trường hợp, các câu trả lời khác đã bao gồm nó vì vậy tôi sẽ không lấy chỗ trống. Nhưng điều quan trọng cần lưu ý là bất kể công thức nào được sử dụng (có, không chỉ một). Bởi vì phạm vi chính xác rất lớn có thể cũng như thời gian tính toán cần thiết. Sự lựa chọn công thức đòi hỏi một chút suy nghĩ hơn là một câu trả lời đơn giản không có trí tuệ.

Bài đăng này từ một người ở nasa, là bài tốt nhất tôi tìm thấy khi thảo luận về các lựa chọn

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

Ví dụ, nếu bạn chỉ là sắp xếp hàng theo khoảng cách trong bán kính 100 dặm. Công thức trái đất phẳng sẽ nhanh hơn nhiều so với haversine.

HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;

Lưu ý rằng chỉ có một cosin và một căn bậc hai. Vs 9 trong số họ trên công thức Haversine.


Đó là một khả năng tốt đẹp. Chỉ cần lưu ý rằng khoảng cách tối đa đề nghị trong các cuộc thảo luận là 12 dặm, không 100 , và rằng ngay cả như vậy, lỗi có thể leo lên đến 30 mét (100 ft), tùy thuộc vào vị trí của trái đất.
Eric Wu

7

Bạn có thể sử dụng bản dựng trong CLLocationDistance để tính toán điều này:

CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]

- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
    CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
    return distanceInMeters;
}

Trong trường hợp của bạn nếu bạn muốn số km chỉ cần chia cho 1000.


7

Tôi không muốn thêm một câu trả lời nữa, nhưng Google maps API v.3 có hình dạng hình cầu (và hơn thế nữa). Sau khi chuyển đổi WGS84 của bạn thành độ thập phân, bạn có thể làm điều này:

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

Không có thông tin nào về cách tính toán chính xác của Google hoặc thậm chí mô hình nào được sử dụng (mặc dù nó nói "hình cầu" thay vì "Geoid". Nhân tiện, khoảng cách "đường thẳng" rõ ràng sẽ khác với khoảng cách nếu một người đi trên bề mặt trái đất là thứ mà mọi người dường như đang đoán trước.


khoảng cách tính bằng mét. cách khác, người ta có thể sử dụng computeLpm ()
electrobabe

7

Python implimentation Origin là trung tâm của Hoa Kỳ tiếp giáp.

from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)

Để có được câu trả lời bằng km chỉ cần đặt dặm = false.


1
Bạn đang nhập một gói không chuẩn, thực hiện tất cả công việc. Tôi không biết nếu đó là tất cả những gì hữu ích.
Teepeemm

Gói này nằm trong PyPI, Python Gói Index, dưới dạng gói python 3 cùng với numpy và scikit-learn. Không chắc chắn tại sao một người được gắn vào các gói. Chúng có xu hướng khá hữu ích. Là nguồn mở, người ta cũng có thể kiểm tra các phương thức có trong đó. Tôi nghĩ rằng nhiều người sẽ thấy gói này hữu ích vì vậy tôi sẽ rời khỏi bài viết mặc dù downvote. Chúc mừng. :)
invoketheshell

7

Có thể có một giải pháp đơn giản hơn và chính xác hơn: Chu vi của trái đất là 40.000Km tại xích đạo, khoảng 37.000 trên chu kỳ Greenwich (hoặc bất kỳ kinh độ) nào. Như vậy:

pythagoras = function (lat1, lon1, lat2, lon2) {
   function sqr(x) {return x * x;}
   function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}

   var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
   var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
   var dy = 37000000.0 * (lat1 - lat2) / 360.0;

   return Math.sqrt(sqr(dx) + sqr(dy));
};

Tôi đồng ý rằng nó nên được tinh chỉnh vì, bản thân tôi đã nói rằng đó là một hình elip, do đó bán kính được nhân với cosin thay đổi. Nhưng nó chính xác hơn một chút. So với Google Maps và nó đã giảm lỗi đáng kể.


Là chức năng này khoảng cách trở lại trong km?
Wikki

Đó là, chỉ vì đường xích đạo và chu kỳ kinh độ được tính bằng Km. Ví dặm, chỉ cần chia 40000 và 37000 của 1.6. Cảm thấy geeky, bạn có thể chuyển đổi nó thành Ris, nhân lên khoảng 7 hoặc thành parasang, chia cho 2.2 ;-)
Meymann

Đây dường như là câu trả lời tốt nhất được cung cấp ở đây. Tôi muốn sử dụng nó nhưng tôi chỉ tự hỏi liệu có cách nào để xác minh tính đúng đắn của thuật toán này. Tôi đã thử nghiệm f (50,5,58,3). Nó cho 832km, trong khi Movable-type.co.uk/scripts/latlong.html sử dụng công thức 'haversine' cho 899km. Có một sự khác biệt lớn như vậy?
Chong Lip Phang

Hơn nữa, tôi nghĩ giá trị được trả về bởi đoạn mã trên là tính bằng m chứ không phải km.
Chong Lip Phang

@ChongLipPhang - THẬN TRỌNG: Định lý Pythagore chỉ là xấp xỉ hợp lý cho các khu vực nhỏ , vì định lý này giả định trái đất phẳng. Như một trường hợp cực đoan, bắt đầu trên đường xích đạo, và di chuyển 90 độ về phía đông và 90 độ về phía bắc. Kết quả cuối cùng tất nhiên là cực bắc, và giống như di chuyển 0 độ đông và 90 độ bắc; vì vậy, làm sqrt (sqr (dx) + sqr (dy)) sẽ hết sức trong trường hợp đầu tiên. ~ sqrt (10km sqr + 10km sqr) ~ = 14,4 km so với khoảng cách chính xác ~ 10km.
ToolmakerSteve

7

Tất cả các câu trả lời trên giả định trái đất là một hình cầu. Tuy nhiên, một xấp xỉ chính xác hơn sẽ là một hình cầu bắt buộc.

a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km

def Distance(lat1, lons1, lat2, lons2):
    lat1=math.radians(lat1)
    lons1=math.radians(lons1)
    R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
    x1=R*math.cos(lat1)*math.cos(lons1)
    y1=R*math.cos(lat1)*math.sin(lons1)
    z1=R*math.sin(lat1)

    lat2=math.radians(lat2)
    lons2=math.radians(lons2)
    R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
    x2=R*math.cos(lat2)*math.cos(lons2)
    y2=R*math.cos(lat2)*math.sin(lons2)
    z2=R*math.sin(lat2)

    return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5

6

Đây là triển khai SQL để tính khoảng cách tính bằng km,

SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * 
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * 
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5  ORDER BY distance LIMIT 0 , 5;

Để biết thêm chi tiết trong việc triển khai bằng cách lập trình langugage, bạn chỉ cần xem qua tập lệnh php được cung cấp ở đây


5

Đây là một bản thực hiện của công thức Haversine

static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
    var deg2Rad = deg => {
        return deg * Math.PI / 180;
    }

    var r = 6371; // Radius of the earth in km
    var dLat = deg2Rad(lat2 - lat1);   
    var dLon = deg2Rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = r * c; // Distance in km
    return d;
}

5

Như đã chỉ ra, một tính toán chính xác cần tính đến việc trái đất không phải là một hình cầu hoàn hảo. Dưới đây là một số so sánh của các thuật toán khác nhau được cung cấp ở đây:

geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km

geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km

geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km

geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km

Trong khoảng cách nhỏ, thuật toán của Keerthana dường như trùng khớp với thuật toán của Google Maps. Google Maps dường như không tuân theo bất kỳ thuật toán đơn giản nào, cho thấy rằng nó có thể là phương pháp chính xác nhất ở đây.

Dù sao, đây là một triển khai Javascript của thuật toán Keerthana:

function geoDistance(lat1, lng1, lat2, lng2){
    const a = 6378.137; // equitorial radius in km
    const b = 6356.752; // polar radius in km

    var sq = x => (x*x);
    var sqr = x => Math.sqrt(x);
    var cos = x => Math.cos(x);
    var sin = x => Math.sin(x);
    var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));

    lat1 = lat1 * Math.PI / 180;
    lng1 = lng1 * Math.PI / 180;
    lat2 = lat2 * Math.PI / 180;
    lng2 = lng2 * Math.PI / 180;

    var R1 = radius(lat1);
    var x1 = R1*cos(lat1)*cos(lng1);
    var y1 = R1*cos(lat1)*sin(lng1);
    var z1 = R1*sin(lat1);

    var R2 = radius(lat2);
    var x2 = R2*cos(lat2)*cos(lng2);
    var y2 = R2*cos(lat2)*sin(lng2);
    var z2 = R2*sin(lat2);

    return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}

4

Kịch bản lệnh này [trong PHP] tính toán khoảng cách giữa hai điểm.

public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
        $lat1 = $source[0];
        $lon1 = $source[1];
        $lat2 = $dest[0];
        $lon2 = $dest[1];

        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        $unit = strtoupper($unit);

        if ($unit == "K") {
            return ($miles * 1.609344);
        }
        else if ($unit == "M")
        {
            return ($miles * 1.609344 * 1000);
        }
        else if ($unit == "N") {
            return ($miles * 0.8684);
        } 
        else {
            return $miles;
        }
    }

4

Thực hiện Java theo công thức Haversine

double calculateDistance(double latPoint1, double lngPoint1, 
                         double latPoint2, double lngPoint2) {
    if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
        return 0d;
    }

    final double EARTH_RADIUS = 6371.0; //km value;

    //converting to radians
    latPoint1 = Math.toRadians(latPoint1);
    lngPoint1 = Math.toRadians(lngPoint1);
    latPoint2 = Math.toRadians(latPoint2);
    lngPoint2 = Math.toRadians(lngPoint2);

    double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) 
            + Math.cos(latPoint1) * Math.cos(latPoint2)
            * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
    distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));

    return distance; //km value
}

3

Để tính khoảng cách giữa hai điểm trên một quả cầu, bạn cần thực hiện phép tính Great Circle .

Có một số thư viện C / C ++ để trợ giúp trình chiếu bản đồ tại MapTools nếu bạn cần điều chỉnh lại khoảng cách của mình đến một bề mặt phẳng. Để làm điều này, bạn sẽ cần chuỗi chiếu của các hệ tọa độ khác nhau.

Bạn cũng có thể tìm thấy MapWindow một công cụ hữu ích để trực quan hóa các điểm. Ngoài ra, nguồn mở của nó là một hướng dẫn hữu ích về cách sử dụng thư viện proj.dll, dường như là thư viện chiếu mã nguồn mở lõi.


3

Đây là cách thực hiện câu trả lời được chấp nhận chuyển sang Java trong trường hợp có ai cần nó.

package com.project529.garage.util;


/**
 * Mean radius.
 */
private static double EARTH_RADIUS = 6371;

/**
 * Returns the distance between two sets of latitudes and longitudes in meters.
 * <p/>
 * Based from the following JavaScript SO answer:
 * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
 * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
 */
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
    double dLat = toRadians(lat2 - lat1);
    double dLon = toRadians(lon2 - lon1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                    Math.sin(dLon / 2) * Math.sin(dLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double d = EARTH_RADIUS * c;

    return d;
}

public double toRadians(double degrees) {
    return degrees * (Math.PI / 180);
}

2

Dưới đây là VB.NET triển khai, việc triển khai này sẽ cho bạn kết quả bằng KM hoặc Miles dựa trên giá trị Enum mà bạn vượt qua.

Public Enum DistanceType
    Miles
    KiloMeters
End Enum

Public Structure Position
    Public Latitude As Double
    Public Longitude As Double
End Structure

Public Class Haversine

    Public Function Distance(Pos1 As Position,
                             Pos2 As Position,
                             DistType As DistanceType) As Double

        Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)

        Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)

        Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)

        Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)

        Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))

        Dim result As Double = R * c

        Return result

    End Function

    Private Function toRadian(val As Double) As Double

        Return (Math.PI / 180) * val

    End Function

End Class

Khi tính "a", bạn đã viết Math.Sin ( dLat ..) hai lần do nhầm lẫn?
Marco Ottina

2

Tôi cô đọng tính toán xuống bằng cách đơn giản hóa công thức.

Đây là Ruby.

include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }

# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
  from, to = coord_radians[from], coord_radians[to]
  cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
  sines_product = sin(to[:lat]) * sin(from[:lat])
  return earth_radius_mi * acos(cosines_product + sines_product)
end

2
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; 
  var miles = d / 1.609344; 

if ( units == 'km' ) {  
return d; 
 } else {
return miles;
}}

giải pháp của Chuck, hợp lệ hàng dặm cũng có.


2

Đây là triển khai java của tôi cho khoảng cách tính toán thông qua độ thập phân sau một số tìm kiếm. Tôi đã sử dụng bán kính trung bình của thế giới (từ wikipedia) tính bằng km. Nếu bạn muốn kết quả dặm sau đó sử dụng bán kính trên thế giới trong dặm.

public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) 
{
  double earthRadius = 6371.0d; // KM: use mile here if you want mile result

  double dLat = toRadian(lat2 - lat1);
  double dLng = toRadian(lng2 - lng1);

  double a = Math.pow(Math.sin(dLat/2), 2)  + 
          Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * 
          Math.pow(Math.sin(dLng/2), 2);

  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

  return earthRadius * c; // returns result kilometers
}

public static double toRadian(double degrees) 
{
  return (degrees * Math.PI) / 180.0d;
}

2

Trong Mysql sử dụng hàm sau truyền các tham số như sử dụng POINT(LONG,LAT)

CREATE FUNCTION `distance`(a POINT, b POINT)
 RETURNS double
    DETERMINISTIC
BEGIN

RETURN

GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters

END;

2
function getDistanceFromLatLonInKm(position1, position2) {
    "use strict";
    var deg2rad = function (deg) { return deg * (Math.PI / 180); },
        R = 6371,
        dLat = deg2rad(position2.lat - position1.lat),
        dLng = deg2rad(position2.lng - position1.lng),
        a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(deg2rad(position1.lat))
            * Math.cos(deg2rad(position1.lat))
            * Math.sin(dLng / 2) * Math.sin(dLng / 2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

console.log(getDistanceFromLatLonInKm(
    {lat: 48.7931459, lng: 1.9483572},
    {lat: 48.827167, lng: 2.2459745}
));

2

đây là một ví dụ trong postgres sql (ở km, cho phiên bản dặm, thay thế 1,609344 bởi phiên bản 0,8684)

CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat  

float, blng  float)
  RETURNS float AS
$BODY$
DECLARE
    v_distance float;
BEGIN

    v_distance = asin( sqrt(
            sin(radians(blat-alat)/2)^2 
                + (
                    (sin(radians(blng-alng)/2)^2) *
                    cos(radians(alat)) *
                    cos(radians(blat))
                )
          )
        ) * cast('7926.3352' as float) * cast('1.609344' as float) ;


    RETURN v_distance;
END 
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;

2

Đây là một mã khác được chuyển đổi thành mã Ruby :

include Math
#Note: from/to = [lat, long]

def get_distance_in_km(from, to)
  radians = lambda { |deg| deg * Math.PI / 180 }
  radius = 6371 # Radius of the earth in kilometer
  dLat = radians[to[0]-from[0]]
  dLon = radians[to[1]-from[1]]

  cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)

  c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) 
  return radius * c # Distance in kilometer
end

1

Có một ví dụ điển hình ở đây để tính khoảng cách với PHP http://www.geodatasource.com/developers/php :

 function distance($lat1, $lon1, $lat2, $lon2, $unit) {

     $theta = $lon1 - $lon2;
     $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
     $dist = acos($dist);
     $dist = rad2deg($dist);
     $miles = $dist * 60 * 1.1515;
     $unit = strtoupper($unit);

     if ($unit == "K") {
         return ($miles * 1.609344);
     } else if ($unit == "N") {
          return ($miles * 0.8684);
     } else {
          return $miles;
     }
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.