Tính khoảng cách giữa hai GeoCoord tọa độ vĩ độ và kinh độ


139

Tôi đang tính khoảng cách giữa hai GeoCoordins. Tôi đang thử nghiệm ứng dụng của mình với 3-4 ứng dụng khác. Khi tôi đang tính toán khoảng cách, tôi có xu hướng để có được trung bình 3,3 dặm tính của tôi trong khi các ứng dụng khác đang nhận được 3,5 dặm. Đó là một sự khác biệt lớn cho tính toán mà tôi đang cố gắng thực hiện. Có thư viện lớp nào tốt để tính khoảng cách không? Tôi đang tính toán như thế này trong C #:

public static double Calculate(double sLatitude,double sLongitude, double eLatitude, 
                               double eLongitude)
{
    var radiansOverDegrees = (Math.PI / 180.0);

    var sLatitudeRadians = sLatitude * radiansOverDegrees;
    var sLongitudeRadians = sLongitude * radiansOverDegrees;
    var eLatitudeRadians = eLatitude * radiansOverDegrees;
    var eLongitudeRadians = eLongitude * radiansOverDegrees;

    var dLongitude = eLongitudeRadians - sLongitudeRadians;
    var dLatitude = eLatitudeRadians - sLatitudeRadians;

    var result1 = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) + 
                  Math.Cos(sLatitudeRadians) * Math.Cos(eLatitudeRadians) * 
                  Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);

    // Using 3956 as the number of miles around the earth
    var result2 = 3956.0 * 2.0 * 
                  Math.Atan2(Math.Sqrt(result1), Math.Sqrt(1.0 - result1));

    return result2;
}

Tôi có thể làm gì sai? Tôi có nên tính toán nó ở km đầu tiên và sau đó chuyển đổi sang dặm?


1
Trái đất có nghĩa là bán kính = 6,371km = 3958,76 dặm
Mitch Wheat


không nên có trên gis.stackexchange.com
Daniel Powell

Nó có thể có, nhưng câu hỏi của tôi liên quan nhiều hơn đến việc tính toán điều này trên Windows Phone, một chút khác biệt. Công thức là như nhau, nhưng các phương thức mới hơn gọi như phương thức Khoảng cách không nhất thiết phải có sẵn.
Jason N. Gaylord

1
Đề nghị bạn lưu trữ pi / 180 để bạn không phải lặp lại phép tính.
Chris Caviness

Câu trả lời:


313

Lớp GeoCoordine (.NET Framework 4 trở lên) đã có GetDistanceTophương thức.

var sCoord = new GeoCoordinate(sLatitude, sLongitude);
var eCoord = new GeoCoordinate(eLatitude, eLongitude);

return sCoord.GetDistanceTo(eCoord);

Khoảng cách tính bằng mét.

Bạn cần tham khảo System.Device.


Nigel, bạn có chắc chắn rằng phương thức Khoảng cách sẽ hoạt động trên điện thoại? Tôi nghĩ rằng nó đã sử dụng phiên bản 2.0 của GeoCoordine cho WP7.
Jason N. Gaylord

1
Tôi đã kiểm tra điều này và GeoCordine cho thiết bị có phương thức GetDistanceTo là những gì bạn đã tham chiếu (nhưng không phải những gì bạn có ở trên). Không sao đâu. Tôi sẽ kiểm tra điều này để xem liệu tính toán tích hợp có tốt hơn không. Cảm ơn Nigel!
Jason N. Gaylord

1
Tôi có thể hỏi một câu hỏi sai, nhưng kết quả là ở đơn vị nào? Là nó Miles, hoặc Km. Tôi không thể tìm thấy nó ở bất cứ đâu.
Saeed Neamati

3
@SaeedNeamati - cũng đang tìm kiếm điều này, theo msdn.microsoft.com/en-us/l Library / trộm - nó tính bằng mét.
Andy Butland

Có, GeoCoordinate.GetDistanceTo () trả về giá trị tính bằng mét. Đối với tôi, ở Mỹ, nếu nó ít hơn 1610, tôi chuyển nó sang feet (mét * 3,28084) nếu không tôi chuyển đổi sang dặm (mét * 0,000621371). Độ chính xác là quá đủ tốt cho mục đích của tôi.
dùng3235770

110

GetDistance là giải pháp tốt nhất , nhưng trong nhiều trường hợp, chúng tôi không thể sử dụng Phương thức này (ví dụ: Ứng dụng toàn cầu)

  • Mã giả của Thuật toán để tính khoảng cách giữa các đồng biến:

    public static double DistanceTo(double lat1, double lon1, double lat2, double lon2, char unit = 'K')
    {
        double rlat1 = Math.PI*lat1/180;
        double rlat2 = Math.PI*lat2/180;
        double theta = lon1 - lon2;
        double rtheta = Math.PI*theta/180;
        double dist =
            Math.Sin(rlat1)*Math.Sin(rlat2) + Math.Cos(rlat1)*
            Math.Cos(rlat2)*Math.Cos(rtheta);
        dist = Math.Acos(dist);
        dist = dist*180/Math.PI;
        dist = dist*60*1.1515;
    
        switch (unit)
        {
            case 'K': //Kilometers -> default
                return dist*1.609344;
            case 'N': //Nautical Miles 
                return dist*0.8684;
            case 'M': //Miles
                return dist;
        }
    
        return dist;
    }
  • Real World C # Thực hiện , sử dụng Phương thức mở rộng

    Sử dụng:

    var distance = new Coordinates(48.672309, 15.695585)
                    .DistanceTo(
                        new Coordinates(48.237867, 16.389477),
                        UnitOfLength.Kilometers
                    );

    Thực hiện:

    public class Coordinates
    {
        public double Latitude { get; private set; }
        public double Longitude { get; private set; }
    
        public Coordinates(double latitude, double longitude)
        {
            Latitude = latitude;
            Longitude = longitude;
        }
    }
    public static class CoordinatesDistanceExtensions
    {
        public static double DistanceTo(this Coordinates baseCoordinates, Coordinates targetCoordinates)
        {
            return DistanceTo(baseCoordinates, targetCoordinates, UnitOfLength.Kilometers);
        }
    
        public static double DistanceTo(this Coordinates baseCoordinates, Coordinates targetCoordinates, UnitOfLength unitOfLength)
        {
            var baseRad = Math.PI * baseCoordinates.Latitude / 180;
            var targetRad = Math.PI * targetCoordinates.Latitude/ 180;
            var theta = baseCoordinates.Longitude - targetCoordinates.Longitude;
            var thetaRad = Math.PI * theta / 180;
    
            double dist =
                Math.Sin(baseRad) * Math.Sin(targetRad) + Math.Cos(baseRad) *
                Math.Cos(targetRad) * Math.Cos(thetaRad);
            dist = Math.Acos(dist);
    
            dist = dist * 180 / Math.PI;
            dist = dist * 60 * 1.1515;
    
            return unitOfLength.ConvertFromMiles(dist);
        }
    }
    
    public class UnitOfLength
    {
        public static UnitOfLength Kilometers = new UnitOfLength(1.609344);
        public static UnitOfLength NauticalMiles = new UnitOfLength(0.8684);
        public static UnitOfLength Miles = new UnitOfLength(1);
    
        private readonly double _fromMilesFactor;
    
        private UnitOfLength(double fromMilesFactor)
        {
            _fromMilesFactor = fromMilesFactor;
        }
    
        public double ConvertFromMiles(double input)
        {
            return input*_fromMilesFactor;
        }
    } 

1
Bạn có thể cung cấp công thức được sử dụng cho phép tính này hoặc có thể một số nhận xét về dòng nào không? những gì tôi sẽ phải thay đổi trực tiếp có khoảng cách dẫn đến Km thay vì dặm mà không cần phải chuyển đổi?
AlbertoFdzM

Cảm ơn về một giải pháp tốt, giờ tôi có thể sử dụng nó trong ứng dụng Desktop của mình.
Jamshaid Kamran

Hoạt động rất tốt trong ứng dụng UWP của tôi, nơi tôi không thể sử dụng GeoCoordine.
Zach Green

1
tính toán đúng 95%. chức năng dưới đây chính xác 100%: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht

31

Và ở đây, đối với những người vẫn chưa hài lòng, mã gốc từ GeoCoordinatelớp .NET-Frameworks , được tái cấu trúc thành một phương thức độc lập:

public double GetDistance(double longitude, double latitude, double otherLongitude, double otherLatitude)
{
    var d1 = latitude * (Math.PI / 180.0);
    var num1 = longitude * (Math.PI / 180.0);
    var d2 = otherLatitude * (Math.PI / 180.0);
    var num2 = otherLongitude * (Math.PI / 180.0) - num1;
    var d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);

    return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3)));
}

8
Câu trả lời đẹp, tôi muốn chỉ ra rằng khoảng cách kết quả là tính bằng mét. như đã nêu trong tài liệu chính thức
LeviathanCode

Cảm ơn! Tôi đang tìm kiếm bán kính thực tế của trái đất được sử dụng trong lớp GeoCoordine.
KRoy

Tối ưu hóa nhỏ, hoặc để dễ đọc hơn, có thể tính toán trước pi / 180 double oneDegree = Math.PI / 180.0;không?
brakeroo

1
@brakeroo Cảm ơn bạn đã trả lời. Tôi muốn để lại câu trả lời, vì đây là mã .NET gốc. Tất nhiên bất cứ ai cũng có thể làm theo đề nghị của bạn.
Marc

17

Đây là phiên bản JavaScript kẻ và các cô gái

function distanceTo(lat1, lon1, lat2, lon2, unit) {
      var rlat1 = Math.PI * lat1/180
      var rlat2 = Math.PI * lat2/180
      var rlon1 = Math.PI * lon1/180
      var rlon2 = Math.PI * lon2/180
      var theta = lon1-lon2
      var rtheta = Math.PI * theta/180
      var dist = Math.sin(rlat1) * Math.sin(rlat2) + Math.cos(rlat1) * Math.cos(rlat2) * Math.cos(rtheta);
      dist = Math.acos(dist)
      dist = dist * 180/Math.PI
      dist = dist * 60 * 1.1515
      if (unit=="K") { dist = dist * 1.609344 }
      if (unit=="N") { dist = dist * 0.8684 }
      return dist
}

10

Đối với những người đang sử dụng Xamarin và không có quyền truy cập vào lớp GeoCoordine, bạn có thể sử dụng lớp Vị trí Android thay thế:

public static double GetDistanceBetweenCoordinates (double lat1, double lng1, double lat2, double lng2) {
            var coords1 = new Location ("");
            coords1.Latitude = lat1;
            coords1.Longitude = lng1;
            var coords2 = new Location ("");
            coords2.Latitude = lat2;
            coords2.Longitude = lng2;
            return coords1.DistanceTo (coords2);
        }

3

Bạn có thể sử dụng chức năng này:

Nguồn: https://www.geodatasource.com/developers/c-sharp

private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
  if ((lat1 == lat2) && (lon1 == lon2)) {
    return 0;
  }
  else {
    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.Acos(dist);
    dist = rad2deg(dist);
    dist = dist * 60 * 1.1515;
    if (unit == 'K') {
      dist = dist * 1.609344;
    } else if (unit == 'N') {
      dist = dist * 0.8684;
    }
    return (dist);
  }
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//::  This function converts decimal degrees to radians             :::
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
private double deg2rad(double deg) {
  return (deg * Math.PI / 180.0);
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//::  This function converts radians to decimal degrees             :::
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
private double rad2deg(double rad) {
  return (rad / Math.PI * 180.0);
}

Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "M"));
Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "K"));
Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "N"));

Hoạt động hoàn hảo! Cảm ơn!
Schnapz

3

Có thư viện này GeoCoordine cho các nền tảng này:

  • Bệnh tăng bạch cầu đơn nhân
  • .NET 4.5
  • Lõi .NET
  • Điện thoại Windows 8.x
  • Nền tảng Windows phổ quát
  • Xamarin iOS
  • Xamarin Android

Cài đặt được thực hiện thông qua NuGet:

PM> Cài đặt gói GeoCoordine

Sử dụng

GeoCoordinate pin1 = new GeoCoordinate(lat, lng);
GeoCoordinate pin2 = new GeoCoordinate(lat, lng);

double distanceBetween = pin1.GetDistanceTo(pin2);

Khoảng cách giữa hai tọa độ, tính bằng mét .


3

Dựa trên chức năng của Elliot Wood và nếu có ai quan tâm đến chức năng C, thì chức năng này đang hoạt động ...

#define SIM_Degree_to_Radian(x) ((float)x * 0.017453292F)
#define SIM_PI_VALUE                         (3.14159265359)

float GPS_Distance(float lat1, float lon1, float lat2, float lon2)
{
   float theta;
   float dist;

   theta = lon1 - lon2;

   lat1 = SIM_Degree_to_Radian(lat1);
   lat2 = SIM_Degree_to_Radian(lat2);
   theta = SIM_Degree_to_Radian(theta);

   dist = (sin(lat1) * sin(lat2)) + (cos(lat1) * cos(lat2) * cos(theta));
   dist = acos(dist);

//   dist = dist * 180.0 / SIM_PI_VALUE;
//   dist = dist * 60.0 * 1.1515;
//   /* Convert to km */
//   dist = dist * 1.609344;

   dist *= 6370.693486F;

   return (dist);
}

Bạn có thể thay đổi nó thành gấp đôi . Nó trả về giá trị tính bằng km.


2

Tính khoảng cách giữa các điểm Vĩ độ và Kinh độ ...

        double Lat1 = Convert.ToDouble(latitude);
        double Long1 = Convert.ToDouble(longitude);

        double Lat2 = 30.678;
        double Long2 = 45.786;
        double circumference = 40000.0; // Earth's circumference at the equator in km
        double distance = 0.0;
        double latitude1Rad = DegreesToRadians(Lat1);
        double latititude2Rad = DegreesToRadians(Lat2);
        double longitude1Rad = DegreesToRadians(Long1);
        double longitude2Rad = DegreesToRadians(Long2);
        double logitudeDiff = Math.Abs(longitude1Rad - longitude2Rad);
        if (logitudeDiff > Math.PI)
        {
            logitudeDiff = 2.0 * Math.PI - logitudeDiff;
        }
        double angleCalculation =
            Math.Acos(
              Math.Sin(latititude2Rad) * Math.Sin(latitude1Rad) +
              Math.Cos(latititude2Rad) * Math.Cos(latitude1Rad) * Math.Cos(logitudeDiff));
        distance = circumference * angleCalculation / (2.0 * Math.PI);
        return distance;

1

Đây là một câu hỏi cũ, tuy nhiên các câu trả lời không làm tôi hài lòng về hiệu suất và tối ưu hóa.

Ở đây, biến thể C # được tối ưu hóa của tôi (khoảng cách tính bằng km, không có biến và tính toán dự phòng, rất gần với biểu thức toán học của Haversine Formular https://en.wikipedia.org/wiki/Haversine_formula ).

Lấy cảm hứng từ: https://rosettacode.org/wiki/Haversine_formula#C.23

public static class Haversine
{
    public static double Calculate(double lat1, double lon1, double lat2, double lon2)
    {
        double rad(double angle) => angle * 0.017453292519943295769236907684886127d; // = angle * Math.Pi / 180.0d
        double havf(double diff) => Math.Pow(Math.Sin(rad(diff) / 2d), 2); // = sin²(diff / 2)
        return 12745.6 * Math.Asin(Math.Sqrt(havf(lat2 - lat1) + Math.Cos(rad(lat1)) * Math.Cos(rad(lat2)) * havf(lon2 - lon1))); // earth radius 6.372,8‬km x 2 = 12745.6
    }
}

Haversine Formular từ Wikipedia


0

Thử cái này:

    public double getDistance(GeoCoordinate p1, GeoCoordinate p2)
    {
        double d = p1.Latitude * 0.017453292519943295;
        double num3 = p1.Longitude * 0.017453292519943295;
        double num4 = p2.Latitude * 0.017453292519943295;
        double num5 = p2.Longitude * 0.017453292519943295;
        double num6 = num5 - num3;
        double num7 = num4 - d;
        double num8 = Math.Pow(Math.Sin(num7 / 2.0), 2.0) + ((Math.Cos(d) * Math.Cos(num4)) * Math.Pow(Math.Sin(num6 / 2.0), 2.0));
        double num9 = 2.0 * Math.Atan2(Math.Sqrt(num8), Math.Sqrt(1.0 - num8));
        return (6376500.0 * num9);
    }

0

Bạn có thể sử dụng System.device.Location:

System.device.Location.GeoCoordinate gc = new System.device.Location.GeoCoordinate(){
Latitude = yourLatitudePt1,
Longitude = yourLongitudePt1
};

System.device.Location.GeoCoordinate gc2 = new System.device.Location.GeoCoordinate(){
Latitude = yourLatitudePt2,
Longitude = yourLongitudePt2
};

Double distance = gc2.getDistanceTo(gc);

chúc may mắn


0

Khi sức mạnh tính toán CPU / toán học bị hạn chế:

Có những lúc (chẳng hạn như trong công việc của tôi) khi sức mạnh tính toán khan hiếm (ví dụ: không có bộ xử lý dấu phẩy động, làm việc với các bộ vi điều khiển nhỏ) trong đó một số chức năng trig có thể mất một lượng thời gian CPU (ví dụ 3000+ chu kỳ xung nhịp), vì vậy khi tôi chỉ cần một xấp xỉ, đặc biệt là nếu CPU không được gắn trong một thời gian dài, tôi sử dụng điều này để giảm thiểu chi phí CPU:

/**------------------------------------------------------------------------
 * \brief  Great Circle distance approximation in km over short distances.
 *
 * Can be off by as much as 10%.
 *
 * approx_distance_in_mi = sqrt(x * x + y * y)
 *
 * where x = 69.1 * (lat2 - lat1)
 * and y = 69.1 * (lon2 - lon1) * cos(lat1/57.3)
 *//*----------------------------------------------------------------------*/
double    ApproximateDisatanceBetweenTwoLatLonsInKm(
                  double lat1, double lon1,
                  double lat2, double lon2
                  ) {
    double  ldRadians, ldCosR, x, y;

    ldRadians = (lat1 / 57.3) * 0.017453292519943295769236907684886;
    ldCosR = cos(ldRadians);
    x = 69.1 * (lat2 - lat1);
    y = 69.1 * (lon2 - lon1) * ldCosR;

    return sqrt(x * x + y * y) * 1.609344;  /* Converts mi to km. */
}

Tín dụng truy cập https://github.com/kristianmandrup/geo_vector/blob/master/Distance%20calc%20notes.txt .

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.