在日常生活中,我們經常會有很多和地理相關的一些應用,比如:地圖導航,附近的人,查詢某些地標等等,以前我們會選擇問路,而隨著智慧裝置的更新和升級,現在我們僅僅只需要一個手機,開啟一個地圖APP,即可快速查詢自己想要的路線或者資訊了。

那麼,附近的一個建築或者商店,是透過怎樣的形式,讓其可以在手機上展示出來的呢?

比如:週末了,躺在床上,使用手機下單,尋找外賣美食。。。這些美食店資訊是怎麼在手機上進行展示的呢,它和我所在的距離是怎麼計算出來的呢? 這時候,可能就需要用到GeoHash這種演算法了。前段時間一個朋友問到過我關於GeoHash的相關知識,但是對這種演算法,只是有所瞭解,並沒有深入去探索內部的實現,在此,給它進行自我解析與記錄。

GeoHash演算法學習講解、解析及原理分析

先簡單介紹一下GeoHash演算法,在日常生活中,我們對某一座標的定位,一半都是使用經緯度來進行標記的。比如:

常營地鐵站(lat:39.9257460000,lng:116.5998310000)

,我們獲取一個區域的位置,是使用一個二維陣列對其進行標記的,

它表示的不是一個具體的點

,而是泛指一片區域,區域的範圍與經緯度的取值精度直接相關。

當我拿到常營地鐵站的經緯度後,透過GeoHash這種演算法進行計算後,獲取到一個可比較的字串,具體計算過程如下:

GeoHash演算法學習講解、解析及原理分析

同樣,對緯度也進行相對應的演算法進行計算得到一個二進位制值(對經緯度取值範圍越小,精度越高,所表示區域範圍越小),在此省去計算過程。

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個字元編碼形式。如程式碼:

GeoHash演算法學習講解、解析及原理分析

Base32對應編碼參考圖:(上面二進位制轉換對應的十進位制數值)

GeoHash演算法學習講解、解析及原理分析

維基百科上Base32位值資訊表

下面是從網上線上解析的常營地鐵站GeoHash值(

wx4gjk32kfrx

GeoHash演算法學習講解、解析及原理分析

網址: http://geohash。co/

Geohash其實就是將整個地圖或者某個分割所得的區域進行一次劃分,由於採用的是base32編碼方式,即Geohash中的每一個字母或者數字(如

wx4g0e

中的w)都是由5bits組成(2^5 = 32,base32),這5bits可以有32中不同的組合(0~31),這樣我們可以將整個地圖區域分為32個區域,透過00000 ~ 11111來標識這32個區域。第一次對地圖劃分後的情況如下圖所示(每個區域中的編號對應於該區域所對應的編碼)

GeoHash演算法學習講解、解析及原理分析

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演算法學習講解、解析及原理分析

GeoHash將每一個區域畫成一塊塊矩形塊,每個矩形塊使用一個字串表示,當我們需要查詢附近的點時,透過自己的座標計算出一個字串,根據這個字串定位到我們所在的矩形塊,然後返回這個矩形塊中的點。例如

wx4e

就包含

wx4e0e,

也就是說

wx4e0e

wx4e

範圍內。

按照這種思路進行,思路逐漸清晰,但是,這種方式會不會有什麼問題呢? 或者說,它有什麼弊端。

GeoHash演算法學習講解、解析及原理分析

按照單個區域情況考慮,就會出現如上所示的情況。所以就得想辦法解決這種情況,就需要將範圍進一步擴大。

目前比較通行的做法就是

我們不僅獲取當前我們所在的矩形區域,還獲取周圍8個矩形塊中的點。

那麼怎樣定位周圍8個點呢?關鍵就是需要獲取周圍8個點的經緯度,那我們已經知道自己的經緯度,

只需要用自己的經緯度減去最小劃分單位的經緯度就行

。因為我們知道經緯度的範圍,有知道需要劃分的次數,所以很容易就能計算出最小劃分單位的經緯度。

GeoHash演算法學習講解、解析及原理分析

透過上面這張圖,我們就能很容易的計算出周圍8個點的經緯度了。有了經緯度就能定位到周圍8個矩形塊了。這樣我們就能獲取包括自己所在矩形塊的9個矩形塊中的所有的點。最後分別計算這些點和自己的距離(由於範圍很小,點的數量就也很少,計算量就很少)過濾掉不滿足條件的點就行了。

試想一下,我們開啟百度地圖或者是百度地圖,在頁面中,我們可以看到一個以自我為中心單位範圍的一個

,之前我一直有疑惑,覺得GeoHash應該是已圓為範圍,半徑為單位進行搜尋附近的標誌。那麼,在實際開發中,會不會地圖上顯示的圓,僅僅是一種參考,以表示自己周圍範圍內的標誌,而在程式碼計算中,依舊是已矩形形式來計算的呢? 這個問題,希望我可以在後面進一步的學習和深入瞭解相關地圖API後得到解答。

附上GeoHash字串對應地圖精度參考圖

GeoHash演算法學習講解、解析及原理分析

GeoHash演算法學習講解、解析及原理分析

對了,

Redis支援Geo

,可檢視Redis操作文件API(

http://redisdoc.com/geo/geohash.html

在Redis3。2之後引入的。

GeoHash演算法學習講解、解析及原理分析

GeoHash演算法學習講解、解析及原理分析

本文參考資料:

連結:

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(常營地鐵站)