การคำนวณระยะทางระหว่างจุดสองจุดโดยใช้ละติจูดลองจิจูด?


98

นี่คือการลองของฉันเป็นเพียงตัวอย่างรหัสของฉัน:

final double RADIUS = 6371.01;
double temp = Math.cos(Math.toRadians(latA))
            * Math.cos(Math.toRadians(latB))
            * Math.cos(Math.toRadians((latB) - (latA)))
            + Math.sin(Math.toRadians(latA))
            * Math.sin(Math.toRadians(latB));
    return temp * RADIUS * Math.PI / 180;

ฉันใช้สูตรนี้เพื่อรับละติจูดและลองจิจูด:

x = Deg + (Min + Sec / 60) / 60)

คำตอบ:


227

โค้ด Java ที่ Dommer ให้ไว้ข้างต้นให้ผลลัพธ์ที่ไม่ถูกต้องเล็กน้อย แต่ข้อผิดพลาดเล็ก ๆ น้อย ๆ จะเพิ่มขึ้นหากคุณกำลังประมวลผลบอกว่าแทร็ก GPS นี่คือการนำเมธอด Haversine ไปใช้ใน Java ซึ่งคำนึงถึงความแตกต่างของความสูงระหว่างสองจุดด้วย

/**
 * Calculate distance between two points in latitude and longitude taking
 * into account height difference. If you are not interested in height
 * difference pass 0.0. Uses Haversine method as its base.
 * 
 * lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
 * el2 End altitude in meters
 * @returns Distance in Meters
 */
public static double distance(double lat1, double lat2, double lon1,
        double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

7
ทำไมไม่ Math.toRadians () แทนที่จะเป็น deg2rad () มันจะมีตัวตนจริงๆ
Aron Lorincz

2
@ บาลา - ไม่ดีของฉันมันอยู่ในความคิดเห็นบนรหัสบนคอมพิวเตอร์ของฉัน แต่หายไปที่นี่ ระยะทางเป็นเมตร
David George

4
@ ÁronNemmondommegavezetéknevemฉันได้อัปเดตวิธีการใช้คำแนะนำที่ดีของคุณแล้ว
David George

2
ความคิดเห็นบางส่วนที่นี่: stackoverflow.com/questions/28510115/…
David George

ความเกี่ยวข้องใด ๆ กับชื่อตัวแปรaและc? เหล่านี้เป็นด้านของสามเหลี่ยมมุมฉากที่มีป้ายชื่อแบบดั้งเดิมa, bและc?
AlainD

79

นี่คือฟังก์ชัน Java ที่คำนวณระยะห่างระหว่างจุด lat / long สองจุดที่โพสต์ไว้ด้านล่างในกรณีที่มันหายไปอีกครั้ง

    private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
      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 * 180.0 / Math.PI);
    }
    
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Miles\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'K') + " Kilometers\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'N') + " Nautical Miles\n");

1
Google Map แสดง 200 Kms สำหรับ 12.915700, 77.632046, 11.665154, 78.145657 โดยที่โค้ดด้านบนแสดง 149.82 Kms มีบางอย่างไม่ถูกต้อง
Samy

1
@Samy ฟังก์ชั่นข้างต้นช่วยให้คุณมีระยะทางเป็นเส้นตรง
Rahul_Pawar

1
อาณาจักรของเราสำหรับนักพัฒนาที่จะติดป้ายตัวแปรของพวกเขากับหน่วย double dist => double distInMiles (หรือคล้ายกัน) (ฉันไม่ได้เคาะคนที่โพสต์คำตอบนี้ฉันกำลังโหวต ....... แต่เป็นตัวดำเนินการดั้งเดิมของโค้ด)
granadaCoder

วิธีนี้ใช้ได้ผลกับระยะทางเล็ก ๆ ฉันคิดว่ามันอาจจะดีกว่าstackoverflow.com/a/20410612/1537394เนื่องจากใช้ sin / cos / acos ซึ่งเป็นตรรกะเนื่องจากโลกเป็นทรงกลม การโหวตขึ้น
Mladen Adamovic

15

ผู้อ่านในอนาคตที่สะดุดกับบทความ SOF นี้

เห็นได้ชัดว่าคำถามถูกถามในปี 2010 และตอนนี้ในปี 2019 แต่มันเกิดขึ้นในช่วงต้นของการค้นหาทางอินเทอร์เน็ต คำถามเดิมไม่ได้ลดการใช้ไลบรารีของบุคคลที่สาม (เมื่อฉันเขียนคำตอบนี้)

public double calculateDistanceInMeters(double lat1, double long1, double lat2,
                                     double long2) {


    double dist = org.apache.lucene.util.SloppyMath.haversinMeters(lat1, long1, lat2, long2);
    return dist;
}

และ

<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-spatial</artifactId>
  <version>8.2.0</version>
</dependency>

https://mvnrepository.com/artifact/org.apache.lucene/lucene-spatial/8.2.0

โปรดอ่านเอกสารเกี่ยวกับ "SloppyMath" ก่อนดำน้ำ!

https://lucene.apache.org/core/8_2_0/core/org/apache/lucene/util/SloppyMath.html


แน่ใจหรือว่าถูกต้อง ไฟล์ jar นี้ไม่มี ".util" ฉันเพิ่งตรวจสอบ
Adnan Ali

นั่นเป็นเหตุผลที่ฉันเผยแพร่เวอร์ชันพร้อมคำตอบเสมอ ตรวจสอบให้แน่ใจว่าคุณได้รับ 8.2.0 13 upvotes ยังชี้ให้เห็นว่าคำตอบนั้นถูกต้อง
granadaCoder

ฉันคิดว่าเป็นแนวทางปฏิบัติที่ไม่ดีในการเพิ่มการพึ่งพาให้กับบางสิ่งหากคุณสามารถแทนที่ด้วย 28 บรรทัดในรหัสของคุณเว้นแต่คุณจะใช้มากขึ้นจากการพึ่งพานั้น
Mladen Adamovic

14

หมายเหตุ: โซลูชันนี้ใช้ได้เฉพาะในระยะทางสั้น ๆ

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

public class FlatEarthDist {
    //returns distance in meters
    public static double distance(double lat1, double lng1, 
                                      double lat2, double lng2){
     double a = (lat1-lat2)*FlatEarthDist.distPerLat(lat1);
     double b = (lng1-lng2)*FlatEarthDist.distPerLng(lat1);
     return Math.sqrt(a*a+b*b);
    }

    private static double distPerLng(double lat){
      return 0.0003121092*Math.pow(lat, 4)
             +0.0101182384*Math.pow(lat, 3)
                 -17.2385140059*lat*lat
             +5.5485277537*lat+111301.967182595;
    }

    private static double distPerLat(double lat){
            return -0.000000487305676*Math.pow(lat, 4)
                -0.0033668574*Math.pow(lat, 3)
                +0.4601181791*lat*lat
                -1.4558127346*lat+110579.25662316;
    }
}

มีใครสามารถเพิ่มรายละเอียดได้หรือไม่ว่าสิ่งนี้ทำงานได้ดีกว่าในระยะทางสั้น ๆ มากกว่าคำตอบที่ยอมรับได้ที่นี่: stackoverflow.com/a/16794680/1537394 ?
Mladen Adamovic

5

นี่คือหน้าที่มีตัวอย่างจาวาสคริปต์สำหรับการคำนวณทรงกลมต่างๆ รายการแรกในหน้าควรให้สิ่งที่คุณต้องการ

http://www.movable-type.co.uk/scripts/latlong.html

นี่คือรหัส Javascript

var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c;

โดยที่ 'd' จะถือระยะห่าง


"a" เป็นลบได้หรือไม่?
Xi Wei

2

เป็นคำตอบที่ยอดเยี่ยมมากมาย แต่ฉันพบข้อบกพร่องด้านประสิทธิภาพดังนั้นขอเสนอเวอร์ชันที่คำนึงถึงประสิทธิภาพการทำงาน ค่าคงที่ทุกค่าจะถูกคำนวณล่วงหน้าและตัวแปร x, y จะถูกนำมาใช้เพื่อหลีกเลี่ยงการคำนวณค่าเดียวกันสองครั้ง หวังว่าจะช่วยได้

    private static final double r2d = 180.0D / 3.141592653589793D;
    private static final double d2r = 3.141592653589793D / 180.0D;
    private static final double d2km = 111189.57696D * r2d;
    public static double meters(double lt1, double ln1, double lt2, double ln2) {
        double x = lt1 * d2r;
        double y = lt2 * d2r;
        return Math.acos( Math.sin(x) * Math.sin(y) + Math.cos(x) * Math.cos(y) * Math.cos(d2r * (ln1 - ln2))) * d2km;
    }

1
package distanceAlgorithm;

public class CalDistance {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    CalDistance obj=new CalDistance();
    /*obj.distance(38.898556, -77.037852, 38.897147, -77.043934);*/
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "M") + " Miles\n");
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "K") + " Kilometers\n");
        System.out.println(obj.distance(32.9697, -96.80322, 29.46786, -98.53506, "N") + " Nautical Miles\n");       
    }   
    public double distance(double lat1, double lon1, double lat2, double lon2, String sr) {


          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 (sr.equals("K")) {
            dist = dist * 1.609344;
          } else if (sr.equals("N")) {
            dist = dist * 0.8684;
            }
          return (dist);
        }
    public double deg2rad(double deg) {
          return (deg * Math.PI / 180.0);
        }
    public double rad2deg(double rad) {
          return (rad * 180.0 / Math.PI);
        }


    }

1

คำตอบที่อัปเกรดเล็กน้อยจาก @David George:

public static double distance(double lat1, double lat2, double lon1,
                              double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

public static double distanceBetweenLocations(Location l1, Location l2) {
    if(l1.hasAltitude() && l2.hasAltitude()) {
        return distance(l1.getLatitude(), l2.getLatitude(), l1.getLongitude(), l2.getLongitude(), l1.getAltitude(), l2.getAltitude());
    }
    return l1.distanceTo(l2);
}

ฟังก์ชั่นระยะทางเหมือนกัน แต่ฉันได้สร้างฟังก์ชัน I small wrapper ซึ่งใช้วัตถุตำแหน่ง 2 ชิ้น ด้วยเหตุนี้ฉันจึงใช้ฟังก์ชันระยะทางก็ต่อเมื่อสถานที่ทั้งสองแห่งมีระดับความสูงจริง ๆ เพราะบางครั้งก็ไม่มี และอาจนำไปสู่ผลลัพธ์ที่แปลก (หากสถานที่ไม่ทราบระดับความสูง 0 จะถูกส่งกลับ) ในกรณีนี้ฉันถอยกลับไปที่ฟังก์ชันdistanceToแบบคลาสสิก


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