CESIUM JS 学习笔记 (持续更新)

CESIUMJS 坐标系

统一坐标系

Cesium 中所有坐标最终都基于WGS84 椭球体 (World Geodetic System 1984),这是一种国际标准的地球参考模型,定义了地球的形状和大小。所有坐标系转换都以 WGS84 为基准进行计算。

常见坐标系

先了解一下,以北京天安门坐标为例,介绍几种常见的坐标系及其转换方法。

坐标系 值示例 说明
WGS84 经纬度 { longitude: 116.3975°, latitude: 39.9075°, height: 50 } 人类可读的地理坐标
弧度坐标 (Cartographic) { longitude: 2.0313, latitude: 0.6964, height: 50 } 经纬度的弧度表示
笛卡尔坐标 (Cartesian3) Cartesian3(-2173633.5, 4387689.5, 4077985.0) 地心直角坐标(单位:米)
屏幕坐标 (Cartesian2) { x: 640, y: 360 } (随窗口尺寸变化) 相对于 Canvas 的像素位置
Web 墨卡托投影 { x: 12958809.1, y: 4825922.9 } 平面投影坐标(EPSG:3857)

地理坐标系(Cartographic)

定义:以经度、纬度和高度表示的坐标系。

参数说明

  • 经度(longitude):东西方向的角度,范围[-180°, 180°]
  • 纬度(latitude):南北方向的角度,范围[-90°, 90°]
  • 高度(height):相对于椭球面的高度,单位米

例:创建地理坐标,由经度、纬度和高度定义的位置,以弧度为单位

注意:我们正常所说的经纬度是角度,Cesium 中角度与弧度之间提供了转化方式

js 复制代码
const cartographic = new Cesium.Cartographic(
  Cesium.Math.toRadians(116.404), // 经度(弧度)
  Cesium.Math.toRadians(39.915), // 纬度(弧度)
  1000, // 高度(米)
)

// 经纬度与弧度转换(弧度转角度)
const lon = Cesium.Math.toDegrees(cartographic.longitude)
const lat = Cesium.Math.toDegrees(cartographic.latitude)

笛卡尔空间坐标系(Cartesian3)

定义:三维直角坐标系,原点位于地球中心。

坐标轴方向

  • X 轴:指向本初子午线与赤道的交点(0° 经度,0° 纬度)
  • Y 轴:指向 90°E 经度与赤道的交点
  • Z 轴:指向北极

代码示例

javascript 复制代码
// 直接创建笛卡尔坐标
const position = new Cesium.Cartesian3(x, y, z)

平面坐标系(Cartesian2)

定义:二维平面坐标系,用于表示屏幕或图像上的二维位置。

参数说明

  • x:水平方向坐标
  • y:垂直方向坐标

代码示例

javascript 复制代码
// 创建屏幕坐标
const screenPosition = new Cesium.Cartesian2(100, 200)

转换方法(核心)

WGS84 ↔ Cartesian3

js 复制代码
// 经纬度转笛卡尔坐标
const cartesian = Cesium.Cartesian3.fromDegrees(
  116.3975, // 经度(度)
  39.9075, // 纬度(度)
  50, // 高度(米)
)

// 笛卡尔坐标转经纬度分两步

// 第一步:笛卡尔坐标转弧度坐标
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)

// 第二步:弧度转度数
const longitude = Cesium.Math.toDegrees(cartographic.longitude) // 116.3975°
const latitude = Cesium.Math.toDegrees(cartographic.latitude) // 39.9075°
const height = cartographic.height // 50米
console.log(longitude, latitude, height, 'longitude, latitude, height')

经纬度 ↔ 弧度

  • 弧度是一种用于测量角度的单位,180 度等于 π 弧度,1 弧度约等于 57.3 度。
  • Cesium 中提供了 Math 类,用于进行弧度和角度之间的转换。
js 复制代码
// 经度转弧度
const lonRad = Cesium.Math.toRadians(116.3975) // ~2.0313 弧度

// 纬度转弧度
const latRad = Cesium.Math.toRadians(39.9075) // ~0.6964 弧度

// 弧度转度数
const lonDeg = Cesium.Math.toDegrees(lonRad) // 116.3975°
const latDeg = Cesium.Math.toDegrees(latRad) // 39.9075°
console.log(lonDeg, latDeg, 'lonDeg, latDeg')

Cartesian3 ↔ 屏幕坐标

注意:API 已由 wgs84ToWindowCoordinates 更新为 worldToWindowCoordinates

js 复制代码
const cartesian = Cesium.Cartesian3.fromDegrees(
  116.3975, // 经度(度)
  39.9075, // 纬度(度)
  50, // 高度(米)
)

// 笛卡尔坐标转屏幕坐标
const screenPos = Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, cartesian)
console.log(`屏幕坐标: (${screenPos.x}, ${screenPos.y})`)

// 屏幕坐标转笛卡尔坐标(地形表面)
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((movement) => {
  console.log(movement.position) // 屏幕坐标
  console.log(viewer.scene.pickPosition(movement.position)) // Cartesian3
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

WGS84 ↔ Web 墨卡托投影

工作中可能会遇到 Web 墨卡托投影坐标,Cesium 也提供了转化方式

js 复制代码
const projection = new Cesium.WebMercatorProjection()

// WGS84 转 Web 墨卡托
const cartographic = Cesium.Cartographic.fromDegrees(116.3975, 39.9075)
const projected = projection.project(cartographic)
console.log(`投影坐标: (${projected.x}, ${projected.y})`)

// Web 墨卡托转 WGS84
const unprojected = projection.unproject(new Cesium.Cartesian3(projected.x, projected.y))
console.log(`WGS84坐标: ${Cesium.Math.toDegrees(unprojected.longitude)}°, 
  ${Cesium.Math.toDegrees(unprojected.latitude)}°`)

计算两点间距离

js 复制代码
// 天安门坐标(带高度)
const cartesian = Cesium.Cartesian3.fromDegrees(
  116.3975, // 经度(度)
  39.9075, // 纬度(度)
  50, // 高度(米)
)

// 故宫坐标(东经116.3972°, 北纬39.9163°)
const palaceCartesian = Cesium.Cartesian3.fromDegrees(116.3972, 39.9163)

// 计算直线距离(米)
const distance = Cesium.Cartesian3.distance(cartesian, palaceCartesian)
console.log(`直线距离: ${distance.toFixed(2)} 米`)

// 计算椭球面距离(沿地球表面)
const geodesic = new Cesium.EllipsoidGeodesic()
const carto1 = Cesium.Cartographic.fromCartesian(cartesian)
const carto2 = Cesium.Cartographic.fromCartesian(palaceCartesian)
geodesic.setEndPoints(carto1, carto2)
console.log(`地表距离: ${geodesic.surfaceDistance.toFixed(2)} 米`)

设置面坐标

返回 Cartesian3 位置数组

js 复制代码
// 返回一个 Cartesian3 位置数组
const cartesian3 = Cesium.Cartesian3.fromDegreesArray([
  116.404,
  39.915, // 第一个点的经度和纬度
  116.404,
  39.916, // 第二个点的经度和纬度
  116.405,
  39.916, // 第三个点的经度和纬度
])

// 返回一个笛卡尔3位置数组,给定经度、纬度和高度值数组
const polygonWithHeights = Cesium.Cartesian3.fromDegreesArrayHeights([
  116.404,
  39.915,
  1000, // 第一个点的经度,纬度,高度
  116.404,
  39.916,
  2000, // 第二个点的经度,纬度,高度
])

总结

主要记住两个类:Cartographic 与 Cartesian3

延伸扩展

注意

上面提到的 地理坐标系(Cartographic) 、笛卡尔空间坐标系(Cartesian3)等坐标系都属于地固系(ECEF,Earth-Centered, Earth-Fixed),即以地球为中心的坐标系。

地固系(ECCF):

  • 再cesium中: Cartesian3 (当用于表示地表位置时) 就是 地固系直角坐标。
  • Cartographic (经纬度) 和 WGS84 是地固系的球面/椭球面表达形式。它们是为了让人类更容易理解(说"东经116度"比说"X轴2000公里"直观)而存在的,但它们描述的都是随地球自转的位置。

惯性坐标系 (ECI)

如果你需要画卫星轨迹,或者通过 TLE (两行轨道根数) 计算位置,算出来的结果通常是惯性系坐标,必须经过一个转换矩阵(考虑地球自转时间、岁差、章动等)转换成 Cartesian3 (地固系),才能正确地画在 Cesium 的地球上。

定义 : ECI(Earth-Centered Inertial)是以地心为原点,但坐标轴方向相对于恒星背景固定不变的坐标系。

核心特征

  • 原点:地球质心。
  • Z 轴:指向地球北极(与 ECEF 一致)。
  • X 轴 :指向春分点(Vernal Equinox,即太阳每年春分时在天球上的位置)。
  • Y 轴:与 X、Z 轴构成右手系。
  • 静止性 :地球在转动,但 ECI 坐标系不随地球自转

应用场景

  • 卫星轨道计算:牛顿运动定律在惯性系中才严格成立。
  • 航天器姿态控制:描述航天器相对于星空的方向。
  • 天文观测:描述恒星、太阳系天体的位置。

ECI 与 ECEF 的区别(关键)

特性 ECEF (地固系 / Fixed) ECI (惯性系 / Inertial)
参考基准 地球表面 恒星背景 (宇宙空间)
随地球自转
典型用途 地图、定位、建筑、车辆 卫星轨道、弹道导弹、深空探测
Cesium 对应 Cartesian3 (默认) 需通过 Transforms 转换

代码示例:ECI 转 ECEF (核心)

在 Cesium 中渲染卫星时,通常拿到的是 ECI 坐标,必须结合当前时间转换为 ECEF 才能正确画在地球上。

1. 手动转换 (使用 Matrix3)

javascript 复制代码
// 假设我们有一个特定时间的 ECI 坐标 (例如来自 TLE 算法)
const time = Cesium.JulianDate.now()
const positionECI = new Cesium.Cartesian3(10000000.0, 20000000.0, 5000000.0)

// 1. 计算转换矩阵 (ICRF 是 ECI 的一种高精度实现)
// 必须提供时间,因为地球转角随时间变化
const transformMatrix = Cesium.Transforms.computeIcrfToFixedMatrix(time)

if (Cesium.defined(transformMatrix)) {
  // 2. 将 ECI 向量乘以转换矩阵 -> 得到 ECEF 坐标
  const positionECEF = Cesium.Matrix3.multiplyByVector(
    transformMatrix,
    positionECI,
    new Cesium.Cartesian3(),
  )

  console.log('可以直接赋值给 Entity 的坐标:', positionECEF)
}

2. 自动转换 (使用 SampledPositionProperty)

Cesium 的 SampledPositionProperty 支持定义参考系,引擎会自动处理转换。

javascript 复制代码
const property = new Cesium.SampledPositionProperty(Cesium.ReferenceFrame.INERTIAL)

// 添加不同时间点的 ECI 坐标样本
const time = Cesium.JulianDate.now()
const positionECI = new Cesium.Cartesian3(7000000.0, 0.0, 0.0)
property.addSample(time, positionECI)

// 创建实体时,Cesium 会根据当前仿真时间自动将 INERTIAL 转为 FIXED 渲染
viewer.entities.add({
  position: property,
  point: { pixelSize: 10, color: Cesium.Color.RED },
})

在 Cesium 开发中,只要涉及卫星或外部数据源(如 STK),务必确认数据是 ECEF 还是 ECI。如果是 ECI,必须引入时间维度进行矩阵转换。

J2000惯性坐标系

J2000定义

J2000.0(Julian epoch 2000.0)是国际天文学联合会(IAU)在 1984 年采纳的标准天球参考历元,指的是:

  • 时间点:儒略日 JD 2451545.0,即 公元 2000年1月1日 12:00:00 TT(地球时)
  • 坐标系:以该时刻的地球赤道面和春分点为基准建立的惯性坐标系
为什么需要J2000

由于地球自转轴存在岁差(Precession)和章动(Nutation),地球赤道面和春分点的方向会随时间缓慢变化:

  • 岁差:地球自转轴以约26,000年为周期绕黄道轴旋转
  • 章动:叠加在岁差上的短周期摆动(主周期约 18.6 年)

因此,必须选择一个固定的时刻作为参考,J2000 就是这个标准历元。

J2000定义
轴向 定义
原点 地球质心(地心)
基本平面 J2000.0 时刻的地球平赤道面
X 轴 指向 J2000.0 时刻的春分点(赤经 0h 方向)
Z 轴 指向 J2000.0 时刻的北天极(垂直于赤道面)
Y 轴 在赤道面内,与 X、Z 构成右手系(赤经 6h 方向)
J2000与ICRF的关系

ICRF(国际天球参考系) 是 J2000 的高精度实现:

  • J2000:理论定义,基于地球动力学
  • ICRF:观测实现,基于河外射电源(类星体)的精密观测
  • 差异:两者原点、方向几乎完全一致,在工程应用中通常视为等同

J2000与其他坐标系的关系

坐标系层次结构
text 复制代码
天球参考系 (Celestial Reference System)
    ├─ ICRF / J2000 (惯性系)
    │   └─ 固定于恒星背景,不随地球自转
    │
    └─ ITRF / ECEF (地固系)
        └─ 固定于地球,随地球自转
转换关系图
text 复制代码
J2000 (ECI)
    ↓ (岁差、章动、极移、地球自转)
GCRS (地心天球参考系)
    ↓ (地球自转角 ERA)
ITRF (国际地球参考系)
    ↓ (WGS84 近似等同)
WGS84 (ECEF)

J2000在Cesium中的应用

Cesium 的参考系枚举
js 复制代码
// Cesium 提供的参考系类型
Cesium.ReferenceFrame = {
  FIXED: 0, // 地固系 (ECEF / WGS84)
  INERTIAL: 1, // 惯性系 (近似 J2000 / ICRF)
}

注意:Cesium 的 INERTIAL 实际上是指 ICRF,可以近似等同于 J2000。

创建 J2000 坐标

示例 1:手动指定J2000坐标

js 复制代码
// J2000 坐标系中的位置(单位:米)
// 假设这是一个卫星在 J2000 系下的位置
const positionJ2000 = new Cesium.Cartesian3(
  7000000.0, // X 轴(指向春分点)
  0.0, // Y 轴
  0.0, // Z 轴(指向北天极)
)

// 将 J2000 坐标转换为 ECEF(地固系)
const time = Cesium.JulianDate.now()
const transformMatrix = Cesium.Transforms.computeIcrfToFixedMatrix(time)

if (Cesium.defined(transformMatrix)) {
  const positionECEF = Cesium.Matrix3.multiplyByVector(
    transformMatrix,
    positionJ2000,
    new Cesium.Cartesian3(),
  )

  console.log('ECEF坐标:', positionECEF)
}

示例 2:使用 SampledPositionProperty(推荐)

js 复制代码
// 创建 J2000 系的位置属性
const positionProperty = new Cesium.SampledPositionProperty(
  Cesium.ReferenceFrame.INERTIAL, // 指定为惯性系
)

// 添加时间-位置样本(J2000 坐标)
const startTime = Cesium.JulianDate.fromIso8601('2025-01-01T00:00:00Z')

for (let i = 0; i <= 90; i++) {
  const time = Cesium.JulianDate.addMinutes(startTime, i, new Cesium.JulianDate())

  // 模拟一个圆形轨道(在 J2000 系中)
  const angle = (i / 90) * 2 * Math.PI
  const radius = 7000000 // 7000 km
  const position = new Cesium.Cartesian3(radius * Math.cos(angle), radius * Math.sin(angle), 0)

  positionProperty.addSample(time, position)
}

// Cesium 会自动将 J2000 坐标转换为 ECEF 渲染
viewer.entities.add({
  position: positionProperty,
  point: {
    pixelSize: 10,
    color: Cesium.Color.CYAN,
  },
})

J2000 ↔ ECEF 转换原理

转换公式

从 J2000 到 ECEF 需要经过以下步骤:

scss 复制代码
J2000 (ECI)
  ↓ [岁差矩阵 P]
Mean Equator and Equinox of Date (平赤道平春分点)
  ↓ [章动矩阵 N]
True Equator and Equinox of Date (真赤道真春分点)
  ↓ [地球自转矩阵 R]
ECEF (地固系)

完整转换公式
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> r ECEF = R ( t ) ⋅ N ( t ) ⋅ P ( t ) ⋅ r J2000 \mathbf{r}{\text{ECEF}} = \mathbf{R}(t) \cdot \mathbf{N}(t) \cdot \mathbf{P}(t) \cdot \mathbf{r}{\text{J2000}} </math>rECEF=R(t)⋅N(t)⋅P(t)⋅rJ2000

其中:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( t ) \mathbf{P}(t) </math>P(t):岁差矩阵(Precession)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> N ( t ) \mathbf{N}(t) </math>N(t):章动矩阵(Nutation)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> R ( t ) \mathbf{R}(t) </math>R(t):地球自转矩阵(Rotation)
Cesium中的转换实现
javascript 复制代码
/**
 * J2000 转 ECEF 的完整示例
 * @param {Cesium.Cartesian3} positionJ2000 - J2000 系坐标(米)
 * @param {Cesium.JulianDate} time - 儒略日期
 * @returns {Cesium.Cartesian3} ECEF 坐标
 */
function j2000ToECEF(positionJ2000, time) {
  // 计算 ICRF(J2000) 到 ITRF(ECEF) 的转换矩阵
  const transformMatrix = Cesium.Transforms.computeIcrfToFixedMatrix(time)

  if (!Cesium.defined(transformMatrix)) {
    console.error('转换矩阵计算失败,可能缺少必要的天文数据')
    return null
  }

  // 执行矩阵乘法
  const positionECEF = Cesium.Matrix3.multiplyByVector(
    transformMatrix,
    positionJ2000,
    new Cesium.Cartesian3(),
  )

  return positionECEF
}

// 使用示例
const j2000Pos = new Cesium.Cartesian3(6378137, 0, 0) // 赤道上方
const currentTime = Cesium.JulianDate.now()
const ecefPos = j2000ToECEF(j2000Pos, currentTime)

console.log('J2000:', j2000Pos)
console.log('ECEF:', ecefPos)
反向转换(ECEF → J2000)
javascript 复制代码
/**
 * ECEF 转 J2000
 * @param {Cesium.Cartesian3} positionECEF - ECEF 坐标(米)
 * @param {Cesium.JulianDate} time - 儒略日期
 * @returns {Cesium.Cartesian3} J2000 坐标
 */
function ecefToJ2000(positionECEF, time) {
  // 计算转换矩阵(从 ITRF 到 ICRF)
  const transformMatrix = Cesium.Transforms.computeFixedToIcrfMatrix(time)

  if (!Cesium.defined(transformMatrix)) {
    console.error('转换矩阵计算失败')
    return null
  }

  const positionJ2000 = Cesium.Matrix3.multiplyByVector(
    transformMatrix,
    positionECEF,
    new Cesium.Cartesian3(),
  )

  return positionJ2000
}

// 使用示例
const ecefPosition = new Cesium.Cartesian3(-2173633.5, 4387689.5, 4077985.0)
const j2000Position = ecefToJ2000(ecefPosition, Cesium.JulianDate.now())

console.log('转换后的 J2000 坐标:', j2000Position)

J2000的应用

TLE轨道数据处理

TLE(Two-Line Element)数据提供的是平赤道平春分点(TEME)坐标系下的轨道根数,需要转换为 J2000:

javascript 复制代码
import * as satellite from 'satellite.js'

/**
 * 从 TLE 计算 J2000 坐标
 * @param {string} tle1 - TLE 第一行
 * @param {string} tle2 - TLE 第二行
 * @param {Date} date - 计算时刻
 * @returns {Cesium.Cartesian3} J2000 坐标(米)
 */
function tleToJ2000(tle1, tle2, date) {
  // 解析 TLE
  const satrec = satellite.twoline2satrec(tle1, tle2)

  // 推算位置(得到 TEME 系坐标,单位:公里)
  const propagated = satellite.propagate(satrec, date)
  if (!propagated.position) {
    throw new Error('轨道推算失败')
  }

  // TEME 转 J2000(简化版,实际需要更复杂的转换)
  // satellite.js 输出的是接近 J2000 的坐标,这里直接转换为米
  const positionJ2000 = new Cesium.Cartesian3(
    propagated.position.x * 1000,
    propagated.position.y * 1000,
    propagated.position.z * 1000,
  )

  return positionJ2000
}

// 使用示例:ISS 国际空间站
const tleLine1 = '1 25544U 98067A   25337.50000000  .00016717  00000-0  10270-3 0  9999'
const tleLine2 = '2 25544  51.6416  28.8015 0006703  45.6789 314.5123 15.50279142453727'

const issJ2000 = tleToJ2000(tleLine1, tleLine2, new Date())
console.log('ISS在J2000系中的位置:', issJ2000)
完整的卫星可视化
javascript 复制代码
/**
 * 在 Cesium 中渲染 J2000 系卫星轨道
 */
async function visualizeSatelliteOrbit(viewer, tle1, tle2) {
  const satrec = satellite.twoline2satrec(tle1, tle2)

  // 创建 J2000 系的位置属性
  const positionProperty = new Cesium.SampledPositionProperty(Cesium.ReferenceFrame.INERTIAL)

  // 设置插值算法
  positionProperty.setInterpolationOptions({
    interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
    interpolationDegree: 5,
  })

  const startTime = Cesium.JulianDate.now()
  const stopTime = Cesium.JulianDate.addSeconds(startTime, 5400, new Cesium.JulianDate())

  // 采样轨道
  for (let i = 0; i <= 5400; i += 30) {
    const currentTime = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate())
    const currentDate = Cesium.JulianDate.toDate(currentTime)

    // 推算 J2000 位置
    const propagated = satellite.propagate(satrec, currentDate)
    if (!propagated.position) continue

    const positionJ2000 = new Cesium.Cartesian3(
      propagated.position.x * 1000,
      propagated.position.y * 1000,
      propagated.position.z * 1000,
    )

    positionProperty.addSample(currentTime, positionJ2000)
  }

  // 创建卫星实体
  const satelliteEntity = viewer.entities.add({
    name: 'Satellite (J2000)',
    availability: new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({ start: startTime, stop: stopTime }),
    ]),
    position: positionProperty,
    point: {
      pixelSize: 10,
      color: Cesium.Color.YELLOW,
      outlineColor: Cesium.Color.WHITE,
      outlineWidth: 2,
    },
    path: {
      resolution: 60,
      material: Cesium.Color.YELLOW.withAlpha(0.5),
      width: 2,
      leadTime: 0,
      trailTime: 600,
    },
    label: {
      text: 'J2000 Orbit',
      font: '14px sans-serif',
      fillColor: Cesium.Color.WHITE,
      outlineColor: Cesium.Color.BLACK,
      outlineWidth: 2,
      style: Cesium.LabelStyle.FILL_AND_OUTLINE,
      pixelOffset: new Cesium.Cartesian2(0, -20),
    },
  })

  // 配置时钟
  viewer.clock.startTime = startTime
  viewer.clock.stopTime = stopTime
  viewer.clock.currentTime = startTime
  viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
  viewer.clock.multiplier = 60

  return satelliteEntity
}

J2000总结

  • J2000 是惯性系的标准:在天文学和航天工程中,J2000 是最常用的惯性坐标系。

  • Cesium 的 INERTIAL = ICRF ≈ J2000:工程应用中可以等同。

  • 必须考虑时间:J2000 ↔ ECEF 转换依赖于具体时刻,因为地球在不断自转。

  • 使用 SampledPositionProperty:让 Cesium 自动处理坐标系转换,避免手动计算。

  • TLE 数据不是纯 J2000 :TLE 使用的是 TEME 系,但 satellite.js 输出的结果已经接近 J2000,可直接使用。

  • 调试技巧:对比 J2000 和 ECEF 坐标,检查转换矩阵是否正常工作。

CESIUMJS 时间

CESIUM 中 JulianDate(儒略日期)和 Clock(时钟)

儒略日期

儒略日期(Julian Date,JD)是一种连续计时系统,用于天文学和航天领域。它从公元前4713年1月1日12:00(儒略历)开始计算,每一天增加1。儒略日期的优点在于它避免了日历系统中的复杂性,如闰年和月份长度的变化,使得时间计算更加简便。

JulianDate 是 Cesium 时间系统的基石,采用天文学儒略日概念,具有以下优势:

  • 连续无间断:不存在历法不连续问题(如闰年、闰秒)
  • 高精度存储:分离日和秒数存储,避免浮点精度损失
  • 时区无关性:以 UTC 时间为基准,避免时区混乱
  • 科学标准:符合国际天文学联合会标准
时间表示结构
js 复制代码
{
  day: 2460830,      // 整数部分 - 从儒略日起始日算起的天数
  secondsOfDay: 43200 // 小数部分 - 当天已过去的秒数(0-86400)
}
JulianDate 与常规日期的转换
js 复制代码
new Cesium.JulianDate(julianDayNumber, secondsOfDay, timeStandard)
名称 描述
julianDayNumber 儒略日数,表示整天数
secondsOfDay 当前儒略日数的秒数
timeStandard 定义前两个参数的时间标准,默认Cesium.TimeStandard.UTC
JulianDate 与北京时间

JulianDate 本身不涉及时区概念,它存储的是 UTC 时间,转换为北京时间需通过addHours添加 8 小时偏移。

js 复制代码
// 创建当前时间对应的 JulianDate
const julianDate = Cesium.JulianDate.fromDate(new Date())
// 将 JulianDate 转换为北京时间
const beijingDate = Cesium.JulianDate.addHours(julianDate, 8, new Cesium.JulianDate())
常规日期 → JulianDate
从当前时间创建
js 复制代码
// 创建当前时间的JulianDate(推荐)
const nowJulian = Cesium.JulianDate.now()

// 从JavaScript Date创建
const jsDate = new Date()
const fromJsDate = Cesium.JulianDate.fromDate(jsDate)
从 ISO 8601 字符串创建
js 复制代码
// 从ISO 8601字符串创建(UTC时间)
const isoString = '2025-06-04T12:00:00Z' // Z表示UTC时间
const fromIso = Cesium.JulianDate.fromIso8601(isoString)

// 带时区的ISO字符串(会自动转换为UTC)
const localIsoString = '2025-06-04T20:00:00+08:00' // 北京时间
const fromLocalIso = Cesium.JulianDate.fromIso8601(localIsoString)
JulianDate → 常规日期
转换为 JavaScript Date
js 复制代码
const julianDate = Cesium.JulianDate.now()

// 转换为JavaScript Date(会丢失纳秒精度)
const jsDate = Cesium.JulianDate.toDate(julianDate)
格式化为 ISO 8601 字符串
js 复制代码
const julianDate = Cesium.JulianDate.now()

// 格式化为ISO 8601字符串(UTC)
const isoString = Cesium.JulianDate.toIso8601(julianDate)
// 输出: "2025-06-04T12:00:00.000Z"

// 带毫秒精度
const isoWithMs = Cesium.JulianDate.toIso8601(julianDate, 3)
// 输出: "2025-06-04T12:00:00.500Z"(假设500ms)
时间操作实用函数
方法名 描述
addDays 增加天数
addHours 增加小时数
addMinutes 增加分钟数
addSeconds 增加秒数
增加时间
js 复制代码
const baseDate = Cesium.JulianDate.fromIso8601('2025-06-04T12:00:00Z')
const result = new Cesium.JulianDate()

// 增加1天3小时30分钟
const newDate = Cesium.JulianDate.addDays(baseDate, 1, result)
Cesium.JulianDate.addHours(newDate, 3, result)
Cesium.JulianDate.addMinutes(result, 30, result)

console.log(Cesium.JulianDate.toIso8601(result))
// 输出: "2025-06-05T15:30:00Z"
减少时间(使用负数)
js 复制代码
const baseDate = Cesium.JulianDate.fromIso8601('2025-06-04T12:00:00Z')
const result = new Cesium.JulianDate()

// 减少2小时
Cesium.JulianDate.addHours(baseDate, -2, result)
console.log(Cesium.JulianDate.toIso8601(result))
// 输出: "2025-06-04T10:00:00Z"

Cesium 中的时钟 Clock

Clock 作为时间推进引擎,其工作流程如下:

  1. 初始化:设置 startTime、stopTime 和初始 currentTime
  2. 推进:根据 clockStep 和 multiplier 计算新时间
  3. 边界检查:根据 clockRange 处理边界行为
  4. 事件触发:调用 onTick 监听器
js 复制代码
new Cesium.Clock(options)
属性 类型 说明
currentTime JulianDate 当前场景时间(可读写)
startTime JulianDate 时间轴起始时间
stopTime JulianDate 时间轴结束时间
multiplier Number 时间流逝倍率(正=正向播放,负=倒放)
clockStep ClockStep 判断对每一帧的调用是否依赖于帧或系统时钟。
clockRange ClockRange 到达时间边界的行为:UNBOUNDED:不停止,CLAMPED:停止在边界,LOOP_STOP:循环播放
shouldAnimate Boolean 是否自动播放(true=播放,false=暂停)
ClockStep 属性

ClockStep 决定时间如何前进:

ClockStep 值 说明 适用场景
SYSTEM_CLOCK 与系统时间同步 实时模拟
SYSTEM_CLOCK_MULTIPLIER 系统时间 × 倍率 加速/减速模拟
TICK_DEPENDENT 每帧前进固定时间 精确控制动画
ClockRange 属性

ClockRange 控制到达边界时的行为:

ClockRange 值 说明 适用场景
UNBOUNDED 不受限制继续前进 无边界时间
CLAMPED 停在边界 固定区间动画
LOOP_STOP 循环播放 重复动画
基础配置
js 复制代码
const clock = new Cesium.Clock({
  startTime: Cesium.JulianDate.fromIso8601('2023-01-01T00:00:00Z'),
  currentTime: Cesium.JulianDate.fromIso8601('2023-01-01T12:00:00Z'),
  stopTime: Cesium.JulianDate.fromIso8601('2023-01-02T00:00:00Z'),
  clockRange: Cesium.ClockRange.LOOP_STOP, // 循环播放
  multiplier: 2.0, // 2倍速播放
  clockStep: Cesium.ClockStep.SYSTEM_CLOCK,
})

// 在 Viewer 中使用
const viewer = new Cesium.Viewer('cesiumContainer', {
  clock: clock,
  shouldAnimate: true, // 播放
})
动态控制
js 复制代码
// 播放/暂停
viewer.clock.shouldAnimate = !viewer.clock.shouldAnimate

// 调整播放速度
viewer.clock.multiplier = 4.0 // 4倍速

// 倒放
viewer.clock.multiplier = -1.0 // 倒放

// 跳转到特定时间
const targetTime = Cesium.JulianDate.fromIso8601('2025-06-04T18:00:00Z')
Cesium.JulianDate.clone(targetTime, viewer.clock.currentTime)

// 重置时钟
viewer.clock.currentTime = Cesium.JulianDate.clone(viewer.clock.startTime)
相关推荐
用户81686947472527 分钟前
Lane 优先级模型与时间切片调度
前端·react.js
虎头金猫27 分钟前
MateChat赋能电商行业智能导购:基于DevUI的技术实践
前端·前端框架·aigc·ai编程·ai写作·华为snap·devui
豆苗学前端37 分钟前
面试复盘:谈谈你对 原型、原型链、构造函数、实例、继承的理解
前端·javascript·面试
Crystal3281 小时前
Git 基础:生成版本、撤消操作、版本重置、忽略文件
前端·git·github
lichenyang4531 小时前
React 组件通讯全案例解析:从 Context 到 Ref 的实战应用
前端
姓王者1 小时前
chen-er 专为Chen式ER图打造的npm包
前端·javascript
青莲8431 小时前
Android Jetpack - 2 ViewModel
android·前端
崽崽的谷雨1 小时前
react里ag-grid实现树形数据展示
前端·react.js·前端框架
栀秋6661 小时前
就地编辑功能开发指南:从代码到体验的优雅蜕变
前端·javascript·代码规范