- 前言
在我們平時(shí)使用美團(tuán),餓了么等app進(jìn)行訂餐,或者使用貓眼進(jìn)行訂電影票的時(shí)候,都有一個(gè)距離的排序,表明該家店距離我們當(dāng)前的位置,這種基于地理位置的服務(wù),統(tǒng)一被稱為L(zhǎng)BS(Location Based Service),而LBS的實(shí)現(xiàn)則是借助于GIS,WC(無(wú)線通信)等信息技術(shù)來實(shí)現(xiàn)。而今天我們所要討論的就是這個(gè)距離的實(shí)現(xiàn)。
GIS,Geographic information system,地理信息系統(tǒng)。
- 計(jì)算方式
由于地球是一個(gè)橢圓形,我們?cè)谟?jì)算的時(shí)候有點(diǎn)麻煩,所以我們更常用的方式是將地球作為一個(gè)球形來計(jì)算,而計(jì)算球面上任意兩點(diǎn)之間的距離的公式通常有兩種:Great-circle distance和Haversine formula,而目前大多數(shù)公司都是用的是Haversine公式,原因可以參考:
Great-circle distance公式用到了大量余弦函數(shù), 而兩點(diǎn)間距離很短時(shí)(比如地球表面上相距幾百米的兩點(diǎn)),余弦函數(shù)會(huì)得出0.999…的結(jié)果, 會(huì)導(dǎo)致較大的舍入誤差。而Haversine公式采用了正弦函數(shù),即使距離很小,也能保持足夠的有效數(shù)字。
而有關(guān)這兩者的介紹可以參考維基百科:Haversine formula 維基百科,Great-circle distance 維基百科。而最終該公式的形式為:
至于為什么是這種形式,其實(shí)目前網(wǎng)上有許多推導(dǎo)公式,感興趣的可以看一下推導(dǎo)過程,順便回憶一下自己當(dāng)年學(xué)過的數(shù)學(xué)知識(shí):
1. 關(guān)于已知兩點(diǎn)經(jīng)緯度求球面最短距離的公式推導(dǎo)
2. 根據(jù)經(jīng)緯度計(jì)算兩點(diǎn)之間的距離的公式推導(dǎo)過程以及google.maps的測(cè)距函數(shù)
而如果要考慮到高度的影響的話,可以參考:https://stackoverflow.com/questions/3694380/calculating-distance-between-two-points-using-latitude-longitude
另外,還有一種方式是 Vincenty’s formulae,該方式也是用于計(jì)算球體表面兩點(diǎn)之間距離的方式,而它所基于的就是地球是扁球體的形狀,因此這種方式比假設(shè)地球是球體的方式應(yīng)該更加準(zhǔn)確,但實(shí)現(xiàn)起來比較麻煩。感興趣的可以查看下維基百科:Vincenty’s formulae 維基百科
接下來說幾點(diǎn)概念:
3.1 地球半徑
由于地球不是一個(gè)完美的球體,所以并不能用一個(gè)特別準(zhǔn)確的值來表示地球的實(shí)際半徑,不過由于地球的形狀很接近球體,用[6357km] 到 [6378km]的范圍值可以涵蓋需要的所有半徑。并且通常情況下,地球半徑有幾個(gè)常用值:
極半徑,從地球中心至南極或北極的距離, 相當(dāng)于6356.7523km;
赤道半徑,從地球中心到赤道的距離,大約6378.137km;
平均半徑,6371.393km,表示地球中心到地球表面所有各點(diǎn)距離的平均值;
RE,地球半徑,有時(shí)被使用作為距離單位, 特別是在天文學(xué)和地質(zhì)學(xué)中常用,大概距離是6370.856km;
所以我們通過地球半徑進(jìn)行計(jì)算的時(shí)候,通常情況下,我們可以使用上面的每一個(gè)值都可以進(jìn)行計(jì)算,不過或多或少都會(huì)有誤差的,但這樣的誤差是也是允許存在的。這里參考自維基百科:維基百科-地球半徑
- MySQL實(shí)現(xiàn)
同樣,在MySQL中實(shí)現(xiàn)該功能,計(jì)算公式還是通過Haversine公式。不過在Google Map中,已經(jīng)提供了相應(yīng)的實(shí)現(xiàn)方式,我們先來看一下。
SELECT
id,
(
3959 * acos(
cos( radians( 37 ) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(- 122 ) ) + sin( radians( 37 ) ) * sin( radians( lat ) )
)
) AS distance
FROM
markers
HAVING
distance < 25
ORDER BY
distance
LIMIT 0,
20;
而如果我們要查詢公里,將3959英里也就是地球半徑,修改為6371即可。
Google Maps地址:Creating a Store Locator on Google Maps php&MySQL
4.2 st_distance函數(shù)
MySQL其實(shí)在很早就提供了這種存儲(chǔ)經(jīng)緯度及相關(guān)運(yùn)算的功能,這種數(shù)據(jù)類型叫做空間數(shù)據(jù)類型,而對(duì)應(yīng)的索引被稱為空間索引,但由于MySQL之前的版本對(duì)InnoDB支持的并不是太好,所以使用的并不多。不過MySQL5.6和MySQL5.7對(duì)此進(jìn)行了優(yōu)化,添加了st_distance等相關(guān)函數(shù)來支持經(jīng)緯度相關(guān)的計(jì)算。
SELECT
s.*,
( st_distance ( point ( lng, lat ), point (- 122.083235, 37.38714 ) ) * 111195 ) AS distance
FROM
markers s
ORDER BY
distance
其中,point是MySQL的空間數(shù)據(jù)類型,先不多說這塊。就這樣,我們只需要通過st_distance函數(shù)就計(jì)算出了我們所需要查詢的結(jié)果,不過這里需要說一下:
st_distance 函數(shù)返回的單位是degrees,也就是空間單位的度數(shù),我們?nèi)绻獙egrees轉(zhuǎn)換為米或者千米的話,需要乘以 EARTH_RADIUS * PI/180, EARTH_RADIUS 也就是地球半徑,至于是米還是千米,就看該變量的單位。
該運(yùn)算其實(shí)就相當(dāng)于對(duì)地球半徑進(jìn)行弧度與角度的轉(zhuǎn)換,也就是Math.toRadians,而上面我們寫的111195其實(shí)是一個(gè)有誤差的值,該值就是通過該計(jì)算得出的結(jié)果。
其實(shí),MySQL有提供直接查詢結(jié)果是米的函數(shù):st_distance_sphere,并且該函數(shù)的計(jì)算結(jié)果要比st_distance轉(zhuǎn)換為米的結(jié)果更精確。不過該函數(shù)是MySQL5.7之后才引入的,5.7之前還是需要通過計(jì)算轉(zhuǎn)換成米。更多可參考官方文檔地址:MySQL 5.7 ST_Distance_Sphere(g1, g2 [, radius])
5 Geohash算法
Geohash是目前比較主流的范圍搜索的算法,比如說搜索附近500米內(nèi)的地點(diǎn)這種問題。Geohash算法將二維的經(jīng)緯度編碼為一個(gè)字符串,每個(gè)字符串代表了某一矩形區(qū)域,也就是說,這個(gè)矩形區(qū)域內(nèi)所有的點(diǎn)(經(jīng)緯度坐標(biāo))都共享相同的GeoHash字符串,這樣在查詢的時(shí)候就可以對(duì)該字符串做索引,然后根據(jù)該字符串進(jìn)行過濾。
Geohash算法的最大用途其實(shí)就是附近地址搜索了,不過,從geohash的編碼算法中可以看出它的一個(gè)缺點(diǎn),也就是邊界問題:雖然兩個(gè)地點(diǎn)距離很近,但恰好位于分界點(diǎn)的兩側(cè),這樣geohash字符串就會(huì)不相同,然后匹配的時(shí)候就會(huì)有問題。不過要解決這個(gè)問題也很簡(jiǎn)單,就是計(jì)算的時(shí)候,計(jì)算出8個(gè)分別分布在周圍8個(gè)區(qū)域的地點(diǎn)。文章來源:http://www.zghlxwxcb.cn/news/detail-448157.html
原文文章來源地址http://www.zghlxwxcb.cn/news/detail-448157.html
到了這里,關(guān)于根據(jù)經(jīng)緯度計(jì)算兩點(diǎn)之間的距離的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!