在日常生活中,我們經常會有很多和地理相關的一些應用,比如:地圖導航,附近的人,查詢某些地標等等,以前我們會選擇問路,而隨著智慧裝置的更新和升級,現在我們僅僅只需要一個手機,開啟一個地圖APP,即可快速查詢自己想要的路線或者資訊了。
那麼,附近的一個建築或者商店,是透過怎樣的形式,讓其可以在手機上展示出來的呢?
比如:週末了,躺在床上,使用手機下單,尋找外賣美食。。。這些美食店資訊是怎麼在手機上進行展示的呢,它和我所在的距離是怎麼計算出來的呢? 這時候,可能就需要用到GeoHash這種演算法了。前段時間一個朋友問到過我關於GeoHash的相關知識,但是對這種演算法,只是有所瞭解,並沒有深入去探索內部的實現,在此,給它進行自我解析與記錄。
先簡單介紹一下GeoHash演算法,在日常生活中,我們對某一座標的定位,一半都是使用經緯度來進行標記的。比如:
常營地鐵站(lat:39.9257460000,lng:116.5998310000)
,我們獲取一個區域的位置,是使用一個二維陣列對其進行標記的,
它表示的不是一個具體的點
,而是泛指一片區域,區域的範圍與經緯度的取值精度直接相關。
當我拿到常營地鐵站的經緯度後,透過GeoHash這種演算法進行計算後,獲取到一個可比較的字串,具體計算過程如下:
同樣,對緯度也進行相對應的演算法進行計算得到一個二進位制值(對經緯度取值範圍越小,精度越高,所表示區域範圍越小),在此省去計算過程。
https://www。cnblogs。com/tgzhu/p/6204173。html
將經緯度的二進位制數進行組合,
以奇數為緯度,偶數為經度組合
,過程如上圖。
然戶將獲取到的經緯度二進位制數
以每5個數為一組
,將每一組都進行
轉換成十進位制
數字。
然後採用Base32對應編碼進行轉換可得到編碼
wx4g0e
這樣的可比較的字串,比如我們的
經緯度都分了10次
,那麼最後生成的字串的長度就是4,範圍是20km,如果我們
經緯度都分20次
,那麼最後生成的字串的長度就是8,範圍可以精確到19m。為什麼是可比較字串,後面會詳細講解到。
在此對Base32編碼進行一番簡單介紹: Base32,是將數字 0~9 ,加上26個字母(去除
a,i,l,o
四個)進行組合而成的32個字元編碼形式。如程式碼:
Base32對應編碼參考圖:(上面二進位制轉換對應的十進位制數值)
維基百科上Base32位值資訊表
下面是從網上線上解析的常營地鐵站GeoHash值(
wx4gjk32kfrx
)
網址: http://geohash。co/
Geohash其實就是將整個地圖或者某個分割所得的區域進行一次劃分,由於採用的是base32編碼方式,即Geohash中的每一個字母或者數字(如
wx4g0e
中的w)都是由5bits組成(2^5 = 32,base32),這5bits可以有32中不同的組合(0~31),這樣我們可以將整個地圖區域分為32個區域,透過00000 ~ 11111來標識這32個區域。第一次對地圖劃分後的情況如下圖所示(每個區域中的編號對應於該區域所對應的編碼)
https://images2015。cnblogs。com/blog/1004194/201612/1004194-20161220214008089-542894090。jpg
Geohash的0、1串序列是經度0、1序列和緯度0、1序列中的數字交替進行排列的,偶數位對應的序列為經度序列,奇數位對應的序列為緯度序列,
在進行第一次劃分時,Geohash0、1序列中的前5個bits(11100),那麼這5bits中有3bits是表示經度,2bits表示緯度
,所以第一次劃分時,是將經度劃分成8個區段(2^3 = 8),將緯度劃分為4個區段(2^2 = 4),
這樣就形成了32個區域(對應Base32)
。如上下圖
GeoHash將每一個區域畫成一塊塊矩形塊,每個矩形塊使用一個字串表示,當我們需要查詢附近的點時,透過自己的座標計算出一個字串,根據這個字串定位到我們所在的矩形塊,然後返回這個矩形塊中的點。例如
wx4e
就包含
wx4e0e,
也就是說
wx4e0e
在
wx4e
範圍內。
按照這種思路進行,思路逐漸清晰,但是,這種方式會不會有什麼問題呢? 或者說,它有什麼弊端。
按照單個區域情況考慮,就會出現如上所示的情況。所以就得想辦法解決這種情況,就需要將範圍進一步擴大。
目前比較通行的做法就是
我們不僅獲取當前我們所在的矩形區域,還獲取周圍8個矩形塊中的點。
那麼怎樣定位周圍8個點呢?關鍵就是需要獲取周圍8個點的經緯度,那我們已經知道自己的經緯度,
只需要用自己的經緯度減去最小劃分單位的經緯度就行
。因為我們知道經緯度的範圍,有知道需要劃分的次數,所以很容易就能計算出最小劃分單位的經緯度。
透過上面這張圖,我們就能很容易的計算出周圍8個點的經緯度了。有了經緯度就能定位到周圍8個矩形塊了。這樣我們就能獲取包括自己所在矩形塊的9個矩形塊中的所有的點。最後分別計算這些點和自己的距離(由於範圍很小,點的數量就也很少,計算量就很少)過濾掉不滿足條件的點就行了。
試想一下,我們開啟百度地圖或者是百度地圖,在頁面中,我們可以看到一個以自我為中心單位範圍的一個
圓
,之前我一直有疑惑,覺得GeoHash應該是已圓為範圍,半徑為單位進行搜尋附近的標誌。那麼,在實際開發中,會不會地圖上顯示的圓,僅僅是一種參考,以表示自己周圍範圍內的標誌,而在程式碼計算中,依舊是已矩形形式來計算的呢? 這個問題,希望我可以在後面進一步的學習和深入瞭解相關地圖API後得到解答。
附上GeoHash字串對應地圖精度參考圖
對了,
Redis支援Geo
,可檢視Redis操作文件API(
http://redisdoc.com/geo/geohash.html
)
在Redis3。2之後引入的。
本文參考資料:
連結:
https://www.cnblogs.com/LBSer/p/3310455.html
連結:
https://zhuanlan.zhihu.com/p/27771446
連結:
https://www.cnblogs.com/tgzhu/p/6204173.html
連結:
https://www。
cnblogs。com/zhenbianshu
/p/6863405。html
2018年4月21日下午地鐵上續更:
前面提到的常營地鐵站(lat:39。9257460000,lng:116。5998310000)
將其按geohash拆分規律拆分後的二進位制值應該為
11100 11101 00100 01111 10001 10010 00011 00010 01110 10111 11101
轉成10進製得到28 29 4 15 17 18 3 2 18 14 23 29
轉換成Base32值為wx4gjk32kfrx
下面是轉換過程
28(w)
16+8+4+0+0
1*2^4+1*2^3+1*2^2+0*2^1+0*2^0
11100 ——> Base32 ——>w
29(x)
16+8+4+0+1
1*2^4+1*2^3+1*2^2+0*2^1+1*2^0
11101 ——> Base32 ——>x
4(4)
0+0+4+0+0
0*2^4+0*2^3+1*2^2+0*2^1+0^2^0
00100 ——> Base32 ——>4
15(g)
0+8+4+2+1
0*2^4+1*2^3+1*2^2+1*2^1+1*2^0
01111 ——> Base32 ——>g
17(j)
16+0+0+0+1
1*2^4+0*2^3+0*2^2+0*2^1+1*2^0
10001 ——> Base32 ——>j
18(k)
16+0+0+2+0
1*2^4+0*2^3+0*2^2+1*2^1+0*2^0
10010 ——> Base32 ——>k
3(3)
0+0+0+2+1
0*2^4+0*2^3+0*2^2+1*2^1+1*2^0
00011 ——> Base32 ——>3
2(2)
0+0+0+2+0
0*2^4+0*2^3+0*2^2+1*2^1+0*2^0
00010 ——> Base32 ——>2
18(k)
16+0+0+2+0
1*2^4+0*2^3+0*2^2+1*2^1+0*2^0
10010 ——> Base32 ——>k
14(f)
0+8+4+2+0
0*2^4+1*2^3+1*2^2+1*2^1+0*2^0
01110 ——> Base32 ——>f
23(r)
16+0+4+2+1
。。。
10111 ——> Base32 ——>r
29(x)
16+8+4+0+1
1*2^4+1*2^3+1*2^2+0*2^1+1*2^0
11101 ——> Base32 ——>x
得到值wx4gjk32kfrx(常營地鐵站)