根據經緯度計算球面距離

根據2個經緯度點,計算這2個經緯度點之間的距離(通過經度緯度得到距離)

 轉自:http://www.cnblogs.com/softfair/p/distance_of_two_latitude_and_longitude_points.html

 

球面上任意兩點之間的距離計算公式可以參考維基百科上的下述文章。

值得一提的是,維基百科推薦使用Haversine公式,理由是Great-circle distance公式用到了大量餘弦函數, 而兩點間距離很短時(比如地球表面上相距幾百米的兩點),餘弦函數會得出0.999...的結果, 會導致較大的舍入誤差。而Haversine公式採用了正弦函數,即使距離很小,也能保持足夠的有效數字。 以前採用三角函數表計算時的確會有這個問題,但經過實際驗證,採用計算機來計算時,兩個公式的區別不大。 穩妥起見,這裏還是採用Haversine公式。

 

其中

 

  • R爲地球半徑,可取平均值 6371km;
  • φ1, φ2 表示兩點的緯度;
  • Δλ 表示兩點經度的差值。

根據2個經緯度座標,距離計算函數

下面就是計算球面間兩點(lat1, lon1) - (lat2, lon2)之間距離的函數。

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HarvenSin
{
    class Program
    {
        /// <summary>
        /// 根據經緯度,計算2個點之間的距離。
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //39.94607,116.32793  31.24063,121.42575
            Console.WriteLine(Distance(39.94607, 116.32793, 31.24063, 121.42575));

        }


        public static double HaverSin(double theta)
        {
            var v = Math.Sin(theta / 2);
            return v * v;
        }


        static double EARTH_RADIUS = 6371.0;//km 地球半徑 平均值,千米

        /// <summary>
        /// 給定的經度1,緯度1;經度2,緯度2. 計算2個經緯度之間的距離。
        /// </summary>
        /// <param name="lat1">經度1</param>
        /// <param name="lon1">緯度1</param>
        /// <param name="lat2">經度2</param>
        /// <param name="lon2">緯度2</param>
        /// <returns>距離(公里、千米)</returns>
        public static double Distance(double lat1,double lon1, double lat2,double lon2)
        {
            //用haversine公式計算球面兩點間的距離。
            //經緯度轉換成弧度
            lat1 = ConvertDegreesToRadians(lat1);
            lon1 = ConvertDegreesToRadians(lon1);
            lat2 = ConvertDegreesToRadians(lat2);
            lon2 = ConvertDegreesToRadians(lon2);

            //差值
            var vLon = Math.Abs(lon1 - lon2);
            var vLat = Math.Abs(lat1 - lat2);

            //h is the great circle distance in radians, great circle就是一個球體上的切面,它的圓心即是球心的一個周長最大的圓。
            var h = HaverSin(vLat) + Math.Cos(lat1) * Math.Cos(lat2) * HaverSin(vLon);

            var distance = 2 * EARTH_RADIUS * Math.Asin(Math.Sqrt(h));

            return distance;
        }

        /// <summary>
        /// 將角度換算爲弧度。
        /// </summary>
        /// <param name="degrees">角度</param>
        /// <returns>弧度</returns>
        public static double ConvertDegreesToRadians(double degrees)
        {
            return degrees * Math.PI / 180;
        }

        public static double ConvertRadiansToDegrees(double radian)
        {
            return radian * 180.0 / Math.PI;
        }

    }
}
複製代碼

 

 

公式來歷:

VERSINE(F)=1-cos(F)

 

 

Haversine名字來歷是Ha-VERSINE,即Half-Versine ,表示sin的一半的意思。

 

hav(A) = (1-cos(A))/2 = sin(A/2)* sin(A/2)

 

推倒過程:

 

如下一個半徑爲1 的圓,O是圓心,A、B是弦(chord)。角度AOB=theta。則角度AOC=theta/2。OC是垂直於AB的垂線(perpendicular)。AC長度是sin(theta/2),AB長度是2*sin(theta/2)。

 

(圖1)

如下地球圖所示,假設半徑R爲1,O是球心,A (lat1,lon1) 和 B (lat2,lon2) 是我們感興趣的2個點。2跟經度線 lon1,lon2相交於北極(north pole)N。EF所在的線是赤道(equator)。ACBD是平面上的等腰梯形的四個頂點(vertice)。AC和DB的弦(直線)在圖上沒有畫出。CD的位置是:C (lat2,lon1) and D (lat1,lon2)。角度AOC是A點與C點的緯度差 dlat。角度EOF是經度E點和經度F點的差dlon。

 

 

 

(圖2)

弦AC的長度,參照圖1的方式,那麼是AC=2*sin(dlat/2),弦BD也是一樣的長度。

E、F 2個點是赤道上的2個點,它們的緯度是0。EF的距離是EF=2*sin(dlon/2)

A、D2個點所在的緯度是lat1。AD所在緯度的圓平面的半徑是cos(lat1)。從A作一條垂線(perpendicular)到OE爲AG,AO是球半徑,則OG=cos(lat1),即A、D所在緯度圓圈的半徑(AO`)。

這時候,AD的弦長AD= 2*sin(dlon/2)*cos(lat1),類似的可以推出CB的長度= CB=2*sin(dlon/2)*cos(lat2)

 

下面看一下如何求AB的長度,回到平面等腰梯形,如下圖:

 

 

(圖3)

AH是到CB的垂線(perpendicular),CH= (CB-AD)/2。

根據勾股定理(Pythagorean theorem): 【^2表示2的平方】

AH^2 = AC^2 - CH^2

       = AC^2 - (CB-AD)^2/4

 

HB 的長度是HB=AD+CH = AD+(CB-AD)/2 = (CB+AD)/2,根據勾股定理得到:

  AB^2 = AH^2 + HB^2

       = AC^2 - (CB-AD)^2/4 + (CB+AD)^2/4

       = AC^2 + CB*AD

根據前面球面上的求經緯距離的方式,我們已經得到 AC、AD和CB的長度,代入公式得到:

 

  AB^2 = 4*(sin^2(dlat/2) + 4*cos(lat1)*cos(lat2)*sin^2(dlon/2))

 

假設中間值h 是AB長度一半的平方,如下

 

  h = (AB/2)^2

    = (sin^2(dlat/2)) + cos(lat1) * cos(lat2) * sin^2(dlon/2)

  (請參看代碼裏的h)

最後一步,是求得代表AB長度的角度AOB。參照圖1的方式,我們可以知道

 

(圖4)

設AC=,根據勾股定理(Pythagorean theorem)得到:

OC= = sqrt(OA^2 - AC^2)

         = = sqrt(1-a)   // sqrt表示開根號

 

如果設c是角AOB的度數值。

tan(<AOC) = tan(c)= AC/OC = sqrt(a)/sqrt(1-a)

則:

  c = 2 * arctan(sqrt(a)/sqrt(1-a)),

最後的AB真實距離,把地球半徑帶上就可以了。

distance = 2 * EARTH_RADIUS * c。

 

 

2)另外一種方法:

 

SQL Server本身是支持空間數據索引的(Spatial Indexing),具有空間數據計算能力。

他是通過一個擴展DLL Microsoft.SqlServer.Types.dll 來實現這些功能的。這是一個託管DLL,那意味着.NET C# asp.net 也可以使用些功能。

例如通過 reference 引用: Microsoft.SqlServer.Types.dll 這個dll。

var a = SqlGeography.Point(22.54587746 , 114.12873077, 4326); //上海的某個點
var b = SqlGeography.Point(23, 115, 4326); //上海的某個點,4236代表WGS84這種座標參照系統。
Console.WriteLine(a.STDistance(b)); //距離

 

 

這個算出來的距離,與上面使用haversine公式算出的距離,誤差在幾米之內。

 

 

 

 

結束。

參考資料:

http://mathforum.org/library/drmath/view/51879.html

http://blog.charlee.li/location-search/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章