百度地图-多区域绘制、编辑、面积计算、重叠判断、清除、回显
写在开头
本章来介绍一下使用百度地图上的区域绘制功能,最终实现效果如上动图。官方DEMO
话不多说,我们先来初始化项目:
npm init @vitejs/app
为了方便快速开发,小编这里采用的是 Vue3+Vite2
技术直接来开搞。
引入百度地图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<link href="//mapopen.cdn.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.css" rel="stylesheet">
<script type="text/javascript" src="//api.map.baidu.com/getscript?type=webgl&v=1.0&ak=你的key"></script>
<script type="text/javascript" src="//mapopen.bj.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
我们直接在 index.html
文件中引入百度地图相关的 CDN
链接。上面引入的 DrawingManager
库是 百度地图GL工具库 中的一个绘制库,它能给我们提供在地图上绘制的能力。DrawingManager API文档
注意:上面代码中需要把 key
换成你自己的,获取自己的 key
你需要先去百度地图那边申请一下账号,传送门。
初始化地图
完成上面地图的引入步骤后,开头第一步我们要先来把地图初始化出来,这就是基本操作了,我们直接来看代码,还不会的小伙伴可以先读读 文档 唷。
<!-- App.vue -->
<template>
<h1>百度地图-多区域绘制、区域编辑、区域面积计算、区域重叠判断、区域清除</h1>
<div class="map-container">
<div id="mapContainer" class="map"></div>
</div>
</template>
<script>
import { defineComponent, onMounted } from 'vue';
export default defineComponent({
setup() {
let map = null;
//初始化-地图
function initMap () {
return new Promise(resolve => {
map = new BMapGL.Map('mapContainer', {
minZoom: 1,
maxZoom: 20,
enableMapClick: false
});
// 设置中心点和缩放级别
map.centerAndZoom(new BMapGL.Point(116.404, 39.915), 15);
// 开启滚轮缩放
map.enableScrollWheelZoom(true);
// 地图加载完成
map.addEventListener('tilesloaded', () => {
resolve();
});
});
}
// 需要等dom渲染完成后再初始化地图
onMounted(() => {
initMap();
});
return {};
}
})
</script>
<style scoped>
.map-container{
width: 1200px;
height: 550px;
position: relative;
margin-bottom: 10px
}
.map{
width: 100%;
height: 100%;
}
</style>
运行项目后,如果能正常看到一幅地图,就说明初始化成功啦。
多区域绘制
地图初始化完成后,接下来我们要来初始化绘制工具。
<template>
<div class="map-container">
<div id="mapContainer" class="map"></div>
<!-- 添加不同绘制图形的按钮, 会把它们定位到左下角 -->
<ul class="drawing-panel">
<li class="bmap-btn bmap-rectangle" id="rectangle" @click="draw($event, 'rectangle')"></li>
<li class="bmap-btn bmap-polygon" id="polygon" @click="draw($event, 'polygon')"></li>
</ul>
</div>
</template>
<script>
import { defineComponent, onMounted } from 'vue';
export default defineComponent({
setup() {
let map = null;
let drawingManager = null;
const styleOptions = {
strokeColor: '#e38932', // 边线颜色
fillColor: '#e38932', // 填充颜色。当参数为空时,圆形没有填充颜色
strokeWeight: 1, // 边线宽度,以像素为单位
strokeOpacity: 1, // 边线透明度,取值范围0-1
fillOpacity: 0.2 // 填充透明度,取值范围0-1
};
const labelOptions = {
borderRadius: '2px',
background: '#FFFBCC',
border: '1px solid #E1E1E1',
color: '#703A04',
fontSize: '12px',
letterSpacing: '0',
padding: '5px'
};
// 初始化-绘制工具
function initDrawingManager() {
// 实例化鼠标绘制工具
drawingManager = new BMapGLLib.DrawingManager(map, {
enableCalculate: true, // 绘制是否进行测距测面
enableSorption: true, // 是否开启边界吸附功能
sorptiondistance: 20, // 边界吸附距离
enableGpc: false, // 是否开启延边裁剪功能
enableLimit: true, // 是否开启超限提示
limitOptions: {
area: 500000000, // 面积超限值
distance: 300000 // 距离超限值
},
circleOptions: styleOptions, // 圆的样式
polylineOptions: styleOptions, // 线的样式
polygonOptions: styleOptions, // 多边形的样式
rectangleOptions: styleOptions, // 矩形的样式
labelOptions: labelOptions, // label样式
});
// 绘制完成后获取相关的信息(面积等)
drawingManager.addEventListener('overlaycomplete', (e, instance) => {
console.log('绘制完成:', e, instance);
console.log('绘制面积:', (e.calculate / 1000 / 1000).toFixed(2) + 'km²');
});
}
function initMap () {
return new Promise(resolve => {
map = new BMapGL.Map('mapContainer', {
minZoom: 1,
maxZoom: 20,
enableMapClick: false
});
map.centerAndZoom(new BMapGL.Point(116.404, 39.915), 15);
map.enableScrollWheelZoom(true);
// 初始化-绘制工具
initDrawingManager();
map.addEventListener('tilesloaded', () => { resolve(); });
});
}
// 绘制功能
function draw(e, id) {
e.target.style.backgroundPositionY = '-52px';
let drawingType = '';
switch (id) {
case 'polyline':
drawingType = 'polyline'; break;
case 'rectangle':
drawingType = 'rectangle'; break;
case 'polygon':
drawingType = 'polygon'; break;
case 'circle':
drawingType = 'circle'; break;
}
// 进行绘制
if (drawingManager._isOpen && drawingManager.getDrawingMode() === drawingType) {
drawingManager.close();
} else {
drawingManager.setDrawingMode(drawingType);
drawingManager.open();
}
}
onMounted(() => {
initMap();
});
return {
draw
};
}
})
</script>
<style scoped>
...
.drawing-panel {
z-index: 999;
position: absolute;
bottom: 3.5rem;
margin-left: 2.5rem;
padding-left: 0;
border-radius: .25rem;
height: 47px;
box-shadow: 0 2px 6px 0 rgba(27, 142, 236, 0.5);
list-style: none;
}
.bmap-btn {
border-right: 1px solid #d2d2d2;
float: left;
width: 64px;
height: 100%;
background-image: url(//api.map.baidu.com/library/DrawingManager/1.4/src/bg_drawing_tool.png);
cursor: pointer;
}
.drawing-panel .bmap-marker {
background-position: -65px 0;
}
.drawing-panel .bmap-polyline {
background-position: -195px 0;
}
.drawing-panel .bmap-rectangle {
background-position: -325px 0;
}
.drawing-panel .bmap-polygon {
background-position: -260px 0;
}
.drawing-panel .bmap-circle {
background-position: -130px 0;
}
</style>
上面我们初始化了绘制工具 BMapGLLib.DrawingManager
对象,通过调用它的 drawingManager.setDrawingMode(DrawingType)
方法,我们可以绘制不同形状的图形区域,百度地图一共提供了五种图形绘制,DrawingType
对应五个常量。
方法 | 返回值 | 描述 |
---|---|---|
close() | 关闭地图的绘制状态 | |
disableCalculate() | 关闭距离或面积计算 | |
enableCalculate() | 打开距离或面积计算 | |
getDrawingMode() | DrawingType | 获取当前的绘制模式 |
open() | 开启地图的绘制模式 | |
setDrawingMode(DrawingType) | Boolean | 设置当前的绘制模式,参数DrawingType,为5个可选常量: BMAP_DRAWING_MARKER 画点 BMAP_DRAWING_CIRCLE 画圆 BMAP_DRAWING_POLYLINE 画线 BMAP_DRAWING_POLYGON 画多边形 BMAP_DRAWING_RECTANGLE 画矩形 |
上面小编 DrawingType
直接使用的是常量的值,你也可以在 window
身上找到这五个常量。
当我们完成一个图形区域的绘制后,会触发 overlaycomplete 回调方法,从这个方法我们可以获取到图形区域的实例、面积等等信息。
这里得到的面积实际并不能满足小编的任务需求,下面会单独写一个计算多边形面积的方法,之所以不能满足呢,是因为有这样的场景,当编辑的时候,调整了图形的大小,并不能及时的获取最新的图形面积。
还有,你也可以单独监听特定图形完成后的方法:
事件 | 参数 | 描述 |
---|---|---|
circlecomplete(overlay) | {Circle} | 绘制圆完成后,派发的事件接口 |
markercomplete(overlay) | {Marker} | 绘制点完成后,派发的事件接口 |
overlaycomplete(e) | {Event Object} | 鼠标绘制完成后,派发总事件的接口 |
polygoncomplete(overlay) | {Polygon} | 绘制多边形完成后,派发的事件接口 |
polylinecomplete(overlay) | {Polyline} | 绘制线完成后,派发的事件接口 |
rectanglecomplete(overlay) | {Polygon} | 绘制矩形完成后,派发的事件接口 |
区域编辑
既然已经实现了绘制图形,那么接下来,我们来看看如何实现图形的再编辑。这个功能主要来自于官方这个 DEMO。
通过示例可以看到,调用每个 图形实例 的 .enableEditing() / .disableEditing()
方法就能控制图形的编辑操作。
图形实例 身上的其他方法可以去文档上查阅,重点可以关注画圈圈的几个类。
了解完相关知识后,我们来看看具体的实现过程:
<template>
<!-- 添加一些按钮来控制逻辑 -->
<div class="btns">
<button v-if="!isDescribe && !isDescribeEdit" @click="describe" type="primary">绘制区域</button>
<button v-if="!isDescribe && !isDescribeEdit && drawItemMap.length" @click="describeEdit">开启区域编辑</button>
<template v-if="isDescribe">
<div class="tip">请点击左下角图标进行对应图形的绘制</div>
<button @click="describe">绘制中</button>
</template>
<button v-if="isDescribeEdit" @click="describeEdit">编辑完成</button>
</div>
<div class="map-container">
<div id="mapContainer" class="map"></div>
<!-- 绘制按钮可控 -->
<ul v-show="isDescribe" class="drawing-panel">
<li class="bmap-btn bmap-rectangle" id="rectangle" @click="draw($event, 'rectangle')"></li>
<li class="bmap-btn bmap-polygon" id="polygon" @click="draw($event, 'polygon')"></li>
</ul>
</div>
</template>
<script>
import { defineComponent, onMounted, ref } from 'vue';
export default defineComponent({
setup() {
...
let isDescribe = ref(false); // 是否能描绘
let isDescribeEdit = ref(false); // 是否能编辑
let drawItemMap = ref([]); // 储存每个图形实例: overlay是覆盖物的抽象基类, 所有覆盖物均继承基类的方法, 详情可以查看文档说明
// 重置图标显示状态
function resetIconStatus() {
let btnDoms = document.querySelectorAll('.bmap-btn');
btnDoms.forEach(item => {
item.style.backgroundPositionY = '0';
});
}
function initDrawingManager() {
...
drawingManager.addEventListener('overlaycomplete', (e, instance) => {
isDescribe.value = false; // 绘制完成就隐藏绘制按钮
resetIconStatus(); // 重置按钮的显示状态
drawItemMap.value.push(instance.overlay);
});
}
function initMap () { ... }
function draw(e, id) {
// 重置按钮的显示状态
resetIconStatus();
...
}
onMounted(() => { initMap(); });
return {
draw,
isDescribe,
isDescribeEdit,
drawItemMap,
// 绘制
describe() {
isDescribe.value = !isDescribe.value
if (!isDescribe.value) {
resetIconStatus();
drawingManager.close(); // 关闭绘制功能
}
},
// 编辑
describeEdit() {
isDescribeEdit.value = !isDescribeEdit.value
drawItemMap.value.forEach((item) => {
if (isDescribeEdit.value) {
item.enableEditing();
} else {
item.disableEditing();
}
})
},
};
}
})
</script>
...
.btns{
width: 1200px;
display: flex;
justify-content: right;
margin-bottom: 8px;
}
</style>
上面我们添加了一些按钮来控制整体的逻辑,主要过程就是每次绘制完成后,保存好图形的实例,编辑的时候,遍历所有图形实例然后调用编辑方法即可。
区域面积计算
前面小编讲过计算图形区域的面积,通过 overlaycomplete 等回调方法返回的值并不能满足某些场景,那只能另寻它路了。
先来回想一下,我们现在能获取到图形的实例,那么实例身上都有些啥呢?
这个时候,我们又该翻翻文档了。实例身上并没有能直接获取到面积的方法,但是有个 getPath()
方法,能获取到图形所有点的经纬度坐标。
后面,小编在翻阅 BMapGLLib 库源码中找到另一个工具包 GeoUtils 里面有个方法,能把经纬度坐标转成面积,这不正好嘛。(✪ω✪)
当然,事情肯定没那么简单,看看这注释,还有“意外之喜” ???
看来这个方法还不能直接使用了,又查了一些资料(百度半天),终于找到了一个方法(还是网友牛逼):
<template>
...
<div style="color: red;width:1200px">
<div>区域总面积:{{data.totalArea}}</div>
<div>区域所有点:</div>
<div v-for="(item, index) in data.totalPoint" :key="index">{{item}}</div>
</div>
</template>
<script>
import { defineComponent, onMounted, ref, reactive } from 'vue';
export default defineComponent({
setup() {
...
let data = reactive({
totalArea: '',
totalPoint: []
});
function resetIconStatus() { ... }
// 计算图形面积
function computeSignedArea(path) {
// path: [{lat, lng}, {lat, lng}]
let polarTriangleArea = (tan1, lng1, tan2, lng2) => {
let deltaLng = lng1 - lng2;
let t = tan1 * tan2;
return 2 * Math.atan2(t * Math.sin(deltaLng), 1 + t * Math.cos(deltaLng));
}
let radius = 6371009
let len = path.length;
if (len < 3) return 0;
let total = 0;
let prev = path[len - 1];
let prevTanLat = Math.tan(((Math.PI / 2 - prev.lat / 180 * Math.PI) / 2));
let prevLng = (prev.lng) / 180 * Math.PI;
for (let i in path) {
let tanLat = Math.tan((Math.PI / 2 -
(path[i].lat) / 180 * Math.PI) / 2);
let lng = (path[i].lng) / 180 * Math.PI;
total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
prevTanLat = tanLat;
prevLng = lng;
}
return Math.abs(total * (radius * radius));
}
// 计算所有图形总面积
function calcTotalArea() {
if (drawItemMap.value.length === 0) return data.totalArea = '';
let total = 0;
drawItemMap.value.forEach(item => {
let points = item.getPath();
let area = computeSignedArea(points);
total += area;
});
data.totalArea = (total / 1000 / 1000).toFixed(2) + 'km²';
}
// 获取所有区域的点的经纬度
function getAllPoint() {
let points = [];
drawItemMap.value.forEach(item => {
points.push(item.getPath()); // .getPath() 获取图形所有点的经纬度坐标
})
data.totalPoint = points;
}
function initDrawingManager() {
...
drawingManager.addEventListener('overlaycomplete', (e, instance) => {
isDescribe.value = false;
resetIconStatus();
drawItemMap.value.push(instance.overlay);
getAllPoint(); // 获取所有点
calcTotalArea(); // 计算面积
});
}
function initMap () { ... }
function draw(e, id) { ... }
onMounted(() => { initMap(); });
return {
...
// 编辑
describeEdit() {
isDescribeEdit.value = !isDescribeEdit.value
drawItemMap.value.forEach((item) => {
if (isDescribeEdit.value) {
item.enableEditing();
} else {
item.disableEditing();
}
})
calcTotalArea(); // 编辑完成后,计算面积
getAllPoint();
},
data
};
}
})
</script>
测试了广州几个景点区域面积,这个方法结果还是比较精准的,可用。(◕ˇ∀ˇ◕)
区域重叠判断
既然支持绘制多个图形区域,那么肯定会发生区域绘制重叠的情况,这在业务上产品经理肯定是要挑理的,那我们还得来解决这个问题。
这个问题也挺好解决,因为在阅读工具包 GeoUtils 源码的时候,小编瞄到有这方面相关的 API
提供。
使用方式很简单,我们先直接来看代码:
<script>
import { defineComponent, onMounted, ref, reactive } from 'vue';
export default defineComponent({
setup() {
...
function resetIconStatus() { ... }
function computeSignedArea(path) { ... }
function calcTotalArea() { ... }
function getAllPoint() { ... }
// 判断两多变形是否存在点与区域的包含关系(A的点在B的区域内或B的点在A的区域内)
function isPointInPolygonBidirectional(plyA, plyB) {
const [pA, pB] = [[], []];
plyA.forEach((item) => {
pA.push(new BMapGL.Point(item.lng, item.lat));
});
plyB.forEach((item) => {
pB.push(new BMapGL.Point(item.lng, item.lat));
});
let [a, b] = [false, false];
a = pA.some(item => BMapGLLib.GeoUtils.isPointInPolygon(item, new BMapGL.Polygon(pB)));
if (!a) {
b = pB.some(item => BMapGLLib.GeoUtils.isPointInPolygon(item, new BMapGL.Polygon(pA)));
}
return a || b;
}
// 判断多边形是否重叠
function isPolygonsOverlap(plyA, plyB) {
return isPointInPolygonBidirectional(plyA, plyB);
}
function initDrawingManager() {
...
drawingManager.addEventListener('overlaycomplete', (e, instance) => {
isDescribe.value = false;
resetIconStatus();
// 判断区域是否重叠
let isOverlap = drawItemMap.value.some(item => isPolygonsOverlap(item.getPath(), instance.overlay.getPath()))
if (isOverlap) {
alert('区域不能重叠')
map.removeOverlay(instance.overlay); // 清除图形
} else {
drawItemMap.value.push(instance.overlay);
calcTotalArea();
getAllPoint();
}
});
}
function initMap () { ... }
function draw(e, id) { ... }
onMounted(() => { initMap(); });
return {
...
};
}
})
</script>
GeoUtils.isPointInPolygon(points: Array<Point>, polygon: Polygon)
方法接收两个参数,第一个参数是图形点的经纬度坐标数组,另一个参数是目标多边形对象的实例,这个实例可以通过 Polygon 类来构造。
代码大概思路就是,把一个图形所有点的经纬度坐标,遍历一遍,判断是否是在另一个图形身上。
区域清除
最后,当我们绘制图形区域有误时,又该如何清除呢?同样也是直接调用相关 API
就行啦。
map.removeOverlay(overlay)
:清除某个图形区域,overlay
为图形实例。map.clearOverlays()
:清除地图上所有的区域。
你以为写到这里就完了嘛? 当然还没,还有一丢丢内容。
区域回显
在我们实际的业务中,我们在地图上所绘制的图形区域,一般会提交到后端保存起来,而我们提交的会是图形所有点的经纬度坐标。然后,当我们查看业务详情的时候,肯定是需要把这些图形回显在地图上,那我们应该如何通过点的经纬度坐标来绘制出这些图形呢?
下方小编写了一个绘制多边形的方法:
// 绘制多边形区域
function drawChart(points) {
if (!points || points.length === 0) return;
let ps = points.map(p => new BMapGL.Point(p.lng, p.lat));
let polygon = new BMapGL.Polygon(ps, styleOptions);
map.addOverlay(polygon); // 把图形绘制在地图上
return polygon;
}
更多其他图形的绘制可以看看官方 DEMO 示例,这里需要注意的是绘制圆的 API
需要一个半径值,所以保存的时候,半径也需要保存起来,这需要提前告知后端人员,以免接口设计有错误。
那么,写到这里本篇文章就完结啦,下面再贴上完整的源码,如你有任何疑问,欢迎在评论区留言告知我。
完整源码
<template>
<h1>百度地图-多区域绘制、区域编辑、区域面积计算、区域重叠判断、区域清除</h1>
<div class="btns">
<button v-if="!isDescribe && !isDescribeEdit" @click="describe" type="primary">绘制区域</button>
<button v-if="!isDescribe && !isDescribeEdit && drawItemMap.length" @click="describeEdit">开启区域编辑</button>
<button v-if="!isDescribe && !isDescribeEdit && drawItemMap.length" @click="clearAllOverlay" type="danger">清除所有区域</button>
<template v-if="isDescribe">
<div class="tip">请点击左下角图标进行对应图形的绘制</div>
<button @click="describe">绘制中</button>
</template>
<button v-if="isDescribeEdit" @click="describeEdit">编辑完成</button>
</div>
<div class="map-container">
<div id="mapContainer" class="map"></div>
<ul v-show="isDescribe" class="drawing-panel">
<li class="bmap-btn bmap-rectangle" id="rectangle" @click="draw($event, 'rectangle')"></li>
<li class="bmap-btn bmap-polygon" id="polygon" @click="draw($event, 'polygon')"></li>
</ul>
</div>
<div style="color: red;width:1200px">
<div>区域总面积:{{data.totalArea}}</div>
<div>区域所有点:</div>
<div v-for="(item, index) in data.totalPoint" :key="index">{{item}}</div>
</div>
</template>
<script>
import { defineComponent, onMounted, ref, reactive } from 'vue';
export default defineComponent({
setup() {
let data = reactive({
totalArea: '',
totalPoint: []
});
let map = null;
let drawingManager = null;
let isDescribe = ref(false);
let isDescribeEdit = ref(false);
let drawItemMap = ref([]);
const styleOptions = {
strokeColor: '#e38932',
fillColor: '#e38932',
strokeWeight: 1,
strokeOpacity: 1,
fillOpacity: 0.2
};
const labelOptions = {
borderRadius: '2px',
background: '#FFFBCC',
border: '1px solid #E1E1E1',
color: '#703A04',
fontSize: '12px',
letterSpacing: '0',
padding: '5px'
};
function computeSignedArea(path) {
// path: [{lat:,lng:}, {lat:,lng:}]
let polarTriangleArea = (tan1, lng1, tan2, lng2) => {
let deltaLng = lng1 - lng2;
let t = tan1 * tan2;
return 2 * Math.atan2(t * Math.sin(deltaLng), 1 + t * Math.cos(deltaLng));
}
let radius = 6371009
let len = path.length;
if (len < 3) return 0;
let total = 0;
let prev = path[len - 1];
let prevTanLat = Math.tan(((Math.PI / 2 - prev.lat / 180 * Math.PI) / 2));
let prevLng = (prev.lng) / 180 * Math.PI;
for (let i in path) {
let tanLat = Math.tan((Math.PI / 2 -
(path[i].lat) / 180 * Math.PI) / 2);
let lng = (path[i].lng) / 180 * Math.PI;
total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
prevTanLat = tanLat;
prevLng = lng;
}
return Math.abs(total * (radius * radius));
}
function calcTotalArea() {
if (drawItemMap.value.length === 0) return data.totalArea = '';
let total = 0;
drawItemMap.value.forEach(item => {
let points = item.getPath();
let area = computeSignedArea(points);
total += area;
});
data.totalArea = (total / 1000 / 1000).toFixed(2) + 'km²';
}
function getAllPoint() {
let points = [];
drawItemMap.value.forEach(item => {
points.push(item.getPath());
})
data.totalPoint = points
}
function resetIconStatus() {
let btnDoms = document.querySelectorAll('.bmap-btn');
btnDoms.forEach(item => {
item.style.backgroundPositionY = '0';
});
}
function isPointInPolygonBidirectional(plyA, plyB) {
const [pA, pB] = [[], []];
plyA.forEach((item) => {
pA.push(new BMapGL.Point(item.lng, item.lat));
});
plyB.forEach((item) => {
pB.push(new BMapGL.Point(item.lng, item.lat));
});
let [a, b] = [false, false];
a = pA.some(item => BMapGLLib.GeoUtils.isPointInPolygon(item, new BMapGL.Polygon(pB)));
if (!a) {
b = pB.some(item => BMapGLLib.GeoUtils.isPointInPolygon(item, new BMapGL.Polygon(pA)));
}
return a || b;
}
function isPolygonsOverlap(plyA, plyB) {
return isPointInPolygonBidirectional(plyA, plyB);
}
function initDrawingManager() {
drawingManager = new BMapGLLib.DrawingManager(map, {
enableCalculate: true,
enableSorption: true,
sorptiondistance: 20,
enableGpc: false,
enableLimit: true,
limitOptions: {
area: 500000000,
distance: 300000
},
circleOptions: styleOptions,
polylineOptions: styleOptions,
polygonOptions: styleOptions,
rectangleOptions: styleOptions,
labelOptions: labelOptions,
});
drawingManager.addEventListener('overlaycomplete', (e, instance) => {
isDescribe.value = false;
resetIconStatus();
let isOverlap = drawItemMap.value.some(item => isPolygonsOverlap(item.getPath(), instance.overlay.getPath()))
if (isOverlap) {
alert('区域不能重叠')
map.removeOverlay(instance.overlay);
} else {
drawItemMap.value.push(instance.overlay);
calcTotalArea();
getAllPoint();
}
});
}
function initMap () {
return new Promise(resolve => {
map = new BMapGL.Map('mapContainer', {
minZoom: 1,
maxZoom: 20,
enableMapClick: false
});
map.centerAndZoom(new BMapGL.Point(116.404, 39.915), 15);
map.enableScrollWheelZoom(true);
initDrawingManager();
map.addEventListener('tilesloaded', () => {
resolve();
});
});
}
function draw(e, id) {
e.target.style.backgroundPositionY = '-52px';
let drawingType = '';
switch (id) {
case 'polyline':
drawingType = 'polyline'; break;
case 'rectangle':
drawingType = 'rectangle'; break;
case 'polygon':
drawingType = 'polygon'; break;
case 'circle':
drawingType = 'circle'; break;
}
if (drawingManager._isOpen && drawingManager.getDrawingMode() === drawingType) {
drawingManager.close();
} else {
drawingManager.setDrawingMode(drawingType);
drawingManager.open();
}
}
onMounted(() => {
initMap();
})
return {
data,
isDescribe,
isDescribeEdit,
drawItemMap,
draw,
describe() {
isDescribe.value = !isDescribe.value
if (!isDescribe.value) {
resetIconStatus();
drawingManager.close();
}
},
describeEdit() {
isDescribeEdit.value = !isDescribeEdit.value
drawItemMap.value.forEach((item) => {
if (isDescribeEdit.value) {
item.enableEditing();
} else {
item.disableEditing();
}
})
calcTotalArea();
getAllPoint();
},
clearAllOverlay() {
if (confirm("您确定清除当前地图上所绘制的区域吗?")) {
map.clearOverlays();
drawItemMap.value = [];
calcTotalArea();
getAllPoint();
}
},
};
}
})
</script>
<style scoped>
.map-container{
width: 1200px;
height: 550px;
position: relative;
margin-bottom: 10px
}
.map{
width: 100%;
height: 100%;
}
.drawing-panel {
z-index: 999;
position: absolute;
bottom: 3.5rem;
margin-left: 2.5rem;
padding-left: 0;
border-radius: .25rem;
height: 47px;
box-shadow: 0 2px 6px 0 rgba(27, 142, 236, 0.5);
list-style: none;
}
.bmap-btn {
border-right: 1px solid #d2d2d2;
float: left;
width: 64px;
height: 100%;
background-image: url(//api.map.baidu.com/library/DrawingManager/1.4/src/bg_drawing_tool.png);
cursor: pointer;
}
.drawing-panel .bmap-marker {
background-position: -65px 0;
}
.drawing-panel .bmap-polyline {
background-position: -195px 0;
}
.drawing-panel .bmap-rectangle {
background-position: -325px 0;
}
.drawing-panel .bmap-polygon {
background-position: -260px 0;
}
.drawing-panel .bmap-circle {
background-position: -130px 0;
}
.btns{
width: 1200px;
display: flex;
justify-content: right;
margin-bottom: 8px;
}
</style>
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。 老样子,点赞+评论=你会了,收藏=你精通了。
转载自:https://juejin.cn/post/7088560838851690510