2 GPS koordinatı arasındaki mesafeyi hesapla


Yanıtlar:


406

Javascript uygulaması da dahil olmak üzere iki koordinat arasındaki mesafeyi enlem ve boylam ile hesaplayın .

Batı ve Güney konumları olumsuzdur. Dakikaların ve saniyelerin 60'ın dışında olduğunu unutmayın, bu nedenle S31 30 '-31.50 derecedir.

Dereceleri radyana çevirmeyi unutmayın . Birçok dilde bu işlev vardır. Ya da basit bir hesaplama: radians = degrees * PI / 180.

function degreesToRadians(degrees) {
  return degrees * Math.PI / 180;
}

function distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
  var earthRadiusKm = 6371;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(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)); 
  return earthRadiusKm * c;
}

İşte bazı kullanım örnekleri:

distanceInKmBetweenEarthCoordinates(0,0,0,0)  // Distance between same 
                                              // points should be 0
0

distanceInKmBetweenEarthCoordinates(51.5, 0, 38.8, -77.1) // From London
                                                          // to Arlington
5918.185064088764

17
Durumda toRad () yöntemine bir özelleştirme olduğunu belli değil Numara prototip gibi: Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Veya aşağıda belirtildiği gibi, (Math.PI/2)daha yüksek performans için 0.0174532925199433 (... gerekli gördüğünüz hassasiyet) ile değiştirebilirsiniz .
Vinney Kelly

44
Özellikle satır sonu yorumları aramayanlarınız bu formüle bakıyor ve bir mesafe birimi arıyorsa, birim km'dir. :)
Dylan Knowles

1
@VinneyKelly Küçük yazım hatası ama yerine (Math.PI / 180) değil (Math.PI / 2), herkes yardım için teşekkürler
Patrick Murphy

1
@ChristianKRider İlk satıra bakın. RMatematikte normalde ne anlama geldiğini düşünün , sonra sayıların eşleşip eşleşmediğini görmek için ilgili Dünya ile ilgili miktarlara bakın.
Monica'nın Davası

3
Emperyal birim (mil) için, değişebilir earthRadiusKmolması var earthRadiusMiles = 3959;, Bilginize.
chapeljuice

59

Google'da haversin arayın; İşte benim çözüm:

#include <math.h>
#include "haversine.h"

#define d2r (M_PI / 180.0)

//calculate haversine distance for linear distance
double haversine_km(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 6367 * c;

    return d;
}

double haversine_mi(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 3956 * c; 

    return d;
}

3
Daha iyi performans için (M_PI / 180.0) öğesini 0.0174532925199433 ile değiştirebilirsiniz.
Hlung

3
Performans açısından: günah (dlat / 2.0) sadece bir kez hesaplanabilir, değişken a1'de saklanabilir ve pow (, 2) yerine a1 * a1 kullanmak daha iyidir. Diğer pow için de aynı (, 2).
pms

71
Evet, ya da sadece 60'lardan sonra bir derleyici kullan.
sağda

17
Bağlamsız kimsenin anlamadığı bir sabit için "optimize etmeye" (M_PI / 180.0) gerek yoktur. Derleyici bu sabit terimleri sizin için hesaplar!
Patrick Cornelissen

2
@ TõnuSamuel Yorumunuz için çok teşekkür ederim. Gerçekten onu takdir ederim. Optimizasyon etkin derleyicinin (-O) sabitlerin işlemlerini önceden hesaplayabilmesi manüel daralmayı işe yaramaz hale getirir. Zamanım olduğunda test edeceğim.
Hlung

44

Haversine'nin C # Sürümü

double _eQuatorialEarthRadius = 6378.1370D;
double _d2r = (Math.PI / 180D);

private int HaversineInM(double lat1, double long1, double lat2, double long2)
{
    return (int)(1000D * HaversineInKM(lat1, long1, lat2, long2));
}

private double HaversineInKM(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * _d2r;
    double dlat = (lat2 - lat1) * _d2r;
    double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r) * Math.Pow(Math.Sin(dlong / 2D), 2D);
    double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
    double d = _eQuatorialEarthRadius * c;

    return d;
}

İşte bunun bir .NET Fiddle'ı , böylece kendi Lat / Longs'unuzla test edebilirsiniz.


1
Ayrıca insanlar bunu kolayca test edebilmek için bir checky .NET keman ekledim.
Pure.Krome

7
Net Framework, GeoCoordinate.GetDistanceTo yönteminde bir yapıya sahiptir. Derleme System.Device başvurulmalıdır. MSDN Makalesi msdn.microsoft.com/tr-tr/library/…
fnx

27

Roman Makarov'un bu konuya verdiği cevaba dayanan Haversine Algoritmasının Java Versiyonu

public class HaversineAlgorithm {

    static final double _eQuatorialEarthRadius = 6378.1370D;
    static final double _d2r = (Math.PI / 180D);

    public static int HaversineInM(double lat1, double long1, double lat2, double long2) {
        return (int) (1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    public static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
                * Math.pow(Math.sin(dlong / 2D), 2D);
        double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;

        return d;
    }

}

@Radu, doğru kullandığınızdan ve lat / log yerlerini herhangi bir yönteme aktarırken değiştirmediğinizden emin olun.
Paulo Miguel Almeida

1
Bu formülü kullanarak oldukça yakın bir cevap aldım. Bu web sitesini kullanarak doğruluğu temel aldım: movable-type.co.uk/scripts/latlong.html ki bana 0.07149km verdi , formülünüz bana 0.07156yaklaşık% 99 doğruluk verdi
Janac Meena

24

Bu, SQL Server 2008'deki coğrafya türü ile çok kolaydır.

SELECT geography::Point(lat1, lon1, 4326).STDistance(geography::Point(lat2, lon2, 4326))
-- computes distance in meters using eliptical model, accurate to the mm

4326, WGS84 elipsoidal Earth modeli için SRID'dir


19

İşte Python'da kullandığım bir Haversine işlevi:

from math import pi,sqrt,sin,cos,atan2

def haversine(pos1, pos2):
    lat1 = float(pos1['lat'])
    long1 = float(pos1['long'])
    lat2 = float(pos2['lat'])
    long2 = float(pos2['long'])

    degree_to_rad = float(pi / 180.0)

    d_lat = (lat2 - lat1) * degree_to_rad
    d_long = (long2 - long1) * degree_to_rad

    a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2)
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    km = 6367 * c
    mi = 3956 * c

    return {"km":km, "miles":mi}


11

Burada C # 'da (radyanlarda lat ve uzun):

double CalculateGreatCircleDistance(double lat1, double long1, double lat2, double long2, double radius)
{
    return radius * Math.Acos(
        Math.Sin(lat1) * Math.Sin(lat2)
        + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(long2 - long1));
}

Enlem ve boyunuz derece cinsindeyse radyanlara dönüştürmek için 180 / PI'ye bölün.


1
Bu, büyük bir daire mesafesinin en az doğru ve en hataya eğilimli hesaplama yöntemi olan "kosinüslerin küresel yasası" hesaplamasıdır.
John Machin

11

Projem için noktalar arasında çok fazla mesafe hesaplamamız gerekiyordu, bu yüzden devam ettim ve kodu optimize etmeye çalıştım, burada buldum. Farklı tarayıcılarda ortalama olarak, yeni uygulamam en çok oylanan cevaptan 2 kat daha hızlı çalışıyor .

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

Benim jsPerf ile oynamak ve sonuçları burada görebilirsiniz .

Son zamanlarda python da aynısını yapmam gerekiyordu, işte bir python uygulaması :

from math import cos, asin, sqrt
def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295
    a = 0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2
    return 12742 * asin(sqrt(a))

Ve bütünlük uğruna: Wiki'de Haversine .


11

PHP sürümü:

( deg2rad()Koordinatlarınız zaten radyandaysa tümünü kaldırın .)

$R = 6371; // km
$dLat = deg2rad($lat2-$lat1);
$dLon = deg2rad($lon2-$lon1);
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($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;

1
Lütfen lat1 ve lat2'yi $ lat1 nad $ lat2 olarak değiştirin.
yerine

7

Bir merkezin mesafesine göre kayıtları seçmek için kullandığım bir T-SQL işlevi

Create Function  [dbo].[DistanceInMiles] 
 (  @fromLatitude float ,
    @fromLongitude float ,
    @toLatitude float, 
    @toLongitude float
  )
   returns float
AS 
BEGIN
declare @distance float

select @distance = cast((3963 * ACOS(round(COS(RADIANS(90-@fromLatitude))*COS(RADIANS(90-@toLatitude))+ 
SIN(RADIANS(90-@fromLatitude))*SIN(RADIANS(90-@toLatitude))*COS(RADIANS(@fromLongitude-@toLongitude)),15)) 
)as float) 
  return  round(@distance,1)
END

Bu, büyük bir daire mesafesinin en az doğru ve en hataya eğilimli hesaplama yöntemi olan "kosinüslerin küresel yasası" hesaplamasıdır.
John Machin

5

Daha doğru bir şeye ihtiyacınız varsa, buna bir göz atın .

Vincenty'nin formülleri, Thaddeus Vincenty (1975a) tarafından geliştirilen, bir kürenin yüzeyindeki iki nokta arasındaki mesafeyi hesaplamak için kullanılan jeodezide kullanılan iki ilişkili yinelemeli yöntemdir. küresel bir Dünya varsayan büyük daire mesafesi gibi yöntemlerden daha doğrudur.

İlk (doğrudan) yöntem, belirli bir mesafe ve azimut (yön) olan bir noktanın konumunu başka bir noktadan hesaplar. İkinci (ters) yöntem, verilen iki nokta arasındaki coğrafi mesafeyi ve azimutu hesaplar. Dünya elipsoidinde 0,5 mm (0,020 ″) içinde hassas oldukları için jeodezide yaygın olarak kullanılmaktadırlar.


5

I. "Galeta unu" yöntemi ile ilgili

  1. Dünya yarıçapı farklı Lat'ta farklıdır. Bu Haversine algoritmasında dikkate alınmalıdır.
  2. Düz çizgileri kemerlere (daha uzun olan) dönüştüren Rulman değişimini düşünün
  3. Hız değişikliğini hesaba katmak kemerleri spirallere (kemerlerden daha uzun veya daha kısa) çevirir
  4. İrtifa değişikliği düz spiralleri 3D spirallere (daha uzun olan) çevirecektir. Bu dağlık alanlar için çok önemlidir.

Aşağıda C'deki # 1 ve # 2'yi dikkate alan işlevi görün:

double   calcDistanceByHaversine(double rLat1, double rLon1, double rHeading1,
       double rLat2, double rLon2, double rHeading2){
  double rDLatRad = 0.0;
  double rDLonRad = 0.0;
  double rLat1Rad = 0.0;
  double rLat2Rad = 0.0;
  double a = 0.0;
  double c = 0.0;
  double rResult = 0.0;
  double rEarthRadius = 0.0;
  double rDHeading = 0.0;
  double rDHeadingRad = 0.0;

  if ((rLat1 < -90.0) || (rLat1 > 90.0) || (rLat2 < -90.0) || (rLat2 > 90.0)
              || (rLon1 < -180.0) || (rLon1 > 180.0) || (rLon2 < -180.0)
              || (rLon2 > 180.0)) {
        return -1;
  };

  rDLatRad = (rLat2 - rLat1) * DEGREE_TO_RADIANS;
  rDLonRad = (rLon2 - rLon1) * DEGREE_TO_RADIANS;
  rLat1Rad = rLat1 * DEGREE_TO_RADIANS;
  rLat2Rad = rLat2 * DEGREE_TO_RADIANS;

  a = sin(rDLatRad / 2) * sin(rDLatRad / 2) + sin(rDLonRad / 2) * sin(
              rDLonRad / 2) * cos(rLat1Rad) * cos(rLat2Rad);

  if (a == 0.0) {
        return 0.0;
  }

  c = 2 * atan2(sqrt(a), sqrt(1 - a));
  rEarthRadius = 6378.1370 - (21.3847 * 90.0 / ((fabs(rLat1) + fabs(rLat2))
              / 2.0));
  rResult = rEarthRadius * c;

  // Chord to Arc Correction based on Heading changes. Important for routes with many turns and U-turns

  if ((rHeading1 >= 0.0) && (rHeading1 < 360.0) && (rHeading2 >= 0.0)
              && (rHeading2 < 360.0)) {
        rDHeading = fabs(rHeading1 - rHeading2);
        if (rDHeading > 180.0) {
              rDHeading -= 180.0;
        }
        rDHeadingRad = rDHeading * DEGREE_TO_RADIANS;
        if (rDHeading > 5.0) {
              rResult = rResult * (rDHeadingRad / (2.0 * sin(rDHeadingRad / 2)));
        } else {
              rResult = rResult / cos(rDHeadingRad);
        }
  }
  return rResult;
}

II. Oldukça iyi sonuçlar veren daha kolay bir yol var.

Ortalama Hıza Göre.

Trip_distance = Trip_average_speed * Trip_time

GPS Hızı Doppler etkisi ile algılandığından ve [Lon, Lat] ile doğrudan ilişkili olmadığından, ana mesafe hesaplama yöntemi değilse en azından ikincil (yedekleme veya düzeltme) olarak düşünülebilir.


4

.NET kullanıyorsanız tekerleği yeniden takmayın. Bkz. System.Device.Location . Başka bir cevapta yorumlarda fnx'e teşekkür ederiz .

using System.Device.Location;

double lat1 = 45.421527862548828D;
double long1 = -75.697189331054688D;
double lat2 = 53.64135D;
double long2 = -113.59273D;

GeoCoordinate geo1 = new GeoCoordinate(lat1, long1);
GeoCoordinate geo2 = new GeoCoordinate(lat2, long2);

double distance = geo1.GetDistanceTo(geo2);

3

Bu Lua kodu Wikipedia'da ve Robert Lipe'nin GPSbabel aracında bulunanlardan uyarlanmıştır :

local EARTH_RAD = 6378137.0 
  -- earth's radius in meters (official geoid datum, not 20,000km / pi)

local radmiles = EARTH_RAD*100.0/2.54/12.0/5280.0;
  -- earth's radius in miles

local multipliers = {
  radians = 1, miles = radmiles, mi = radmiles, feet = radmiles * 5280,
  meters = EARTH_RAD, m = EARTH_RAD, km = EARTH_RAD / 1000, 
  degrees = 360 / (2 * math.pi), min = 60 * 360 / (2 * math.pi)
}

function gcdist(pt1, pt2, units) -- return distance in radians or given units
  --- this formula works best for points close together or antipodal
  --- rounding error strikes when distance is one-quarter Earth's circumference
  --- (ref: wikipedia Great-circle distance)
  if not pt1.radians then pt1 = rad(pt1) end
  if not pt2.radians then pt2 = rad(pt2) end
  local sdlat = sin((pt1.lat - pt2.lat) / 2.0);
  local sdlon = sin((pt1.lon - pt2.lon) / 2.0);
  local res = sqrt(sdlat * sdlat + cos(pt1.lat) * cos(pt2.lat) * sdlon * sdlon);
  res = res > 1 and 1 or res < -1 and -1 or res
  res = 2 * asin(res);
  if units then return res * assert(multipliers[units])
  else return res
  end
end

3
    private double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }

    private double GetDistance(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Distance in Kilo Meter
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = Math.Abs(Math.Round(rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344 * 1000, 0));
        return (dist);
    }

    private double GetDirection(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Direction in Degrees
        double dlat = deg2rad(lat1) - deg2rad(lat2);
        double dlon = deg2rad(lon1) - deg2rad(lon2);
        double y = Math.Sin(dlon) * Math.Cos(lat2);
        double x = Math.Cos(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) - Math.Sin(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(dlon);
        double direct = Math.Round(rad2deg(Math.Atan2(y, x)), 0);
        if (direct < 0)
            direct = direct + 360;
        return (direct);
    }

    private double GetSpeed(double lat1, double lon1, double lat2, double lon2, DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Round(TimeDifference.TotalSeconds, 0);
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344;
        double Speed = Math.Abs(Math.Round((dist / Math.Abs(TimeDifferenceInSeconds)) * 60 * 60, 0));
        return (Speed);
    }

    private double GetDuration(DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Abs(Math.Round(TimeDifference.TotalSeconds, 0));
        return (TimeDifferenceInSeconds);
    }

1
Bence metre içinde fonksiyon GetDistance döner değeri
Przemek

Bu doğru mu? GetDirection (), 'dlat'ı kullanmaz.
Ocak'ta gubby

3

Bu MySQL ve Kilometre için uyarlanmış "Henry Vilinskiy" versiyonu:

CREATE FUNCTION `CalculateDistanceInKm`(
  fromLatitude float,
  fromLongitude float,
  toLatitude float, 
  toLongitude float
) RETURNS float
BEGIN
  declare distance float;

  select 
    6367 * ACOS(
            round(
              COS(RADIANS(90-fromLatitude)) *
                COS(RADIANS(90-toLatitude)) +
                SIN(RADIANS(90-fromLatitude)) *
                SIN(RADIANS(90-toLatitude)) *
                COS(RADIANS(fromLongitude-toLongitude))
              ,15)
            )
    into distance;

  return  round(distance,3);
END;

MySQLdediSomething is wrong in your syntax near '' on line 8 // declare distance float;
Lejyoner

Bu, büyük bir daire mesafesinin en az doğru ve en hataya eğilimli hesaplama yöntemi olan "kosinüslerin küresel yasası" hesaplamasıdır
John Machin

3

İşte cevabın Swift uygulaması

func degreesToRadians(degrees: Double) -> Double {
    return degrees * Double.pi / 180
}

func distanceInKmBetweenEarthCoordinates(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {

    let earthRadiusKm: Double = 6371

    let dLat = degreesToRadians(degrees: lat2 - lat1)
    let dLon = degreesToRadians(degrees: lon2 - lon1)

    let lat1 = degreesToRadians(degrees: lat1)
    let lat2 = degreesToRadians(degrees: lat2)

    let a = sin(dLat/2) * sin(dLat/2) +
    sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2)
    let c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return earthRadiusKm * c
}

3

en iyi cevabı aldım ve bir Scala programında kullandım

import java.lang.Math.{atan2, cos, sin, sqrt}

def latLonDistance(lat1: Double, lon1: Double)(lat2: Double, lon2: Double): Double = {
    val earthRadiusKm = 6371
    val dLat = (lat2 - lat1).toRadians
    val dLon = (lon2 - lon1).toRadians
    val latRad1 = lat1.toRadians
    val latRad2 = lat2.toRadians

    val a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(latRad1) * cos(latRad2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    earthRadiusKm * c
}

İki konumdan birinin sabit olduğu ve mesafe üretmek için sadece bir çift lat / lon gerektiren fonksiyonların kolayca üretilebilmesi için işlevi körükledim.


2

Sanırım bunu dünyanın eğriliği boyunca istiyorsun. İki noktanız ve dünyanın merkezi bir düzlemde. Dünyanın merkezi o düzlemdeki bir dairenin merkezidir ve iki nokta (kabaca) o dairenin çevresindedir. Bundan bir noktadan diğerine açının ne olduğunu bularak mesafeyi hesaplayabilirsiniz.

Noktalar aynı yükseklikte değilse veya dünyanın mükemmel bir küre olmadığını dikkate almanız gerekiyorsa, biraz daha zorlaşır.


2

Son zamanlarda aynı şeyi yapmak zorunda kaldım. Ben bu web sitesi ile birlikte takip etmek kolay örnekleri ile küresel trig açıklayan çok yararlı buldum .


2

bunun bir uygulamasını (bazı iyi açıklamalarla birlikte) f # ' de F # ' de bulabilirsiniz

İşte önemli kısımlar:


let GreatCircleDistance<[<Measure>] 'u> (R : float<'u>) (p1 : Location) (p2 : Location) =
    let degToRad (x : float<deg>) = System.Math.PI * x / 180.0<deg/rad>

    let sq x = x * x
    // take the sin of the half and square the result
    let sinSqHf (a : float<rad>) = (System.Math.Sin >> sq) (a / 2.0<rad>)
    let cos (a : float<deg>) = System.Math.Cos (degToRad a / 1.0<rad>)

    let dLat = (p2.Latitude - p1.Latitude) |> degToRad
    let dLon = (p2.Longitude - p1.Longitude) |> degToRad

    let a = sinSqHf dLat + cos p1.Latitude * cos p2.Latitude * sinSqHf dLon
    let c = 2.0 * System.Math.Atan2(System.Math.Sqrt(a), System.Math.Sqrt(1.0-a))

    R * c

2

Bunu PowerShell'de uygulamam gerekiyordu, umarım başka birine yardımcı olabilir. Bu yöntem hakkında bazı notlar

  1. Herhangi bir satırı bölmeyin, aksi takdirde hesaplama yanlış olur
  2. KM'de hesaplamak için $ mesafe hesaplamasında * 1000'i kaldırın
  3. $ EarthsRadius = 3963.19059 değerini değiştirin ve mesafeyi mil cinsinden hesaplamak için $ mesafe hesaplamasında * 1000'i kaldırın
  4. Haversine kullanıyorum, diğer yayınlar Vincenty'nin formüllerinin çok daha doğru olduğunu belirtti

    Function MetresDistanceBetweenTwoGPSCoordinates($latitude1, $longitude1, $latitude2, $longitude2)  
    {  
      $Rad = ([math]::PI / 180);  
    
      $earthsRadius = 6378.1370 # Earth's Radius in KM  
      $dLat = ($latitude2 - $latitude1) * $Rad  
      $dLon = ($longitude2 - $longitude1) * $Rad  
      $latitude1 = $latitude1 * $Rad  
      $latitude2 = $latitude2 * $Rad  
    
      $a = [math]::Sin($dLat / 2) * [math]::Sin($dLat / 2) + [math]::Sin($dLon / 2) * [math]::Sin($dLon / 2) * [math]::Cos($latitude1) * [math]::Cos($latitude2)  
      $c = 2 * [math]::ATan2([math]::Sqrt($a), [math]::Sqrt(1-$a))  
    
      $distance = [math]::Round($earthsRadius * $c * 1000, 0) #Multiple by 1000 to get metres  
    
      Return $distance  
    }
    

2

Scala versiyonu

  def deg2rad(deg: Double) = deg * Math.PI / 180.0

  def rad2deg(rad: Double) = rad / Math.PI * 180.0

  def getDistanceMeters(lat1: Double, lon1: Double, lat2: Double, lon2: Double) = {
    val theta = lon1 - lon2
    val dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) *
      Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta))
    Math.abs(
      Math.round(
        rad2deg(Math.acos(dist)) * 60 * 1.1515 * 1.609344 * 1000)
    )
  }

1

// Belki yazım hatası?
GetDirection'da kullanılmayan değişken bir dlonumuz var,
sanırım

double y = Math.Sin(dlon) * Math.Cos(lat2);
// cannot use degrees in Cos ?

olmalı

double y = Math.Sin(dlon) * Math.Cos(dlat);

1
Bu bir cevap değil, en iyi yorum.
Kevin

1

İşte İksir'deki uygulamam

defmodule Geo do
  @earth_radius_km 6371
  @earth_radius_sm 3958.748
  @earth_radius_nm 3440.065
  @feet_per_sm 5280

  @d2r :math.pi / 180

  def deg_to_rad(deg), do: deg * @d2r

  def great_circle_distance(p1, p2, :km), do: haversine(p1, p2) * @earth_radius_km
  def great_circle_distance(p1, p2, :sm), do: haversine(p1, p2) * @earth_radius_sm
  def great_circle_distance(p1, p2, :nm), do: haversine(p1, p2) * @earth_radius_nm
  def great_circle_distance(p1, p2, :m), do: great_circle_distance(p1, p2, :km) * 1000
  def great_circle_distance(p1, p2, :ft), do: great_circle_distance(p1, p2, :sm) * @feet_per_sm

  @doc """
  Calculate the [Haversine](https://en.wikipedia.org/wiki/Haversine_formula)
  distance between two coordinates. Result is in radians. This result can be
  multiplied by the sphere's radius in any unit to get the distance in that unit.
  For example, multiple the result of this function by the Earth's radius in
  kilometres and you get the distance between the two given points in kilometres.
  """
  def haversine({lat1, lon1}, {lat2, lon2}) do
    dlat = deg_to_rad(lat2 - lat1)
    dlon = deg_to_rad(lon2 - lon1)

    radlat1 = deg_to_rad(lat1)
    radlat2 = deg_to_rad(lat2)

    a = :math.pow(:math.sin(dlat / 2), 2) +
        :math.pow(:math.sin(dlon / 2), 2) *
        :math.cos(radlat1) * :math.cos(radlat2)

    2 * :math.atan2(:math.sqrt(a), :math.sqrt(1 - a))
  end
end

1

Dart Sürümü

Haversine Algoritması.

import 'dart:math';

class GeoUtils {

  static double _degreesToRadians(degrees) {
    return degrees * pi / 180;
  }

  static double distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
    var earthRadiusKm = 6371;

    var dLat = _degreesToRadians(lat2-lat1);
    var dLon = _degreesToRadians(lon2-lon1);

    lat1 = _degreesToRadians(lat1);
    lat2 = _degreesToRadians(lat2);

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

0

R algoritması bir sürümü hala eksik olduğunu düşünüyorum :

gpsdistance<-function(lat1,lon1,lat2,lon2){

# internal function to change deg to rad

degreesToRadians<- function (degrees) {
return (degrees * pi / 180)
}

R<-6371e3  #radius of Earth in meters

phi1<-degreesToRadians(lat1) # latitude 1
phi2<-degreesToRadians(lat2) # latitude 2
lambda1<-degreesToRadians(lon1) # longitude 1
lambda2<-degreesToRadians(lon2) # longitude 2

delta_phi<-phi1-phi2 # latitude-distance
delta_lambda<-lambda1-lambda2 # longitude-distance

a<-sin(delta_phi/2)*sin(delta_phi/2)+
cos(phi1)*cos(phi2)*sin(delta_lambda/2)*
sin(delta_lambda/2)

cc<-2*atan2(sqrt(a),sqrt(1-a))

distance<- R * cc

return(distance)  # in meters
}

0

İşte bir Kotlin varyasyonu:

import kotlin.math.*

class HaversineAlgorithm {

    companion object {
        private const val MEAN_EARTH_RADIUS = 6371.0
        private const val D2R = Math.PI / 180.0
    }

    private fun haversineInKm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
        val lonDiff = (lon2 - lon1) * D2R
        val latDiff = (lat2 - lat1) * D2R
        val latSin = sin(latDiff / 2.0)
        val lonSin = sin(lonDiff / 2.0)
        val a = latSin * latSin + (cos(lat1 * D2R) * cos(lat2 * D2R) * lonSin * lonSin)
        val c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
        return EQATORIAL_EARTH_RADIUS * c
    }
}

Neden ortalama Dünya yarıçapı yerine ekvator yarıçapı kullandınız?
user13044086

@ user13044086 Güzel soru. Çünkü bunu Paulo Miguel Almeida'nın Java versiyonundan aldım. Görünüşe göre C # sürümü de bu mesafeyi kullanıyor. Buradaki diğer sürümler 6371'e sahiptir, ancak o zaman tüm bu algoritmaların Dünya'nın jeoid şeklini mükemmel şekilde ele almayabileceğini fark etmelisiniz. Bunu değiştirmekten çekinmeyin ve 6371'i kullanın. Bana bunun daha kesin değerlere yol açtığını söylerseniz cevabımı değiştireceğim.
Csaba Toth

1
6371.008 yaygın olarak kullanılmaktadır, çünkü movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

Yeterince adil! Cevabımı yarın düzenleyeceğim
Csaba Toth

@ user13044086 Bağlantı için teşekkürler, cevabımı bir süre önce buna dayanarak düzenledim
Csaba Toth
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.