vue2+高德地图实现绘制多边形电子围栏
文章如有错漏之处,还望不吝赐教,诸君共勉
文章末尾会贴出所有代码,有注释,可以直接去看
不行,我高估csdn了,他这个代码块一次粘贴不下,相信各位都有这个拼接的能力
嫌麻烦可以直接下资源
查询新增逻辑介绍
1.页面打开时查询已录入的电子围栏
----1.1.创建围栏实例并将表单信息塞入实例,挨个add到地图里
2.点击右上角绘制按钮绘制电子围栏
----2.1.双击左键或单机右键结束绘制,并显示信息录入表单
----2.2.禁用绘制按钮
----2.3.将信息录入完整后点击保存按钮调用新增接口
--------2.3.1.新增接口返回成功后创建高德地图Text实例,获取围栏中心 点后,将Text实例根据中心点加入地图
--------2.3.2.将表单信息塞入围栏实例中并关闭信息录入表单,解除绘制按钮禁用
----2.4.点击取消将电子围栏从地图上删除并关闭信息录入表单,解除绘制按钮禁用
接下来是编辑删除逻辑介绍
1.双击电子围栏进入编辑状态,将围栏携带的表单数据赋值给信息录入表单并显示
2.修改完后点击保存按钮
----2.1.接口返回后将text实例更新,并关闭编辑状态和信息录入表单
3.信息录入表单显示删除按钮
----3.1.删除后将围栏实例和Text实例从地图移除,并关闭信息录入表单
高德地图文档地址
https://lbs.amap.com/api/javascript-api-v2/guide/abc/amap-vue
下载依赖
npm i @amap/amap-jsapi-loader --save
在main.js文件中加一个高德地图key全局变量
Vue.prototype.$aMapKey = "你在高德开放平台申请的key";// 高德地图key
登录高德开放平台在浏览器地址栏里粘贴下面地址
https://console.amap.com/dev/key/app
添加Key
然后选择 Web端(JS API)
然后在vue文件中的 script 标签下引入高德地图的加载方法
import AMapLoader from '@amap/amap-jsapi-loader';
在 template 标签中添加一个 div 标签作为地图容器
切记一定要 给一个确定的宽高
经过计算的宽高会导致地图无法加载,例如css中的 calc()
我是在父容器给的
给标签设置id的时候要注意 整个项目的id不能有重复的 因为vue的节点是虚拟的,给两个一样的id可能会导致地图加载失败,echarts同理
<div v-loading="loading" id="bzdContainer" class="am-map" style="width:100%;height: 100%;"></div>
给地图加个搜索的控件,不需要可以跳过
~~ ~~
当初找这个搜索控件找了好久,裂开,索性一起说了吧
我直接掏他档间一个绝对定位给它扔到左上角
input一定要加 type=“text”
<div class="sarch-city">
地图搜索:
<input id="pickerInput" type="text" />
</div>
~~ ~~
接下来就是js环节了,啊……忘了一步
给页面加一个性感的绘制按钮
disabled是防止在绘制完成后录入表单时用户想不开再次点击绘制按钮
<el-button class="draw" type="success" icon="el-icon-edit" :disabled="isHave" circle @click="drawPolygon" ></el-button>
再来个绘制完后的表单录入
这个颜色更改做出来了,但是领导说没必要,我就给注了,一会儿顺便说一下
<div v-show="showPolygonForm" class="form-div" >
<el-form ref="polygonForm" :model="polygonForm" size="mini" label-width="90px">
<el-form-item label="停车场名称">
<el-input v-model="polygonForm.name"></el-input>
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="polygonForm.lotPeople"></el-input>
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="polygonForm.lotPhone"></el-input>
</el-form-item>
<!-- <el-form-item label="停车场颜色">
<el-color-picker v-model="polygonForm.color" size="mini" @change="setNowColor"></el-color-picker>
</el-form-item> -->
</el-form>
<!-- 新增/编辑 操作按钮-->
<div class="form-footer" >
<div>
<el-button v-loading="saveLoading" type="primary" size="mini" @click="submit">保存</el-button>
<el-button size="mini" @click="cancel" >取消</el-button>
<el-button v-loading="saveLoading" v-show="!isNew" type="danger" size="mini" @click="doDelLot">删除</el-button>
</div>
</div>
</div>
接下来就是js环节了
首先 return 定义一下
// 地图loading
loading: false,
// 按钮loading
saveLoading: false,
// 显示电子停车场表单
showPolygonForm: false,
// 是否为新增
isNew: false,
// 是否正在绘制
isHave: false,
// 地图
map: null,
// 编辑工具
polyEditor: null,
// 电子停车场数据集
polygonArr: [],
// 电子停车场label数据集
labelArr: [],
// 当前电子停车场
nowPolygon: null,
// 当前电子停车场label
nowLabel: null,
// 电子停车场搜索表单
searchForm: {
fenceName: '', // 电子停车场名称
lotPeople: '', // 电子停车场类型
lotPhone: '', // 电子停车场类型
},
// 电子停车场弹窗表单
polygonForm: {
id: null,
name: '', // 电子停车场名称
lotPeople: '', // 联系人
lotPhone: '', // 联系电话
lnglat: '', //经纬度
// color: '#409EFF'
},
然后方法
看注释吧…… 旁边有个傻gay后台一直搞我心态
// 获得图形的中心点
getCenterPoint(path) {
let x = 0.0;
let y = 0.0;
for (let i = 0; i < path.length; i++) {
x = x + parseFloat(path[i].lng);
y = y + parseFloat(path[i].lat);
}
x = x / path.length;
y = y / path.length;
return new AMap.LngLat(x, y);
},
// 电子停车场加label
showText(polygon, name) {
var point = this.getCenterPoint(polygon.getPath()); //获得中心点
var label = new AMap.Text({
text: name,
anchor: 'center', // 设置文本标记锚点
style: {
'text-shadow': 'white 1px 0 0, white 0 1px 0, white -1px 0 0, white 0 -1px 0',
'background-color': 'transparent',
'font-weight': 700,
'border-width': 0,
'text-align': 'center',
'font-size': '14px',
'color': '#409EFF',
},
position: point
});
// 将Text实例设入地图
label.setMap(this.map);
return label;
},
// 初始化地图
initAMap() {
const _this = this
// 加载高德地图
AMapLoader.load({
key: _this.$aMapKey, //设置高德地图申请的key
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.ToolBar', 'AMap.PolygonEditor'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
AMapUI: {
version: "1.1",
plugins: []
},
Loca: {
version: "2.0"
},
}).then(async (AMap) => {
_this.map = await new AMap.Map("bzdContainer", { //设置地图容器id
viewMode: "2D", // 默认使用 2D 模式
mapStyle: 'amap://styles/whitesmoke', //设置地图的显示样式
zoom: 10 //初始化地图层级
});
// 地图搜索控件
new AMapUI.loadUI(['misc/PoiPicker'], (PoiPicker) => {
// 根据input的id创建实例
const poiPicker = new PoiPicker({
input: 'pickerInput'
});
//用户选中的poi点信息
poiPicker.on('poiPicked', function(poiResult) {
// 设置地图中心点
_this.map.setCenter(poiResult.item.location);
});
});
// 图层样式切换控件(卫星图)
AMapUI.loadUI(['control/BasicControl'], function(BasicControl) {
//图层切换控件
_this.map.addControl(new BasicControl.LayerSwitcher({
position: 'rb' // 右下角
}));
});
// 初始化编辑插件
_this.polyEditor = await new AMap.PolygonEditor(_this.map)
_this.polyEditor._opt.createOptions = { // 创建区域的样式
fillColor: "#409EFF", // 多边形填充颜色,使用16进制颜色代码赋值,如:#00B2D5
fillOpacity: 0.3, // 多边形填充透明度,取值范围 [0,1] ,0表示完全透明,1表示不透明。默认为0.5
strokeWeight: 3, // 轮廓线宽度
strokeColor: "#409EFF", // 线条颜色,使用16进制颜色代码赋值。默认值为#00D3FC
strokeOpacity: 1, // 轮廓线透明度,取值范围 [0,1] ,0表示完全透明,1表示不透明。默认为0.5
};
_this.polyEditor._opt.editOptions = {// 编辑区域的样式
fillColor: "#409EFF",
fillOpacity: 0.3,
strokeWeight: 3,
strokeColor: "#409EFF",
strokeOpacity: 1,
};
//多边形添加
_this.polyEditor.on('add', function (data) {
// 获取电子停车场参数
var polygon = data.target;
_this.nowPolygon = polygon
_this.showPolygonForm = true
// 获取给停车场设置标题 不能给空字符串,不然会创建失败,给个空格可以实现新创建的停车场没有标题效果
let label = _this.showText(polygon, " ");
_this.nowLabel = label
// 加个双击监听 双击图层进行编辑
polygon.on('dblclick', () => {
if (_this.polyEditor) {
// 将触发的围栏实例和Text实例拿出来存好
_this.nowPolygon = polygon
_this.nowLabel = label
// 打开信息录入表单
_this.showPolygonForm = true
// 禁用绘制按钮
_this.isHave = true
// 不是新增
_this.isNew = false
// 将围栏携带的数据复制到表单中
_this.polygonForm.id = polygon._opts.extData.mapId
_this.polygonForm.name = polygon._opts.extData.fenceName
_this.polygonForm.lotPeople = polygon._opts.extData.lotPeople
_this.polygonForm.lotPhone = polygon._opts.extData.lotPhone
// 给编辑器设定要编辑的围栏实例
_this.polyEditor.setTarget(polygon);
// 打开编辑器
_this.polyEditor.open();
}
})
})
// 获取停车场数据
_this.getLotData()
}).catch(e => {
console.log(e);
})
},
// 获取电子停车场数据并渲染
async getLotData() {
this.loading = true
// 先清空地图覆盖物
await this.map.clearMap()
let dealPolygonArr = []
let dealLabelArr = []
this.polygonArr = []
this.polygonArr = []
// 调用后台接口获得一堆携带经纬度的围栏数据
const polygonRes = await listLot(this.searchForm)
if (polygonRes.code === 200) {
// 循环创建围栏实例
for (let item of polygonRes.rows) {
// 字符串转数字(这方法原来是用来计算的)
let path = eval(item.fencePoint)
let fence = new AMap.Polygon({
path: path, // 经纬度
fillColor: "#409EFF",
fillOpacity: 0.3,
strokeWeight: 3,
strokeColor: "#409EFF",
strokeOpacity: 1,
zIndex: 1, // 围栏层级
extData: { // 额外携带参数
mapId: item.id,
fenceName: item.fenceName,
lotPeople: item.lotPeople,
lotPhone: item.lotPhone,
}
})
// 创建Text实例
let label = this.showText(fence, item.fenceName);
// 这里生成的围栏也得加双击监听 双击图层进行编辑
fence.on('dblclick', () => {
if (this.polyEditor) {
this.nowPolygon = fence
this.nowLabel = label
this.showPolygonForm = true
this.isHave = true
this.isNew = false
this.polygonForm.id = fence._opts.extData.mapId
this.polygonForm.name = fence._opts.extData.fenceName
this.polygonForm.lotPeople = fence._opts.extData.lotPeople
this.polygonForm.lotPhone = fence._opts.extData.lotPhone
this.polyEditor.setTarget(fence);
this.polyEditor.open();
}
})
// 将电子停车场存入集合
dealPolygonArr.push(fence)
this.polygonArr = dealPolygonArr
// 将电子停车场label存入集合
dealLabelArr.push(label)
this.labelArr = dealLabelArr
}
// 地图上批量添加围栏实例
this.map.add(this.polygonArr);
// 缩放地图到合适的视野级别 setFitView传入什么就根据什么缩放
// 什么都不传就根据地图上所有覆盖物进行缩放
this.map.setFitView(this.polygonArr)
this.loading = false
} else {
Message({
type:"warning",
message: polygonRes.msg
})
this.loading = false
}
},
// 绘制电子停车场
drawPolygon() {
// 禁用绘制按钮
this.isHave = true
// 新增状态
this.isNew = true
// 关闭一下编辑器,防止出bug
this.polyEditor.close();
// 置空编辑区即为新增
this.polyEditor.setTarget();
// 打开编辑器即可开始绘制
this.polyEditor.open();
},
// 保存电子停车场
async submit() {
this.saveLoading = true
// 验证必填
if (this.polygonForm.name === null || this.polygonForm.name === '') {
Message({
type:"warning",
message: "请输入电子停车场名称"
})
this.saveLoading = false
return
}
// 获取电子围栏的所有点
const arr = await this.nowPolygon.getPath()
const pathArr = []
// 将获取的停车场坐标点转换格式后存入数组
for (let item of arr) {
const list = [item.lng, item.lat]
pathArr.push(list)
}
// 将坐标点集合转为json字符串
const pointString = await JSON.stringify(pathArr)
let obj = {
id: this.polygonForm.id,
fenceName: this.polygonForm.name,
lotPeople: this.polygonForm.lotPeople,
lotPhone: this.polygonForm.lotPhone,
fencePoint: pointString,
// fenceColor: this.polygonForm.color
}
let response = null
// 判断是否为新增电子停车场
if (this.isNew) {
response = await addLot(obj)
} else {
response = await updateLot(obj)
}
// 后台新增接口返回成功时
if(response.code === 200) {
// 关闭编辑器
this.polyEditor.close();
// 新增成功后将电子停车场名称设置到地图
if(this.isNew) {
// 将新增的围栏加入围栏数据集中
this.polygonArr.push(this.nowPolygon)
// 将输入的标题设置到当前Text实例里
this.nowLabel.setText(response.data.fenceName)
// 设置围栏携带参数
this.nowPolygon._opts.extData = {
mapId: response.data.id,
fenceName: response.data.fenceName,
lotPeople: response.data.lotPeople,
lotPhone: response.data.lotPhone,
}
} else {
// 将输入的标题设置到当前Text实例里
this.nowLabel.setText(obj.fenceName)
// 更新围栏携带参数
this.nowPolygon._opts.extData = {
mapId: obj.id,
fenceName: obj.fenceName,
lotPeople: obj.lotPeople,
lotPhone: obj.lotPhone,
}
}
// 清空表单
this.polygonForm.id = null
this.polygonForm.name = ""
this.polygonForm.lotPeople = ""
this.polygonForm.lotPhone = ""
// 解除按钮禁用
this.isHave = false
this.isNew = false
// 关闭信息录入表单
this.showPolygonForm = false
this.saveLoading = false
Message({
type: "success",
message: response.msg
})
} else {
Message({
type:"warning",
message: response.msg
})
this.saveLoading = false
}
},
// 取消电子停车场绘制
cancel() {
// 如果是新增则删除刚增加的电子围栏
if (this.isNew) {
this.map.remove(this.nowPolygon)
}
// 关闭编辑器、关闭信息录入表单、重置表单
this.polyEditor.close();
this.showPolygonForm = false
this.isHave = false
this.polygonForm.id = null
this.polygonForm.name = ""
this.polygonForm.lotPeople = ""
this.polygonForm.lotPhone = ""
this.polygonForm.type = null
},
// 删除单子停车场
async doDelLot() {
this.saveLoading = true
const response = await delLot(this.polygonForm.id)
// 等删除接口返回成功时
if (response.code === 200) {
// 遍历围栏集合将删除的围栏删除
for (let i = 0; i < this.polygonArr.length; i++) {
const element = this.polygonArr[i];
if(this.nowPolygon === this.polygonArr[i]){
this.polygonArr.splice(i,1)
break
}
}
// 在地图删除围栏及标题
this.map.remove(this.nowPolygon)
this.map.remove(this.nowLabel)
// 关闭编辑器、关闭信息录入表单、重置表单
this.polyEditor.close();
this.showPolygonForm = false
this.isHave = false
this.saveLoading = false
this.polygonForm.id = null
this.polygonForm.name = ""
this.polygonForm.lotPeople = ""
this.polygonForm.lotPhone = ""
this.polygonForm.type = null
Message({
type: "success",
message: response.msg
})
} else {
this.saveLoading = false
Message({
type: "warning",
message: response.msg
})
}
}
如果我没有满足你,你可以去文档里找找看能不能实现
文档地址:https://lbs.amap.com/api/jsapi-v2/summary/
打开网址后点击参考手册搜索下方关键词
标题实例是 Text
多边形实例是 Polygon
用的编辑器是 PolygonEditor
地图搜索文档地址:https://lbs.amap.com/api/amap-ui/reference-amap-ui/other/poipicker
完活,开始补代码
首先是template
<div :style="{height: pheight + 'px'}" class="box">
<!-- 地图 -->
<div v-loading="loading" id="bzdContainer" class="am-map" style="width:100%;height: 100%;"></div>
<div class="sarch-city">
地图搜索:
<input id="pickerInput" type="text" />
</div>
<!-- 电子停车场搜索表单 -->
<div class="search-form" >
<el-form ref="searchForm" :model="searchForm" size="mini" style="display: flex;" label-width="90px">
<el-form-item label="停车场名称">
<el-input v-model="searchForm.fenceName" clearable></el-input>
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="searchForm.lotPeople" clearable></el-input>
</el-form-item>
<el-form-item label="联系人电话">
<el-input v-model="searchForm.lotPhone" clearable></el-input>
</el-form-item>
<el-button type="primary" size="mini" style="margin-left: 10px;" @click="getLotData">搜索</el-button>
<el-button size="mini" style="margin-left: 10px;" @click="getLotData">重置</el-button>
</el-form>
</div>
<!-- 控制按钮组 -->
<div class="map-button">
<!-- 绘制电子停车场 -->
<el-button class="draw" type="success" icon="el-icon-edit" :disabled="isHave" circle @click="drawPolygon" ></el-button>
</div>
<!-- 停车场表单 -->
<div v-show="showPolygonForm" class="form-div" >
<el-form ref="polygonForm" :model="polygonForm" size="mini" label-width="90px">
<el-form-item label="停车场名称">
<el-input v-model="polygonForm.name"></el-input>
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="polygonForm.lotPeople"></el-input>
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="polygonForm.lotPhone"></el-input>
</el-form-item>
<!-- <el-form-item label="停车场颜色">
<el-color-picker v-model="polygonForm.color" size="mini" @change="setNowColor"></el-color-picker>
</el-form-item> -->
</el-form>
<!-- 新增/编辑 操作按钮-->
<div class="form-footer" >
<div>
<el-button v-loading="saveLoading" type="primary" size="mini" @click="submit">保存</el-button>
<el-button size="mini" @click="cancel" >取消</el-button>
<el-button v-loading="saveLoading" v-show="!isNew" type="danger" size="mini" @click="doDelLot">删除</el-button>
</div>
</div>
</div>
</div>
OKOK 全体目光看向我 我宣布个事 接下来是script
methods里的方法我已经在上面放了,是连着的,稍微往上找找,很明显的
import AMapLoader from '@amap/amap-jsapi-loader';
import { Message } from "element-ui"
export default {
data() {
return {
// 地图loading
loading: false,
// 按钮loading
saveLoading: false,
// 显示电子停车场表单
showPolygonForm: false,
// 是否为新增
isNew: false,
// 是否正在绘制
isHave: false,
// 地图
map: null,
// 编辑工具
polyEditor: null,
// 电子停车场数据集
polygonArr: [],
// 电子停车场label数据集
labelArr: [],
// 当前电子停车场
nowPolygon: null,
// 当前电子停车场label
nowLabel: null,
// 电子停车场搜索表单
searchForm: {
fenceName: '', // 电子停车场名称
lotPeople: '', // 电子停车场类型
lotPhone: '', // 电子停车场类型
},
// 电子停车场弹窗表单
polygonForm: {
id: null,
name: '', // 电子停车场名称
lotPeople: '', // 联系人
lotPhone: '', // 联系电话
lnglat: '', //经纬度
// color: '#409EFF'
},
// 地图高度
pheight: Number(document.body.clientHeight - 85),
}
},
created() {
},
mounted() {
// 调用初始化地图函数
this.initAMap();
// 监听页面尺寸变化
window.addEventListener(
"resize",
() => {
this.pheight = Number(document.body.clientHeight - 85);
},
false
);
},
接下来 style~
<style scoped>
.box{
position: relative;
}
.sarch-city{
text-shadow: white 1px 0 0, white 0 1px 0, white -1px 0 0, white 0 -1px 0;
font-size: 14px;
color: #606266;
font-weight: 700;
position: absolute;
height: 20px;
left: 20px;
top: 18px;
}
::v-deep .el-form-item__label {
text-shadow: white 1px 0 0, white 0 1px 0, white -1px 0 0, white 0 -1px 0;
}
.map-button{
position: absolute;
top: 10px;
right: 10px;
z-index: 1;
}
.search-form{
position: absolute;
/* background-color: white; */
padding: 15px;
border-radius: 10px;
top: 0px;
left: 250px;
z-index: 1;
}
.search-form /deep/ .el-form-item {
margin-bottom: 0px !important;
}
.form-div{
position: absolute;
background-color: white;
box-shadow:0 0 10px #909399;
padding: 15px;
border-radius: 10px;
top: 10px;
right: 50px;
z-index: 1;
}
.form-footer{
display: flex;
justify-content: center;
}
.am-map /deep/ .amap-logo{
display: none !important;
}
.am-map /deep/ .amap-copyright{
display: none !important;
}
</style>
行,完事了,都散了吧,有时可以留言,我如果记起来我在csdn有号的话会看的