赞
踩
之前,公司需要我来开发项目中的围栏管理功能。业务需求也不是太难,就是实现在百度地图上使用百度地图工具来绘制一块区域,给该区域添加项目相关的区域信息,然后保存到数据库,另外实现对保存到数据库的围栏的增删改查功能。
因为公司的项目是一个基于vue的前后端分离的项目。所以开发这一功能时,我一开始想到尝试网上有没有关于百度地图的插件可以使用的。我找到了
vue-baidu-map 这一插件。由于没有太多时间来了解这一插件,便pass了。但是开发者们可以去了解一下。
使用百度地图插件的案例 : https://www.cnblogs.com/WorkingBoy/p/15424905.html 可以点击看看这位博主的案例。
为了学习百度地图引入vue项目,我选择了最原始的方法,通过手动js引入。
下面是js代码 : 可以命名为 bmpjl.js。
const ak = '你的密钥' // 百度的地图密钥 /** * 异步加载百度地图 * @returns {Promise} * @constructor */ function loadBaiDuMap() { return new Promise(function (resolve, reject) { try { console.log('BMap is defined:', BMapGL === undefined || BMapGL) resolve(BMapGL) } catch (err) { window.init = function () { resolve(BMapGL) } let script = document.createElement('script') script.type = 'text/javascript' script.src = `http://api.map.baidu.com/api?v=1.0&type=webgl&ak=${ak}&callback=init` script.onerror = reject document.body.appendChild(script) } }) } export { loadBaiDuMap } /** * 异步加载百度地图,以及绘制工具 * @returns {Promise} * @constructor */ function loadBaiDuDrawMap() { return loadBaiDuMap().then(BMapGL => { let loaded = false try { loaded = (BMapGLLib && BMapGLLib.DrawingManager) } catch (err) { loaded = false } if (!loaded) { console.log('BMapLib.DrawingManager loading!') let script = document.createElement('script') script.type = 'text/javascript' script.src = 'http://mapopen.cdn.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.js' document.body.appendChild(script) let link = document.createElement('link') link.rel = 'stylesheet' link.href = 'http://mapopen.cdn.bcebos.com/github/BMapGLLib/DrawingManager/src/DrawingManager.min.css' document.body.appendChild(link) } else { console.log('BMapLib.DrawingManager is loaded!') } return BMapGL }) } export { loadBaiDuDrawMap }
引入百度地图前,你需要去网上搜索百度地图官方文档 ,即 百度地图开发者平台 链接地址是 https://www.baidu.com/link?url=hY2nl1O5PMifStkUFQsDmEbycTTly1JfAarPlyzFc7ICq-Mn4oWR8JWsQp0Wy3dL&wd=&eqid=f47ede5a0003ee7600000006629185d0
进入官网后,选择控制台菜单 -> 应用管理 -> 我的应用 .
点击创建应用 输入应用名称 选择安排ap[服务等,提交即可获得一个自己的密钥。
开发者们也可以通过官方开发文档一步步学习,来创建自己的地图。文档里面除了 js方向,还有小程序,android方向等开发文档。
下面是在页面中引入之前的js文件,展示地图以及围栏管理功能的源码:这里我命名为weilan.vue。
<template> <div class="home" style="width: 100%; height: 100%"> <!-- BMapGLLib 控件,这里自己去优化一下吧,写成循环吧 ,,懒得写了 --> <!-- <ul class="drawing-panel"> <li class="bmap-btn bmap-marker" @click="draw('marker')" :style="{ 'background-position-y': actNav === 'marker' ? '-52px' : '0px'}"></li> <li class="bmap-btn bmap-polyline" @click="draw('polyline')" :style="{ 'background-position-y': actNav === 'polyline' ? '-52px' : '0px' }"></li> <li class="bmap-btn bmap-rectangle" @click="draw('rectangle')" :style="{ 'background-position-y': actNav === 'rectangle' ? '-52px' : '0px' }"></li> <li class="bmap-btn bmap-polygon" @click="draw('polygon')" :style="{'background-position-y': actNav === 'polygon' ? '-52px' : '0px'}"></li> <li class="bmap-btn bmap-circle" @click="draw('circle')" :style="{'background-position-y': actNav === 'circle' ? '-52px' : '0px'}"></li> </ul> --> <div class="search"> <a-input v-model="queryParams.unitname" maxlength="250" :allowClear="true" style="width: 60%" placeholder="请输入单位名称" ></a-input> <a-button style="width: 30px; margin-left: 20px" type="primary" shape="circle" icon="search" @click="search()" /> </div> <ul class="drawing-panel"> <li :key="item.id" v-for="item in mapToolbar"> <a href="javascript:;"> <my-icon :type="item.icon" class="tool-icon" @click="item.action === 'polygon' ? draw(item.action) : ''" ></my-icon> </a> </li> <!-- <li> <a href="javascript:;"><my-icon type="icon-sousuo" class="tool-icon" @click="search"></my-icon></a> </li> --> </ul> <!-- 地图容器 --> <div id="container" class="mymap" @ready="handler"></div> <div id="r-result" class="minput"> <label for="">请输入:</label> <a-input id="suggestCity" v-model="address" maxlength="250" :allowClear="true" style="width: 60%; margin-left: 20px" placeholder="请输入你想搜索的地点" /> </div> <div id="searchResultPanel" style=" border: 1px solid #c0c0c0; width: 150px; height: auto; display: none; " ></div> <!-- 新增覆盖物 --> <add-overlay ref="addoverlay" @addoverlay="addLocalOverlay" @canceloverlay="cancelOverlay" ></add-overlay> </div> </template> <script> import { i, log } from "mathjs"; // 引入异步引入地图的方法 import { loadBaiDuDrawMap } from "./bmpgl"; import AddOverlay from "./Modals/AddOverlay.vue"; //围栏管理后台接口 import { sysAreaApi as Sapi } from "@/api"; //地图工具栏 const mapToolbar = [ { id: 0, title: "正方形", icon: "icon-checkbox", action: "rectangle", }, { id: 1, title: "多边形", icon: "icon-duobianxing", action: "polygon", }, { id: 2, title: "圆圈", icon: "icon-big-circle", action: "circle", }, ]; export default { name: "mapCom", components: { AddOverlay, }, data() { return { map: null, actNav: "", myValue: "", mapToolbar, //绘制对象 _drawingManager: null, //页面存储覆盖物的数组 overlays: [], //查询地点 address: "", //查询参数 queryParams: {}, //列表中获取到的点数组 localOverlays: [], loadData: (parameter) => { var _this = this; let searchArr = [], queryRules = { unitname: "eq", }, defaultOperator = "eq"; for (let [key, value] of Object.entries(this.queryParams)) { if (value || value == 0) { searchArr.push({ column: key, value: value, type: queryRules[key] || defaultOperator, }); } } searchArr.length && (parameter.search = JSON.stringify(searchArr)); return this.$api .get(Sapi.list, parameter) .then((res) => { if (res.success) { _this.$nextTick(() => { _this.localOverlays = res.data.records; _this.$forceUpdate(); if (_this.localOverlays.length > 2) { return; } else { _this.drawSearchResult(); } }); // return res.data; } else { return this.getListError(res.msg); } }) .catch((err) => { return this.getListError(err); }); }, }; }, mounted() { this.initMap(); }, methods: { handler({ BMapGL, map }) { this.map = map; }, getListError(msg) { this.$message.warning(msg); this.localOverlays = []; }, initMap() { // 初始化地图 loadBaiDuDrawMap().then((BMapGL) => { // 创建地图实例 var _this = this; let map = new BMapGL.Map("container", { minZoom: 4, maxZoom: 22 }); //添加地图平移缩放控件 // map.addControl(new BMapGL.NavigationControl()); // 添加比例尺控件 map.addControl( new BMapGL.ScaleControl({ anchor: BMAP_ANCHOR_BOTTOM_LEFT, offset: new BMapGL.Size(10, 10), }) ); // 添加缩放控件 map.addControl( new BMapGL.ZoomControl({ anchor: BMAP_ANCHOR_BOTTOM_RIGHT, offset: new BMapGL.Size(10, 10), }) ); // 创建点坐标 axios => res 获取的初始化定位坐标 const point = new BMapGL.Point(114.031761, 22.542826); // 初始化地图,设置中心点坐标和地图级别 map.centerAndZoom(point, 19); //开启鼠标滚轮缩放 map.enableScrollWheelZoom(true); //设置地图3d视角 // map.setHeading(64.5); // map.setTilt(73); this.$nextTick(() => { // 保存地图 this.map = map; }); this.getLocation(); this.accuarySearch(); this.$nextTick(() => { const overlays = this.map.getOverlays(); this.refresh(); }); }); }, //获取当前地理位置 将地图中心点移动到定位位置 getLocation() { var _this = this; var geolocation = new BMapGL.Geolocation(); //创建定位对象 geolocation.getCurrentPosition( function (r) { //通过定位对象调用定位函数,回调函数形参r表示定位结果 if (this.getStatus() == BMAP_STATUS_SUCCESS) { //如果定位成功 var mk = new BMapGL.Marker(r.point); //创建标记,r是定位结果,r.point就是当前定位的地点 _this.$nextTick(() => { _this.map.addOverlay(mk); //将标记对象添加到地图上 _this.map.panTo(r.point); //将地图中心店移动到定位地点 }); } else { alert("定位失败!"); } }, { enableHighAccuracy: true } ); }, draw(type) { this.actNav = type; //设置开始绘图时的线条配置 const styleOptions = { strokeColor: "#5E87DB", // 边线颜色 fillColor: "#5E87DB", // 填充颜色。当参数为空时,圆形没有填充颜色 strokeWeight: 2, // 边线宽度,以像素为单位 strokeOpacity: 1, // 边线透明度,取值范围0-1 fillOpacity: 0.2, // 填充透明度,取值范围0-1 }; // 实例化鼠标绘制工具 const drawingManager = new BMapGLLib.DrawingManager(this.map, { // isOpen: true, // 是否开启绘制模式 enableCalculate: false, // 绘制是否进行测距测面 enableSorption: true, // 是否开启边界吸附功能 sorptiondistance: 20, // 边界吸附距离 rectangleOptions: styleOptions, // 矩形的样式 }); this._drawingManager = drawingManager; if (drawingManager._isOpen && drawingManager.getDrawingMode() === type) { drawingManager.close(); } else { drawingManager.setDrawingMode(type); drawingManager.open(); } //设置覆盖物绘制完成监听事件 this._drawingManager.addEventListener( "overlaycomplete", this.overlayComplete.bind(this._drawingManager) ); }, //绘制完成事件处理 overlayComplete(e) { console.log(this._drawingManager); //添加一个新的覆盖物到覆盖物数组中 this.overlays.push(e.overlay); // //获取绘制类型 // let drawingMode = e.drawingMode; //边界点 let path = e.overlay.getPath(); //打开模态框 this.$refs.addoverlay.openModal(e.overlay, path); // if (drawingModeOperate[drawingMode]) drawingModeOperate[drawingMode](e) // this._drawingManager.close(); //关闭绘图 //给所有的覆盖物添加删除覆盖物的监听事件 for (let over = 0; over < this.overlays.length; over++) { //添加点击事件 this.overlays[over].addEventListener( "click", this.removeOverlay.bind(this.overlays[over]) ); //添加鼠标经过事件 this.overlays[over].addEventListener( "mouseover", this.overOverlay.bind(this.overlays[over]) ); //添加鼠标离开事件 this.overlays[over].addEventListener( "mouseout", this.outOverlay.bind(this.overlays[over]) ); } }, //删除围栏 removeOverlay(e) { var _this = this; this.$confirm({ title: "提示", content: "确定删除这个围栏吗?", onOk() { _this.$api.delete(Sapi.del + "/" + e.target.unitid).then((res) => { if (res.success) { _this.map.removeOverlay(e.target); _this.map.clearOverlays() _this.$message.success(res.msg); } else { _this.$message.error(res.msg); } }); }, onCancel() {}, }); }, drawSearchResult() { var _this = this; if (_this.localOverlays.length == 1) { var overlays = this.map.getOverlays(); if (overlays.length > 0) { _this.map.clearOverlays(); } //把围栏名称添加到覆盖物上 var unitid = _this.localOverlays[0].unitid; var unitname = _this.localOverlays[0].unitname; const localOverlay = _this.localOverlays[0].area.split(";"); var points = []; for (var i = 0; i < localOverlay.length; i++) { var point = new BMapGL.Point( Number(localOverlay[i].split(",")[0]), Number(localOverlay[i].split(",")[1]) ); points.push(point); } const styleOptions = { strokeColor: "#5E87DB", // 边线颜色 fillColor: "#5E87DB", // 填充颜色。当参数为空时,圆形没有填充颜色 strokeWeight: 2, // 边线宽度,以像素为单位 strokeOpacity: 1, // 边线透明度,取值范围0-1 fillOpacity: 0.2, // 填充透明度,取值范围0-1 }; var polygon = new BMapGL.Polygon(points, styleOptions); // //给查询到的围栏做个标识,以便之后的删除 polygon.unitid = unitid; polygon.unitname = unitname; _this.overlays.push(polygon); //给所有的覆盖物添加删除覆盖物的监听事件 for (let over = 0; over < this.overlays.length; over++) { //添加点击事件 this.overlays[over].addEventListener( "click", this.removeOverlay.bind(this.overlays[over]) ); //添加鼠标经过事件 this.overlays[over].addEventListener( "mouseover", this.overOverlay.bind(this.overlays[over]) ); //添加鼠标离开事件 this.overlays[over].addEventListener( "mouseout", this.outOverlay.bind(this.overlays[over]) ); } //添加围栏到地图上 _this.map.addOverlay(polygon); //添加标签至覆盖物上 _this.addLabel(polygon); var centerPoint = polygon.getBounds(); centerPoint = centerPoint.getCenter(); //地理值转换为像素值 // var centerPixel = _this.map.PointToPixel(centerPoint) _this.map.panTo(centerPoint); // _this.map.panBy(centerPixel) _this.queryParams = {}; } else if (_this.localOverlays.length == 0) { this.$message.warning("当前单位目前暂无围栏!"); this.queryParams = {}; return; } }, //新增围栏 addLocalOverlay(overlay) { var overlays = this.map.getOverlays(); if (overlays.length > 0) { this.map.clearOverlays(); } //添加围栏到地图上 this.map.addOverlay(overlay); this.addLabel(overlay); this.refresh(); }, //取消围栏编辑 cancelOverlay(record) { this.map.removeOverlay(record); }, //刷新页面 refresh() { const parameter = { pageNo: 1, pageSize: 25, }; this.loadData(parameter); }, //搜索围栏 search() { this.refresh(); }, //给围栏添加标签 addLabel(overlay) { var centerPoint = overlay.getBounds(); centerPoint = centerPoint.getCenter(); let label = new BMapGL.Label(overlay.unitname, { position: centerPoint }); // label.setContent(overlay.unitname) label.setStyle({ color: "black", fontSize: "14px", fontWeight: 700, }); // label.setPosition(centerPoint) this.map.addOverlay(label); }, //鼠标经过覆盖物 overOverlay(e) { e.target.setFillColor("#6f6cd8"); }, //鼠标离开覆盖物 outOverlay(e) { e.target.setFillColor("#fff"); }, //定点查询 accuarySearch() { var _this = this; //建立一个自动完成的实例对象 let autocomplete = new BMapGL.Autocomplete({ location: this.map, //设定返回结果的所属范围。例如“北京市” // types:'city', //设置值为"city",将返回省市区县乡镇街道地址类型数据。 input: "suggestCity", //文本输入框元素或其id }); //处理函数 function handleAutoComplete(e) { var str = ""; var _value = e.fromitem.value; var value = ""; if (e.fromitem.index > -1) { value = _value.province + _value.city + _value.district + _value.street + _value.business; } str = "FromItem<br />index = " + e.fromitem.index + "<br />value = " + value; value = ""; if (e.toitem.index > -1) { _value = e.toitem.value; value = _value.province + _value.city + _value.district + _value.street + _value.business; } str += "<br />ToItem<br />index = " + e.toitem.index + "<br />value = " + value; document.getElementById("searchResultPanel").innerHTML = str; document.getElementById("tangram-suggestion--TANGRAM__1").style.zIndex = 2000; } //鼠标放在下拉列表上的事件 autocomplete.addEventListener("highlight", handleAutoComplete); //处理函数 function handleOnConfirm(e) { var _value = e.item.value; _this.myValue = _value.province + _value.city + _value.district + _value.street + _value.business; document.getElementById("searchResultPanel").innerHTML = "onconfirm<br />index = " + e.item.index + "<br />myValue = " + _this.myValue; document.getElementById("tangram-suggestion--TANGRAM__1").style.zIndex = 2000; _this.setPlace(); } //鼠标点击下拉列表后的事件 autocomplete.addEventListener("confirm", handleOnConfirm); }, //跳转到指定地点 setPlace() { var _this = this; this.map.clearOverlays(); //清除地图上所有覆盖物 //智能搜索 function myFun() { //获取第一个智能搜索的结果 var pp = local.getResults().getPoi(0).point; _this.map.centerAndZoom(pp, 18); //添加标注 _this.map.addOverlay(new BMapGL.Marker(pp)); } var local = new BMapGL.LocalSearch(_this.map, { onSearchComplete: myFun, }); local.search(this.myValue); this.myValue = ""; }, }, }; </script> <style > /* 隐藏版权信息 */ .BMap_cpyCtrl { display: none; } /* 隐藏版权图标 */ .anchorBL img { display: none; } </style> <style scoped> .mymap { width: 100%; height: 95vh; position: relative; z-index: 1; } .change-map-type { position: absolute; right: 50px; bottom: 10px; z-index: 2; } ul li { list-style: none; margin: 0; padding: 0; } .info { z-index: 999; width: auto; min-width: 22rem; padding: 0.75rem 1.25rem; margin-left: 1.25rem; position: fixed; top: 1rem; background-color: #fff; border-radius: 0.25rem; font-size: 14px; color: #666; box-shadow: 0 2px 6px 0 rgba(27, 142, 236, 0.5); } .drawing-panel { display: flex; z-index: 999; position: fixed; top: 108px; left: 250px; border-radius: 0.25rem; height: 47px; box-shadow: 0 2px 6px 0 rgba(27, 142, 236, 0.5); background-color: #fff; padding-inline-start: 0; } .drawing-panel li { width: 47px; height: 47px; /* float: left; */ flex: 1; border-right: 1px solid #d2d2d2; text-align: center; line-height: 47px; cursor: pointer; } ::v-deep .drawing-panel li:hover, .drawing-panel li:active { background-color: rgb(98, 169, 214); } .tool-icon { font-size: 30px; color: #aaa; text-align: center; margin-top: 12px; } .tool-icon:hover { transform: translate(1.1); text-align: center; color: #fff; } .search { position: fixed; z-index: 2; top: 60px; left: 240px; width: 288px; height: 30px; line-height: 30px; } .minput { position: fixed; width: 400px; top: 60px; right: 20px; z-index: 99; } ::v-deep.tangram-suggestion-main { z-index: 100 !important; } ::v-deep table tbody { z-index: 100 !important; } /* .bmap-btn { border-right: 1px solid #d2d2d2; float: left; width: 64px; height: 100%; background-image: url(http://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>
下面是新增围栏的模态框源码:这里我命名为 addOverlay.vue。
<template> <div style="width:100%"> <drag-modal :title="modalTitle" width="400px" v-if="visible" :visible="visible" :confirmLoading="confirmLoading" @ok="handleSubmit" @cancel="handleCancel" > <template slot="modalBody"> <a-form :form="form" autocomplete="off"> <a-form-item label="单位" :labelCol="{span:4}" :wrapperCol="{span:16}" > <a-tree-select :allowClear="true" showSearch treeNodeFilterProp="title" :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" :treeData="deptTreeData" v-decorator="[ 'unitid', { rules: [{ required: true, message: '请选择所属单位' }], initialValue: formdata.unitid || '' } ]" > </a-tree-select> </a-form-item> </a-form> </template> </drag-modal> </div> </template> <script> //获取单位树 import { userApi as api } from '@/api' //围栏管理接口 import {sysAreaApi as Sapi} from '@/api' export default { data(){ return { visible:false, form:this.$form.createForm(this), modalTitle:'新增围栏编辑', formdata: {}, treeExpandedKeys: [], deptTreeData: [], //当前编辑的围栏 overlay:{}, //当前围栏的点数组字符串 points:'', confirmLoading:false } }, created: function () { //获取单位树数据 this.$api.get(api.deptTree).then((res) => { this.deptTreeData = res.data.map((item) => { item.selectable = false return item }) }) }, methods:{ openModal(overlay,points){ this.visible = true this.overlay = overlay points.forEach(item =>{ this.points = this.points + item.lng + ',' + item.lat + ';' }) this.points = this.points.substring(0,this.points.length - 1) console.log("overlay:",overlay,this.points); }, handleSubmit(){ // this.$emit('addoverlay',obj) this.form.validateFields((err, values) => { if (err) { this.confirmLoading = false return } //-----------字符串去首尾空格-------- for (let key in values) { if (typeof values[key] == 'string') { values[key] = values[key].trim() } } //-----------字段关联值处理---------- let _this = this ;(function () { for (let i = 0, pDept; (pDept = _this.deptTreeData[i++]); ) { for (let j = 0, dept; (dept = pDept.children[j++]); ) { if (dept.value == values.unitid) { // values.paraentid = pDept.value // values.paraentname = pDept.title values.unitname = dept.title return } } } })() let params = {} params.id = 0 params.inputtime = "" params.area = this.points params.unitid = values.unitid params.unitname = values.unitname //保存 console.log("params:",params); this.$api.post(Sapi.save,params).then((res) =>{ if (res.success) { this.visible = false this.$message.success(res.msg) this.overlay.unitid = params.unitid this.overlay.unitname = params.unitname this.$emit('addoverlay',this.overlay) this.points = '' this.overlay = {} } this.confirmLoading = false }).catch((err) => { //保存请求出错,给出提示 this.confirmLoading = false this.$message.warning(err) }) }) }, handleCancel(){ this.visible = false this.$emit('canceloverlay',this.overlay) } } } </script> <style scoped> </style>
代码中已经有了详细的注释,开发者们可以根据注释修改修改代码,就可以实现自己的百度地图引入以及围栏管理模块了。
地图展示:
新增围栏:(以多边形为例)
区域绘制
保存围栏
查询围栏:
修改围栏:通过新增围栏 数据库记录覆盖以达到修改目的。
删除围栏:
地点搜索:
跳至搜索地点:
编码不易,点个小红心呗。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。