百度地图自定义marker图标layer覆盖层

38次阅读

共计 3741 个字符,预计需要花费 10 分钟才能阅读完成。

概要

本文只要涉及的内容有,web 中动态引入百度地图,基于百度地图的本地搜索(公交,地铁,停车场),自定义 marker,layer,接入微信内置地图(微信中使用第三方导航)。

效果预览

地图懒加载

本示例应用于小程序内嵌的 webview,web 开发使用 react。由于示例作为项目中的一个不必要模块,不是每次进入都会加载,因此选择在项目确定使用百度地图时,在进行加载。即动态加载百度地图的地图服务资源(代码直接从网上 copy 了一个):

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…

自定义 marker

这个比较简单,直接参考官方 demo。先生成 Icon,然后将 icon 传入 marker。

             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 的坐标,再去把图层先是至相关位置点。

自定义图层参考 demo:http://lbsyun.baidu.com/jsdem…

将 marker 与自定义图层关联起来,主要依靠
pt: marker 坐标
this.bmap: 地图实例

 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 将地图移至中心。

调用腾讯内部地图

在小程序中通过,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();

正文完
 0