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 作为时间推进引擎,其工作流程如下:
- 初始化:设置 startTime、stopTime 和初始 currentTime
- 推进:根据 clockStep 和 multiplier 计算新时间
- 边界检查:根据 clockRange 处理边界行为
- 事件触发:调用 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)