何為地理索引:
? ? 在工程領(lǐng)域,尤其是電商平臺灯萍,外賣平臺轧铁,打車平臺頻繁使用的比如某一范圍內(nèi)的商品,商家功能等旦棉。會使用到某一范圍比如10Km范圍內(nèi)的所有商家場景齿风。那么如何更加方便,簡單的實現(xiàn)這樣的場景呢绑洛?
? ?常用的有:基于GeoHash原理的實現(xiàn)救斑,Google-S2 空間索引的實現(xiàn),Geomesa Z3的實現(xiàn)真屯,Uber H3索引的實現(xiàn)等脸候;
? ?但是不同的使用場景,每個不同公司可以按照需求的不同選擇自己所需的實現(xiàn)技術(shù)棧绑蔫。本文將會結(jié)合我們已經(jīng)在工程領(lǐng)域中實現(xiàn)的Uber H3索引進行詳細講解运沦。
1.H3索引介紹
H3索引,由Uber公司開源配深⌒恚基礎(chǔ)為C語言編寫的算法;h3-java為包裝JNI的通用接口抽象篓叶;(由于我們的主要技術(shù)語言為java烈掠,本文將著重介紹我們關(guān)于java方面的使用場景)
官方介紹文檔手冊:https://uber.github.io/h3/#/documentation/overview/introduction
開源代碼地址:https://github.com/uber/h3
java版本開源代碼地址:https://github.com/uber/h3-java
? ? ? ? ??H3地理空間索引系統(tǒng)是具有分級線性索引索引的球體的多精度六角形平鋪。
? ? ? ? ? H3核心庫對緯度/經(jīng)度坐標和之間的轉(zhuǎn)換提供了函數(shù)H3的地理空間索引澜共。
2.當前階段Uber開源的H3工具包能夠?qū)ν馓峁┑姆眨?br>
? ? ? ? ? 給定緯度/經(jīng)度點向叉,找到特定分辨率下包含H3單元的索引
? ? ? ? ? 給定H3索引,找到緯度/經(jīng)度單元中心
? ? ? ? ? 給定H3索引嗦董,確定緯度/經(jīng)度坐標中的單元邊界
? ? ? ? ?其他等
3.重要的參考指標:
? ? ? ? 指標如下:
? ? 核心指標介紹: Resolution (精度)母谎,正六邊形覆蓋面積,正六邊形變長京革,每個精度對應的索引個數(shù)奇唤;
4. 轉(zhuǎn)換下思維方式:
? ? ?也就是從單純的點到點覆蓋范圍計算幸斥,查詢效率低下,而Uber H3通過為全球地理位置定制了一套咬扇,正六邊形構(gòu)成的覆蓋圖甲葬,每個覆蓋圖有一個唯一Id,通過空間換時間的思想懈贺,讓點對點的查詢變成了索引結(jié)構(gòu)的匹配查詢经窖。實現(xiàn)了在沒有巨大誤差前提下的高效查詢。
5.工程化實踐介紹:
使用依賴如下:
<dependency>? ? ?
? ? <groupId>com.uber</groupId>? ?
? ? <artifactId>h3</artifactId>? ?
? ? <version>3.4.0</version>
</dependency>
初始化H3的計算對象和通過經(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ù)的合法校驗
? if (!isValidCoordinate(latitude, longitude)) {
? ? ? logger.warn("getH3IndexByCoordinate param is invalid,coordinate:{},{}", longitude, latitude);
? ? ? throw new IllegalArgumentException();
? }
//execute h3 index calculate 執(zhí)行通過經(jīng)緯度 選擇的精度画侣,計算H3索引邏輯
? ? try {
? ? ? ? if (h3 == null) {
? ? ? ? ? ? init();
? ? }
? ? ? ? // 得到對應精度的H3索引
? ? ? ? return h3.geoToH3Address(latitude, longitude, resolution.precision);
? ? } catch (Exception e) {
? ? ? ? logger.error("geoToH3Address-error", e);
? ? ? ? throw e;
? ? }
}
通過H3索引找到對應的正六邊形中心點坐標經(jīng)緯度:
public static Optional<GeoCoord> getCoordinateByH3Index(String h3Index) {
? ? try {
? ? ? ? //param validate 校驗H3索引字符串是否合法
? ? ? ? if (h3PreCheckInvalid(h3Index)) {
? ? ? ? logger.warn("getCoordinateByH3Index,param is invalid!");
? ? ? ? return Optional.empty();
? ? ? ? }
? ? ? ? //execute 獲取正六邊形中心坐標
? ? ? ? GeoCoord geoCoord = h3.h3ToGeo(h3Index);
? ? ? ? return Optional.of(geoCoord);
? ? } catch (Exception e) {
? ? ? ? logger.error("getCoordinateByH3Index error,h3Index:" + h3Index, e);
? ? ? ? return Optional.empty();
? ? }
}
有了上述的兩種方式,其實我們已經(jīng)可以通過Uber H3索引的屬性做自己需要的業(yè)務拓展了堡妒。
比如通過某一個點,獲取到符合精度的正六邊形中心坐標皮迟,以這個正六邊形中心坐標伏尼,計算直線距離N公里內(nèi)有多少個索引休溶,得到索引集合扰她。
那么則可以通過索引集合來查詢貨物或者商家是否有符合集合內(nèi)索引的屬性,如果有則證明在N公里范圍內(nè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.了解自己的需求場景是否符合范圍查找的場景。
? ? ? ? ? ? ? 2.判斷是否對范圍查找的誤差要求沒有特別的高瞻讽。
? ? ? ? ? ? ? 3.下載Uber H3源碼包:源碼包中提供了鸳吸,基礎(chǔ)的場景測試用例,基準測試用例速勇。下載代碼后本地debug就可以進行調(diào)試晌砾。
? ? ? ? ? ? ? https://github.com/uber/h3-java/tree/master/src/test/java/com/uber/h3core??
? ? ? ? ? ? ? 4.調(diào)試完畢后,根據(jù)自己的場景模型烦磁,構(gòu)造符合自己使用的API或者工具類养匈,提供給使用方哼勇。
歡迎大家多提寶貴意見。