คำนวณระยะทางระหว่างจุดละติจูด - ลองจิจูดสองจุดหรือไม่ (สูตร Haversine)


905

ฉันจะคำนวณระยะทางระหว่างจุดสองจุดที่ระบุโดยละติจูดและลองจิจูดได้อย่างไร

เพื่อความกระจ่างฉันต้องการระยะทางเป็นกิโลเมตร จุดใช้ระบบ WGS84 และฉันต้องการที่จะเข้าใจความถูกต้องของวิธีการที่มีอยู่


เพื่อความแม่นยำที่ดีขึ้น - ดูstackoverflow.com/questions/1420045/…
Lior Kogan

3
โปรดทราบว่าคุณไม่สามารถใช้สูตรฮาวาซีนในรูปวงรีของการปฏิวัติเช่น WGS 84 คุณสามารถใช้วิธีนี้กับทรงกลมที่มีรัศมีเท่านั้น
Mike T

3
คำตอบส่วนใหญ่ที่นี่ใช้ตรีโกณมิติทรงกลมธรรมดาดังนั้นผลลัพธ์จึงค่อนข้างหยาบเมื่อเทียบกับระยะทางทรงรี WGS84 ที่ใช้ในระบบ GPS คำตอบบางคำอ้างถึงสูตรของ Vincenty สำหรับทรงรี แต่อัลกอริธึมนั้นออกแบบมาเพื่อใช้กับเครื่องคิดเลขของยุค 1960 และมันมีปัญหาเรื่องความเสถียร เรามีฮาร์ดแวร์และซอฟต์แวร์ที่ดีขึ้นในขณะนี้ โปรดดูGeographicLibสำหรับห้องสมุดคุณภาพสูงที่มีการใช้งานในภาษาต่างๆ
PM 2Ring

@MikeT - จริงแม้ว่าคำตอบมากมายที่นี่ดูเหมือนจะมีประโยชน์ในระยะทางสั้น ๆ : ถ้าคุณใช้ lat / long จาก WGS 84 และใช้ Haversine ราวกับว่าคำเหล่านั้นเป็นจุดบนทรงกลมคุณจะไม่ได้รับคำตอบที่มีข้อผิดพลาดเท่านั้น ปัจจัยแฟบของโลกดังนั้นอาจอยู่ในสูตรที่แม่นยำกว่า 1% ด้วยข้อแม้ที่ว่าสิ่งเหล่านี้เป็นระยะทางเล็ก ๆ ให้พูดภายในเมืองเดียว
ToolmakerSteve

1
สำหรับ plateforms เหล่านี้: โมโน /. NET 4.5 / .NET Core / Windows Phone 8.x / แพลตฟอร์ม Windows สากล / Xamarin iOS / Xamarin Android ดูstackoverflow.com/a/54296314/2736742
A. Morel

คำตอบ:


1145

ลิงค์นี้อาจมีประโยชน์กับคุณเนื่องจากรายละเอียดการใช้สูตร Haversineเพื่อคำนวณระยะทาง

ข้อความที่ตัดตอนมา:

สคริปต์นี้ [ใน Javascript] คำนวณระยะทางวงกลมใหญ่ระหว่างจุดสองจุดนั่นคือระยะทางที่สั้นที่สุดเหนือพื้นผิวโลกโดยใช้สูตร '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
การคำนวณ / วิธีการนี้บัญชีสำหรับโลกเป็นทรงกลม (ไม่ใช่ทรงกลมที่สมบูรณ์แบบ) หรือไม่? คำถามเดิมถามหาระยะทางระหว่างจุดบนโลก WGS84 ไม่แน่ใจว่าเกิดข้อผิดพลาดมากแค่ไหนในการใช้ทรงกลมที่สมบูรณ์แบบ แต่ฉันคิดว่ามันอาจจะค่อนข้างมากขึ้นอยู่กับจุดที่อยู่บนโลกดังนั้นความแตกต่างจึงคุ้มค่า
redcalx

15
สูตรฮาเวอรีนไม่ได้หมายถึงว่าโลกเป็นดาวฤกษ์ดังนั้นคุณจะได้รับข้อผิดพลาดเนื่องจากความจริงข้อนี้ ไม่สามารถรับประกันว่าถูกต้องจะดีกว่า 0.5% นั่นอาจเป็นข้อผิดพลาดในระดับที่ยอมรับได้หรือไม่ก็ได้
Brandon

24
มีเหตุผลอะไรบ้างที่จะใช้Math.atan2(Math.sqrt(a), Math.sqrt(1-a))แทนMath.asin(Math.sqrt(h))ซึ่งจะเป็นการนำสูตรไปใช้โดยตรงที่บทความ Wikipedia ใช้? มีประสิทธิภาพมากขึ้นและ / หรือมีเสถียรภาพเชิงตัวเลขมากขึ้นหรือไม่
musiphil

16
@UsmanMutawakil ดี 38 ไมล์ที่คุณได้รับคือระยะทางบนถนน อัลกอริทึมนี้คำนวณระยะทางเส้นตรงบนพื้นผิวโลก Google Maps มีเครื่องมือระยะทาง (ซ้ายล่าง "Labs") ที่ทำแบบเดียวกันใช้เพื่อเปรียบเทียบ
ปาสกาล

4
@ Forte_201092: เพราะมันไม่จำเป็น - (sin(x))²เท่ากับ(sin(-x))²
Jean Hominal

359

ฉันจำเป็นต้องคำนวณระยะทางจำนวนมากระหว่างจุดต่าง ๆ สำหรับโครงการของฉันดังนั้นฉันจึงไปข้างหน้าและพยายามเพิ่มประสิทธิภาพรหัสฉันพบที่นี่ โดยเฉลี่ยในเบราว์เซอร์ที่แตกต่างกันการใช้งานใหม่ของฉันทำงานเร็วกว่าคำตอบ upvoted ที่สุด2 เท่า

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
}

คุณสามารถเล่นกับ jsPerf ของฉันและเห็นผลที่นี่

เมื่อเร็ว ๆ นี้ฉันต้องการทำสิ่งเดียวกันใน python ดังนั้นนี่คือการใช้งาน 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...

และเพื่อความสมบูรณ์: ฮาวาซีนบนวิกิ


13
@AngularM และมีความเป็นไปได้สูงที่ Google จะคำนวณระยะทางถ้าคุณจะใช้ถนนบางสายไม่ใช่เส้นตรง
Salvador Dali

3
Google คำนวณระยะทางในการขับขี่ซึ่งจะคำนวณ "เป็นอีกาบิน"
Hobbyist

4
@Oadie และจะปรับปรุงความเร็วหรือไม่ ส่วนใหญ่อาจไม่ แต่ฉันจะจบลงด้วย 'สิ่งที่คุณไม่ทำงาน' สำหรับคนที่คัดลอกมันในเบราว์เซอร์เก่า
Salvador Dali

4
ใช่ แต่สิ่งที่// 2 * R; R = 6371 kmหมายถึงอะไร และวิธีการปัจจุบันให้คำตอบในกิโลเมตรหรือไมล์? ต้องการเอกสารที่ดีกว่า ขอบคุณ
Khalil Khalaf

20
@KhalilKhalaf คุณล้อเล่นหรือพยายามหมุนรอบที่นี่? กม. หมายถึงกิโลเมตร คุณคิดว่า R หมายถึงอะไร (โดยเฉพาะถ้าเราพูดถึง shpere)? เดาว่าหน่วยคำตอบจะเป็นอย่างไรถ้าคุณเห็นกม. คุณกำลังมองหาเอกสารประเภทใดที่นี่: มี 4 บรรทัดที่แท้จริง
Salvador Dali

69

นี่คือการใช้ 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
คุณกำลังใช้รัศมีเส้นศูนย์สูตร แต่คุณควรใช้รัศมีเฉลี่ยซึ่งคือ 6371 กม.
Philippe Leybaert

7
สิ่งนี้ไม่ควรdouble dlon = Radians(lon2 - lon1);และdouble dlat = Radians(lat2 - lat1);
Chris Marisic

ฉันเห็นด้วยกับ Chris Marisic ฉันใช้รหัสเดิมและการคำนวณผิด ฉันเพิ่มการโทรเพื่อแปลงเดลตาเป็นเรเดียนและทำงานได้อย่างถูกต้องในขณะนี้ ฉันส่งการแก้ไขแล้วและกำลังรอการตรวจสอบอยู่
Bryan Bedard

ฉันส่งการแก้ไขอื่นเนื่องจาก lat1 & lat2 ต้องถูกแปลงเป็นเรเดียนด้วย ฉันยังแก้ไขสูตรสำหรับการมอบหมายให้ a เพื่อให้ตรงกับสูตรและรหัสที่พบที่นี่: movable-type.co.uk/scripts/latlong.html
Bryan Bedard

ไม่RADIUSจำเป็นที่จะต้องคุ้มค่าที่จะเป็น 6371 ในขณะที่คำตอบอื่น ๆ ?
Chris Hayes

66

นี่คือการใช้งานจาวาของสูตร 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));
}

โปรดทราบว่าที่นี่เรากำลังปัดเศษคำตอบไปยังกม. ที่ใกล้ที่สุด


2
หากเราต้องการคำนวณระยะห่างระหว่างจุดสองจุดเป็นเมตรวิธีที่แม่นยำกว่าคืออะไร เพื่อใช้6371000เป็นรัศมีของโลก? (เฉลี่ยของรัศมีโลกคือ 6371000 เมตร) หรือแปลงกิโลเมตรเป็นเมตรจากฟังก์ชั่นของคุณ?
Micro

หากคุณต้องการไมล์ให้คูณผลลัพธ์ด้วย0.621371
lasec0203

42

ขอบคุณมากสำหรับสิ่งนี้ทั้งหมด ฉันใช้รหัสต่อไปนี้ในแอพ Objective-C iPhone ของฉัน:

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

ละติจูดและลองจิจูดเป็นทศนิยม ฉันไม่ได้ใช้ min () สำหรับการโทร asin () เนื่องจากระยะทางที่ฉันใช้นั้นเล็กมากจนไม่ต้องการ

มันให้คำตอบที่ไม่ถูกต้องจนกว่าฉันจะส่งผ่านค่าในเรเดียน - ตอนนี้มันค่อนข้างเหมือนกับค่าที่ได้จากแอปแผนที่ของ Apple :-)

ปรับปรุงพิเศษ:

หากคุณใช้ iOS4 หรือใหม่กว่า Apple จะให้วิธีการบางอย่างเพื่อให้สามารถใช้ฟังก์ชันการทำงานเดียวกันได้:

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

    MKMapPoint  start, finish;


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

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}

1
iOS SDK มีการใช้งานของตัวเอง: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler

ฉันคิดว่าเครื่องหมายวงเล็บpow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))ไม่ถูกต้อง ลบสิ่งเหล่านั้นออกและผลลัพธ์ตรงกับสิ่งที่ฉันได้รับเมื่อฉันใช้การใช้งานอื่น ๆ ในหน้านี้หรือใช้สูตร Haversine จากWikipediaตั้งแต่เริ่มต้น
zanedp

ใช้พิกัด (40.7127837, -74.0059413) สำหรับ NYC และ (34.052234, -118.243685) สำหรับ LA ด้วย()ผลรวมนั้นฉันได้ 3869.75 หากไม่มีพวกเขาฉันจะได้รับ 3935.75 ซึ่งเป็นสิ่งที่การค้นหาเว็บปรากฏขึ้น
zanedp

40

นี่เป็นฟังก์ชั่น PHP ที่ง่ายซึ่งจะให้การประมาณที่สมเหตุสมผล (ภายใต้ +/- 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;
}
?>

ดังกล่าวก่อนหน้านี้; โลกไม่ใช่ทรงกลม มันเหมือนเบสบอลเก่าที่ Mark McGwire ตัดสินใจที่จะฝึกด้วย - มันเต็มไปด้วยรอยบุบและการกระแทก การคำนวณที่ง่ายกว่า (เช่นนี้) ถือว่าเป็นทรงกลม

วิธีการที่แตกต่างกันอาจมีความแม่นยำมากขึ้นหรือน้อยลงตามตำแหน่งที่คุณอยู่บนรีออพอยด์ที่ผิดปกตินี้และระยะห่างของคะแนนของคุณจะอยู่ไกลแค่ไหน ยิ่งความคาดหวังของคุณแม่นยำมากขึ้นคณิตศาสตร์ที่ซับซ้อนมากขึ้น

สำหรับข้อมูลเพิ่มเติม: ระยะทางวิกิพีเดียทางภูมิศาสตร์


4
มันทำงานได้อย่างสมบูรณ์แบบ! ฉันเพิ่งเพิ่ม $ distance_miles = $ km * 0.621371; และนั่นคือทั้งหมดที่ฉันต้องการสำหรับระยะทางโดยประมาณเป็นไมล์! ขอบคุณโทนี่

31

ฉันโพสต์ที่นี่ตัวอย่างการทำงานของฉัน

แสดงรายการจุดทั้งหมดในตารางที่มีระยะห่างระหว่างจุดที่กำหนด (เราใช้จุดสุ่ม - lat: 45.20327, ยาว: 23.7806) น้อยกว่า 50 กม. พร้อมละติจูด & ลองจิจูดใน MySQL (เขตข้อมูลตารางเป็น coord_lat และ coord_long):

แสดงรายการทั้งหมดที่มี DISTANCE <50 ในหน่วยกิโลเมตร (พิจารณารัศมีของโลก 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

ตัวอย่างข้างต้นได้รับการทดสอบใน MySQL 5.0.95 และ 5.5.16 (Linux)


ฉันคิดว่าวิธีการที่ดีอาจเป็นการกรองผลลัพธ์ล่วงหน้าโดยใช้การประมาณดังนั้นสูตรหนักจะถูกนำไปใช้ในบางกรณีเท่านั้น มีประโยชน์เป็นพิเศษหากคุณมีเงื่อนไขอื่น ๆ ฉันใช้สิ่งนี้สำหรับ aprox เริ่มต้น: stackoverflow.com/questions/1253499/…
Pato

28

ในอีกคำตอบการใช้งานใน ที่ขาดหายไป.

การคำนวณระยะห่างระหว่างจุดสองจุดนั้นค่อนข้างตรงไปตรงมากับdistmฟังก์ชั่นจากgeosphereแพ็คเกจ:

distm(p1, p2, fun = distHaversine)

ที่อยู่:

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

เนื่องจากโลกไม่ได้เป็นทรงกลมอย่างสมบูรณ์สูตร Vincenty สำหรับทรงรีจึงน่าจะเป็นวิธีที่ดีที่สุดในการคำนวณระยะทาง ดังนั้นในgeosphereแพ็คเกจที่คุณใช้แล้ว:

distm(p1, p2, fun = distVincentyEllipsoid)

นอกหลักสูตรคุณไม่จำเป็นต้องใช้geosphereแพ็คเกจคุณสามารถคำนวณระยะทางจากฐานRด้วยฟังก์ชั่น:

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

เพื่อให้แน่ใจว่าฉันชัดเจนในสิ่งที่คุณพูด: รหัสที่คุณให้ไว้ตอนท้ายของการโพสต์: นั่นคือการใช้สูตร Vincenty หรือไม่? เท่าที่คุณรู้มันควรให้คำตอบเช่นเดียวกับการเรียก Vincenty ในพื้นที่ทางภูมิศาสตร์? [ฉันไม่มี geosphere หรือห้องสมุดอื่น ๆ ; เพียงมองหารหัสที่จะรวมไว้ในแอพข้ามแพลตฟอร์ม แน่นอนฉันจะตรวจสอบกรณีทดสอบกับเครื่องคิดเลขที่ดีที่รู้จักกันดี]
ToolmakerSteve

1
@ToolmakerSteve ฟังก์ชั่นในตอนท้ายของคำตอบของฉันคือการใช้งานของวิธีการ Haversine
Jaap

สวัสดี @Jaap ฉันสามารถถามหน่วยการวัดสำหรับสูตรได้อย่างไร มันเป็นหน่วยเมตรหรือเปล่า?
แจ็คสัน

11

Haversine เป็นสูตรที่ดีสำหรับคนส่วนใหญ่แน่นอนคำตอบอื่น ๆ รวมไว้แล้วดังนั้นฉันจะไม่ใช้พื้นที่ แต่สิ่งสำคัญคือต้องทราบว่าไม่ว่าจะใช้สูตรใด (ใช่ไม่ใช่เพียงสูตรเดียว) เนื่องจากมีความแม่นยำสูงและใช้เวลาในการคำนวณ การเลือกสูตรต้องใช้ความคิดมากกว่าคำตอบง่ายๆ

การโพสต์นี้จากบุคคลที่ nasa เป็นสิ่งที่ดีที่สุดที่ฉันพบเมื่อพูดถึงตัวเลือก

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

ตัวอย่างเช่นหากคุณเพียงเรียงลำดับแถวตามระยะทางในรัศมี 100 ไมล์ สูตรดินราบจะเร็วกว่า 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;

สังเกตว่ามีโคไซน์เพียงหนึ่งและรูทสแควร์หนึ่งอัน Vs 9 ของพวกเขาในสูตร Haversine


มันเป็นไปได้ที่ดี เพิ่งทราบว่าระยะทางสูงสุดที่แนะนำในการสนทนาคือ12ไมล์ไม่ใช่100และถึงอย่างนั้นข้อผิดพลาดอาจคืบคลานสูงสุด 30 เมตร (100 ฟุต) ขึ้นอยู่กับตำแหน่งของโลก
Eric Wu

7

คุณสามารถใช้บิลด์ใน CLLocationDistance เพื่อคำนวณสิ่งนี้:

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

ในกรณีของคุณถ้าคุณต้องการกิโลเมตรหารด้วย 1,000


7

ฉันไม่ชอบการเพิ่มคำตอบอื่น แต่ Google maps API v.3 มีรูปทรงกลม (และอื่น ๆ ) หลังจากแปลง WGS84 ของคุณเป็นองศาทศนิยมคุณสามารถทำสิ่งนี้:

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

ไม่มีคำพูดใด ๆ เกี่ยวกับการคำนวณของ Google ที่แม่นยำหรือรูปแบบที่ใช้ (แม้ว่าจะพูดว่า "เป็นทรงกลม" แทนที่จะเป็น "geoid") อย่างไรก็ตามระยะทาง "เส้นตรง" จะแตกต่างจากระยะทางอย่างชัดเจน พื้นผิวโลกซึ่งเป็นสิ่งที่ทุกคนคิดว่าน่าจะเป็น


ระยะทางเป็นหน่วยเมตร อีกวิธีหนึ่งสามารถใช้ computeLength ()
electrobabe

7

Python implimentation Origin เป็นศูนย์กลางของสหรัฐอเมริกาที่ต่อเนื่องกัน

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

เพื่อให้ได้คำตอบเป็นกิโลเมตรให้ตั้งค่าไมล์ = false


1
คุณกำลังนำเข้าแพคเกจที่ไม่ได้มาตรฐานซึ่งทำงานได้ทั้งหมด ฉันไม่รู้ว่านั่นคือทั้งหมดที่มีประโยชน์
Teepeemm

แพคเกจอยู่ใน PyPI, Python Package Index, เป็น python 3 package พร้อมกับจำนวน numpy และ scikit-Learn ไม่แน่ใจว่าทำไมจึงมีการผนวกเข้ากับแพ็คเกจ พวกเขามักจะมีประโยชน์มาก ในฐานะที่เป็นโอเพนซอร์สเราสามารถตรวจสอบวิธีการต่าง ๆ ที่มีอยู่ ฉันคิดว่าหลายคนจะพบว่าแพ็คเกจนี้มีประโยชน์ดังนั้นฉันจะออกจากโพสต์แม้จะมีข้อเสีย ไชโย :)
invoketheshell

7

อาจมีวิธีแก้ปัญหาที่ง่ายกว่าและถูกต้องมากขึ้น: ขอบเขตของโลกคือ 40,000 กิโลเมตรที่เส้นศูนย์สูตรประมาณ 37,000 รอบกรีนนิช (หรือรอบลองจิจูด) ดังนั้น:

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

ฉันเห็นด้วยว่าควรปรับให้ดีเพราะฉันเองบอกว่ามันเป็นรูปวงรีดังนั้นรัศมีที่จะคูณด้วยโคไซน์นั้นแตกต่างกันไป แต่มันก็แม่นยำกว่านิดหน่อย เปรียบเทียบกับ Google Maps และมันลดข้อผิดพลาดลงอย่างมาก


ฟังก์ชั่นนี้ส่งคืนระยะทางเป็นกิโลเมตรหรือไม่
Wikki

มันเป็นเพียงเพราะเส้นศูนย์สูตรและรอบลองจิจูดอยู่ในกม. สำหรับไมล์แบ่ง 40000 และ 37000 ด้วย 1.6 รู้สึก geeky คุณสามารถแปลงเป็น Ris คูณด้วยประมาณ 7 หรือเพื่อ parasang หารด้วย 2.2 ;-)
Meymann

นี่น่าจะเป็นคำตอบที่ดีที่สุดที่นี่ ฉันต้องการใช้ แต่ฉันสงสัยว่ามีวิธีการยืนยันความถูกต้องของอัลกอริทึมนี้หรือไม่ ฉันทดสอบ f (50,5,58,3) มันทำให้ 832km ขณะmovable-type.co.uk/scripts/latlong.htmlใช้ 'haversine' สูตรให้ 899km มีความแตกต่างใหญ่เช่นนี้หรือไม่?
ปากช่องปากพัง

ยิ่งกว่านั้นฉันคิดว่าค่าที่ส่งคืนโดยโค้ดด้านบนอยู่ในหน่วย m และไม่ใช่ km
ปากช่องปากพัง

@ChongLipPhang - ข้อควรระวัง: ทฤษฎีบทพีทาโกรัสเป็นเพียงการประมาณที่เหมาะสมสำหรับพื้นที่ขนาดเล็กเนื่องจากทฤษฎีบทนี้สันนิษฐานว่าโลกแบน ในกรณีที่รุนแรงให้เริ่มจากเส้นศูนย์สูตรแล้วเคลื่อนที่ 90 องศาทางตะวันออกและ 90 องศาเหนือ ผลลัพธ์สุดท้ายของการเรียนคือขั้วเหนือและเหมือนกับการเคลื่อนที่ 0 องศาตะวันออกและ 90 องศาเหนือ ดังนั้นการทำ sqrt (sqr (dx) + sqr (dy)) จะถูกปิดอย่างรุนแรงในกรณีแรก ~ sqrt (10km sqr + 10km sqr) ~ = 14.4 km เทียบกับระยะทางที่ถูกต้อง ~ 10km
ToolmakerSteve

7

คำตอบทั้งหมดข้างต้นถือว่าโลกเป็นทรงกลม อย่างไรก็ตามการประมาณที่แม่นยำยิ่งขึ้นน่าจะเป็นของทรงกลม oblate

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

นี่คือ SQL Implementation เพื่อคำนวณระยะทางเป็นกิโลเมตร

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;

สำหรับรายละเอียดเพิ่มเติมในการใช้งานโดยการเขียนโปรแกรม langugage คุณสามารถอ่านสคริปต์ php ที่กำหนดได้ที่นี่


5

นี่คือการใช้typescriptของสูตร 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

การคำนวณที่แม่นยำควรคำนึงถึงว่าโลกไม่ได้เป็นรูปทรงกลมที่สมบูรณ์แบบ นี่คือการเปรียบเทียบอัลกอริทึมต่างๆที่มีให้ที่นี่:

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

ในระยะทางสั้น ๆ อัลกอริทึมของ Keerthana ดูเหมือนจะตรงกับของ Google Maps Google แผนที่ดูเหมือนจะไม่ทำตามอัลกอริธึมง่ายๆโดยบอกว่าอาจเป็นวิธีที่แม่นยำที่สุดที่นี่

อย่างไรก็ตามนี่คือการใช้ Javascript ของอัลกอริทึมของ 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

สคริปต์นี้ [ใน PHP] คำนวณระยะทางระหว่างจุดสองจุด

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

การใช้งาน Java ในสูตร 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

การคำนวณระยะทางระหว่างจุดสองจุดบนทรงกลมที่คุณต้องทำคำนวณรทเซอร์เคิล

มีไลบรารี C / C ++ จำนวนมากที่จะช่วยในการฉายแผนที่ที่MapToolsหากคุณต้องการปฏิเสธระยะทางของคุณไปยังพื้นผิวเรียบ ในการทำเช่นนี้คุณจะต้องใช้สตริงการฉายภาพของระบบพิกัดต่างๆ

คุณอาจพบว่าMapWindowเป็นเครื่องมือที่มีประโยชน์ในการมองเห็นจุดต่างๆ นอกจากนี้ยังเป็นโอเพ่นซอร์สซึ่งเป็นคู่มือที่มีประโยชน์เกี่ยวกับวิธีการใช้ไลบรารี proj.dll ซึ่งดูเหมือนจะเป็นห้องสมุดฉายภาพโอเพ่นซอร์สหลัก


3

ต่อไปนี้เป็นคำตอบที่ได้รับการยอมรับซึ่งนำไปใช้กับ Java ในกรณีที่ทุกคนต้องการ

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

นี่คือการดำเนินการ VB.NET การดำเนินการนี้จะให้ผลลัพธ์เป็น KM หรือไมล์ตามค่า Enum ที่คุณผ่าน

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

เมื่อคำนวณ "a" คุณเขียน Math.Sin ( dLat .. ) สองครั้งโดยไม่ได้ตั้งใจหรือไม่?
Marco Ottina

2

ฉันย่อการคำนวณลงโดยทำให้สูตรง่ายขึ้น

นี่คือในทับทิม:

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

โซลูชันของ Chuck ใช้ได้กับไมล์เช่นกัน


2

นี่คือการใช้จาวาของฉันสำหรับการคำนวณระยะทางผ่านองศาทศนิยมหลังจากค้นหาบางส่วน ฉันใช้รัศมีเฉลี่ยของโลก (จากวิกิพีเดีย) เป็นกิโลเมตร ถ้าคุณต้องการไมล์ผลลัพธ์จากนั้นใช้รัศมีโลกเป็นไมล์

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

ใน Mysql ใช้ฟังก์ชั่นต่อไปนี้ผ่านพารามิเตอร์ที่ใช้ 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

นี่คือตัวอย่างในpostgres sql (ในกม., สำหรับรุ่นไมล์แทนที่ 1.609344 โดย 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

นี่คืออีกแปลงเป็นรหัสทับทิม :

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

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