2 개의 GPS 좌표 사이의 거리 계산


답변:


406

Javascript 구현을 포함하여 위도 및 경도로 두 좌표 사이의 거리를 계산하십시오 .

서쪽남쪽 위치는 음수입니다. 분과 초가 60을 초과하므로 S31 30 '은 -31.50 도입니다.

도를 라디안 으로 변환하는 것을 잊지 마십시오 . 많은 언어들이이 기능을 가지고 있습니다. 또는 간단한 계산 : 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;
}

사용 예는 다음과 같습니다.

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
확실하지 않은 경우 toRad () 메서드는 다음 과 같이 Number 프로토 타입에 대한 사용자 정의 Number.prototype.toRad = function() { return this * (Math.PI / 180); }; 입니다. 또는 아래에 표시된대로 (Math.PI/2)성능 향상을 위해 0.0174532925199433 (... 필요한 정밀도)으로 대체 할 수 있습니다 .
Vinney Kelly

44
줄 끝 주석을 찾지 않는 사람이이 공식을보고 거리 단위를 찾는 경우 단위는 km입니다. :)
Dylan Knowles

1
@VinneyKelly 작은 오타이지만 (Math.PI / 2)가 아닌 (Math.PI / 180)을 교체하십시오. 모두 도움을 주셔서 감사합니다.
Patrick Murphy

1
@ChristianKRider 첫 줄을보세요. R수학에서 일반적으로 무엇을 의미 하는지 생각한 다음 관련 지구 관련 수량을 찾아 숫자가 일치하는지 확인하십시오.
기금 모니카의 소송

3
영국식 단위 (마일)의 경우 fyi로 변경할 earthRadiusKm수 있습니다 var earthRadiusMiles = 3959;.
예배당 주스

59

구글과의 유망주를 찾으십시오. 여기 내 해결책이 있습니다.

#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
더 나은 성능을 위해 (M_PI / 180.0)을 0.0174532925199433으로 바꿀 수 있습니다.
Hlung

3
성능면에서 sin (dlat / 2.0)을 한 번만 계산하고 변수 a1에 저장하고 pow (, 2) 대신 a1 * a1을 사용하는 것이 좋습니다. 다른 pow (, 2)도 마찬가지입니다.
pms

71
예, 60 년대 이후의 컴파일러를 사용하십시오.
rightfold

17
컨텍스트 없이는 아무도 이해하지 못하는 상수를 "최적화"(M_PI / 180.0) 할 필요가 없습니다. 컴파일러는 이러한 고정 용어를 계산합니다!
Patrick Cornelissen

2
@ TõnuSamuel 귀하의 의견에 감사드립니다. 정말 감사. 최적화가 활성화 된 (-O) 컴파일러는 상수 연산을 미리 계산하여 수동 축소를 쓸모 없게 만들 수 있습니다. 시간이 있으면 테스트 해 보겠습니다.
Hlung

44

Haversine의 C # 버전

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

여기에 .NET Fiddle이 있으므로 자신의 Lat / Longs로 테스트 할 수 있습니다.


1
또한 사람들이 쉽게 이것을 테스트 할 수 있도록 체크 .NET 바이올린을 추가했습니다.
Pure.Krome

7
.Net Framework에는 GeoCoordinate.GetDistanceTo 메소드가 빌드되어 있습니다. 어셈블리 System.Device를 참조해야합니다. MSDN 기사 msdn.microsoft.com/en-us/library/…
fnx

27

이 글타래에 대한 로마 마카로프의 답변을 기반으로 한 Haversine 알고리즘의 Java 버전

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는 올바르게 사용하고 있으며 메소드에 전달할 때 위도 / 로그 위치를 교환하지 않아야합니다.
Paulo Miguel Almeida

1
이 공식을 사용하여 합리적인 대답을 얻었습니다. :이 웹 사이트를 사용하여 정확도를 기반으로 movable-type.co.uk/scripts/latlong.html 나에게 준 0.07149수식이 내게 준 반면 km를 0.07156약 99 %의 정확도 인
Janac 미나

24

이것은 SQL Server 2008의 지리 유형으로 수행하기가 매우 쉽습니다.

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 모델의 SRID입니다.


19

내가 사용하는 Python의 Haversine 함수는 다음과 같습니다.

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}

16

정확도가 필요한 경우 정확도가 얼마나 필요한지에 따라 다릅니다. 정확한 정확도가 필요한 경우 Vincenty 알고리즘과 같은 구보다는 타원체를 사용하여 알고리즘을 보는 것이 가장 좋습니다. http://en.wikipedia.org/wiki/Vincenty%27s_algorithm


11

다음은 C # (라디안 및 위도)입니다.

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

위도 및 경도가도 단위 인 경우 180 / PI로 나누어 라디안으로 변환하십시오.


1
이것은 "코사인 구면 법칙"계산으로 원거리를 계산하는 데있어 가장 정확하고 오류가 발생하기 쉬운 방법입니다.
John Machin

11

프로젝트의 포인트 사이의 거리를 많이 계산해야했기 때문에 코드를 최적화하려고했습니다. 다른 브라우저에서 평균적으로 새로운 구현 가장 많이 응답 된 답변보다 2 배 빠르게 실행됩니다 .

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
}

내 jsPerf로 게임을하고 결과를 확인할 수 있습니다 .

최근에는 파이썬에서 동일한 작업을 수행해야 했으므로 다음은 파이썬 구현입니다 .

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

그리고 완전성을 위해 : 위키의 Haversine .


11

PHP 버전 :

deg2rad()좌표가 이미 라디안 인 경우 모두 제거하십시오 .

$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
lat1 및 lat2를 $ lat1 nad $ lat2로 변경하십시오.
대신

7

중심 거리를 기준으로 레코드를 선택하는 데 사용하는 T-SQL 기능

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

이것은 "코사인 구면 법칙"계산으로 원거리를 계산하는 데있어 가장 정확하고 오류가 발생하기 쉬운 방법입니다.
John Machin

5

당신이 더 정확한 뭔가를해야 할 경우 다음이 이것 봐 .

Vincenty의 공식은 Thaddeus Vincenty (1975a)가 개발 한 구상 표면에서 두 점 사이의 거리를 계산하기 위해 측지학에서 사용되는 두 가지 관련 반복 방법입니다. 구형 지구를 가정하는 원거리와 같은 방법보다 더 정확합니다.

첫 번째 (직접) 방법은 다른 점으로부터 주어진 거리와 방위 (방향) 인 점의 위치를 ​​계산합니다. 두 번째 (역) 방법은 주어진 두 점 사이의 지리적 거리와 방위각을 계산합니다. 지구 타원체에서 0.5mm (0.020 인치) 이내의 정확도로 측지에서 널리 사용되었습니다.


5

I. "빵 부스러기"방법에 관하여

  1. 위도에 따라 지구 반경이 다릅니다. Haversine 알고리즘에서이 점을 고려해야합니다.
  2. 직선을 아치로 바꾸는 베어링 변경을 고려하십시오 (더 길다).
  3. 속도 변경을 고려하면 아치가 나선 (아치보다 길거나 짧은)으로 바뀝니다.
  4. 고도 변화는 편평한 나선을 3D 나선 (더 이상 길게)으로 바꿉니다. 구릉 지역에서는 매우 중요합니다.

아래는 # 1과 # 2를 고려한 C의 함수를 참조하십시오.

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. 꽤 좋은 결과를 얻는 더 쉬운 방법이 있습니다.

평균 속도로.

Trip_distance = Trip_average_speed * Trip_time

GPS 속도는 도플러 효과에 의해 감지되며 [Lon, Lat]과 직접 관련이 없으므로 주 거리 계산 방법이 아닌 경우 적어도 보조 (백업 또는 수정)로 간주 할 수 있습니다.


4

.NET을 사용하는 경우 휠을 없애지 마십시오. System.Device.Location을 참조하십시오 . 다른 답변 의 의견에 fnx를 제공합니다 .

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

이 Lua 코드는 Wikipedia와 Robert Lipe의 GPSbabel 도구 에서 찾은 내용에서 수정되었습니다 .

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
내 생각 미터 함수 GetDistance 반환 값
Przemek

이 올바른지? GetDirection ()은 'dlat'을 사용하지 않습니다.
gubby

3

이 버전은 "Henry Vilinskiy"의 MySQL 및 Kilometers 버전입니다.

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;

MySQL말했다Something is wrong in your syntax near '' on line 8 // declare distance float;
Legionar

이것은 원거리를 계산하는 가장 정확하고 오류가 발생하기 쉬운 "코사인의 구형 법칙"계산입니다.
John Machin

3

다음은 답변에서 Swift 구현입니다.

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

나는 최고의 답변을 받아 Scala 프로그램에서 사용했습니다.

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
}

두 위치 중 하나를 고정 하고 거리를 생성하기 위해 한 쌍의 위도 / 경도 만 필요한 함수를 쉽게 생성 할 수 있도록 함수를 커리했습니다 .


2

지구의 곡률을 따라 원한다고 생각합니다. 당신의 두 지점과 지구의 중심은 비행기에 있습니다. 지구의 중심은 해당 평면에서 원의 중심이며 두 점은 그 원의 둘레에 (거의) 있습니다. 그로부터 한 점에서 다른 점으로의 각도가 무엇인지 알아 냄으로써 거리를 계산할 수 있습니다.

점의 높이가 같지 않거나 지구가 완벽한 구가 아니라는 점을 고려해야하는 경우 조금 더 어려워집니다.


2

나는 최근에 같은 일을해야했다. 웹 사이트는 구형 삼각법을 설명하는 데 도움이되는 예제로 매우 도움 되는 것으로 나타났습니다 .


2

fssnip의 F # 에서 ( 구체적인 설명과 함께) 구현을 찾을 수 있습니다.

중요한 부분은 다음과 같습니다.


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

PowerShell에서 이것을 구현해야했습니다. 다른 사람을 도울 수 있기를 바랍니다. 이 방법에 대한 참고 사항

  1. 선을 분할하지 마십시오. 그렇지 않으면 계산이 잘못됩니다
  2. KM에서 계산하려면 $ distance 계산에서 * 1000을 제거하십시오.
  3. 거리를 마일로 계산하기 위해 $ earthsRadius = 3963.19059를 변경하고 $ 거리 계산에서 * 1000을 제거하십시오.
  4. 다른 게시물에서 Vincenty의 공식이 훨씬 정확하다고 지적 했으므로 Haversine을 사용하고 있습니다.

    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

스칼라 버전

  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

// 오타 오류일까요?
우리는 getDirection로에서 사용되지 않는 변수 dlon을 가지고,
나는 가정

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

해야한다

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

1
이것은 대답이 아니며 기껏해야 의견입니다.
케빈

1

다음은 Elixir에서의 구현입니다.

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

다트 버전

Haversine 알고리즘.

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 의 알고리즘 버전 이 여전히 누락 되었다고 생각합니다 .

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

다음은 Kotlin 변형입니다.

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

왜 평균 지구 반경 대신 적도 반경을 사용 했습니까?
user13044086

@ user13044086 좋은 질문입니다. Paulo Miguel Almeida의 Java 버전에서 이것을 파생했기 때문입니다. C # 버전도 해당 거리를 사용하는 것 같습니다. 여기에 다른 버전은 6371이 있지만 이러한 모든 알고리즘이 지구의 지오이드 모양을 완벽하게 처리하지 못할 수 있음을 알아야합니다. 이 값을 수정하고 6371을 사용하십시오. 더 정확한 값으로 연결되면 대답을 변경하겠습니다.
Csaba Toth

1
페이지의 노트에 설명 된대로 6371.008 일반적으로는 식의 상대 오차를 최소화하기 때문에 사용 movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

그럴 수 있지! 내일 내 답변을 편집하겠습니다
Csaba Toth

@ user13044086 링크 주셔서 감사합니다, 나는 그것을 기반으로 내 답변을 편집했습니다
Csaba Toth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.