Cesium保姆教程之3dtiles操作(移动+旋转)

在平常的工作中,难免会用到倾斜摄影,当加载倾斜摄影的时候,最头疼的就是倾斜摄影的偏移问题,在代码中进行修改加载倾斜摄影的偏移参数,虽然简单但过于麻烦,也耽误开发的效率,因此我就本着能不能在三维场景中对倾斜摄影进行手动操作,无需再改代码并可将倾斜摄影放在较为正确的位置。为了实现这个功能,我就从网上找一些相关的资料,参照一些网上的大佬的代码进行修改,并补充相关知识,并不是那么及时的完成了这篇文章。希望大家多多收藏和点赞。

下文就是本文的实现思路和代码,并对文中的代码做出一定的注释,对于文章有不对的地方也希望大家进行指正,有大家的参与我才会有动力继续写下去,虽然产量微乎其微😄,但我希望对大家有帮助啊,取之开源用之开源啊。

接下来就开始直入主题啊,首先是绘制旋转标识器和平移标识器,毕竟有方向才好进行辨别啊。

移动指示器的绘制比较简单,通过Cesium.PolylineCollection()将指示器的三个方向方向添加到其中,并将绘制形成的指示器通过primitive的方式进行添加。 代码主要思路如下:

javascript 复制代码
  /**
   * @description 绘制箭头
   * @param {object} originDegree 初始点坐标点
   * @param {object} targetDegree 目标点坐标点
   */
  initLineArrow(originDegree, targetDegree, length) {
    const arrows = new Cesium.PolylineCollection()
    //坐标系方向存放原始坐标和目的点坐标
    const xPos = [
    originDegree.lng,originDegree.lat,originDegree.alt,
    targetDegree.lng,originDegree.lat,originDegree.alt
    ]
    const xArrow = this.drawArrow(arrows,'model_edit_xArrow',xPos,Cesium.Color.GREEN)
    const yPos = [
      originDegree.lng,originDegree.lat,originDegree.alt,
      originDegree.lng,targetDegree.lat,originDegree.alt
    ]
    const yArrow = this.drawArrow(arrows,'model_edit_yArrow',yPos,Cesium.Color.BLUE)
    const zPos = [
      originDegree.lng,originDegree.lat,originDegree.alt,
      originDegree.lng,originDegree.lat,targetDegree.alt
    ]
    const zArrow = this.drawArrow(arrows,'model_edit_zArrow',zPos,Cesium.Color.RED)
    this._coordArrows = this._viewer.scene.primitives.add(arrows)
    this._coordArrows._name = 'CoordAxis'
  }
  /**
   * @description 绘制空间坐标轴(移动指示器)
   * @param {Cesium.Collection} arrows 坐标轴集合
   * @param {string} name xyz轴名称
   * @param {Array} positions 坐标系位置
   * @param {Cesium.Color} color 坐标系颜色
   */
  drawArrow(arrows, name, positions, color) {
    const arrow = arrows.add({
      positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
      width: this._defaultWidth,
      material: Cesium.Material.fromType(Cesium.Material.PolylineArrowType, {
        color: color
      })
    })
    arrow._name = name
  }

移动指示器的绘制原理也类似于移动指示器的绘制,通过Cesium.PolylineCollection()将指示器的三个旋转方向添加到其中,并将绘制形成的指示器通过primitive的方式进行添加。

javascript 复制代码
/**
   * @description 绘制旋转转轴
   * @param {string} name 转轴的名字 
   * @param {Array} position 转轴的位置
   * @param {Cesium.Matrix4} matrix 东北坐标系矩阵
   * @param {Cesium.Color} color 转轴颜色
   * @returns 
   */
createAxisSphere(name, position, matrix, color) {
    let result = new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        id: name,
        geometry: new Cesium.PolylineGeometry({
          positions: position,
          width: 5
        }),
        attributes: {
          color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
        }
      }),
      releaseGeometryInstances: false,
      appearance: new Cesium.PolylineColorAppearance({
        translucent: false
      }),
      modelMatrix: matrix
    })
    result._name = name
    this._coordCircle.push(result)
    return result
  }
  /**
   * @description 绘制旋转指示器
   * @param {number} lng 经度 单位:度
   * @param {number} lat 纬度 单位:度
   * @param {number} height 高度 单位:米
   * @param {number} radius 半径 单位:米 
   */
  createCircle(lng, lat, height, radius) {
    const position = []
    for (let i = 0; i <= 360; i += 3) {
      const sin = Math.sin(Cesium.Math.toRadians(i))
      const cos = Math.cos(Cesium.Math.toRadians(i))
      const x = radius * cos
      const y = radius * sin
      position.push(new Cesium.Cartesian3(x, y, 0))
    }
    const matrix = Cesium.Transforms.eastNorthUpToFixedFrame(
      new Cesium.Cartesian3.fromDegrees(lng, lat, height)
    )
    //绕Z轴
    const axisSphereZ = this.createAxisSphere(
      'model_edit_zCircle',
      position,
      matrix,
      Cesium.Color.RED
    )
    this._viewer.scene.primitives.add(axisSphereZ)
 
    //绕Y轴
    const axisSphereY = this.createAxisSphere(
      'model_edit_yCircle',
      position,
      matrix,
      Cesium.Color.GREEN
    )
    this._viewer.scene.primitives.add(axisSphereY)
    let my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(90))
    let rotationY = Cesium.Matrix4.fromRotationTranslation(my)
    Cesium.Matrix4.multiply(
      axisSphereY.geometryInstances.modelMatrix,
      rotationY,
      axisSphereY.geometryInstances.modelMatrix
    )
 
    //绕X轴
    const axisSphereX = this.createAxisSphere(
      'model_edit_xCircle',
      position,
      matrix,
      Cesium.Color.BLUE
    )
    this._viewer.scene.primitives.add(axisSphereX)
    let mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(90))
    let rotationX = Cesium.Matrix4.fromRotationTranslation(mx)
    Cesium.Matrix4.multiply(
      axisSphereX.geometryInstances.modelMatrix,
      rotationX,
      axisSphereX.geometryInstances.modelMatrix
    )
  }

以上代码就可以将移动指示器和旋转指示器绘制出来了,当然对于代码出现的this.xx变量不知道是啥不要着急啊,你只需要这是需要全局使用的变量,把它们当作定义好的即可,后面会慢慢做出解释哈。

绘制完两个指示器之后就是准备开始对模型进行操作了。

旋转操作

javascript 复制代码
/**
   * 开始旋转编辑
   */
  editRtation() {
    const option = this.initParam()
    this.createCircle(
      option.originDegree.lng,
      option.originDegree.lat,
      option.originDegree.alt,
      option.length
    )
  }

移动操作

javascript 复制代码
/**
   * 开始编辑平移
   */
  editTranslation() {
    const option = this.initParam()
    const length = option.length
    let translateCartesian = new Cesium.Cartesian3(length, length, length) //单位为米
    let originPos = JSON.parse(JSON.stringify(this._b3dm.boundingSphere.center))
    let targetDegree = this.getTransPostion(originPos, translateCartesian)
    this.initLineArrow(option.originDegree, targetDegree, length)
  }

操作过程中,要随时更新模型位置和指示器位置,因此要做鼠标操作中进行操作,更新模型位置和指示器位置

鼠标操作

javascript 复制代码
//初始化鼠标事件(移动,按下,抬起)
  initEvent() {
    const $this = this
    const viewer = this._viewer
    $this._handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
    $this._handler.setInputAction(function(event) {
      let pick = viewer.scene.pick(event.position) //获取的pick对象
      if (
        pick &&
        pick.primitive &&
        pick.primitive._name &&
        pick.primitive._name.indexOf('model_edit') != -1
      ) {
        viewer.scene.screenSpaceCameraController.enableRotate = false //锁定相机
        //高亮加粗显示
        $this._currentPick = pick.primitive
        $this._currentPick.width = 25
        let downPos = viewer.scene.camera.pickEllipsoid(
          event.position,
          viewer.scene.globe.ellipsoid
        )
        let _tx = 0,
          _ty = 0,
          _tz = 0 //xyz方向的平移量(经纬度,经纬度,米)
        let _rx = 0,
          _ry = 0,
          _rz = 0 //xyz方向的旋转量(度)
        // 防止点击到地球之外报错,加个判断
        if (downPos && Cesium.defined(downPos)) {
          _tx = 0
          _ty = 0
          _tz = 0
          _rx = 0
          _ry = 0
          _rz = 0
          const downDegree = CoordTransform.transformCartesianToWGS84(
            downPos
          )
          $this._handler.setInputAction(function(movement) {
            let endPos = viewer.scene.camera.pickEllipsoid(
              movement.endPosition,
              viewer.scene.globe.ellipsoid
            )
            const endDegree = CoordTransform.transformCartesianToWGS84(
              endPos
            )
            const _yPix = movement.endPosition.y - event.position.y
            const _xPix = movement.endPosition.x - event.position.x
            switch ($this._currentPick._name) {
              case 'model_edit_xArrow':
                _tx = endDegree.lng - downDegree.lng
                break
              case 'model_edit_yArrow':
                _ty = endDegree.lat - downDegree.lat
                break
              case 'model_edit_zArrow':
                _tz = -$this._dStep * _yPix
                break
              case 'model_edit_xCircle':
                _rx = $this._rStep * _yPix
                break
              case 'model_edit_yCircle':
                _ry = $this._rStep * _xPix
                break
              case 'model_edit_zCircle':
                _rz = $this._rStep * _xPix
                break
            }
            $this.updateModel($this._params, _tx, _ty, _tz, _rx, _ry, _rz)
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        }
 
        $this._handler.setInputAction(function(eve) {
          viewer.scene.screenSpaceCameraController.enableRotate = true // 取消相机锁定
          $this._currentPick.width = $this._defaultWidth
          $this._currentPick = undefined
          $this._params.tx += _tx
          $this._params.ty += _ty
          $this._params.tz += _tz
          $this._params.rx += _rx
          $this._params.ry += _ry
          $this._params.rz += _rz
          //为viewer绑定LEFT_UP事件监听器(执行函数,监听的事件)
          $this._handler.removeInputAction(
            Cesium.ScreenSpaceEventType.MOUSE_MOVE
          ) // 解除viewer的LEFT_UP事件监听器
          $this._handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP) // 解除viewer的LEFT_UP事件监听器
        }, Cesium.ScreenSpaceEventType.LEFT_UP)
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
  }

最后就是销毁事件了

销毁移动指示器

javascript 复制代码
removeCoordCircle() {
    this._coordCircle.forEach(element => {
      this._viewer.scene.primitives.remove(element)
    })
    this._coordCircle = []
  }

销毁旋转指示器

javascript 复制代码
removeCoordArrows() {
    if (this._coordArrows) {
      this._viewer.scene.primitives.remove(this._coordArrows)
      this._coordArrows = undefined
    }
  }

销毁鼠标事件

javascript 复制代码
/**
   * 关闭/注销
   */
  destroy() {
    this.removeAllTools()
    this._handler.destroy()
  }

前端调用方式

vue 复制代码
<template>
    <div>
        <div class="edit">
            <el-button size="medium" @click="editObj">开始编辑</el-button>
            <el-button size="medium" @click="rotation">旋转</el-button>
            <el-button size="medium" @click="translation">平移</el-button>
            <el-button size="medium" @click="destroy">关闭编辑</el-button>
        </div>
        <div id="cesiumContainer"></div>
    </div>
</template>

<script setup>
import * as Cesium from "cesium"
import "cesium/Source/Widgets/widgets.css"
import initCesium from "@/cesiumUtils/initCesium"
import { onMounted } from "vue";
import EditB3DM from "@/cesiumUtils/EditB3DM"
let viewer = null;
let tilesetModel = null
let b3dm = null;
//生命周期钩子
onMounted(async () => {
    viewer = await initCesium("cesiumContainer");
    tilesetModel = new Cesium.Cesium3DTileset({
        url: "/3dtiles/data/tileset.json"
    });
    viewer.scene.primitives.add(tilesetModel);
    viewer.flyTo(tilesetModel)
})
//开始编辑
const editObj = () => {
    b3dm = new EditB3DM(viewer, tilesetModel, 1, 1)
    return b3dm
}
//旋转
const rotation = () => {
    b3dm.editRtation()
}
//移动操作
const translation = () => {
    b3dm.editTranslation()
}
//关闭编辑
const destroy = () => {
    b3dm.destroy()
}
</script>
</style>

展示效果

旋转

移动

以上就是本文的全部内容了,对于本文的代码量有点过多,就不在本文进行展示进而占用大量篇幅,对于想要源代码的内容,可访问本人的旋转操作代码,对于本文中的this.xx是什么含义可通过源代码进行得知啊。

以后本教程的全部源代码都会及时在我的GitHub主页上进行开源,这上边的代码的注释也更加详细,也会。当然也希望大家多多star,您的star就是我的动力,当然这篇文章也希望大家多多点赞和收藏,更希望大家多多评论批评指正,希望和大家一起进步哈。

相关推荐
郝晨妤5 分钟前
【鸿蒙5.0】向用户申请麦克风授权
linux·服务器·前端·华为·harmonyos·鸿蒙
神秘代码行者15 分钟前
使用 contenteditable 属性实现网页内容可编辑化
前端·html5
小鱼人爱编程16 分钟前
Look My Eyes 最新IDEA快速搭建Java Web工程的两种方式
java·前端·后端
郝晨妤17 分钟前
【鸿蒙5.0】鸿蒙登录界面 web嵌入(隐私页面加载)
前端·华为·harmonyos
小鱼人爱编程22 分钟前
当上小组长的第3天,我裁掉了2年老员工
前端·后端·面试
晓得迷路了23 分钟前
栗子前端技术周刊第 74 期 - 2025 Vue.js 现状报告、Element Plus X、Material UI v7...
前端·javascript·vue.js
知识分享小能手27 分钟前
CSS3学习教程,从入门到精通, CSS3 变形效果(2D 和 3D)的详细语法知识点及案例代码(22)
前端·javascript·css·学习·3d·css3·html5
花之亡灵28 分钟前
.net 6 + vue3中使用SignaIR实现双向通信功能
前端·javascript·笔记·websocket·.net·信息与通信
小鱼人爱编程29 分钟前
Flutter 打包APK的几种方式
android·前端·后端
zy01010138 分钟前
React 直接操作 DOM
前端·javascript·react.js·dom·react操作dom