cesium获取天地图三维地形高度

项目场景:

由于cesium默认的地形初次加载跨域option请求资源很慢,一般需要一两分钟才能加载完成,影响用户体验,于是改成天地图三维地形,解决了首次加载慢问题

问题描述

在使用过程中发现存在问题,无法通过正常的cesium.sampleTerrain 方法获取地形高度,获得高度undefined
无法获取高度


原因分析:

对比了下两种地形的加载方式,发现存在很大差别
1.cesium 地形先加载了一个远程的json文件,也就是这一步首次加载非常耗时,然后再加载terrain元数据文件
在这里插入图片描述
2.天地图的加载方式看起来貌似只是一张地形图片
所以是不可能有数据的(因为sampleTerrain 方法通过发送请求获取地形数据的,显然这只是一张图片)
在这里插入图片描述

初步分析可能天地图没有提供地形高度方面的数据?


解决方案:

但是通过点击地球射线的方式依然可以获取到地形高度

3d.addEventListener(e => {
    let ray = viewer.camera.getPickRay(e.position);
    let cartesian = viewer.scene.globe.pick(ray, viewer.scene);
    if (cartesian) {
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      console.log('cartographic',cartographic)
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

在这里插入图片描述
于是就可以通过创建射线Ray的方式,拾取射线与地形的交点高度、

export const getTerrainHeightByRay = (lon, lat) => {
  // 转换为三维坐标(假设初始高度为 0 或高空)
  const targetPosition = Cesium.Cartesian3.fromDegrees(lon, lat, 0); // 目标点(椭球表面)
  const startPosition = viewer.camera.positionWC; // 相机世界坐标

  // 计算射线方向(从起点指向目标点)
  const direction = Cesium.Cartesian3.normalize(
    Cesium.Cartesian3.subtract(targetPosition, startPosition, new Cesium.Cartesian3()),
    new Cesium.Cartesian3()
  );

  // 创建射线
  const ray = new Cesium.Ray(startPosition, direction);

  // 拾取地形表面的交点
  const cartesian = viewer.scene.globe.pick(ray, viewer.scene);

  if (cartesian) {
    // 转换为经纬度和高度
    const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
    const longitude = Cesium.Math.toDegrees(cartographic.longitude);
    const latitude = Cesium.Math.toDegrees(cartographic.latitude);
    const height = cartographic.height;

    console.log(
      `经度: ${longitude.toFixed(4)}, 纬度: ${latitude.toFixed(4)}, 高度: ${height.toFixed(2)}`
    );
    return height;
  } else {
    console.log('未拾取到地形表面');
    return null;
  }
};

切记这种方法存在问题:只会在地形加载过才会奏效(或者说在视角内),否则拾取不到地形的交点, 解决办法是相机先flyto到目标上方一个足够高的位置(需要自己判断),然后在飞行结束complete回调里面通过创建射线的方式获取到地形交点的高度,最后在飞到指定目标位置处。

const showPlot = async data => {
  const v = 3d.getViewer();
  const coord = data[0]
    .replace('MULTIPOLYGON', '')
    .replace(/[()]/g, '')
    .replace(/\s/g, ',')
    .split(',')
    .map(item => Number(item));

  v.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(coord[0], coord[1], 5000),
    complete: () => loadEntity(data),
  });
};
const loadEntity = async data => {
  const v = 3d.getViewer();
  v.entities.removeAll();
  await Promise.all(
    data.map(async (i, index) => {
      let geoms = i
        .replace('MULTIPOLYGON', '')
        .replace(/[()]/g, '')
        .replace(/\s/g, ',')
        .split(',')
        .map(item => Number(item));
      const height = getTerrainHeightByRay(geoms[0], geoms[1]); // 获取地形高度
      const entityHeight = height + 30; // 高出地形30米
      v.entities.add({
        id: `clickItem_polygon${index}`,
        polygon: {
          hierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(geoms)),
          outline: true,
          extrudedHeight: entityHeight,
          outlineColor: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
          material: new Cesium.Color(1.0, 1.0, 0.0, 0.8),
        },
      });
    })
  );
   
   // 飞入到目标实体
  v.flyTo(v.entities.values, {
    duration: 1.5,
  });
  viewer.scene.requestRender(); //渲染
};

上面的获取天地图地形高度的方式仅仅是我个人的尝试,属于是奇淫技巧,目前在网上没有搜到任何关于如何获取到天地图地形高度的资料,准确性还没有得到验证,目前也没发现有什么问题,只是地形高度可能会有一定的偏差,有待进一步考量。如果有大佬对这方面熟悉的也可以指导一二,欢迎留言!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值