百度地图基础使用
控件和覆盖物
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
方法