openlayers+vue3 測距翁垂、面铆遭、角組件

  • 組件代碼
<template>
<div></div>
</template>
<script lang='ts' setup>
import { ref, reactive } from 'vue'

import { Draw } from "ol/interaction";
import { Vector as VectorSource } from "ol/source";
import { Vector as VectorLayer } from "ol/layer";
import Overlay from 'ol/Overlay';
import { Polygon, LineString } from 'ol/geom';
import Feature from 'ol/Feature';
import { unByKey } from 'ol/Observable'
import { getLength, getArea } from 'ol/sphere';
import Style from "ol/style/Style";
import Stroke from "ol/style/Stroke";
import Fill from "ol/style/Fill";
import Circle from "ol/style/Circle";

// let selectList = [
//   {id:'distence',title:'測距'},
//   {id:'area',title:'測面'},
//   {id:'angle',title:'測角'},
// ]

interface Props {
  map?:any
}
const {map} = defineProps<Props>()

let measureType:string ='diatence' //類型
let draw:any = ref(null)
let vectorLayer:any = ref(null)
let tipDiv:any = ref(null)
let pointermoveEvent:any = null // 地圖pointermove事件
let sketchFeature:any = null // 繪制的要素
let geometryListener:any = null // 要素幾何change事件
let measureResult:any = "0" // 測量結(jié)果


const creatDraw = (type:string) => {
  let maxPoints:any = null;
  if (measureType == "angle") maxPoints = 3
  else maxPoints = null

  // 矢量圖層源
  let vectorSource = new VectorSource({
    wrapX: false
  });
  // 矢量圖層
  vectorLayer.value = new VectorLayer({
    source: vectorSource,
    style: new Style({
      fill: new Fill({
        color: 'rgba(46, 198, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: '#2ec6ff',
        width: 3
      }),
      image: new Circle({
        radius: 0,
        fill: new Fill({
          color: '#2ec6ff'
        })
      })
    })
  });
  map.addLayer(vectorLayer.value)
  draw.value = new Draw({
    source: vectorSource,
    type: type,
    maxPoints: maxPoints,
    style: new Style({
      fill: new Fill({
        color: 'rgba(46, 198, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: '#2ec6ff',
        lineDash: [10, 10],
        width: 3
      }),
      image: new Circle({
        radius: 0,
        fill: new Fill({
          color: '#2ec6ff'
        })
      })
    }),
    // 繪制時(shí)點(diǎn)擊處理事件tipDiv.value
    condition: (evt) => {
      // 測距時(shí)添加點(diǎn)標(biāo)注
      if (measureResult != "0" && !map.getOverlayById(measureResult) && measureType == "distence")
        creatMark(null, measureResult, measureResult).setPosition(evt.coordinate)
      return true
    }
  });
  map.addInteraction(draw.value);

  /**
   * 繪制開始事件
   */
   draw.value.on("drawstart", (e:any) => {
    sketchFeature = e.feature
    let proj = map.getView().getProjection()
    //******距離測量開始時(shí)*****//
    if (measureType == "distence") {
      creatMark(null, "起點(diǎn)", "start").setPosition(map.getCoordinateFromPixel(e.target.downPx_))
      tipDiv.value.innerHTML = "總長:0 m</br>單擊確定地點(diǎn),雙擊結(jié)束";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        measureResult = distenceFormat(getLength(evt.target, { "projection": proj, "radius": 6378137 }))
        tipDiv.value.innerHTML = "總長:" + measureResult + "</br>單擊確定地點(diǎn)沿猜,雙擊結(jié)束";
      })
    }
    //******面積測量開始時(shí)*****//
    else if (measureType == "area") {
      tipDiv.value.innerHTML = "面積:0 m<sup>2</sup></br>繼續(xù)單擊確定地點(diǎn)";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        if (evt.target.getCoordinates()[0].length < 4) tipDiv.value.innerHTML = "面積:0m<sup>2</sup></br>繼續(xù)單擊確定地點(diǎn)";
        else {
          measureResult = formatArea(getArea(evt.target, { "projection": proj, "radius": 6378137 }))
          tipDiv.value.innerHTML = "面積:" + measureResult + "</br>單擊確定地點(diǎn)枚荣,雙擊結(jié)束";
        }
      })
    }
    //******角度測量開始時(shí)*****//
    else if (measureType == "angle") {
      tipDiv.value.innerHTML = "繼續(xù)單擊確定頂點(diǎn)";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        if (evt.target.getCoordinates().length < 3) tipDiv.value.innerHTML = "繼續(xù)單擊確定頂點(diǎn)";
        else {
          measureResult = formatAngle(evt.target)
          tipDiv.value.innerHTML = "角度:" + measureResult + "</br>繼續(xù)單擊結(jié)束";
        }
      })
    }
  });

  /**
   * 繪制開始事件
   */
   draw.value.on("drawend", (e:any) => {
    let closeBtn = document.createElement('span');
    closeBtn.innerHTML = "×";
    closeBtn.title = "清除測量"
    closeBtn.style = `
      width: 20px;
      height:20px;
      color:#000;
      line-height: 12px;
      text-align: center;
      border-radius: 50%;
      display: inline-block;
      padding: 2px;
      color: rgb(46, 198, 255);
      border: 2px solid rgb(46, 198, 255);
      background-color: rgb(255, 255, 255);
      font-weight: 600;
      position: absolute;
      top: -36px;
      right: -17px;
      font-size: 15px;
      cursor: pointer;`;
    closeBtn.addEventListener('click', () => {
      clearMeasure()
    })
    //******距離測量結(jié)束時(shí)*****//
    if (measureType == "distence") {
      creatMark(closeBtn, null, "close1").setPosition(e.feature.getGeometry().getLastCoordinate());
      creatMark(null, "總長:" + measureResult + "", "length").setPosition(e.feature.getGeometry().getLastCoordinate())
      map.removeOverlay(map.getOverlayById(measureResult))
    }
    //******面積測量結(jié)束時(shí)*****//
    else if (measureType == "area") {
      creatMark(closeBtn, null, "close2").setPosition(e.feature.getGeometry().getInteriorPoint().getCoordinates());
      creatMark(null, "總面積:" + measureResult + "", "area").setPosition(e.feature.getGeometry().getInteriorPoint().getCoordinates())
    }
    //******角度測量結(jié)束時(shí)*****//
    else if (measureType == "angle") {
      creatMark(closeBtn, null, "close3").setPosition(e.feature.getGeometry().getCoordinates()[1]);
      creatMark(null, "角度:" + measureResult + "", "angle").setPosition(e.feature.getGeometry().getCoordinates()[1])
    }
    // 停止測量
    stopMeasure();
  });
}
// 測量
const measure = (type:string) => {
  if (draw.value != null) return false; // 防止在繪制過程再創(chuàng)建測量
  measureType = type;
  if (vectorLayer.value != null) clearMeasure();
  tipDiv.value = document.createElement('div');
  tipDiv.value.innerHTML = '單擊確定起點(diǎn)';
  tipDiv.value.className = "tipDiv.value";
  tipDiv.value.style = `
    width:auto;
    height:auto;
    color:#000;
    padding:4px;
    border:1px solid #2ec6ff;
    font-size:12px;
    background-color:#fff;
    position:relative;
    top:60%;
    left:60%;
    font-weight:600;`

  let overlay = new Overlay({
    element: tipDiv.value,
    autoPan: false,
    positioning: "bottom-center",
    id: "tipLay",
    stopEvent: false //停止事件傳播到地圖
  });
  map.addOverlay(overlay);

  pointermoveEvent = map.on("pointermove", (evt:any) => {
    overlay.setPosition(evt.coordinate)
  })
  if (measureType == "distence" || measureType == "angle") {
    creatDraw("LineString")
  }
  else if (measureType == "area") {
    creatDraw("Polygon")
  }
}
// 創(chuàng)建標(biāo)記
const creatMark = (markDom:any, txt:any, idstr:any) => {
  if (markDom == null) {
    markDom = document.createElement('div');
    markDom.innerHTML = txt
    markDom.style = `
      width:auto;
      height:auto;
      color:#000;
      padding:4px;
      border:1px solid #2ec6ff;
      font-size:12px;
      background-color:#fff;
      position:relative;
      top:60%;
      left:60%;
      font-weight:600;`
  }
  let overlay = new Overlay({
    element: markDom,
    autoPan: false,
    positioning: "bottom-center",
    id: idstr,
    stopEvent: false
  });
  map.addOverlay(overlay)
  return overlay;
}
// 格式化距離結(jié)果輸出
const distenceFormat = (length:number) => {
  let output;
  if (length > 100) {
    output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km'; //換算成km單位
  } else {
    output = (Math.round(length * 100) / 100) + ' ' + 'm'; //m為單位
  }
  return output;//返回線的長度
}
// 格式化面積輸出
const formatArea = (area:number) => {
  let output;
  if (area > 10000) {
    output = (Math.round(area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>'; //換算成km單位
  } else {
    output = (Math.round(area * 100) / 100) + ' ' + 'm<sup>2</sup>';//m為單位
  }
  return output; //返回多邊形的面積
}
// 計(jì)算角度輸出
const formatAngle = (line:any) => {
  var coordinates = line.getCoordinates();
  var angle = '0°';
  if (coordinates.length == 3) {
    const disa:any = getLength(new Feature({
      geometry: new LineString([coordinates[0], coordinates[1]])
    }).getGeometry(), {
      radius: 6378137,
      projection: map.getView().getProjection()
    });

    const disb = getLength(new Feature({
      geometry: new LineString([coordinates[1], coordinates[2]])
    }).getGeometry(), {
      radius: 6378137,
      projection: map.getView().getProjection()
    });

    const disc = getLength(new Feature({
      geometry: new LineString([coordinates[0], coordinates[2]])
    }).getGeometry(), {
      radius: 6378137,
      projection: map.getView().getProjection()
    });
    var cos = (disa * disa + disb * disb - disc * disc) / (2 * disa * disb); // 計(jì)算cos值
    angle = (Math.acos(cos) * 180) / Math.PI; // 角度值
    angle = angle.toFixed(2); // 結(jié)果保留兩位小數(shù)
  }
  if (isNaN(angle)) return "0°"
  else return angle + "°"; // 返回角度
}
// 停止測量
const stopMeasure = () => {
  tipDiv.value = null
  map.removeInteraction(draw.value); // 移除繪制組件
  draw.value = null;
  map.removeOverlay(map.getOverlayById("tipLay")) // 移除動態(tài)提示框
}
// 清除測量
const clearMeasure = () => {
  vectorLayer.value.getSource().clear()
  map.getOverlays().clear()
  //移除監(jiān)聽事件
  unByKey(pointermoveEvent) // 清除鼠標(biāo)在地圖的pointermove事件
  unByKey(geometryListener) // 清除繪制圖像change事件
  pointermoveEvent = null;
  geometryListener = null;
  measureResult = "0"
}
// 重置
const resetMeasure = () => {
  if (draw.value != null) stopMeasure();
  if (vectorLayer.value != null) clearMeasure();
}

defineExpose({
  measure,
  clearMeasure,
  resetMeasure
})

</script>
<style scoped lang='scss'>

</style>
  • 效果


    計(jì)算面積
距離測量
角度測量
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市啼肩,隨后出現(xiàn)的幾起案子橄妆,更是在濱河造成了極大的恐慌,老刑警劉巖祈坠,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件害碾,死亡現(xiàn)場離奇詭異,居然都是意外死亡赦拘,警方通過查閱死者的電腦和手機(jī)慌随,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躺同,“玉大人儒陨,你說我怎么就攤上這事∷褡眩” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵椭员,是天一觀的道長车海。 經(jīng)常有香客問我,道長隘击,這世上最難降的妖魔是什么侍芝? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮埋同,結(jié)果婚禮上州叠,老公的妹妹穿的比我還像新娘。我一直安慰自己凶赁,他們只是感情好咧栗,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布逆甜。 她就那樣靜靜地躺著,像睡著了一般致板。 火紅的嫁衣襯著肌膚如雪交煞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天斟或,我揣著相機(jī)與錄音素征,去河邊找鬼。 笑死萝挤,一個(gè)胖子當(dāng)著我的面吹牛御毅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怜珍,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼端蛆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绘面?” 一聲冷哼從身側(cè)響起欺税,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎揭璃,沒想到半個(gè)月后晚凿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘦馍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年歼秽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片情组。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡燥筷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出院崇,到底是詐尸還是另有隱情肆氓,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布底瓣,位于F島的核電站谢揪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏捐凭。R本人自食惡果不足惜拨扶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茁肠。 院中可真熱鬧患民,春花似錦、人聲如沸垦梆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惋嚎,卻和暖如春杠氢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背另伍。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工鼻百, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摆尝。 一個(gè)月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓温艇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堕汞。 傳聞我的和親對象是個(gè)殘疾皇子勺爱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容