Tính khoảng cách giữa 2 tọa độ GPS


Câu trả lời:


406

Tính khoảng cách giữa hai tọa độ theo vĩ độ và kinh độ , bao gồm cả việc triển khai Javascript.

Địa điểm TâyNam là âm. Nhớ số phút và giây nằm ngoài 60 nên S31 30 'là -31,50 độ.

Đừng quên chuyển đổi độ sang radian . Nhiều ngôn ngữ có chức năng này. Hoặc là một phép tính đơn giản : radians = degrees * PI / 180.

function degreesToRadians(degrees) {
  return degrees * Math.PI / 180;
}

function distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
  var earthRadiusKm = 6371;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(lat2);

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  return earthRadiusKm * c;
}

Dưới đây là một số ví dụ về cách sử dụng:

distanceInKmBetweenEarthCoordinates(0,0,0,0)  // Distance between same 
                                              // points should be 0
0

distanceInKmBetweenEarthCoordinates(51.5, 0, 38.8, -77.1) // From London
                                                          // to Arlington
5918.185064088764

17
Trong trường hợp không rõ ràng, phương thức toRad () là một tùy chỉnh cho nguyên mẫu Số, chẳng hạn như : Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Hoặc, như được chỉ ra dưới đây, bạn có thể thay thế (Math.PI/2)bằng 0,0174532925199433 (... bất cứ độ chính xác nào bạn thấy cần thiết) để tăng hiệu suất.
Vinney Kelly

44
Nếu bất cứ ai, đặc biệt là những người bạn không tìm kiếm bình luận cuối dòng, đang nhìn chằm chằm vào công thức này và tìm kiếm một đơn vị khoảng cách, đơn vị là km. :)
Dylan Knowles

1
@VinneyKelly Lỗi đánh máy nhỏ nhưng thay thế (Math.PI / 180) thì không (Math.PI / 2), cảm ơn vì sự giúp đỡ của mọi người
Patrick Murphy

1
@ChristianKRider Nhìn vào dòng đầu tiên. Hãy suy nghĩ về những gì Rthường có nghĩa trong toán học, sau đó tra cứu các đại lượng liên quan đến Trái đất để xem các số có khớp nhau không.
Vụ kiện của Quỹ Monica

3
Đối với đơn vị hoàng đế (dặm), bạn có thể thay đổi earthRadiusKmđược var earthRadiusMiles = 3959;, FYI.
chapeljuice

59

Tìm kiếm haversine với Google; đây là giải pháp của tôi:

#include <math.h>
#include "haversine.h"

#define d2r (M_PI / 180.0)

//calculate haversine distance for linear distance
double haversine_km(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 6367 * c;

    return d;
}

double haversine_mi(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 3956 * c; 

    return d;
}

3
Bạn có thể thay thế (M_PI / 180.0) bằng 0,0174532925199433 để có hiệu suất tốt hơn.
Hlung

3
Về mặt hiệu suất: người ta chỉ có thể tính sin (dlat / 2.0) một lần, lưu trữ nó trong biến a1 và thay vì pow (, 2) tốt hơn là sử dụng a1 * a1. Tương tự cho các pow khác (, 2).
pms

71
Vâng, hoặc chỉ sử dụng trình biên dịch sau 60 giây.
đúng vào

17
Không cần phải "tối ưu hóa" (M_PI / 180.0) thành một hằng số mà không ai hiểu được nếu không có ngữ cảnh. Trình biên dịch tính toán các điều khoản cố định cho bạn!
Patrick Cornelissen

2
@ TõnuSamuel Cảm ơn bạn rất nhiều vì bình luận của bạn. Tôi rất trân trọng điều này. Điều hợp lý là trình biên dịch có bật tối ưu hóa (-O) có thể tính toán trước các hoạt động của các hằng số, làm cho việc thu gọn thủ công trở nên vô dụng. Tôi sẽ kiểm tra nó khi tôi có thời gian.
Hlung

44

Phiên bản C # của Haversine

double _eQuatorialEarthRadius = 6378.1370D;
double _d2r = (Math.PI / 180D);

private int HaversineInM(double lat1, double long1, double lat2, double long2)
{
    return (int)(1000D * HaversineInKM(lat1, long1, lat2, long2));
}

private double HaversineInKM(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * _d2r;
    double dlat = (lat2 - lat1) * _d2r;
    double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r) * Math.Pow(Math.Sin(dlong / 2D), 2D);
    double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
    double d = _eQuatorialEarthRadius * c;

    return d;
}

Đây là một Fiddle .NET về điều này , vì vậy bạn có thể kiểm tra nó bằng Lat / Longs của riêng bạn.


1
Tôi cũng đã thêm một câu đố .NET kiểm tra để mọi người có thể dễ dàng kiểm tra điều này.
Pure.Krom

7
.Net Framework có bản dựng theo phương thức GeoCoordine.GetDistanceTo. Hệ thống lắp ráp System.Device phải được tham chiếu. Bài viết MSDN msdn.microsoft.com/en-us/l
Library / từ

27

Phiên bản Java của thuật toán Haversine dựa trên câu trả lời của Roman Makarov cho chủ đề này

public class HaversineAlgorithm {

    static final double _eQuatorialEarthRadius = 6378.1370D;
    static final double _d2r = (Math.PI / 180D);

    public static int HaversineInM(double lat1, double long1, double lat2, double long2) {
        return (int) (1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    public static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
                * Math.pow(Math.sin(dlong / 2D), 2D);
        double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;

        return d;
    }

}

@Radu đảm bảo bạn đang sử dụng chính xác và không trao đổi địa điểm lat / log khi chuyển chúng sang bất kỳ phương thức nào.
Paulo Miguel Almeida

1
Tôi đã có một câu trả lời hợp lý gần bằng cách sử dụng công thức này. Tôi dựa trên độ chính xác bằng cách sử dụng trang web này: Movable-type.co.uk/scripts/latlong.html đã cho tôi 0.07149km trong khi công thức của bạn cho tôi 0.07156độ chính xác khoảng 99%
Janac Meena

24

Điều này rất dễ thực hiện với loại địa lý trong SQL Server 2008.

SELECT geography::Point(lat1, lon1, 4326).STDistance(geography::Point(lat2, lon2, 4326))
-- computes distance in meters using eliptical model, accurate to the mm

4326 là SRID cho mô hình Trái đất linh tinh WGS84


19

Đây là hàm Haversine trong Python mà tôi sử dụng:

from math import pi,sqrt,sin,cos,atan2

def haversine(pos1, pos2):
    lat1 = float(pos1['lat'])
    long1 = float(pos1['long'])
    lat2 = float(pos2['lat'])
    long2 = float(pos2['long'])

    degree_to_rad = float(pi / 180.0)

    d_lat = (lat2 - lat1) * degree_to_rad
    d_long = (long2 - long1) * degree_to_rad

    a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2)
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    km = 6367 * c
    mi = 3956 * c

    return {"km":km, "miles":mi}

16

Nó phụ thuộc vào mức độ chính xác mà bạn cần, nếu bạn cần độ chính xác chính xác, tốt nhất là xem xét một thuật toán sử dụng ellipsoid, thay vì hình cầu, như thuật toán của Vincenty, chính xác đến mm. http://en.wikipedia.org/wiki/Vincenty%27s_alacticm


11

Đây là C # (lat và long tính bằng radian):

double CalculateGreatCircleDistance(double lat1, double long1, double lat2, double long2, double radius)
{
    return radius * Math.Acos(
        Math.Sin(lat1) * Math.Sin(lat2)
        + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(long2 - long1));
}

Nếu lat và long của bạn tính theo độ thì chia cho 180 / PI để chuyển đổi thành radian.


1
Đây là phép tính "luật hình cầu của vũ trụ", là phương pháp tính toán ít chính xác nhất và dễ bị lỗi nhất của khoảng cách vòng tròn lớn.
John Machin

11

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
def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295
    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))

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


11

Phiên bản PHP:

(Xóa tất cả deg2rad()nếu tọa độ của bạn đã được tính bằng radian.)

$R = 6371; // km
$dLat = deg2rad($lat2-$lat1);
$dLon = deg2rad($lon2-$lon1);
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);

$a = sin($dLat/2) * sin($dLat/2) +
     sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2); 

$c = 2 * atan2(sqrt($a), sqrt(1-$a)); 
$d = $R * $c;

1
Vui lòng thay đổi lat1 và lat2 thành $ lat1 nad $ lat2.
thay vì

7

Hàm T-SQL mà tôi sử dụng để chọn các bản ghi theo khoảng cách cho một trung tâm

Create Function  [dbo].[DistanceInMiles] 
 (  @fromLatitude float ,
    @fromLongitude float ,
    @toLatitude float, 
    @toLongitude float
  )
   returns float
AS 
BEGIN
declare @distance float

select @distance = cast((3963 * ACOS(round(COS(RADIANS(90-@fromLatitude))*COS(RADIANS(90-@toLatitude))+ 
SIN(RADIANS(90-@fromLatitude))*SIN(RADIANS(90-@toLatitude))*COS(RADIANS(@fromLongitude-@toLongitude)),15)) 
)as float) 
  return  round(@distance,1)
END

Đây là phép tính "luật hình cầu của vũ trụ", là phương pháp tính toán ít chính xác nhất và dễ bị lỗi nhất của khoảng cách vòng tròn lớn.
John Machin

5

Nếu bạn cần một cái gì đó chính xác hơn thì hãy xem cái này .

Công thức của Vincenty là hai phương pháp lặp có liên quan được sử dụng trong trắc địa để tính khoảng cách giữa hai điểm trên bề mặt của một hình cầu, được phát triển bởi Thaddeus Vincenty (1975a) Chúng dựa trên giả định rằng hình Trái đất là một hình cầu bắt buộc, và do đó chính xác hơn các phương pháp như khoảng cách vòng tròn lớn giả định Trái đất hình cầu.

Phương thức (trực tiếp) đầu tiên tính toán vị trí của một điểm là một khoảng cách nhất định và góc phương vị (hướng) từ một điểm khác. Phương pháp thứ hai (nghịch đảo) tính khoảng cách địa lý và góc phương vị giữa hai điểm đã cho. Chúng đã được sử dụng rộng rãi trong trắc địa vì chúng chính xác trong phạm vi 0,5 mm (0,020) trên trái đất ellipsoid.


5

I. Về phương pháp "Breadcrumbs"

  1. Bán kính trái đất là khác nhau trên Lat khác nhau. Điều này phải được xem xét trong thuật toán Haversine.
  2. Xem xét thay đổi vòng bi, biến đường thẳng thành vòm (dài hơn)
  3. Đưa tốc độ thay đổi vào tài khoản sẽ biến vòm thành hình xoắn ốc (dài hơn hoặc ngắn hơn vòm)
  4. Thay đổi độ cao sẽ biến các hình xoắn ốc phẳng thành hình xoắn ốc 3D (dài hơn một lần nữa). Điều này rất quan trọng đối với các khu vực đồi núi.

Dưới đây xem chức năng trong C có số 1 và số 2 vào tài khoản:

double   calcDistanceByHaversine(double rLat1, double rLon1, double rHeading1,
       double rLat2, double rLon2, double rHeading2){
  double rDLatRad = 0.0;
  double rDLonRad = 0.0;
  double rLat1Rad = 0.0;
  double rLat2Rad = 0.0;
  double a = 0.0;
  double c = 0.0;
  double rResult = 0.0;
  double rEarthRadius = 0.0;
  double rDHeading = 0.0;
  double rDHeadingRad = 0.0;

  if ((rLat1 < -90.0) || (rLat1 > 90.0) || (rLat2 < -90.0) || (rLat2 > 90.0)
              || (rLon1 < -180.0) || (rLon1 > 180.0) || (rLon2 < -180.0)
              || (rLon2 > 180.0)) {
        return -1;
  };

  rDLatRad = (rLat2 - rLat1) * DEGREE_TO_RADIANS;
  rDLonRad = (rLon2 - rLon1) * DEGREE_TO_RADIANS;
  rLat1Rad = rLat1 * DEGREE_TO_RADIANS;
  rLat2Rad = rLat2 * DEGREE_TO_RADIANS;

  a = sin(rDLatRad / 2) * sin(rDLatRad / 2) + sin(rDLonRad / 2) * sin(
              rDLonRad / 2) * cos(rLat1Rad) * cos(rLat2Rad);

  if (a == 0.0) {
        return 0.0;
  }

  c = 2 * atan2(sqrt(a), sqrt(1 - a));
  rEarthRadius = 6378.1370 - (21.3847 * 90.0 / ((fabs(rLat1) + fabs(rLat2))
              / 2.0));
  rResult = rEarthRadius * c;

  // Chord to Arc Correction based on Heading changes. Important for routes with many turns and U-turns

  if ((rHeading1 >= 0.0) && (rHeading1 < 360.0) && (rHeading2 >= 0.0)
              && (rHeading2 < 360.0)) {
        rDHeading = fabs(rHeading1 - rHeading2);
        if (rDHeading > 180.0) {
              rDHeading -= 180.0;
        }
        rDHeadingRad = rDHeading * DEGREE_TO_RADIANS;
        if (rDHeading > 5.0) {
              rResult = rResult * (rDHeadingRad / (2.0 * sin(rDHeadingRad / 2)));
        } else {
              rResult = rResult / cos(rDHeadingRad);
        }
  }
  return rResult;
}

II. Có một cách dễ dàng hơn mà cho kết quả khá tốt.

Theo tốc độ trung bình.

Trip_distance = Trip_alusive_speed * Trip_time

Vì Tốc độ GPS được phát hiện bằng hiệu ứng Doppler và không liên quan trực tiếp đến [Lon, Lat], nên ít nhất nó có thể được coi là thứ cấp (sao lưu hoặc hiệu chỉnh) nếu không phải là phương pháp tính khoảng cách chính.


4

Nếu bạn đang sử dụng .NET, đừng dùng lại bánh xe. Xem System.Device.Location . Tín dụng cho fnx trong các ý kiến ​​trong một câu trả lời khác .

using System.Device.Location;

double lat1 = 45.421527862548828D;
double long1 = -75.697189331054688D;
double lat2 = 53.64135D;
double long2 = -113.59273D;

GeoCoordinate geo1 = new GeoCoordinate(lat1, long1);
GeoCoordinate geo2 = new GeoCoordinate(lat2, long2);

double distance = geo1.GetDistanceTo(geo2);

3

Mã Lua này được điều chỉnh từ những thứ được tìm thấy trên Wikipedia và trong công cụ GPSbabel của Robert Lipe :

local EARTH_RAD = 6378137.0 
  -- earth's radius in meters (official geoid datum, not 20,000km / pi)

local radmiles = EARTH_RAD*100.0/2.54/12.0/5280.0;
  -- earth's radius in miles

local multipliers = {
  radians = 1, miles = radmiles, mi = radmiles, feet = radmiles * 5280,
  meters = EARTH_RAD, m = EARTH_RAD, km = EARTH_RAD / 1000, 
  degrees = 360 / (2 * math.pi), min = 60 * 360 / (2 * math.pi)
}

function gcdist(pt1, pt2, units) -- return distance in radians or given units
  --- this formula works best for points close together or antipodal
  --- rounding error strikes when distance is one-quarter Earth's circumference
  --- (ref: wikipedia Great-circle distance)
  if not pt1.radians then pt1 = rad(pt1) end
  if not pt2.radians then pt2 = rad(pt2) end
  local sdlat = sin((pt1.lat - pt2.lat) / 2.0);
  local sdlon = sin((pt1.lon - pt2.lon) / 2.0);
  local res = sqrt(sdlat * sdlat + cos(pt1.lat) * cos(pt2.lat) * sdlon * sdlon);
  res = res > 1 and 1 or res < -1 and -1 or res
  res = 2 * asin(res);
  if units then return res * assert(multipliers[units])
  else return res
  end
end

3
    private double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }

    private double GetDistance(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Distance in Kilo Meter
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = Math.Abs(Math.Round(rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344 * 1000, 0));
        return (dist);
    }

    private double GetDirection(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Direction in Degrees
        double dlat = deg2rad(lat1) - deg2rad(lat2);
        double dlon = deg2rad(lon1) - deg2rad(lon2);
        double y = Math.Sin(dlon) * Math.Cos(lat2);
        double x = Math.Cos(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) - Math.Sin(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(dlon);
        double direct = Math.Round(rad2deg(Math.Atan2(y, x)), 0);
        if (direct < 0)
            direct = direct + 360;
        return (direct);
    }

    private double GetSpeed(double lat1, double lon1, double lat2, double lon2, DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Round(TimeDifference.TotalSeconds, 0);
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344;
        double Speed = Math.Abs(Math.Round((dist / Math.Abs(TimeDifferenceInSeconds)) * 60 * 60, 0));
        return (Speed);
    }

    private double GetDuration(DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Abs(Math.Round(TimeDifference.TotalSeconds, 0));
        return (TimeDifferenceInSeconds);
    }

1
Tôi nghĩ rằng chức năng của bạn GetDistance trả về giá trị tính bằng mét
Przemek

Điều này có đúng không? GetDirection () không sử dụng 'dlat'.
gubby

3

Đây là phiên bản từ "Henry Vilinskiy" được điều chỉnh cho MySQL và Kilômét:

CREATE FUNCTION `CalculateDistanceInKm`(
  fromLatitude float,
  fromLongitude float,
  toLatitude float, 
  toLongitude float
) RETURNS float
BEGIN
  declare distance float;

  select 
    6367 * ACOS(
            round(
              COS(RADIANS(90-fromLatitude)) *
                COS(RADIANS(90-toLatitude)) +
                SIN(RADIANS(90-fromLatitude)) *
                SIN(RADIANS(90-toLatitude)) *
                COS(RADIANS(fromLongitude-toLongitude))
              ,15)
            )
    into distance;

  return  round(distance,3);
END;

MySQLđã nóiSomething is wrong in your syntax near '' on line 8 // declare distance float;
Legionar

Đây là phép tính "luật hình cầu của vũ trụ", là phương pháp tính toán ít chính xác nhất và dễ bị lỗi nhất trong khoảng cách vòng tròn lớn
John Machin

3

Đây là cách triển khai Swift từ câu trả lời

func degreesToRadians(degrees: Double) -> Double {
    return degrees * Double.pi / 180
}

func distanceInKmBetweenEarthCoordinates(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {

    let earthRadiusKm: Double = 6371

    let dLat = degreesToRadians(degrees: lat2 - lat1)
    let dLon = degreesToRadians(degrees: lon2 - lon1)

    let lat1 = degreesToRadians(degrees: lat1)
    let lat2 = degreesToRadians(degrees: lat2)

    let a = sin(dLat/2) * sin(dLat/2) +
    sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2)
    let c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return earthRadiusKm * c
}

3

tôi đã trả lời hàng đầu và sử dụng nó trong một chương trình Scala

import java.lang.Math.{atan2, cos, sin, sqrt}

def latLonDistance(lat1: Double, lon1: Double)(lat2: Double, lon2: Double): Double = {
    val earthRadiusKm = 6371
    val dLat = (lat2 - lat1).toRadians
    val dLon = (lon2 - lon1).toRadians
    val latRad1 = lat1.toRadians
    val latRad2 = lat2.toRadians

    val a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(latRad1) * cos(latRad2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    earthRadiusKm * c
}

tôi đã chỉnh sửa hàm để có thể dễ dàng tạo ra các hàm có một trong hai vị trí cố định và chỉ cần một cặp lat / lon để tạo khoảng cách.


2

Tôi đoán bạn muốn nó dọc theo độ cong của trái đất. Hai điểm của bạn và tâm trái đất nằm trên một mặt phẳng. Tâm của trái đất là tâm của một đường tròn trên mặt phẳng đó và hai điểm nằm (gần như) trên chu vi của vòng tròn đó. Từ đó bạn có thể tính khoảng cách bằng cách tìm ra góc từ điểm này đến điểm kia là gì.

Nếu các điểm không có cùng độ cao hoặc nếu bạn 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 thì sẽ khó khăn hơn một chút.


2

Gần đây tôi đã phải làm điều tương tự. Tôi thấy trang web này rất hữu ích để giải thích trig hình cầu với các ví dụ dễ theo dõi cùng.


2

bạn có thể tìm thấy cách thực hiện điều này (với một số giải thích tốt) trong F # trên fssnip

Đây là những phần quan trọng:


let GreatCircleDistance<[<Measure>] 'u> (R : float<'u>) (p1 : Location) (p2 : Location) =
    let degToRad (x : float<deg>) = System.Math.PI * x / 180.0<deg/rad>

    let sq x = x * x
    // take the sin of the half and square the result
    let sinSqHf (a : float<rad>) = (System.Math.Sin >> sq) (a / 2.0<rad>)
    let cos (a : float<deg>) = System.Math.Cos (degToRad a / 1.0<rad>)

    let dLat = (p2.Latitude - p1.Latitude) |> degToRad
    let dLon = (p2.Longitude - p1.Longitude) |> degToRad

    let a = sinSqHf dLat + cos p1.Latitude * cos p2.Latitude * sinSqHf dLon
    let c = 2.0 * System.Math.Atan2(System.Math.Sqrt(a), System.Math.Sqrt(1.0-a))

    R * c

2

Tôi cần thực hiện điều này trong PowerShell, hy vọng nó có thể giúp đỡ người khác. Một số lưu ý về phương pháp này

  1. Đừng phân chia bất kỳ dòng nào hoặc tính toán sẽ sai
  2. Để tính bằng KM, hãy xóa * 1000 trong phép tính khoảng cách $
  3. Thay đổi $ earthsRadius = 3963,19059 và remove * 1000 trong tính toán của $ khoảng cách để calulate khoảng cách trong dặm
  4. Tôi đang sử dụng Haversine, vì các bài đăng khác đã chỉ ra công thức của Vincenty chính xác hơn nhiều

    Function MetresDistanceBetweenTwoGPSCoordinates($latitude1, $longitude1, $latitude2, $longitude2)  
    {  
      $Rad = ([math]::PI / 180);  
    
      $earthsRadius = 6378.1370 # Earth's Radius in KM  
      $dLat = ($latitude2 - $latitude1) * $Rad  
      $dLon = ($longitude2 - $longitude1) * $Rad  
      $latitude1 = $latitude1 * $Rad  
      $latitude2 = $latitude2 * $Rad  
    
      $a = [math]::Sin($dLat / 2) * [math]::Sin($dLat / 2) + [math]::Sin($dLon / 2) * [math]::Sin($dLon / 2) * [math]::Cos($latitude1) * [math]::Cos($latitude2)  
      $c = 2 * [math]::ATan2([math]::Sqrt($a), [math]::Sqrt(1-$a))  
    
      $distance = [math]::Round($earthsRadius * $c * 1000, 0) #Multiple by 1000 to get metres  
    
      Return $distance  
    }
    

2

Phiên bản Scala

  def deg2rad(deg: Double) = deg * Math.PI / 180.0

  def rad2deg(rad: Double) = rad / Math.PI * 180.0

  def getDistanceMeters(lat1: Double, lon1: Double, lat2: Double, lon2: Double) = {
    val theta = lon1 - lon2
    val dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) *
      Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta))
    Math.abs(
      Math.round(
        rad2deg(Math.acos(dist)) * 60 * 1.1515 * 1.609344 * 1000)
    )
  }

1

// Có thể là một lỗi đánh máy?
Chúng tôi có một dlon biến không được sử dụng trong GetDirection,
tôi giả sử

double y = Math.Sin(dlon) * Math.Cos(lat2);
// cannot use degrees in Cos ?

nên là

double y = Math.Sin(dlon) * Math.Cos(dlat);

1
Đây không phải là một câu trả lời, tốt nhất là một nhận xét.
Kevin

1

Đây là triển khai của tôi trong Elixir

defmodule Geo do
  @earth_radius_km 6371
  @earth_radius_sm 3958.748
  @earth_radius_nm 3440.065
  @feet_per_sm 5280

  @d2r :math.pi / 180

  def deg_to_rad(deg), do: deg * @d2r

  def great_circle_distance(p1, p2, :km), do: haversine(p1, p2) * @earth_radius_km
  def great_circle_distance(p1, p2, :sm), do: haversine(p1, p2) * @earth_radius_sm
  def great_circle_distance(p1, p2, :nm), do: haversine(p1, p2) * @earth_radius_nm
  def great_circle_distance(p1, p2, :m), do: great_circle_distance(p1, p2, :km) * 1000
  def great_circle_distance(p1, p2, :ft), do: great_circle_distance(p1, p2, :sm) * @feet_per_sm

  @doc """
  Calculate the [Haversine](https://en.wikipedia.org/wiki/Haversine_formula)
  distance between two coordinates. Result is in radians. This result can be
  multiplied by the sphere's radius in any unit to get the distance in that unit.
  For example, multiple the result of this function by the Earth's radius in
  kilometres and you get the distance between the two given points in kilometres.
  """
  def haversine({lat1, lon1}, {lat2, lon2}) do
    dlat = deg_to_rad(lat2 - lat1)
    dlon = deg_to_rad(lon2 - lon1)

    radlat1 = deg_to_rad(lat1)
    radlat2 = deg_to_rad(lat2)

    a = :math.pow(:math.sin(dlat / 2), 2) +
        :math.pow(:math.sin(dlon / 2), 2) *
        :math.cos(radlat1) * :math.cos(radlat2)

    2 * :math.atan2(:math.sqrt(a), :math.sqrt(1 - a))
  end
end

1

Phiên bản phi tiêu

Thuật toán Haversine.

import 'dart:math';

class GeoUtils {

  static double _degreesToRadians(degrees) {
    return degrees * pi / 180;
  }

  static double distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
    var earthRadiusKm = 6371;

    var dLat = _degreesToRadians(lat2-lat1);
    var dLon = _degreesToRadians(lon2-lon1);

    lat1 = _degreesToRadians(lat1);
    lat2 = _degreesToRadians(lat2);

    var a = sin(dLat/2) * sin(dLat/2) +
        sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2);
    var c = 2 * atan2(sqrt(a), sqrt(1-a));
    return earthRadiusKm * c;
  }
}

0

Tôi nghĩ rằng một phiên bản của thuật toán trong R vẫn còn thiếu:

gpsdistance<-function(lat1,lon1,lat2,lon2){

# internal function to change deg to rad

degreesToRadians<- function (degrees) {
return (degrees * pi / 180)
}

R<-6371e3  #radius of Earth in meters

phi1<-degreesToRadians(lat1) # latitude 1
phi2<-degreesToRadians(lat2) # latitude 2
lambda1<-degreesToRadians(lon1) # longitude 1
lambda2<-degreesToRadians(lon2) # longitude 2

delta_phi<-phi1-phi2 # latitude-distance
delta_lambda<-lambda1-lambda2 # longitude-distance

a<-sin(delta_phi/2)*sin(delta_phi/2)+
cos(phi1)*cos(phi2)*sin(delta_lambda/2)*
sin(delta_lambda/2)

cc<-2*atan2(sqrt(a),sqrt(1-a))

distance<- R * cc

return(distance)  # in meters
}

0

Đây là một biến thể của Kotlin:

import kotlin.math.*

class HaversineAlgorithm {

    companion object {
        private const val MEAN_EARTH_RADIUS = 6371.0
        private const val D2R = Math.PI / 180.0
    }

    private fun haversineInKm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
        val lonDiff = (lon2 - lon1) * D2R
        val latDiff = (lat2 - lat1) * D2R
        val latSin = sin(latDiff / 2.0)
        val lonSin = sin(lonDiff / 2.0)
        val a = latSin * latSin + (cos(lat1 * D2R) * cos(lat2 * D2R) * lonSin * lonSin)
        val c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
        return EQATORIAL_EARTH_RADIUS * c
    }
}

Tại sao bạn sử dụng bán kính xích đạo thay vì bán kính trái đất trung bình?
dùng13044086

@ user13044086 Câu hỏi hay. Đó là bởi vì tôi bắt nguồn từ phiên bản Java của Paulo Miguel Almeida. Có vẻ như phiên bản C # cũng đang sử dụng khoảng cách đó. Các phiên bản khác ở đây có 6371, nhưng sau đó bạn phải nhận ra rằng tất cả các thuật toán này có thể không xử lý hoàn hảo hình dạng Geoid của Trái đất. Vui lòng sửa đổi điều này và sử dụng 6371. Nếu bạn nói với tôi điều đó dẫn đến các giá trị chính xác hơn, tôi sẽ thay đổi câu trả lời của mình.
Csaba Toth

1
6371.008 thường được sử dụng vì nó giảm thiểu lỗi tương đối của công thức như được giải thích trong ghi chú trên trang Movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

Đủ công bằng! Tôi sẽ chỉnh sửa câu trả lời của tôi vào ngày mai
Csaba Toth

@ user13044086 Cảm ơn liên kết, tôi đã chỉnh sửa câu trả lời của mình cách đây một thời gian dựa trên đó
Csaba Toth
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.