【踩坑】百度地图js API与mapvgl的一些梳理

4,547 阅读5分钟

本文首发于此。

1. 背景

需求背景省略n (n >= 20)字。写作背景可概括为如下几条:

  1. 百度地图和mapvgl虽然都有官方文档[1][2],但实际过程中仍然有官方文档中没有详尽说明之处;
  2. 在使用React, Vue等框架等开发地图类需求时没有官方的方案与组件,并且在与框架结合过程中仍有很多的问题,需要开发者进行实际探索。

所以这篇文章就是梳理上述踩坑之处的。

2. 技术选型

如果使用原生js或jQuery开发地图类需求,本小节可忽略。如使用Vue / React开发的话,本小节非常适合阅读。

无论何时,除了开发原生,mapvgl都只推荐npm install mapvgl

2.1 React

百度地图+框架的官方唯一解决方案是React-BMapGL[3],但它存在如下问题:

  1. 只支持Class Component, 且用React Hooks写必报错,官方也明确解释过对React 18支持不良的问题,但好像没打算修复。
  2. 自定义控件支持不友好。
  3. 从官方文档来看,未找到捕获百度地图原生BMap实例的方法。

如果你的项目中用的React版本<= 16, 那么可以一试官方组件。如果是React Hooks,那么只推荐使用React Baidu Map[4]。具体使用可以看官方文档,这个官方文档还挺详尽的。如需要捕获BMapBMapGL实例并引入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,遇到类型不易推断的地方直接用unknownany,不过一定要把注释写的很详尽,并且推荐把官方文档粘到注释上。

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. 百度地图需要注意的

ReactVue版本的百度地图组件封装都是只封装了地图的基础能力,包括基础控件、自定义覆盖物、图层组件等,不过从文档上来看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类两个实例方法

坑点一: 虽然根据官方的描述, getPointgetLocation好像是异步函数(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需要注意的点

  1. 所有图层属性的colorsize均可以不局限于numberstring类型,也可以是一个回调函数,传值为每一项datadata中包含geometryproperties两个属性,通过Intensity实例的getColorgetSize计算返回值。具体可参考实例: 不同颜色大小点图
  2. 点聚合图层是以半径聚合的,而不是你自定义的聚合依据。

Tips

(1) 这一点并未出现在官方说明之中,而是实践经验

参考

  1. jspopularGL | 百度地图API SDK (baidu.com)
  2. MapVGL | 快速入门 (baidu.com)
  3. React-BMapGL文档 (baidu.com)
  4. React Baidu Map - 百度地图 React 组件 (gitee.io)
  5. Vue3 BaiduMap GL
  6. Vue Baidu Map
  7. 百度地图JSAPI WebGL v1.0类参考 (bcebos.com)