- 組件代碼
<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ì)算面積
距離測量
角度測量