本文首发于此。
1. 背景
需求背景省略n (n >= 20)字。写作背景可概括为如下几条:
- 百度地图和mapvgl虽然都有官方文档[1][2],但实际过程中仍然有官方文档中没有详尽说明之处;
- 在使用React, Vue等框架等开发地图类需求时没有官方的方案与组件,并且在与框架结合过程中仍有很多的问题,需要开发者进行实际探索。
所以这篇文章就是梳理上述踩坑之处的。
2. 技术选型
如果使用原生js或jQuery开发地图类需求,本小节可忽略。如使用Vue / React开发的话,本小节非常适合阅读。
无论何时,除了开发原生,mapvgl都只推荐npm install mapvgl。
2.1 React
百度地图+框架的官方唯一解决方案是React-BMapGL[3],但它存在如下问题:
- 只支持
Class Component, 且用React Hooks写必报错,官方也明确解释过对React 18支持不良的问题,但好像没打算修复。 - 自定义控件支持不友好。
- 从官方文档来看,未找到捕获百度地图原生
BMap实例的方法。
如果你的项目中用的React版本<= 16, 那么可以一试官方组件。如果是React Hooks,那么只推荐使用React Baidu Map[4]。具体使用可以看官方文档,这个官方文档还挺详尽的。如需要捕获BMap或BMapGL实例并引入mapvgl,可采用如下代码:
// 只写关键代码
import React, { useState, useEffect, useRef } from 'react';
import { APILoader, Map } from '@uiw/react-baidu-map';
// 记得开个.d.ts文件加一行 declare module 'mapvgl',然后全程用unknown或any。
// 因为没有官方的ts类型定义
import * as mapvgl from 'mapvgl';
export function BoxedMap() {
// 这里填写ak
const ak = 'Your ak';
// 由于BMap/BMapGL类的TS类型定义不全,所以这里使用unknown类型或any类型
const [mapInst, setMapInst] = useState<unknown>(null);
// bmapgl的view层
const bmapglView = useRef<unknown>(null);
useEffect(() => {
// 初始bmapgl View层的操作
if (mapInst) {
bmapglView.current = new mapvgl.View({
map: mapInst
});
}
}, [mapInst]);
return (
<>
<APILoader akay={sl}>
{/** 这里的ref是关键 */}
<Map
ref={props => {
const { map: bMap } = props;
setMapInst(bMap);
}}
></Map>
</APILoader>
</>
);
}
React Baidu Map的一处须知: disableScripts要永远设为false, 并且在<APILoader />组件中传入ak引入,而坚决不要用去index.html里粘贴cdn的方式引入,不然会线上报错.
2.2 Vue
如果是vue 3用户,直接用Vue3 BaiduMap GL[5]就可以。这个文档比React版更详尽,待我真的踩过坑后再来补充。
如果是vue 2用户,用Vue Baidu Map[5]。具体也等我踩过坑之后补充吧。
2.3 其他框架
去index.html按原生方式用cdn引入,然后自己封装一个组件再使用。如果开了ts,遇到类型不易推断的地方直接用unknown或any,不过一定要把注释写的很详尽,并且推荐把官方文档粘到注释上。
2.4 使用mapvgl要注意的地方
一定要等到地图实例可以被获取到之后再加载mapvgl图层。下面仍然以React代码为例:
// 摘取自上面的代码片断
// 由于BMap/BMapGL类的TS类型定义不全,所以这里使用unknown类型或any类型
const [mapInst, setMapInst] = useState<unknown>(null);
// bmapgl的view层
const bmapglView = useRef<unknown>(null);
useEffect(() => {
// 初始bmapgl View层的操作
if (mapInst) {
bmapglView.current = new mapvgl.View({
map: mapInst
});
}
}, [mapInst]);
3. 百度地图需要注意的
React和Vue版本的百度地图组件封装都是只封装了地图的基础能力,包括基础控件、自定义覆盖物、图层组件等,不过从文档上来看Vue 3版本的百度地图封装更完善一些。但无论如何,想让地图具有更完善的能力,操作原生一定是绕不开的一关。而原生地图类却几乎每个接口都有坑。这里整理一下我在实践中遇到的一些坑。后续开发中遇到踩坑之处会随时补充进去。
3.1 经纬度大小
坑点: 按我所学的知识来讲,一般情况中国地区的都是经度 > 纬度,但对于Point类来说,lng (longitude, 经度)和lat (latitude, 纬度)很可能是反直觉的[7]。按照官方文档来说,new Point需要的参数是(lng, lat),即先经后纬,但有些方法的运算结果会返回先纬后经的Point类。
影响面: 可能会导致mapvgl在渲染时将本来在中国的点位渲染到非洲去。
如何解坑?
1.在使用mapvgl时,mapvgl图层所需geometry.coordinates字段需要的是[lng, lat]数组,即先大的后小的。每次获取到一个点时,先在控制台打一下,确认该点返回的lng, lat属性的大小顺序。
3.2 Geocoder类两个实例方法
坑点一: 虽然根据官方的描述, getPoint与getLocation好像是异步函数(1),执行起来也是异步函数,但它们均不是Promise实现的异步逻辑,而是最为传统的callback式异步,所以没法把它们当Promise处理。因此,注意它们的执行时机,不然会绕坑里。
坑点二: 在getPoint()的回调中获取的点是先纬后经的点,如果要在mapvgl中使用需要相应地转换。
示例:
// 创建地理解析器实例
const geoc = new BMapGL.GeoCoder();
['河北省石家庄市', '山东省济南市', '山东省青岛市'].forEach(location => {
geoc.getPoint(location, point => {
console.log(point);
});
});
console.log('all points');
/**
* 上面一段代码实际执行中,
* 最先被打印的不是每个地址获取后的点坐标,
* 而是all points。
* 每一个point是先纬后经的点
* 需要用BMapGL.Point(point.lat, point.lng)转换一下才是正常点
*/
坑点三: getPoint方法至少要细化到市级,如山东省济南市。如只传省级单位如山东省,则只会返回北京市的点坐标。当然,如果传直辖市,返回的是准确的点位。
4. mapvgl需要注意的点
- 所有图层属性的
color与size均可以不局限于number或string类型,也可以是一个回调函数,传值为每一项data,data中包含geometry与properties两个属性,通过Intensity实例的getColor和getSize计算返回值。具体可参考实例: 不同颜色大小点图 - 点聚合图层是以半径聚合的,而不是你自定义的聚合依据。
Tips
(1) 这一点并未出现在官方说明之中,而是实践经验