在使用 leaflet 調(diào)用第三方瓦片地圖服務(wù)的項(xiàng)目,主要谷歌地圖、高德地圖、百度地圖和 OSM 地圖,與其他三種地圖對(duì)比,百度地圖的瓦片組織方式是不同的。百度從中心點(diǎn)經(jīng)緯度(0,0)度開(kāi)始計(jì)算瓦片,而谷歌地圖是從左上角經(jīng)緯度(-180,90)度開(kāi)始計(jì)算瓦片;如果直接使用百度瓦片地圖服務(wù)會(huì)請(qǐng)求不到瓦片,因此需要轉(zhuǎn)換一下。借助 leaflet-tileLayer-baidu 這個(gè)插件:
//需要引入 proj4.js 和 proj4leaflet.js 插件,使用script標(biāo)簽引入的方式
L.CRS.Baidu = new L.Proj.CRS('EPSG:900913', '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', {
resolutions: function () {
level = 19
var res = [];
res[0] = Math.pow(2, 18);
for (var i = 1; i < level; i++) {
res[i] = Math.pow(2, (18 - i))
}
return res;
}(),
origin: [0, 0],
bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
});
L.tileLayer.baidu = function (option) {
option = option || {};
var layer;
var subdomains = '0123456789';
switch (option.layer) {
//單圖層
case "vec":
default:
//'http://online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=pl&b=0&limit=60&scaler=1&udt=20170525'
layer = L.tileLayer('http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=' + (option.bigfont ? 'ph' : 'pl') + '&scaler=1&p=1', {
name:option.name,subdomains: subdomains, tms: true
});
break;
case "img_d":
layer = L.tileLayer('http://shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46', {
name: option.name, subdomains: subdomains, tms: true
});
break;
case "img_z":
layer = L.tileLayer('http://online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=' + (option.bigfont ? 'sh' : 'sl') + '&v=020', {
name: option.name, subdomains: subdomains, tms: true
});
break;
case "custom"://Custom 各種自定義樣式
//可選值:dark,midnight,grayscale,hardedge,light,redalert,googlelite,grassgreen,pink,darkgreen,bluish
option.customid = option.customid || 'midnight';
layer = L.tileLayer('http://api{s}.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&scale=1&customid=' + option.customid, {
name: option.name, subdomains: "012", tms: true
});
break;
case "time"://實(shí)時(shí)路況
var time = new Date().getTime();
layer = L.tileLayer('http://its.map.baidu.com:8002/traffic/TrafficTileService?x={x}&y={y}&level={z}&time=' + time + '&label=web2D&v=017', {
name: option.name, subdomains: subdomains, tms: true
});
break;
//合并
case "img":
layer = L.layerGroup([
L.tileLayer.baidu({ name: "底圖", layer: 'img_d', bigfont: option.bigfont }),
L.tileLayer.baidu({ name: "注記", layer: 'img_z', bigfont: option.bigfont })
]);
break;
}
return layer;
};
初始化之后,就可以直接實(shí)例化生成調(diào)用百度瓦片地圖服務(wù)的實(shí)例;
this.map = L.map('_map',{crs: L.CRS.Baidu, fullscreenControl: true}).setView(this.originPoint, this.originZoom);
L.tileLayer.baidu({layer: 'vec'}, {maxZoom: this.maxZoom, minZoom: this.minZoom}).addTo(this.map);
/* 這里是VUE項(xiàng)目,使用了 fullscreenControl 全屏插件 */
需要注意:
1、地圖切換:
如果只是實(shí)現(xiàn)高德、谷歌地圖、天地圖的Leaflet 切換,可以使用 leaflet插件?Leaflet.ChineseTmsProviders;
2、坐標(biāo)系系統(tǒng):
國(guó)內(nèi)使用的地圖坐標(biāo)都是經(jīng)過(guò)設(shè)備返回的坐標(biāo)(WGS84)加密的,一般是GCJ02(高德地圖、谷歌地圖中國(guó)區(qū)服務(wù)都是用這一標(biāo)準(zhǔn));百度地圖則在GCJ02的基礎(chǔ)上又進(jìn)行了二次加密,使用的BD09坐標(biāo);因此直接在地圖顯示原始點(diǎn)的位置信息會(huì)有幾十米到幾百米的誤差。地圖官方都提供了在線的坐標(biāo)轉(zhuǎn)換接口以及web服務(wù)api接口:以百度地圖為例:
//百度地圖坐標(biāo)轉(zhuǎn)換api
var points = [
new BMap.Point(116.3786889372559,39.90762965106183),
new BMap.Point(116.38632786853032,39.90795884517671),
new BMap.Point(116.39534009082035,39.907432133833574),
new BMap.Point(116.40624058825688,39.90789300648029),
new BMap.Point(116.41413701159672,39.90795884517671)
]
var convertor = new BMap.Convertor();
convertor.translate(points, 1, 5, translateCallback) //translateCallback 回調(diào)
// 百度地圖在線轉(zhuǎn)換接口地址
http://api.map.baidu.com/geoconv/v1/?coords=114.21892734521,29.575429778924&from=1&to=5&ak=你的密鑰 //GET請(qǐng)求
百度地圖支持單個(gè)點(diǎn)轉(zhuǎn)換和批量點(diǎn)轉(zhuǎn)換,同時(shí)支持 WGS84轉(zhuǎn)BD09,和GCJ02轉(zhuǎn)BD09坐標(biāo);?
但是存在以下問(wèn)題:
①、百度地圖在線api 最多一次支持10個(gè)點(diǎn),web服務(wù)api單次請(qǐng)求可批量解析100個(gè)坐標(biāo),對(duì)于海量點(diǎn)(幾千到幾萬(wàn))來(lái)說(shuō),這樣的處理速度顯然是不夠的;高德地圖同樣滿足不了業(yè)務(wù)需求;
②、不支持逆向解析,例如BD09 沒(méi)有對(duì)應(yīng)的接口轉(zhuǎn)GCJ02和WGS84;因此在地圖切換的時(shí)候,使用百度地圖拾取的點(diǎn)就無(wú)法在其他地圖上準(zhǔn)確顯示(高德地圖提供BD09到GCJ02的坐標(biāo)轉(zhuǎn)換,但業(yè)務(wù)使用的只是高德的瓦片服務(wù));
可以使用?gcoord?插件解決上述問(wèn)題,支持上述三種坐標(biāo)系互轉(zhuǎn),且是同步的接口,海量數(shù)據(jù)無(wú)性能瓶頸。(因?yàn)槟嫦蚪馕鰶](méi)有公開(kāi)的算法,原始坐標(biāo)只是無(wú)限接近,基本滿足地圖使用要求。)
3、海量點(diǎn)性能問(wèn)題
使用百度地圖點(diǎn)的數(shù)量在2000左右時(shí)候,無(wú)論是打點(diǎn)、還是縮放、平移都會(huì)影響用戶體驗(yàn)了,海量點(diǎn)接口雖可以支持萬(wàn)級(jí)別的數(shù)據(jù),但是不能自定義圖標(biāo)、不能顯示label;這是要是DOM操作嚴(yán)重影響了性能,可以使用Leaflet.Canvas-Markers解決海量點(diǎn)性能問(wèn)題。
4、國(guó)外坐標(biāo)識(shí)別
百度在線的api轉(zhuǎn)換接口 ,會(huì)智能判斷點(diǎn)的坐標(biāo)是否在國(guó)內(nèi)還是在國(guó)外,如果判斷在國(guó)外就直接返回?cái)?shù)據(jù);但如果使用?gcoord?就無(wú)法智能識(shí)別;需要手動(dòng)寫(xiě)一個(gè)坐標(biāo)判斷識(shí)別庫(kù),并且需要國(guó)界線經(jīng)緯度數(shù)據(jù)。由于精確顯示國(guó)界線的經(jīng)緯度數(shù)據(jù)十分龐大,因此隨著點(diǎn)精度的提高,需要犧牲瀏覽器性能,(在線轉(zhuǎn)會(huì)有點(diǎn)的限制,估計(jì)也是性能考究)。
判斷一個(gè)點(diǎn)是否在國(guó)內(nèi),可以參考?判斷一個(gè)點(diǎn)是否在多邊形內(nèi)部 - 射線法思路,結(jié)合國(guó)界線的經(jīng)緯度數(shù)據(jù),就可以判斷點(diǎn)的坐標(biāo)是否在國(guó)內(nèi)。射線法判斷算法:
function pointInChina(position = [31.172800343248,121.406021546488]) {
if(position.length !== 2) {
console.error('The argument is an array of longitude and latitude, with longitude first and latitude last')
return;
};
if((position[0] < 3.85 || position[0] > 53.55) || (position[1] < 73.55) || position[1] > 135.08) {
console.log('Must be abroad')
return false;
}function isInPolygon(checkPoint, polygonPoints) {
var counter = 0;
var i;
var xinters;
var p1, p2;
var pointCount = polygonPoints.length;
p1 = polygonPoints[0];
for (i = 1; i <= pointCount; i++) {
p2 = polygonPoints[i % pointCount];
if (checkPoint[0] > Math.min(p1[0], p2[0]) && checkPoint[0] <= Math.max(p1[0], p2[0])) {
if (checkPoint[1] <= Math.max(p1[1], p2[1])) {
if (p1[0] != p2[0]) {
xinters = (checkPoint[0] - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
if (p1[1] == p2[1] || checkPoint[1] <= xinters) {
counter++;
}
}
}
}
p1 = p2;
}
if (counter % 2 == 0) {
return false;
} else {
return true;
}
}
try {
let a = isInPolygon(position, mainLand_array); //大陸
let b = isInPolygon(position, taiWan_array); // 臺(tái)北
if(a || b ) {
return true;
}else {
return false;
}
}catch(err){
console.log(err);
}
}
?5、驗(yàn)證坐標(biāo)轉(zhuǎn)換正確性:
可以使用這個(gè)瀏覽器插件 :WebGIS-Helper

參考鏈接:
1、百度與谷歌地圖瓦片組織方式對(duì)比;
2、?leaflet-tileLayer-baidu?? 作者還提供了一個(gè)?在線預(yù)覽的地址?;
3、點(diǎn)在多邊形內(nèi)算法,JS判斷一個(gè)點(diǎn)是否在一個(gè)復(fù)雜多邊形的內(nèi)部文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-483064.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-483064.html
到了這里,關(guān)于Leaflet 調(diào)用百度瓦片地圖服務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!