制作一个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
1

页面中使用,注意容器节点 id,容器需要设置宽高

<div id="gdmap-container" />
1
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);
})
1
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);
})

1
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());
1
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);
  });
});
1
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"], // 这里需要加上对应的插件
})
1
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>
1
2
3
4
5

这里的输入框用于后面输入关键字搜索。

这里有几个需要注意的点:

  1. PlaceSearch 的参数需要传 map 用于关联地图上的点,如果正确传入,会导致在点击的时候报错,阻碍点击事件。
  2. 点击列表地点事件监听,官方文档是 AMap.event.addListener,但是加上后提示找不到 event.addListener,通过打印 AMap 才发现是 Event。
  3. placeSearch.searchNearBy 一开始加上后的回调返回一直报错:error INVALID_USER_SCODE,后面通过排查,是因为在初始化 AMapLoader 的时候没有带上安全密钥。在 AMapLoader 的文档open in new window上找到配置密钥的方法:
    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
    但是发现并没有用,所以我使用了直接在index.html 加上密钥的方式解决问题。
    <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);
});
1
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);
},
1
2
3
4
5
6

返回定位信息

最后一步就是返回定位信息了,这个在上面搜索周边的代码里面已经可以获取到了:

AMap.Event.addListener(placeSearch, "listElementClick", (e) => {
  console.log("获取到地点信息: ", e);
  const { name, location } = e.data;
});
1
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