美文网首页
openlayers+vue3 测距、面、角组件

openlayers+vue3 测距、面、角组件

作者: 月下小酌_dbd5 | 来源:发表于2023-08-18 14:06 被阅读0次
  • 组件代码
<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" // 测量结果


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'
        })
      })
    }),
    // 绘制时点击处理事件tipDiv.value
    condition: (evt) => {
      // 测距时添加点标注
      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()
    //******距离测量开始时*****//
    if (measureType == "distence") {
      creatMark(null, "起点", "start").setPosition(map.getCoordinateFromPixel(e.target.downPx_))
      tipDiv.value.innerHTML = "总长:0 m</br>单击确定地点,双击结束";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        measureResult = distenceFormat(getLength(evt.target, { "projection": proj, "radius": 6378137 }))
        tipDiv.value.innerHTML = "总长:" + measureResult + "</br>单击确定地点,双击结束";
      })
    }
    //******面积测量开始时*****//
    else if (measureType == "area") {
      tipDiv.value.innerHTML = "面积:0 m<sup>2</sup></br>继续单击确定地点";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        if (evt.target.getCoordinates()[0].length < 4) tipDiv.value.innerHTML = "面积:0m<sup>2</sup></br>继续单击确定地点";
        else {
          measureResult = formatArea(getArea(evt.target, { "projection": proj, "radius": 6378137 }))
          tipDiv.value.innerHTML = "面积:" + measureResult + "</br>单击确定地点,双击结束";
        }
      })
    }
    //******角度测量开始时*****//
    else if (measureType == "angle") {
      tipDiv.value.innerHTML = "继续单击确定顶点";
      geometryListener = sketchFeature.getGeometry().on('change', (evt:any) => {
        if (evt.target.getCoordinates().length < 3) tipDiv.value.innerHTML = "继续单击确定顶点";
        else {
          measureResult = formatAngle(evt.target)
          tipDiv.value.innerHTML = "角度:" + measureResult + "</br>继续单击结束";
        }
      })
    }
  });

  /**
   * 绘制开始事件
   */
   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()
    })
    //******距离测量结束时*****//
    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))
    }
    //******面积测量结束时*****//
    else if (measureType == "area") {
      creatMark(closeBtn, null, "close2").setPosition(e.feature.getGeometry().getInteriorPoint().getCoordinates());
      creatMark(null, "总面积:" + measureResult + "", "area").setPosition(e.feature.getGeometry().getInteriorPoint().getCoordinates())
    }
    //******角度测量结束时*****//
    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; // 防止在绘制过程再创建测量
  measureType = type;
  if (vectorLayer.value != null) clearMeasure();
  tipDiv.value = document.createElement('div');
  tipDiv.value.innerHTML = '单击确定起点';
  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")
  }
}
// 创建标记
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;
}
// 格式化距离结果输出
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; //返回多边形的面积
}
// 计算角度输出
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); // 计算cos值
    angle = (Math.acos(cos) * 180) / Math.PI; // 角度值
    angle = angle.toFixed(2); // 结果保留两位小数
  }
  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")) // 移除动态提示框
}
// 清除测量
const clearMeasure = () => {
  vectorLayer.value.getSource().clear()
  map.getOverlays().clear()
  //移除监听事件
  unByKey(pointermoveEvent) // 清除鼠标在地图的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>
  • 效果


    计算面积
距离测量 角度测量

相关文章

  • 激光雷达运动畸变去除

    激光雷达传感器测距原理:三角测距与飞行时间(TOF)三角测距: TOF TOF精度更高更常用。 激光雷达的数学模型...

  • 激光雷达数学模型

    激光雷达传感器介绍 测距原理 三角测距主要国产雷达:雷神、思岚 TOF雷达:SICK ,HoKuyo,雷神,思岚 ...

  • Vue+OpenLayers 天地图,绘制,测量

    Vue + OpenLayers 采用天地图,绘制,测距,测面,添加标注git地址:https://github....

  • 三角视差法测距

    电视剧《亮剑》中不止一次出现这样的场景,一名战士两眼平视,向正前方展开胳膊伸出大拇指,测量目标离自己的距离。这就是...

  • 测距

    4米 4.1 8米 8.0 12米 11.9 16米16 20米20.1 100米99.6 96 95.3 92 ...

  • 三角测距激光雷达原理

    激光雷达近几年越来越普及了,复杂的比如应用在无人驾驶汽车上,简单的比如用在扫地机上去。随着无人驾驶和服务机器人行业...

  • 通过什么指标可以判断激光雷达性能好坏?

    对于大多数用户而言,测距范围、扫描频率、角分辨率、测量精度等参数是其衡量激光雷达性能好坏的重要指标,当然,在这些重...

  • 毕设无人机识别二维码2019-07-03

    单目摄像机测距

  • Vue组件开发系列之Badge组件

    一个数字角标组件 演示地址:http://widget-ui.cn/Badge 组件源码:https://gith...

  • 班主任培训

    一、一日常规管理 卫生、文明、特色 二、班级建设 三角:劳动角生物角图书角 四面:前后左右四面 、活动宣传 1宣传...

网友评论

      本文标题:openlayers+vue3 测距、面、角组件

      本文链接:https://www.haomeiwen.com/subject/lvkdmdtx.html