# mapEsDemo **Repository Path**: maohe101/map-es-demo ## Basic Information - **Project Name**: mapEsDemo - **Description**: ES配合高德地图JS-API实现地理位置查询Demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-29 - **Last Updated**: 2024-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 功能-地理位置搜索(ES实现) - *在SpringBoot环境中使用**ES**实现地理位置查询。* - 高德地图功能接口 **目标:**将商户信息(包含地理坐标)存入ES中,然后获取当前用户的地理位置,计算出当前用户距离商户的远近,以及用户可根据距离的远近来筛选出符合距离的商户。 # 测试-技术栈 **前端:**Vue3.x ,Pinia,ElementPlus **后端:**Spring Boot3.x , ElasticSearch7.x **地图相关服务提供:**高德地图JS API 2.0 # 功能实现 - 商户入驻选址:商户根据地图组件选择地址,获取商户选择的地址信息和地理位置传入后端,后端将信息存储到ES中。 - 获取用户当前坐标:浏览器地理定位API:navigator.geolocation,前提是用户需要赋予位置权限。 - 显示商户距离:使用ES中的地理查询(圆形过滤)geo_distance类型进行查询附近商户,并且在sort中配置商户远近排序。 - 距离筛选:在ES原形过滤时修改distance参数值重新查询商户。 # 具体实现 ## Vue整合高德地图JS API-2.0 1. 安装依赖包:npm i @amap/amap-jsapi-loader ,用于创建地图相关对象。 2. 配置key和密钥 (安全问题:将key和密钥放到环境变量中)。 3. 初始化地图对象,显示地图。 **遇到bug:**v-show 控制组件的隐藏显示,导致组件中的地图组件不能正常显示,报错:**Invalid Object: LngLat(NaN, NaN)**,**改用v-if即可。** ## 加入商户 用户点击右上角“加入商户”,显示表单填写商户信息,以及显示高德地图组件,根据用户填写的省市区位置设置地图组件中心点,当用户点击地图中指定位置时,获取点击位置的坐标,然后调用【逆地理编码】接口将地理坐标转换为详细地址显示。用户可适当修改详细地址后,提交-新增完毕。 **1.创建ES的索引结构,编写后端新增接口。** ```json 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中文文档不全面。 ```xml org.elasticsearch.client elasticsearch-rest-high-level-client 7.12.1 ``` \- 创建客户端Bean并加入Ioc容器,编写实体类。 \- 编写新增接口 **2.前端信息收集,地址信息获取。** \- 用户输入省市地址,将地图中心点转至对应省市,供用户标记选择详细地址。 \- 当用户点击标记地图上地址后,获取标记处的详细地址映射到详细地址输入框中展示。 \- 将数据打包发给后端添加到ES中。 ## 查询用户当前地理坐标 使用浏览器内置函数:navigator.geolocation,实现当前用户地理坐标的查询。 *用户需要在弹出的授权框授权地理位置允许。* ```javascript // 当前用户的地理坐标 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.编写后端接口,根据**用户当前位置**和想要**查询的半径**获取包含在圆形区域的内酒店信息。 ```java @PostMapping("/searchByLocation") public List> 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> hotels = new ArrayList<>(); for (SearchHit hit : hits) { Map hotel = hit.getSourceAsMap(); // 可以获取距离信息(如果需要显示距离) double distance = (double) hit.getSortValues()[0]; hotel.put("distance_km", distance); hotels.add(hotel); } return hotels; } ``` 2. 前端传参,获取商家数据展示。 ```javascript // 使用圆形过滤,过滤出当前用户指定距离的附近酒店 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完成地理位置查询功能点,测试完毕。