瓦片
瓦片是构成地图的单元,一张地图由若干的瓦片平铺而成,并且瓦片还会在高度上叠加,就像PS图层一样:居下的瓦片显示地形地貌,被称为底图;居上的透明瓦片仅标记地名等信息,被称为注记图。
一张地图的首次加载以及后续对地图的平移缩放操作都需要请求很多瓦片,每次向天地图请求瓦片时都需要带上开发者key:
请求天地图瓦片有当日次数限制
每个key当天请求瓦片次数是有限制的,如下图:
个人开发者key一天所能请求的同种类瓦片为1万次,如何突破次数限制?我的做法是首先申请多个key(一个账号可以申请5个key),将这些key都存在一个数组中,然后请求瓦片时从数组中取key,让数组中的每个key轮流参与请求瓦片。
突破天地图请求瓦片次数限制
首先将申请下来的key都存到一个数组中(我申请了3个):
let keys = [
"075b2ed2e2f63b7544aee51be98ca57a",
"90425f13ed47e5a05f472e5d91d31594",
"f4544db8c930fbea8272002799c78351"
];
当你导入天地图API时,全局作用域下就会有一个对象T
,该对象装载了天地图API,T
有一个属性tk
,经过我的观察,T.tk
保存的值就是请求瓦片时使用的key:
因此我们需要做的是:当访问T.tk
时就从keys
中取一个key作为访问本次T.tk
的值。实现此功能可以使用Proxy代理,代码如下:
//当前请求瓦片使用的key的下标
window.currentIndex = -1;
//代理T,实现对访问T.tk进行拦截
T = new Proxy(T, {
get: function (target, key) {
//如果访问T.tk时
if (key === "tk") {
//取用的key在keys中的下标
currentIndex = (currentIndex + 1) % keys.length;
return `tk=${keys[currentIndex]}&`;
}
return target[key];
},
});
到这里,已经可以实现请求瓦片时使用不同key:
瓦片请求异常时自动换key重新请求
当瓦片所使用的key到达当日请求上限时,这张瓦片就会请求失败,我们可以全局注册error事件,然后在error事件回调函数里判断出错的节点是不是瓦片,如果是瓦片产生的错误,将瓦片使用的key从keys
中移除,并更换另一个key重新加载该出错的瓦片:
window.addEventListener(
"error",
(e) => {
//target是产生错误的节点
const target = e.target;
//用来匹配产生错误的节点所请求的资源路径是否为天地图瓦片。
//img_w为影像底图,cia_w为影像注记图,根据你使用的底图类型和注记图类型替换
const regExp = /tianditu.gov.cn\/(img_w|cia_w)\/wmts/;
//用来提取请求的瓦片的资源路径中的参数tk的值
const regExp2 = /(?<=\&tk\=)(.+?)(?=\&)/
/*
如果target是img节点,那么他的currentSrc属性保存着请求资源的路径;
如果请求的资源路径能匹配上正则表达式regExp,说明target就是瓦片节点。
*/
if (regExp.test(target.currentSrc)) {
//如果keys里的key都耗尽,则打印警告信息,结束处理函数的后续执行
if (!keys.length) {
console.warn("天地图备选key已全部耗尽!");
return;
}
//将当日已超额的key从keys中移除
const tk = target.currentSrc.match(regExp2)[1];
const removeIndex = keys.findIndex((item) => item === tk);
if (removeIndex !== -1) {
keys.splice(removeIndex, 1);
}
//将原来的url参数中当日已超额的key替换为下一个key,此操作会使图片重新请求资源
loopURL = target.src.replace(
regExp2,
keys[currentIndex]
);
target.src = loopURL;
target.currentSrc = loopURL;
currentIndex = (currentIndex + 1) % keys.length;
}
},
true
);
完整的实现代码
window.keys = [
"075b2ed2e2f63b7544aee51be98ca57a",
"90425f13ed47e5a05f472e5d91d31594",
"f4544db8c930fbea8272002799c78351"
];
window.currentIndex = -1;
T = new Proxy(T, {
get: function (target, key) {
if (key === "tk") {
currentIndex = (currentIndex + 1) % keys.length;
T.w.TMAP_AUTHKEY = keys[currentIndex];
return `tk=${keys[currentIndex]}&`;
}
return target[key];
},
});
window.addEventListener(
"error",
(e) => {
const target = e.target;
const regExp = /tianditu.gov.cn\/(img_w|cia_w)\/wmts/;
const regExp2 = /(?<=\&tk\=)(.+?)(?=\&)/
if (regExp.test(target.currentSrc)) {
if (!keys.length) {
console.warn("天地图备选key已全部耗尽!");
return;
}
const tk = target.currentSrc.match(regExp2)[1];
const removeIndex = keys.findIndex((item) => item === tk);
if (removeIndex !== -1) {
keys.splice(removeIndex, 1);
}
loopURL = target.src.replace(
regExp2,
keys[currentIndex]
);
target.src = loopURL;
target.currentSrc = loopURL;
currentIndex = (currentIndex + 1) % keys.length;
}
},
true
);
注意这套代码要在引入天地图API之后再执行。本代码仅提供解决问题的思路,使用这个思路,可以解决开发阶段未申请到企业key,而开发者key的请求次数又不够的问题。