**目标:**将商户信息(包含地理坐标)存入ES中,然后获取当前用户的地理位置,计算出当前用户距离商户的远近,以及用户可根据距离的远近来筛选出符合距离的商户。
**前端:**Vue3.x ,Pinia,ElementPlus
**后端:**Spring Boot3.x , ElasticSearch7.x
**地图相关服务提供:**高德地图JS API 2.0
**遇到bug:**v-show 控制组件的隐藏显示,导致组件中的地图组件不能正常显示,报错:Invalid Object: LngLat(NaN, NaN),改用v-if即可。
用户点击右上角“加入商户”,显示表单填写商户信息,以及显示高德地图组件,根据用户填写的省市区位置设置地图组件中心点,当用户点击地图中指定位置时,获取点击位置的坐标,然后调用【逆地理编码】接口将地理坐标转换为详细地址显示。用户可适当修改详细地址后,提交-新增完毕。
1.创建ES的索引结构,编写后端新增接口。
PUT /hotels
{
"mappings": {
"properties": {
"picture": { "type": "keyword" },
"name": { "type": "text","analyzer": "ik_smart"},
"score": { "type": "float"},
"distance": { "type": "float"},
"desc": { "type": "text" },
"newPrice": { "type": "float" },
"oldPrice": { "type": "float" },
"sale_month": { "type": "integer" },
"location": {
"type": "geo_point"
}
}
}
}
引入ES依赖:注意引入8以下的,8以上版本Java客户端变化太大,API中文文档不全面。
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
- 创建客户端Bean并加入Ioc容器,编写实体类。
- 编写新增接口
2.前端信息收集,地址信息获取。
- 用户输入省市地址,将地图中心点转至对应省市,供用户标记选择详细地址。
- 当用户点击标记地图上地址后,获取标记处的详细地址映射到详细地址输入框中展示。
- 将数据打包发给后端添加到ES中。
使用浏览器内置函数:navigator.geolocation,实现当前用户地理坐标的查询。
用户需要在弹出的授权框授权地理位置允许。
// 当前用户的地理坐标
const position = ref(null);
function getUserLocation(){
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(pos) => {
position.value = {
lat: pos.coords.latitude,
lng: pos.coords.longitude,
};
// 根据用户地理坐标获取周围指定距离的酒店
getNearHotel(position.value);
// console.log("当前用户地理位置:",position.value);
},
(err) => {
error.value = err.message;
}
);
} else {
error.value = 'Geolocation is not supported by this browser.';
}
}
1.编写后端接口,根据用户当前位置和想要查询的半径获取包含在圆形区域的内酒店信息。
@PostMapping("/searchByLocation")
public List<Map<String, Object>> searchHotelsByLocation(
@RequestBody SearchVo searchVo
) throws IOException {
// 构建查询请求
SearchRequest searchRequest = new SearchRequest("hotels");
// 构建查询DSL
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.filter(QueryBuilders.geoDistanceQuery("location")
.point(searchVo.getLat(), searchVo.getLon())
.distance(searchVo.getRadiusKm(), DistanceUnit.KILOMETERS));
sourceBuilder.query(boolQuery);
sourceBuilder.sort(SortBuilders.geoDistanceSort("location", searchVo.getLat(), searchVo.getLon())
.order(SortOrder.ASC) // 实现升序排序,距离越近越靠前
.unit(DistanceUnit.KILOMETERS));
searchRequest.source(sourceBuilder);
// 执行查询
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
List<Map<String, Object>> hotels = new ArrayList<>();
for (SearchHit hit : hits) {
Map<String, Object> hotel = hit.getSourceAsMap();
// 可以获取距离信息(如果需要显示距离)
double distance = (double) hit.getSortValues()[0];
hotel.put("distance_km", distance);
hotels.add(hotel);
}
return hotels;
}
// 使用圆形过滤,过滤出当前用户指定距离的附近酒店
async function getNearHotel(userLocation){
// 定义搜索半径
let radiusKm = 3.0;
// 构造查询参数
let searchArgs = {
lat:userLocation.lat,
lon:userLocation.lng,
radiusKm:radiusKm
}
const res = await axios.post("http://localhost:9009/es/searchByLocation",searchArgs);
console.log("查询到"+ radiusKm + "公里内,存在"+res.data.length+"家酒店/旅社");
// 处理距离小数点,保留逗号后两位
for(let item of res.data){
item.distance_km = item.distance_km.toFixed(2);
}
cardsD.value = res.data
}
至此,使用高德地图API配合ES完成地理位置查询功能点,测试完毕。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。