经纬志
首页 / Cesium / 教程

三维坐标、高度与局部坐标系

先看结论

同样一个「北京」,在 Cesium 里可以写成经纬度、写成地心直角坐标, 也可以写成相对某个点的局部坐标。混用这三种表达,是坐标类问题的共同来源。

最后验证 2026-11-03 · CesiumJS 1.121 · 难度:进阶 · 核验 2026-10-29 · 引用本文

三种表达,三种单位

类型类单位使用场景
大地坐标Cesium.Cartographic经度、纬度用弧度,高度用米地形采样、测地线、瓦片范围
地心直角坐标Cesium.Cartesian3米,原点在地球质心实体位置、相机、着色器、模型矩阵
局部坐标Matrix4 变换后的 Cartesian3米,原点自定建筑局部建模、模型摆放、批量构件
// 经纬度 -> 大地坐标(弧度)
var carto = Cesium.Cartographic.fromDegrees(116.391263, 39.907359, 50);
console.log(carto.longitude);   // 2.0313...
console.log(carto.latitude);    // 0.6965...
console.log(carto.height);      // 50

// 大地坐标 -> 地心直角坐标
var cartesian = Cesium.Cartesian3.fromRadians(
  carto.longitude, carto.latitude, carto.height
);

// 地心直角坐标 -> 大地坐标
var back = Cesium.Cartographic.fromCartesian(cartesian, Cesium.Ellipsoid.WGS84);

// 批量转换:一次给一串经纬度
var positions = Cesium.Cartesian3.fromDegreesArray([
  116.391, 39.907,
  116.404, 39.915,
  116.418, 39.921
]);

Cartographic.fromDegrees 收的是度,存的是弧度。 把 carto.longitude 直接塞回 fromDegrees,坐标会跑到地球另一边。 读写 Cartographic 时始终记住它是弧度。

高度是米,不是弧度。Cartographic 的三个字段单位不统一, 这是 Cesium 的历史设计。所有涉及 height 的计算都按米处理。

高度参考与贴地

实体的 position 高度为 0 时,点会落在椭球面上,而不是地形表面。 要让点随地形起伏,需要设置 heightReference,由渲染器在每帧按地形调整。

viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(116.391263, 39.907359),
  point: {
    pixelSize: 10,
    color: Cesium.Color.fromCssColorString('#4d9bff'),
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
  },
  label: {
    text: '地面点',
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
    disableDepthTestDistance: Number.POSITIVE_INFINITY
  }
});

// 需要知道某点地面高度时,查当前地形
var carto = Cesium.Cartographic.fromDegrees(116.391263, 39.907359);
var ground = viewer.scene.globe.getHeight(carto);   // 地形未加载完时返回 undefined

HeightReference 有三个取值:NONE 不贴地、 CLAMP_TO_GROUND 贴地形、RELATIVE_TO_GROUND 在给定高度基础上叠加地形。折线要贴地则用 clampToGround: true, 它走的是完全另一条渲染管线(GroundPrimitive),比普通折线贵。

局部坐标系:把建筑放到一个点上

Transforms.eastNorthUpToFixedFrame 生成一个以某点为原点、 X 轴朝东、Y 轴朝北、Z 轴朝天的变换矩阵。局部坐标 (x, y, z) 乘上这个矩阵,就得到地心直角坐标。

var origin = Cesium.Cartesian3.fromDegrees(116.397, 39.908, 0);
var enu = Cesium.Transforms.eastNorthUpToFixedFrame(origin);

// 原点以东 120 米、以北 30 米、高 8 米
var local = new Cesium.Cartesian3(120, 30, 8);
var world = Cesium.Matrix4.multiplyByPoint(enu, local, new Cesium.Cartesian3());

// 反过来:世界坐标 -> 局部坐标
var inverse = Cesium.Matrix4.inverseTransformation(enu, new Cesium.Matrix4());
var backLocal = Cesium.Matrix4.multiplyByPoint(inverse, world, new Cesium.Cartesian3());

// 一次算多个点
var ring = [
  new Cesium.Cartesian3(-40, -40, 0),
  new Cesium.Cartesian3( 40, -40, 0),
  new Cesium.Cartesian3( 40,  40, 0),
  new Cesium.Cartesian3(-40,  40, 0)
];
var worldRing = ring.map(function (p) {
  return Cesium.Matrix4.multiplyByPoint(enu, p, new Cesium.Cartesian3());
});

局部坐标非常适合「一栋楼内部有许多构件」这类场景:构件坐标保持小数值, 精度稳定;换一个位置只需换一次矩阵,不用重新计算每个构件。

屏幕坐标与地理坐标互转

交互里拿到的鼠标位置是画布像素。要换算成三维点, 用 camera.pickEllipsoid 打到椭球面,或用 scene.pickPosition 打到模型或地形表面。两者结果可能相差几十米。

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

handler.setInputAction(function (movement) {
  var screen = movement.position;

  // 打到椭球面(忽略地形与模型)
  var onEllipsoid = viewer.camera.pickEllipsoid(
    screen, viewer.scene.globe.ellipsoid
  );

  // 打到当前渲染的深度缓冲(地形、模型都算)
  var onScene = viewer.scene.pickPosition(screen);

  if (Cesium.defined(onEllipsoid)) {
    var carto = Cesium.Cartographic.fromCartesian(onEllipsoid);
    console.log(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 反向:三维点 -> 画布像素
var canvasPos = viewer.scene.cartesianToCanvasCoordinates(
  Cesium.Cartesian3.fromDegrees(116.391, 39.907, 100)
);

注意事项

  • Cartographic.fromCartesian 不传椭球时使用 WGS84,国内 CGCS2000 与 WGS84 在米级上相容,但不要和北京 54 混用。
  • pickPosition 依赖深度缓冲,部分低端设备或关闭 scene.pickTranslucentDepth 时对透明物体会失效。
  • 批量坐标用 fromDegreesArray、fromDegreesArrayHeights,比循环调用 fromDegrees 少大量临时对象。
  • 局部坐标系原点选在场景中心附近,离原点几公里外的局部坐标会开始出现浮点抖动。
  • scene.globe.getHeight 只反映当前已加载地形,和 sampleTerrainMostDetailed 的结果可能不同。

一条排查原则:坐标看起来「偏了几十公里」,先查经纬度是否被当成弧度; 偏了几十米,查高程基准与投影;偏移随缩放变化,才是渲染精度问题。

引用本文

经纬志(2026)。《三维坐标、高度与局部坐标系》。https://www.jingweizhi.com/cesium/tutorial/coordinates.html(访问日期 2026-11-03)。