// 使用
import MeatureTool from "@/components/webgisMap/measureTool";
measureDistance() {
// ID可以自定义
const layerId = String(new Date().getTime())
this.meatureTool = new MeatureTool(this.mapBoxMap)
this.meatureTool.measureDistance(layerId)
// 防止函数冲突
this.meatureTool.setDistance()
},
// measureTool.js
// import mapboxgl from '@/third/maplibre-gl/maplibre-gl.js'
import mapboxgl from "mapbox-gl";
// import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
import "mapbox-gl/dist/mapbox-gl.css";
// import * as turf from '@turf/turf'
import * as turf from '@/components/webgisMap/turf.min.js';
import './measureIcon.css'
import pointMark from '@/assets/mapboxgis/icon_online.png'
export default class MeatureTool {
map = void 0
sdMap = void 0
constructor(sdMap) {
this.sdMap = sdMap
this.map = sdMap
}
layerDistanceList = []
layerAreaList = []
setDistance = () => { }
setArea = () => { }
/**
* 测量距离
* @param {*} layerId
*/
measureDistance(layerId) {
this.layerDistanceList.push(layerId)
var isMeasure = true
const map = this.map
const sdMap = this.sdMap
map.doubleClickZoom.disable()
let catchMark = null
let isEdit = false
let Geolist = []
let dragPointOrder = 0
let pointOnLine = [0, 0]
const jsonPoint = {
type: 'FeatureCollection',
features: [],
}
const jsonLine = {
type: 'FeatureCollection',
features: [],
}
// 添加测量结果弹窗
const ele = document.createElement('div')
ele.setAttribute('class', 'measure-move')
ele.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 32px 5px;')
const option = {
element: ele,
anchor: 'left',
offset: [8, 0],
}
const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)
const eleM = document.createElement('div')
eleM.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 17px 5px;')
eleM.setAttribute('class', 'measure-move')
const optionM = {
element: eleM,
anchor: 'left',
offset: [8, 30],
}
eleM.innerHTML = '单击确定起点';
let moveTip = new mapboxgl.Marker(optionM).setLngLat([0, 0]).addTo(map)
let hoverTip = null;
// 添加测量图层
map.addSource('points' + layerId, {
type: 'geojson',
data: jsonPoint,
})
map.addSource('point-move' + layerId, {
type: 'geojson',
data: jsonPoint,
})
map.addSource('line' + layerId, {
type: 'geojson',
data: jsonLine,
})
map.addSource('line-move' + layerId, {
type: 'geojson',
data: jsonLine,
})
map.addSource('point-follow' + layerId, {
type: 'geojson',
data: jsonPoint,
})
map.addLayer({
id: 'line' + layerId,
type: 'line',
source: 'line' + layerId,
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65,
},
})
map.addLayer({
id: 'line-move' + layerId,
type: 'line',
source: 'line-move' + layerId,
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65,
'line-dasharray': [5, 2],
},
})
map.addLayer({
id: 'points' + layerId,
type: 'circle',
source: 'points' + layerId,
paint: {
'circle-color': '#ffffff',
'circle-radius': 3.5,
'circle-stroke-width': 1.5,
'circle-stroke-color': '#ff0000',
},
})
map.addLayer({
id: 'point-move' + layerId,
type: 'circle',
source: 'point-move' + layerId,
paint: {
'circle-color': '#ffffff',
'circle-radius': 3.5,
'circle-stroke-width': 1.5,
'circle-stroke-color': '#ff0000',
},
})
// 活动点可以选择用图层,也可以选择用Mark
map.addLayer({
id: 'point-follow' + layerId,
type: 'circle',
source: 'point-follow' + layerId,
paint: {
'circle-color': '#199afc',
'circle-radius': 5.5,
'circle-stroke-width': 1.5,
'circle-stroke-color': '#ffffff',
},
})
// 清除面积测量
this.setArea = () => {
isMeasure = false
map.removeLayer('point-move' + layerId)
map.removeLayer('line-move' + layerId)
return isMeasure
}
/**
* 添加点
* @param {*} _e
*/
function addPointforJSON(_e) {
if (isMeasure) {
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [_e.lngLat.lng, _e.lngLat.lat],
},
properties: {
id: String(new Date().getTime()),
},
}
jsonPoint.features.push(point)
map.getSource('points' + layerId).setData(jsonPoint)
drawLine(jsonPoint)
addMeasureRes(jsonPoint)
}
}
/**
* 绘制线
* @param {*} jsonPoint
*/
function drawLine(jsonPoint) {
if (jsonPoint.features.length > 1) {
jsonLine.features = []
for (let i = 0; i < jsonPoint.features.length - 1; i++) {
const coords = jsonPoint.features[i].geometry.coordinates
const next_coords = jsonPoint.features[i + 1].geometry.coordinates
jsonLine.features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [coords, next_coords],
},
})
}
map.getSource('line' + layerId).setData(jsonLine)
}
}
/**
* 添加dom
* @param {*} jsonPoint 点集
*/
function addMeasureRes(jsonPoint) {
if (jsonPoint.features.length > 0) {
removedom()
const pointList = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coords = jsonPoint.features[i].geometry.coordinates
pointList.push(coords)
const close = document.createElement('div')
close.setAttribute('class', `measure-result ${layerId} close`)
close.onclick = (__e) => {
if (!isMeasure) {
// 移除点
__e.stopPropagation()
removePoint(coords)
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
}
}
const clear = document.createElement('div')
clear.setAttribute('class', `measure-result ${layerId} clear`)
clear.setAttribute('style', 'position: absolute;left:8px;top:-18px;')
clear.onclick = (__e) => {
// 全部删除
__e.stopPropagation()
removeLayer()
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
}
const edit = document.createElement('div')
edit.setAttribute('class', `measure-result ${layerId} edit`)
edit.onclick = (__e) => {
// 编辑线
__e.stopPropagation()
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
editLine()
}
const element = document.createElement('div')
element.setAttribute('class', 'measure-result ' + layerId)
const option = {
element: element,
anchor: 'left',
offset: [8, 0],
}
element.innerHTML = i === 0 ? '起点' : getLength(pointList);
if ((jsonPoint.features.length === i + 1) & !isMeasure) {
element.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 17px 5px;margin: 20px 0 0 -10px;')
element.innerHTML = `总长:<span style="color:red">${getLength(pointList)}</span>`
// element.appendChild(edit)
element.appendChild(clear)
}
// element.appendChild(close)
new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
}
}
}
/**
* 移除点
* @param {*} coords 点坐标
*/
function removePointID(id) {
if (jsonPoint.features.length > 0) {
if (jsonPoint.features.length === 2) {
jsonPoint.features = []
jsonLine.features = []
map.getSource('points' + layerId).setData(jsonPoint)
map.getSource('line' + layerId).setData(jsonLine)
removedom()
} else {
for (let i = 0; i < jsonPoint.features.length; i++) {
if (
(jsonPoint.features[i].properties.id === id)
) {
jsonPoint.features.splice(i, 1)
}
}
drawLine(jsonPoint)
addMeasureRes(jsonPoint)
map.getSource('points' + layerId).setData(jsonPoint)
}
}
}
function removePoint(coords) {
if (jsonPoint.features.length > 0) {
if (jsonPoint.features.length === 2) {
jsonPoint.features = []
jsonLine.features = []
map.getSource('points' + layerId).setData(jsonPoint)
map.getSource('line' + layerId).setData(jsonLine)
removedom()
} else {
for (let i = 0; i < jsonPoint.features.length; i++) {
console.log(jsonPoint.features[i]);
console.log(jsonPoint.features[i].geometry.coordinates[1]);
if (
(jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
(jsonPoint.features[i].geometry.coordinates[1] === coords[1])
) {
jsonPoint.features.splice(i, 1)
}
}
drawLine(jsonPoint)
addMeasureRes(jsonPoint)
map.getSource('points' + layerId).setData(jsonPoint)
}
}
}
/**
* 计算长度
* @param {*} pointList
* @returns
*/
function getLength(pointList) {
const line = turf.lineString(pointList)
let len = turf.length(line)
if (len < 1) {
len = Math.round(len * 1000) + '米'
} else {
len = len.toFixed(2) + '公里'
}
return len
}
/**
* 移除dom
*/
function removedom() {
const dom = document.getElementsByClassName('measure-result ' + layerId)
const len = dom.length
if (len) {
for (let i = len - 1; i >= 0; i--) {
if (dom[i]) dom[i].remove()
}
}
}
/**
* 移除图层
*/
function removeLayer() {
jsonPoint.features = []
jsonLine.features = []
map.removeLayer('points' + layerId)
map.removeLayer('line' + layerId)
removedom()
}
/**
* 鼠标move事件
* @param {} _e
*/
function mouseMove(_e) {
if (isMeasure) {
map.getCanvas().style.cursor = 'default'
var coords = [_e.lngLat.lng, _e.lngLat.lat]
const jsonp = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coords,
},
}
map.getSource('point-move' + layerId).setData(jsonp)
if (jsonPoint.features.length > 0) {
moveTip.remove();
const pointList = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
pointList.push(coord)
}
pointList.push(coords)
const prev = jsonPoint.features[jsonPoint.features.length - 1]
const jsonl = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [prev.geometry.coordinates, coords],
},
}
map.getSource('line-move' + layerId).setData(jsonl)
ele.innerHTML = `总长:<span style="color:red">${getLength(pointList)}</span></br> <span style="color:#999;">单击确定地点,双击结束</span>`
tooltip.setLngLat(coords)
} else {
moveTip.setLngLat(coords)
}
}
}
/**
* 绘制完成
* @param {*} _e
*/
function finish(_e) {
if (isMeasure) {
setTimeout(() => {
isMeasure = false
var coords = [_e.lngLat.lng, _e.lngLat.lat]
removePoint(coords)
map.removeLayer('point-move' + layerId)
map.removeLayer('line-move' + layerId)
map.getCanvas().style.cursor = 'default'
tooltip.remove()
editLine();
}, 100);
}
}
map.on('click', 'points' + layerId, (e) => {
if (!isMeasure) {
removePointID(e.features[0].properties.id)
}
});
map.on("mouseenter", 'points' + layerId, (_e) => {
if (!isMeasure) {
map.getCanvas().style.cursor = "pointer";
var coords = [_e.lngLat.lng, _e.lngLat.lat]
const ele = document.createElement('div')
ele.setAttribute('style', 'border:1px solid #f00;')
ele.setAttribute('class', 'measure-move')
const option = {
element: ele,
anchor: 'left',
offset: [5, -20],
}
ele.innerHTML = '单击可删除此点,拖拽可调整位置';
hoverTip = new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
}
});
map.on("mouseleave", 'points' + layerId, () => {
map.getCanvas().style.cursor = "";
hoverTip && hoverTip.remove()
});
map.on('click', function (_e) {
addPointforJSON(_e)
})
map.on('mousemove', function (_e) {
mouseMove(_e)
})
map.on('dblclick', function (_e) {
finish(_e)
})
/**
* 编辑测量线
*/
function editLine() {
// catchMark = createMarker()
UpdataGeolist()
map.on('mousemove', onMouseMove)
map.on('mousedown', onmousedown)
}
function onMouseMove(e) {
const moveCoord = [e.lngLat.lng, e.lngLat.lat]
if (jsonPoint.features.length > 1) {
// 计算当前指针与线段最近的点
pointOnLine = getNearestPointOnLine(Geolist, moveCoord) // 自己计算
const screenOnLine = Object.values(map.project(pointOnLine)) // 线上屏幕坐标
const screenP = [e.point.x, e.point.y]
const screenDist = screenDistance(screenOnLine, screenP) // 距离
if (screenDist < 15) {
isEdit = true
// catchMark.setLngLat(pointOnLine).addTo(map)
// catchMark.getElement().style.display = 'block'
} else {
isEdit = false
// catchMark.getElement().style.display = 'none'
}
} else {
isEdit = false
// catchMark.getElement().style.display = 'none'
map.dragPan.enable()
}
}
function onmousedown(e) {
if (isEdit) {
map.dragPan.disable()
let isExist = false
// 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
isExist = true
}
}
// 获取编辑点在列表中的位置
dragPointOrder = getDragCoords(pointOnLine, Geolist)
if (!isExist) {
// 添加编辑点
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: pointOnLine,
},
properties: {
id: String(new Date().getTime()),
},
}
jsonPoint.features.splice(dragPointOrder, 0, point)
// 更新绘制要素
updataFeature()
}
map.on('mousemove', onDrag)
map.on('mouseup', onMouseup)
}
}
function onDrag(e) {
// 开始计时
// var start = new Date().getTime()
const movePoint = [e.lngLat.lng, e.lngLat.lat]
// 点跟随鼠标移动
jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint
// 更新绘制要素
updataFeature()
// 计时结束
// var end1 = new Date().getTime()
// console.log('渲染时间:', end1 - start + 'ms')
}
// 更新绘制要素
function updataFeature() {
UpdataGeolist()
map.getSource('points' + layerId).setData(jsonPoint)
drawLine(jsonPoint)
addMeasureRes(jsonPoint)
}
function onMouseup(e) {
map.off('mousemove', onDrag)
map.dragPan.enable()
}
// 创建Marker
function createMarker() {
const markerParam = {
map: sdMap.map,
imgUrl: pointMark,
lngLat: [0, 0],
height: 13,
width: 13,
size: 13,
isDrag: false,
cursor: 'default',
}
const option = {
element: ele,
anchor: 'bottom-left',
}
ele.innerHTML = ''
// return sdMap.createMarker(markerParam)
return new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)
}
// 更新点集
function UpdataGeolist() {
Geolist = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
Geolist.push(coord)
}
}
// 计算点到直线最近距离的点
function getNearestPointOnLine(list, moveCoord) {
var dis, point1, point2
for (let i = 0; i < list.length - 1; i++) {
const distance = getNearestDistance(moveCoord, list[i], list[i + 1])
if (i === 0) {
dis = distance
point1 = list[i]
point2 = list[i + 1]
} else {
if (distance < dis) {
dis = distance
point1 = list[i]
point2 = list[i + 1]
}
}
}
const Point = getNearestPoint(moveCoord, point1, point2)
return Point
}
// 计算点point到线段point1, point2最近距离
function getNearestDistance(point, point1, point2) {
const P = {}
const A = {}
const B = {}
P.x = point[0]
P.y = point[1]
A.x = point1[0]
A.y = point1[1]
B.x = point2[0]
B.y = point2[1]
// 计算向量AP和向量AB的点积
const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)
// 计算向量AB的长度的平方
const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)
// 计算点P到线段AB的投影点C
const t = dotProduct / lengthSquare
const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) }
// 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
const isInside = dotProduct >= 0 && dotProduct <= lengthSquare
if (isInside) {
return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y))
} else {
return Math.min(
Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
)
}
}
// 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
function getNearestPoint(point, point1, point2) {
var x, y, x0, y0, x1, y1, x2, y2
x0 = point[0]
y0 = point[1]
x1 = point1[0]
y1 = point1[1]
x2 = point2[0]
y2 = point2[1]
if (x1 !== x2 && y1 !== y2) {
const a = (y2 - y1) / (x2 - x1)
const b = y1 - a * x1
const k2 = -1 / a
const b2 = y0 - k2 * x0
x = (b2 - b) / (a - k2)
y = a * x + b
} else if (x1 === x2) {
x = x1
y = y0
} else if (y1 === y2) {
x = x0
y = y1
}
// 点不能超出线段
if (x1 < x2) {
if (x2 < x) {
x = x2
} else if (x < x1) {
x = x1
}
} else {
if (x1 < x) {
x = x1
} else if (x < x2) {
x = x2
}
}
if (y1 < y2) {
if (y2 < y) {
y = y2
} else if (y < y1) {
y = y1
}
} else {
if (y1 < y) {
y = y1
} else if (y < y2) {
y = y2
}
}
// 点吸附端点
const screenX0 = Object.values(map.project([x0, y0])) // 屏幕坐标
const screenX1 = Object.values(map.project([x1, y1])) // 屏幕坐标
const screenX2 = Object.values(map.project([x2, y2])) // 屏幕坐标
const screenDistX1 = screenDistance(screenX0, screenX1) // 距离
const screenDistX2 = screenDistance(screenX0, screenX2) // 距离
if (screenDistX1 < 10) {
x = x1
y = y1
}
if (screenDistX2 < 10) {
x = x2
y = y2
}
return [x, y]
}
// 屏幕距离
function screenDistance(point1, point2) {
const x2 = Math.pow(point1[0] - point2[0], 2)
const y2 = Math.pow(point1[1] - point2[1], 2)
const dist = Math.sqrt(x2 + y2)
return dist
}
// 计算编辑点在线段上的添加位置
function getDragCoords(coords, list) {
var x, y, x1, y1, x2, y2
let index = 0
x = coords[0]
y = coords[1]
for (let i = 0; i < list.length - 1; i++) {
x1 = list[i][0]
y1 = list[i][1]
x2 = list[i + 1][0]
y2 = list[i + 1][1]
if (x === x1 && y === y1) {
index = i
break
} else {
// 计算线段的长度
const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
// 计算点到线段起点的距离
const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
// 计算点到线段终点的距离
const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2))
// 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
if (Math.abs(length - distance1 - distance2) < 0.00001) {
index = i + 1
break
}
}
}
return index
}
}
/**
* 测量面积
* @param {*} layerId
*/
measureArea(layerId) {
this.layerAreaList.push(layerId)
var isMeasure = true
const map = this.map
const sdMap = this.sdMap
map.doubleClickZoom.disable()
let catchMark = null
let isEdit = false
let Geolist = []
let dragPointOrder = 0
let pointOnLine = [0, 0]
const jsonPoint = {
type: 'FeatureCollection',
features: [],
}
const jsonLine = {
type: 'FeatureCollection',
features: [],
}
const ele = document.createElement('div')
ele.setAttribute('class', 'measure-move')
const option = {
element: ele,
anchor: 'left',
offset: [8, 0],
}
const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)
map.addSource('points-area' + layerId, {
type: 'geojson',
data: jsonPoint,
})
map.addSource('point-move' + layerId, {
type: 'geojson',
data: jsonPoint,
})
map.addSource('line-area' + layerId, {
type: 'geojson',
data: jsonLine,
})
map.addSource('line-move' + layerId, {
type: 'geojson',
data: jsonLine,
})
map.addLayer({
id: 'line-move' + layerId,
type: 'line',
source: 'line-move' + layerId,
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65,
'line-dasharray': [5, 2],
},
})
map.addLayer({
id: 'line-area' + layerId,
type: 'fill',
source: 'line-area' + layerId,
paint: {
'fill-color': '#ff0000',
'fill-opacity': 0.1,
},
})
map.addLayer({
id: 'line-area-stroke' + layerId,
type: 'line',
source: 'line-area' + layerId,
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65,
},
})
map.addLayer({
id: 'points-area' + layerId,
type: 'circle',
source: 'points-area' + layerId,
paint: {
'circle-color': '#ffffff',
'circle-radius': 3.5,
'circle-stroke-width': 1.5,
'circle-stroke-color': '#ff0000',
},
})
map.addLayer({
id: 'point-move' + layerId,
type: 'circle',
source: 'point-move' + layerId,
paint: {
'circle-color': '#ffffff',
'circle-radius': 3.5,
'circle-stroke-width': 1.5,
'circle-stroke-color': '#ff0000',
},
})
this.setDistance = () => {
isMeasure = false
map.removeLayer('point-move' + layerId)
map.removeLayer('line-move' + layerId)
return isMeasure
}
/**
* 添加点
* @param {*} _e
*/
function addPointforJSON(_e) {
if (isMeasure) {
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [_e.lngLat.lng, _e.lngLat.lat],
},
properties: {
id: String(new Date().getTime()),
},
}
jsonPoint.features.push(point)
map.getSource('points-area' + layerId).setData(jsonPoint)
addMeasureRes(jsonPoint)
}
}
/**
* 添加dom
* @param {*} jsonPoint 点集
*/
function addMeasureRes(jsonPoint) {
if (jsonPoint.features.length > 0) {
removedom()
const pointList = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coords = jsonPoint.features[i].geometry.coordinates
pointList.push(coords)
const close = document.createElement('div')
close.setAttribute('class', `measure-result ${layerId} close`)
close.onclick = (__e) => {
// 移除点
__e.stopPropagation()
removePoint(coords)
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
}
if ((jsonPoint.features.length === i + 1) & !isMeasure) {
const clear = document.createElement('div')
clear.setAttribute('class', `measure-result ${layerId} clear`)
clear.onclick = (__e) => {
// 全部移除
__e.stopPropagation()
removeLayer()
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
}
const edit = document.createElement('div')
edit.setAttribute('class', `measure-result ${layerId} edit`)
edit.onclick = (__e) => {
// 编辑
__e.stopPropagation()
map.off('mousemove', onMouseMove)
map.off('mousedown', onmousedown)
if (catchMark) {
catchMark.remove()
}
editArea()
}
const element = document.createElement('div')
element.setAttribute('class', 'measure-result ' + layerId)
const option = {
element: element,
anchor: 'left',
offset: [0, 0],
}
element.innerHTML = getArea(pointList)
element.appendChild(edit)
element.appendChild(clear)
element.appendChild(close)
new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
} else {
const option = {
element: close,
anchor: 'left',
offset: [5, -15],
}
new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
}
}
}
}
/**
* 计算面积
* @param {*} pointList
* @returns
*/
function getArea(pointList) {
pointList.push(pointList[0])
const polygon = turf.polygon([pointList])
let area = turf.area(polygon)
if (area < 10000) {
area = Math.round(area) + '平方米'
} else {
area = (area / 1000000).toFixed(2) + '平方公里'
}
return area
}
/**
* 移除点
* @param {*} coords 点坐标
*/
function removePoint(coords) {
if (jsonPoint.features.length > 0) {
if (jsonPoint.features.length === 3) {
jsonPoint.features = []
jsonLine.features = []
map.getSource('points-area' + layerId).setData(jsonPoint)
map.getSource('line-area' + layerId).setData(jsonLine)
removedom()
} else {
for (let i = 0; i < jsonPoint.features.length; i++) {
if (
(jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
(jsonPoint.features[i].geometry.coordinates[1] === coords[1])
) {
jsonPoint.features.splice(i, 1)
}
}
addMeasureRes(jsonPoint)
map.getSource('points-area' + layerId).setData(jsonPoint)
const pointList = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
pointList.push(coord)
}
const pts = pointList.concat([pointList[0]])
const jsona = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [pts],
},
}
map.getSource('line-area' + layerId).setData(jsona)
}
}
}
/**
* 移除dom
*/
function removedom() {
const dom = document.getElementsByClassName('measure-result ' + layerId)
const len = dom.length
if (len) {
for (let i = len - 1; i >= 0; i--) {
if (dom[i]) dom[i].remove()
}
}
}
/**
* 移除图层
*/
function removeLayer() {
jsonPoint.features = []
jsonLine.features = []
map.removeLayer('points-area' + layerId)
map.removeLayer('line-area' + layerId)
map.removeLayer('line-area-stroke' + layerId)
removedom()
}
/**
* 鼠标move事件
* @param {} _e
*/
function mouseMove(_e) {
if (isMeasure) {
map.getCanvas().style.cursor = 'default'
const coords = [_e.lngLat.lng, _e.lngLat.lat]
const jsonp = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coords,
},
}
map.getSource('point-move' + layerId).setData(jsonp)
if (jsonPoint.features.length > 0) {
if (jsonPoint.features.length === 1) {
const prev = jsonPoint.features[jsonPoint.features.length - 1]
const jsonl = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [prev.geometry.coordinates, coords],
},
}
map.getSource('line-move' + layerId).setData(jsonl)
} else {
const json = {
type: 'FeatureCollection',
features: [],
}
map.getSource('line-move' + layerId).setData(json)
const pointList = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
pointList.push(coord)
}
pointList.push(coords)
const pts = pointList.concat([pointList[0]])
const jsona = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [pts],
},
}
map.getSource('line-area' + layerId).setData(jsona)
ele.innerHTML = getArea(pointList)
tooltip.setLngLat(coords)
}
}
}
}
/**
* 绘制完成
* @param {*} _e
*/
function finish(_e) {
if (isMeasure) {
isMeasure = false
const coords = [_e.lngLat.lng, _e.lngLat.lat]
removePoint(coords)
map.removeLayer('point-move' + layerId)
map.removeLayer('line-move' + layerId)
map.getCanvas().style.cursor = 'default'
tooltip.remove()
}
}
map.on('click', function (_e) {
addPointforJSON(_e)
})
map.on('dblclick', function (_e) {
finish(_e)
})
map.on('mousemove', function (_e) {
mouseMove(_e)
})
/**
* 编辑测量面
*/
function editArea() {
catchMark = createMarker()
UpdataGeolist()
map.on('mousemove', onMouseMove)
map.on('mousedown', onmousedown)
}
function onMouseMove(e) {
const moveCoord = [e.lngLat.lng, e.lngLat.lat]
if (jsonPoint.features.length > 1) {
// 计算当前指针与线段的距离
pointOnLine = getNearestPointOnLine(Geolist, moveCoord) // 线上实际坐标
const screenOnLine = Object.values(map.project(pointOnLine)) // 线上屏幕坐标
const screenP = [e.point.x, e.point.y]
const screenDist = screenDistance(screenOnLine, screenP) // 距离
if (screenDist < 15) {
isEdit = true
catchMark.setLngLat(pointOnLine).addTo(map)
catchMark.getElement().style.display = 'block'
} else {
isEdit = false
catchMark.getElement().style.display = 'none'
}
} else {
isEdit = false
catchMark.getElement().style.display = 'none'
map.dragPan.enable()
}
}
function onmousedown(e) {
if (isEdit) {
map.dragPan.disable()
let isExist = false
// 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
isExist = true
}
}
// 获取编辑点在列表中的位置
dragPointOrder = getDragCoords(pointOnLine, Geolist)
if (!isExist) {
// 添加编辑点
const point = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: pointOnLine,
},
properties: {
id: String(new Date().getTime()),
},
}
jsonPoint.features.splice(dragPointOrder, 0, point)
// 更新绘制要素
updataFeature()
}
map.on('mousemove', onDrag)
map.on('mouseup', onMouseup)
}
}
function onDrag(e) {
const movePoint = [e.lngLat.lng, e.lngLat.lat]
// 点跟随鼠标移动
jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint
// 更新绘制要素
updataFeature()
}
// 更新绘制要素
function updataFeature() {
UpdataGeolist()
const pts = Geolist
const jsona = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [pts],
},
}
map.getSource('line-area' + layerId).setData(jsona)
map.getSource('points-area' + layerId).setData(jsonPoint)
addMeasureRes(jsonPoint)
}
function onMouseup(e) {
map.off('mousemove', onDrag)
map.dragPan.enable()
}
// 创建Marker
function createMarker() {
const markerParam = {
map: sdMap.map,
imgUrl: pointMark,
lngLat: [0, 0],
height: 13,
width: 13,
size: 13,
isDrag: false,
cursor: 'default',
}
return sdMap.createMarker(markerParam)
}
// 更新点集
function UpdataGeolist() {
Geolist = []
for (let i = 0; i < jsonPoint.features.length; i++) {
const coord = jsonPoint.features[i].geometry.coordinates
Geolist.push(coord)
}
Geolist.push(Geolist[0])
}
// 计算点到直线最近距离的点
function getNearestPointOnLine(list, moveCoord) {
var dis, point1, point2
for (let i = 0; i < list.length - 1; i++) {
const distance = getNearestDistance(moveCoord, list[i], list[i + 1])
if (i === 0) {
dis = distance
point1 = list[i]
point2 = list[i + 1]
} else {
if (distance < dis) {
dis = distance
point1 = list[i]
point2 = list[i + 1]
}
}
}
const Point = getNearestPoint(moveCoord, point1, point2)
return Point
}
// 计算点point到线段point1, point2最近距离
function getNearestDistance(point, point1, point2) {
const P = {}
const A = {}
const B = {}
P.x = point[0]
P.y = point[1]
A.x = point1[0]
A.y = point1[1]
B.x = point2[0]
B.y = point2[1]
// 计算向量AP和向量AB的点积
const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)
// 计算向量AB的长度的平方
const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)
// 计算点P到线段AB的投影点C
const t = dotProduct / lengthSquare
const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) }
// 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
const isInside = dotProduct >= 0 && dotProduct <= lengthSquare
if (isInside) {
return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y))
} else {
return Math.min(
Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
)
}
}
// 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
function getNearestPoint(point, point1, point2) {
var x, y, x0, y0, x1, y1, x2, y2
x0 = point[0]
y0 = point[1]
x1 = point1[0]
y1 = point1[1]
x2 = point2[0]
y2 = point2[1]
if (x1 !== x2 && y1 !== y2) {
const a = (y2 - y1) / (x2 - x1)
const b = y1 - a * x1
const k2 = -1 / a
const b2 = y0 - k2 * x0
x = (b2 - b) / (a - k2)
y = a * x + b
} else if (x1 === x2) {
x = x1
y = y0
} else if (y1 === y2) {
x = x0
y = y1
}
// 点不能超出线段
if (x1 < x2) {
if (x2 < x) {
x = x2
} else if (x < x1) {
x = x1
}
} else {
if (x1 < x) {
x = x1
} else if (x < x2) {
x = x2
}
}
if (y1 < y2) {
if (y2 < y) {
y = y2
} else if (y < y1) {
y = y1
}
} else {
if (y1 < y) {
y = y1
} else if (y < y2) {
y = y2
}
}
// 点吸附端点
const screenX0 = Object.values(map.project([x0, y0])) // 屏幕坐标
const screenX1 = Object.values(map.project([x1, y1])) // 屏幕坐标
const screenX2 = Object.values(map.project([x2, y2])) // 屏幕坐标
const screenDistX1 = screenDistance(screenX0, screenX1) // 距离
const screenDistX2 = screenDistance(screenX0, screenX2) // 距离
if (screenDistX1 < 10) {
x = x1
y = y1
}
if (screenDistX2 < 10) {
x = x2
y = y2
}
return [x, y]
}
// 屏幕距离
function screenDistance(point1, point2) {
const x2 = Math.pow(point1[0] - point2[0], 2)
const y2 = Math.pow(point1[1] - point2[1], 2)
const dist = Math.sqrt(x2 + y2)
return dist
}
// 计算编辑点在线段上的添加位置
function getDragCoords(coords, list) {
var x, y, x1, y1, x2, y2
let index = 0
x = coords[0]
y = coords[1]
for (let i = 0; i < list.length - 1; i++) {
x1 = list[i][0]
y1 = list[i][1]
x2 = list[i + 1][0]
y2 = list[i + 1][1]
if (x === x1 && y === y1) {
index = i
break
} else {
// 计算线段的长度
const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
// 计算点到线段起点的距离
const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
// 计算点到线段终点的距离
const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2))
// 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
if (Math.abs(length - distance1 - distance2) < 0.00001) {
index = i + 1
break
}
}
}
return index
}
}
/**
* 清除所有测量要素
*/
clearMeasureAll() {
const dom = document.getElementsByClassName('measure-result')
const len = dom.length
if (len) {
for (let i = len - 1; i >= 0; i--) {
if (dom[i]) dom[i].remove()
}
}
if (this.layerDistanceList) {
for (let i = 0; i < this.layerDistanceList.length; i++) {
const layerid = this.layerDistanceList[i]
try {
this.map.removeLayer('points' + layerid)
this.map.removeLayer('line' + layerid)
this.map.removeLayer('point-move' + layerid)
this.map.removeLayer('line-move' + layerid)
} catch (error) {
console.log(error)
}
}
}
this.layerDistanceList = []
if (this.layerAreaList) {
for (let i = 0; i < this.layerAreaList.length; i++) {
const layerid = this.layerAreaList[i]
try {
this.map.removeLayer('points-area' + layerid)
this.map.removeLayer('line-area' + layerid)
this.map.removeLayer('line-area-stroke' + layerid)
this.map.removeLayer('point-move' + layerid)
this.map.removeLayer('line-move' + layerid)
} catch (error) {
console.log(error)
}
}
}
this.layerAreaList = []
this.map.doubleClickZoom.enable()
}
}
// css
.measure-move {
background-color: white;
border-radius: 3px;
height: 16px;
line-height: 16px;
padding: 0 3px;
font-size: 12px;
box-shadow: 0 0 0 1px #ccc;
float: right;
cursor: default;
}
.measure-result {
background-color: white;
border-radius: 3px;
height: 16px;
line-height: 16px;
padding: 0 3px;
font-size: 12px;
box-shadow: 0 0 0 1px #ccc;
float: right;
cursor: default;
z-index: 10;
}
.close {
width: 3px;
height: 14px;
text-align: center;
border-radius: 3px;
padding-top: 0px;
padding-right: 10px;
box-shadow: 0 0 0 0px #ccc;
cursor: pointer;
background: url('~@/assets/mapboxgis/close.png') no-repeat center;
border: 1px solid rgba(100, 100, 100, 0)
}
.clear {
width: 3px;
height: 14px;
text-align: center;
border-radius: 3px;
padding-right: 10px;
box-shadow: 0 0 0 0px #ccc;
cursor: pointer;
float: right;
background: url('~@/assets/mapboxgis/close.png') no-repeat center;
border: 1px solid rgba(100, 100, 100, 0)
}
.edit {
width: 3px;
height: 14px;
text-align: center;
border-radius: 3px;
padding-right: 10px;
box-shadow: 0 0 0 0px #ccc;
cursor: pointer;
float: right;
/* background: url(../measureicon/edit.png) no-repeat center; */
border: 1px solid rgba(100, 100, 100, 0)
}
.close:hover {
border: 1px solid rgba(52, 98, 152, 1);
}
.clear:hover {
border: 1px solid rgba(52, 98, 152, 1);
}
.edit:hover {
border: 1px solid rgba(52, 98, 152, 1);
}