Geohash精度和原理


轉自:https://blog.csdn.net/u011497262/article/details/81210634

   https://www.jianshu.com/p/1ecf03293b9a

 geohash基本原理是將地球理解為一個二維平面,將平面遞歸分解成更小的子塊,每個子塊在一定經緯度范圍內擁有相同的編碼,這種方式簡單粗暴,可以滿足對小規模的數據進行經緯度的檢索

目錄:

  • 經緯度常識
  • 認識geohash
  • geohash算法
  • geohash原理
  • 對照表

經緯度常識


  • 經線是縱的,經度是橫的,用於表示不同的經線,緯線是橫的,緯度是縱的,用於表示不同的緯線,如下圖
  •    
  • 緯線:地球儀上的橫線,lat,赤道是最大的緯線,從赤道開始分為北緯和南緯,都是0-90°,緯線是角度數值,並不是米;
  • 經線:地球儀上的豎線,lng,子午線為0°,分為西經和東經,都是0-180°,經線也是角度數值;
  • 經緯線和米的換算:經度或者緯度0.00001度,約等於1米,這個在GPS測算距離的時候可以體會到,GPS只要精確到小數點后五位,就是10米范圍內的精度
  • 經度0度的位置為本初子午線,在180度的位置轉為西經,數字由大到小依次經過北美洲到達西歐.緯度0度的位置為赤道
  • 為了便於理解,將地球看成一個基於經緯度線的坐標系。緯線就是平行於赤道平面的那些平面的周線,經線就是連接南北兩極的大圓線的半圓弧。緯度分為北緯(正),南緯(負),赤道所在的緯度值為0。經度以本初子午線界(本初子午線經度為0),分為東經(正),西經(負)。故緯度范圍可表示為[-90o, 0o),(0o, 90o],經度范圍可表示為[-180o, 0o),(0o, 180o]
  • 將經度和緯度看成二維坐標系中的兩個緯度,橫軸表示經度,縱軸表示緯度,如上圖

認識geohash


  • GeoHash將二維的經緯度轉換成字符串,比如下圖展示了北京9個區域的GeoHash字符串,分別是WX4ER,WX4G2、WX4G3等等,每一個字符串代表了某一矩形區域。也就是說,這個矩形區域內所有的點(經緯度坐標)都共享相同的GeoHash字符串,這樣既可以保護隱私(只表示大概區域位置而不是具體的點),又比較容易做緩存。
  • 不同的編碼長度,表示不同的范圍區間,字符串越長,表示的范圍越精確
  • 字符串相似的表示距離相近(特殊情況后文闡述),這樣可以利用字符串的前綴匹配來查詢附近的POI信息。如下兩個圖所示,一個在城區,一個在郊區,城區的GeoHash字符串之間比較相似,郊區的字符串之間也比較相似,而城區和郊區的GeoHash字符串相似程度要低些
  • 總結:GeoHash就是一種將經緯度轉換成字符串的方法,並且使得在大部分情況下,字符串前綴匹配越多的距離越近

geohash算法


    以經緯度值:(116.389550, 39.928167)進行算法說明,對緯度39.928167進行逼近編碼 (地球緯度區間是[-90,90])

  1. 區間[-90,90]進行二分為[-90,0),[0,90],稱為左右區間,可以確定39.928167屬於右區間[0,90],給標記為1
  2. 接着將區間[0,90]進行二分為 [0,45),[45,90],可以確定39.928167屬於左區間 [0,45),給標記為0
  3. 遞歸上述過程39.928167總是屬於某個區間[a,b]。隨着每次迭代區間[a,b]總在縮小,並越來越逼近39.928167
  4. 如果給定的緯度x(39.928167)屬於左區間,則記錄0,如果屬於右區間則記錄1,序列的長度跟給定的區間划分次數有關,如下圖
  • 同理,地球經度區間是[-180,180],可以對經度116.389550進行編碼
  • 通過上述計算,緯度產生的編碼為1 1 0 1 0 0 1 0 1 1 0 0 0 1 0,經度產生的編碼為1 0 1 1 1 0 0 0 1 1 0 0 0 1 1
  • 合並:偶數位放經度,奇數位放緯度,把2串編碼組合生成新串如下圖:
  • 首先將11100 11101 00100 01111 0000  01101轉成十進制,對應着28、29、4、15,0,13 十進制對應的base32編碼就是wx4g0e,如下圖
  • Ø同理,將編碼轉換成經緯度的解碼算法與之相反

 geohash原理


  • Geohash其實就是將整個地圖或者某個分割所得的區域進行一次划分,由於采用的是base32編碼方式,即Geohash中的每一個字母或者數字(如wx4g0e中的w)都是由5bits組成(2^5 = 32,base32),這5bits可以有32中不同的組合(0~31),這樣我們可以將整個地圖區域分為32個區域,通過00000 ~ 11111來標識這32個區域。第一次對地圖划分后的情況如下圖所示(每個區域中的編號對應於該區域所對應的編碼):

  • Geohash的0、1串序列是經度0、1序列和緯度0、1序列中的數字交替進行排列的,偶數位對應的序列為經度序列,奇數位對應的序列為緯度序列,在進行第一次划分時,Geohash0、1序列中的前5個bits(11100),那么這5bits中有3bits是表示經度,2bits表示緯度,所以第一次划分時,是將經度划分成8個區段(2^3 = 8),將緯度划分為4個區段(2^2 = 4),這樣就形成了32個區域。如下圖

  •  

  • 同理,可以按照第一次划分所采用的方式對第一次划分所得的32個區域各自再次划分. 

對照表


  • 5.  GeoHash缺陷

            上文講了GeoHash的計算步驟,僅僅說明是什么而沒有說明為什么?為什么分別給經度和維度編碼?為什么需要將經緯度兩串編碼交叉組合成一串編碼?本節試圖回答這一問題。

            如圖所示,我們將二進制編碼的結果填寫到空間中,當將空間划分為四塊時候,編碼的順序分別是左下角00,左上角01,右下腳10,右上角11,也就是類似於Z的曲線,當我們遞歸的將各個塊分解成更小的子塊時,編碼的順序是自相似的(分形),每一個子快也形成Z曲線,這種類型的曲線被稱為Peano空間填充曲線。

            這種類型的空間填充曲線的優點是將二維空間轉換成一維曲線(事實上是分形維),對大部分而言,編碼相似的距離也相近,但Peano空間填充曲線最大的缺點就是突變性,有些編碼相鄰但距離卻相差很遠,比如0111與1000,編碼是相鄰的,但距離相差很大。

     
     

            除Peano空間填充曲線外,還有很多空間填充曲線,如圖所示,其中效果公認較好是Hilbert空間填充曲線,相較於Peano曲線而言,Hilbert曲線沒有較大的突變。但是由於Peano曲線實現更加簡單,在使用的時候配合一定的解決手段,可以很好的滿足大部分需求,因此TD內部Geohash算法采用的是Peano空間填充曲線。

     
     

    6. 使用注意點

        a. 由於GeoHash是將區域划分為一個個規則矩形,並對每個矩形進行編碼,這樣在查詢附近POI信息時會導致以下問題,比如紅色的點是我們的位置,綠色的兩個點分別是附近的兩個餐館,但是在查詢的時候會發現距離較遠餐館的GeoHash編碼與我們一樣(因為在同一個GeoHash區域塊上),而較近餐館的GeoHash編碼與我們不一致。這個問題往往產生在邊界處。

     
     

            解決的思路很簡單,我們查詢時,除了使用定位點的GeoHash編碼進行匹配外,還使用周圍8個區域的GeoHash編碼,這樣可以避免這個問題。

        b. 我們已經知道現有的GeoHash算法使用的是Peano空間填充曲線,這種曲線會產生突變,造成了編碼雖然相似但距離可能相差很大的問題,因此在查詢附近餐館時候,首先篩選GeoHash編碼相似的POI點,然后進行實際距離計算。

        c. GeoHash Base32編碼長度與精度。可以看出,當geohash base32編碼長度為8時,精度在19米左右,而當編碼長度為9時,精度在2米左右,編碼長度需要根據數據情況進行選擇。

     
     

    7. 計算圍欄內所有Geohash

            理解了geohash算法的基本原理之后,本節將介紹一個實際應用中常見的場景:計算圍欄范圍內所有的Geohash編碼。該場景封裝為函數可以表示如下:輸入組成圍欄的點經緯度坐標集合和指定的geohash長度,輸出一組geohash編碼。

            public static Set getHashByFence(List points, int length)

            具體算法步驟如下:

    1. 輸入圍欄點坐標集合List points和指定的geohash長度length

    2. 計算圍欄的外包矩形的左上角和右下角坐標lat_min、lat_max、lng_min、lng_max

    3. 根據lat_min、lat_max、lng_min、lng_max,計算外包矩形對角定點的距離d

    4. 以外包矩形中心點為圓心,以d/2為半徑做一個圓,計算圓覆蓋范圍內的geohash

        4.1 獲取圓的外包矩形左上角和右下角定點坐標經緯度,存儲到double[] locs

        4.2 根據geohash字符長度計算該長度geohash編碼對應的經緯度間隔(latA,lngA)

        4.3 根據latA和lngA,計算出locs組成的矩形的左上角和右下角定點的經緯度,在geohash划分的網格的索引(也就是第幾個),分別記為lat_min,lat_max,lng_min,lng_max

        4.4 計算lat_min,lat_max,lng_min,lng_max對應范圍內左右geohash的二進制編碼,然后將經緯度二進制編碼uncode為geohash字符編碼,保存為Set sets

    5. 剔除sets中geohash編碼對應矩形的中心點不在points圍欄范圍內的geohash,得到最終的geohash結果集




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM