一、技术栈
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>