教同事用Cesium写一个测量工具并支持三角测量,同事请我吃了一顿大餐

大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第39/100篇文章。
可视化&Webgis交流群+V:brown_7778(备注来意)

前言

最近在开发智慧城市的项目,产品想让同事基于Cesium开发一个测量工具,需要支持长度测量面积测量以及三角测量,但同事挠了挠头,说做这个有点费劲,还反问了产品:做这功能有啥意义?

产品经理:测量工具在智慧城市中发挥了重要的作用,通过对城市道路,地形,建筑物,场地等的精确测量,确保施工规划能够与现实场景精准吻合,节省人力以及施工成本。

对桥梁、隧道、地铁、管网等城市基础设施进行结构健康监测,安装传感器,实时监测结构体震动以及结构体偏移量等数据,确保设施安全运行并能够提前发现问题,防患于未然。

开发同事听完,觉得还蛮有道理,看向我:浪浪,如何应对?

我:呐,拿走直接抄!下班请吃铜锅涮肉!

三角测量

先来了解下三角测量:是一种基于三角形几何原理的测量方法,用于确定未知点的位置。它通过已知基线(即两个已知点之间的距离)和从这两个已知点测量的度,计算出目标点的精确位置。

例如在建筑施工中,工程师使用三角测量法来测量楼体高度、桥梁等结构的位置角度,确保建筑的精准施工。

代码解析

接下来看下这个MeasureTool类,主要包含以下功能:

  1. 坐标转换:整理了地理坐标(WGS84)与笛卡尔坐标(Cartesian)之间的转换功能。
  2. 拾取功能:通过屏幕坐标拾取场景中的三维位置,并判断该位置是位于模型上、地形上还是椭球体表面。
  3. 距离测量:绘制线段,并在场景中显示起点和终点之间的距离。
  4. 面积测量:通过给定的一组坐标,计算它们组成的多边形面积。
  5. 三角测量:绘制一个三角形来测量水平距离、直线距离和高度差。

坐标转换功能

  • transformWGS84ToCartesian: 将WGS84坐标(经度、纬度、高度)转换为Cesium中的三维笛卡尔坐标。

  • transformCartesianToWGS84: 将Cesium的三维笛卡尔坐标转换为WGS84坐标。

核心代码:

transformWGS84ToCartesian(position, alt) {
    
    
   return position
     ? Cesium.Cartesian3.fromDegrees(
         position.lng || position.lon,
         position.lat,
         (position.alt = alt || position.alt),
         Cesium.Ellipsoid.WGS84
       )
     : Cesium.Cartesian3.ZERO;
 }

transformCartesianToWGS84(cartesian) {
    
    
   var ellipsoid = Cesium.Ellipsoid.WGS84;
   var cartographic = ellipsoid.cartesianToCartographic(cartesian);
   return {
    
    
     lng: Cesium.Math.toDegrees(cartographic.longitude),
     lat: Cesium.Math.toDegrees(cartographic.latitude),
     alt: cartographic.height,
   };
 }

Cesium的Cartesian3.fromDegreesEllipsoid.WGS84.cartesianToCartographic方法分别用于实现经纬度与笛卡尔坐标系的相互转换。

拾取功能

拾取功能允许通过屏幕像素坐标来获取3D场景中的位置。主要依赖scene.pickPositionscene.globe.pick来实现拾取。

核心代码:

getCatesian3FromPX(px) {
    
    
   var picks = this._viewer.scene.drillPick(px);
   var cartesian = this._viewer.scene.pickPosition(px);
   if (!cartesian) {
    
    
      var ray = this._viewer.scene.camera.getPickRay(px);
      cartesian = this._viewer.scene.globe.pick(ray, this._viewer.scene);
   }
   return cartesian;
}

这里首先尝试从3D模型或地形上拾取位置,如果未能拾取到模型或地形上的点,则尝试通过射线投射到椭球体表面。

距离测量

通过拾取点并记录每个点的坐标,计算相邻两个点的距离,并显示在Cesium场景中。通过ScreenSpaceEventHandler来捕获鼠标点击和移动事件。

核心代码:

drawLineMeasureGraphics(options = {
     
     }) {
    
    
   var positions = [];
   var _handlers = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
   _handlers.setInputAction(function (movement) {
    
    
     var cartesian = this.getCatesian3FromPX(movement.position);
     positions.push(cartesian);
   }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
   
   _handlers.setInputAction(function (movement) {
    
    
     var cartesian = this.getCatesian3FromPX(movement.endPosition);
     positions.pop();
     positions.push(cartesian);
   }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

   _handlers.setInputAction(function () {
    
    
     _handlers.destroy();
   }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}

测距的基本思想是通过鼠标点击获取多个点的坐标,然后计算每两个相邻点的距离。

面积测量

面积测量通过计算多个点围成的多边形的面积,基于Cesium的PolygonHierarchy实现多边形绘制。

核心代码:

getPositionsArea(positions) {
    
    
   let ellipsoid = Cesium.Ellipsoid.WGS84;
   let area = 0;
   positions.push(positions[0]); // 闭合多边形
   for (let i = 1; i < positions.length; i++) {
    
    
      let p1 = ellipsoid.cartographicToCartesian(this.transformWGS84ToCartographic(positions[i - 1]));
      let p2 = ellipsoid.cartographicToCartesian(this.transformWGS84ToCartographic(positions[i]));
      area += p1.x * p2.y - p2.x * p1.y;
   }
   return Math.abs(area) / 2.0;
}

这里通过一个简单的多边形面积公式(叉乘)来计算笛卡尔坐标下的面积。

三角测量

三角测量通过拾取三个点,计算它们之间的直线距离水平距离以及高度差,构建一个三角形并在场景中显示这些信息。

核心代码:

drawTrianglesMeasureGraphics(options = {
     
     }) {
    
    
   var _positions = [];
   var _handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
   _handler.setInputAction(function (movement) {
    
    
      var position = this.getCatesian3FromPX(movement.position);
      _positions.push(position);
      if (_positions.length === 3) _handler.destroy();
   }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

该方法核心思想是获取三个点的坐标,通过高度差来构建水平线和垂线,然后显示相应的距离和高度差信息。

使用

封装好,之后,使用起来就非常简单了。

import MeasureTool from "@/utils/cesiumCtrl/measure.js";
const measure = new MeasureTool(viewer);
**
 * 测距
 */
const onLineMeasure = () => {
    
    
  measure.drawLineMeasureGraphics({
    
    
    clampToGround: true,
    callback: (e) => {
    
    
      console.log("----", e);
    },
  });
};
/**
 * 测面积
 */
const onAreaMeasure = () => {
    
    
  measure.drawAreaMeasureGraphics({
    
    
    clampToGround: true,
    callback: () => {
    
    },
  });
};
/**
 * 三角量测
 */
const onTrianglesMeasure = () => {
    
    
  measure.drawTrianglesMeasureGraphics({
    
    
    callback: () => {
    
    },
  });
};

最后

这些测量工具都是依赖于Cesium提供的坐标转换、拾取以及事件处理机制,核心思路是通过ScreenSpaceEventHandler捕捉鼠标事件,获取坐标点,并通过几何算法计算距离、面积和高度。

【完整源码地址】:https://github.com/tingyuxuan2302/cesium-vue3-vite

如果认为有帮助,希望可以给我们一个免费的star,激励我们持续开源更多代码。

如果想系统学习Cesium,可以看下作者的Cesium系列教程《Cesium从入门到实战》,将Cesium的知识点进行串联,让不了解Cesium的小伙伴拥有一个完整的学习路线,学完后直接上手做项目,+作者:brown_7778(备注来意)了解课程细节。

另外有需要进可视化&Webgis交流群可以加我:brown_7778(备注来意),也欢迎数字孪生可视化领域的交流合作。

猜你喜欢

转载自blog.csdn.net/weixin_40580834/article/details/142910872