Leaflet 調用百度瓦片地圖服務

在使用 leaflet 調用第三方瓦片地圖服務的項目,主要谷歌地圖、高德地圖、百度地圖和 OSM 地圖,與其他三種地圖對比,百度地圖的瓦片組織方式是不同的。百度從中心點經緯度(0,0)度開始計算瓦片,而谷歌地圖是從左上角經緯度(-180,90)度開始計算瓦片;如果直接使用百度瓦片地圖服務會請求不到瓦片,因此需要轉換一下。藉助 leaflet-tileLayer-baidu 這個插件:

//需要引入 proj4.js 和 proj4leaflet.js 插件,使用script標籤引入的方式
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"://實時路況
            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;
};

 

初始化之後,就可以直接實例化生成調用百度瓦片地圖服務的實例;

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項目,使用了 fullscreenControl 全屏插件 */

 

需要注意:

1、地圖切換:

如果只是實現高德、谷歌地圖、天地圖的Leaflet 切換,可以使用 leaflet插件 Leaflet.ChineseTmsProviders

2、座標系系統:

國內使用的地圖座標都是經過設備返回的座標(WGS84)加密的,一般是GCJ02(高德地圖、谷歌地圖中國區服務都是用這一標準);百度地圖則在GCJ02的基礎上又進行了二次加密,使用的BD09座標;因此直接在地圖顯示原始點的位置信息會有幾十米到幾百米的誤差。地圖官方都提供了在線的座標轉換接口以及web服務api接口:以百度地圖爲例:

//百度地圖座標轉換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 回調
// 百度地圖在線轉換接口地址
http://api.map.baidu.com/geoconv/v1/?coords=114.21892734521,29.575429778924&from=1&to=5&ak=你的密鑰 //GET請求

百度地圖支持單個點轉換和批量點轉換,同時支持 WGS84轉BD09,和GCJ02轉BD09座標; 

但是存在以下問題:

①、百度地圖在線api 最多一次支持10個點,web服務api單次請求可批量解析100個座標,對於海量點(幾千到幾萬)來說,這樣的處理速度顯然是不夠的;高德地圖同樣滿足不了業務需求;

②、不支持逆向解析,例如BD09 沒有對應的接口轉GCJ02和WGS84;因此在地圖切換的時候,使用百度地圖拾取的點就無法在其他地圖上準確顯示(高德地圖提供BD09到GCJ02的座標轉換,但業務使用的只是高德的瓦片服務);

可以使用 gcoord 插件解決上述問題,支持上述三種座標系互轉,且是同步的接口,海量數據無性能瓶頸。(因爲逆向解析沒有公開的算法,原始座標只是無限接近,基本滿足地圖使用要求。)

 

3、海量點性能問題

使用百度地圖點的數量在2000左右時候,無論是打點、還是縮放、平移都會影響用戶體驗了,海量點接口雖可以支持萬級別的數據,但是不能自定義圖標、不能顯示label;這是要是DOM操作嚴重影響了性能,可以使用Leaflet.Canvas-Markers解決海量點性能問題。

4、國外座標識別

百度在線的api轉換接口 ,會智能判斷點的座標是否在國內還是在國外,如果判斷在國外就直接返回數據;但如果使用 gcoord 就無法智能識別;需要手動寫一個座標判斷識別庫,並且需要國界線經緯度數據。由於精確顯示國界線的經緯度數據十分龐大,因此隨着點精度的提高,需要犧牲瀏覽器性能,(在線轉會有點的限制,估計也是性能考究)。

判斷一個點是否在國內,可以參考 判斷一個點是否在多邊形內部 - 射線法思路,結合國界線的經緯度數據,就可以判斷點的座標是否在國內。射線法判斷算法:

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);  // 臺北
        if(a || b ) {
            return true;
        }else {
            return false;
        }
    }catch(err){
        console.log(err);
    }
}

 

參考鏈接:

1、百度與谷歌地圖瓦片組織方式對比

2、 leaflet-tileLayer-baidu   作者還提供了一個 在線預覽的地址 

3、點在多邊形內算法,JS判斷一個點是否在一個複雜多邊形的內部

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章