問題描述php
如何計算緯度和經度指定的兩點之間的距離?
爲了澄清,我想要距離千米;這些點使用WGS84系統,我想了解可用方法的相對準確性。
最佳解決方案html
這個link可能對您有幫助,由於它詳細說明了使用Haversine formula計算距離。java
摘抄:python
This script [in Javascript] calculates great-circle distances between the two points – that is, the shortest distance over the earth’s surface – using the ‘Haversine’ formula.git
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) }
次佳解決方案瀏覽器
我須要計算個人項目點數之間的距離,因此我繼續嘗試優化代碼,我在這裏找到。平均來講,在不一樣瀏覽器中,個人新實現運行速度比最受歡迎的答案快2倍。jsp
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,看看results here。函數
最近我須要作一樣的python,因此這裏是一個python實現:測試
from math import cos, asin, sqrt def distance(lat1, lon1, lat2, lon2): p = 0.017453292519943295 #Pi/180 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)) #2*R*asin...
爲了完整性:維基上的Haversine。優化
第三種解決方案
這是一個C#實現:
static class DistanceAlgorithm { const double PIx = 3.141592653589793; const double RADIUS = 6378.16; /// <summary> /// Convert degrees to Radians /// </summary> /// <param name="x">Degrees</param> /// <returns>The equivalent in radians</returns> public static double Radians(double x) { return x * PIx / 180; } /// <summary> /// Calculate the distance between two places. /// </summary> /// <param name="lon1"></param> /// <param name="lat1"></param> /// <param name="lon2"></param> /// <param name="lat2"></param> /// <returns></returns> public static double DistanceBetweenPlaces( double lon1, double lat1, double lon2, double lat2) { double dlon = Radians(lon2 - lon1); double dlat = Radians(lat2 - lat1); double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2)); double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); return angle * RADIUS; }
第四種方案
這是一個Java實現的Haversine公式。
描述 java.lang.Math.toRadians(double angdeg) 轉換爲度大體相等的角度,以弧度爲單位的角度。從角度到弧度的轉換一般是不精確的。 聲明 如下是聲明java.lang.Math.toRadians()方法 public static double toRadians(double angdeg) 參數 angdeg -- an angle, in degrees 返回值 此方法返回的的角度angdeg弧度測量。 異常 NA
import java.lang.*; public class MathDemo { public static void main(String[] args) { // get two double numbers numbers double x = 45; double y = -180; // convert them in radians x = Math.toRadians(x); y = Math.toRadians(y); // print the hyperbolic tangent of these doubles System.out.println("Math.tanh(" + x + ")=" + Math.tanh(x)); System.out.println("Math.tanh(" + y + ")=" + Math.tanh(y)); } }
讓咱們來編譯和運行上面的程序,這將產生如下結果:
Math.tanh(0.7853981633974483)=0.6557942026326724
Math.tanh(-3.141592653589793)=-0.99627207622075
public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371; public int calculateDistanceInKilometer(double userLat, double userLng, double venueLat, double venueLng) { double latDistance = Math.toRadians(userLat - venueLat); double lngDistance = Math.toRadians(userLng - venueLng); double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat)) * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c)); }
請注意,在這裏咱們將答案舍入到最近的千米。
第五種方案
很是感謝全部這一切。我在個人Objective-C iPhone應用程序中使用瞭如下代碼:
const double PIx = 3.141592653589793; const double RADIO = 6371; // Mean radius of Earth in Km double convertToRadians(double val) { return val * PIx / 180; } -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { double dlon = convertToRadians(place2.longitude - place1.longitude); double dlat = convertToRadians(place2.latitude - place1.latitude); double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2); double angle = 2 * asin(sqrt(a)); return angle * RADIO; }
緯度和經度是十進制數。我沒有使用min()做爲asin()調用,由於我使用的距離是如此之小,以致於不須要它。
它給了不正確的答案,直到我經過了Radians的價值觀 – 如今它幾乎與從Apple的Map應用程序得到的值相同:-)
額外更新:
若是您正在使用iOS4或更高版本,那麼Apple提供一些方法來執行此操做,所以能夠經過如下方式實現相同的功能:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { MKMapPoint start, finish; start = MKMapPointForCoordinate(place1); finish = MKMapPointForCoordinate(place2); return MKMetersBetweenMapPoints(start, finish) / 1000; }
第六種方案
我在這裏張貼個人工做實例。
列出表中具備小於50KM的指定點(咱們使用隨機點 – 緯度:45.20327,長:23.7806)之間的距離的全部點,緯度&經度在MySQL(表格欄位是coord_lat和coord_long):
列出全部DISTANCE< 50,千米(被認爲是地球半徑6371 KM):
SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta FROM obiective WHERE coord_lat<>'' AND coord_long<>'' HAVING distanta<50 ORDER BY distanta desc
以上示例在MySQL 5.0.95和5.5.16(Linux)中進行了測試。
第七種方案
這是一個簡單的PHP函數,它將給出很是合理的近似值(低於+/- 1%偏差範圍)。
<?php function distance($lat1, $lon1, $lat2, $lon2) { $pi80 = M_PI / 180; $lat1 *= $pi80; $lon1 *= $pi80; $lat2 *= $pi80; $lon2 *= $pi80; $r = 6372.797; // mean radius of Earth in km $dlat = $lat2 - $lat1; $dlon = $lon2 - $lon1; $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $km = $r * $c; //echo '<br/>'.$km; return $km; } ?>
如前所述:地球不是一個球體。它就像一個老舊的棒球,標記mcguire決定練習 – 它充滿了凹痕和顛簸。更簡單的計算(像這樣)將其視爲一個球體。
不一樣的方法可能根據您在這種不規則卵形上的位置或多或少精確,而且您的點距離相差甚遠(絕對偏差越小越小)。你的指望越準確,數學越複雜。
更多信息:wikipedia geographic distance
第八種方案
您可使用CLLocationDistance中的構建來計算:
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1]; CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2]; [self distanceInMetersFromLocation:location1 toLocation:location2] - (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 { CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2]; return distanceInMeters; }
在你的狀況下,若是你想要千分之一除以1000。
第九種方案
在其餘答案中,缺乏r中的一個實現。
使用geosphere封裝的distm功能計算兩點之間的距離是很是簡單的:
distm(p1, p2, fun = distHaversine)
哪裏:
p1 = longitude/latitude for point(s) p2 = longitude/latitude for point(s) # type of distance calculation fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
因爲地球不是完美的球面,Vincenty formula for ellipsoids多是計算距離的最佳方式。所以,在geosphere
包中,您可使用:
distm(p1, p2, fun = distVincentyEllipsoid)
固然你也不必定要用geosphere
包,還能夠用R
的基礎距離計算一下功能:
hav.dist <- function(long1, lat1, long2, lat2) { R <- 6371 diff.long <- (long2 - long1) diff.lat <- (lat2 - lat1) a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2 c <- 2 * asin(min(1,sqrt(a))) d = R * c return(d) }
第十種方案
它取決於你想要的準確程度,以及所定義的datum的長度和長度。很是,很是近,你作一個小的球形觸發,但糾正事實,地球不是一個球體,使公式更復雜。
注:本文內容整合自google/baidu/bing輔助翻譯的英文資料結果。若是您對結果不滿意,能夠加入咱們改善翻譯效果:gxnotes#qq.com(#替換爲@)。