開篇
我調試了很多開源項目,包括開源商城、IM即時通訊等很多,感興趣的可以看看源碼點我頭像進去,覺得有用給個關注吧。
最近在看o2o相關的原理,網上說了很多geohash演算法的原理。但其實原理網上有很多,但真正的調試少之又少,今天就來寫代碼調試一下geo演算法和spatial4工具j。輕鬆實現,附近的商家功能。覺得有用,就點個贊轉個發吧。看文章的時候最好是跟著做,如果沒時間就收藏轉發吧。
geohash演算法原理簡單介紹
geo 是地理位置的geography 的縮寫,hash其實就是hash演算法。大家都知道,我們平常用的百度地圖,高德地圖其實每一個點都是有經緯度的。你可以理解為geohash,其實就是經緯度經過一些列運算出來的hash值,相似的經緯度得到的geohash值前綴幾乎一致,這樣就可以把一個區域內的點都可以直接在資料庫中查出來,當然具體原理自己可以去搜索。這裡就不介紹了,今天重點介紹,如何使用。
源碼可以到github上看:https://github.com/kungfoo/geohash-java
spatial4j工具
其實地球是球形的大家都知道,但基本上在地圖上你可以認為是平面。例如查詢附近1km的範圍你怎麼查呢?給定圓心坐標和半徑,求該圓外切正方形四個頂點的坐標。不是嗎?這個計算我們就用到spatial4j了。github上的地址是:https://github.com/locationtech/spatial4j
核心調試
1、建表,建工程
新建一個spring boot 工程,集成mybatis等,當然建個商戶表,有商戶名稱,經緯度,geohash等欄位。
CREATE TABLE `merchant` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
`merchant_name` VARCHAR(64) NOT NULL COMMENT '名稱',
`longitude` DOUBLE(9,6) NOT NULL COMMENT '經度',
`latitude` DOUBLE(8,6) NOT NULL COMMENT '緯度',
`geo_code` CHAR(12) NOT NULL COMMENT 'geohash編碼',
PRIMARY KEY (`id`),
KEY `idx_merchant_longitude_latitude` (`longitude`,`latitude`),
KEY `idx_merchant_geo_code` (`geo_code`)
)COMMENT='商戶表' CHARSET=utf8mb4 ENGINE=InnoDB;
2、方法一、查詢附近的1km的用spatial4j實現
A、首先引入jar包:
<dependency>
<groupId>com.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.5</version>
</dependency>
B、計算四個點的的值
public static void main(String[] args) {
//起始點經緯度
double lon = 116.312528, lat = 39.983733;
// 千米
int radius = 1;
SpatialContext geo = SpatialContext.GEO;
Rectangle rectangle = geo.getDistCalc().calcBoxByDistFromPt(
geo.makePoint(lon, lat),
radius * DistanceUtils.KM_TO_DEG, geo, null);
System.out.println(rectangle.getMinX() +
"-" + rectangle.getMaxX());// 經度範圍
System.out.println(rectangle.getMinY() +
"-" + rectangle.getMaxY());// 緯度範圍
}
C、根據範圍,在資料庫中查找即可,查到附近1km的數據。當然,經緯度最好做聯合索引。這兩個數據是上面算出來的。
SELECT id, merchant_name
FROM merchant
WHERE (longitude BETWEEN ? AND ?) AND (latitude BETWEEN ? AND ?);
3、根據geohash查詢。此時會用到geo_code 欄位。
A、引入geohash的jar包
<!-- https://mvnrepository.com/artifact/ch.hsr/geohash -->
<dependency>
<groupId>ch.hsr</groupId>
<artifactId>geohash</artifactId>
<version>1.3.0</version>
</dependency>
B、對照geohash的長度對應範圍表,我們可以知道,查詢附近1km的數據,只要長度是5就可以了。具體計算,GeohashUtils.encodeLatLon(lat, lon, 5),代入底線sql即可。
SELECT id, merchant_name
FROM merchant
WHERE geo_code LIKE CONCAT(?, '%');
C、但geohash有邊界問題誤差。因為geohash是某個區域的共同的hash,索引邊界以外的距離很近的點可能造成geohash完全不同,那麼怎麼辦呢?其實也不難解決,你這個時候把周圍的八個區域的geohash都算出來即可。
public static void main(String[] args) {
// 移動設備經緯度
double lon = 116.312528, lat = 39.983733;
GeoHash geoHash = GeoHash.
withCharacterPrecision
(lat, lon, 6);
// 當前
System.out.println(geoHash.toBase32());
// N, NE, E, SE, S, SW, W, NW
System.out.println("---------------------------");
//東南西北,東北、西北、東南、西南等
GeoHash[] adjacent = geoHash.getAdjacent();
for (GeoHash hash : adjacent) {
System.out.println(hash.toBase32());
}
}
D、mysql查詢,查詢出這幾個區域的商家即可,geohash邊界問題就可以解決。
SELECT id, merchant_name
FROM merchant
WHERE geo_code IN (?, ?, ?, ?, ?, ?, ?, ?, ?);
結語
o2o中最常見的應用場景就是附近的商家,希望此篇文章對於o2o的同學有些幫助。覺得有用就點個贊轉發一下吧。
另外我還調試了其他很多開源項目
Java 開源的基於微服務 Spring cloud 快速開發腳手架調試實戰
調試個開源Java 輕量級高性能IM,單機支持幾十萬至百萬在線用戶
前端牛人寫的開源的CMS系統調試實戰,流體布局兼容手機端瀏覽器
覺得有用可以給個關注哦