文章目錄
- Cesium 基礎理解(二)
- TimeLine & Clock 應用場景
- 核心代碼實例及解釋
- 代碼解釋
- Cesium 之 實體動畫
- 構建實體動畫的技巧
- 1. 利用時間屬性
- 2. 組合動畫效果
- 3. 使用動畫曲線
- 優化點
- 1. 減少屬性更新頻率
- 2. 優化實體數量
- 3. 合理使用材質和紋理
- 注意事項
- 1. 時間同步
- 2. 內存管理
- 3. 兼容性
- 坐標系轉換
- 1. WGS84 與 Web 墨卡托投影坐標系轉換
- 1.1 WGS84 轉 Web 墨卡托
- 1.2 Web 墨卡托轉 WGS84
- 2. 二維屏幕坐標與 WGS84 坐標轉換
- 2.1 二維屏幕坐標轉 WGS84 坐標
- 2.2 WGS84 坐標轉二維屏幕坐標
- 3. 二維平面坐標與 WGS84 坐標轉換
- 3.1 二維平面坐標(局部坐標系)轉 WGS84 坐標
- 3.2 WGS84 坐標轉二維平面坐標(局部坐標系)
- 技術名詞解釋(局部坐標系)
- 定義與概念
- 為什么需要局部坐標系
- 局部坐標系的構成要素
- 在 Cesium 中的應用示例解釋
- 定義局部二維平面坐標(x, y 偏移量)的原因
- 是否可以隨便改值
Cesium 基礎理解(二)
在 Cesium 里,TimeLine
(時間軸)與 Clock
(時鐘)是用來管理和展示時間相關數據與動畫的重要組件。Clock
負責管理時間的推進,而 TimeLine
則提供了一個可視化的界面,讓用戶能夠與時間進行交互。下面為你詳細介紹它們的應用,并給出核心代碼及解釋。
TimeLine & Clock 應用場景
- 動態數據可視化:像氣象數據、交通流量數據這類隨時間變化的數據,可借助時間軸與時鐘來展示不同時間點的數據。
- 動畫演示:在模擬飛行、天體運動等場景時,能夠利用時間軸和時鐘控制動畫的播放。
- 歷史數據回放:對歷史事件進行回放,用戶可通過時間軸選擇特定的時間點查看相應的數據。
核心代碼實例及解釋
下面的代碼展示了如何在 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);
});
代碼解釋
- 初始化 Cesium Viewer:
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
事件,在每次時鐘更新時,根據當前時間計算一個顏色值,并更新實體的顏色。這樣就實現了一個簡單的動畫效果。
Cesium 之 實體動畫
構建實體動畫的技巧
1. 利用時間屬性
- 插值動畫:借助
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
,以精確控制動畫的開始、結束和當前播放位置。
2. 組合動畫效果
- 多屬性動畫:同時對實體的多個屬性(如位置、顏色、大小)進行動畫設置,從而創建更豐富的動畫效果。例如,在實體移動的同時改變其顏色。
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);
});
- 序列動畫:按順序依次播放不同的動畫,實現復雜的動畫流程。你可以通過在不同的時間區間設置不同的屬性值來達成這一目的。
3. 使用動畫曲線
- 自定義插值函數:除了默認的線性插值,你還可以自定義插值函數,以實現更自然的動畫效果。例如,使用緩動函數(如
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);
});
優化點
1. 減少屬性更新頻率
- 批量更新:避免在每一幀都對實體的屬性進行更新,可在必要時批量更新屬性。例如,只有當時間間隔達到一定值時才更新實體的顏色。
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;}
});
- 緩存計算結果:對于一些復雜的計算結果,如三角函數值、插值系數等,進行緩存,避免重復計算。
2. 優化實體數量
- 按需加載:僅在需要時加載和顯示實體,避免一次性加載過多實體導致性能下降。例如,根據相機的位置和視野范圍,動態加載和卸載實體。
- 合并實體:如果多個實體具有相似的屬性和動畫效果,可以考慮將它們合并為一個實體,減少渲染開銷。
3. 合理使用材質和紋理
- 簡單材質:盡量使用簡單的材質和紋理,避免使用過于復雜的紋理和特效,以減少 GPU 負擔。
- 紋理壓縮:對紋理進行壓縮,降低紋理的內存占用。
注意事項
1. 時間同步
- 時鐘設置:確保所有實體的動畫都使用相同的
Clock
對象,以保證動畫的時間同步。 - 跨幀一致性:在動畫回調函數中,要確保每次更新的結果在不同幀之間具有一致性,避免出現跳躍或閃爍的現象。
2. 內存管理
- 實體銷毀:當動畫結束或不再需要某個實體時,及時銷毀該實體,釋放內存。
viewer.entities.remove(entity);
- 屬性清理:對于不再使用的屬性對象,及時清理,避免內存泄漏。
3. 兼容性
- 不同瀏覽器和設備:在不同的瀏覽器和設備上測試動畫效果,確保動畫在各種環境下都能正常顯示和運行。
- 版本兼容性:注意 Cesium 庫的版本兼容性,不同版本的 API 可能會有所不同。
坐標系轉換
在 Cesium 里,坐標系轉化是常見的操作,特別是在處理 WGS84(地理坐標系)、Web 墨卡托投影坐標系以及二維坐標與平面坐標轉換時。下面為你詳細介紹這些坐標系轉化的方法,并給出對應的代碼示例。
1. WGS84 與 Web 墨卡托投影坐標系轉換
1.1 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);
1.2 Web 墨卡托轉 WGS84
// 定義 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;
2. 二維屏幕坐標與 WGS84 坐標轉換
2.1 二維屏幕坐標轉 WGS84 坐標
當用戶在屏幕上點擊某個位置時,需要將屏幕坐標轉換為 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);
2.2 WGS84 坐標轉二維屏幕坐標
// 定義 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);
3. 二維平面坐標與 WGS84 坐標轉換
3.1 二維平面坐標(局部坐標系)轉 WGS84 坐標
如果有一個局部的二維平面坐標系,需要將其轉換為 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;
3.2 WGS84 坐標轉二維平面坐標(局部坐標系)
// 定義目標 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 地理坐標系)而言的,它是為了方便在特定區域內進行精確的位置描述和計算而建立的一種相對坐標系。在局部坐標系里,我們可以自由定義原點、坐標軸方向和單位長度,以此簡化在該特定區域內的幾何計算和數據處理。
為什么需要局部坐標系
- 簡化計算:在處理小范圍的地理數據或者三維模型時,使用全局的 WGS84 坐標系可能會因為數值過大或者地球曲率的影響,導致計算復雜且容易出現精度問題。而局部坐標系可以將坐標值縮小到一個相對較小的范圍內,減少計算誤差,提高計算效率。
- 方便建模:在創建三維模型或者進行場景設計時,使用局部坐標系可以更直觀地描述物體的位置和方向。例如,在設計一個建筑物的三維模型時,我們可以將建筑物的某個角點作為局部坐標系的原點,然后根據建筑物的實際尺寸和方向來定義坐標軸,這樣就可以更方便地確定建筑物各個部分的位置。
局部坐標系的構成要素
- 原點:局部坐標系的原點是一個在全局坐標系中已知位置的點。在地理場景中,這個原點可以是某個特定的地理坐標點,比如一個城市的中心、一個建筑物的某個角點等。
- 坐標軸方向:局部坐標系的坐標軸方向通常是根據具體的應用需求來定義的。在二維平面中,常見的做法是將一個坐標軸定義為水平方向(通常是向東),另一個坐標軸定義為垂直方向(通常是向北)。在三維空間中,還需要定義一個垂直于平面的坐標軸(通常是向上)。
- 單位長度:局部坐標系的單位長度可以根據實際情況進行定義。在地理場景中,常用的單位長度是米。
在 Cesium 中的應用示例解釋
在之前提供的 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;
- 原點定義:代碼中首先定義了一個原點的 WGS84 坐標,將其轉換為笛卡爾坐標
originCartesian
,這個點就是局部坐標系的原點。 - 局部坐標表示:
localX
和localY
表示在局部坐標系中的偏移量,單位為米。通過將這些偏移量加到原點的笛卡爾坐標上,就可以得到在全局坐標系中的新位置。 - 坐標轉換:最后,將得到的笛卡爾坐標轉換回 WGS84 坐標,這樣就完成了從局部坐標系到全局坐標系的轉換。
通過這種方式,我們可以在局部坐標系中方便地進行位置計算和處理,然后再將結果轉換回全局坐標系進行展示和分析。
定義局部二維平面坐標(x, y 偏移量)的原因
在構建局部坐標系并進行坐標轉換時,定義 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 坐標并輸出。