【搬磚筆記】 利用GeoHash爲地理位置編碼——理論篇

兄弟篇:【搬磚筆記】 利用GeoHash爲地理位置編碼——實現篇html

1、前言

最近有個需求,要計算出客戶座標附近5千米的全部門店,並按照步行距離排序。算法

最直接的方法就是遍歷該城市下的全部門店,可是該方法明顯不可取,由於在門店數量巨大,且還須要計算步行距離並排序的狀況下,時間複雜度太高。post

忽然想到當年作圖像碰見一個問題:給定一個視頻中連續的三千幀,可是已經亂序,告訴你第一幀,將這三千幀進行排序。遍歷圖像的全部像素點一樣不可取,當時的解決方案是利用感知哈希計算出全部圖像的指紋,接着利用明氏距離計算距離第一張最近的圖像做爲第二張,而後計算距離第二張最近的做爲第三張,以此類推。編碼

一樣,確定也有哈希方法可將地理位置轉換成某種編碼,而且編碼可用於地理計算。它就是 GeoHash.net

2、相關知識

進入正文以前,先一塊兒回顧一下初中地理:code

本初子午線以西爲西經,分十二區,每區15度共180度;以東爲東經,一樣分十二區,共180度。cdn

赤道以北爲北緯,共90度;以南爲南緯,一樣90度。視頻

那麼在計算機中,座標表示爲:htm

西經爲負數,東經爲正數,所以經度取值[-180, 180]blog

北緯爲正數,南緯爲負數,所以緯度取值[-90, 90]

咱們知道赤道長約四萬千米,所以經度上每度約等於111千米。地球其實是個不規則球體,但爲了簡便計算,咱們假設把緯度上每度約等於222千米。

3、初識 GeoHash

1. 計算二進制編碼

首先計算二進制編碼,經度上以[-180, 180]開始,緯度以[-90, 90]開始,每次將區間進行二分,若輸入座標小於中間值則編碼爲0,下次區間取左半區間;若大於則編碼爲1,下次區間取右半區間。以此類推,編碼越長,越接近座標值,進而越精確。

118°04′04, 24°26′46 爲例,首先計算經度的編碼:

編碼長度 區間 中間值 編碼 說明 精度(公里)
1 [-180, 180] 0 1 118°04′04 大於 0°,所以編碼1,取右區間 19980
2 [0, 180] 90 1 118°04′04 大於 90° 9990
3 [90, 180] 135 0 118°04′04 小於 135°,所以編碼0,取左區間 4995
4 [90, 135] 112.5 1 2497.5
5 [112.5, 135] 123.75 0 1248.75
6 [112.5, 123.75] 118.125 0 624.375‬
7 [112.5, 118.125] 115.3125 1 312.188
8 [115.3125, 118.125] 116.71875‬ 1 156.094
9 [116.71875‬, 118.125] 117.421875 1 78.047‬
10 [117.421875, 118.125] 117.7734375‬ 1 39.024
N ... ... . ...

一樣道理,計算緯度得編碼:

編碼長度 區間 中間值 編碼 說明 精度(公里)
1 [-90, 90] 0 1 24°26′46 大於 0°,所以編碼1,取右區間 19980
2 [0, 90] 45 0 24°26′46 小於 45°,所以編碼0,取左區間 9990
3 [0, 45] 22.5 1 4995
4 [22.5, 45] 33.75 0 2497.5
5 [22.5, 33.75] 28.125 0 1248.75
6 [22.5, 28.125] 25.3125 0 624.375‬
7 [22.5, 25.3125] 23.906 1 312.188
8 [23.906, 25.3125] 24.609 0 156.094
9 [23.906, 24.609] 24.2575‬ 1 78.047‬
10 [24.2575‬, 24.609] 24.433 0 39.024
N ... ... . ...

綜上,假設咱們只取十位編碼,經度上獲得得編碼爲 1101001111,緯度上編碼爲 1010001010

將兩個編碼就像經緯交織網同樣進行交織:

在這裏插入圖片描述
最後獲得經緯二進制編碼爲 11100110000011101110,結合以上兩表的精度數據,咱們知道該編碼其實表明的是一塊長寬爲39.024公里的矩形塊。

2. 轉換base32編碼

二進制編碼其實就能夠用來做爲地理位置編碼,可是:

  1. 不便於查找周邊鄰塊。計算鄰塊,須要解析成經、緯兩個編碼再作計算。而採用base32編碼後,可用查表法,加速計算。
  2. 二進制編碼長度過長,不利於檢索。

所以,GeoHash 採用了 base32 和 base36編碼,由於大多數採用 base32 編碼,所以本文僅介紹 base32 編碼。

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Base 32 0 1 2 3 4 5 6 7 8 9 B C D E F G
Decimal 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Base 32 H J K M N P Q R S T U V W X Y Z

base32 編碼共 32 個編碼,所以須要 5 個bit,將11100110000011101110轉換爲 base32 編碼:

在這裏插入圖片描述
最後獲得編碼爲 WS7F

4、計算鄰近塊

1. 通用法

通用法的輸入是二進制編碼,所以不像查表法有位數限制,適用於全部狀況,流程簡單。

首先將經緯交織的二進制編碼拆分爲經度、緯度,若要求輸入位置以北,則將緯度加一;以南,則將緯度減一;以西,則將經度減一;以東,則將經度加一。須要注意的是,加減後必須保持原有的有效位數(好比,11 + 01 = 00,保持兩位有效位)。最後將計算結果從新組合,獲得要求位置的二進制編碼。

過程以下圖爲例,再也不贅述:

在這裏插入圖片描述

2. 查表法

查表法的計算比通用法快不少,不用將輸入編碼拆分紅經緯度進行加減,但須要注意的是:輸入的是base32編碼,所以僅適用於二進制編碼位數爲5的倍數的GeoHash

網上大部分文章僅講訴如何利用查表法計算鄰塊,那麼這個查表如何獲得呢? 獲得了編碼後,如何計算鄰塊?網上大部分文章僅講訴如何利用查表法計算鄰塊,可是這個查表如何獲得呢?

根據前面,咱們知道編碼由5個bit二進制,並經緯交織組成。所以,若該編碼處於奇數位上,即 經 緯 經 緯 經 交織方式;若處於偶數位上,則 緯 經 緯 經 緯 交織方式。

那麼,以 W 爲例,二進制爲 11100,若處於奇數位則在表中爲 右 上 右 下 左,若處於偶數位則在表中爲 上 右 下 左 下,所以 W 在查表中的位置以下圖:

在這裏插入圖片描述
一樣,求出其餘編碼的位置,結果爲:
在這裏插入圖片描述
WS7F爲例,如今要求該位置的鄰近塊,取末位 F,從偶數位表中能夠看到, F 的鄰近爲 E, G, D, 9, C,以及往右出了界的 5, 4, 1。所以 5, 4, 1 三個鄰塊還須要看倒數第二位 7。從奇數表中能夠看到, 7 的右邊沒有超界(若超界了,看倒數第三位,以此類推),爲 K

所以,獲得周圍鄰塊分別位 WS7E, WS7G, WS7D, WS79, WS7C, WSK5, WSK4, WSK1,位置關係以下:

在這裏插入圖片描述

5、總結

主要從理論上介紹:

  1. GeoHash對地理位置點進行編碼的方法:根據「遞歸二分」獲取二進制,將二進制轉換爲 Base32 編碼。
  2. GeoHash鄰塊的快速計算方法:取末位編碼,根據偶數位表查找對應鄰塊編碼,若某方向出界,則查找前一位編碼,並根據奇/偶表查找相應方向的編碼。

算法實現:【搬磚筆記】 利用GeoHash爲地理位置編碼——實現篇

6、延申

  1. GeoHash 不只能夠用來對位置點進行編碼,也能夠用來對面進行編碼,有助於處理點、面的多種組合關係計算。好比,判斷點位置是否在門店的配送圍欄以內。

  2. GeoHash 其實就是 Peano 曲線 的一種應用,以下:

    在這裏插入圖片描述

    還有許多空間填充曲線,好比公認比較好的,沒有較大突變的 Hilbert 曲線

在這裏插入圖片描述

參考

  1. GeoHash核心原理解析
  2. 基於快速GeoHash,如何實現海量商品與商圈的高效匹配?
相關文章
相關標籤/搜索