Hitung jarak antara 2 koordinat GPS


Jawaban:


406

Hitung jarak antara dua koordinat dengan lintang dan bujur , termasuk implementasi Javascript.

Lokasi Barat dan Selatan negatif. Ingat menit dan detik dari 60 sehingga S31 30 'adalah -31,50 derajat.

Jangan lupa untuk mengubah derajat ke radian . Banyak bahasa memiliki fungsi ini. Atau perhitungan sederhana: 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;
}

Berikut ini beberapa contoh penggunaan:

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
Dalam kasus itu tidak jelas, metode toRad () adalah kustomisasi dengan Nomor prototipe seperti: Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Atau, seperti yang ditunjukkan di bawah ini, Anda dapat mengganti (Math.PI/2)dengan 0,0174532925199433 (... presisi apa pun yang Anda anggap perlu) untuk meningkatkan kinerja.
Vinney Kelly

44
Jika ada orang, khususnya Anda yang tidak mencari komentar garis akhir, menatap rumus ini dan mencari satuan jarak, unit tersebut km. :)
Dylan Knowles

1
@VinneyKelly Kesalahan ketik kecil tapi ganti (Math.PI / 180) bukan (Math.PI / 2), terima kasih atas bantuan semua orang
Patrick Murphy

1
@ChristianKRider Lihatlah baris pertama. Pikirkan tentang apa yang Rbiasanya berarti dalam matematika, lalu cari yang relevan, jumlah terkait Bumi untuk melihat apakah jumlahnya cocok.
Dana Gugatan Monica

3
Untuk unit kekaisaran (mil) kamu bisa berubah earthRadiusKmmenjadi var earthRadiusMiles = 3959;, fyi.
chapeljuice

59

Cari haversine dengan Google; inilah solusi saya:

#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
Anda dapat mengganti (M_PI / 180.0) dengan 0,0174532925199433 untuk kinerja yang lebih baik.
Hlung

3
Dalam hal kinerja: seseorang dapat menghitung dosa (dlat / 2.0) hanya sekali, menyimpannya dalam variabel a1, dan bukannya pow (, 2) BANYAK lebih baik menggunakan a1 * a1. Hal yang sama untuk pow lainnya (, 2).
pms

71
Ya, atau cukup gunakan kompiler post-'60 -an.
dibuka kembali

17
Tidak perlu "mengoptimalkan" (M_PI / 180.0) ke konstanta yang tidak ada yang mengerti tanpa konteks. Kompiler menghitung ketentuan-ketentuan tetap ini untuk Anda!
Patrick Cornelissen

2
@ TõnuSamuel Terima kasih banyak atas komentar Anda. Saya sangat menghargai itu. Masuk akal bahwa kompiler dengan optimasi yang diaktifkan (-O) dapat melakukan pra-perhitungan operasi konstanta, membuat runtuh manual menjadi tidak berguna. Saya akan mengujinya ketika saya punya waktu.
Hlung

44

C # Versi Haversine

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

Berikut ini. NET Fiddle dari ini , sehingga Anda dapat mengujinya dengan Lat / Long Anda sendiri.


1
Saya juga menambahkan biola. NET biola sehingga orang dapat dengan mudah menguji ini.
Murni

7
Framework .Net memiliki metode build in GeoCoordinate.GetDistanceTo. Sistem perakitan. Layanan harus direferensikan. Artikel MSDN msdn.microsoft.com/en-us/library/…
fnx

27

Algoritma Haversine Versi Java berdasarkan pada balasan Roman Makarov untuk utas ini

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 pastikan Anda menggunakannya dengan benar dan tidak bertukar tempat lat / log saat meneruskannya ke metode apa pun.
Paulo Miguel Almeida

1
Saya mendapat jawaban yang cukup dekat menggunakan rumus ini. Saya mendasarkan akurasi menggunakan situs web ini: movable-type.co.uk/scripts/latlong.html yang memberi saya 0.07149km sedangkan formula Anda memberi saya 0.07156akurasi sekitar 99%
Janac Meena

24

Ini sangat mudah dilakukan dengan tipe geografi di 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 adalah SRID untuk model Earth elipsoidal WGS84


19

Berikut adalah fungsi Haversine dalam Python yang saya gunakan:

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

Ini dia dalam C # (lat dan long di radian):

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

Jika lat dan panjang Anda dalam derajat maka bagi dengan 180 / PI untuk dikonversi ke radian.


1
Ini adalah perhitungan "hukum kosinus bulat" yang merupakan metode penghitungan jarak lingkaran besar yang paling tidak akurat dan paling rawan kesalahan.
John Machin

11

Saya perlu menghitung banyak jarak antara titik-titik untuk proyek saya, jadi saya melanjutkan dan mencoba mengoptimalkan kode, saya temukan di sini. Rata-rata di berbagai browser implementasi saya yang baru berjalan 2 kali lebih cepat daripada jawaban yang paling banyak dipilih.

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
}

Anda dapat bermain dengan jsPerf saya dan lihat hasilnya di sini .

Baru-baru ini saya perlu melakukan hal yang sama di python, jadi di sini adalah implementasi python :

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

Dan demi kelengkapan: Haversine on wiki.


11

Versi PHP:

(Hapus semua deg2rad()jika koordinat Anda sudah ada dalam radian.)

$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
Ubah lat1 dan lat2 menjadi $ lat1 dan $ lat2.
sebagai gantinya

7

Fungsi T-SQL, yang saya gunakan untuk memilih catatan berdasarkan jarak untuk pusat

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

Ini adalah perhitungan "hukum kosinus bulat" yang merupakan metode penghitungan jarak lingkaran besar yang paling tidak akurat dan paling rawan kesalahan.
John Machin

5

Jika Anda membutuhkan sesuatu yang lebih akurat maka lihatlah ini .

Rumus Vincenty adalah dua metode iteratif terkait yang digunakan dalam geodesi untuk menghitung jarak antara dua titik pada permukaan spheroid, yang dikembangkan oleh Thaddeus Vincenty (1975a). Rumus ini didasarkan pada asumsi bahwa sosok Bumi adalah spheroid oblate, dan karenanya lebih akurat daripada metode seperti jarak lingkaran besar yang mengasumsikan Bumi bulat.

Metode (langsung) pertama menghitung lokasi suatu titik yang merupakan jarak dan azimuth (arah) yang diberikan dari titik lain. Metode kedua (terbalik) menghitung jarak geografis dan azimuth antara dua titik yang diberikan. Mereka telah banyak digunakan dalam geodesi karena mereka akurat hingga 0,5 mm (0,020 ″) di ellipsoid bumi.


5

I. Mengenai metode "Breadcrumbs"

  1. Jari-jari bumi berbeda pada Lat yang berbeda. Ini harus dipertimbangkan dalam algoritma Haversine.
  2. Pertimbangkan perubahan bantalan, yang mengubah garis lurus menjadi lengkungan (yang lebih panjang)
  3. Memperhatikan perubahan kecepatan akan membuat lengkungan menjadi spiral (yang lebih panjang atau lebih pendek dari lengkungan)
  4. Perubahan ketinggian akan mengubah spiral datar menjadi spiral 3D (yang lebih panjang lagi). Ini sangat penting untuk daerah berbukit.

Di bawah ini lihat fungsi dalam C yang memperhitungkan # 1 dan # 2:

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 Ada cara yang lebih mudah yang memberikan hasil yang cukup bagus.

Dengan Kecepatan Rata-Rata.

Trip_distance = Trip_average_speed * Trip_time

Karena Kecepatan GPS terdeteksi oleh efek Doppler dan tidak terkait langsung dengan [Lon, Lat], maka setidaknya dapat dianggap sebagai sekunder (cadangan atau koreksi) jika bukan sebagai metode perhitungan jarak utama.


4

Jika Anda menggunakan .NET jangan mengaktifkan kembali roda. Lihat System.Device.Location . Kredit ke fnx di komentar di jawaban lain .

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

Kode Lua ini diadaptasi dari hal-hal yang ditemukan di Wikipedia dan dalam alat GPSbabel Robert Lipe :

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
Saya pikir fungsi Anda GetDistance mengembalikan nilai dalam meter
Przemek

Apakah ini benar? GetDirection () tidak menggunakan 'dlat'.
gemuk

3

Ini adalah versi dari "Henry Vilinskiy" yang diadaptasi untuk MySQL dan Kilometer:

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;

MySQLberkataSomething is wrong in your syntax near '' on line 8 // declare distance float;
Legionar

Ini adalah perhitungan "hukum kosinus bulat" yang merupakan metode penghitungan jarak lingkaran besar yang paling tidak akurat dan paling salah kesalahan
John Machin

3

di sini adalah implementasi Swift dari jawabannya

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

Saya mengambil jawaban teratas dan menggunakannya dalam program 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
}

saya menjelajah fungsi agar dapat dengan mudah menghasilkan fungsi yang memiliki salah satu dari dua lokasi tetap dan hanya membutuhkan sepasang lat / lon untuk menghasilkan jarak.


2

Saya kira Anda menginginkannya di sepanjang lengkungan bumi. Dua titik Anda dan pusat bumi ada di pesawat. Pusat bumi adalah pusat lingkaran pada bidang itu dan dua titik (kira-kira) berada di sekeliling lingkaran itu. Dari sana Anda dapat menghitung jarak dengan mencari tahu sudut dari satu titik ke titik lainnya.

Jika titik-titiknya tidak sama dengan ketinggian, atau jika Anda perlu memperhitungkan bahwa bumi bukanlah bola yang sempurna, ia akan menjadi sedikit lebih sulit.


2

Baru-baru ini saya harus melakukan hal yang sama. Saya menemukan situs web ini sangat membantu menjelaskan trigonometri bola dengan contoh-contoh yang mudah diikuti.


2

Anda dapat menemukan implementasi ini (dengan beberapa penjelasan yang baik) di F # on fssnip

di sini adalah bagian penting:


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

Saya perlu menerapkan ini di PowerShell, semoga bisa membantu orang lain. Beberapa catatan tentang metode ini

  1. Jangan membagi salah satu garis atau perhitungannya akan salah
  2. Untuk menghitung dalam KM hapus * 1000 dalam perhitungan $ distance
  3. Ubah $ earthsRadius = 3963.19059 dan hapus * 1000 dalam perhitungan $ distance untuk menghitung jarak dalam mil
  4. Saya menggunakan Haversine, seperti posting lain menunjukkan formula Vincenty jauh lebih akurat

    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

Versi scala

  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

// Mungkin kesalahan ketik?
Kami memiliki dlon variabel yang tidak digunakan di GetDirection,
saya kira

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

seharusnya

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

1
Ini bukan jawaban, yang terbaik adalah komentar.
Kevin

1

Inilah implementasi saya di 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

Versi Dart

Algoritma 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

Saya pikir versi algoritma di R masih hilang:

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

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

Mengapa Anda menggunakan jari-jari khatulistiwa dan bukan jari-jari Bumi?
user13044086

@ user13044086 Pertanyaan bagus. Itu karena saya mendapatkan ini dari versi Java Paulo Miguel Almeida. Sepertinya versi C # juga menggunakan jarak itu. Versi lain di sini memiliki 6371, tetapi kemudian Anda harus menyadari bahwa semua algoritma ini mungkin tidak secara sempurna menangani bentuk geoid Bumi. Jangan ragu untuk memodifikasi ini dan menggunakan 6371. Jika Anda memberi tahu saya itu mengarah ke nilai yang lebih tepat saya akan mengubah jawaban saya.
Csaba Toth

1
6371.008 umumnya digunakan karena meminimalkan kesalahan relatif dari rumus seperti yang dijelaskan dalam catatan di halaman movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

Cukup adil! Saya akan mengedit jawaban saya besok
Csaba Toth

@ user13044086 Terima kasih atas tautannya, saya mengedit jawaban saya beberapa waktu lalu berdasarkan itu
Csaba Toth
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.