superMap for cesium 飞行管理

前言

之前写过一篇 vue2.6内嵌超图11i发布的场景 的文章;当初的背景是前端使用的vue2,所以使用了超图发布在npm上的vue2对应的 @supermap/vue-iclient3d-webgl,这个前端依赖是超图5年前发布的,本地搭建的超图iserver是11i的,但vue2版本依赖的@supermap/vue-iclient3d-webgl只支持到10i,所以当时魔搭了一下,先装@supermap/iclient3d-vue-for-webgl,将包内public地下的cesium文件夹拷贝到项目public下,然后uninstall,再安装@supermap/vue-iclient3d-webgl,这样就解决了vue2项目内嵌超图11i发布的场景的问题

问题

超图官网示例展示的飞行案例是基于fpf文件的,但fpf文件是基于超图的idesktop手动打点(用过),或者基于路线生成(没用过)的。

项目做出来后总不能让用户自己去idesktop上搭建场景 →打点→导出fpf文件→上传到项目→web端漫游吧?所以就有了这篇文章!

思路

既然idesktop能创建并导出fpf,我只需要记录每次点击的场景的x,y,z,方位角,倾斜角,然后创建站点,添加到RouteCollection,再通过flymanager调用就行了,也省的生成文件再加载到内存中了;于是就搜索相关的问题,在超图官网问答版块找到了一篇类似的问题,直指优快云,但示例代码需要积分下载,就PASS掉了。

解决思路

这里笼统说下,省的各位同行浪费时间

web场景中打点 →项目内置一个空的fpf文件 →将打点数据给到RouteCollection→调用flyManager.play()

详细步骤

重点:如果你参考我之前的文章引用的@supermap/vue-iclient3d-webgl,那么你需要先安装超图发布的iclient3d-webgl

npm install @supermap/iclient3d-webgl

然后从下载的包中将Cesium拷贝到你的前端项目的public下,替换到原来从@supermap/vue-iclient3d-webgl拷贝的Cesium包

原因:@supermap/vue-iclient3d-webgl是5年前发布的,依赖的cesium比较旧,导致漫游相机视角异常和飞行结束后场景假死状态

准备一个不包含任何站点的fpf文件内嵌在项目中,因为超图官网说

我的fpf文件

<?xml version="1.0" encoding="UTF-8"?>
<SceneRoute xmlns="http://www.supermap.com.cn/ugc60">
  <route name="飞行路线_1" speed="120" lineType="0" showroutestop="True" showrouteline="True"
         altitudefree="False" headingfree="False" tiltfree="False" flycircle="False" alongline="True">
    <style>
      <geostyle3d>
        <linecolor>RGBA(147,112,219,100)</linecolor>
        <linewidth>2</linewidth>
        <altitudeMode>Absolute</altitudeMode>
        <bottomAltitude>0.00</bottomAltitude>
      </geostyle3d>
    </style>
  </route>
</SceneRoute>

然后在你的场景中添加绘制marker的函数,进入页面时,初始化marker函数,当点击添加站点时,调用handlerMarker.activate()激活打点功能;代码如下:

 // 标注工具初始化
    initMarker() {
      //标注相关
      common.initHandler('Marker ', Cesium.ClampMode.S3mModel)
      handlerMarker.activeEvt.addEventListener(function (isActive) {
        if (isActive == true) {
          viewer.enableCursorStyle = false;
          viewer._element.style.cursor = '';
          document.body.classList.remove('drawCur');
          document.body.classList.add('drawCur');
        } else {
          viewer.enableCursorStyle = true;
          document.body.classList.remove('drawCur');
        }
      });
      handlerMarker.movingEvt.addEventListener((windowPosition) => {
        tooltip.showAt(windowPosition, '<p>点击绘制地标</p>');
      });
        // 标绘结束后的回调,这里将每次点击的点位数据保存到vue中,用于后续添加飞行站点
      handlerMarker.drawEvt.addEventListener(result => this.drawListener(result));
    },
  // 场景新增漫游节点,激活marker功能
    addNewPoint() {
      // 激活marker
      handlerMarker.activate()
    },

将flyManager定义到vue之外,因为官网说了

初始化flyManager 示例代码

<script>
// 定义在vue之外
var flyManager;
export default {
  data() {
    return {

    }
  },
  mounted() {
    initFlyManager();
  },
  methods: {
    //漫游相关
    initFlyManager() {
      let routes = new Cesium.RouteCollection(viewer.entities);
      let fpfUrl = '/fpf/exportFlyManager.fpf';
      routes.fromFile(fpfUrl);
      //初始化飞行管理
      flyManager = new Cesium.FlyManager({
        scene: scene,
        routes: routes
      });
      flyManager.readyPromise.then(() => { // 飞行路线就绪
        var currentRoute = flyManager.currentRoute;
        currentRoute.isLineVisible = false;//不显示路线
        currentRoute.isStopVisible = false;//不显示站点标记
      })
       // 飞行过程中,始终看向下一个站点
      flyManager.stopArrived.addEventListener(function (routeStop) {
        flyManager.viewToStop(routeStop)
      });
    },
  }
}
</script>

之后在你的vue页面添加用于保存站点的pointObj属性,在 handlerMarker的回调函数中,将经度、纬度、高程、方位角、倾斜角属性保存在pointObj中;

代码如下:

//标注处理逻辑
    drawListener(result) {
      let that = this;
      tooltip.setVisible(false);
      if (result && result.object.position) {
        let descriptionEntity = that.createEntity(null, that.note,result.object.position, result.object.image, result.object.scale);
        viewer.entities.add(descriptionEntity);
        // 飞行站点添加逻辑
        if (that.pageVal === 0) {
          console.log(result)
          // 将鼠标当前点坐标转化成经纬度
          var cartographic = Cesium.Cartographic.fromCartesian(result.object.position);
          that.pointObj.longitude = Cesium.Math.toDegrees(cartographic.longitude);
          that.pointObj.latitude = Cesium.Math.toDegrees(cartographic.latitude);
          that.pointObj.altitude = cartographic.height + 2;//因为点击的是路面,+2用于模拟行车高度视角
          // 获取当前相机的朝向信息
          const headingRadians = viewer.camera.heading; // 弧度
          const pitchRadians = viewer.camera.pitch;   // 弧度
          // 将弧度转换为度数
          that.pointObj.heading = Cesium.Math.toDegrees(headingRadians);
          that.pointObj.tilt = Cesium.Math.toDegrees(pitchRadians) + 90;
          // 给树节点添加子节点
          that.$refs.flyRouteTree.data[0].children.push({
            label: '站点' + (that.$refs.flyRouteTree.data[0].children.length + 1),
            id: descriptionEntity.id,// 将新增的maker自动生成的uuid 绑定到树上,便于后续删除
            data: {...that.pointObj}
          })

          // 最后也要关闭标注工具
          handlerMarker.clear();
          handlerMarker.deactivate();
        }
      }
    },
//创建entity 标注
    createEntity(id, title, position, image, scale) {
      return new Cesium.Entity({
        id: id,
        position: position,
        label: {
          text: title, // 标注的文本
          font: '14px', // 字体样式
          pixelOffset: new Cesium.Cartesian2(0, -30), // 文本偏移量,负值将文本向上移动
          disableDepthTestDistance: Number.POSITIVE_INFINITY // 防止标签被地形遮挡
        },
        billboard: {
          image: image,
          scale: scale,
        },
      });
    },

注意:添加站点至少要3个站点,因为两个站点漫游的话, 导致飞行时相机视角转圈;

重点:每次新建飞行路线时,需要将上一次添加的站点清空,超图官网文档removeStop老是不行,也没有clear()

点击开始漫游时遍历树节点的数据,将站点添加到stopCollection中;代码如下:

 // 新建的飞行路径的漫游按钮触发
    flyStart() {
      if (this.newFlyRoute[0].children.length === 0) {
        this.$message.error('请先添加漫游站点');
        return;
      }
      if (this.newFlyRoute[0].children.length < 3) {
        this.$message.error('漫游节点太少,无法漫游');
        return;
      }
      // 添加站点数据到flyManager.stopCollection
      this.setFlyStopData(this.newFlyRoute);
      flyManager.readyPromise.then(() => { // 飞行路线就绪
        if (flyManager._flyStatus === 2) {// 防止重复点击按钮
          flyManager.play()
        }
      })
    },
  // 封装站点数据到flymanager.stopCollection
    setFlyStopData(collection) {
      // 上述的空的fpf获取的
      let routes = flyManager.routes.get(0);
      //新建飞行站点时,如果flymanager.stopCollection为空时,则添加路线名称;
      // 保存后再新建飞行路线时, 将添加到场景中的站点和路线清空
      if (routes.stopCount !== 0) {
        routes.clear()// 这个函数在超图官网没有,打断点看cesium源码看到的
        routes._addedStops = [];// 这个属性在超图官网没有,打断点看cesium源码看到的
        routes._stopMarkCollection = []// 这个属性在超图官网没有,打断点看cesium源码看到的
        viewer.entities.removeAll()
      }
        
      let collectionData = collection[0];
      for (let i = 0; i < collectionData.children.length; i++) {
        let single = collectionData.children[i];
        // 创建飞行站点
        let routeStop = new Cesium.RouteStop({
          index: i,
          duration: 5,// 两个站点之间的飞行时间设置为固定的5秒
          point: Cesium.Cartesian3.fromDegrees(single.data.longitude, single.data.latitude, single.data.altitude),
          stopName: single.label,
          heading: single.data.heading,
          tilt: single.data.tilt,
        });
        //添加站点
        routes.addStop(routeStop)
      }
    },

通过上述代码,就将idesktop上创建飞行路线的功能嵌入到了web端;飞行暂停啊,停止啊之类的功能直接调用flyManager.pause(),flyManager.stop()就行了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值