AboutPostsGithub

百度地图基础使用

控件和覆盖物

0.基础介绍

百度地图javascript API是一套由javascript语言编写的应用程序接口,可帮助您在网站中构建功能丰富、交互性强的地图应用,支持PC端和移动端基于浏览器的地图应用开发,且支持HTML5特性的地图开发。

百度地图javascript API支持HTTP和HTTPS,免费对外开放,开发者只需要申请ak密匙即可直接使用。

1.直接使用<script>标签引入

//为了保证地图铺满DOM
<style type="text/css">  
    html{height:100%}    
    body{height:100%;margin:0px;padding:0px}    
    #container{height:100%}    
</style> 
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=您的密钥"></script>
<div id="container"></div> 

然后,js文件这样写

let map = new BMapGL.Map("container");
// 创建地图实例 
let point = new BMapGL.Point(116.404, 39.915);
// 创建点坐标 
map.centerAndZoom(point, 15);
map.enableScrollWheelZoom(true);
//开启滚轮缩放

2.自定义控件

控件指悬浮在地图上的元素节点,可以直接使用Bmap的自带的,也可以自己自定义控件(控件本质上就是一段html,所以高度自定义)

自定义控件有两个思路,一是直接在html上直接写,然后再在js中获取div元素并转换为控件,二是使用js捏一个元素出来,两种方式的代码依次如下。

class MyMapContrl extends BMapGL.Control {
    constructor(option) {
        console.log(option);
        super()
        // 默认停靠位置和偏移量
        // 也可以由传入的参数控制
        this.defaultAnchor = option.anchor || BMAP_ANCHOR_TOP_LEFT
        this.defaultOffset = option.offset || new BMapGL.Size(20, 20)
        // 自定义参数
        // .....
    }
    initialize(map) {
        // 这个初始化方法需要传入map,但事实上使用时没有手动调用initialize方法,也就没有传入map地图实例
        // 在添加这个控件实例到地图上时,自动调用初始化方法,然后传入了当前的地图实例
        console.log(map)
        const div = document.getElementById('control')

        map.getContainer().appendChild(div)
        // 将DOM元素返回
        return div
    }
}
// 创建控件
let myCtrl = new MyMapContrl({ anchor: BMAP_ANCHOR_TOP_LEFT, offset: '' });
// 添加到地图当中
map.addControl(myCtrl);
class MyMapContrlButtom extends BMapGL.Control {
    constructor() {
        super()
        // 默认停靠位置和偏移量
        // 也可以由传入的参数控制
        this.defaultAnchor = BMAP_ANCHOR_TOP_LEFT
        this.defaultOffset = new BMapGL.Size(20, 20)
        // 自定义参数
        // .....
    }
    initialize(map) {
        // 这个初始化方法需要传入map,但事实上使用时没有手动调用initialize方法,也就没有传入map地图实例
        // 在添加这个控件实例到地图上时,自动调用初始化方法,然后传入了当前的地图实例

        console.log(map)
        const div = document.createElement("button")
        div.setAttribute("id", "recordB")
        div.innerText = "开始录制"
        div.setAttribute("onclick", "startRecord()")
        div.setAttribute("style", "margin-left: 50px;background - color: rgba(245, 245, 245, 0.986);border-radius:10px;font-size:18px;cursor:pointer;")
        map.getContainer().appendChild(div)
        // 将DOM元素返回
        return div
    }
}

当然,两者使用的都是ES6语法糖class,官网的是ES5写法。

3.覆盖物

所有叠加或覆盖到地图的内容,我们统称为地图覆盖物。覆盖物拥有自己的地理坐标,当您拖动或缩放地图时,它们会相应的移动。

覆盖物主要分为:标注(点标注、矢量图形(包括折线、多边形、圆))、信息窗口、图层。

3.1.Maker(点标注)

maker就是地图的标记,一个倒置的水滴形状图标(实际上这个图标也可以自定义),只需要一个点实例就可以制作,代码如下:

let basedata = res.items[i].basedata;
let Mpoint = new BMapGL.Point(basedata.CPOINT_LONGITUDE, basedata.CPOINT_LATITUDE);
let marker = new BMapGL.Marker(point);        // 创建标注

还可以定义点击标注时候的回调函数

marker.addEventListener("click", function () {
    console.log("mapCenter", map.getCenter());
    console.log("Mpoint", Mpoint);
    map.openInfoWindow(infoWindow, Mpoint);
});

此处可配和infowindow使用,这个比较简单此处不做记录,内容可以使用html片段。

3.2.多边体

let detaildata = res.items[i].detaildata
let MeshPointArr = []
for (let i = 0; i < detaildata.length; i++) {
    MeshPointArr.push(new BMapGL.Point(detaildata[i].POINT_LONGITUDE,detaildata[i].POINT_LATITUDE))
}
let polygon = new BMapGL.Polygon(MeshPointArr, { strokeColor: "blue", strokeWeight: 2, strokeOpacity: 0.5 });

BMapGL.Polygon传入的第一个参数是一个点实例的数组,之后是一个配置的json。

4.录制点击并绘制

一个奇妙的需求,记录用户点击的点经纬度并且直观的绘制在地图上。

//记录录制中点的数组
let pointArr = []

//录制中点击触发事件
function handleClick(e) {
    // console.log("触发点击事件");
    pointArr.push({ lng: e.latlng.lng, lat: e.latlng.lat })
    if(pointArr.length>1){ReordDraw(pointArr)}
};

//录制中覆盖物重绘
function ReordDraw(pointArr) {
    hiddenMarker("Record")
    let Arr = []
    for (let i = 0; i < pointArr.length; i++) {
        Arr.push(new BMapGL.Point(pointArr[i].lng, pointArr[i].lat))
    }
    let polygon = new BMapGL.Polygon(Arr, { strokeColor: "red", strokeWeight: 2, strokeOpacity: 0.5 });
    polygon.name = "Record"
    map.addOverlay(polygon);

}

//录制按钮的处理
function startRecord() {
    let button = document.getElementById("recordB")
    if (!recording) {
        // console.log("点击按钮事件");

        map.addEventListener('click', handleClick);
        button.innerText = "结束录制";
        recording = !recording;

    } else if (recording) {
        map.removeEventListener('click', handleClick);
        // console.log(JSON.stringify(pointArr));
        alert(JSON.stringify(pointArr));
        pointArr.splice(0);
        button.innerText = "开始录制";
        hiddenMarker("Record")
        recording = !recording;
    }
}

自定义覆盖物

class WindowOverlay extends BMapGL.Overlay {
    constructor(option) {
        super()
        // 这里的this是这个覆盖物实例对象
        this._center = option.position;
        this._type = option.type;
        this._data = option.data;
    }
    // new的时候自动调用此方法
    initialize(map) {
        // 保存map对象实例   
        this._map = map;
        // 这里通过挂在this的方法可以拿到new对象时候的参数
        const detalData = this._data
        // 创建div元素,作为自定义覆盖物的容器   
        const div = document.createElement("div");
        div.innerHTML = `
        <img src="${polyImg[this._type]}" alt="" style="pointer-events: none;">
        <div class="markinfo" style="pointer-events: none;">
            <span style="color:${detalData.eventStatus == 1 ? "#FF6464" : "#184F73"}">${detalData.eventStatus == 1 ? "待处理" : "处理中"}</span>
            <img src="${detalData.eventStatus == 1 ? polygonRightRed : polygonRightBlue}" alt="">
        </div>`
        div.className = "markWindow"
        // // 可以根据参数设置元素样式   
        div.style.position = "absolute";
        div.type = this._type;
        div.style.boxShadow = detalData.eventStatus == 1 ? "0px 10px 10px rgba(163, 14, 14, 0.281)" : "0px 16px 29px -11px rgba(0,44,71,0.7700)";
        // 三个事件监听
        div.addEventListener("mouseover", function (e) {
            vue.$store.dispatch('eventPop/hoverMarkWindow', {
                top: this.offsetTop,
                left: this.offsetLeft,
                show: true,
                data: detalData,
                type: this.type,
            })
        })
        div.addEventListener("mouseout", function (e) {
            vue.$store.dispatch('eventPop/hoverMarkWindow', { show: false })
        })
        div.addEventListener("click", function (e) {
            vue.$store.dispatch('eventPop/changeEventDetal', true)
        })
        // 将div添加到覆盖物容器中
        map.getPanes().markerPane.appendChild(div);
        // 保存div实例   
        this._div = div;
        // 需要将div元素作为方法的返回值,当调用该覆盖物的show、hide方法,或者对覆盖物进行移除时,API都将操作此元素。   
        return div;
    }
    draw() {
        let position = this._map.pointToOverlayPixel(this._center);
        this._div.style.left = position.x - 2 + "px";
        this._div.style.top = position.y - 95 + "px";
    }
    show() {
        if (this._div) {
            this._div.style.display = "";
        }
    }
    hide() {
        if (this._div) {
            this._div.style.display = "none";
        }
    }
}

class上挂载的方法可以在每一个实例上调用,可从此窥见ES6中class的用法

依赖注入和控制反转

再一次使用百度地图,不仅是因为之前的代码我忘记保存了,也是对之前没有考虑class新建的时候将div抽象出来的设计模式的重构与反思。

之前div在class初始化的时候使用js创建,导致代码复用性极差,并且要使用很迷惑的方式去更改div样式(指的就是js更改),这次在构造覆盖物对象的时候传入写好的div,而在初始化的时候拿到,就实现了覆盖物和class的解耦(这个词我不确定是否使用正确,但大概就那么个意思),提升代码复用性。

代码如下:

class WindowOverlay extends BMapGL.Overlay {
    constructor(option) {
        super()
        // 这里的this是这个覆盖物实例对象
        this._center = option.position;
        this._div = option.div;
    }
    // new的时候自动调用此方法(初始化)
    initialize(map) {
        // 保存map对象实例   
        this._map = map;
        // 这里通过挂在this的方法可以拿到new对象时候的参数
        // 将div添加到覆盖物容器中+
        const div = this._div
        map.getPanes().markerPane.appendChild(div);
        // 需要将div元素作为方法的返回值,当调用该覆盖物的show、hide方法,或者对覆盖物进行移除时,API都将操作此元素。   
        return div;
    }
    draw() {
        const position = this._map.pointToOverlayPixel(this._center);
        // console.log('position', position)
        this._div.style.left = position.x - 2 + "px";
        this._div.style.top = position.y - 95 + "px";
    }
    show() {
        if (this._div) {
            this._div.style.display = "";
        }
    }
    hide() {
        if (this._div) {
            this._div.style.display = "none";
        }
    }
}

export const Bmap = {
    // zoom:缩放数量级,lng/lat 经纬度
    // div:地图载体元素
    initMap(div, zoom, lng, lat) {
        // 创建地图实例
        const map = new BMapGL.Map(div);
        // 创建地图实例 
        const point = new BMapGL.Point(lng, lat);
        map.centerAndZoom(point, zoom);
        map.enableScrollWheelZoom(true);
        return map
    },
    uesStyle(map) {
        // 将地图样式改为
        map.setMapStyleV2({ styleJson: style });
    },
    addOverlay(map, div, lng, lat) {
        const overlay = new WindowOverlay({
            position: { lng: lng, lat: lat },
            div: div
        })
        map.addOverlay(overlay);
    }


}

另外一件事,百度地图可以自定义样式,具体参考uesStyle方法