2008-09-23 31 views
74

Tôi muốn tìm khoảng cách giữa hai điểm khác nhau. Điều này tôi biết có thể được thực hiện với khoảng cách vòng tròn lớn. http://www.meridianworlddata.com/Distance-calculation.aspLàm cách nào để đo khoảng cách và tạo một hộp giới hạn dựa trên hai vĩ độ + kinh độ trong Java?

Sau khi thực hiện, với một điểm và khoảng cách, tôi muốn tìm điểm mà khoảng cách về phía bắc, và khoảng cách đó về phía đông để tạo hộp xung quanh điểm.

+1

tham khảo blog này http://xebee.xebia.in/2010/10/28/working-with-geolocations/ – Robin

Trả lời

20

Chúng tôi đã có một số thành công bằng cách sử dụng OpenMap để vẽ rất nhiều dữ liệu vị trí. Có một lớp LatLonPoint có một số chức năng cơ bản, bao gồm khoảng cách.

+5

Cảnh báo để chấp nhận có thể: Tôi chỉ chạy vào một vấn đề BIG với OpenMap; chúng sử dụng phao nổi bên trong cho lat/lon thập phân, giới hạn độ chính xác tùy thuộc vào mức độ gần đường xích đạo của bạn. Họ có kế hoạch hỗ trợ các tùy chọn để tăng gấp đôi với các lớp OMGraphic của họ bắt đầu từ phiên bản 4.7, nhưng ổn định hiện tại chỉ là 4.6.5 (tính đến tháng 3 năm 2010). Nguồn: http://openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.html – Marc

+0

Cũng lưu ý rằng Thỏa thuận cấp phép phần mềm OpenMap hiện tại http://openmap.bbn.com/license.html có được coi là không miễn phí. http://web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Một số cuộc thảo luận với BBN đã xảy ra để thay đổi giấy phép nhưng chưa có gì xảy ra. –

+0

Liên kết LatLonPoint bị hỏng – Petriborg

7

Tìm kiếm nhanh trên Google biến thành GeoTools, có khả năng có loại chức năng bạn đang tìm kiếm.

140

Dưới đây là cách triển khai Java là Haversine công thức. Tôi sử dụng này trong một dự án để tính toán khoảng cách trong dặm giữa thép dài/lat.

public static double distFrom(double lat1, double lng1, double lat2, double lng2) { 
    double earthRadius = 3958.75; // miles (or 6371.0 kilometers) 
    double dLat = Math.toRadians(lat2-lat1); 
    double dLng = Math.toRadians(lng2-lng1); 
    double sindLat = Math.sin(dLat/2); 
    double sindLng = Math.sin(dLng/2); 
    double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2) 
      * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)); 
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    double dist = earthRadius * c; 

    return dist; 
    } 
+22

Chỉ cần một lưu ý về vấn đề này, nó sẽ trở lại khoảng cách bằng dặm (vì các thiết lập earthRadius). Đối với các đơn vị khác thay đổi earthRadius (xem http://en.wikipedia.org/wiki/Earth_radius để biết thêm) –

+0

Có lý do nào khiến bạn sử dụng phao thay vì tăng gấp đôi không? Nếu tôi hiểu đúng, bạn có thể tăng độ chính xác của kết quả của bạn bằng cách thay đổi các kiểu tham số đầu vào của bạn – Hamy

+0

Cũng không cần phải tính sin của một nửa vùng đồng bằng hai lần. Tính toán nó một lần và nhân nó với chính nó. Nó chắc chắn là giá trị trong một vòng lặp chặt chẽ. –

43

Hoặc bạn có thể sử dụng SimpleLatLng. Apache 2.0 được cấp phép và sử dụng trong một hệ thống sản xuất mà tôi biết về: của tôi.

Câu chuyện ngắn:

Tôi đã tìm kiếm một thư viện địa lý đơn giản và không thể tìm thấy thư viện phù hợp với nhu cầu của tôi. Và ai muốn viết và kiểm tra và gỡ lỗi các công cụ địa lý nhỏ này nhiều lần trong mọi ứng dụng? Có phải là một cách tốt hơn!

Vì vậy, SimpleLatLng được sinh ra như một cách để lưu trữ dữ liệu kinh độ vĩ độ, thực hiện các phép tính khoảng cách và tạo các ranh giới có hình dạng.

Tôi biết tôi đã quá muộn để giúp áp phích gốc, nhưng mục đích của tôi là giúp những người như tôi tìm thấy câu hỏi này trong tìm kiếm. Tôi rất thích có một số người sử dụng nó và góp phần vào việc thử nghiệm và tầm nhìn của tiện ích nhỏ gọn này.

+0

điều này có thể giúp tôi! bạn đã tạo nó chưa Bạn có sử dụng công thức Haversine để tính toán khoảng cách không?Tôi sẽ cố gắng nhảy vào nếu tôi tìm thấy thời gian! – Gevorg

+0

Đúng, nó sử dụng Haversine để tính toán khoảng cách với sự nhấn mạnh (mặc dù thừa nhận không phải là một ám ảnh với) tốc độ và cấu hình bộ nhớ thấp. Tôi nghĩ rằng nó cũng có một số thuộc tính xử lý số đẹp khác, như xem xét các tọa độ "thực sự gần" bằng nhau. – JavadocMD

1

Bạn có thể sử dụng Java Geodesy Library for GPS, nó sử dụng Vincenty's formulae tính đến độ cong bề mặt của trái đất.

Thực hiện đi như thế này:

import org.gavaghan.geodesy.*; 
... 
GeodeticCalculator geoCalc = new GeodeticCalculator(); 
Ellipsoid reference = Ellipsoid.WGS84; 
GlobalPosition pointA = new GlobalPosition(latitude, longitude, 0.0); 
GlobalPosition userPos = new GlobalPosition(userLat, userLon, 0.0); 
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance(); 

Khoảng cách kết quả là tính bằng mét.

11

Đối với một khoảng cách chính xác hơn (0.5mm), bạn cũng có thể sử dụng xấp xỉ Vincenty:

/** 
* Calculates geodetic distance between two points specified by latitude/longitude using Vincenty inverse formula 
* for ellipsoids 
* 
* @param lat1 
*   first point latitude in decimal degrees 
* @param lon1 
*   first point longitude in decimal degrees 
* @param lat2 
*   second point latitude in decimal degrees 
* @param lon2 
*   second point longitude in decimal degrees 
* @returns distance in meters between points with 5.10<sup>-4</sup> precision 
* @see <a href="http://www.movable-type.co.uk/scripts/latlong-vincenty.html">Originally posted here</a> 
*/ 
public static double distVincenty(double lat1, double lon1, double lat2, double lon2) { 
    double a = 6378137, b = 6356752.314245, f = 1/298.257223563; // WGS-84 ellipsoid params 
    double L = Math.toRadians(lon2 - lon1); 
    double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat1))); 
    double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat2))); 
    double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1); 
    double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2); 

    double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM; 
    double lambda = L, lambdaP, iterLimit = 100; 
    do { 
     sinLambda = Math.sin(lambda); 
     cosLambda = Math.cos(lambda); 
     sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) 
       + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)); 
     if (sinSigma == 0) 
      return 0; // co-incident points 
     cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; 
     sigma = Math.atan2(sinSigma, cosSigma); 
     sinAlpha = cosU1 * cosU2 * sinLambda/sinSigma; 
     cosSqAlpha = 1 - sinAlpha * sinAlpha; 
     cos2SigmaM = cosSigma - 2 * sinU1 * sinU2/cosSqAlpha; 
     if (Double.isNaN(cos2SigmaM)) 
      cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6) 
     double C = f/16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); 
     lambdaP = lambda; 
     lambda = L + (1 - C) * f * sinAlpha 
       * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); 
    } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0); 

    if (iterLimit == 0) 
     return Double.NaN; // formula failed to converge 

    double uSq = cosSqAlpha * (a * a - b * b)/(b * b); 
    double A = 1 + uSq/16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); 
    double B = uSq/1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); 
    double deltaSigma = B 
      * sinSigma 
      * (cos2SigmaM + B 
        /4 
        * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B/6 * cos2SigmaM 
          * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM))); 
    double dist = b * A * (sigma - deltaSigma); 

    return dist; 
} 

Mã này đã được tự do chuyển thể từ http://www.movable-type.co.uk/scripts/latlong-vincenty.html

6

Corrected thức Haversine cách ....

public static double HaverSineDistance(double lat1, double lng1, double lat2, double lng2) 
{ 
    // mHager 08-12-2012 
    // http://en.wikipedia.org/wiki/Haversine_formula 
    // Implementation 

    // convert to radians 
    lat1 = Math.toRadians(lat1); 
    lng1 = Math.toRadians(lng1); 
    lat2 = Math.toRadians(lat2); 
    lng2 = Math.toRadians(lng2); 

    double dlon = lng2 - lng1; 
    double dlat = lat2 - lat1; 

    double a = Math.pow((Math.sin(dlat/2)),2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2),2); 

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 

    return EARTH_RADIUS * c; 
} 
+0

Trái đất không phải là một quả cầu hoàn hảo –

+0

Đúng, đôi khi ** đóng ** là đủ tốt. IE: Tại sao ông Inman tạo ra nó. Tôi nói với anh ta anh ta sai, nhưng anh ấy đã chết. : o (Nếu bạn cần tính toán cho hình dạng thuôn dài của thế giới, có công thức tốt hơn cho điều đó. Ngoài ra còn có một số thư viện apache tuyệt vời mà bạn có thể sử dụng. Nếu bạn chỉ cần một cái gì đó đơn giản, đây là một ví dụ nhanh . :) –

+0

Ya, công thức của Haversine được xây dựng dựa trên yếu tố 'đóng đủ tốt'. Vào thời điểm đó chúng tôi đã đo khoảng cách đó là <50 dặm để xác định khoảng cách từ nơi này đến nơi khác như một 'dựa trên kinh nghiệm'. –

0

Tôi thường sử dụng MATLAB với Mapping Toolbox, và sau đó sử dụng các mã trong Java của tôi sử dụng MATLAB Builder JA. Nó làm cho cuộc sống của tôi rất nhiều đơn giản hơn. Hầu hết các trường đều có quyền truy cập miễn phí cho sinh viên, bạn có thể dùng thử (hoặc lấy phiên bản dùng thử để tiếp tục công việc của bạn).

2

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

public static Double distanceBetweenTwoLocationsInKm(Double latitudeOne, Double longitudeOne, Double latitudeTwo, Double longitudeTwo) { 
     if (latitudeOne == null || latitudeTwo == null || longitudeOne == null || longitudeTwo == null) { 
      return null; 
     } 

     Double earthRadius = 6371.0; 
     Double diffBetweenLatitudeRadians = Math.toRadians(latitudeTwo - latitudeOne); 
     Double diffBetweenLongitudeRadians = Math.toRadians(longitudeTwo - longitudeOne); 
     Double latitudeOneInRadians = Math.toRadians(latitudeOne); 
     Double latitudeTwoInRadians = Math.toRadians(latitudeTwo); 
     Double a = Math.sin(diffBetweenLatitudeRadians/2) * Math.sin(diffBetweenLatitudeRadians/2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians/2) 
       * Math.sin(diffBetweenLongitudeRadians/2); 
     Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 
     return (earthRadius * c); 
    } 
1

Tôi biết rằng có rất nhiều câu trả lời, nhưng trong thực hiện một số nghiên cứu về chủ đề này, tôi thấy rằng hầu hết các câu trả lời ở đây sử dụng công thức Haversine, nhưng công thức Vincenty là thực sự chính xác. Có một bài viết đã điều chỉnh tính toán từ một phiên bản Javascript, nhưng nó rất khó sử dụng. Tôi thấy một phiên bản vượt trội hơn vì:

  1. Nó cũng có giấy phép mở.
  2. Nó sử dụng nguyên tắc OOP.
  3. Có tính linh hoạt cao hơn để chọn ellipsoid bạn muốn sử dụng.
  4. Nó có nhiều phương pháp để cho phép tính toán khác nhau trong tương lai.
  5. Nó cũng được ghi lại.

VincentyDistanceCalculator

1

Phương pháp này sẽ giúp bạn tìm được khoảng cách giữa vị trí địa lý ở km.

private double getDist(double lat1, double lon1, double lat2, double lon2) 
{ 
    int R = 6373; // radius of the earth in kilometres 
    double lat1rad = Math.toRadians(lat1); 
    double lat2rad = Math.toRadians(lat2); 
    double deltaLat = Math.toRadians(lat2-lat1); 
    double deltaLon = Math.toRadians(lon2-lon1); 

    double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) + 
      Math.cos(lat1rad) * Math.cos(lat2rad) * 
      Math.sin(deltaLon/2) * Math.sin(deltaLon/2); 
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 

    double d = R * c; 
    return d; 
} 
Các vấn đề liên quan