o2o附近的商家功能常见的算法:geohash和spatial4j调试实战

o2o附近的商家功能常见的算法:geohash和spatial4j调试实战

编码文章call10242024-12-15 11:46:3481A+A-

开篇

我调试了很多开源项目,包括开源商城、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 开源 验证码 神器

Java 搭建的开源的spring boot商城系统实战

前后端分离的开源在线考试系统调试实战

调试个开源Java 轻量级高性能IM,单机支持几十万至百万在线用户

Java 百分之百开源 CMS 系统项目调试实战

前端牛人写的开源的CMS系统调试实战,流体布局兼容手机端浏览器

觉得有用可以给个关注哦

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4