高德地图+vue实现页面点击绘制多边形及多边形切割拆分
2020/5/22 11:27:36
本文主要是介绍高德地图+vue实现页面点击绘制多边形及多边形切割拆分,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
最终效果
技术栈
项目中使用到的技术 高德基于vue的vue-amap,组件使用的element,算法是用的turf.js
配置步骤划分
1.创建vue文件,项目中引入vue-amap 官方有两种引入方式,一种npm 安装,一种是cdn引入,这里我选择的是npm安卓的
官方文档:vue-amap
2.根据文档的步骤,在main.js引入,页面中导入,就可以使用官方的组件的,这边主要使用的组件有三个,一个地图组件el-amap,一个点坐标el-amap-marker,一个多边形的遮盖物el-amap-polygon(最主要的实现组件)
多边形
3.地图的js算法是turf.js,turf是一个用于空间分析的js库。它包括传统的空间操作、用于创建GeoJSON数据的辅助功能以及数据分类和统计工具。Turf可以作为客户端插件添加到你的网站上,或者你可以用在Node.js开发后端上,感兴趣的可以看一下它的github,一个开发地图非常好用的库
github
使用npm(npm install @turf/turf)下载下来以后,在页面中引入它的js文件,并赋值给一个变量(const turf = require('@turf/turf');),
实现思路
1.监听页面中的点击,获取当前点击点的坐标,然后将经纬度存到data里面
通过地图组件绑定的events属性,绑定的属性对象中,监听click点击事件,将事件保存下来
2.通过vue的 watch来监听data中的lng或lat的变化,当值发生变化的时候,页面新增点位
3.当页面中markers点位数量变化的同时,监听markers的length长度是不是大于3,当点位多于三个的时候,在页面中通过遮盖物el-amap-polygon组件来把多边形画出来但这边有一个问题,就是如果页面点击的坐标点顺序不能够构成一个标志的多边形的话,这个要怎么判断出来,因为el-amap-polygon组件是根据你数组的坐标顺序来构建多边形的,比如,一个不等的长方形,如果点位顺序不是顺时针或者逆时针的话,就会形成两个顶点相交的三角形,这个时候就需要用到turf来判断这个数组的坐标点顺序是不是顺时针或者逆时针的
4.通过turf的kinks方法,来判断这个多边形是否是合法多边形,即是否存在自相交情况
这四步下来,一个多边形就在页面中画了出来
网格拆分实现思路
上面已经将多边形画出来了以后,现在要做的就是将大的多边形划分为一个一个小的多边型,这一步的思路是先得出一个包围在多边形外部的四边形,然后拆分这个四边形,得出一个一个小的正四边形,然后判断每个正四边形是否与所画的多边形是否相交,然后将相交的部分裁剪显示出来,这一步共分为五步
第一步,先判断这个多边形的大小,是否符合规定
因为画一个太大的多边形的话,比如将整个中国都包括进来,如果按一公里的面积来拆分,那计算量是个比较巨大的数子,需要很高的性能,性能低的可能会直接卡死。这里通过vue的环境变量来控制这一个数组
然后根据我的业务逻辑来判断这个多边形的面积是否是需要拆分的,或者是用户是否选择拆分如下图
当用户点击确定就继续进行,点击取消就结束整个的任务
第二步,获取覆盖绘画多边形的外多边形,然后拆分成一个一个小的多边形
这一步需要借助
bboxPolygon来获取覆盖在所画多边形外围的四边形
然后将外四边形拆分,拆分成一个一个小的正方形,这里需要用到
squareGrid它有三个参数,第一个bbox,第二个是所需拆分成小正方形的编程,第三个是可选项,包括使用什么单位(英里,公里等)
第三步,裁剪小网格,获取与所画多边形重合的部分
使用
intersect获取裁剪后的网格
let clipped = turf.intersect(obj, polygon);
第四部,调用绘画函数,绘画一个一个小网格
this.polygonArr[index] = { draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, path: [] }; // 多边形的样式 let coordinates = clipped.geometry.coordinates[0]; let coordinatesLeng = coordinates.length; for (let j = 0; j < coordinatesLeng - 1; j++) { this.polygonArr[index].path[j] = coordinates[j]; } 复制代码
到此为止,一个多边形的拆分就已经出来了,但这样拆分有一个问题,如下图
通过上述四部绘画拆分出来的,会发现拆分的多边形它并不是完全的拆分,这是因为
squareGrid是通过中心点向外分割的,所以如果外部面积不足以拆分一个网格的时候,就出现了上述的情况,所以我们要通过第五步来解决一下
第五步,解决与所画多边形重合部分拆分不完全问题
既然当前画出来的因为面积问题,拆分到最外层的时候剩余面积不足以继续拆分,那我们就讲外面包裹所花多边形的面积给他扩大一下,这样不就行了吗,这里就需要用到
transformScale直接将它的面积给扩大,这样一来不就解决了吗
/** * 放大外多边形的,获取覆盖绘画多边形的网格 * @param {Object} polygon * @param {number} value * @param {number} sides */ acoverage(polygon, value, sides) { this.polygonArr.splice(this.polygonArr.length - len, len); let bbox = turf.bbox(polygon); console.log('bbox'); console.log(bbox); //bboxPolygon Takes a bbox and returns an equivalent polygon. let bboxPolygon = turf.bboxPolygon(bbox).geometry.coordinates[0]; // 获取覆盖所画多边形的外围四边形 console.log(bboxPolygon); let polygon2 = turf.polygon([bboxPolygon], { name: 'poly2' }); console.log('polygon2', polygon2); let scaledPoly = turf.transformScale(polygon2, sides); // 将外多边形放大 console.log('scaledPoly', scaledPoly); let bbox2 = turf.bbox(scaledPoly); console.log(bbox2); let options = { units: 'kilometers', mask: scaledPoly }; let squareGrid = turf.squareGrid(bbox2, value, options); // 获得覆盖多边形的网格坐标 console.log('squareGrid'); console.log(squareGrid); this.check(squareGrid.features, polygon); }, 复制代码
最终效果
关键步骤完整代码
/** * 绘制完多边形判断Polygon是否为自相交 * @param {Object} polygon turfpolygon类型 */ isIntersect(polygon) { var kinks = turf.kinks(polygon); if (kinks.features.length > 0) { //存在自相交情况 this.$message({ showClose: true, message: '错误,多边形不合法', type: 'error' }); this.obliterate(); } else { //不存在自相交情况 area = turf.area(polygon) / 1000000; console.log(area); console.log(process.env.VUE_APP_MAP_AREA); // vue环境变量,读取最大绘画面积 if (area < process.env.VUE_APP_MAP_AREA) { area > 1 ? this.promptArea(polygon) : this.noPromptArea(); } else { this.$message({ message: '绘画面积过大', type: 'warning' }); } } }, /** * 选择多边形不需要拆分 */ noPromptArea() { this.taskNauticaArr[0] = { flight_task_name: '任务1', task_coordinate_points: this.blockNautica }; }, /** * 判断多边形是否需要拆分 */ promptArea(polygon) { this.polygonArr = []; // 清空网格 this.$prompt('该测区面积较大,是否拆分?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', // inputPattern: /^[+]?[1-9][1-9]*$/, inputPattern: /^[+]?[1-9]$/, inputErrorMessage: '请输入大于0小于10的正整数,单位平方千米' }) .then(({ value }) => { let sides = 1; // 绘画的方格边长 if (value <= 2) { sides = 1.4; } else if (value <= 6) { sides = 1.6; } else { sides = 2; } this.acoverage(polygon, value, sides); }) .catch(err => { console.log(err); this.$message({ type: 'info', duration: 1500, message: '取消拆分' }); this.noPromptArea(); }); }, /** * 放大外多边形的,获取覆盖绘画多边形的网格 * @param {Object} polygon * @param {number} value * @param {number} sides */ acoverage(polygon, value, sides) { this.polygonArr.splice(this.polygonArr.length - len, len); let bbox = turf.bbox(polygon); console.log('bbox'); console.log(bbox); //bboxPolygon Takes a bbox and returns an equivalent polygon. let bboxPolygon = turf.bboxPolygon(bbox).geometry.coordinates[0]; // 获取覆盖所画多边形的外围四边形 console.log(bboxPolygon); let polygon2 = turf.polygon([bboxPolygon], { name: 'poly2' }); console.log('polygon2', polygon2); let scaledPoly = turf.transformScale(polygon2, sides); // 将外多边形放大 console.log('scaledPoly', scaledPoly); let bbox2 = turf.bbox(scaledPoly); console.log(bbox2); let options = { units: 'kilometers', mask: scaledPoly }; let squareGrid = turf.squareGrid(bbox2, value, options); // 获得覆盖多边形的网格坐标 console.log('squareGrid'); console.log(squareGrid); this.check(squareGrid.features, polygon); }, /** *添加等待提示,循环绘画方法 * @param {Array} arr * @param {Object} polygon */ check(arr, polygon) { // 循环函数,绘画拆分的每个正方形方格 // console.log(arr); const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); len = arr.length; this.taskNauticaArr = []; for (let i = 0; i < len; i++) { this.cropdef(arr[i], polygon); } loading.close(); }, /** * 裁剪函数,获取与内多边形 polygons 重合部分 * @param {Object} obj * @param {Object} polygon */ cropdef(obj, polygon) { let clipped = turf.intersect(obj, polygon); // 裁剪每个小网格与多边形相交的补发部分显示出来 if (clipped) { let clippedArr = clipped.geometry.coordinates[0]; this.addDrawPolygon(clipped); // 绘画函数 } }, /** * 绘画函数 * @param {Object} clipped */ addDrawPolygon(clipped) { let index = this.polygonArr.length; this.polygonArr[index] = { draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, path: [] }; // 多边形的样式 let coordinates = clipped.geometry.coordinates[0]; let coordinatesLeng = coordinates.length; for (let j = 0; j < coordinatesLeng - 1; j++) { this.polygonArr[index].path[j] = coordinates[j]; } } 复制代码
watch中监听鼠标点击的点的经纬度变化
watch: { /** * 当lng的值发生变化是,页面新增点位 */ lng() { if (this.alertShow != false) { let obj = { position: [this.lng, this.lat], events: {}, visible: true, draggable: false, template: '<div style="width: 8px;height: 8px;background: #000;border: 2px solid #49B2FF;border-radius: 50%;margin: 28px 0 0 6px"></div>' }; this.markers.push(obj); if (this.markers.length > 3 && this.alertShow != false) { this.polygons[indexes] = { draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, path: [] }; let arr = []; // 用来存储画的多边形的坐标数组 let lengthNum = this.markers.length; for (let i = 0; i < lengthNum; i++) { this.polygons[indexes].path[i] = this.markers[i].position; arr[i] = this.markers[i].position; } arr.push(this.markers[0].position); let polygon = turf.polygon([arr], { name: 'poly1' }); this.isIntersect(polygon); } else { } } } }复制代码
这篇关于高德地图+vue实现页面点击绘制多边形及多边形切割拆分的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04package.json 文件位置在哪?-icode9专业技术文章分享
- 2024-10-01Craco.js学习:从入门到实践指南
- 2024-10-01Create-React-App学习:入门与实践指南
- 2024-10-01CSS-in-JS学习:从入门到实践指南
- 2024-09-30JSX语法学习:从入门到初步掌握
- 2024-09-30Mock.js学习:入门教程与实战演练
- 2024-09-30React Hooks学习:从入门到实践
- 2024-09-30受控组件学习:React中的基础入门教程
- 2024-09-29JS定时器教程:初学者必看指南
- 2024-09-29JS对象教程:初学者的全面指南