一、公共数据
javascript
复制代码
const rangingObj = ref([]); // 测距点位集合
const totalRang = ref(0); // 测距距离长度
const isFinishRang = ref(false); // 是否为新的测量,true为是,false为否
// ----------角度测量----------
const angleObj = ref([]); // 角度测量线条点位集合
const anglePointObj = ref([]); // 角度测量点位集合
const isFinishAngle = ref(false); //是否为新的测量,true为是,false为否
// ----------面积测量----------
const areaObj = ref([]); // 面积测量点位集合
const isFinishArea = ref(false); // 是否为新的测量,true为是,false为否
let rightClickEvent = ref(null); // 右键单击事件
let mouseMoveType = ref(null); // 当前鼠标移动的操作类型
二、公共函数
1、屏幕坐标转经纬度
javascript
复制代码
const converted = (x, y, height = 0) => {
// 屏幕坐标转换为二维笛卡尔坐标
let twoCoordinate = new Cesium.Cartesian2(x, y);
// 获取屏幕坐标的对应椭球面位置
let wordCoordinate = viewer.scene.camera.pickEllipsoid(
twoCoordinate,
viewer.scene.globe.ellipsoid
);
// 笛卡尔坐标转弧度
let cartographic = Cesium.Cartographic.fromCartesian(
wordCoordinate,
viewer.scene.globe.ellipsoid,
new Cesium.Cartographic()
);
// Cesium.Math.toDegrees 将弧度转换成经纬度
let lon = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
// 返回转换结果
return {
lon,
lat,
height,
};
};
2、世界坐标转经纬度
javascript
复制代码
const wordConverted = (x, y, z) => {
// 坐标转换
let cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic({
x,
y,
z,
});
// 获取经纬度
let lon = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
// 返回转换结果
return [lon, lat];
};
3、计算中心点
javascript
复制代码
// 计算中心点
const calcCenter = (value) => {
// 计算中心点
let pointArray = turf.polygon([value]);
let center = turf.centerOfMass(pointArray);
// 获取经纬度
let lon = center.geometry.coordinates[0];
let lat = center.geometry.coordinates[1];
// 返回计算结果
return { lon, lat };
};
4、计算角度
javascript
复制代码
// 计算角度
const calcAngle = (value) => {
// 将经纬度转换为笛卡尔坐标
const cartesian1 = Cesium.Cartesian3.fromDegrees(value[0].lon, value[0].lat);
const cartesian2 = Cesium.Cartesian3.fromDegrees(value[1].lon, value[1].lat);
const cartesian3 = Cesium.Cartesian3.fromDegrees(value[2].lon, value[2].lat);
// 构造向量(从顶点point2指向point1和point3)
const vec1 = Cesium.Cartesian3.subtract(
cartesian1,
cartesian2,
new Cesium.Cartesian3()
);
const vec2 = Cesium.Cartesian3.subtract(
cartesian3,
cartesian2,
new Cesium.Cartesian3()
);
// 归一化向量(单位向量)
Cesium.Cartesian3.normalize(vec1, vec1);
Cesium.Cartesian3.normalize(vec2, vec2);
// 计算点积
const dot = Cesium.Cartesian3.dot(vec1, vec2);
// 处理浮点误差(确保值在[-1, 1]范围内)
const clampedDot = Math.min(Math.max(dot, -1.0), 1.0);
// 计算夹角(弧度转角度)
const angleRad = Math.acos(clampedDot);
// 返回计算结果
return Cesium.Math.toDegrees(angleRad);
};
三、测量计算
1、左键单击事件
javascript
复制代码
// 左键单击
const leftClick = () => {
// 添加用户输入监听范围(element)
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// 处理用户输入事件
handler.setInputAction((event) => {
switch (indexStore.mapTools) {
case 1: // 距离测量
// 修改鼠标移动操作类型
mouseMoveType.value = "calcLength";
measureDistance(event);
break;
case 2: // 角度测量
// 修改鼠标移动操作类型
mouseMoveType.value = "calcAngle";
measureAngle(event);
break;
case 3: // 面积测量
// 修改鼠标移动操作类型
mouseMoveType.value = "calcArea";
measureArea(event);
break;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
2、测量函数
javascript
复制代码
// 距离测量
const measureDistance = (event) => {
// 判断是否完成本次测量
if (isFinishRang.value) {
clearDistance();
mouseMoveType.value = "calcLength";
}
// 屏幕坐标转经纬度
let currentPosition = converted(event.position.x, event.position.y);
let { lon, lat } = currentPosition;
if (rangingObj.value.length == 0) {
rangingObj.value.push(currentPosition);
} else {
// 保存测距点
rangingObj.value.push(currentPosition);
// 获取倒数第二位
let beforePosition = rangingObj.value[rangingObj.value.length - 3];
// 计算倒数第二位和当前位置的距离
calcLength([beforePosition.lon, beforePosition.lat], [lon, lat]);
}
// 添加测距点
addPoint(currentPosition, crypto.randomUUID(), "distance");
// 绘制标签
addLabel(
{ lon, lat, height: 200 },
crypto.randomUUID(),
"distance",
`${totalRang.value.toFixed(2)}(公里)`
);
// 右键单击事件监听
if (!rightClickEvent.value) {
rightClickEvent.value = viewer.screenSpaceEventHandler.setInputAction(
(arg) => {
let lastPosition = converted(arg.position.x, arg.position.y);
let { lon, lat } = lastPosition;
// 保存测距点
rangingObj.value.push(lastPosition);
// 获取倒数第二位
let beforePosition = rangingObj.value[rangingObj.value.length - 3];
// 计算倒数第二位和当前位置的距离
calcLength([beforePosition.lon, beforePosition.lat], [lon, lat]);
// 添加绘制
addPoint(lastPosition, crypto.randomUUID(), "distance");
// 绘制标签
addLabel(
{ lon, lat, height: 200 },
crypto.randomUUID(),
"distance",
`${totalRang.value.toFixed(2)}(公里)`
);
// 已完成本次测量
isFinishRang.value = true;
},
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
}
};
// 测量角度
const measureAngle = (event) => {
// 判断是否完成本次测量
if (isFinishAngle.value) {
clearAngleDistance();
mouseMoveType.value = "calcAngle";
}
// 屏幕坐标转经纬度
let currentPosition = converted(event.position.x, event.position.y);
// 保存点位
angleObj.value.push(currentPosition);
// 添加点位
addPoint(currentPosition, crypto.randomUUID(), "distanceAngle");
// 如果点数量>3结束绘制,并计算
if (angleObj.value.length > 3) {
// 删除最后一个点
angleObj.value.pop();
// -----计算三角形的中心点-----
let temp = angleObj.value.flatMap((obj) => [[obj.lon, obj.lat]]);
temp.push([angleObj.value[0].lon, angleObj.value[0].lat]);
// 计算多边形的中心点
let center = calcCenter(temp);
// -----计算夹角角度-----
const angleDeg = calcAngle(angleObj.value);
// -----添加label标签-----
addLabel(
{
lon: center.lon,
lat: center.lat,
height: 10,
},
crypto.randomUUID(),
`distanceAngle`,
`${angleDeg}°`
);
// 清除鼠标移动操作类型
mouseMoveType.value = null;
// 已完成本次测量
isFinishAngle.value = true;
}
};
// 测量面积
const measureArea = (event) => {
// 判断是否完成了本次测量
if (isFinishArea.value) {
clearAreaDistance();
mouseMoveType.value = "calcArea";
}
// 保存点位
let { lon, lat } = converted(event.position.x, event.position.y);
areaObj.value.push(Cesium.Cartesian3.fromDegrees(lon, lat));
if (!findModelById("areaModel")) {
modeClass.polygon.entities({
id: "areaModel",
outline: true,
outlineColor: Cesium.Color.CYAN,
outlineWidth: 20,
height: 0,
hierarchy: new Cesium.CallbackProperty(() => {
return new Cesium.PolygonHierarchy(areaObj.value);
}, false),
material: Cesium.Color.CYAN.withAlpha(0.3),
});
}
// 右键监听事件
if (!rightClickEvent.value) {
rightClickEvent.value = viewer.screenSpaceEventHandler.setInputAction(
(arg) => {
// 计算中心点
let temp = [];
// 格式化点数据
areaObj.value.forEach((item) => {
temp.push(wordConverted(item.x, item.y, item.z));
});
// 最后一个点与第一个点相同
temp.push(
wordConverted(
areaObj.value[0].x,
areaObj.value[0].y,
areaObj.value[0].z
)
);
// 计算多边形的中心点
let center = calcCenter(temp);
// 格式化数据
let pointArray = turf.polygon([temp]);
// 计算多边形面积,默认为平方米,转换为平方公里(除以1000000)
let area = (turf.area(pointArray) / 1000000).toFixed(6);
// 添加label
addLabel(
{
lon: center.lon,
lat: center.lat,
height: 10,
},
"areaModelLabel",
"areaModel",
`${area}(平方公里)`
);
// 已完成本次测量
isFinishArea.value = true;
},
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
}
};
3、鼠标移动事件
javascript
复制代码
viewer.screenSpaceEventHandler.setInputAction((arg) => {
switch (mouseMoveType.value) {
case "calcLength": // 距离测量移动事件
let calcPost = converted(arg.endPosition.x, arg.endPosition.y);
if (rangingObj.value.length > 1) {
rangingObj.value.pop();
}
rangingObj.value.push(calcPost);
// 绘制线条
if (rangingObj.value.length == 2) {
modeClass.line.entities({
id: crypto.randomUUID(),
name: `distance`,
// 参数依次为[经度1, 纬度1, 高度1, 经度2, 纬度2, 高度2]
positions: new Cesium.CallbackProperty(() => {
return Cesium.Cartesian3.fromDegreesArrayHeights(
rangingObj.value.flatMap((obj) => [obj.lon, obj.lat, 0])
);
}, false),
width: 4,
material: new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.TOMATO,
outlineWidth: 2,
outlineColor: Cesium.Color.YELLOW,
}),
clampToGround: false,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
150000000
),
});
}
break;
case "calcAngle": // 角度测量移动事件
let calcAngle = converted(arg.endPosition.x, arg.endPosition.y);
if (angleObj.value.length > 1) {
angleObj.value.pop();
}
// 保存点位
angleObj.value.push(calcAngle);
// 绘制线条
if (angleObj.value.length == 2) {
modeClass.line.entities({
id: crypto.randomUUID(),
name: `distanceAngle`,
// 参数依次为[经度1, 纬度1, 高度1, 经度2, 纬度2, 高度2]
positions: new Cesium.CallbackProperty(() => {
return Cesium.Cartesian3.fromDegreesArrayHeights(
angleObj.value.flatMap((obj) => [obj.lon, obj.lat, 0])
);
}, false),
width: 4,
material: new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.TOMATO,
outlineWidth: 2,
outlineColor: Cesium.Color.YELLOW,
}),
clampToGround: false,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
150000000
),
});
}
break;
case "calcArea": // 面积测量移动事件
let areaPost = converted(arg.endPosition.x, arg.endPosition.y);
if (areaObj.value.length > 1) {
areaObj.value.pop();
}
areaObj.value.push(
Cesium.Cartesian3.fromDegrees(areaPost.lon, areaPost.lat)
);
break;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
4、清除测量函数
javascript
复制代码
// 清除距离测量
const clearDistance = () => {
// 清除距离测量右键单击事件
viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
// 清除右键单击事件
rightClickEvent.value = null;
// 清空鼠标移动操作类型
mouseMoveType.value = null;
// 清除点位数据
rangingObj.value = [];
// 清理距离数据
totalRang.value = 0;
// 是否为新的测量,true为是,false为否
isFinishRang.value = false;
// 删除所有距离测量模型
let entitiesArr = viewer.entities.values;
for (let i = 0; i < entitiesArr.length; i++) {
if (entitiesArr[i]._name == "distance") {
removeModelById(entitiesArr[i]._id);
i--;
}
}
};
// 清除角度测量
const clearAngleDistance = () => {
// 清除距离测量右键单击事件
viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
// 清除右键单击事件
rightClickEvent.value = null;
// 清空鼠标移动操作类型
mouseMoveType.value = null;
// 是否为新的测量,true为是,false为否
isFinishAngle.value = false;
// 清除角度测量线条点位集合
angleObj.value = [];
// 清除角度测量点位集合
anglePointObj.value = [];
// 删除所有距离测量模型
let entitiesArr = viewer.entities.values;
for (let i = 0; i < entitiesArr.length; i++) {
if (entitiesArr[i]._name == "distanceAngle") {
removeModelById(entitiesArr[i]._id);
i--;
}
}
};
// 清除面积测量
const clearAreaDistance = () => {
// 清除距离测量右键单击事件
viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
// 清除右键单击事件
rightClickEvent.value = null;
// 清空鼠标移动操作类型
mouseMoveType.value = null;
// 是否为新的测量,true为是,false为否
isFinishArea.value = false;
// 清除面积点位集合
areaObj.value = [];
// 清除所有测试的模型
removeModelById("areaModel");
removeModelById("areaModelLabel");
};