Funkcja do obliczania odległości między dwoma współrzędnymi

145

Obecnie używam poniższej funkcji i nie działa ona poprawnie. Według Google Maps odległość między tymi współrzędnymi (od 59.3293371,13.4877472do 59.3225525,13.4619422) to 2.2kilometry, podczas gdy funkcja zwraca 1.6kilometry. Jak sprawić, by ta funkcja zwracała prawidłową odległość?

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
źródło
4
Przyszedłem tutaj tylko po to, aby zdobyć tę formułę, dzięki :)
David Callanan

Odpowiedzi:

182

To, czego używasz, nazywa się formułą haversine , która oblicza odległość między dwoma punktami na kuli w linii prostej . Podany przez Ciebie link do Map Google pokazuje odległość jako 2,2 km, ponieważ nie jest to linia prosta.

Wolphram Alpha to świetne źródło do wykonywania obliczeń geograficznych, a także pokazuje odległość 1,652 km między tymi dwoma punktami .

Odległość jazdy a odległość w linii prostej (moja czerwona linia).

Jeśli szukasz odległości w linii prostej (jak pliki wrona), twoja funkcja działa poprawnie. Jeśli chcesz pokonać odległość (lub odległość jazdy rowerem, odległość transportu publicznego lub odległość pieszo), musisz użyć interfejsu API mapowania ( najpopularniejsze są Google lub Bing ), aby uzyskać odpowiednią trasę, która będzie zawierać odległość.

Nawiasem mówiąc, interfejs API Map Google zapewnia spakowaną metodę odległości sferycznej w swojej google.maps.geometry.sphericalprzestrzeni nazw (szukaj computeDistanceBetween). To prawdopodobnie lepsze niż toczenie własnym (na początek używa dokładniejszej wartości promienia Ziemi).

Dla wybrednych z nas, kiedy mówię „odległość w linii prostej”, mam na myśli „linię prostą na kuli”, która jest w rzeczywistości linią zakrzywioną (tj. Ortodromą), oczywiście.

Ethan Brown
źródło
7
Cała przyjemność po mojej stronie! Fajnie było odpowiedzieć.
Ethan Brown,
7
To piękna odpowiedź. Mówię pięknie, ponieważ podane szczegóły są zbyt dobre, by zrozumieć różnicę nawet dla nowicjusza takiego jak ja.
Supreet
84

Podobne równanie napisałem wcześniej - przetestowałem i też dostałem 1,6 km.

Twoje mapy Google pokazywały odległość JAZDY.

Twoja funkcja oblicza w linii prostej (odległość w linii prostej).

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;
    }
Derek
źródło
6
Myślę, że twoje nazwy zmiennych są zbyt opisowe :)
pie6k
16

Rozwiązanie Dereka działało dobrze dla mnie i właśnie przekonwertowałem je na PHP, mam nadzieję, że komuś pomoże!

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
źródło
6

Spróbuj tego. Znajduje się w VB.net i musisz przekonwertować go na JavaScript. Ta funkcja przyjmuje parametry w minutach dziesiętnych.

    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

Edytuj Przekonwertowana funkcja w 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
źródło
7
这个 问题 问 了 JavaScript 的 答案 。. Musisz przekonwertować go na angielski :)
VulfCompressor
2
dodano wersję javascript
Noorul
Spróbuj użyć bardziej znaczących nazw dla swoich zmiennych. Nie bój się być rozwlekłym, ponieważ obecnie javascript jest zwykle zminimalizowany, a nazwy zmiennych stają się przydatne tylko dla ludzi. na przykład: const earthsMajorAccess = 6378137.0; wyeliminowanie potrzeby przydatnych komentarzy (ponieważ nazwa zmiennej wskazuje, czym ona jest)
Adriano Michael
Ta funkcja została napisana ponad 5 lat temu. Zapraszamy do modyfikacji :)
Noorul
4

Używając formuły Haversine, źródło kodu :

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//:::                                                                         :::
//:::  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 [email protected]                  :::
//:::                                                                         :::
//:::  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;
    }
}

Przykładowy kod jest objęty licencją LGPLv3.

Saikat
źródło
3

Oblicz odległość między dwoma punktami w 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
}

Aby uzyskać więcej informacji, zobacz: Link referencyjny

Manjunath Bilwar
źródło
2

Napisałem funkcję do znajdowania odległości między dwoma współrzędnymi. Zwróci odległość w metrach.

 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;
}
Sanjay
źródło
2

Odległość po ortodromie - od długości cięciwy

Oto eleganckie rozwiązanie wykorzystujące wzorzec projektowania strategii; Mam nadzieję, że jest wystarczająco czytelny.

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 = pow(ΔX, 2);
    const ΔYSquare = pow(ΔY, 2);
    const ΔZSquare = pow(ΔZ, 2);

    const chordLength = sqrt(ΔXSquare + ΔYSquare + ΔZSquare);

    return chordLength;
}

konwersja-stopni na radian.js:

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

    return degrees * Math.PI / 180;
};

Jest to zgodne z odległością wielkiego koła - od długości cięciwy, udokumentowaną tutaj .

Loukan ElKadi
źródło
1

Dodanie tego dla użytkowników Node.JS. Możesz skorzystać z haversine-distancemodułu, aby to zrobić, więc nie będziesz musiał samodzielnie wykonywać obliczeń. Więcej informacji można znaleźć na stronie npm .

Żeby zainstalować:

npm install --save haversine-distance

Z modułu możesz korzystać w następujący sposób:

var haversine = require("haversine-distance");

//First point in your haversine calculation
var point1 = { lat: 6.1754, lng: 106.8272 }

//Second point in your haversine calculation
var point2 = { lat: 6.1352, lng: 106.8133 }

var haversine_m = haversine(point1, point2); //Results in meters (default)
var haversine_km = haversine_m /1000; //Results in kilometers

console.log("distance (in meters): " + haversine_m + "m");
console.log("distance (in kilometers): " + haversine_km + "km");
Nicholas Smith
źródło
0

Odwiedź ten adres. https://www.movable-type.co.uk/scripts/latlong.html Możesz użyć tego kodu:

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.cos(φ1) * Math.cos(φ2) *
          Math.sin(Δλ/2) * Math.sin(Δλ/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

const d = R * c; // in metres
Ali Hosein wlać
źródło