场景一: 查找附近的充电桩
需求:车主在某个位置,想查找附近的我们的充电桩
其他业务类似:查找附近的门店,附近的人
实现方案一:
- 1.在添加充电桩站点信息的时候,有2个字段,分别是站点的经度和维度,在这2个字段数据入库的时候,我同时往mongodb中存储了一份数据,包括:站点id,站点经度,站点维度。
- 2.当车主在某个位置时,小程序的getLocation相关的接口就能获取到车主的位置,得到车主的经纬度数据
- 3.小程序那端把车主的经纬度、查找范围传递到后端接口,利用mondodb提供的api能力,做以下几个步骤
- 以车主位置的经纬度构建一个GeoJsonPoint 对象,形成圆心
- 再根据查找范围,构建Distance,也即圆的半径
- 再利用 Circle对象,结合上面的圆心和半径,画一个圆
- 再使用Query对象的query方法根据经纬度的圆在mongodb中进行查找,就能得到附近的所有充电站点的id了
- 一个站点下有多个充电桩,那么根据站点id,就能找到所有的充电桩了
实现方案二:
Redis中有个数据类型,我们使用Redis中的Geo(Geospatial)数据结构实现的
Redis的Geo功能允许我们存储地理位置信息,并提供了根据地理位置查询数据的各种功能,比如本文中的查询附近的充电桩功能。
1.在添加充电桩站点信息的时候,有2个字段,分别是站点的经度和维度,在这2个字段数据入库的时候,我同时往redis中存储了一份数据,使用geoadd命令,包括:站点id,站点经度,站点维度。
powershellGEOADD stations:locations 121.245996 31.114995 站点id1 GEOADD stations:locations 121.241343 31.113264 站点id2 GEOADD stations:locations 121.356703 31.161321 站点id3 ...2.查找附近的充电桩
使用
GEORADIUS命令来查询指定范围内的用户powershellGEORADIUS stations:locations 121.2439(当前位置精度) 31.114678(当前位置维度) 5000 m WITHDIST WITHCOORD COUNT 10 ASC上面这条命令:查找以(116.405285, 39.904989)为中心,半径为5000米内的最近的10个站点,并返回站点ID、距离、和坐标
参考代码
java
package com.itsoku.lesson066.controller;
import com.itsoku.lesson066.dto.NearbyUserDto;
import com.itsoku.lesson066.dto.AddUserLocationReq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.Metrics;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* <b>description</b>: Java高并发、微服务、性能优化实战案例100讲,视频号:程序员路人,源码 & 文档 & 技术支持,请加个人微信号:itsoku <br>
* <b>time</b>:2024/7/18 20:23 <br>
* <b>author</b>:ready likun_557@163.com
*/
@RestController
public class UserLocationController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 批量将用户地理位置信息添加到redis中(实际工作中,大家可以提供一个用户地理位置上报的接口,客户端可以每隔10秒,上报一下地理位置坐标,将其丢到redis中)
*
* @param userLocationReqList
* @return
*/
@PostMapping("/addUserLocation")
public boolean addUserLocation(@RequestBody List<AddUserLocationReq> userLocationReqList) {
String key = "users:locations";
for (AddUserLocationReq userLocationReq : userLocationReqList) {
String userId = userLocationReq.getUserId();
Double longitude = userLocationReq.getLongitude();
Double latitude = userLocationReq.getLatitude();
this.stringRedisTemplate.opsForGeo().add(key, new Point(longitude, latitude), userId);
}
return true;
}
/**
* 获取附近的人列表,以(longitude,latitude)为圆心,以 radius 为半径,获取count个用户
*
* @param longitude 进度
* @param latitude 纬度
* @param radius 圆的半径(米)
* @param count 获取用户的数量
* @return
*/
@GetMapping("/findNearbyUserList")
public List<NearbyUserDto> findNearbyUserList(@RequestParam("longitude") double longitude,
@RequestParam("latitude") double latitude,
@RequestParam("radius") double radius,
@RequestParam("count") int count) {
List<NearbyUserDto> nearbyUserDtoList = new ArrayList<>();
//从redis中获取附近的用户列表
String key = "users:locations";
Circle circle = new Circle(new Point(longitude, latitude), new Distance(radius, Metrics.METERS));
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeCoordinates()
.includeDistance()
.sortAscending().limit(count);
GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = stringRedisTemplate.opsForGeo().radius(key, circle, args);
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = geoResults.getContent();
for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoResultGeoResult : content) {
RedisGeoCommands.GeoLocation<String> geoLocation = geoResultGeoResult.getContent();
Point point = geoLocation.getPoint();
String userId = geoLocation.getName();
//拿到用于的id、经纬度、距离
NearbyUserDto nearbyUserDto = new NearbyUserDto();
nearbyUserDto.setUserId(userId);
nearbyUserDto.setLongitude(point.getX());
nearbyUserDto.setLatitude(point.getY());
nearbyUserDto.setDistance(geoResultGeoResult.getDistance().getValue());
nearbyUserDtoList.add(nearbyUserDto);
}
return nearbyUserDtoList;
}
}行车路线
调用高德或者腾讯地图接口即可