Cesium.js中基于速度的渐变折线实现及3D Tileset适配技术问询
Cesium.js中基于速度的渐变折线实现及3D Tileset适配技术问询
你好!要实现类似Garmin或Strava那种基于速度的渐变轨迹线,在Cesium里完全可以做到,核心思路是把轨迹拆分成相邻点的线段,为每个线段设置从起点速度颜色到终点速度颜色的渐变材质,同时也能轻松适配3D Tileset。下面是完整的实现方案和代码修改:
核心实现步骤
- 数据预处理:把经纬度+速度数据转换成Cesium可用的坐标格式,同时提取速度值用于颜色映射
- 颜色映射逻辑:将速度值(从最小到最大)映射到蓝-黄-红的渐变区间,用HSL颜色空间实现自然过渡
- 分段折线创建:遍历每一对相邻轨迹点,为每个线段单独设置渐变材质
- 3D Tiles适配:让折线贴合3D Tiles表面,保证视觉一致性
完整实现代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script src="https://cesium.com/downloads/cesiumjs/releases/1.122/Build/Cesium/Cesium.js"></script> <link href="https://cesium.com/downloads/cesiumjs/releases/1.122/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> <style> html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } </style> </head> <body> <div id="cesiumContainer"></div> <script> Cesium.Ion.defaultAccessToken = 'MY_CESIUM.ION_ACCESS_TOKEN'; const viewer = new Cesium.Viewer("cesiumContainer", { requestRenderMode: true, maximumRenderTimeChange: Infinity }); // 修正后的数据格式:每个元素是[经度, 纬度, 速度(m/s)] const trackData = [ [-75, 35, 30], [-100, 35, 56], [-125, 35, 76], [-150, 35, 75] ]; // 提取所有速度值,计算最小和最大速度 const speeds = trackData.map(point => point[2]); const minSpeed = Math.min(...speeds); const maxSpeed = Math.max(...speeds); const speedRange = maxSpeed - minSpeed; // 颜色映射函数:速度 -> 蓝-黄-红渐变 function getColorFromSpeed(speed) { // HSL颜色空间:蓝色H=240,黄色H=60,红色H=0 let hue = 240 - ((speed - minSpeed) / speedRange) * 240; // 处理速度等于max的情况,避免hue为负数 hue = Math.max(0, Math.min(240, hue)); return Cesium.Color.fromHsl(hue / 360, 1.0, 0.5); } // 转换经纬度到Cesium笛卡尔坐标 function getCartesian(lon, lat) { return Cesium.Cartesian3.fromDegrees(lon, lat); // 如果需要贴合3D Tiles/地形,改用下面的异步方法(需开启地形支持) // return Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [Cesium.Cartographic.fromDegrees(lon, lat)]) // .then(results => Cesium.Cartesian3.fromRadians(results[0].longitude, results[0].latitude, results[0].height)); } // 遍历创建每个分段折线 for (let i = 0; i < trackData.length - 1; i++) { const startPoint = trackData[i]; const endPoint = trackData[i + 1]; // 获取起点和终点坐标 const startCartesian = getCartesian(startPoint[0], startPoint[1]); const endCartesian = getCartesian(endPoint[0], endPoint[1]); // 获取起点和终点颜色 const startColor = getColorFromSpeed(startPoint[2]); const endColor = getColorFromSpeed(endPoint[2]); // 添加分段折线实体 viewer.entities.add({ polyline: { positions: [startCartesian, endCartesian], width: 5, // 使用渐变材质 material: new Cesium.PolylineMaterialProperty({ fabric: { type: 'PolylineGradient', uniforms: { color: new Cesium.CallbackProperty(() => { return Cesium.ColorGeometryInstanceAttribute.toValue(startColor, endColor); }, false) } } }) } }); } // 定位到轨迹区域 const boundingSphere = Cesium.BoundingSphere.fromPoints(trackData.map(p => getCartesian(p[0], p[1]))); viewer.camera.flyToBoundingSphere(boundingSphere); </script> </body> </html>
关键细节说明
- 颜色映射:用HSL颜色空间实现的渐变比RGB更自然,蓝色到红色的过渡会经过黄色,完美匹配你想要的效果
- 分段折线:将长折线拆成小线段是Cesium中实现线段间渐变的常用方法,因为原生Polyline不支持跨多段的渐变
- 3D Tiles适配:如果你的场景加载了3D Tiles,只需要把
getCartesian函数换成异步获取地形/3D Tiles高度的方法,同时可以设置polyline.clampToGround = true让折线自动贴合地面(需要开启地形支持)
效果说明
最终实现的轨迹线会根据每个线段两端的速度呈现渐变颜色:速度最低的部分显示蓝色,中等速度显示黄色,最高速度显示红色,和你想要的Garmin参考图效果一致。
备注:内容来源于stack exchange,提问作者Tobiasz
相关产品推荐
相关产品推荐

