当前位置: 首页 > news >正文

Cesium距离测量、角度测量、面积测量

一、公共数据

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、屏幕坐标转经纬度

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、世界坐标转经纬度

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、计算中心点

// 计算中心点
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、计算角度

// 计算角度
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、左键单击事件

// 左键单击
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、测量函数

// 距离测量
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);// 添加labeladdLabel({lon: center.lon,lat: center.lat,height: 10,},"areaModelLabel","areaModel",`${area}(平方公里)`);// 已完成本次测量isFinishArea.value = true;},Cesium.ScreenSpaceEventType.RIGHT_CLICK);}
};

3、鼠标移动事件

  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、清除测量函数

// 清除距离测量
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");
};

相关文章:

  • Redis初识第一期
  • 1.线性表的顺序存储-顺序表
  • VAS5081电动工具专用3-8节串联电池监控芯片奇力科技
  • Javascript 单例模式
  • 【QT】 QGraphicsItem 获取点坐标的几种方法
  • vue3项目移动端实现进度条可手动滑动控制进度和点击控制进度
  • 我的世界进阶模组开发教程——开发机械动力附属模组(2)
  • 使用Python和PyTorch框架,基于RetinaNet模型进行目标检测,包含数据准备、模型训练、评估指标计算和可视化
  • linux 安装 opencv源码4.5.4记录
  • DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_学习计划日历示例(CalendarView01_20)
  • Python图像处理基础(六)
  • (十三)计算机视觉中的深度学习:特征表示、模型架构与视觉认知原理
  • 第29节 Node.js Query Strings
  • ETLCloud中数据脱敏规则的使用技巧
  • Linux中source和bash的区别
  • 深度学习环境搭建(pycharm+yolov5)
  • STM32外设学习之串口
  • asio之socket_ops
  • 【线程与线程池】线程数设置(四)
  • 在 Flask 或 Tornado 中返回渲染后的页面内容
  • 设计网站页面特效怎么做/宁波seo排名费用
  • 可以做盗版漫画网站吗/做关键词优化
  • 杨浦做网站/计算机培训
  • wordpress网站资源/制作网站免费
  • 长沙营销型网站建设制作/seo专员工作内容
  • 福州网站建设外包/网站关键词怎么添加