1. 程式人生 > >GeoHash 經緯度座標編碼與解碼演算法

GeoHash 經緯度座標編碼與解碼演算法

關於GeoHash的瞭解是我在做爬蟲時發現一些網站比如美團、餓了麼都會把一些地理位置進行編碼,在檢索時能夠更快的進行地理空間上的檢索,找到距離相近的位置。

GeoHash 原理

將二維的經緯度座標點轉換為一維的字串,也就是編碼,某一個字串表示了某一個矩形區域,也就是說在這個矩形區域中的所有經緯度點都共享一套編碼也就是字串。
內部的實現採用的是GeoHash演算法,其實質其實是二分法。
緯度範圍在[-90,90],經度範圍在[-180,180]
拿到一個location時(116.389550, 39.928167)
對緯度區間[-90,90]二分,[-90,0]及[0,90],顯然緯度39.928167在[0,90]
得到第一次二分的結果,得到第一次二分結果1
同樣繼續二分[0,90],[0,45]及[45,90],39.928167在[0,45],得到第二次二分結果0。
不斷進行二分,39.928167總是屬於某個區間[a,b]。隨著每次迭代區間[a,b]總在縮小,並越來越逼近39.928167。
而二分停止時區間的長度也就決定了編碼的長度,也決定了所表示範圍的精確程度。
這裡寫圖片描述


這樣緯度二分結束,可以得到一串0-1編碼,緯度產生的編碼為10111 00011,經度也是這樣組碼,經度產生的編碼為11010 01011
偶數位放經度,奇數位放緯度,把2串編碼組合生成新串:11100 11101 00100 01111,這裡的偶數為是從0開始的。
最後採用base32進行編碼,所謂的base32就是用0-9、b-z(去掉a, i, l, o)這32個字母進行編碼。
先將0-1串轉為十進位制,28、29、4、15,0,13,對應表如下
這裡寫圖片描述
因此(116.389550, 39.928167)就可以編碼為wx4g0e。

GeoHash 編碼特點

1)字串越長,表示的範圍越精確,5位的編碼能表示10平方千米範圍的矩形區域,而6位編碼能表示更精細的區域(約0.34平方千米)
這裡寫圖片描述


2)字串相似的表示距離相近(特殊情況後文闡述),這樣可以利用字串的字首匹配來查詢附近的POI資訊。一個在城區,一個在郊區,城區的GeoHash字串之間比較相似,郊區的字串之間也比較相似,而城區和郊區的GeoHash字串相似程度要低些。

GeoHash編碼的好處

查詢複雜度高,通過計算位置的距離來查詢與當前位置距離近的位置計算成本高,採用GeoHash編碼後可以將二維座標點轉換為一維資料,進行排序,實現空間索引來進行查詢。另外,就像是餓了麼、美團在選餐時利用當前位置的GeoHash的字串返回共享這一GeoHash字串的矩形區域來推薦是一個查詢速度快並且實用的策略。

GeoHash編碼存在的問題

GeoHash 雖然能解決從二維到一維的轉變,但也存在一些問題。比如我們在比較三個位置的距離時,最簡單的方法是我們就利用路網距離,可能比較複雜,就用歐式距離來做,分別據算出任意兩個位置的距離比較,從而獲得距離最近的兩個位置。但是如果現在不僅僅是三個位置,如果是幾十萬甚至是更多的位置,我們應該如何處理呢?如果還是求任意兩個位置的歐式距離顯然那是災難性的。
而GeoHash對這些位置進行編碼,通過字首匹配,匹配度越高的位置就越相近,但是仔細想想如果兩個位置被分到兩個不同的矩形區域中,它們的匹配度很低,但是兩個位置距離很近,比如下面的和紅點距離近的綠點顯然和紅點是在一個矩形區域中,而和紅點匹配度高的顯然是和它在一個矩形區域中的另外一個綠點,這樣就尷尬了。
出現這種問題的原因是因為GeoHash採用了Peano空間填充曲線,填充過程
這裡寫圖片描述
我們在前面組碼經緯緯度時就是這樣的,經度緯度經度緯度的間隔組碼,因此會出現上面所說的情況,匹配度很低,但是距離很近的情況。
解決的思路很簡單,我們查詢時,除了使用定位點的GeoHash編碼進行匹配外,還使用周圍8個區域的GeoHash編碼,這樣可以避免這個問題。