前言
因公司业务有地图离线化的需求,支持私有化部署,而百度地图官方不支持离线地图,刚开始以为直接将百度地图js的sdk下载进行本地化就可以了,后来发现这事没那么简单,在网上搜了一堆,发现之前的资料都比较老了,没有新版的,自己在此整理一下,以备后人参考,以及自己的不时之需。
思路
我们需要想办法把在线的代码改成离线的代码,并保证业务正确运行,这个过程可以分为三个技术点:
- 百度地图sdk离线化
- 百度地图瓦片离线化
- 百度地图个性化地图改造
实现
百度地图sdk的离线化改造
这个过程可以分为两步:
- 将相关的静态资源全部下载下来
- 改造js文件,使其引用本地的资源
下载保存sdk主文件
我们知道在线的百度地图引入只需要引入一段js脚本
<script
type="text/javascript"
src="https://api.map.baidu.com/api?v=3.0&ak=自己的ak"
></script>
我们发现它只是创建一个新的script标签去请求一个js文件,这个js文件就是真正的百度地图sdk了。
这是个get请求,我们直接将这个url请求在浏览器中打开,CTRL+A选中所有的代码,并复制下来,在本地新建一个bmap_offline_v3.0_min.js,将代码保存下来。
方便起见,我在public目录下建个resource/map目录(vue3项目),将bmap_offline_v3.0_min.js文件放进去,再创建个load.js用于地图加载的配置。
在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。
因为随机种子的原因会导致各种请求变量参数改变, 所以所有模块文件都要是和主sdk版本一一对应。打开浏览器本地缓存,可以看到缓存了很多BMap的模块文件,这些文件的命名规范是BMap_{模块名}_{hash}。
我们复制某一模块的hash后缀,搜索sdk主文件,可以发现skd文件中存储了一份所有模块文件的map。
我们需要将这些模块文件也全部离线化存储到本地,在resource/map/下面再创建一个modules目录,将本地存储中的文件全部复制到目录下面,命名规则为{模块名}_{hash}.js
我们将getmodules请求返回的模块代码拷贝到相应模块,不要直接拷贝localStorage当中的代码。
下载保存离线图片资源
将网络请求中关于百度的图片过滤、下载下来
在map下面再创建一个images文件夹,将存放离线图片资源
屏蔽外部网络请求
搜索 &callback=BMap._rd._cbk 以及 查看前面是否有代码 Math.random()。这个函数用于发送请求,在第一行加一行 if (/(baidu.com)|(^http\:\/\/maponline)/.test(a)) return;
修改静态资源请求的域名
搜索 url.domain.main_domain_nocdn.other
,将下面一行注释掉改成 C.pa = BMAP_CONFIG.home;
修改模块加载方式
搜索 &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();
}
修改模块加载url
最新版的代码将url的请求参数用&拼接了,导致请求失效
搜索&seckey
,将&v=3.0
改为?v=3.0
地图瓦片离线化改造
同样有两步:
- 将所需的地图瓦片从第三方下载保存下来
- 将地图瓦片引入方式从在线改为本地
地图瓦片下载
有很多第三方电子地图瓦片下载器,选择下载自己所需的地图区域、类型、层级就行(很多要付费),存放到本地、文件服务器、cdn上都行,但要和load.js中的tiles_dir对应,普通街道图放在tiles_dir目录下的normal目录下,卫星图放在tiles_dir目录下的satellite目录下
改造地图瓦片引入方式
搜索getTilesUrl
方法,并且下方拥有qt=vtile
和normal
字符的
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
字符的,将瓦片地址替换成本地
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从百度地图个性化编辑器中复制到本地保存。
使用setMapStyleV2
设置个性化样式
如果是在线,现在已经接入个性化地图了,但是做离线现在才刚刚开始。。。。
将mapstyle请求本地化
在网络中搜索mapstyle,将请求返回的结果保存到modules文件夹下,新建一个mapstyle.json文件
搜索setMapStyleV2
,找到对应的混淆后的函数,找到这个函数
这个函数中直接找到styleJson
,发现是传给了b函数调用,找到sb(b, g, window[e + "cb"])
这行,这行是用于发送网络请求,将其改造成请求本地
尾言
可参考配置好的离线百度地图sdk