计算两个纬度和经度地理坐标之间的距离

Jas*_*ord 126 c# geocoding latitude-longitude windows-phone-7

我正在计算两个GeoCoordinates之间的距离.我正在针对其他3-4个应用测试我的应用.当我计算距离时,我计算得到的平均值为3.3英里,而其他应用程序的距离为3.5英里.这与我正在尝试执行的计算有很大不同.有没有好的类库来计算距离?我在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;
}
Run Code Online (Sandbox Code Playgroud)

我能做错什么?我应该先以km为单位计算,然后转换为里程?

Nig*_*son 294

会有地理座标类(.NET框架4和更高)已经有GetDistanceTo方法.

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

return sCoord.GetDistanceTo(eCoord);
Run Code Online (Sandbox Code Playgroud)

距离以米为单位.

您需要引用System.Device.

  • https://www.nuget.org/packages/GeoCoordinate.NetCore/将其用于.net核心 (4认同)
  • @SaeedNeamati - 也在寻找这个,根据https://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate.getdistanceto(v=vs.110).aspx-它以米为单位. (3认同)

Dav*_*ner 98

GetDistance是最好的解决方案,但在很多情况下我们无法使用此方法(例如通用应用程序)

  • 用于计算 coorindates之间距离的算法的伪代码:

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 真实世界C#实现,它使用扩展方法

    用法:

    var distance = new Coordinates(48.672309, 15.695585)
                    .DistanceTo(
                        new Coordinates(48.237867, 16.389477),
                        UnitOfLength.Kilometers
                    );
    
    Run Code Online (Sandbox Code Playgroud)

    执行:

    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;
        }
    } 
    
    Run Code Online (Sandbox Code Playgroud)

  • 计算正确率95%。以下函数 100% 准确:/sf/answers/3628734091/ (2认同)

Ell*_*ood 16

这是JavaScript版本的男人和女孩

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
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*arc 13

在这里,对于那些仍然不满意的人,.NET-Frameworks GeoCoordinate类中的原始代码被重构为一个独立的方法:

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)));
}
Run Code Online (Sandbox Code Playgroud)

  • 小的优化,或者为了更容易阅读,可以预先计算 pi/180 `double oneDegree = Math.PI / 180.0;` ? (4认同)
  • 美丽的答案,我想指出的是,距离以米为单位。如[官方文档](https://docs.microsoft.com/it-it/dotnet/api/system.device.location.geocoordinate.getdistanceto?view=netframework-4.8#System_Device_Location_GeoCoordinate_GetDistanceTo_System_Device_Location_GeoCoordinate_)所述 (2认同)
  • @brakeroo 感谢您的回复。我想保留答案,因为这是原始的 .NET 代码。当然,任何人都可以随意遵循您的建议。 (2认同)

Jus*_*tin 10

对于那些使用Xamarin并且无法访问GeoCoordinate类的人,可以使用Android Location类:

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);
        }
Run Code Online (Sandbox Code Playgroud)


Jan*_*anW 10

这是一个老问题,但在性能和优化方面,答案并不能令我满意。

\n\n

这是我优化的 C# 变体(距离以公里为单位,没有变量和冗余计算,非常接近半正弦公式的数学表达式https://en.wikipedia.org/wiki/Haversine_formula)。

\n\n

灵感来源:\n https://rosettacode.org/wiki/Haversine_formula#C.23

\n\n
public static class Haversine\n{\n    public static double Calculate(double lat1, double lon1, double lat2, double lon2)\n    {\n        double rad(double angle) => angle * 0.017453292519943295769236907684886127d; // = angle * Math.Pi / 180.0d\n        double havf(double diff) => Math.Pow(Math.Sin(rad(diff) / 2d), 2); // = sin\xc2\xb2(diff / 2)\n        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\xe2\x80\xackm x 2 = 12745.6\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

来自维基百科的半正矢公式

\n


A. *_*rel 7

有适用于这些平台的地理坐标库:

  • 单核细胞增多症
  • .NET 4.5
  • .NET核心
  • Windows Phone 8.x
  • 通用Windows平台
  • Xamarin iOS
  • Xamarin Android

安装是通过 NuGet 完成的:

PM> 安装包 GeoCoordinate

用法

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

double distanceBetween = pin1.GetDistanceTo(pin2);
Run Code Online (Sandbox Code Playgroud)

两个坐标之间的距离,以为单位。


V. *_*ler 5

当CPU/数学计算能力有限时:

有时(例如在我的工作中)计算能力稀缺(例如没有浮点处理器,使用小型微控制器),某些三角函数可能会占用大量 CPU 时间(例如 3000+ 时钟周期),所以当我只需要一个近似值,特别是如果 CPU 不能长时间占用的话,我用它来最小化 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. */
}
Run Code Online (Sandbox Code Playgroud)

信用转到https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt