全网最新最全的js v3.0百度地图离线化改造

1,562 阅读5分钟

前言

因公司业务有地图离线化的需求,支持私有化部署,而百度地图官方不支持离线地图,刚开始以为直接将百度地图js的sdk下载进行本地化就可以了,后来发现这事没那么简单,在网上搜了一堆,发现之前的资料都比较老了,没有新版的,自己在此整理一下,以备后人参考,以及自己的不时之需。

思路

我们需要想办法把在线的代码改成离线的代码,并保证业务正确运行,这个过程可以分为三个技术点:

  1. 百度地图sdk离线化
  2. 百度地图瓦片离线化
  3. 百度地图个性化地图改造

实现

百度地图sdk的离线化改造

这个过程可以分为两步:

  • 将相关的静态资源全部下载下来
  • 改造js文件,使其引用本地的资源
下载保存sdk主文件

我们知道在线的百度地图引入只需要引入一段js脚本

<script
  type="text/javascript"
  src="https://api.map.baidu.com/api?v=3.0&ak=自己的ak"
></script>

image.png
我们发现它只是创建一个新的script标签去请求一个js文件,这个js文件就是真正的百度地图sdk了。

image.png
这是个get请求,我们直接将这个url请求在浏览器中打开,CTRL+A选中所有的代码,并复制下来,在本地新建一个bmap_offline_v3.0_min.js,将代码保存下来。

image.png
方便起见,我在public目录下建个resource/map目录(vue3项目),将bmap_offline_v3.0_min.js文件放进去,再创建个load.js用于地图加载的配置。

image.png
在load.js中写入如下代码,主要就是加载bmap_offline_v3.0_min.js文件

const BMAP_CONFIG = {
  normal_imgext: ".png",
  satellite_imgext: ".jpg",
  tiles_dir: "",      // 地图瓦片存放目录
};

// 上面代码根据自己项目结构修改, 下面保持不动
const scripts = document.getElementsByTagName("script");
const fullPath = scripts[scripts.length - 1].src;
BMAP_CONFIG.home = fullPath.substring(0, fullPath.lastIndexOf("/") + 1); // 取到根目录

(function () {
  const script = document.createElement("script");
  script.src = BMAP_CONFIG.home + "bmap_offline_v3.0_min.js";
  document.body.appendChild(script);
})();
下载保存离线模块文件

百度地图除了核心的sdk,还有很多模块扩展文件,用于扩展百度地图的功能,例如maker标绘、mapclick点击等,百度地图会按需加载这些模块,搜索getmodules可以看到加载的模块,这些模块加载一次后会缓存到localStorage。

image.png 因为随机种子的原因会导致各种请求变量参数改变, 所以所有模块文件都要是和主sdk版本一一对应。打开浏览器本地缓存,可以看到缓存了很多BMap的模块文件,这些文件的命名规范是BMap_{模块名}_{hash}。

image.png
我们复制某一模块的hash后缀,搜索sdk主文件,可以发现skd文件中存储了一份所有模块文件的map。

image.png
我们需要将这些模块文件也全部离线化存储到本地,在resource/map/下面再创建一个modules目录,将本地存储中的文件全部复制到目录下面,命名规则为{模块名}_{hash}.js

image.png
我们将getmodules请求返回的模块代码拷贝到相应模块,不要直接拷贝localStorage当中的代码。

image.png

下载保存离线图片资源

将网络请求中关于百度的图片过滤、下载下来

image.png 在map下面再创建一个images文件夹,将存放离线图片资源

image.png

屏蔽外部网络请求

搜索  &callback=BMap._rd._cbk 以及 查看前面是否有代码 Math.random()。这个函数用于发送请求,在第一行加一行 if (/(baidu.com)|(^http\:\/\/maponline)/.test(a)) return;

image.png

修改静态资源请求的域名

搜索 url.domain.main_domain_nocdn.other,将下面一行注释掉改成 C.pa = BMAP_CONFIG.home; image.png

修改模块加载方式

搜索  &mod= 找到load函数,这个函数会加载各种模块,百度地图为了性能优化,默认将多个模块拼在一起发送一个网络请求,我们改成逐个请求本地。 将0 == a.length ? f.AL() : ra(f.PG.GQ + "&mod=" + a.join(","));这一行修改为

  if (a.length) {
    for (i = 0; i < a.length; i++) {
      mf = BMAP_CONFIG.home + "modules/" + a[i] + ".js";
      ra(mf);
    }
  } else {
    f.AL();
  }

image.png

修改模块加载url

最新版的代码将url的请求参数用&拼接了,导致请求失效 image.png
搜索&seckey,将&v=3.0改为?v=3.0 image.png

地图瓦片离线化改造

同样有两步:

  • 将所需的地图瓦片从第三方下载保存下来
  • 将地图瓦片引入方式从在线改为本地

地图瓦片下载

有很多第三方电子地图瓦片下载器,选择下载自己所需的地图区域、类型、层级就行(很多要付费),存放到本地、文件服务器、cdn上都行,但要和load.js中的tiles_dir对应,普通街道图放在tiles_dir目录下的normal目录下,卫星图放在tiles_dir目录下的satellite目录下

改造地图瓦片引入方式

搜索getTilesUrl方法,并且下方拥有qt=vtilenormal字符的
image.png

image.png

var tdir = (BMAP_CONFIG.tiles_dir.length > 0) ? BMAP_CONFIG.tiles_dir : BMAP_CONFIG.home + "/tiles";
return tdir + "/normal" + "/" + b + "/" + e + "/" + a + BMAP_CONFIG.normal_imgext; // 使用本地的瓦片

搜索getTilesUrl方法,并且下方拥有satellite字符的,将瓦片地址替换成本地

image.png

var tdir = BMAP_CONFIG.tiles_dir.length > 0 ? BMAP_CONFIG.tiles_dir : BMAP_CONFIG.home + "tiles";
return tdir + "/satellite/" + b + "/" + c + "/" + e + BMAP_CONFIG.satellite_imgext; // 使用本地的瓦片

个性化地图离线改造

百度地图支持个性化定制,但也需要在线才能使用,离线需要进行改造。个性化地图不同于街道图和卫星图,没有专门的瓦片,因此改造难度更大。
第一步按照百度官方教程设置个性化地图,官方提供了样式ID和样式JSON两种方式,离线使用JSON方式,将样式JSON从百度地图个性化编辑器中复制到本地保存。 image.png image.png
使用setMapStyleV2设置个性化样式

image.png
如果是在线,现在已经接入个性化地图了,但是做离线现在才刚刚开始。。。。

将mapstyle请求本地化

在网络中搜索mapstyle,将请求返回的结果保存到modules文件夹下,新建一个mapstyle.json文件

image.png

image.png
搜索setMapStyleV2,找到对应的混淆后的函数,找到这个函数

image.png
这个函数中直接找到styleJson,发现是传给了b函数调用,找到sb(b, g, window[e + "cb"])这行,这行是用于发送网络请求,将其改造成请求本地

image.png

image.png

尾言

可参考配置好的离线百度地图sdk