Функция для расчета расстояния между двумя координатами

137

В настоящее время я использую указанную ниже функцию, и она работает некорректно. Согласно Google Maps, расстояние между этими координатами (от 59.3293371,13.4877472до 59.3225525,13.4619422) - 2.2километры, а функция возвращает 1.6километры. Как я могу заставить эту функцию возвращать правильное расстояние?

function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  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;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

jsFiddle: http://jsfiddle.net/edgren/gAHJB/

Airikr
источник
4
Я пришел сюда, чтобы получить эту формулу, спасибо :)
Дэвид

Ответы:

175

То, что вы используете, называется формулой гаверсинуса , которая вычисляет расстояние между двумя точками на сфере по прямой . В предоставленной вами ссылке Google Maps указано расстояние 2,2 км, потому что это не прямая линия.

Wolphram Alpha - отличный ресурс для выполнения географических расчетов, он также показывает расстояние 1,652 км между этими двумя точками .

Расстояние проезда в зависимости от расстояния по прямой (красная линия).

Если вы ищете расстояние по прямой (как файлы вороны), ваша функция работает правильно. Если вам нужно расстояние на автомобиле (или расстояние на велосипеде, или расстояние до общественного транспорта, или расстояние пешком), вам придется использовать API картографирования ( Google или Bing являются наиболее популярными), чтобы получить соответствующий маршрут, который будет включать расстояние.

Между прочим, API Карт Google предоставляет пакетный метод для определения сферического расстояния в своем google.maps.geometry.sphericalпространстве имен (ищите computeDistanceBetween). Это, вероятно, лучше, чем катать свой собственный (для начала, здесь используется более точное значение радиуса Земли).

Для придирчивых из нас, когда я говорю «прямолинейное расстояние», я имею в виду «прямую линию на сфере», которая, конечно же, является изогнутой линией (то есть расстоянием по дуге большого круга).

Итан Браун
источник
5
С удовольствием! Было весело отвечать.
Итан Браун
5
Прекрасный ответ. Я говорю красиво, потому что представленные детали слишком хороши для понимания разницы даже для новичка, такого как я.
Supreet
78

Я писал подобное уравнение раньше - тестировал и тоже получил 1,6 км.

Ваши карты Google показывали ПРОДОЛЬНОЕ расстояние.

Ваша функция вычисляет по прямой (расстояние по прямой).

alert(calcCrow(59.3293371,13.4877472,59.3225525,13.4619422).toFixed(1));



    //This function takes in latitude and longitude of two location and returns the distance between them as the crow flies (in km)
    function calcCrow(lat1, lon1, lat2, lon2) 
    {
      var R = 6371; // km
      var dLat = toRad(lat2-lat1);
      var dLon = toRad(lon2-lon1);
      var lat1 = toRad(lat1);
      var lat2 = toRad(lat2);

      var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
      var d = R * c;
      return d;
    }

    // Converts numeric degrees to radians
    function toRad(Value) 
    {
        return Value * Math.PI / 180;
    }
Дерек
источник
5
Я думаю, что ваши имена переменных слишком
информативны
14

Решение Дерека отлично сработало для меня, и я просто преобразовал его на PHP, надеюсь, он кому-то поможет!

function calcCrow($lat1, $lon1, $lat2, $lon2){
        $R = 6371; // km
        $dLat = toRad($lat2-$lat1);
        $dLon = toRad($lon2-$lon1);
        $lat1 = toRad($lat1);
        $lat2 = toRad($lat2);

        $a = sin($dLat/2) * sin($dLat/2) +sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2); 
        $c = 2 * atan2(sqrt($a), sqrt(1-$a)); 
        $d = $R * $c;
        return $d;
}

// Converts numeric degrees to radians
function toRad($Value) 
{
    return $Value * pi() / 180;
}
Behzad
источник
5

Попробуй это. Он находится на VB.net, и вам нужно преобразовать его в Javascript. Эта функция принимает параметры в десятичных минутах.

    Private Function calculateDistance(ByVal long1 As String, ByVal lat1 As String, ByVal long2 As String, ByVal lat2 As String) As Double
    long1 = Double.Parse(long1)
    lat1 = Double.Parse(lat1)
    long2 = Double.Parse(long2)
    lat2 = Double.Parse(lat2)

    'conversion to radian
    lat1 = (lat1 * 2.0 * Math.PI) / 60.0 / 360.0
    long1 = (long1 * 2.0 * Math.PI) / 60.0 / 360.0
    lat2 = (lat2 * 2.0 * Math.PI) / 60.0 / 360.0
    long2 = (long2 * 2.0 * Math.PI) / 60.0 / 360.0

    ' use to different earth axis length
    Dim a As Double = 6378137.0        ' Earth Major Axis (WGS84)
    Dim b As Double = 6356752.3142     ' Minor Axis
    Dim f As Double = (a - b) / a        ' "Flattening"
    Dim e As Double = 2.0 * f - f * f      ' "Eccentricity"

    Dim beta As Double = (a / Math.Sqrt(1.0 - e * Math.Sin(lat1) * Math.Sin(lat1)))
    Dim cos As Double = Math.Cos(lat1)
    Dim x As Double = beta * cos * Math.Cos(long1)
    Dim y As Double = beta * cos * Math.Sin(long1)
    Dim z As Double = beta * (1 - e) * Math.Sin(lat1)

    beta = (a / Math.Sqrt(1.0 - e * Math.Sin(lat2) * Math.Sin(lat2)))
    cos = Math.Cos(lat2)
    x -= (beta * cos * Math.Cos(long2))
    y -= (beta * cos * Math.Sin(long2))
    z -= (beta * (1 - e) * Math.Sin(lat2))

    Return Math.Sqrt((x * x) + (y * y) + (z * z))
End Function

Изменить преобразованную функцию в javascript

function calculateDistance(lat1, long1, lat2, long2)
  {    

      //radians
      lat1 = (lat1 * 2.0 * Math.PI) / 60.0 / 360.0;      
      long1 = (long1 * 2.0 * Math.PI) / 60.0 / 360.0;    
      lat2 = (lat2 * 2.0 * Math.PI) / 60.0 / 360.0;   
      long2 = (long2 * 2.0 * Math.PI) / 60.0 / 360.0;       


      // use to different earth axis length    
      var a = 6378137.0;        // Earth Major Axis (WGS84)    
      var b = 6356752.3142;     // Minor Axis    
      var f = (a-b) / a;        // "Flattening"    
      var e = 2.0*f - f*f;      // "Eccentricity"      

      var beta = (a / Math.sqrt( 1.0 - e * Math.sin( lat1 ) * Math.sin( lat1 )));    
      var cos = Math.cos( lat1 );    
      var x = beta * cos * Math.cos( long1 );    
      var y = beta * cos * Math.sin( long1 );    
      var z = beta * ( 1 - e ) * Math.sin( lat1 );      

      beta = ( a / Math.sqrt( 1.0 -  e * Math.sin( lat2 ) * Math.sin( lat2 )));    
      cos = Math.cos( lat2 );   
      x -= (beta * cos * Math.cos( long2 ));    
      y -= (beta * cos * Math.sin( long2 ));    
      z -= (beta * (1 - e) * Math.sin( lat2 ));       

      return (Math.sqrt( (x*x) + (y*y) + (z*z) )/1000);  
    }
Noorul
источник
6
这个问题问了JavaScript的答案.. Вы должны перевести его на английский :)
VulfCompressor
2
добавлена ​​версия javascript
Noorul
Попробуйте использовать более понятные имена для своих переменных. Не бойтесь быть многословным, так как в настоящее время javascript обычно минифицирован, а имена переменных становятся полезными только для людей. например: const earthsMajorAccess = 6378137.0; устранение необходимости в полезных комментариях (поскольку имя переменной подразумевает, что это такое)
Адриано Майкл
Эта функция была написана более 5 лет назад. Вы можете вносить изменения :)
Noorul
2

Рассчитать расстояние между двумя точками в javascript

function distance(lat1, lon1, lat2, lon2, unit) {
        var radlat1 = Math.PI * lat1/180
        var radlat2 = Math.PI * lat2/180
        var theta = lon1-lon2
        var radtheta = Math.PI * theta/180
        var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
        dist = Math.acos(dist)
        dist = dist * 180/Math.PI
        dist = dist * 60 * 1.1515
        if (unit=="K") { dist = dist * 1.609344 }
        if (unit=="N") { dist = dist * 0.8684 }
        return dist
}

Для получения дополнительной информации см. Ссылку на ссылку

Манджунатх Билвар
источник
2

Исходный код с использованием формулы Хаверсина :

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//:::                                                                         :::
//:::  This routine calculates the distance between two points (given the     :::
//:::  latitude/longitude of those points). It is being used to calculate     :::
//:::  the distance between two locations using GeoDataSource (TM) prodducts  :::
//:::                                                                         :::
//:::  Definitions:                                                           :::
//:::    South latitudes are negative, east longitudes are positive           :::
//:::                                                                         :::
//:::  Passed to function:                                                    :::
//:::    lat1, lon1 = Latitude and Longitude of point 1 (in decimal degrees)  :::
//:::    lat2, lon2 = Latitude and Longitude of point 2 (in decimal degrees)  :::
//:::    unit = the unit you desire for results                               :::
//:::           where: 'M' is statute miles (default)                         :::
//:::                  'K' is kilometers                                      :::
//:::                  'N' is nautical miles                                  :::
//:::                                                                         :::
//:::  Worldwide cities and other features databases with latitude longitude  :::
//:::  are available at https://www.geodatasource.com                         :::
//:::                                                                         :::
//:::  For enquiries, please contact sales@geodatasource.com                  :::
//:::                                                                         :::
//:::  Official Web site: https://www.geodatasource.com                       :::
//:::                                                                         :::
//:::               GeoDataSource.com (C) All Rights Reserved 2018            :::
//:::                                                                         :::
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

function distance(lat1, lon1, lat2, lon2, unit) {
    if ((lat1 == lat2) && (lon1 == lon2)) {
        return 0;
    }
    else {
        var radlat1 = Math.PI * lat1/180;
        var radlat2 = Math.PI * lat2/180;
        var theta = lon1-lon2;
        var radtheta = Math.PI * theta/180;
        var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
        if (dist > 1) {
            dist = 1;
        }
        dist = Math.acos(dist);
        dist = dist * 180/Math.PI;
        dist = dist * 60 * 1.1515;
        if (unit=="K") { dist = dist * 1.609344 }
        if (unit=="N") { dist = dist * 0.8684 }
        return dist;
    }
}

Образец кода находится под лицензией LGPLv3.

Saikat
источник
1

Я написал функцию, чтобы найти расстояние между двумя координатами. Он вернет расстояние в метрах.

 function findDistance() {
   var R = 6371e3; // R is earth’s radius
   var lat1 = 23.18489670753479; // starting point lat
   var lat2 = 32.726601;         // ending point lat
   var lon1 = 72.62524545192719; // starting point lon
   var lon2 = 74.857025;         // ending point lon
   var lat1radians = toRadians(lat1);
   var lat2radians = toRadians(lat2);

   var latRadians = toRadians(lat2-lat1);
   var lonRadians = toRadians(lon2-lon1);

   var a = Math.sin(latRadians/2) * Math.sin(latRadians/2) +
        Math.cos(lat1radians) * Math.cos(lat2radians) *
        Math.sin(lonRadians/2) * Math.sin(lonRadians/2);
   var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

   var d = R * c;

   console.log(d)
}

function toRadians(val){
    var PI = 3.1415926535;
    return val / 180.0 * PI;
}
Санджай
источник
1

Расстояние по дуге большого круга - От длины хорды

Вот элегантное решение, использующее шаблон разработки стратегии; Надеюсь, он достаточно читабельный.

TwoPointsDistanceCalculatorStrategy.js :

module.exports = () =>

class TwoPointsDistanceCalculatorStrategy {

    constructor() {}

    calculateDistance({ point1Coordinates, point2Coordinates }) {}
};

GreatCircleTwoPointsDistanceCalculatorStrategy.js:

module.exports = ({ TwoPointsDistanceCalculatorStrategy }) =>

class GreatCircleTwoPointsDistanceCalculatorStrategy extends TwoPointsDistanceCalculatorStrategy {

    constructor() {
        super();
    }

    /**
     * Following the algorithm documented here: 
     * https://en.wikipedia.org/wiki/Great-circle_distance#Computational_formulas
     * 
     * @param {object} inputs
     * @param {array} inputs.point1Coordinates
     * @param {array} inputs.point2Coordinates
     * 
     * @returns {decimal} distance in kelometers
     */
    calculateDistance({ point1Coordinates, point2Coordinates }) {

        const convertDegreesToRadians = require('../convert-degrees-to-radians');
        const EARTH_RADIUS = 6371;   // in kelometers

        const [lat1 = 0, lon1 = 0] = point1Coordinates;
        const [lat2 = 0, lon2 = 0] = point2Coordinates;

        const radianLat1 = convertDegreesToRadians({ degrees: lat1 });
        const radianLon1 = convertDegreesToRadians({ degrees: lon1 });
        const radianLat2 = convertDegreesToRadians({ degrees: lat2 });
        const radianLon2 = convertDegreesToRadians({ degrees: lon2 });

        const centralAngle = _computeCentralAngle({ 
            lat1: radianLat1, lon1: radianLon1, 
            lat2: radianLat2, lon2: radianLon2, 
        });

        const distance = EARTH_RADIUS * centralAngle;

        return distance;
    }
};


/**
 * 
 * @param {object} inputs
 * @param {decimal} inputs.lat1
 * @param {decimal} inputs.lon1
 * @param {decimal} inputs.lat2
 * @param {decimal} inputs.lon2
 * 
 * @returns {decimal} centralAngle
 */
function _computeCentralAngle({ lat1, lon1, lat2, lon2 }) {

    const chordLength = _computeChordLength({ lat1, lon1, lat2, lon2 });
    const centralAngle = 2 * Math.asin(chordLength / 2);

    return centralAngle;
}


/**
 * 
 * @param {object} inputs
 * @param {decimal} inputs.lat1
 * @param {decimal} inputs.lon1
 * @param {decimal} inputs.lat2
 * @param {decimal} inputs.lon2
 * 
 * @returns {decimal} chordLength
 */
function _computeChordLength({ lat1, lon1, lat2, lon2 }) {

    const { sin, cos, pow, sqrt } = Math;

    const ΔX = cos(lat2) * cos(lon2) - cos(lat1) * cos(lon1);
    const ΔY = cos(lat2) * sin(lon2) - cos(lat1) * sin(lon1);
    const ΔZ = sin(lat2) - sin(lat1);

    const ΔXSquare = powX, 2);
    const ΔYSquare = powY, 2);
    const ΔZSquare = powZ, 2);

    const chordLength = sqrtXSquare + ΔYSquare + ΔZSquare);

    return chordLength;
}

конвертировано градусов к radians.js:

module.exports = function convertDegreesToRadians({ degrees }) {

    return degrees * Math.PI / 180;
};

Это расстояние по Большому кругу - от длины хорды, задокументированной здесь .

Лукан Эль-Кади
источник
0

Посетите этот адрес. https://www.movable-type.co.uk/scripts/latlong.html Вы можете использовать этот код:

JavaScript:     

const R = 6371e3; // metres
const φ1 = lat1 * Math.PI/180; // φ, λ in radians
const φ2 = lat2 * Math.PI/180;
const Δφ = (lat2-lat1) * Math.PI/180;
const Δλ = (lon2-lon1) * Math.PI/180;

const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
          Math.cos1) * Math.cos2) *
          Math.sin(Δλ/2) * Math.sin(Δλ/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

const d = R * c; // in metres
Али Хосейн налей
источник