การคำนวณระยะทางระหว่างสองละติจูดและลองจิจูด GeoCoordinates


139

ฉันคำนวณระยะทางระหว่างสอง 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;
}

ฉันทำอะไรผิดได้ ฉันควรคำนวณเป็นกิโลเมตรก่อนแล้วแปลงเป็นไมล์หรือไม่


1
รัศมีเฉลี่ยโลก = 6,371km = 3958.76 ไมล์
มิทช์ข้าวสาลี


สิ่งนี้ไม่ควรอยู่ใน gis.stackexchange.com
Daniel Powell

อาจมี แต่คำถามของฉันเกี่ยวข้องกับการคำนวณนี้บน Windows Phone ซึ่งแตกต่างกันเล็กน้อย สูตรเหมือนกัน แต่วิธีการที่ใหม่กว่าเรียกเช่นวิธี DistanceTo ไม่จำเป็นต้องมี
Jason N. Gaylord

1
แนะนำให้คุณเก็บ pi / 180 เพื่อที่คุณจะได้ไม่ต้องคำนวณซ้ำ
Chris Caviness

คำตอบ:


313

GeoCoordinateระดับ (.NET Framework 4 และสูงกว่า) แล้วมีGetDistanceToวิธีการ

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

return sCoord.GetDistanceTo(eCoord);

ระยะทางเป็นเมตร

คุณต้องอ้างอิง System.Device


ไนเจลคุณแน่ใจหรือไม่ว่าวิธี DistanceTo ใช้งานได้บนโทรศัพท์ ฉันคิดว่ามันใช้ GeoCoordinate รุ่น 2.0 สำหรับ WP7
Jason N. Gaylord

1
ฉันตรวจสอบสิ่งนี้และ GeoCordinate สำหรับอุปกรณ์นั้นมีวิธี GetDistanceTo ซึ่งเป็นสิ่งที่คุณอ้างอิง (แต่ไม่ใช่สิ่งที่คุณมีด้านบน) ไม่ใช่เรื่องใหญ่. ฉันจะทดสอบสิ่งนี้เพื่อดูว่าการคำนวณในตัวดีขึ้นหรือไม่ ขอบคุณไนเจล!
Jason N. Gaylord

1
ฉันอาจถามคำถามผิด แต่ผลลัพธ์ของหน่วยคืออะไร? มันเป็นไมล์หรือกิโลเมตร ฉันไม่พบที่ใดก็ได้
Saeed Neamati

3
@ SaeedNeamati - กำลังมองหาสิ่งนี้เช่นกันตามmsdn.microsoft.com/en-us/library/… - มันอยู่ในหน่วยเมตร
Andy Butland

ใช่ GeoCoordinate.GetDistanceTo () ส่งคืนค่าเป็นเมตร สำหรับฉันในสหรัฐอเมริกาถ้ามันน้อยกว่า 1610 ฉันจะแปลงเป็นฟุต (เมตร * 3.28084) มิฉะนั้นฉันจะแปลงเป็นไมล์ (เมตร * 0.000621371) ความแม่นยำนั้นดีเกินพอสำหรับวัตถุประสงค์ของฉัน
user3235770

110

GetDistance เป็นทางออกที่ดีที่สุดแต่ในหลาย ๆ กรณีเราไม่สามารถใช้วิธีนี้ (เช่น Universal App)

  • Pseudocodeของอัลกอริทึมในการคำนวณระยะทางระหว่างถึง 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;
    }
  • การใช้งาน C # ในโลกแห่งความจริงซึ่งใช้ประโยชน์จากวิธีการขยาย

    การใช้งาน:

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

    การดำเนินงาน:

    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
คุณสามารถระบุสูตรที่ใช้สำหรับแคลคูลัสนี้หรืออาจมีความคิดเห็นเกี่ยวกับบรรทัดใดบ้าง ฉันต้องเปลี่ยนอะไรให้มีระยะทางตรงเป็นกิโลเมตรแทนกิโลเมตรโดยไม่ต้องแปลง
AlbertoFdzM

ขอบคุณสำหรับวิธีแก้ปัญหาที่ดีตอนนี้ฉันสามารถใช้งานได้ในแอปพลิเคชัน Desktop ของฉัน
Jamshaid Kamran

ทำงานได้ดีในแอพ UWP ของฉันที่ฉันไม่สามารถใช้ GeoCoordinate
Zach Green

1
การคำนวณเป็น 95% จริง ฟังก์ชั่นด้านล่างมีความแม่นยำ 100%: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht

31

และที่นี่สำหรับผู้ที่ยังไม่พอใจรหัสต้นฉบับจากGeoCoordinateคลาส. NET-Frameworks ปรับโครงสร้างเป็นวิธีสแตนด์อโลน:

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
คำตอบที่ยอดเยี่ยมฉันอยากจะชี้ให้เห็นว่าระยะทางที่เกิดขึ้นเป็นหน่วยเมตร ตามที่ระบุไว้ในเอกสารอย่างเป็นทางการ
LeviathanCode

ขอบคุณ! ฉันกำลังมองหารัศมีที่แท้จริงของโลกที่ใช้ในชั้น GeoCoordinate
KRoy

การปรับให้เหมาะสมเล็กน้อยหรือเพื่อให้อ่านง่ายขึ้นสามารถคำนวณ pi / 180 ล่วงหน้าได้double oneDegree = Math.PI / 180.0;หรือไม่
brakeroo

1
@brakeroo ขอบคุณสำหรับการตอบกลับของคุณ ฉันต้องการออกคำตอบเหมือนเดิมเพราะนี่เป็นรหัส. NET ดั้งเดิม แน่นอนว่าทุกคนสามารถปฏิบัติตามคำแนะนำของคุณได้
Marc

17

นี่คือ guys และ gals เวอร์ชัน 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
}

10

สำหรับผู้ที่ใช้ Xamarin และไม่สามารถเข้าถึงชั้น GeoCoordinate คุณสามารถใช้คลาส Location 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);
        }

3

คุณสามารถใช้ฟังก์ชั่นนี้:

ที่มา: 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"));

ทำงานได้อย่างสมบูรณ์แบบ! ขอบคุณ!
Schnapz

3

มีห้องสมุดGeoCoordinateสำหรับแพลตฟอร์มเหล่านี้:

  • โมโน
  • .NET 4.5
  • .NET Core
  • 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);

ระยะห่างระหว่างสองพิกัดในเมตร


3

ตามฟังก์ชั่นของ Elliot Wood และหากใครสนใจฟังก์ชั่น C อันนี้ก็ใช้งานได้ ...

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

คุณอาจจะเปลี่ยนเป็นสองเท่า มันส่งคืนค่าเป็นกิโลเมตร


2

กำลังคำนวณระยะทางระหว่างจุดละติจูดและลองจิจูด ...

        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

นี่เป็นคำถามเก่าอย่างไรก็ตามคำตอบไม่ได้ทำให้ฉันพึงพอใจกับประสิทธิภาพและการเพิ่มประสิทธิภาพ

นี่คือตัวแปร C # ที่เหมาะที่สุดของฉัน (ระยะทางเป็นกิโลเมตรโดยไม่มีตัวแปรและการคำนวณซ้ำซ้อนใกล้กับนิพจน์ทางคณิตศาสตร์ของ Haversine Formular https://en.wikipedia.org/wiki/Haversine_formula )

แรงบันดาลใจจาก: 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 จาก Wikipedia


0

ลองสิ่งนี้:

    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

คุณสามารถใช้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);

โชคดี


0

เมื่อพลังการคำนวณของ CPU / คณิตศาสตร์ จำกัด :

มีบางครั้ง (เช่นในที่ทำงานของฉัน) เมื่อพลังในการคำนวณหายาก (เช่นไม่มีตัวประมวลผลแบบลอยตัวทำงานกับไมโครคอนโทรลเลอร์ขนาดเล็ก) ซึ่งฟังก์ชันตรีโกณมิติบางตัวสามารถใช้เวลา CPU มากเกินไป (เช่น 3000+ รอบนาฬิกา) ดังนั้นเมื่อฉัน ต้องการการประมาณเท่านั้นโดยเฉพาะถ้าหากซีพียูต้องไม่ถูกผูกไว้เป็นเวลานานฉันใช้มันเพื่อลดค่าใช้จ่ายของ 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. */
}

เครดิตไปที่https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.