专栏名称: 小猿猴GISer
GIS遥感交流学习
目录
相关文章推荐
直播海南  ·  刚刚回应:招聘不存在违规操作 ·  11 小时前  
Kevin在纽约  ·  分享图片 -20250222212332 ·  昨天  
最高人民法院  ·  中央军委主席习近平签署命令 ... ·  2 天前  
直播海南  ·  首批200名嫌犯,被押解回国! ·  3 天前  
51好读  ›  专栏  ›  小猿猴GISer

在加载的 3D Tiles 数据中拾取要素

小猿猴GISer  · 公众号  ·  · 2025-01-30 12:38

正文

前言

本文将复现并改造官方案例 3D Tiles Feature Picking [1] ,首先加载了 Cesium Ion 中的“纽约市三维建筑物数据”,具体如下图所示:通过查看数据描述内容,这样我们便可以读懂每个属性字段的含义啦。

注:关于如何加载 Cesium Ion 中的数据请移步至我曾在 加载官方在 Cesium Ion 中提供的三维模型数据 一文中的介绍。

实现效果

针对官方示例,这里进行了改造,使用了曾在 使用 Cesium 实现属性弹窗的功能 一文中使用的 Popup 弹窗组件。

代码实现

首先,需要先加载 3d 建筑物数据:

/**
 * 加载建筑物 3D tiles 数据
 */

async function addTilesetData({
  try {
    const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(75343);
    viewer.scene.primitives.add(tileset);
  } catch (err) {
    ElMessage.error(`tileset数据加载出错: ${err}`);
  }
}

关于悬停或点击高亮要素,代码实现如下:我们需要分别处理 支持轮廓 silhouette )效果和 不支持轮廓 silhouette )效果两种情况。经过我的测试,手机上的edge和chrome都是支持的。这里我们用到了 PostProcessStageLibrary [2] 类的 createEdgeDetectionStage() createSilhouetteStage() 方法来实现高亮的效果,在代码中我作了相应的注释。

注:关于Cesium中的后处理阶段,请阅读我在知乎上找到的一篇技术文章,进行进一步的学习: https://zhuanlan.zhihu.com/p/491788997

function init({
  clickHandler = viewer.screenSpaceEventHandler.getInputAction(
    Cesium.ScreenSpaceEventType.LEFT_CLICK
  );

/**
   * 检查当前场景是否支持轮廓(silhouette)效果
   * 若支持,鼠标悬停于要素上方时,轮廓显示为蓝色,鼠标单击要素时,轮廓显示为绿色;
   * 否则,鼠标悬停于要素上方时,轮廓显示为黄色,鼠标单击要素时,轮廓显示为绿色。
   */

if (Cesium.PostProcessStageLibrary.isSilhouetteSupported(viewer.scene)) {
    ElMessage({
      message"当前场景支持轮廓效果",
      type"success",
    });
    //创建一个检测轮廓的后处理阶段
    const silhouetteBlue =
      Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
    silhouetteBlue.uniforms.color = Cesium.Color.BLUE; //高亮轮廓显示的颜色,默认值为Color.Black
    silhouetteBlue.uniforms.length = 0.01//边缘的长度(以像素为单位)。默认值为 0.5
    silhouetteBlue.selected = [];

    const silhouetteGreen =
      Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
    silhouetteGreen.uniforms.color = Cesium.Color.LIME; //酸橙绿色
    silhouetteGreen.uniforms.length = 0.01;
    silhouetteGreen.selected = [];

    //创建一个应用轮廓效果的后处理阶段(复合的后处理阶段)
    const postProcessStageComposite =
      Cesium.PostProcessStageLibrary.createSilhouetteStage([
        silhouetteBlue,
        silhouetteGreen,
      ]);
    //将后期处理阶段添加到集合中
    viewer.scene.postProcessStages.add(postProcessStageComposite);

    //在鼠标悬停于要素上时将其轮廓颜色设置为蓝色
    viewer.screenSpaceEventHandler.setInputAction((movement) => {
      //若当前要素之前被高亮了,取消其高亮显示
      silhouetteBlue.selected = [];

      const pickedFeature = viewer.scene.pick(movement.endPosition);

      if (!Cesium.defined(pickedFeature)) {
        hoverRef.value.style.left = "-400px";
        hoverRef.value.style.bottom = "-200px";
        return;
      }

      //TODO:更新悬浮窗信息
      // updateHoverWindow(pickedFeature, movement.endPosition);

      if (pickedFeature !== selected.feature) {
        silhouetteBlue.selected = [pickedFeature];
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    //在鼠标点击要素时将其轮廓颜色设置为绿色
    viewer.screenSpaceEventHandler.setInputAction((movement) => {
      silhouetteGreen.selected = [];
      const pickedFeature = viewer.scene.pick(movement.position);
      if (!Cesium.defined(pickedFeature)) {
        clickHandler(movement);
        return;
      }
      //TODO:更新弹窗位置和信息
      updatePopup(pickedFeature, movement);
      //若单击选择的对象已被选择了,则直接返回
      if (silhouetteGreen.selected[0] === pickedFeature) {
        return;
      }
      //我们先获取悬停高亮的要素
      const highlightedFeature = silhouetteBlue.selected[0];
      if (pickedFeature === highlightedFeature) {
        silhouetteBlue.selected = []; //若点击的要素和悬停的要素相同,则取消悬停高亮的效果
      }
      //添加点击高亮的效果
      silhouetteGreen.selected = [pickedFeature];
      //设置要显示选择指示器的对象实例,这里我们不用选择指示器的话可以省略这行代码
      // viewer.selectedEntity = selectedEntity;
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  } else {
    ElMessage({
      message"当前场景不支持轮廓效果",
      type"warning",
    });
    //若不支持上述轮廓效果,则直接更改要素颜色
    const highlighted = {
      featureundefined,
      originalColornew Cesium.Color(),
    };

    viewer.screenSpaceEventHandler.setInputAction((movement) => {
      if (Cesium.defined(highlighted.feature)) {
        highlighted.feature.color = highlighted.originalColor;
        highlighted.feature = undefined;
      }
      const pickedFeature = viewer.scene.pick(movement.endPosition);
      if (!Cesium.defined(pickedFeature)) {
        return;
      }
      //TODO:更新悬浮窗信息
      // updateHoverWindow(pickedFeature, movement.endPosition);

      if (pickedFeature !== selected.feature) {
        highlighted.feature = pickedFeature;
        //将选择的要素本身的颜色存储在高亮要素的originalColor,以便鼠标不在该要素悬停时恢复原有的颜色
        Cesium.Color.clone(pickedFeature.color, highlighted.originalColor);
        pickedFeature.color = Cesium.Color.YELLOW; //将要素颜色改为黄色
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    viewer.screenSpaceEventHandler.setInputAction((movement) => {
      if (Cesium.defined(selected.feature)) {
        selected.feature.color = selected.originalColor;
        selected.feature = undefined;
      }

      const pickedFeature = viewer.scene.pick(movement.position);
      if (!Cesium.defined(pickedFeature)) {
        clickHandler(movement);
        return;
      }

      //TODO:更新属性弹窗
      updatePopup(pickedFeature, movement);

      if (selected.feature === pickedFeature) {
        return;
      }

      selected.feature = pickedFeature;
      if (pickedFeature === highlighted.feature) {
        Cesium.Color.clone(highlighted.originalColor, selected.originalColor);
        highlighted.feature = undefined;
      } else {
        Cesium.Color.clone(pickedFeature.color, selected.originalColor);
      }
      pickedFeature.color = Cesium.Color.LIME;
      //设置要显示选择指示器的对象实例,这里我们不用选择指示器的话可以省略这行代码
      // viewer.selectedEntity = selectedEntity;
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  }
}

我们在解析 Cesium3DTileFeature [3] 获取其所有属性名,然后再借助 getPropertyIds() 可以获取其所有属性名,然后再借助 getProperty(propertyId) 获取对应的属性值。

const propertyIds = pickedFeature.getPropertyIds();
const length = propertyIds.length;
for (let i = 0; i < length; ++i) {
  const propertyId = propertyIds[i];
  console.log(`${propertyId}${pickedFeature.getProperty(propertyId)}`);
}

处理弹窗属性代码如下:

function updatePopup(pickedFeature, e{
const






请到「今天看啥」查看全文