高德地圖+vue實現頁面點擊繪製多邊形及多邊形切割拆分

最終效果

技術棧

項目中使用到的技術 高德基於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的變化,當值發生變化的時候,頁面新增點位數組

3.當頁面中markers點位數量變化的同時,監聽markers的length長度是否是大於3,當點位多於三個的時候,在頁面中經過遮蓋物el-amap-polygon組件來把多邊形畫出來

但這邊有一個問題,就是若是頁面點擊的座標點順序不可以構成一個標誌的多邊形的話,這個要怎麼判斷出來,由於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 {
    }
  }
 }
 }複製代碼
相關文章
相關標籤/搜索