在 Cesium 里,TimeLine
(时间轴)与 Clock
(时钟)是用来管理和展示时间相关数据与动画的重要组件。Clock
负责管理时间的推进,而 TimeLine
则提供了一个可视化的界面,让用户能够与时间进行交互。下面为你详细介绍它们的应用,并给出核心代码及解释。
下面的代码展示了如何在 Cesium 中使用 TimeLine
和 Clock
来创建一个简单的动画:
// 初始化 Cesium Viewer
var viewer = new Cesium.Viewer('cesiumContainer', {
timeline: true, // 显示时间轴
animation: true // 显示动画控制器
});
// 设置时钟的起始时间和结束时间
var start = Cesium.JulianDate.fromIso8601('2025-04-03T00:00:00Z');
var stop = Cesium.JulianDate.addDays(start, 1, new Cesium.JulianDate());
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
// 设置时间乘数和时钟范围
viewer.clock.multiplier = 3600; // 每帧前进一小时
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 到达结束时间后循环播放
viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER;
// 创建一个实体,用于演示动画
var entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
// 动画回调函数
viewer.clock.onTick.addEventListener(function (clock) {
// 在每次时钟更新时,更新实体的颜色
var currentTime = clock.currentTime;
var fraction = Cesium.JulianDate.secondsDifference(currentTime, start) / Cesium.JulianDate.secondsDifference(stop, start);
entity.point.color = Cesium.Color.fromHsl(fraction, 1.0, 0.5);
});
var viewer = new Cesium.Viewer('cesiumContainer', {
timeline: true, // 显示时间轴
animation: true // 显示动画控制器
});
创建一个 Cesium Viewer 实例,并且启用时间轴和动画控制器。
var start = Cesium.JulianDate.fromIso8601('2025-04-03T00:00:00Z');
var stop = Cesium.JulianDate.addDays(start, 1, new Cesium.JulianDate());
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
定义时钟的起始时间和结束时间,并且将当前时间设置为起始时间。
viewer.clock.multiplier = 3600; // 每帧前进一小时
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 到达结束时间后循环播放
viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER;
multiplier
:设置时间推进的速度,这里表示每帧前进一小时。clockRange
:设置时钟到达结束时间后的行为,LOOP_STOP
表示到达结束时间后循环播放。clockStep
:设置时钟推进的方式,SYSTEM_CLOCK_MULTIPLIER
表示根据系统时钟和 multiplier
推进。var entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
创建一个红色的点实体,用于演示动画。
viewer.clock.onTick.addEventListener(function (clock) {
// 在每次时钟更新时,更新实体的颜色
var currentTime = clock.currentTime;
var fraction = Cesium.JulianDate.secondsDifference(currentTime, start) / Cesium.JulianDate.secondsDifference(stop, start);
entity.point.color = Cesium.Color.fromHsl(fraction, 1.0, 0.5);
});
监听 viewer.clock.onTick
事件,在每次时钟更新时,根据当前时间计算一个颜色值,并更新实体的颜色。这样就实现了一个简单的动画效果。
SampledPositionProperty
、ConstantProperty
等属性类,为实体的位置、姿态等属性设置随时间变化的值。例如,你可以使用 SampledPositionProperty
按时间顺序添加多个位置点,Cesium 会自动在这些点之间进行插值,从而实现平滑的移动动画。var positionProperty = new Cesium.SampledPositionProperty();
positionProperty.addSample(Cesium.JulianDate.fromIso8601('2025-04-03T00:00:00Z'), Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883));
positionProperty.addSample(Cesium.JulianDate.fromIso8601('2025-04-03T01:00:00Z'), Cesium.Cartesian3.fromDegrees(-76.59777, 41.03883));
var entity = viewer.entities.add({
position: positionProperty,
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
Clock
的 startTime
、stopTime
和 currentTime
,以精确控制动画的开始、结束和当前播放位置。viewer.clock.onTick.addEventListener(function (clock) {
var currentTime = clock.currentTime;
var fraction = Cesium.JulianDate.secondsDifference(currentTime, start) / Cesium.JulianDate.secondsDifference(stop, start);
entity.point.color = Cesium.Color.fromHsl(fraction, 1.0, 0.5);
entity.point.pixelSize = 10 + 10 * Math.sin(fraction * Math.PI * 2);
});
easeInOut
)让动画的开始和结束更加平滑。function easeInOut(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
viewer.clock.onTick.addEventListener(function (clock) {
var currentTime = clock.currentTime;
var fraction = Cesium.JulianDate.secondsDifference(currentTime, start) / Cesium.JulianDate.secondsDifference(stop, start);
var easedFraction = easeInOut(fraction);
entity.point.color = Cesium.Color.fromHsl(easedFraction, 1.0, 0.5);
});
var updateInterval = 0.1; // 每 0.1 秒更新一次
var lastUpdateTime = -Infinity;
viewer.clock.onTick.addEventListener(function (clock) {
var currentTime = clock.currentTime;
var elapsedSeconds = Cesium.JulianDate.secondsDifference(currentTime, start);
if (elapsedSeconds - lastUpdateTime >= updateInterval) {
var fraction = elapsedSeconds / Cesium.JulianDate.secondsDifference(stop, start);
entity.point.color = Cesium.Color.fromHsl(fraction, 1.0, 0.5);
lastUpdateTime = elapsedSeconds;
}
});
Clock
对象,以保证动画的时间同步。viewer.entities.remove(entity);
在 Cesium 里,坐标系转化是常见的操作,特别是在处理 WGS84(地理坐标系)、Web 墨卡托投影坐标系以及二维坐标与平面坐标转换时。下面为你详细介绍这些坐标系转化的方法,并给出对应的代码示例。
WGS84 是一种地理坐标系,使用经度、纬度和高度来表示位置;而 Web 墨卡托投影坐标系是一种平面坐标系,常用于地图显示。在 Cesium 中,可以使用 Cesium.Cartographic
和 Cesium.WebMercatorProjection
来实现转换。
// 定义 WGS84 坐标(经度、纬度、高度)
var longitude = Cesium.Math.toRadians(-75.59777);
var latitude = Cesium.Math.toRadians(40.03883);
var height = 0;
// 创建 Cartographic 对象
var cartographic = new Cesium.Cartographic(longitude, latitude, height);
// 创建 Web 墨卡托投影对象
var projection = new Cesium.WebMercatorProjection();
// 转换为 Web 墨卡托坐标
var webMercator = projection.project(cartographic);
// 定义 Web 墨卡托坐标
var x = webMercator.x;
var y = webMercator.y;
// 反投影到 WGS84
var cartographicBack = projection.unproject(webMercator);
// 获取经度、纬度和高度
var longitudeBack = Cesium.Math.toDegrees(cartographicBack.longitude);
var latitudeBack = Cesium.Math.toDegrees(cartographicBack.latitude);
var heightBack = cartographicBack.height;
当用户在屏幕上点击某个位置时,需要将屏幕坐标转换为 WGS84 坐标。可以使用 viewer.scene.pickPosition
方法来实现。
// 监听鼠标点击事件
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
// 获取屏幕坐标
var cartesian = viewer.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
if (cartesian) {
// 将笛卡尔坐标转换为 WGS84 坐标
var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
var longitude = Cesium.Math.toDegrees(cartographic.longitude);
var latitude = Cesium.Math.toDegrees(cartographic.latitude);
var height = cartographic.height;
console.log('经度:', longitude, '纬度:', latitude, '高度:', height);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 定义 WGS84 坐标
var longitude = Cesium.Math.toRadians(-75.59777);
var latitude = Cesium.Math.toRadians(40.03883);
var height = 0;
// 创建 Cartographic 对象
var cartographic = new Cesium.Cartographic(longitude, latitude, height);
// 将 WGS84 坐标转换为笛卡尔坐标
var cartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(cartographic);
// 将笛卡尔坐标转换为屏幕坐标
var canvasPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, cartesian);
如果有一个局部的二维平面坐标系,需要将其转换为 WGS84 坐标,可以先定义一个原点的 WGS84 坐标,然后根据平面坐标的偏移量进行转换。
// 定义原点的 WGS84 坐标
var originLongitude = Cesium.Math.toRadians(-75.59777);
var originLatitude = Cesium.Math.toRadians(40.03883);
var originHeight = 0;
var originCartographic = new Cesium.Cartographic(originLongitude, originLatitude, originHeight);
var originCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(originCartographic);
// 定义局部二维平面坐标(x, y 偏移量)
var localX = 100;
var localY = 200;
// 假设局部坐标系的单位为米,计算偏移后的笛卡尔坐标
var offsetCartesian = new Cesium.Cartesian3(
originCartesian.x + localX,
originCartesian.y + localY,
originCartesian.z
);
// 将笛卡尔坐标转换为 WGS84 坐标
var cartographic = Cesium.Cartographic.fromCartesian(offsetCartesian);
var longitude = Cesium.Math.toDegrees(cartographic.longitude);
var latitude = Cesium.Math.toDegrees(cartographic.latitude);
var height = cartographic.height;
// 定义目标 WGS84 坐标
var targetLongitude = Cesium.Math.toRadians(-75.59778);
var targetLatitude = Cesium.Math.toRadians(40.03884);
var targetHeight = 0;
var targetCartographic = new Cesium.Cartographic(targetLongitude, targetLatitude, targetHeight);
var targetCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(targetCartographic);
// 计算相对于原点的偏移量
var offset = Cesium.Cartesian3.subtract(targetCartesian, originCartesian, new Cesium.Cartesian3());
var localX = offset.x;
var localY = offset.y;
通过上述代码示例,你可以在 Cesium 中实现不同坐标系之间的转换。在实际应用中,需要根据具体需求选择合适的转换方法。
在地理信息系统(GIS)和三维可视化场景(如使用 Cesium 构建的场景)中,局部坐标系是一种非常有用的概念,下面为你详细解释。
局部坐标系是相对于全局坐标系(如 WGS84 地理坐标系)而言的,它是为了方便在特定区域内进行精确的位置描述和计算而建立的一种相对坐标系。在局部坐标系里,我们可以自由定义原点、坐标轴方向和单位长度,以此简化在该特定区域内的几何计算和数据处理。
在之前提供的 Cesium 代码示例中,我们构建了一个简单的局部二维平面坐标系并进行了坐标转换:
// 定义原点的 WGS84 坐标
var originLongitude = Cesium.Math.toRadians(-75.59777);
var originLatitude = Cesium.Math.toRadians(40.03883);
var originHeight = 0;
var originCartographic = new Cesium.Cartographic(originLongitude, originLatitude, originHeight);
var originCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(originCartographic);
// 定义局部二维平面坐标(x, y 偏移量)
var localX = 100;
var localY = 200;
// 假设局部坐标系的单位为米,计算偏移后的笛卡尔坐标
var offsetCartesian = new Cesium.Cartesian3(
originCartesian.x + localX,
originCartesian.y + localY,
originCartesian.z
);
// 将笛卡尔坐标转换为 WGS84 坐标
var cartographic = Cesium.Cartographic.fromCartesian(offsetCartesian);
var longitude = Cesium.Math.toDegrees(cartographic.longitude);
var latitude = Cesium.Math.toDegrees(cartographic.latitude);
var height = cartographic.height;
originCartesian
,这个点就是局部坐标系的原点。localX
和 localY
表示在局部坐标系中的偏移量,单位为米。通过将这些偏移量加到原点的笛卡尔坐标上,就可以得到在全局坐标系中的新位置。通过这种方式,我们可以在局部坐标系中方便地进行位置计算和处理,然后再将结果转换回全局坐标系进行展示和分析。
在构建局部坐标系并进行坐标转换时,定义 localX
和 localY
这两个偏移量是为了描述在局部坐标系中某个点相对于原点的位置。
在实际应用场景里,我们常常需要在特定的小范围内进行精确的位置计算和表示。以地理信息系统为例,当你要描述一个建筑物内部各个房间的位置,或者一个小型园区内各个设施的位置时,直接使用全局的地理坐标(如 WGS84 坐标)会比较复杂,而且在小范围内使用地理坐标进行计算还可能会因为地球曲率等因素产生精度问题。
这时,我们就可以建立一个局部坐标系,将这个小范围的某个特征点(如建筑物的某个墙角、园区的大门位置等)作为局部坐标系的原点。然后,使用 localX
和 localY
来表示其他点相对于原点在水平和垂直方向上的偏移距离,这样就能更方便、更精确地描述这些点的位置。
可以随意修改 localX
和 localY
的值,这取决于你具体的需求:
localX
和 localY
的值可以表示局部坐标系中不同的点。例如,如果你想描述局部坐标系中另一个位置的点,就可以通过调整这两个偏移量来实现。比如,将 localX
改为 300,localY
改为 400,就表示该点相对于原点在水平方向上偏移 300 个单位(这里假设单位是米),在垂直方向上偏移 400 个单位。localX
和 localY
的值,来模拟物体在局部坐标系中的移动。例如,在一个模拟车辆在园区内行驶的场景中,随着时间的推移,不断更新 localX
和 localY
的值,就可以让车辆在局部坐标系中移动,然后再将其转换到全局坐标系中进行展示。不过,在修改这两个值时,需要注意以下几点:
localX
和 localY
的单位与你所设定的局部坐标系的单位一致。如果单位是米,那么所有的偏移量都应该以米为单位进行计算。以下是一个简单的示例代码,展示了如何动态改变 localX
和 localY
的值来模拟物体的移动:
// 定义原点的 WGS84 坐标
var originLongitude = Cesium.Math.toRadians(-75.59777);
var originLatitude = Cesium.Math.toRadians(40.03883);
var originHeight = 0;
var originCartographic = new Cesium.Cartographic(originLongitude, originLatitude, originHeight);
var originCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(originCartographic);
// 初始局部二维平面坐标(x, y 偏移量)
var localX = 0;
var localY = 0;
// 模拟物体移动,每 1 秒更新一次位置
setInterval(function () {
localX += 10;
localY += 10;
// 假设局部坐标系的单位为米,计算偏移后的笛卡尔坐标
var offsetCartesian = new Cesium.Cartesian3(
originCartesian.x + localX,
originCartesian.y + localY,
originCartesian.z
);
// 将笛卡尔坐标转换为 WGS84 坐标
var cartographic = Cesium.Cartographic.fromCartesian(offsetCartesian);
var longitude = Cesium.Math.toDegrees(cartographic.longitude);
var latitude = Cesium.Math.toDegrees(cartographic.latitude);
var height = cartographic.height;
console.log('当前位置:经度', longitude, '纬度', latitude, '高度', height);
}, 1000);
在这个示例中,每 1 秒 localX
和 localY
都会增加 10,从而模拟物体在局部坐标系中的移动,然后将其转换为全局的 WGS84 坐标并输出。