SpringBoot⼊门教程(五)Java基于MySQL实现附近的⼈
“附近的⼈”这个功能估计都不陌⽣,与之类似的功能最开始是在各⼤地图应⽤上接触过,⽐如搜附近的电影院,附近的超市等等。然⽽真正让附近的⼈⽕遍⼤江南北的应该是"附近的⼈"这个功能,记得刚出的时候,坊间还有⼀句"寂寞⼥聊玩,寂寞男⼈搜附
近"的说法。
v准备⼯作
创建测试数据库
CREATE TABLE `userposition` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`city` varchar(20) NOT NULL,
`position` varchar(128) NOT NULL,
`longitude` decimal(18,15) NOT NULL,
`latitude` decimal(18,15) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1DEFAULT CHARSET=utf8;
insert into `userposition` values(1,'北京市','回龙观新村中区', 116.310771,40.06263);
insert into `userposition` values(2,'北京市','⾦域华府', 116.310127,40.064379);
insert into `userposition` values(3,'北京市','融泽嘉园中区', 116.311962,40.064822);
insert into `userposition` values(4,'北京市','回龙观新村东区', 116.312541,40.063246);
insert into `userposition` values(5,'北京市','上地东⾥', 116.314168,40.033075);
测试数据中的经度和纬度可以⽤或者提取。
v附近的⼈
原理
先算出某个坐标位置周围的矩形的四个点,然后使⽤经纬度去直接匹配数据库中的记录。
思路
⾸先算出“给定坐标附近1000⽶”这个范围的坐标范围。虽然它是个圆,但我们可以先求出该圆的外接正⽅形,然后拿正⽅形的经纬度范围去搜索数据库。圆形内为要求的搜索范围,⽅形内为我们能间接得到的结果范围。
先来求东西两侧的的范围边界。在haversin公式中令φ1 = φ2,可得
Java实现
/**
* 查附近的⼈
* @param radii 半径距离(单位km)
* @param lon 经度
* @param lat 纬度
* @return
*/
@GetMapping("/nearby")
public List<UserPosition> getVicinity(double radii, double lon, double lat){
double r = 6371;//地球半径千⽶
double dis = radii;
double dlng =  2*Math.asin(Math.sin(dis/(2*r))/s(lat*Math.PI/180));
dlng = dlng*180/Math.PI;//⾓度转为弧度
double dlat = dis/r;
dlat = dlat*180/Math.PI;
double minlat =lat-dlat;
double maxlat = lat+dlat;
double minlng = lon -dlng;
double maxlng = lon + dlng;
Vicinity(BigDecimal.valueOf(minlng), BigDecimal.valueOf(maxlng), BigDecimal.valueOf(minlat), BigDecimal.valueOf(maxlat));    }
mybatis
<select id="getvicinity" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from userposition
where longitude >= #{minlng} and longitude <= #{maxlng} and latitude >= #{minlat} and latitude <= #{maxlat}
</select>
List<UserPosition> getvicinity(@Param("minlng") BigDecimal minlng,
@Param("maxlng") BigDecimal maxlng,
@Param("minlat") BigDecimal minlat,
@Param("maxlat") BigDecimal maxlat);
测试效果
在地图中到回龙新村的经纬度,然后测试。
v按距离远近排序
Java代码
/**
* 附近的⼈排序
* @param lon 经度
* @param lat 纬度
* @return
*/
@GetMapping("/nearbysort")
public List<UserPosition> getVicinitySort(double lon, double lat){
vicinitysort(BigDecimal.valueOf(lon), BigDecimal.valueOf(lat));
}
mybatis代码
<select id="getvicinitysort" resultMap="BaseResultMap">
SELECT id, city, position, longitude,latitude,
(POWER(MOD(ABS(longitude - #{longitude}),360),2) + POWER(ABS(latitude - #{latitude}),2)) AS distance
FROM `userposition`
ORDER BY distance LIMIT 20
</select>
List<UserPosition> getvicinitysort(@Param("longitude") BigDecimal longitude,
@Param("latitude") BigDecimal latitude);
测试效果
补充,如果需要按距离排序,并返回距离的字段。可以按如下⽅式实现。
SELECT
*,
ROUND(
6378.138*2*ASIN(
SQRT(
POW(
SIN(
(
$latitude *PI() /180- latitude *PI() /180
)
/2
),
2
) +COS($latitude *PI() /180) *COS(latitude *PI() /180) * POW( SIN(
(
$longitude *PI() /180- longitude *PI() /180
) /2
),
2
)
bigdecimal取值范围)
)
*1000
) AS distance
FROM
userposition
ORDER BY
distance ASC
v博客总结
如果数据量⼤的话,还可以考虑基于Redis实现附近的⼈。
v源码地址
其他参考资料:
1. 根据⼀个给定经纬度的点,进⾏附近500⽶地点查询–合理利⽤算法__算法
2. mysql 下计算两点经纬度之间的距离
3. MYSQL创建⼀个function⽤来计算经纬度距离