项目在内网上使用,不得连接外网,所以需要开发离线地图功能
这个项目是vue+vue-cli的,需要对vue和这种项目结构有些了解
这是自己写的vue项目构建的文章,很详细,不了解的可以看看:
https://blog.csdn.net/PGguoqi/article/details/88977403
我这里算是对这次开发做个总结,主要参考了下面两篇文章,但是因为或多或少都不是很详细,中间卡了好久,最后捣鼓出来了,所以写个文章记录分享下,照着一步一步走,绝对能开发出来。
https://blog.csdn.net/wml00000/article/details/82219015
https://blog.csdn.net/a312024054/article/details/70213444
如有谬误,还望指正。希望对大家有所帮助
访问这个地址 http://api.map.baidu.com/api?v=3.0 ,打开之后是一段代码:
在代码中找到 src="http://api.map.baidu.com/getscript?v=3.0&ak=&services=&t=20180823175819"
,打开这个链接,就可以看到压缩后的js代码,这个是我们要用到的代码:
在站长工具 http://tool.chinaz.com/tools/jsformat.aspx 将上面的压缩代码格式化备用,以便下面查看与修改。
在static/js
目录下新建个js文件,我这里命名为 bmap_offline_api_min.js
,将上面格式化后的js代码复制到这个文件中:
最后,需要在入口index.html
的head中引入这个js:
在 bmap_offline_api_min.js
文件中,用 Math.random()
多找几次,定位到下列代码位置:
(以下代码方法名称和一些变量名称可能会有出入,我下载的代码就和网上一些贴子上看到的代码不尽相同)
function oa(a, b) {
if (b) {
var c = (1E5 * Math.random()).toFixed(0);
z._rd["_cbk" + c] = function(a) {
b && b(a);
delete z._rd["_cbk" + c]
};
a += "&callback=BMap._rd._cbk" + c
}
var d = K("script", {
type: "text/javascript"
});
d.charset = "utf-8";
d.src = a;
d.addEventListener ? d.addEventListener("load",
function(a) {
a = a.target;
a.parentNode.removeChild(a)
},
q) : d.attachEvent && d.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(d);
d = p
},
1)
};
然后修改上面的代码,对http拦截,不进行外部访问,只需在最开始加一行代码if (/^http/.test(a)) return;
function oa(a, b) {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (/^http/.test(a)) return; // !!!!!这里加判断,如果是调用外部资源就退出去
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (b) {
var c = (1E5 * Math.random()).toFixed(0);
z._rd["_cbk" + c] = function(a) {
b && b(a);
delete z._rd["_cbk" + c]
};
a += "&callback=BMap._rd._cbk" + c
}
var d = K("script", {
type: "text/javascript"
});
d.charset = "utf-8";
d.src = a;
d.addEventListener ? d.addEventListener("load",
function(a) {
a = a.target;
a.parentNode.removeChild(a)
},
q) : d.attachEvent && d.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(d);
d = p
},
1)
};
在 bmap_offline_api_min.js
文件中,用 url.domain.main_domain_cdn.baidu[0]
多找几次,定位到下面的代码:
z.url = z.Y_[z.Qy];
z.wp = z.url.proto + z.url.domain.baidumap + "/";
z.wc = z.url.proto + ("2" == z.Qy ? z.url.domain.main_domain_nocdn.other: z.url.domain.main_domain_nocdn.baidu) + "/";
z.ma = z.url.proto + ("2" == z.Qy ? z.url.domain.main_domain_cdn.other[0] : z.url.domain.main_domain_cdn.baidu[0]) + "/";
z.cj = z.url.proto + z.url.domain.main_domain_cdn.webmap[0] + "/";
z.rg = function(a, b) {
var c, d, b = b || "";
switch (a) {
case "main_domain_nocdn":
...
然后将 z.cj = z.url.proto + z.url.domain.main_domain_cdn.webmap[0] + "/"
改为 z.cj = ''
:
z.url = z.Y_[z.Qy];
z.wp = z.url.proto + z.url.domain.baidumap + "/";
z.wc = z.url.proto + ("2" == z.Qy ? z.url.domain.main_domain_nocdn.other: z.url.domain.main_domain_nocdn.baidu) + "/";
z.ma = z.url.proto + ("2" == z.Qy ? z.url.domain.main_domain_cdn.other[0] : z.url.domain.main_domain_cdn.baidu[0]) + "/";
//!!!!!!!!!!!!!!!!!!!!!!!
z.cj = '';
//!!!!!!!!!!!!!!!!!!!!!!!
z.rg = function(a, b) {
var c, d, b = b || "";
switch (a) {
case "main_domain_nocdn":
...
所谓本地资源,就是在使用地图时需要用到的一些模块(module),比如图层类,标记类,控件类。
当你在地图中用到这些模块时,它会自动加载,因此我们需要先把这些模块的js文件下载下来,保存到本地。
这些模块有几十个之多,我们这里并不都存本地,而是用到哪个下载哪个,那么如何知道用到了哪个模块呢?
bmap_offline_api_min.js
文件中,用 &mod=
定位到下面的代码,然后加一行代码将用到的模块打印出来:load: function(a, b, c) {
var d = this.ib(a);
if (d.Bd == this.Fj.cq) c && b();
else {
if (d.Bd == this.Fj.pG) {
this.lK(a);
this.zN(a);
var e = this;
e.DC == q && (e.DC = o, setTimeout(function() {
for (var a = [], b = 0, c = e.Pd.Fn.length; b < c; b++) {
var d = e.Pd.Fn[b],
l = "";
ia.Ly.fK(d) ? l = ia.Ly.get(d) : (l = "", a.push(d + "_" + Rb[d]));
e.Pd.Tv.push({
TM: d,
KE: l
})
}
e.DC = q;
e.Pd.Fn.length = 0;
//!!!!!!!!!!!!!!!!!!!!
console.log(a); //!!!!!打印所需模块,这很重要
//!!!!!!!!!!!!!!!!!!!!
0 == a.length ? e.XK() : oa(e.iG.$P + "&mod=" + a.join(","))
0 == a.length ? e.UK() : Qb("js/getmodules2.0.js")
},
1));
d.Bd = this.Fj.JP
}
d.Ru.push(b)
}
},
mounted(){
this.$nextTick(() => {
// 百度地图
var map = new BMap.Map("container");
var point = new BMap.Point(106.550963, 29.572012);
map.centerAndZoom(point, 12);
//设置定位点的弹跳动画
var marker = new BMap.Marker(point); // 创建标注
map.addOverlay(marker); // 将标注添加到地图中
marker.setAnimation(BMAP_ANIMATION_BOUNCE); //跳动的动画
map.enableScrollWheelZoom(true);
//添加工具条比例尺控件
var top_left_control = new BMap.ScaleControl({anchor: BMAP_ANCHOR_TOP_LEFT});// 左上角,添加比例尺
var top_left_navigation = new BMap.NavigationControl(); //左上角,添加默认缩放平移控件
var top_right_navigation = new BMap.NavigationControl({anchor: BMAP_ANCHOR_TOP_RIGHT, type: BMAP_NAVIGATION_CONTROL_SMALL}); //右上角,仅包含平移和缩放按钮
map.addControl(top_left_control);
map.addControl(top_left_navigation);
map.addControl(top_right_navigation);
})
},
特别注意:如果你要在进入页面就初始化地图,最好像上面那样,放在 mounted
生命函数的 this.$nextTick(() => {})
里,以确保地图容器 #container
元素渲染完成,不然有可能因为初始化时地图容器还未渲染而报错:
刷新这个vue页,关注控制台,就能看到要实现这些地图功能所需要的模块名,是个数组集合,如下图,我这里需要"control_ff1mc0", "navictrl_wxtn3y"两个模块:
在上面步骤打印模块名的地方,做如下修改:
load: function(a, b, c) {
var d = this.ib(a);
if (d.Bd == this.Fj.cq) c && b();
else {
if (d.Bd == this.Fj.pG) {
this.lK(a);
this.zN(a);
var e = this;
e.DC == q && (e.DC = o, setTimeout(function() {
for (var a = [], b = 0, c = e.Pd.Fn.length; b < c; b++) {
var d = e.Pd.Fn[b],
l = "";
ia.Ly.fK(d) ? l = ia.Ly.get(d) : (l = "", a.push(d + "_" + Rb[d]));
e.Pd.Tv.push({
TM: d,
KE: l
})
}
e.DC = q;
e.Pd.Fn.length = 0;
console.log(a); //!!!!!!打印所需模块
// 0 == a.length ? e.XK() : oa(e.iG.$P + "&mod=" + a.join(","))
// 0 == a.length ? e.UK() : Qb("js/getmodules2.0.js")
// !!!!!!!!!!!!!!!!!!!!!!!!引用本地下载好的模块文件资源
if( a.length > 0 ){
for(let i=0; i<a.length;i++){
let mf = './static/modules/'+a[i]+'.js';
oa( mf );
console.log('加载模块文件:'+mf); //IE error
}
} else {
e.XK()
}
// !!!!!!!!!!!!!!!!!!!!!
},
1));
d.Bd = this.Fj.JP
}
d.Ru.push(b)
}
},
现在就能成功加载模块资源了。
这里要注意路径问题,如果路径不对,找不到模块文件,会报错:
离线瓦片可以理解为地图离线包,没有它,离线地图是无法显示的。
首先,在 static
目录下新建文件夹 tiles
来存放瓦片:
用水经注或者太乐地图下载器下载瓦片,我用太乐。
选择百度
下载 -> 选择行政区划
,选完了之后该区域就会出现ufo图标
点击图标,选择 地图
:
选择直接保存瓦片
后,存储标准选择百度瓦片
,存储格式会自动选择 .png
。然后选择级别之后确定即可:
级别就是调用百度地图api处设置的缩放级别:
下载完成后可在这里查看:
找到存储目录:我这里下载了重庆12级的瓦片,所有的瓦片就存储在了12这个文件夹下
将12这个文件夹整个复制到项目static/tiles目录下
你会发现除了下载了12级,还下载了9、10、11级,因为离线的关系,在地图上进行缩放操作到11级,但是如果没有11级的瓦片,地图就什么都不显示。所以如果想要缩放多少级,这些级别的瓦片必须都下载到本地。
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主目录
在 bmap_offline_api_min.js
文件中,可以用 getTilesUrl
多找几次,定位到下面代码:
Yc.getTilesUrl = function(a, b, c) {
var d = a.x,
a = a.y,
e = Qb("normal"),
f = 1,
c = Xc[c];
this.map.Ix() && (f = 2);
d = this.map.eb.Bw(d, b).ds;
return (Wc[Math.abs(d + a) % Wc.length] + "?qt=vtile&x=" + (d + "").replace(/-/gi, "M") + "&y=" + (a + "").replace(/-/gi, "M") + "&z=" + b + "&styles=" + c + "&scaler=" + f + (6 == x.da.la ? "&color_dep=32&colors=50": "") + "&udt=" + e).replace(/-(\d+)/gi, "M$1")
}
修改如下:
Yc.getTilesUrl = function(a, b, c) {
var d = a.x,
a = a.y,
e = Qb("normal"),
f = 1,
c = Xc[c];
// this.map.Ix() && (f = 2);
// d = this.map.eb.Bw(d, b).ds;
// return (Wc[Math.abs(d + a) % Wc.length] + "?qt=vtile&x=" + (d + "").replace(/-/gi, "M") + "&y=" + (a + "").replace(/-/gi, "M") + "&z=" + b + "&styles=" + c + "&scaler=" + f + (6 == x.da.la ? "&color_dep=32&colors=50": "") + "&udt=" + e).replace(/-(\d+)/gi, "M$1")
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let tdir = bmapcfg.tiles_dir.length>0?bmapcfg.tiles_dir:bmapcfg.home + "tiles";
console.log(tdir + "/" + b + "/" + d + "/" + a + bmapcfg.imgext)
return tdir + "/" + b + "/" + d + "/" + a + bmapcfg.imgext; // 使用本地的瓦片
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
};
在这里可将调用瓦片的地址打印出来看看是否正确:
如果瓦片存在,且路径正确,就能正常显示地图了。
ps:地图不能显示出来,是瓦片相关有问题
地图的功能不能实现,是模块相关有问题
这里在 mp_load.js
里已经取到了主路径,可以将之前加载模块(三 - 4)处代码修改成:(不修改也可)
离线并不是指断网,而是指只连内部局域网,表现在你无法访问百度,但是本地跑项目localhost的页面是能展示的。
想调试的话可以将本地的ip改为你知道的内网中一个计算机的ip。
这是中国地图瓦片,可以看到第11级就1G了,16级1T,17级4T。如果你的地图需要带缩放功能,就吧这些大小累加,几T吧~~
来感受下11级的缩放程度,根本不够用
15级的凑合:
17级: