cesium方案论证分析

1. 介绍

这是一个涉及Cesium.js(一个用于Web的3D地球和地图的JavaScript库)和前后端交互的复杂项目。项目的主要功能包括地形平压、模型放置和调整、方案保存和查看等。

2. 分析

2.1. 场景加载

加载Cesium的Melbourne Photogrammetry的倾斜摄影作为底图,本身是贴地的,使用Cesium的primitives功能加载特定ID的数据集。

2.2. 地形平压

  • 激活画笔工具,用户可在地图上绘制多边形区域。
  • 绘制完成后,所选区域的地形将被“平压”,即该区域的地形高度将被统一至某一水平面,视觉效果变为类似水泥的平坦表面。
  • 平压操作完成后,系统将显示平压区域的坐标范围以及平压后的高度信息。

2.3. 模型放置

  • 预设三维模型库,包括购物商城、L型办公楼、政务大楼、教学楼等,模型默认尺寸用0.001
  • 用户选择模型后,激活画笔工具,在地图上绘制放置点。
  • 绘制完成后,系统根据点的坐标将模型放置到对应位置。
  • 提供调整按钮,允许用户对已放置的模型进行旋转、位置移动、放大缩小等操作。
  • 调整完成后,用户可提交方案,输入方案名称并保存至数据库。

2.4. 绘制区域

  • 用户可随时激活画笔工具,重新绘制或修改已存在的平压区域。
  • 系统将实时更新绘制区域的视觉效果和相关信息。

2.5. 查看方案

  • 用户可通过请求获取已添加的方案列表。
  • 点击列表中的方案,系统将跳转到该方案的视角,并移除其他已展示的方案。
  • 用户可方案列表中进行删除操作。
  • 删除方案时,系统将发送删除请求至服务器,并实时更新方案列表。

2.6. 数据库的字段

image-20240625005124296

id:方案的唯一标识符,自增

name:方案名称

src:模型的存放路径,用于在还原方案时获取模型文件

position:模型的坐标信息,支持存储经纬度+高度或笛卡尔坐标。提交方案时存储的坐标格式,在还原时将保持不变,并不会改变你的数据源

tileHeight:压平高度,不是真实模型的高度,是模型坐标系的高度

heading:模型的角度,用于控制模型的旋转方向

scale:模型的尺寸缩放比例

polygon:压平区域的坐标数据,以GeoJSON格式存储

cameraPosition:相机的笛卡尔坐标,记录提交方案时相机的位置信息

cameraOrt:相机视角,提交方案时候的视角

如果你觉得设计的不是很好,可以手动点击获取相机的视角,放到信息卡片上,调整后再进行提交

create_at:方案的创建时间,自动记录方案添加到数据库的时间

delete_at:方案的删除时间,当方案被删除时自动记录时间戳

2.7. 接口

增加

/api/v1/addScheme

传3个number类型,其他都是字符串。

image-20240625011829144

获取(不需要传id)

/api/v1/getScheme

删除(需要传id)

/api/v1/delScheme

3. 难点

3.1. 调整模型的位置

在3D空间中,调整模型位置时容易改变其高度,导致模型悬浮在空中。改变笛卡尔坐标不好调,不管改了哪个轴,高度会发生改变。

原则:无论怎么调整位置,高度都不能发生改变,否则模型就会悬浮在空中。

解决:将笛卡尔坐标转为经纬度,可以按照上北下南左西右东调整,再转为笛卡尔坐标。

另一个bug:当视角刚好是上北下南左西右东时,是没有问题的,如果视角转了一个角度的话,方位就会打乱。

解决:应该是按照屏幕坐标的像素移动,不管哪个方位,都可以实现上下左右移动。

笛卡尔坐标转屏幕坐标,屏幕坐标再转笛卡尔坐标的实现代码:

  // 屏幕转世界坐标
  let pick1 = new Cesium.Cartesian2(0,0)
  let cartesian = viewer.scene.globe.pick(viewer.camera.getPickRay(pick1),viewer.scene);
  // 注意这里的屏幕坐标一定要在球上,否则生成的cartesian对象是undefined

  // 世界坐标转屏幕坐标 (笛卡尔坐标高度为0)
  Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene,Cartesian3)
  // 结果是Cartesian2对象,取出X,Y即为屏幕坐标

3.2. 旋转

鼠标按下去不动一直触发事件,鼠标抬起、按下并释放鼠标和松开鼠标都会取消事件。

实现代码:

app.directive("longpress", {
  mounted: function (el, binding, vNode) {
    // Make sure expression provided is a function
    if (typeof binding.value !== "function" && vNode.context !== undefined) {
      // pass warning to console
      let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be`;

      console.warn(warn);
    }

    // Define variable
    let pressTimer = null;

    // Define funtion handlers
    // Create timeout ( run function after 1s )
    let start = (e) => {
      if (e instanceof MouseEvent && e.type === "click" && e.button !== 0) {
        return;
      }
      handler(e);
    };

    // Cancel Timeout
    let cancel = () => {
      // Check if timer has a value or not
      if (pressTimer !== null) {
        clearTimeout(pressTimer);
        pressTimer = null;
      }
    };
    // Run Function 按下60毫秒触发
    const handler = (e) => {
          pressTimer = setTimeout(() => {
        binding.value(e);
        // 执行递归 调用自身
        handler(e);
      }, 60);
    };

    // Add Event listeners
    el.addEventListener("mousedown", start);
    // Cancel timeouts if this events happen
    el.addEventListener("click", cancel);
    el.addEventListener("mouseup", cancel);
    el.addEventListener("mouseout", cancel);
  },
});

参考文档:在 Vue 中定义一个「长按事件」(in TypeScript)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值