Skip to content

GIS / BIM 切换组件

功能介绍

通过 GIS / BIM 切换组件,能够在 GIS 功能与 BIM 功能之间自由切换。

本文推荐使用右键菜单 开启 3DTiles 模型交互事件 进行 GIS / BIM 切换。

你可以使用组件 BIM 场景视图盒子组件 以更好地控制视角与摄像机。

注:该功能使用了 elementUI 库,使用前需安装该库,具体方法见 elementUI 安装

不妨通过代码示例在 Vue 中尝试一下:

在线演示

点击 在线链接 以查看在线演示。

组件代码示例

默认路径为 components/GisBimSwitch/index.vue

vue
<template>
  <div>
    <!-- GIS / BIM 切换组件 -->
    <el-card class="box-card">
      <div
        id="move-layer"
        class="title"
        @mousedown="mousedown"
        @mouseup="mouseup"
      >
        GIS / BIM 切换组件
      </div>
      <hr />
      <div class="button" @click="gbSwitch(false)">切换 GIS</div>
      <div class="button" @click="gbSwitch(true)">切换 BIM</div>
    </el-card>
  </div>
</template>

<script type="text/javascript">
import * as Cesium from 'cesium'

export default {

  methods: {
    init (bimRecallFunc = null, gisRecallFunc = null) {
      this.bimRecallFunc = bimRecallFunc;
      this.gisRecallFunc = gisRecallFunc;
    },

    gbSwitch (bool = true) {

      let that = this;

      const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas);

      // 开启地下模式
      uniCore.model.undergroundMode(bool);

      if (bool) {
        this.$message(
          { message: "请点击所需切换到BIM场景的模型。" }
        )
        handler.setInputAction(function (e) {
          const pickObj = viewer.scene.pick(e.position);
          if (!!pickObj) {
            const modelId = pickObj.id?.id === undefined ? pickObj.tileset.debugPickedTile.id : pickObj.id?.id;
            const lockBoundingSphere = pickObj?.primitive.boundingSphere === undefined ? pickObj.tileset.boundingSphere : pickObj.primitive.boundingSphere;
            const lockAxiz = uniCore.position.cartesian3_2axis(lockBoundingSphere.center);

            that.$message(
              { message: `已点击到模型:${modelId}` }
            )

            // 触发回调函数
            if (!!that.bimRecallFunc) {
              that.bimRecallFunc(pickObj, lockBoundingSphere);
            }

            window.viewer.scene.backgroundColor = Cesium.Color.fromCssColorString("#b9d3ee");
            window.viewer.terrainProvider = null;

            // 只留该模型显示,其他全部隐藏
            uniCore.model.setPrimitivesShow(modelId, false, false)

            // 隐藏所有HTML标签
            try {
              window.htmlTipList && window.htmlTipList.forEach(e => {
                document.getElementById(e).style.display = "none";
              })
            } catch (error) {

            }

            // 隐藏所有标签
            // 记住先前的状态
            window.tipBeforeSet = []
            window.viewer.scene.primitives._primitives.forEach((e) => {
              try {
                for (let i of e._labels) {
                  window.tipBeforeSet.push(i.show)
                  i.show = false;
                }
              } catch (error) { }
            })

            // 打开视角锁定
            uniCore.position.lockTo(window.viewer, bool, lockAxiz, -45, -30, pickObj.primitive.boundingSphere.radius * 3);

            handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)//移除事件

          }

        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      } else {
        window.viewer.scene.backgroundColor = null;
        window.viewer.terrainProvider = window.terrainProvider;

        // 触发回调函数
        if (!!that.gisRecallFunc) {
          that.gisRecallFunc();
        }

        // 还原所有模型显示
        uniCore.model.setPrimitivesShow('', true)

        // 还原所有HTML标签
        try {
          window.htmlTipList && window.htmlTipList.forEach(e => {
            document.getElementById(e).style.display = "block";
          })
        } catch (error) {

        }

        // 还原所有标签
        window.viewer.scene.primitives._primitives.forEach((e) => {
          try {
            e._labels.forEach((ele, index) => {
              ele.show = window.tipBeforeSet[index]
            })
          } catch (error) { }
        })

        // 关闭视角锁定
        uniCore.position.lockTo(uniCore.viewer, bool, [0, 0]);

      }

    },

    /**
 * 鼠标与窗口拖动相关
 */
    mousedown (event, id) {
      if (document.elementFromPoint(event.clientX, event.clientY).id === 'move-layer') {
        this.selectElement = document.elementFromPoint(event.clientX, event.clientY).parentNode.parentNode;
        document.querySelectorAll('.box-card').forEach((e) => {
          e.style.zIndex = 1000;
        })
        this.selectElement.style.zIndex = 1001;
        var div1 = this.selectElement
        this.selectElement.style.cursor = 'move'
        this.isDowm = true
        var distanceX = event.clientX - this.selectElement.offsetLeft
        var distanceY = event.clientY - this.selectElement.offsetTop
        document.onmousemove = function (ev) {
          var oevent = ev || event
          div1.style.left = oevent.clientX - distanceX + 'px'
          div1.style.top = oevent.clientY - distanceY + 'px'
        }
        document.onmouseup = function () {
          document.onmousemove = null
          document.onmouseup = null
          div1.style.cursor = 'default'
        }
      }

    },
    //鼠标抬起
    mouseup () {
      this.isMove = false;
      this.selectElement = "null"
    }



  }
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-card__body {
  padding: 20px 0px 0 0px;
}
::v-deep .box-card {
  position: absolute;
  top: 3%;
  left: 3%;
  width: 300px;
  z-index: 1;
  background: rgb(26 26 26 / 83%);
  border: 1px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0px 24px 54px 0px rgba(35, 41, 50, 0.5);
  border-radius: 15px;
  padding: 0 24px 12px 24px;
  margin-bottom: 12px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  transition: none;
  user-select: none;

  .title {
    font-size: 18px;
    font-weight: bold;
    color: #fefeff;
    display: block;
    margin-left: 24px;
    margin-bottom: 10px;
    user-select: none;
    overflow: hidden;
    cursor: move;
  }

  hr {
    margin-left: 24px;
    margin-bottom: 10px;
    border: none;
    border-bottom: 1px solid #ffffff1a;
  }

  .button {
    display: inline-flex;
    margin: 5px 10px;
    color: white;
    background: #4d4d4dd1;
    border-radius: 10px;
    padding: 7px 20px;
    cursor: pointer;
    transition: 0.3s;
  }
}
</style>

调用代码示例

vue
<template>
  <div id="unicoreContainer">
    <!-- GIS / BIM 切换组件窗口卡片开始 -->
    <gbSet ref="gbSetId"></gbSet>
    <!-- GIS / BIM 切换组件窗口卡片结束 -->
    <!-- BIM视图盒子组件开始 -->
    <bcSet ref="bcSetId"></bcSet>
    <!-- BIM视图盒子组件结束 -->
  </div>
</template>

<script>
import { UniCore } from 'unicore-sdk'
import { config } from 'unicore-sdk/unicore.config'
import 'unicore-sdk/Widgets/widgets.css'
import gbSet from '@/components/GisBimSwitch/index'; //GIS/BIM切换组件
import bcSet from '@/components/BimCubeSet/index.vue'; //BIM视图盒子组件

export default {

  components: {
    gbSet, bcSet
  },
  // 生命周期 - 挂载完成(可以访问DOM元素)
  mounted () {
    this.init();
  },

  // 方法集合
  methods: {

    /**
    * 通用图形引擎初始化
    */
    init () {

      // 初始化UniCore

      // 目前采用Cesium的地形&底图数据,这里配置Cesium的token
      let accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxNjEwMzI4My01MjBmLTQzYzktOGZiMS0wMDRhZjE0N2IyMGIiLCJpZCI6MTc1NzkyLCJpYXQiOjE3MTM3NzQ3OTh9.zU-R4MNvHr8rvn1v28PQfDImyutnpPF2lmEgGeSPckQ";
      // 初始化unicore
      let uniCore = new UniCore(config, accessToken);
      uniCore.init("unicoreContainer");
      window.uniCore = uniCore;
      let viewer = uniCore.viewer;

      // 视角初始化
      uniCore.position.buildingPosition(viewer, [113.12380548015745, 28.250758831850005, 700], -20, -45, 1);

      // 初始化视图盒子方法
      this.$refs.gbSetId.init(
        (pickObj, boundingSphere) => this.$refs.bcSetId.show(uniCore, uniCore.position.cartesian3_2axis(boundingSphere.center), boundingSphere.radius * 3),
        () => this.$refs.bcSetId.hide()
      );

      /**
       * 小别墅1号示例
       */
      let options = {
        id: '小别墅1号示例',
        url: '../../assets/3Dtiles/sample3_方法2_小别墅属性(1)/tileset.json',
        propertysURL: '../../assets/3Dtiles/sample3_方法2_小别墅属性(1)/01 小别墅.json'
      }
      //加载3dtiles
      uniCore.model.createTileset(options.url, options).then(cityLeft => {
        uniCore.model.changeModelPos(cityLeft, [113.12098820449636, 28.256150218457687, 130], [0, 0, 0])

        // 开启右键菜单、点击高亮、属性property
        uniCore.interact.setTilesRightClickMenu([{
          id: '小别墅1号示例',
          url: '../../assets/3Dtiles/sample3_方法2_小别墅属性(1)/tileset.json',
          propertysURL: '../../assets/3Dtiles/sample3_方法2_小别墅属性(1)/01 小别墅.json'
        }], (property) => console.log(property));
      })



      /**
         * 小别墅2号示例
         */
      uniCore.model.addGltf({
        lon: 0,
        lat: 0,
        height: 0
      }, {
        id: "小别墅2号示例",
        name: null,
        url: '../../../assets/gltf/小别墅.glb',
        scale: 1.0,
        property: null
      }).then(cityModel => {
        uniCore.model.changeModelPos(cityModel, [113.12098820449636, 28.257150218457687, 130], [90, 0, 0])
      })

      // 开启glTF模型右键交互
      uniCore.interact.setGltfRightClickMenu((property) => console.log(property));
    }
  }

}
</script>
<style scoped>
#unicoreContainer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: black;
}
</style>

示例运行结果

切换 GIS 模式效果(加载多源异构数据及GIS系统):

Alt text

切换 BIM 模式效果(切换到单个模型的BIM系统):

等待点击所需切换到 BIM 模式的模型(示例中分别为小别墅的 3DTiles 模型及 glTF模型)

Alt text

切换至对应 BIM 模式模型

Alt text

调用代码示例中的关键代码

组件提供 init 方法,参数分别为切换 BIM 场景触发的回调函数及切换至 GIS 场景触发的回调函数。其中切换 BIM 场景触发的回调函数提供两个参数,分别是点击模型返回的tileset,及模型的包围盒数据。

js
this.$refs.gbSetId.init(
  bimRecallFunc,
  gisRecallFunc
);

示例中使用了 BIM 场景视图盒子组件 :

js
// 初始化视图盒子方法
this.$refs.gbSetId.init(
  (pickObj, boundingSphere) => this.$refs.bcSetId.show(uniCore, uniCore.position.cartesian3_2axis(boundingSphere.center), boundingSphere.radius * 3),
  () => this.$refs.bcSetId.hide()
);