vue集合离线百度地图

javascript/jquery

浏览数:286

2020-6-16

一、需求场景

所谓离线,大概都是项目在内网中使用,无法连接外网,所以需要开发离线地图功能。
在看以下步骤前,先提示这个vue项目是vue-cli生成的

二、开发步骤

1、通过API下载百度接口JS

访问此链接

打开会看到以下内容

在代码中找到
src=http://api.map.baidu.com/getscript?v=3.0&ak=biuHZmoAow03mjwThwt8f2whaf4mVdHf&services=&t=20191018171908
打开src里的链接,就可以获得百度api的js代码

2、js代码清洗,保存

在线JSON校验工具,将上面的代码,格式化,以便下面查看与修改。

然后新建js文件,将格式化后的代码粘贴进去,命名为baidu-api.js

再然后将此js文件放在vue项目中,我的放在了static下

最后,在vue项目的启动入口index.html的head中引入这个js

3、修改API文件

3.1、在baidu-api.js文件中,用Math.random()全局查找几次,定位到以下代码位置:

(以下代码会在百度更新有些出入,但基本样式不变,找相似)

function pa(a, b) {
   if (b) {
      var c = (1E5 * Math.random()).toFixed(0);
      D._rd["_cbk" + c] = function(a) {
         b && b(a);
         delete D._rd["_cbk" + c]
      };
      a += "&callback=BMap._rd._cbk" + c
   }
   var e = O("script", {
      type: "text/javascript"
   });
   e.charset = "utf-8";
   e.src = a;
   e.addEventListener ? e.addEventListener("load", function(a) {
      a = a.target;
      a.parentNode.removeChild(a)
   }, t) : e.attachEvent && e.attachEvent("onreadystatechange", function() {
      var a = window.event.srcElement;
      a && ("loaded" == a.readyState || "complete" == a.readyState) && a.parentNode.removeChild(a)
   });
   setTimeout(function() {
      document.getElementsByTagName("head")[0].appendChild(e);
      e = s
   }, 1)
};

到此步,默认你已找到!!!
然后修改上面的代码,对HTTP拦截,不进行外部访问,只需在最开始添加一行:

if (/^http/.test(a)) return; 

就是如下样式

function pa(a, b) {
   //////
   if (/^http/.test(a)) return; // !!!!!这里加判断,如果是调用外部资源就退出去
   //////

   if (b) {
      var c = (1E5 * Math.random()).toFixed(0);
      D._rd["_cbk" + c] = function(a) {
         b && b(a);
         delete D._rd["_cbk" + c]
      };
      a += "&callback=BMap._rd._cbk" + c
   }
   var e = O("script", {
      type: "text/javascript"
   });
   e.charset = "utf-8";
   e.src = a;
   e.addEventListener ? e.addEventListener("load", function(a) {
      a = a.target;
      a.parentNode.removeChild(a)
   }, t) : e.attachEvent && e.attachEvent("onreadystatechange", function() {
      var a = window.event.srcElement;
      a && ("loaded" == a.readyState || "complete" == a.readyState) && a.parentNode.removeChild(a)
   });
   setTimeout(function() {
      document.getElementsByTagName("head")[0].appendChild(e);
      e = s
   }, 1)
};

3.2、设置引用本地资源路径

在baidu-api.js文件中,用url.domain.main_domain_cdn.baidu[0]全局多查找几次,定位到下面的代码:

D.Nt = window.HOST_TYPE || "0";
D.url = D.I_[D.Nt];
D.Uo = D.url.proto + D.url.domain.baidumap + "/";
D.Cd = D.url.proto + ("2" == D.Nt ? D.url.domain.main_domain_nocdn.other : D.url.domain.main_domain_nocdn.baidu) + "/";
D.oa = D.url.proto + ("2" == D.Nt ? D.url.domain.main_domain_cdn.other[0] : D.url.domain.main_domain_cdn.baidu[0]) + "/";
D.Ri = D.url.proto + D.url.domain.main_domain_cdn.webmap[0] + "/";

然后将
D.Ri = D.url.proto + D.url.domain.main_domain_cdn.webmap[0] + “/”
改为 D.Ri = ” :

如下样式:

D.Nt = window.HOST_TYPE || "0";
D.url = D.I_[D.Nt];
D.Uo = D.url.proto + D.url.domain.baidumap + "/";
D.Cd = D.url.proto + ("2" == D.Nt ? D.url.domain.main_domain_nocdn.other : D.url.domain.main_domain_nocdn.baidu) + "/";
D.oa = D.url.proto + ("2" == D.Nt ? D.url.domain.main_domain_cdn.other[0] : D.url.domain.main_domain_cdn.baidu[0]) + "/";
///////
D.Ri = '';
///////
D.Yh = function(a, b) {
   var c, e, b = b || "";

3.3、下载本地资源

所谓本地资源,就是在使用地图时需要用到的一些模块(module),比如图层类,标记类,控件类。
当你在地图中用到这些模块时,它会自动加载,因此我们需要先把这些模块的js文件下载下来,保存到本地。
这些模块有几十个之多,我们这里并不都存本地,而是用到哪个下载哪个,那么如何知道用到了哪个模块呢?

首先,在 baidu-api.js 文件中,用 &mod= 定位到下面的代码,然后加一行代码将用到的模块打印出来:

load: function(a, b, c) {
   var e = this.lb(a);
   if (e.Ke == this.tj.Bp) c && b();
   else {
      if (e.Ke == this.tj.BF) {
         this.nJ(a);
         this.EM(a);
         var f = this;
         f.OB == t && (f.OB = p, setTimeout(function() {
            for (var a = [], b = 0, c = f.Pd.Vm.length; b < c; b++) {
               var e = f.Pd.Vm[b],
                  n = "";
               ja.Zx.iJ(e) ? n = ja.Zx.get(e) : (n = "", a.push(e + "_" + Tb[e]));
               f.Pd.qv.push({
                  VL: e,
                  UD: n
               })
            }
            f.OB = t;
            f.Pd.Vm.length = 0;

            //!!!!!!!!!!!!!!!!!!!!
            console.log("打印所需模块");
            console.log(a);   //!!!!!打印所需模块,这很重要
            //!!!!!!!!!!!!!!!!!!!!

            //0 == a.length ? f.XJ() : pa(f.tF.$O + "&mod=" + a.join(","))

            //////引用本地下载好的模块文件资源
            if( a.length > 0 ){
               for(let i=0; i<a.length;i++){
                  let mf = './static/modules/'+a[i]+'.js';
                  pa( mf );
                  console.log('加载模块文件:'+mf); //IE error
               }
            } else {
               f.XK()
            }
            //////
         }, 1));
         e.Ke = this.tj.RO
      }

然后,我们在需要使用地图的vue页面里,按照官方文档正常使用:

<template>
    <div id='MapBox'>
        <div  class='baiduMap' id='mapShow' ref="mapShow"></div>
    </div>
</template>
<script>
    export default {
        name:'baiduMap',
        data() {
            return{
                map: undefined,
                overView: undefined,
                marker: undefined,
                BMap: undefined,
            }
        },
        methods:{
            baiduMap(){
                debugger
                this.BMap = BMap;
                // 创建地图实例
                this.map = new BMap.Map("mapShow");
                // 创建点坐标
                let point = new BMap.Point(118.8035, 32.0658);
                //创建标注
                this.marker=new BMap.Marker(point);
                //缩略地图控件。
                this.overView=new BMap.OverviewMapControl({isOpen: true});
                //添加控件
                this.map.addControl(this.overView);
                //添加一个标注
                this.map.addOverlay(this.marker);
                // 初始化地图,设置中心点坐标和地图级别
                this.map.centerAndZoom(point, 12);
                //this.map.setCurrentCity("南京");
                //开启鼠标滚轮缩放
                this.map.enableScrollWheelZoom(true);
            }
        },
        mounted(){
            this.$nextTick(() => {
                this.baiduMap();
            })
        }
    }
</script>
<style>
    #MapBox {
        width: 100%;
        height: 100%;
        padding: 10px;
        position: relative;
    }
    /* 地图 */
    .baiduMap{
        height: 100%;
        width: 100%;
    }
    /* 去除地图上,左下字体标注 */
    .anchorBL{
        display:none;
    }
</style>

特别注意:如果你要在进入页面就初始化地图,最好像上面那样,放在 mounted 生命函数的 this.$nextTick(() => {})里,以确保地图容器 #mapShow 元素渲染完成,不然有可能因为初始化时地图容器还未渲染而报错:

从上继续:
刷新这个vue页,关注控制台,就能看到要实现这些地图功能所需要的模块名,是个数组集合,如下图,我这里需要以下十一个模块:

下载api依赖模块的地址

http://api.map.baidu.com/getm…

通过上面的地址,只需要更换mod后面的值就可以找到需要的模块
然后在static下建一个modules文件夹来存放即将下载的模块文件

然后新建以打印出的模块为命名的js,将通过地址搜索到的js复制到此js文件里。这样模块文件就下载好了。

3.4、引用本地资源

在上面打印模块名的地方,做如下修改:

load: function(a, b, c) {
   var e = this.lb(a);
   if (e.Ke == this.tj.Bp) c && b();
   else {
      if (e.Ke == this.tj.BF) {
         this.nJ(a);
         this.EM(a);
         var f = this;
         f.OB == t && (f.OB = p, setTimeout(function() {
            for (var a = [], b = 0, c = f.Pd.Vm.length; b < c; b++) {
               var e = f.Pd.Vm[b],
                  n = "";
               ja.Zx.iJ(e) ? n = ja.Zx.get(e) : (n = "", a.push(e + "_" + Tb[e]));
               f.Pd.qv.push({
                  VL: e,
                  UD: n
               })
            }
            f.OB = t;
            f.Pd.Vm.length = 0;

            //!!!!!!!!!!!!!!!!!!!!
            console.log("打印所需模块");
            console.log(a);   //!!!!!打印所需模块,这很重要
            //!!!!!!!!!!!!!!!!!!!!

            //0 == a.length ? f.XJ() : pa(f.tF.$O + "&mod=" + a.join(","))

            //////引用本地下载好的模块文件资源
            if( a.length > 0 ){
               for(let i=0; i<a.length;i++){
                  let mf = './static/modules/'+a[i]+'.js';
                  pa( mf );
                  console.log('加载模块文件:'+mf); //IE error
               }
            } else {
               f.XK()
            }
            //////
         }, 1));

现在就能成功加载模块资源了。
这里要注意路径问题,如果路径不对,找不到模块文件,会报错:

三、加载瓦片改为本地离线瓦片

离线瓦片可以理解为地图离线包,没有它,离线地图无法显示的。

1、瓦片嵌入在项目中引用

1.1、下载瓦片

网上提供了水经注或者太乐地图下载器下载瓦片,介绍我用的太乐
太乐地图下载器

选择百度

下载 -> 选择行政区划,选完了之后该区域就会出现ufo图标

点击图标,选择 地图:

选择直接保存瓦片后,存储标准选择百度瓦片,存储格式会自动选择 .png。然后选择级别之后确定即可:

级别就是调用百度地图api处设置的缩放级别:

下载完成后可在这里查看:

找到存储目录:我这里下载了重庆12级的瓦片,所有的瓦片就存储在了12这个文件夹下

将所有瓦片文件夹整个复制到项目static/tiles目录下

你会发现除了下载了12级,还下载了9、10、11级,因为离线的关系,在地图上进行缩放操作到11级,但是如果没有11级的瓦片,地图就什么都不显示。所以如果想要缩放多少级,这些级别的瓦片必须都下载到本地。

1.2、瓦片配置文件

static目录下新建mp_load.js文件,定义瓦片路径及瓦片格式即地图api的主目录:

我们的瓦片是png格式的:

var bmapcfg = {
    'imgext'      : '.png',   //瓦片图的后缀  根据需要修改,一般是 .png .jpg
    'tiles_dir'   : '',       //普通瓦片图的地址,为空默认在tiles/ 目录
};
var scripts = document.getElementsByTagName("script");
var JS__FILE__ = scripts[scripts.length - 1].getAttribute("src");  //获得当前js文件路径
bmapcfg.home = JS__FILE__.substr(0, JS__FILE__.lastIndexOf("/")+1); //地图API主目录

然后在API文件之前引入该配置文件:

<script type="text/javascript" src="static/mp\_load.js"\></script\>

1.3、修改API文件加载瓦片路径

在 baidu-api.js 文件中,可以用 getTilesUrl 多找几次,定位到下面代码:

Hd.getTilesUrl = function(a, b, c) {
   var e = a.x,
      a = a.y,
      f = Sb("normal"),
      g = 1,
      c = Gd[c];
       this.map.Yw() && (g = 2);
      e = this.map.oh.Uv(e, b).Ol;
      return (Fd[Math.abs(e + a) % Fd.length] + "?qt=vtile&x=" + (e + "").replace(/-/gi, "M") + "&y=" + (a + "").replace(/-/gi, "M") + "&z=" + b + "&styles=" + c + "&scaler=" + g + (6 == z.ga.ma ? "&color_dep=32&colors=50" : "") + "&udt=" + f).replace(/-(\d+)/gi, "M$1")
};

修改如下:

Hd.getTilesUrl = function(a, b, c) {
   var e = a.x,
      a = a.y,
      f = Sb("normal"),
      g = 1,
      c = Gd[c];
   // this.map.Yw() && (g = 2);
   // e = this.map.oh.Uv(e, b).Ol;
   // return (Fd[Math.abs(e + a) % Fd.length] + "?qt=vtile&x=" + (e + "").replace(/-/gi, "M") + "&y=" + (a + "").replace(/-/gi, "M") + "&z=" + b + "&styles=" + c + "&scaler=" + g + (6 == z.ga.ma ? "&color_dep=32&colors=50" : "") + "&udt=" + f).replace(/-(\d+)/gi, "M$1")
   //////加载本地瓦片
   let tdir = bmapcfg.tiles_dir.length>0?bmapcfg.tiles_dir:bmapcfg.home + "tiles";
   console.log(tdir + "/" + b + "/" + e + "/" + a + bmapcfg.imgext);
   return tdir + "/" + b + "/" + e + "/" + a + bmapcfg.imgext; // 使用本地的瓦片
   //////
};

在这里可将调用瓦片的地址打印出来看看是否正确:


如果瓦片存在,且路径正确,就能正常显示地图了。
ps:地图不能显示出来,是瓦片相关有问题
地图的功能不能实现,是模块相关有问题
这里在 mp_load.js 里已经取到了主路径,可以将之前加载模块处代码修改成:

1.4、关于地图下载器

太乐

太乐地图下载器在没有付费的情况下,最高只能下载12级,而且文件数量有限制,也就是说如果你选择的区域很大,可能连12级都无法下载,比如这里,我要下载中国地图,只能下载到第8级:

而且下载下来的瓦片会有很大很夸张的水印:

但是,如果你买了vip,那就没有任何限制了,具体收费情况我没问
水经注
我没用水经注来说明,是因为水经注下载器,如果没有付费,下载不了瓦片。水经注的购买费用大概800元,买了后没有限制,一直可用。

瓦片文件大小
这是中国地图瓦片,可以看到第11级就1G了,16级1T,17级4T。如果你的地图需要带缩放功能,就吧这些大小累加,几T吧~~

来感受下11级的缩放程度,根本不够用

15级的凑合:

17级:

——————————————–特别注意——————————————
作为一个技术猿,怎会花钱去买,所以经过不懈努力,找到了可以下载完整瓦片的地图下载器,作为题主,自然要拿出来分享,通过以下内容,到百度云下载即可:
https://pan.baidu.com/s/1Mxvf…
提取码:
ojji

如果可以帮助到你,麻烦小手点个赞,走一波爱心支持,小生在此谢过

2、通过Nginx反向代理在项目中引用瓦片

转载链接

作者:king