Vue + Openlayers入门教程(五):Turf.js 实现距离量算功能

距离量算功能实现

一、技术栈

Vue 3 + Typescript +Openlayers 7 + Element plus + turf.js
在这里插入图片描述

二、组件代码

  • OLMeasure.vue

    其中计算距离为大地线距离。

<template>
  <div class="measureDiv">
    <el-button-group style="margin-right: 10px">
      <el-button @click="beginMeasure">大地线测量</el-button>
      <el-button @click="stopMeasure">停止测量</el-button>
    </el-button-group>
  </div>
</template>
<script setup lang="ts">
import { Feature, Map, Overlay } from "ol";
import bus from "@/utils/bus.ts";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import { Geometry, LineString, Point } from "ol/geom";
import { Draw } from "ol/interaction";
import * as turf from "@turf/turf";
import { Coordinate } from "ol/coordinate";
import { unByKey } from "ol/Observable";
import { EventsKey } from "ol/events";
import { transform } from "ol/proj";

//监听获取map数据
let mapCopy: Map;
bus.on("mapToChecked", (res: Map) => {
  // 传参由回调函数中的形参接受
  mapCopy = res;
});
let drawLine: Draw;
let drawGeometry: LineString;
let drawFeature: Feature<Geometry> | null; //定义一个全局获取绘制的要素
let toolTipElement: HTMLElement; //提示信息dom元素
let toolTip: Overlay;
let distancePopupElement: HTMLElement; //距离弹窗dom元素
let distancePopup: Overlay;
let listener: EventsKey | EventsKey[]; //接收地图监听
//开始测量
const beginMeasure = () => {
  //创建一个矢量图层接收绘制的多线段
  const lineSource = new VectorSource();
  const lineLayer = new VectorLayer({
    source: lineSource,
    properties: {
      title: "lineLayer",
      name: "测量图层",
      type: "VectorLayer",
    },
  });

  //将矢量图层加载到地图实例
  mapCopy.addLayer(lineLayer);
  //创建一个Draw对象,绘制距离线段
  drawLine = new Draw({
    source: lineSource,
    type: "LineString",
  });
  drawLine.setProperties({ name: "Draw" });
  mapCopy.addInteraction(drawLine);

  //创建toolTipElement
  creatToolTip();
  //创建距离弹窗
  createDistancePopup();
  //监听地图的鼠标移动事件,设置提示信息弹窗
  listener = mapCopy.on("pointermove", (evt) => {
    let helpMsg = "点击开始测量";
    if (drawFeature) {
      helpMsg = "双击结束测量";
    }
    toolTipElement.innerHTML = helpMsg; //设置提示文字
    toolTip.setPosition(evt.coordinate); //设置提示位置跟着鼠标走
    toolTipElement.classList.remove("hidden"); //显示提示框
  });

  //监听开始绘制事件
  drawLine.on("drawstart", (evt) => {
    //获取绘制的要素
    drawFeature = evt.feature; //多线段
    //监听几何变化事件
    drawFeature.getGeometry()?.on("change", (evt) => {
      //获取绘制的几何对象
      drawGeometry = evt.target as LineString;
      //获取多线段的点的坐标数组长度
      const length = drawGeometry.getCoordinates().length;
      //获取多线段上最后一个点坐标
      const bottomCoord: Coordinate = drawGeometry.getLastCoordinate();
      //计算多线段的长度,点的坐标为4548坐标系,需要转换到4326地理坐标系
      const lineCoordArr: Coordinate[] = [];
      drawGeometry.getCoordinates().forEach((coord) => {
        lineCoordArr.push(transform(coord, "EPSG:4548", "EPSG:4326"));
      });
      const line = turf.lineString(lineCoordArr); //大地线距离
      const distance = turf.length(line, { units: "kilometers" });

      //将计算距离结果加载到提示框上
      distancePopupElement.innerHTML = `${distance.toFixed(2)} km`; //对距离保留两位小数
      distancePopup.setPosition(bottomCoord); //设置距离弹窗位置
    });
  });
  //监听双击绘制完成事件
  drawLine.on("drawend", (evt) => {
    distancePopupElement.className = "toolTip distancePopupEnd";
    distancePopup.setOffset([0, -7]);
    drawFeature = null;
    toolTipElement.classList.add("hidden");
  });
};
//创建toolTipElement
const creatToolTip = () => {
  if (toolTipElement) {
    //如果已经存在就先初始化
    toolTipElement.parentNode?.removeChild(toolTipElement);
  }
  //创建dom元素--提示信息
  toolTipElement = document.createElement("div");
  toolTipElement.className = "toolTip hidden";
  //创建Overlay弹窗
  toolTip = new Overlay({
    element: toolTipElement,
    offset: [15, 0],
    positioning: "center-left",
  });
  //添加Overlay
  mapCopy.addOverlay(toolTip);
};
//创建dom元素--距离弹窗
const createDistancePopup = () => {
  if (distancePopupElement) {
    distancePopupElement.parentNode?.removeChild(distancePopupElement);
  }
  distancePopupElement = document.createElement("div");
  distancePopupElement.className = "toolTip distancePopupStart";
  distancePopup = new Overlay({
    element: distancePopupElement,
    offset: [15, 0],
    positioning: "bottom-center",
    stopEvent: false,
    insertFirst: false,
  });
  mapCopy.addOverlay(distancePopup);
};

//停止测量
const stopMeasure = () => {
  //如果draw为空不执行删除,防止删除了默认交互
  mapCopy.removeInteraction(drawLine);
  mapCopy.removeOverlay(toolTip); //关闭信息弹窗
  unByKey(listener); //移除监听
};
</script>
<style>
.measureDiv {
  display: flex;
}
.hidden {
  display: none;
}

.toolTip {
  position: relative;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  color: white;
  padding: 4px 8px;
  opacity: 0.7;
  white-space: nowrap;
  font-size: 12px;
  cursor: default;
  user-select: none;
}

.distancePopupStart {
  opacity: 1;
  font-weight: bold;
}

.distancePopupEnd {
  background-color: #ffcc33;
  color: black;
  border: 1px solid white;
}

.distancePopupStart:before,
.distancePopupEnd:before {
  border-top: 6px solid rgba(0, 0, 0, 0.5);
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  content: "";
  position: absolute;
  bottom: -6px;
  margin-left: -7px;
  left: 50%;
}

.distancePopupEnd:before {
  border-top-color: #ffcc33;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值