开篇
我调试了很多开源项目,包括开源商城、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包:
com.spatial4j spatial4j 0.5
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_nameFROM merchantWHERE (longitude BETWEEN ? AND ?) AND (latitude BETWEEN ? AND ?);
3、根据geohash查询。此时会用到geo_code 字段。
A、引入geohash的jar包
ch.hsr geohash 1.3.0
B、对照geohash的长度对应范围表,我们可以知道,查询附近1km的数据,只要长度是5就可以了。具体计算,GeohashUtils.encodeLatLon(lat, lon, 5),代入底线sql即可。
SELECT id, merchant_nameFROM merchantWHERE 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_nameFROM merchantWHERE geo_code IN (?, ?, ?, ?, ?, ?, ?, ?, ?);
结语
o2o中最常见的应用场景就是附近的商家,希望此篇文章对于o2o的同学有些帮助。觉得有用就点个赞转发一下吧。
另外我还调试了其他很多开源项目
Java 开源的基于微服务 Spring cloud 快速开发脚手架调试实战
超好用的 Java 开源 验证码 神器
Java 搭建的开源的spring boot商城系统实战
前后端分离的开源在线考试系统调试实战
调试个开源Java 轻量级高性能IM,单机支持几十万至百万在线用户
Java 百分之百开源 CMS 系统项目调试实战
前端牛人写的开源的CMS系统调试实战,流体布局兼容手机端浏览器
觉得有用可以给个关注哦