Як я можу виміряти відстань і створити обмежувальну рамку на основі двох точок широти + довготи в Java?


75

Я хочу знайти відстань між двома різними точками. Це, я знаю, можна досягти за великої відстані кола. http://www.meridianworlddata.com/Distance-calculation.asp

Закінчивши, з точкою та відстанню я хотів би знайти точку, яка відстань на північ, і цю відстань на схід, щоб створити поле навколо точки.


1
зверніться до цього блогу xebee.xebia.in/2010/10/28/working-with-geolocations
Робін

Відповіді:


21

Ми досягли певного успіху за допомогою OpenMap для побудови багато позиційних даних. Існує клас LatLonPoint, який має деякі основні функціональні можливості, включаючи відстань.


5
Попередження для можливих усиновителів: Я щойно зіткнувся з ВЕЛИКОЮ проблемою з OpenMap; вони внутрішньо використовують плаваючі знаки для десяткової ширини / довготи, що обмежує точність залежно від того, наскільки ви близькі до екватора. Вони планують підтримати опцію для парних з їхніми класами OMGraphic, починаючи з версії 4.7, але поточна стабільна лише 4.6.5 (станом на березень 2010). Джерело: openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.html
Марк

Також зауважте, що чинна ліцензійна угода для програмного забезпечення OpenMap openmap.bbn.com/license.html визнана невільною. web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Деякі обговорення з BBN мали місце для зміни ліцензій, але ще нічого не сталося.
Лейф Груенвольт

3
Ця відповідь зараз застаріла, оскільки обидва посилання не в мережі.
xivo

Я щойно виправив посилання на новий домен на openmap-java.org . Ліцензія тепер тут: openmap-java.org/License.html (не знаю, чи була вона змінена після коментаря від @LeifGruenwoldt
Rüdiger Schulz

152

Ось реалізація Java формули Haversine . Я використовую це в проекті для розрахунку відстані в милях між широтою / довжиною.

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

23
Тільки примітка щодо цього, він поверне відстань у милях (через налаштування earthRadius). Для інших одиниць зміни earthRadius (див en.wikipedia.org/wiki/Earth_radius для більш)
Джон Мігер

Чи є причина, що ви використовуєте поплавці замість подвійних? Якщо я добре розумію, ви можете підвищити точність результатів, просто змінивши типи вхідних параметрів
Hamy

Також немає необхідності обчислювати синус половини дельт двічі. Обчисліть його один раз і помножте на себе. Це, безумовно, варто затягнути.
Гленн Бех,

1
Висновок для цього можна знайти на mathforum.org/library/drmath/view/51879.html
Ананд Сандерраман,

6
Використовуйте earthRadius як 6371, щоб отримати результат у кілометрах .
stevo.mit

45

Або ви можете використовувати SimpleLatLng . Apache 2.0 ліцензований і використовується в одній виробничій системі, про яку я знаю: моїй.

Коротка розповідь:

Я шукав просту геотеку і не міг знайти її, яка відповідала б моїм потребам. А хто хоче писати, тестувати та налагоджувати ці маленькі геоінструменти знову і знову у кожному додатку? Має бути кращий спосіб!

Тож SimpleLatLng народився як спосіб зберігання даних широти та довготи, обчислення відстані та створення фігурних меж.

Я знаю, що я запізнився на два роки, щоб допомогти оригінальному плакату, але моя мета - допомогти таким людям, як я, які знаходять це питання в пошуку. Я хотів би, щоб деякі люди використовували його та сприяли випробуванню та баченню цієї маленької полегшеної утиліти.


це може мені допомогти! ти його створив? Чи використовуєте ви формулу Гаверсіна для розрахунків відстані ?? Я спробую заскочити, якщо знайду час!
Марселлус Уоллес

Правильно, він використовує Haversine для обчислення відстані з акцентом на (хоча, правда, не одержимість) швидкістю та низьким профілем пам'яті. Я думаю, що у нього є й деякі інші приємні властивості обробки чисел, наприклад, розглядати рівні "дійсно близькі" координати рівними.
JavadocMD

11

Для більш точної відстані (0,5 мм) ви також можете скористатися наближенням Вінсенті:

/**
 * 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


6

Виправлена ​​формула Haversine Distance ....

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

Земля не є ідеальною сферою
Стів Куо,

Правильний, іноді близький - це досить добре. IE: Чому містер Інман створив його. Я б сказав йому, що він помиляється, але він мертвий. : o (Якщо вам потрібно розрахувати довгасту форму світу, для цього існують кращі формули. Є також кілька чудових бібліотек apache, якими ви можете скористатися. Якщо вам просто потрібно щось просте, це хороший швидкий приклад . :)
Matthew Hager

1
Так, формула Haversine побудована на принципі "близько досить добре". У той час ми вимірювали відстані, що були менше 50 миль, щоб визначити близькість від одного місця до іншого як "евристичний".
Matthew Hager

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

Ви можете використовувати бібліотеку геодезії Java для GPS , вона використовує формули Вінсенті, які враховують викривлення поверхні землі.

Впровадження відбувається так:

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

Отримана відстань у метрах.


1

Я знаю, що є багато відповідей, але, проводячи деякі дослідження на цю тему, я виявив, що більшість відповідей тут використовують формулу Гаверсіна, але формула Вінсенті насправді є більш точною. Був один допис, який адаптував розрахунок за версією Javascript, але це дуже громіздко. Я знайшов версію, яка є вищою, оскільки:

  1. Він також має відкриту ліцензію.
  2. Він використовує принципи ООП.
  3. Він має більшу гнучкість у виборі еліпсоїда, який ви хочете використовувати.
  4. Він має більше методів, що дозволяють проводити різні розрахунки в майбутньому.
  5. Це добре задокументовано.

VincentyDistanceCalculator


1

Цей метод допоможе вам знайти відстань від географічного розташування в км.

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

1

Котлінський варіант формули Гаверсин. Повернутий результат у метрах. Перевірено на 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
}

привіт, щоб я міг використовувати це як геозону, правильно ???? Код геозони на Android не працює для мене з якоїсь невідомої причини, і я думав використати це як альтернативу
Pemba

0

Зазвичай я використовую MATLAB із набором інструментів відображення , а потім використовую код у своїй Java за допомогою MATLAB Builder JA. Це робить моє життя набагато простішим. Враховуючи, що більшість шкіл мають безкоштовний доступ для студентів, ви можете спробувати (або отримати пробну версію, щоб закінчити свою роботу).


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.