最终效果

技术栈
项目中使用到的技术 高德基于vue的vue-amap,组件使用的element,算法是用的turf.jsvue
配置步骤划分
1.建立vue文件,项目中引入vue-amap 官方有两种引入方式,一种npm 安装,一种是cdn引入,这里我选择的是npm安卓的git
官方文档:vue-amapgithub
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(npm install @turf/turf)下载下来之后,在页面中引入它的js文件,并赋值给一个变量(const turf = require('@turf/turf');),编程
实现思路
1.监听页面中的点击,获取当前点击点的坐标,而后将经纬度存到data里面 经过地图组件绑定的events属性,绑定的属性对象中,监听click点击事件,将事件保存下来
后端

2.经过vue的 watch来监听data中的lng或lat的变化,当值发生变化的时候,页面新增点位数组


但这边有一个问题,就是若是页面点击的坐标点顺序不可以构成一个标志的多边形的话,这个要怎么判断出来,由于el-amap-polygon组件是根据你数组的坐标顺序来构建多边形的,好比,一个不等的长方形,若是点位顺序不是顺时针或者逆时针的话,就会造成两个顶点相交的三角形,这个时候就须要用到turf来判断这个数组的坐标点顺序是否是顺时针或者逆时针的
4.经过turf的kinks方法,来判断这个多边形是不是合法多边形,便是否存在自相交状况ide


这四步下来,一个多边形就在页面中画了出来

网格拆分实现思路
上面已经将多边形画出来了之后,如今要作的就是将大的多边形划分为一个一个小的多边型,这一步的思路是先得出一个包围在多边形外部的四边形,而后拆分这个四边形,得出一个一个小的正四边形,而后判断每一个正四边形是否与所画的多边形是否相交,而后将相交的部分裁剪显示出来,这一步共分为五步
函数
第一步,先判断这个多边形的大小,是否符合规定
由于画一个太大的多边形的话,好比将整个中国都包括进来,若是按一千米的面积来拆分,那计算量是个比较巨大的数子,须要很高的性能,性能低的可能会直接卡死。这里经过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 {
}
}
}
}复制代码