百度地圖自定義marker(圖標),layer(覆蓋層)

概要

本文只要涉及的內容有,web中動態引入百度地圖,基於百度地圖的本地搜索(公交,地鐵,停車場),自定義marker,layer,接入微信內置地圖(微信中使用第三方導航)。javascript

效果預覽

圖片描述

地圖懶加載

本示例應用於小程序內嵌的webview,web開發使用react。因爲示例做爲項目中的一個沒必要要模塊,不是每次進入都會加載,所以選擇在項目肯定使用百度地圖時,在進行加載。即動態加載百度地圖的地圖服務資源(代碼直接從網上copy了一個):html

MP(ak) {
        return new Promise(resolve=> {
            const script = document.createElement("script");
            script.type = "text/javascript";
            script.src = `https://api.map.baidu.com/api?v=2.0&ak=${ak}&callback=init`;
            document.head.appendChild(script);
            window.init = () => {
                resolve(window.BMap);
            };
        });
    }
openBMap() {
            this.MP("你的ak").then(BMap => {
                this.bmap = new BMap.Map("allmap");            // 建立Map實例
                const mPoint = new BMap.Point(103.96120956167579, 30.67880629052847);
                this.bmap.enableScrollWheelZoom();
                this.bmap.centerAndZoom(mPoint, 15);
                const options = {
                    onSearchComplete: (results) => {
                        if (local.getStatus() == BMAP_STATUS_SUCCESS) {
                            this.searchResults = results;
                            this.generateMarker(0); // 生成marker
                        }
                    }
                };
                const local = new BMap.LocalSearch(this.bmap, options);
                // 
                local.searchNearby(["公交站", "地鐵站", "停車場"], mPoint, 1200);
            });
    }

local.searchNearby 爲搜索附近api,搜索參數依次爲搜索內容(string| arr["obj"]),搜索中心點,搜索範圍。
詳細參考:http://lbsyun.baidu.com/cms/j...java

自定義marker

這個比較簡單,直接參考官方demo。先生成Icon,而後將icon傳入marker。react

const myIcon = new BMap.Icon(imgUrl, new BMap.Size(40, 40));
            const marker = new BMap.Marker(this.searchResults[index].Ar[i].point, {icon: myIcon});
            marker.addEventListener("click", (e) => {
                this.filterMarker(e.target.point, index);
            });
            this.bmap.addOverlay(marker);

自定義layer

這個就麻煩一點了。
由於以前有使用mapbox的經驗,因此最初的思路是直接在生成的marker上添加一個popup,適當作一些偏移。可是百度地圖跟marker直接作關聯的只有一個信息窗口,即InfoWindow。當時這裏花費了比較多的時間去改樣式,主要參考了這篇文章,可是改出來的效果並不太好,可能sdk的版本不太同樣吧。因而乎才選擇了百度地圖的自定義圖層,可是這個圖層沒法直接跟marker關聯,因此只能去獲取marker的座標,再去把圖層先是至相關位置點。git

自定義圖層參考demo:http://lbsyun.baidu.com/jsdem...es6

將marker與自定義圖層關聯起來,主要依靠
pt: marker 座標
this.bmap: 地圖實例web

const clickMarker = this.searchResults.Ar.filter((v, i) => {
            if (v.point.lng == pt.lng && v.point.lat == pt.lat) {
                clickIndex = i;
            }
            return v.point.lng == pt.lng && v.point.lat == pt.lat;
        });

marker 的過濾主要依賴點擊獲取的經緯度,與初始搜索存儲的marker列表進行對比。(點擊事件中未找到uid,故對比經緯度)。小程序

將獲得的點擊marker 中的信息傳入圖層,在marker點擊事件中註冊 地圖的移動事件,即 this.bmap.panTo(pt); 保證每次點擊marker 將地圖移至中心。api

調用騰訊內部地圖

在小程序中經過,openLocation 來打開微信內置地圖,這裏有兩個點要注意。一是小程序的openLocation 接受的經緯度必須是number,即:微信

wx.openLocation({
            latitude: +gcjPoint[1],
            longitude: +gcjPoint[0],
            name: destination.title,
            address: destination.address,
            scale: 18
        });

第二點是騰訊(國內)地圖接受的座標都是gcj02(火星座標),若是傳入wgs84(全球座標)會存在必定距離的誤差。這裏把網上的一段wgs84 轉 gcj02 代碼用es6 重寫了一遍。

class Wgs2Gcj {
    a = 6378245.0;
    ee = 0.00669342162296594323;

    constructor(wgLon, wgLat) {
        this.wgLat = wgLat;
        this.wgLon = wgLon;
    }

    outOfChina(lon, lat) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }

    transform() {
        if (this.outOfChina(this.wgLon, this.wgLat)) {
            return [this.wgLon, this.wgLat];
        }
        let dLat = this.transformLat(this.wgLon - 105.0, this.wgLat - 35.0);
        let dLon = this.transformLon(this.wgLon - 105.0, this.wgLat - 35.0);
        let radLat = this.wgLat / 180.0 * Math.PI;
        let magic = Math.sin(radLat);
        magic = 1 - this.ee * magic * magic;
        let sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtMagic) * Math.PI);
        dLon = (dLon * 180.0) / (this.a / sqrtMagic * Math.cos(radLat) * Math.PI);
        let mgLat = this.wgLat + dLat;
        let mgLon = this.wgLon + dLon;

        return [mgLon, mgLat];
    }

    transformLat(x, y) {
        let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
        return ret;
    };

    transformLon(x, y) {
        let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
        return ret;
    };
}

調用方法:

const gcjPoint = new Wgs2Gcj(destination.point.lng, destination.point.lat).transform();
相關文章
相關標籤/搜索