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就是我的动力,当然这篇文章也希望大家多多点赞和收藏,更希望大家多多评论批评指正,希望和大家一起进步哈。

相关推荐
小豆豆儿4 分钟前
【VUE3】【Naive UI】<n-button> 标签
前端·ui·html
只会偷懒12 分钟前
Element UI 中国省市区级联数据
前端·elementui
余生H23 分钟前
Brain.js(二):项目集成方式详解——npm、cdn、下载、源码构建
前端·javascript·神经网络·webml·brain.js
跳跳的小古风23 分钟前
vue3.0 根据富文本html页面生成压缩包(含视频在线地址、图片在线地址、前端截图、前端文档)
前端·html·音视频
new出一个对象30 分钟前
uniapp-vue2引用了vue-inset-loader插件编译小程序报错
前端·javascript·vue.js
weixin_1122331 小时前
基于java web的网上书店系统设计
java·前端·sqlite
@Autowire1 小时前
请你谈谈:vue的渲染机制(render)- 3举例说明问题
前端·javascript·vue.js
命运之光2 小时前
【趣味】斗破苍穹修炼文字游戏HTML,CSS,JS
前端·css
ross2 小时前
freeswitch通过bridge+定制化distributor,进行sip媒体的负载均衡代理
java·服务器·前端
开发那点事儿~2 小时前
vue3实现el-table的拖拽
前端·javascript·vue.js