制作一个web端高德地图组件
日期:2022-09-07
项目中遇到一个需求,门店地址需要获取到经纬度,在移动端应用上可以很方便地拿到,例如使用 uniapp 的 uni.chooseLocation,微信小程序的 wx.choosePoi。
如上图是使用 uniapp 的地点选择页面,有地图展示,可以移动地图改变地点,地图下面的地点列表相应发生变化,支持搜索地点。选中地点后点击右上角的确认按钮,将地点信息传回上个页面。
pc端项目的表单
在门店地址输入框后面简单的添加一个按钮,点击弹出弹窗,在弹窗中实现类似上面移动端那种地图地点选择器。
看一下最终的实现效果:
一步步来实现以上功能。
这里使用高德地图,需要先在高德开放平台open in new window创建一个应用。创建后主要拿到应用key 与安全密钥。
因为是在pc web端使用,web服务需要选择Web端(JS API)
展示地图
参考了官方文档JSAPI的加载open in new window,我这里使用amap-jsapi-loader 来加载 JSAPI。
安装依赖
npm i @amap/amap-jsapi-loader --save
页面中使用,注意容器节点 id,容器需要设置宽高
<div id="gdmap-container" />
import AMapLoader from '@amap/amap-jsapi-loader';
AMapLoader.load({
"key": "", // 申请好的Web端开发者Key,首次调用 load 时必填
"version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
"plugins": [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap)=>{
map = new AMap.Map('gdmap-container');
}).catch(e => {
console.log(e);
})
2
3
4
5
6
7
8
9
10
11
配置与样式正常的情况下就可以看到地图的展示了。
当前定位
现在只能看到地图和挪动地图,我们平时使用地图习惯性就是定位到当前位置,这个怎么实现呢?
在上面加载了 AMapLoader 之后可以拿到 AMap 对象
参考官方文档-定位open in new window,加上定位代码:
AMapLoader.load({
"key": "", // 申请好的Web端开发者Key,首次调用 load 时必填
"version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
"plugins": ["AMap.Geolocation"], // 这里需要加上对应的插件
}).then((AMap)=>{
map = new AMap.Map('gdmap-container');
// 定位
AMap.plugin("AMap.Geolocation", () => {
var geolocation = new AMap.Geolocation({
// 是否使用高精度定位,默认:true
enableHighAccuracy: true,
// 设置定位超时时间,默认:无穷大
timeout: 10000,
// 定位按钮的停靠位置的偏移量
offset: [10, 20],
// 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
zoomToAccuracy: true,
// 定位按钮的排放位置, RB表示右下
position: "RB"
});
geolocation.getCurrentPosition((status, result) => {
if (status === "complete") {
console.log("positon data: ", result);
const { lng, lat } = result.position;
const cpoint = [lng, lat];
map.setCenter(cpoint); // 设置地图中心点
} else {
// 定位出错
console.log("position error: ", result);
}
});
});
}).catch(e => {
console.log(e);
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
现在可以在初始化地图之后,将地图中心定位到你的当前位置了,但是地图上面还缺少一个定位按钮,需要加上地图控件。
// 在图面添加定位控件,用来获取和展示用户主机所在的经纬度位置
map.addControl(new AMap.Geolocation());
2
右下角的定位按钮也有了。
搜索周边
接下来需要有地点信息供用户点选,因为这里要拿到的是当前门店的地点信息,所以这里通过定位到当前位置,搜索周边地点信息。 (参考官方文档-输入提示与POI搜索open in new window)
AMap.plugin("AMap.PlaceSearch", () => {
// 构造地点查询类
const placeSearch = new AMap.PlaceSearch({
map: map, // AMap.Map对象, 展现结果的地图实例。当指定此参数后,搜索结果的标注、线路等均会自动添加到此地图上。可选值
showCover: false, // 在使用map属性时,是否在地图上显示周边搜索的圆或者范围搜索的多边形,默认为true
autoFitView: false, // 用于控制在搜索结束后,是否自动调整地图视野使绘制的Marker点都处于视口的可见范围
panel: "gdmap-panel", // 结果列表的HTML容器id或容器元素,提供此参数后,结果列表将在此容器中进行展示。可选值
type: "汽车服务|汽车销售|汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施"
});
// 这里有一个坑,官方文档是 AMap.event.addListener,而实际是 Event
AMap.Event.addListener(placeSearch, "listElementClick", (e) => {
console.log("获取到地点信息: ", e);
const { name, location } = e.data;
});
// const cpoint = [lng, lat]; // 中心点坐标 lng, lat
/**
* searchNearBy 第一个参数是关键词
* 第二个参数是中心坐标点,这里通过定位后获取经纬度
* 第三个参数是半径 取值范围:0-50000
*/
placeSearch.searchNearBy(this.keyword || "", cpoint, 400, function(status, result) {
console.log("searchNearBy result: ", status);
});
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
同样,在load plugin 里面需要加上插件
AMapLoader.load({
"key": "", // 申请好的Web端开发者Key,首次调用 load 时必填
"version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
"plugins": ["AMap.Geolocation", "AMap.PlaceSearch"], // 这里需要加上对应的插件
})
2
3
4
5
因为需要用到地点展示的面板,所以需要加一个容器元素,id 对应上面 PlaceSearch 的参数 panel。
<div id="gdmap-container" />
<div class="panl-content">
<el-input v-model="keyword" placeholder="请输入地点" @change="keywordChange" />
<div id="gdmap-panel" ref="gdmap-panel" />
</div>
2
3
4
5
这里的输入框用于后面输入关键字搜索。
这里有几个需要注意的点:
- PlaceSearch 的参数需要传 map 用于关联地图上的点,如果正确传入,会导致在点击的时候报错,阻碍点击事件。
- 点击列表地点事件监听,官方文档是 AMap.event.addListener,但是加上后提示找不到 event.addListener,通过打印 AMap 才发现是 Event。
- placeSearch.searchNearBy 一开始加上后的回调返回一直报错:error INVALID_USER_SCODE,后面通过排查,是因为在初始化 AMapLoader 的时候没有带上安全密钥。在 AMapLoader 的文档open in new window上找到配置密钥的方法:但是发现并没有用,所以我使用了直接在index.html 加上密钥的方式解决问题。
AMapLoader.load({ "key": "我的key", // 申请好的Web端开发者Key,首次调用 load 时必填 "security": { "securityJsCode": "我的密钥" }, "version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 "plugins": ["AMap.Geolocation", "AMap.PlaceSearch"] // 需要使用的的插件列表,如比例尺'AMap.Scale'等 })
1
2
3
4
5
6
7
8<script type="text/javascript"> window._AMapSecurityConfig = { securityJsCode:'「您申请的安全密钥」', } </script>
1
2
3
4
5
移动地图搜索地点
绑定地图事件监听
let searchTimer;
// 地图移动结束监听
map.on("moveend", (e) => {
const { lng, lat } = map.getCenter(); // 获取当前地图中心位置
const cpoint = [lng, lat];
if (searchTimer) {
clearTimeout(searchTimer);
}
searchTimer = setTimeout(() => {
console.log("search: ", cpoint);
this.handlePlaceSearch(cpoint);
clearTimeout(searchTimer);
}, 2000);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
这里监听地图移动结束事件,获取地图中心点来搜索周围地点,handlePlaceSearch 方法就是封装了上面搜索周边方法。
这里有个巧妙的地方,地图渲染后执行定位方法,会移动地图,地图移动结束后会触发 moveend 事件,很丝滑地完成了搜索周围地点的操作。
之后关键词搜索,就通过 input 录入框的 change 事件:
keywordChange() {
// ...
const { lng, lat } = this.mapIns.getCenter(); // 获取当前地图中心位置
const cpoint = [lng, lat];
this.handlePlaceSearch(cpoint);
},
2
3
4
5
6
返回定位信息
最后一步就是返回定位信息了,这个在上面搜索周边的代码里面已经可以获取到了:
AMap.Event.addListener(placeSearch, "listElementClick", (e) => {
console.log("获取到地点信息: ", e);
const { name, location } = e.data;
});
2
3
4
所以这里只需要将数据返回外部即可。
其他
什么是 POI ?
POI是Point of Interest的缩写,是指兴趣点。每个POI至少包含以下4项基本信息:名称(Name)、类别(Type)、经度(longitude)、纬度(latitude),所以poiName指的是兴趣点名称,它可以是一栋房子、一个商铺、一个公厕或一个公交站等。
参考:
https://lbs.amap.com/api/jsapi-v2/summary/
https://amapjs.derekli.com/guide/security.html