假設方位角是α, 那從點1到點2的平移距離分別如下所示d*sinα, d*cosα。 這裏正北爲0度。基中點1經緯度(long1, lat1)和距離d是已知的。 求點2的經緯度(long2,lat2)
還有一個隱藏的信息,就是點1所在的緯度,其實也是一個有用的角度,通過它可以知道當前緯度的那個切面的半徑長度,下圖紅線所示用arc表示。這裏假設地球是近似球體,赤道圓的半徑是ARC,側從下圖中可以得出:
就是知道φ是當前點1的緯度。則當前的緯度的切面半徑 arc = ARC*cos(φ) ,其中φ其實就是當前的方位的緯度值,即arc = ARC*cos(lat1)
這裏還要再討論下地球半徑,其實地球是一個橢球體。
極半徑 從地心到北極或南極的距離,大約3950英里(6356.9088千米)(兩極的差極小,可以忽略)。
赤道半徑 是從地心到赤道的距離,大約3963英里(6377.830千米)。
如果只是做近似計算的,我們這裏取平均距離,平均半徑 大約3959英里(6371.393千米) 。這個數字是地心到地球表面所有各點距離的平均值。
這裏取平均半徑那麼ARC=6371393(米)
通過上面的知識鋪墊後, 計算就簡單化了,
【計算思路】
1. 計算第二點的經度,就是 水平平移的距度(d*sinα)除以 當前緯度切面周長(2π*arc),再每乘以360度) ,就知道了水平橫向平移了多少度,再加上long1,就是long2的值了。
2. 計算第二點的緯度,比較簡單,就是, 垂直平移的距離d(d*cosα)除以 地球縱向周長,再乘上360度,就知道縱向平移了多少度,再加上lat1,就知道lat2的值了。
long2 = long1 + d*sinα/[ARC*cos(lat1)*2π/360]
lat2 = lat1 +d*cosα/ (ARC *2π/360)
注意:所有的三角函數中使用的不是角度,必須是弧度,必須是弧度,必須是弧度。原因是弧度制統一了度量弧與半徑的單位,從而大大簡化了有關公式及運算,尤其在物理、數學中,其優點就格外明顯。
例如:
當採用弧度時 Lim(x->0)sin(x)=x
當採用角度時 Lim(x->0)sin(x)=x*pi/180
微積分就更明顯了.
/// <summary>
/// 計算移動後的經緯度
/// </summary>
/// <param name="lon">經度</param>
/// <param name="lat">緯度</param>
/// <param name="a">方位角(弧度)</param>
/// <param name="dst">移動距離</param>
/// <returns></returns>
public double[] LongLatOffset(double lon, double lat, double a, double dst)
{
double arc = 6371.393 * 1000;
lon += dst * Math.Sin(a) / (arc * Math.Cos(lat) * 2 * Math.PI / 360);
lat += dst * Math.Cos(a) / (arc * 2 * Math.PI / 360);
return new[] { lon, lat };
}
優化後:
// 比之前快30%
func CalibrateByDistance(distance, heading, lat, lng float64) (nLat, nLng float64) {
const earthRadius = 6371393
const earthGirth = earthRadius * 2 * math.Pi
const radianPerAngel = math.Pi / 180
rad := heading * radianPerAngel
angelPerMeter := distance / earthGirth * 360
nLat = lat + angelPerMeter*math.Cos(rad)
nLng = lng + angelPerMeter*math.Sin(rad)/math.Cos(lat*radianPerAngel)
return
}