Рассчитать расстояние между двумя точками широты и долготы? (Формула Haversine) - PullRequest
807 голосов
/ 26 августа 2008

Как рассчитать расстояние между двумя точками, указанными по широте и долготе?

Для пояснения, я бы хотел расстояние в километрах; точки используют систему WGS84, и я хотел бы понять относительную точность доступных подходов.

Ответы [ 37 ]

7 голосов
/ 20 июля 2014

Мне не нравится добавлять еще один ответ, но API карт Google 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 или даже какая модель используется (хотя в ней говорится «сферический», а не «геоидный»). Кстати, расстояние по «прямой», очевидно, будет отличаться от расстояния, если путешествует по поверхности земли, что, как кажется, все предполагают.

6 голосов
/ 17 июня 2016

Все приведенные выше ответы предполагают, что земля является сферой. Однако, более точное приближение было бы приближением сплющенного сфероида.

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 голосов
/ 10 февраля 2016

Может быть более простое решение и более правильное: периметр земли составляет 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 это значительно уменьшило ошибку.

6 голосов
/ 14 июля 2015

Python implimentation Происхождение является центром смежных Соединенных Штатов.

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

Чтобы получить ответ в километрах, просто установите мили = false.

5 голосов
/ 26 августа 2008

Это скорее зависит от того, насколько точным вы хотите быть и на каком уровне определены широта и длина Очень, очень приблизительно, вы делаете небольшой сферический триггер, но поправка на то, что земля не является сферой, усложняет формулы

5 голосов
/ 12 декабря 2015

Вот машинопись реализация формулы Хаверсайна

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;
}
4 голосов
/ 09 октября 2018

Вот реализация SQL для расчета расстояния в км,

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;
4 голосов
/ 21 февраля 2017

Этот скрипт [в 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;
        }
    }
3 голосов
/ 19 октября 2008

Чтобы вычислить расстояние между двумя точками на сфере, вам нужно выполнить Расчет по Большому кругу .

Существует ряд библиотек C / C ++, которые могут помочь с проекцией карты на MapTools , если вам необходимо перепроецировать ваши расстояния на плоскую поверхность. Для этого вам понадобится проекционная строка различных систем координат.

Вы также можете найти MapWindow полезным инструментом для визуализации точек. Кроме того, в качестве открытого источника полезное руководство по использованию библиотеки proj.dll, которая является базовой библиотекой проекций с открытым исходным кодом.

3 голосов
/ 12 октября 2015

Вот принятая реализация ответа, портированная на 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:
 * /16405/rasschitat-rasstoyanie-mezhdu-dvumya-tochkami-shiroty-i-dolgoty-formula-haversine,
 * 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);
}
...