前端也能做地形分析?用 Cesium 在浏览器里算 “坡度”

发布时间:2026/7/30 18:17:10
前端也能做地形分析?用 Cesium 在浏览器里算 “坡度” 不用后台不用 GIS 服务器纯前端实现地形坡度分析——从采样到渲染一篇文章讲清楚。开篇上周做 Cesium 项目甲方要在地图上直接分析地形坡度画个矩形框框里哪陡哪缓一目了然。我第一反应是调 ArcGIS Server 的 GP 服务。后来一琢磨——不对啊。Cesium 自己就能采样地形高度坡度计算本质上就是个体力活干嘛非要绕一圈后端于是花了一个下午纯前端撸了一个。画矩形、采地形、算坡度、上色渲染全在浏览器里跑完。这篇文章把我踩过的坑和核心思路拆开讲清楚文末附完整代码拿来就能跑。环境技术栈很简单Cesium 1.140CDN 引入 Vue 3做响应式 lil-gui做控制面板。Cesium CDN → 球体 地形采样 Vue 3 CDN → 数据绑定 状态管理 lil-gui → 参数调节面板网格数、色带、透明度…地形数据用的本地CesiumTerrainProviderDEM 切片服务换成 Cesium 官方地形Cesium World Terrain完全一样采样接口是通用的。核心功能1. 矩形绘制给分析区画个框交互逻辑不复杂但细节不少左键点第一下→ 记录第一个角点 →移动鼠标→ 实时预览半透明矩形 →左键点第二下→ 锁定范围。关键技术点两个经纬度点确定Cesium.Rectangle。不管用户先点左上还是右下代码自动取 min/max 算出 west/east/south/northletwestMath.min(lon1,lon2);leteastMath.max(lon1,lon2);letsouthMath.min(lat1,lat2);letnorthMath.max(lat1,lat2);预览矩形用了CallbackProperty——鼠标移动时实时更新坐标不用反复创建销毁 Entity性能好得多。坑点预览矩形要设heightReference: CLAMP_TO_GROUND并开启depthTestAgainstTerrain否则矩形会飘在半空或者被地形遮挡。这事儿我当时调试了半小时。2. 地形采样把面变成网格点这是整个流程里最耗时的一步。用户画的矩形框里面要生成一个网格——行列数可调默认 100×100 10000 个采样点。用Cesium.sampleTerrain(terrainProvider, level, positions)批量采样。采样层级terrainLevel越高精度越好但请求量指数增长。默认 12 级对应约 38 米分辨率做坡度分析够用了。这里有个反常识的发现网格密度cols×rows对视觉效果的影响远大于采样层级。100×100 在 level 10 下已经能看但 40×40 在 level 15 下依然很糙。所以默认用 100×100 level 12性价比最高。说白了网格密度才是画面细腻度的瓶颈采样层级只是决定了高度值本身有多准。// 构建采样点网格for(letj0;jrows;j){letlatsouth(latSpan*j)/(rows-1);for(leti0;icols;i){letlonwest(lonSpan*i)/(cols-1);positions.push(newCesium.Cartographic(lon,lat,0));}}letsampledawaitCesium.sampleTerrain(provider,level,positions);3. 坡度计算中心差分法这是整个工具的心脏。对每个网格点(i, j)不是只看它自己的高度而是看它和相邻点的高度差分别算出东西方向和南北方向的梯度// 东西方向梯度中心差分dhdEast(height[i1][j]-height[i-1][j])/(2*cellSizeMeters);// 南北方向梯度dhdNorth(height[i][j1]-height[i][j-1])/(2*cellSizeMeters);// 坡度 atan(√(dz/dx² dz/dy²)) 转角度slopeDegMath.atan(Math.sqrt(dhdEast²dhdNorth²))*180/Math.PI;边界点用单侧差分向前或向后保证每个有效格网都有坡度值。注意cellSizeMeters的计算——经纬度跨度要转成米。纬度方向直接用地球半径6378137m经度方向要乘cos(latitude)leteastMeterPerRad6378137*Math.cos(lat);letnorthMeterPerRad6378137;漏掉这个cos(lat)高纬度地区的坡度会严重偏大。4. 色带渲染从数字到颜色坡度算出来是一堆数字0°~90°要变成看得见的颜色。做法是用 Canvas 逐像素绘制→toDataURL()→SingleTileImageryProvider→ 贴回 Cesium 球体。每一像素对应一个格网点根据坡度值和色带配置算出 RGBA// 坡度归一化到 0~1传入色带查色lettMath.max(0,Math.min(1,slope/maxSlope));letcolorinterpolateColorRamp(colorRamp,t);data[p]color[0];// Rdata[p1]color[1];// Gdata[p2]color[2];// Bdata[p3]Math.round(255*alpha);// A内置了5 种色带经典坡度绿→黄→橙→红→紫、地形高差、热力图、冷暖对比、灰度。色带切换 实时重分析随便试。SingleTileImageryProvider的好处渲染结果就是一张图片不占额外显存缩放性能跟普通影像图层一样。5. 悬停交互鼠标指哪看哪分析结果显示后鼠标移上去能看到当前点的精确坡度值。实现ScreenSpaceEventHandler监听MOUSE_MOVE→scene.globe.pick反算经纬度 → 算出在网格中的行列号 → 读slopeGrid.values[j*colsi]。整个 tooltip 用 Vue 响应式驱动v-if控制显隐v-html渲染内容divv-ifhoverTooltip.showclasshover-tooltip:style{ left: hoverTooltip.xpx, top: hoverTooltip.ypx }v-htmlhoverTooltip.html/div参数调优建议总结几个经验值默认 100×100 网格 level 12 采样 45° 最大坡度——适合大多数丘陵和山地场景。平坦地区把maxSlope调到 20° 左右对比度会更明显。注意网格设到 500×500 以上采样点超过 25 万个浏览器会明显卡顿。不是算不动是 Canvas 像素太多。建议控制在 300×300 以内。写在最后纯前端做坡度分析这件事说实话比我预想的顺。Cesium 的地形采样接口设计得挺好剩下就是数学题和一个 Canvas。这个方案的局限性也明显采样精度受限于 DEM 数据源。如果 DEM 是 30 米分辨率你网格再密也没用。真要工程级别的精度分析还是得上专业 GIS 软件。不过作为前端可视化工具——快速看一眼地形走势判断哪里适合建、哪里要避开——这个纯前端方案完全够用了。 拿去改改用吧有问题的欢迎留言。完整代码下面是可以直接运行的完整 HTML 文件。改一下CesiumTerrainProvider.fromUrl里的地形地址或用 Cesium 官方地形打开就能用。见原文https://mp.weixin.qq.com/s/duxDtA4ZvRaVC5OyDqlMoQ往期精选Cesium 通视分析3D 城市模型上划线看可见区手搓 3D 热力图从 Canvas 热力到 GPU 顶点着色器Cesium 点聚合海量点地图可视化方案自定义 “虚线箭头” 材质把两个内置 Material 焊在一起Cesium 中文字体贴地 / 贴墙 / 动态文字方案网络地图坐标系完全指南WGS84 / GCJ02 / BD09 / CGCS2000 与坐标转换实战