最終效果
![](http://static.javashuo.com/static/loading.gif)
技術棧
項目中使用到的技術 高德基於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點擊事件,將事件保存下來
後端
![](http://static.javashuo.com/static/loading.gif)
2.經過vue的 watch來監聽data中的lng或lat的變化,當值發生變化的時候,頁面新增點位數組
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
但這邊有一個問題,就是若是頁面點擊的座標點順序不可以構成一個標誌的多邊形的話,這個要怎麼判斷出來,由於el-amap-polygon組件是根據你數組的座標順序來構建多邊形的,好比,一個不等的長方形,若是點位順序不是順時針或者逆時針的話,就會造成兩個頂點相交的三角形,這個時候就須要用到turf來判斷這個數組的座標點順序是否是順時針或者逆時針的
4.經過turf的kinks方法,來判斷這個多邊形是不是合法多邊形,便是否存在自相交狀況ide
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
這四步下來,一個多邊形就在頁面中畫了出來
![](http://static.javashuo.com/static/loading.gif)
網格拆分實現思路
上面已經將多邊形畫出來了之後,如今要作的就是將大的多邊形劃分爲一個一個小的多邊型,這一步的思路是先得出一個包圍在多邊形外部的四邊形,而後拆分這個四邊形,得出一個一個小的正四邊形,而後判斷每一個正四邊形是否與所畫的多邊形是否相交,而後將相交的部分裁剪顯示出來,這一步共分爲五步
函數
第一步,先判斷這個多邊形的大小,是否符合規定
由於畫一個太大的多邊形的話,好比將整個中國都包括進來,若是按一千米的面積來拆分,那計算量是個比較巨大的數子,須要很高的性能,性能低的可能會直接卡死。這裏經過vue的環境變量來控制這一個數組
![判斷大小](http://static.javashuo.com/static/loading.gif)
而後根據個人業務邏輯來判斷這個多邊形的面積是不是須要拆分的,或者是用戶是否選擇拆分以下圖
![](http://static.javashuo.com/static/loading.gif)
當用戶點擊肯定就繼續進行,點擊取消就結束整個的任務
第二步,獲取覆蓋繪畫多邊形的外多邊形,而後拆分紅一個一個小的多邊形
這一步須要藉助
bboxPolygon來獲取覆蓋在所畫多邊形外圍的四邊形
![](http://static.javashuo.com/static/loading.gif)
而後將外四邊形拆分,拆分紅一個一個小的正方形,這裏須要用到
squareGrid它有三個參數,第一個bbox,第二個是所需拆分紅小正方形的編程,第三個是可選項,包括使用什麼單位(英里,千米等)
![](http://static.javashuo.com/static/loading.gif)
第三步,裁剪小網格,獲取與所畫多邊形重合的部分
使用
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];
}
複製代碼
到此爲止,一個多邊形的拆分就已經出來了,但這樣拆分有一個問題,以下圖
![](http://static.javashuo.com/static/loading.gif)
經過上述四部繪畫拆分出來的,會發現拆分的多邊形它並非徹底的拆分,這是由於
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);
},
複製代碼
最終效果
![](http://static.javashuo.com/static/loading.gif)
關鍵步驟完整代碼
/**
* 繪製完多邊形判斷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 {
}
}
}
}複製代碼