Skip to content

拾取:从屏幕回到地理空间 ​

渲染把三维世界压缩到二维屏幕;鼠标点击则试图逆向找回含义。一个屏幕像素可能对应建筑、天气面、地形或天空,因此“获取点击经纬度”不是唯一确定的操作。先决定要选中哪个对象、哪一种表面,再选择拾取方法。

点击确定方向,表面求交确定距离;这需要向量的概念。若想理解深度反算,可继续读齐次坐标与投影。

射线(ray)可写成 p(t)=o+t*d:o 是相机原点,d 是长度为 1 的观察方向,t≥0 是沿射线走过的距离。o 和 p 用米,t 也用米。一个二维像素只确定一条方向线;还需要与表面求交(intersection)才能选出具体三维位置。

三种问题对应不同路径 ​

scene.pick 返回被拾取的对象或其标识,用来回答“点中了谁”。scene.pickPosition 利用深度缓冲重建三维位置,回答“此像素记录的表面在哪里”。globe.pick 则让相机射线与当前地球表面求交,适合明确要求地表位置的功能。

以下是接在第二课初始化之后的功能片段,沿用已经导入的 Cesium 命名空间和已创建的 viewer,省略容器与初始化代码。

js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(({ position }) => {
  const picked = viewer.scene.pick(position);
  const ray = viewer.camera.getPickRay(position);
  const ground = ray && viewer.scene.globe.pick(ray, viewer.scene);
  if (!ground) return; // 点击天空等情况没有地表交点。
  const c = Cesium.Cartographic.fromCartesian(ground);
  console.log({
    object: picked?.id,
    longitude: Cesium.Math.toDegrees(c.longitude),
    latitude: Cesium.Math.toDegrees(c.latitude),
    ellipsoidHeight: c.height,
  });
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 功能卸载时:handler.destroy();

这里同时获取对象和地表位置,二者可能来自不同表面。例如点击高楼时,对象是建筑,地表交点位于建筑下方。若产品要显示楼顶坐标,应另选合适的深度拾取路径,不能把地表结果当作建筑表面。

深度读数并不覆盖所有对象 ​

使用 pickPosition 前需检查 pickPositionSupported,还应处理返回空值。透明对象是否进入相应深度拾取过程,与场景设置和渲染方式有关;pickTranslucentDepth 也会增加额外工作。屏幕上看得见的半透明天气场,不保证可以直接得到其科学高度。

气象场查询更常见的做法是:先得到地理位置,再按网格定义采样原始数值。最终显示的 RGB 色值不能替代温度、风速等数据。查询时应同时返回单位、层次、时间与缺测状态,避免给一个漂亮的数字却没有可解释语义。

对象如何被画出EXPERIMENT 05 · CESIUM 1.145.0

对象如何被画出

比较 Entity 与 Primitive,查看几何与材质的边界。

在浏览器本地运行 · 无需账号 · 鼠标拖动旋转,滚轮缩放

底图:Natural Earth II(公共领域);其余几何和地形为教学合成。 数据说明
查看运行源码

此代码直接读取实际运行的实验模块。按 kind 分支定位当前实验,也可查看数据公式和 GLSL。

读取源码…

观察任务: 单击不同盒子,查看“拾取结果”,再单击空白区域。切换 Entity 与 Primitive 后重复操作。实验展示的是对象拾取;若进一步查询地表,天空区域和地平线附近又会有什么困难?

展开解释

天空通常没有地表交点。地平线附近射线与表面接近相切,少量屏幕偏移可能造成较大地理位置变化;这叫误差放大(error amplification),不是增加显示小数位就能解决。工具提示应允许无命中,并根据数据精度显示合理的小数位数;多显示几位小数不会提高实际定位精度。

官方延伸:Scene 拾取接口、Globe 射线求交。

气象学 · 数学基础 · 地理可视化