Uber開(kāi)源H3算法(地理索引)的工程化實(shí)現(xiàn)

何為地理索引:

? ? 在工程領(lǐng)域,尤其是電商平臺(tái),外賣平臺(tái),打車平臺(tái)頻繁使用的比如某一范圍內(nèi)的商品,商家功能等。會(huì)使用到某一范圍比如10Km范圍內(nèi)的所有商家場(chǎng)景。那么如何更加方便,簡(jiǎn)單的實(shí)現(xiàn)這樣的場(chǎng)景呢?

? ?常用的有:基于GeoHash原理的實(shí)現(xiàn),Google-S2 空間索引的實(shí)現(xiàn),Geomesa Z3的實(shí)現(xiàn),Uber H3索引的實(shí)現(xiàn)等;

? ?但是不同的使用場(chǎng)景,每個(gè)不同公司可以按照需求的不同選擇自己所需的實(shí)現(xiàn)技術(shù)棧。本文將會(huì)結(jié)合我們已經(jīng)在工程領(lǐng)域中實(shí)現(xiàn)的Uber H3索引進(jìn)行詳細(xì)講解。

1.H3索引介紹

H3索引,由Uber公司開(kāi)源?;A(chǔ)為C語(yǔ)言編寫的算法;h3-java為包裝JNI的通用接口抽象;(由于我們的主要技術(shù)語(yǔ)言為java,本文將著重介紹我們關(guān)于java方面的使用場(chǎng)景)

官方介紹文檔手冊(cè):https://uber.github.io/h3/#/documentation/overview/introduction

開(kāi)源代碼地址:https://github.com/uber/h3

java版本開(kāi)源代碼地址:https://github.com/uber/h3-java

? ? ? ? ??H3地理空間索引系統(tǒng)是具有分級(jí)線性索引索引的球體的多精度六角形平鋪。

? ? ? ? ? H3核心庫(kù)對(duì)緯度/經(jīng)度坐標(biāo)和之間的轉(zhuǎn)換提供了函數(shù)H3的地理空間索引。

2.當(dāng)前階段Uber開(kāi)源的H3工具包能夠?qū)ν馓峁┑姆?wù):

? ? ? ? ? 給定緯度/經(jīng)度點(diǎn),找到特定分辨率下包含H3單元的索引

? ? ? ? ? 給定H3索引,找到緯度/經(jīng)度單元中心

? ? ? ? ? 給定H3索引,確定緯度/經(jīng)度坐標(biāo)中的單元邊界

? ? ? ? ?其他等

3.重要的參考指標(biāo):

? ? ? ? 指標(biāo)如下:

? ? 核心指標(biāo)介紹: Resolution (精度),正六邊形覆蓋面積,正六邊形變長(zhǎng),每個(gè)精度對(duì)應(yīng)的索引個(gè)數(shù);

4. 轉(zhuǎn)換下思維方式:

? ? ?也就是從單純的點(diǎn)到點(diǎn)覆蓋范圍計(jì)算,查詢效率低下,而Uber H3通過(guò)為全球地理位置定制了一套,正六邊形構(gòu)成的覆蓋圖,每個(gè)覆蓋圖有一個(gè)唯一Id,通過(guò)空間換時(shí)間的思想,讓點(diǎn)對(duì)點(diǎn)的查詢變成了索引結(jié)構(gòu)的匹配查詢。實(shí)現(xiàn)了在沒(méi)有巨大誤差前提下的高效查詢。

5.工程化實(shí)踐介紹:

使用依賴如下:

<dependency>? ? ?

? ? <groupId>com.uber</groupId>? ?

? ? <artifactId>h3</artifactId>? ?

? ? <version>3.4.0</version>

</dependency>

初始化H3的計(jì)算對(duì)象和通過(guò)經(jīng)緯度獲得H3索引:

private static void init() {

? ? try {

? ? ? ? h3 = H3Core.newInstance();

? ? }catch (IOException e) {?

? ? ? ?logger.error("H3Instance init failed", e);

? ? }

}

public static String getH3IndexByCoordinate(double latitude, double longitude, H3ResolutionEnum resolution) {

//param validate 經(jīng)緯度參數(shù)的合法校驗(yàn)

? if (!isValidCoordinate(latitude, longitude)) {

? ? ? logger.warn("getH3IndexByCoordinate param is invalid,coordinate:{},{}", longitude, latitude);

? ? ? throw new IllegalArgumentException();

? }

//execute h3 index calculate 執(zhí)行通過(guò)經(jīng)緯度 選擇的精度,計(jì)算H3索引邏輯

? ? try {

? ? ? ? if (h3 == null) {

? ? ? ? ? ? init();

? ? }

? ? ? ? // 得到對(duì)應(yīng)精度的H3索引

? ? ? ? return h3.geoToH3Address(latitude, longitude, resolution.precision);

? ? } catch (Exception e) {

? ? ? ? logger.error("geoToH3Address-error", e);

? ? ? ? throw e;

? ? }

}

通過(guò)H3索引找到對(duì)應(yīng)的正六邊形中心點(diǎn)坐標(biāo)經(jīng)緯度:

public static Optional<GeoCoord> getCoordinateByH3Index(String h3Index) {

? ? try {

? ? ? ? //param validate 校驗(yàn)H3索引字符串是否合法

? ? ? ? if (h3PreCheckInvalid(h3Index)) {

? ? ? ? logger.warn("getCoordinateByH3Index,param is invalid!");

? ? ? ? return Optional.empty();

? ? ? ? }

? ? ? ? //execute 獲取正六邊形中心坐標(biāo)

? ? ? ? GeoCoord geoCoord = h3.h3ToGeo(h3Index);

? ? ? ? return Optional.of(geoCoord);

? ? } catch (Exception e) {

? ? ? ? logger.error("getCoordinateByH3Index error,h3Index:" + h3Index, e);

? ? ? ? return Optional.empty();

? ? }

}

有了上述的兩種方式,其實(shí)我們已經(jīng)可以通過(guò)Uber H3索引的屬性做自己需要的業(yè)務(wù)拓展了。

比如通過(guò)某一個(gè)點(diǎn),獲取到符合精度的正六邊形中心坐標(biāo),以這個(gè)正六邊形中心坐標(biāo),計(jì)算直線距離N公里內(nèi)有多少個(gè)索引,得到索引集合。

那么則可以通過(guò)索引集合來(lái)查詢貨物或者商家是否有符合集合內(nèi)索引的屬性,如果有則證明在N公里范圍內(nèi),如果沒(méi)有則證明不在N公里范圍內(nèi)。

H3核心API方法列舉:

final class NativeMethods {

? ? NativeMethods() {

? ? }

? ??native int maxH3ToChildrenSize(long var1, int var3);

? ??native void h3ToChildren(long var1, int var3, long[] var4);

? ??native boolean h3IsValid(long var1);

? ??native int h3GetBaseCell(long var1);

? ? native boolean h3IsPentagon(long var1);

? ? native long geoToH3(double var1, double var3, int var5);

? ? native void h3ToGeo(long var1, double[] var3);

? ? native int h3ToGeoBoundary(long var1, double[] var3);

? ? native int maxKringSize(int var1);

? ? native void kRing(long var1, int var3, long[] var4);

? ? native void kRingDistances(long var1, int var3, long[] var4, int[] var5);

? ? native int hexRange(long var1, int var3, long[] var4);

? ? native int hexRing(long var1, int var3, long[] var4); native int h3Distance(long var1, long var3);

? ? native int experimentalH3ToLocalIj(long var1, long var3, int[] var5);

? ? native long experimentalLocalIjToH3(long var1, int var3, int var4);

? ? native int h3LineSize(long var1, long var3);

? ? native int h3Line(long var1, long var3, long[] var5);

? ? native int maxPolyfillSize(double[] var1, int[] var2, double[] var3, int var4);

? ? native void polyfill(double[] var1, int[] var2, double[] var3, int var4, long[] var5);

? ? native void h3SetToLinkedGeo(long[] var1, ArrayList<List<List<GeoCoord>>> var2);

? ? native int compact(long[] var1, long[] var2);

? ? native int maxUncompactSize(long[] var1, int var2);

? ? native int uncompact(long[] var1, int var2, long[] var3);

? ? native double hexAreaKm2(int var1); native double hexAreaM2(int var1);

? ? native double edgeLengthKm(int var1);

? ? native double edgeLengthM(int var1);

? ? native long numHexagons(int var1);

? ? native void getRes0Indexes(long[] var1);

? ? native boolean h3IndexesAreNeighbors(long var1, long var3);

? ? native long getH3UnidirectionalEdge(long var1, long var3);

? ? native boolean h3UnidirectionalEdgeIsValid(long var1);

? ? native long getOriginH3IndexFromUnidirectionalEdge(long var1);

? ? native long getDestinationH3IndexFromUnidirectionalEdge(long var1);

? ? native void getH3IndexesFromUnidirectionalEdge(long var1, long[] var3);

? ? native void getH3UnidirectionalEdgesFromHexagon(long var1, long[] var3);

? ? native int getH3UnidirectionalEdgeBoundary(long var1, double[] var3);

}

那么如何正確的入手Uber H3呢?

我建議:1.了解自己的需求場(chǎng)景是否符合范圍查找的場(chǎng)景。

? ? ? ? ? ? ? 2.判斷是否對(duì)范圍查找的誤差要求沒(méi)有特別的高。

? ? ? ? ? ? ? 3.下載Uber H3源碼包:源碼包中提供了,基礎(chǔ)的場(chǎng)景測(cè)試用例,基準(zhǔn)測(cè)試用例。下載代碼后本地debug就可以進(jìn)行調(diào)試。

? ? ? ? ? ? ? https://github.com/uber/h3-java/tree/master/src/test/java/com/uber/h3core??

? ? ? ? ? ? ? 4.調(diào)試完畢后,根據(jù)自己的場(chǎng)景模型,構(gòu)造符合自己使用的API或者工具類,提供給使用方。

歡迎大家多提寶貴意見(jiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容