IT TIP

Java에서 두 개의 위도 + 경도 지점을 기반으로 거리를 측정하고 경계 상자를 만드는 방법은 무엇입니까?

itqueen 2020. 10. 17. 12:41
반응형

Java에서 두 개의 위도 + 경도 지점을 기반으로 거리를 측정하고 경계 상자를 만드는 방법은 무엇입니까?


두 지점 사이의 거리를 찾고 싶습니다. 내가 아는 이것은 대권 거리로 달성 할 수 있습니다. http://www.meridianworlddata.com/Distance-calculation.asp

완료되면 점과 거리를 사용하여 점 주위에 상자를 만들기 위해 북쪽으로 그 거리와 동쪽으로 그 거리를 찾고 싶습니다.


OpenMap사용하여 많은 위치 데이터를 그리는 데 성공했습니다 . 거리를 포함한 몇 가지 기본 기능이 있는 LatLonPoint 클래스가 있습니다.


다음은 Haversine 공식 의 Java 구현입니다 . 나는 이것을 프로젝트에서 위도 / 경도 사이의 거리를 마일 단위로 계산하는 데 사용합니다.

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

또는 SimpleLatLng를 사용할 수 있습니다 . Apache 2.0은 내가 아는 하나의 프로덕션 시스템에서 라이센스를 받고 사용했습니다.

단편:

간단한 지리 라이브러리를 찾고 있었는데 내 필요에 맞는 라이브러리를 찾을 수 없었습니다. 그리고 누가 모든 애플리케이션에서이 작은 지리 도구를 반복해서 작성하고 테스트하고 디버그하고 싶습니까? 더 나은 방법이 있어야합니다!

따라서 SimpleLatLng는 위도-경도 데이터를 저장하고, 거리 계산을 수행하고, 모양이있는 경계를 만드는 방법으로 탄생했습니다.

원본 포스터를 돕기에는 2 년이 너무 늦었지만 제 목표는 검색에서이 질문을 찾는 저와 같은 사람들을 돕는 것입니다. 나는 일부 사람들이 그것을 사용하고이 작은 경량 유틸리티의 테스트와 비전에 기여하고 싶습니다.


보다 정확한 거리 (0.5mm)를 위해 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;
}

이 코드는 http://www.movable-type.co.uk/scripts/latlong-vincenty.html 에서 자유롭게 수정되었습니다 .


Haversine 거리 공식 수정 ....

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

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

GPS 용 Java Geodesy Library를 사용할 수 있으며 지구 표면 곡률을 고려한 Vincenty의 공식사용합니다 .

구현은 다음과 같습니다.

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

결과 거리는 미터 단위입니다.


많은 답변이 있다는 것을 알고 있지만이 주제에 대한 조사를하면서 여기에서 대부분의 답변이 Haversine 공식을 사용한다는 것을 알았습니다. 그러나 Vincenty 공식은 실제로 더 정확합니다. Javascript 버전의 계산을 적용한 게시물이 하나 있었지만 매우 다루기 어렵습니다. 다음과 같은 이유로 더 우수한 버전을 찾았습니다.

  1. 또한 공개 라이센스가 있습니다.
  2. OOP 원칙을 사용합니다.
  3. It has greater flexibility to choose the ellipsoid you want to use.
  4. It has more methods to allow for different calculations in the future.
  5. It is well documented.

VincentyDistanceCalculator


This method would help you find the distance between to geographic location in 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;
}

I typically use MATLAB with the Mapping Toolbox, and then use the code in my Java using MATLAB Builder JA. It makes my life a lot simpler. Given most schools have it for free student access, you can try it out (or get the trial version to get over your work).


For Android, there is a simple approach.

 public static float getDistanceInMeter(LatLng start, LatLng end) { 
    float[] results = new float[1];
    Location.distanceBetween(start.latitude, start.longitude, end.latitude, end.longitude, results);
    return results[0];

}

;

https://developer.android.com/reference/android/location/Location#distanceBetween(lat1,lng1,lat2,lng2,output[])


Kotlin version of Haversine formula. Returned result in meters. Tested on https://www.vcalc.com/wiki/vCalc/Haversine+-+Distance

const val EARTH_RADIUS_IN_METERS = 6371007.177356707

fun distance(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double {
    val latDiff = Math.toRadians(abs(lat2 - lat1))
    val lngDiff = Math.toRadians(abs(lng2 - lng1))
    val a = sin(latDiff / 2) * sin(latDiff / 2) +
        cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
        sin(lngDiff / 2) * sin(lngDiff / 2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return EARTH_RADIUS_IN_METERS * c
}

참고URL : https://stackoverflow.com/questions/120283/how-can-i-measure-distance-and-create-a-bounding-box-based-on-two-latitudelongi

반응형