上一篇结尾我撂下一句话:
这条空间分析线才刚开头。地形挖开了,下一步自然就是沿着一条线把它切开看看里面长什么样------剖面分析,我们下一篇见。
坑挖完了,现在该切了。
从 #53 的标记标绘,到 #54 的地形开挖,这条空间分析线走的其实是同一个逻辑:**先把球上看得见的东西,变成算得准的东西。**点位标记那篇,我们把"一个点"变成了清单里的一行;地形开挖那篇,我们把"一块地"变成了一个洞。今天轮到"一条线"------把球上那条剖面线,变成一张高程曲线图。
剖面分析是空间分析里最不花哨、但最常被要求交付的功能。选线、定管线、算输电通道、评估徒步路线,最后交出去的东西往往就是一张剖面图:横轴距离、纵轴高程,一眼看出一条线是爬升还是下降、哪里最陡、哪里有个垭口。它是那种做对了没人夸、画歪了立刻被指着鼻子问的功能。
但这条线比看起来难搞。球上那条红线有它自己走的路径,剖面图有它自己采的路径,中间还夹着一个类别轴------本猿一开始以为这三件事是一回事,量完之后发现是三回事。
大家好,我是 Cesium 酱(也可以叫我"本猿"),一名在 WebGIS 领域摸爬滚打多年的前端开发者。
写这篇的时候是凌晨,我把 demo 里的默认路径来回画了十几遍,就为了确认一个反直觉的现象:球上那条红线中间明明没拐弯,剖面曲线却在中间鼓了个包。后来发现那不是我画歪了------是红线和剖面采的,根本不是同一条线。
本文目标:把剖面分析这条链路切成四段------密化路径、挑采样点、取高程、画曲线,然后把每一段的误差都量出来:剖面曲线上的每个点到底在哪儿、横轴上那个"距离"是不是真的距离、那条 10° 的斜线上两条线能差出多远。💪
💡 本文涉及的全部代码均已开源,完整的仓库地址见文末,可自行取用、随意魔改。
一、痛点与场景:为什么"切条线看剖面"这么容易翻车?
🎯 背景现状 :剖面分析干的事很朴素------沿着一条线,每隔一段取一次地面高程,再把"距离---高程"画成二维曲线。它是把一个三维问题压成二维,而压的过程里每一步都在丢信息:路径被简化成折点、折点之间被插成采样点、采样点被取成高程、高程被画进一个横轴等距的画布里。四步走下来,图是圆的,数据是尖的,差在哪儿全靠底层实现。
典型场景里,它要同时满足这些需求:
- 🛣️ 选线:一条拟建公路 / 管廊 / 输电线路,要先知道它翻几道梁、最深挖多少
- 🚰 管线纵断面:市政管线要按坡度走,剖面图是唯一的校核依据
- 🥾 徒步 / 越野路线评估:累计爬升、最大坡度、补给点落差
- 📊 填挖方粗算:沿线的地面线与设计线一对比,粗略知道哪段是挖、哪段是填
- 🧪 科研 / 教学:地形起伏的可视化表达,丘陵、河谷、阶地一眼可见
🤦 传统痛点:本猿把常见的几种做法列了一下,每一条都踩过:
| 方案 | 问题 |
|---|---|
| 桌面 GIS 切剖面,再导出图表 | 数据要出浏览器、出进程、出软件,来回三趟;改一次参数重跑一遍 |
直接 sampleTerrain 猛打点 |
拿到的是一串高程,但它不知道"这条线的哪一段最陡"------因为没算累积距离 |
| 采完样随手用 Excel 连线 | 横轴按"第几个点"排,不是按距离排;点密的地方被压扁、点疏的地方被撑开 |
| 拿球上那条红线当基准 | 那条线走的是测地线(大圆) ,而采样走的是经纬度线性插值,长线上根本不在一条沟里 |
用 globe.getHeight 取高程 |
它返回 undefined 的时候你未必看得见------demo 里一句 ?? 0 就把它变成海平面了 |
💡 本文方案:这个案例的实现链路其实清清楚楚,就四段:
1️⃣ 密化路径 :把用户给的折点(或内置的演示路径)按每段固定 500 个插值点铺成一条密集折线,同时给每个点算一个累积距离
2️⃣ 挑采样点 :按"每段步数 / 固定间距 / 总点数"三种方式,从密化折线上挑出真正要取高程的位置
3️⃣ 取高程 :对每个采样点调 viewer.scene.globe.getHeight(),拿回三角网格上的高度
4️⃣ 画曲线:把(累积距离,高程)丢给 ECharts,画一条带面积填充的曲线
✨ 一句话总结:把一条剖面线当成一条带累积距离的密集折线 ,用地形三角网格的射线求交 当高程源,最后把结果按采样序号等距 摆进一张二维图里------每一步都对,可每一步都在丢东西。问题不在于哪一步算错了,在于没人告诉你丢的是什么。
二、核心技术原理:一条剖面,四段拼出来
2.1 路径与采样的数据结构
整个案例前后跑动的其实只有两个结构,一个比一个大:
ts
// 密化折线上的一个点:位置 + 累积距离
type SamplePoint = { lon: number; lat: number; cumDistM: number }
// demo 里路径本身只用 [lon, lat] 两个数表示
const DEFAULT_PATH: [number, number][] = [
[90.5, 30.5], [91, 31], [91.5, 31.5], [90.5, 31.5]
]
// 剖面数组里每一行的含义:经度 / 纬度 / 高程 / 累积距离
type ProfilePoint = [number, number, number, number]
// 源码级剧透:
buildDenseSamples里那个DENSE_PER_SEGMENT = 500是每段 插 500 个点,不是"整条线插 500 个点"。所以默认 3 段路径的密化数组长度是1 + 500 × 3 = 1501;而你手绘一条 20 个折点的路径,它会涨到1 + 500 × 19 = 9501。
这个 500 是写死的,跟段长一点关系都没有:一段 100 米的线和一段 1500 公里的线,都插 500 个点。这笔账第五节还要再算一次。
2.2 完整技术路线
我们设计的完整技术路线:

核心亮点:第 ④ 步"取高程"是本文的杀手锏 🔪------它的返回值只有三种可能:一个数字、undefined、或者抛异常。而 demo 里只接住了第一种。
2.3 三种采样方式的差别
面板上那个下拉框,看着只是"参数习惯不同",实际决定的是横轴上一格代表多少米 。三种方式的算法(源码里就是一个 if / else if / else):
| 采样方式 | 原理 | 默认路径点数 | 一格代表 |
|---|---|---|---|
| 每段步数(默认) | 每段独立按 stepCount 等分,stepCount 20~300 |
3×100+1−2 = 301 | 每段不同(733.080 / 738.842 / 957.667 m) |
| 固定间距 | n = floor(总长 / 间距),从 0 取到 n×间距 |
5000 m 档 → 49 点 | 恒定(5000 m) |
| 总采样点数 | n = 总点数,直接等分全长 |
20~1000 | 恒定(总长 ÷ (n−1)) |
前两种的差别很微妙但很要命:
- 每段步数 :每段都给同样多的点,所以段越长、每格越大。这条路径的段 3 就是活证据。
- 固定间距 :每格一样宽,但它是从起点往后数整格,最后一格走不满就被丢掉------
totalLength报的是n × 间距,不是真实全长。
🎯 一句必须记住的话:**"固定间距"下报告的总长永远 ≤ 真实总长,缺口最大是一个间距。**默认路径实测:100 m 档丢 62.471 m,2000 m 档丢 1262.471 m,5000 m 档丢 1262.471 m。而"总采样点数"和"每段步数"两个模式不截断,最后一点就压在终点上。
三、代码实战:三个关键实现
⚠️ 温馨提示 :下面只贴关键片段,完整的单文件组件在仓库 src/cases/profile-analysis/ProfileAnalysisDemo.vue。这个 demo 依赖 Bing 影像 + Cesium World Terrain,把 Bing 换成自己的底图就能跑。
3.1 密化 + 采样:两个数组,一次去重
ts
const DENSE_PER_SEGMENT = 500
// 🔑 距离用球面大圆公式,半径取的是 IUGG 平均半径,不是 WGS84
function haversineMeters(lon1, lat1, lon2, lat2) {
const R = 6371008.8
const dLat = CesiumMath.toRadians(lat2 - lat1)
const dLon = CesiumMath.toRadians(lon2 - lon1)
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(CesiumMath.toRadians(lat1)) * Math.cos(CesiumMath.toRadians(lat2)) *
Math.sin(dLon / 2) ** 2
return 2 * R * Math.asin(Math.sqrt(a))
}
// 🌰 密化的核心:位置各自线性插值,距离按段长等比分配
for (let i = 1; i <= DENSE_PER_SEGMENT; i++) {
const t = i / DENSE_PER_SEGMENT
dense.push({
lon: CesiumMath.lerp(sx, ex, t),
lat: CesiumMath.lerp(sy, ey, t),
cumDistM: cum + segLen * t
})
}
// 🔑 "每段步数"模式下,第 i 段的终点和第 i+1 段的起点是同一个累积距离
for (let i = 0; i <= stepCount; i++) targets.push(cum + (segLen * i) / stepCount)
targets = targets.filter((v, i) => i === 0 || v !== targets[i - 1])
🔑 关键点:
- 位置和距离用的是两套算法 。位置是
lon / lat各自线性插值,距离是起点到终点的 haversine 距离乘以t。两者在短段上贴合得很好,段一长就分家。 - "每段步数"模式下
targets天然有重复 ,靠上面那句filter去重。本猿把stepCount从 20 到 300 逐个试了一遍,重复对恒等于"段数 − 1",去重是真的生效的 ------因为segLen × stepCount / stepCount在这个量级上恰好浮点相等。 - 但"重复的折点"去不掉 。如果你在地图上手抖,把同一个位置点了两次,那一整段的
targets全部相等,去重后只剩 1 个------少采一整段 。实测 4 个折点里首两重合、stepCount = 100时,采样点从 301 掉到 201。
3.2 取高程:一个 ?? 0 掩盖了三种情况
ts
for (const sample of samples) {
let height = 0
try {
const heightValue = viewer.scene.globe.getHeight(
Cartographic.fromDegrees(sample.lon, sample.lat)
)
height = heightValue ?? 0 // 🔑 未加载 / 出界 / 没渲染 → 一律 0 m
} catch {
height = 0 // 🔑 连异常也吃掉
}
profile.push([sample.lon, sample.lat, height, sample.cumDistM])
}
🔑 关键点:
getHeight的返回值只有三种,demo 只接住了一种 。Cesium 未压缩源码里Globe.prototype.getHeight有三处return undefined:零级瓦片还没建好(L866)、经纬度不在任何零级瓦片里(L874)、以及往下找到的最深那层瓦片没有renderedMesh(L912)。- 它是"逐级往下钻"的 :从零级瓦片沿四叉树一路往下,只把"有
renderedMesh"的瓦片记成tileWithMesh,最后在这块瓦片上取交点。也就是说------剖面精度取决于当前渲染到哪一层。你把相机拉近、瓦片加载得更细,同一条剖面线的高程会变。 - 高程来自三角面,不是高程栅格 。链路是
GlobeSurfaceTile.pick→TerrainMesh.pick→terrainPicker.rayIntersect,本质是沿椭球面法线打一条射线、和三角网格求交,落在三角形内部就是线性插值。所以采样点比网格密的那部分,是白采的。
3.3 画曲线:三个"看起来无害"的选项
ts
chart.setOption({
grid: { left: 40, right: 16, top: 24, bottom: 28 },
xAxis: {
type: 'category', // 🔑 类别轴:等距,与距离数值无关
boundaryGap: false,
name: '距离 (km)',
data: distances, // 🌰 距离字符串,只当刻度标签用
axisLabel: {
showMinLabel: true, showMaxLabel: true,
interval: Math.max(1, Math.floor(distances.length / 6))
}
},
yAxis: { type: 'value', name: '高程 (m)', scale: true }, // 🔑 scale 不从 0 起
series: [{
data: heights, type: 'line', smooth: true, showSymbol: false,
lineStyle: { color: '#2f80ed', width: 2 },
areaStyle: { color: 'rgba(47, 128, 237, 0.35)' }
}]
})
🔑 关键点:
type: 'category'把 301 个采样点摆成 301 个等宽刻度 。distances数组里的 "0.00 / 41.01 / 82.03 ..." 只是标签,不参与定位。这一点是后面所有横轴失真的总源头。interval是"跳过几个",不是"每几个显示一个" 。写interval: 50,实测显示的是第 0、51、102、153、204、255 个刻度;再加上showMaxLabel: true补的终点,一共 7 个标签。scale: true让 y 轴不从 0 起 。默认数据范围 2760~4210 m,实测轴范围被算成 2700~4500 m ------那块蓝色面积的基线是 2700 m,不是海平面。smooth: true不会制造假峰 。本猿本来攒了一堆"平滑把尖峰冲过头"的素材,实测发现 ECharts 把控制点钳制在了相邻两点的包围盒里(源码注释写得很直白:Avoid exceeding extreme after smoothing),实测最大越界只有 0.74 m------那是像素取整的噪声(172 px 对应 2200 m,1 px = 12.79 m)。所以这一条不用避。
四、功能亮点与性能优势

4.1 三种采样方式的点数与代价
本猿把三种方式在默认路径上都跑了一遍(Node 里复刻了同一套算法,@cesium/engine 26.2.0 + ECharts 6.1.0,不是估算):
| 采样方式 | 档位 | 采样点数 | 密化数组 | 报告总长 | 与真实总长 241262.471 m 的缺口 |
|---|---|---|---|---|---|
| 每段步数 | 20 | 61 | 1501 | 241262.471 m | 0 |
| 每段步数 | 100(默认) | 301 | 1501 | 241262.471 m | 0 |
| 每段步数 | 300 | 901 | 1501 | 241262.471 m | 0 |
| 固定间距 | 100 m | 2413 | 1501 | 241200 m | −62.471 m |
| 固定间距 | 500 m | 483 | 1501 | 241000 m | −262.471 m |
| 固定间距 | 2000 m | 121 | 1501 | 240000 m | −1262.471 m |
| 固定间距 | 5000 m | 49 | 1501 | 240000 m | −1262.471 m |
| 总采样点数 | 20 | 20 | 1501 | 241262.471 m | 0 |
| 总采样点数 | 1000 | 1000 | 1501 | 241262.471 m | 0 |
注意最后一列:"固定间距"永远差那么一点,而且差多少由间距决定,不由路径决定 ------缺口精确等于 真实总长 mod 间距。
再看采样点数和绘制对象数的关系------折点数会让采样点数爆炸:
| 路径 | 密化数组 | 每段步数 20 | 每段步数 100 | 每段步数 300 |
|---|---|---|---|---|
| 默认 4 折点 | 1501 | 61 | 301 | 901 |
| 手绘 20 折点 | 9501 | 381 | 1901 | 5701 |
4.2 横坐标:等距的画布,不等距的现实
这是本猿最想让大家看一眼的一张图:

实测三段的真实长度(haversine,R = 6371008.8):73308.018 / 73145.390 / 94809.063 m ,占全长的 30.39 % / 30.32 % / 39.30 %。
而图上它们占的宽度是 33.33 % / 33.00 % / 33.00 %。于是:
| 段 | 真实长度 | 真实占比 | 图上占比 | 每格米数 | 失真系数 |
|---|---|---|---|---|---|
| 段 1 | 73308.018 m | 30.39 % | 33.33 % | 733.080 m | 1.0970× |
| 段 2 | 73145.390 m | 30.32 % | 33.00 % | 738.842 m | 1.0885× |
| 段 3 | 94809.063 m | 39.30 % | 33.00 % | 957.667 m | 0.8398× |
最疏的一格比最密的一格宽 1.3064 倍 。段 3 比段 1 长 21501.045 m ,在图上却一样宽------它的每一段爬升被横向压扁到 76.5 %,看着比实际平缓 23.5 %。
简单说:**类别轴画出来的是"采样序号的曲线",不是"距离的曲线"。**采样均匀(固定间距模式)时两者一致;采样不均匀时(每段步数模式、或折点分布不均时),图上的坡度就是假的。
顺带说一句 y 轴。
scale: true把轴范围定在 2700~4500 m,面积图的基线落在这 2700 m 上------那块看着像"山体截面"的蓝色填充,底座其实是悬空的 。真要表达"填挖方体积"这类量,scale必须关掉(关掉后轴范围实测是 0~5000 m)。
4.3 那条红线到底走的是哪条线
球上那条红线是 polyline.clampToGround = true,没有指定 arcType。查源码,GroundPolylineGeometry 的默认值是这么写的:
js
// @cesium/engine · Source/Core/GroundPolylineGeometry.js
// @param {ArcType} [options.arcType=ArcType.GEODESIC]
// @param {number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE]
this.arcType = options.arcType ?? ArcType.GEODESIC
granularity 的默认值是 1°,也就是 111.195 km 。实测 PolylinePipeline.generateArc 在不同跨度上输出的顶点数:
| 路径跨度 | generateArc 顶点数 |
|---|---|
| 0.5° | 2 |
| 1° | 2 |
| 2° | 3 |
| 5° | 6 |
| 10° | 10 |
默认路径三段全都不够 1°,红线中间一个点都没插 ------它就是两个端点之间的一条直线。而剖面的采样点是在 lon / lat 上线性插值的,画在球上是一条"经纬线"。两者中间差多少?

| 路径 | haversine 段长 | 采样点偏离测地线最大距离 |
|---|---|---|
| 默认段 1(0.5° 斜线) | 73.308 km | 67.567 m |
| 默认段 3(1.0° 纬线) | 94.809 km | 108.297 m |
| 手绘 5° 纬线 | 476.524 km | 2684.266 m |
| 手绘 10° 纬线 | 952.806 km | 10754.602 m |
| 手绘 10° 斜线 | 1435.335 km | 30229.783 m |
默认那两段上,一百米上下的偏差在球上确实看不出来。但只要你画的线超过 1°------一条从北京到天津的剖面线就差不多 1.2° ------偏差立刻进入"米"级;到了省级跨度(5°~10°),剖面采的点和球上红线的最远处能差 30 公里。那时候你指着球上那条红线说"我要看这条线的剖面",实际上切的是另一条山脊。
核心优势
- 🚀 纯前端闭环:影像、地形、采样、绘图全在浏览器里,没有任何服务端依赖
- 📏 三种采样口径:等步数 / 等间距 / 等点数,分别对应"每段都要看细"、"我要固定物理间距"、"我只想要 N 个点"
- 🖐️ 手绘即算 :
ScreenSpaceEventHandler收LEFT_CLICK攒折点,RIGHT_CLICK或LEFT_DOUBLE_CLICK收尾,边画边看 - 📈 图随线动:剖面图和球上红线共用同一份路径数据,改折点两边一起变
五、小贴士:避坑指南

🎯 关于长度:haversine 用的是平均半径
haversineMeters 里 R = 6371008.8(IUGG 平均半径),而 Cesium 的 Ellipsoid.WGS84 是椭球。两者在同一段上的差不是常数符号,是会互相抵消的------默认路径三段实测:
- 段 1:haversine 比测地线 少 62.719 m(−0.0856 %)
- 段 2:少 59.448 m(−0.0813 %)
- 段 3:多 192.929 m(+0.2035 %)------因为它是纯纬线,纬圈半径在 lat 31.5° 上约为 95 km/°,平均半径算不出来这一段
- 合计:haversine 241262.471 m ,测地线 241333.234 m ,短 70.763 m
⚠️ 重要提示 :三项误差恰好抵消掉一大半,纯属这条路径运气好。别把"这次对了"当成"这个公式对" 。要跟 Cesium 内部完全一致,直接用
EllipsoidGeodesic.surfaceDistance:
ts
const g = new EllipsoidGeodesic(
Cartographic.fromDegrees(a[0], a[1]),
Cartographic.fromDegrees(b[0], b[1])
)
const meters = g.surfaceDistance // 🔑 Cesium 自己就是这么算的
📐 关于横轴:interval 是"跳过几个"
axisLabel.interval 的语义容易反着记:写 n 表示每 n+1 个刻度显示一个 。demo 写的是 Math.max(1, Math.floor(点数 / 6)),实测在 20 ~ 1000 个点上,显示出来的标签永远是 6~7 个 (showMinLabel 和 showMaxLabel 还会各补一个)。
⚠️ 重要提示 :想让横轴真的是距离轴,把
xAxis.type换成'value',data换成[[距离, 高程], ...]的点对,再把boundaryGap/interval一起删掉。收益是坡度立刻变真;代价是点密的地方会被挤在一起------而这恰恰是真实情况。
⛰️ 关于高程:先确认它真的取到了
剖面图最怕的不是"不准",是"静默地不准"。getHeight 返回 undefined 时,?? 0 会给你一条 0 米高的假平台,而 try / catch 连异常都吞了。
⚠️ 重要提示 :把
undefined和"真的 0 米"分开报出来。最小改动就是if (h === undefined) missed++,然后把?? 0留在后面,最后在面板上加一行"缺失点 N 个"。顺带提醒:分析前先把相机飞到路径上方、等它加载完 。getHeight只认已经渲染出网格的瓦片,相机没看过的地方就是undefined。
⚡ 关于采样:密不等于准
Cesium World Terrain 的 CesiumTerrainProvider 默认用 GeographicTilingScheme(源码 L186),level L 的瓦片经纬度跨度都是 180 / 2^L 度:
| 层级 | 角度跨度 | 纬向边长 | 经向边长(lat 31°) |
|---|---|---|---|
| 10 | 0.175781° | 19489.0 m | 16787.9 m |
| 11 | 0.087891° | 9744.5 m | 8393.9 m |
| 12 | 0.043945° | 4872.2 m | 4197.0 m |
而采样点间距实测:每段步数 300 → 268.07 m;固定间距 100 m 档 → 100.03 m。这些点里绝大多数两两落在同一个三角形内部,取到的是同一张平面上的线性插值。
⚠️ 重要提示 :与其把采样点加密,不如先把相机拉近、让瓦片加载到更细的一层------那才真的增加了信息。采样密度真正解决的是另一个问题:**别把山脊采漏。**本猿用一条构造的地形(山脊半宽约 2412 m)做了 40 次相位扫描,同一个山脊,不同采样间距能踩到的最坏情况是:
| 采样点数 | 采样间距 | 最坏漏掉的山脊高度 |
|---|---|---|
| 20 | 12698.0 m | 725.08 m |
| 100 | 2437.0 m | 564.04 m |
| 301 | 804.2 m | 94.88 m |
| 901 | 268.1 m | 15.78 m |
同一个山头,采样点少的时候能不能看见它,全看它是不是恰好落在某个采样点上。
📦 关于几个上限
Math.max(...heights)的实参上限 :实测在本机 V8 上是 124000 ~ 125000 ,超了直接RangeError: Maximum call stack size exceeded。这个 demo 的最坏情况是"每段步数 300 + 415 个折点",日常够用;但如果哪天把上限放开,记得改成reduce。- "固定间距"没有点数上限 :间距滑到最小 100 m,一条 241 km 的路径就是 2413 个点、2413 次同步射线求交。
Cartographic.fromDegrees本身的构造成本只有 0.3729 ms (2413 次,已预热),但getHeight才是大头------而且它是同步的,全压在主线程上。 - 密化数组只跟折点数有关 :
1 + 500 × (N−1)。手绘 20 个折点就是 9501 个对象,跟路径多长无关。
目前的局限与后续可以做的:
- 采样循环是同步
for,采样点上千时会卡一下 UI------按 200 个一批切开,或挪进 Worker DENSE_PER_SEGMENT = 500写死------应该按段长自适应(ceil(段长 / 目标间距))- 累积距离和位置用了两套算法------统一成
EllipsoidGeodesic就干净了 - 横轴是类别轴------换
type: 'value'才是真距离轴 - 缺采样点没有上报------
undefined和 0 米现在长得一模一样 - 只能画一条剖面------没法两条线对比填挖方
六、写在最后
剖面分析这件事,做久了会有一种错觉:/ 以为自己切开的是地形,/ 其实切开的是这个引擎的一层层抽象------/ 你手里那张曲线,只是抽象漏下来的一点影子。
从 #53 把点变成清单,到 #54 把地挖出一个洞,再到今天顺着一条线把地形切开------空间分析这条线终于从"看"走到了"量"。🎉
现在,Cesium 不仅能告诉你"那里有多高",还能告诉你"这一路是怎么爬上来的"了。虽然它爬的可能是另一条山脊。
这个案例还不完美:横轴是类别轴、密化常数写死、缺采样点静默变 0、长线上红线和采样线各走各的。但它把"一条剖面是怎么拼出来的"这件事讲清楚了,这就够了。
这条线还能继续往下切。下一次我们不停在地表上了------站在一个点上往四周看:哪些地方看得见,哪些地方被挡着。通视分析,我们下一篇见。
如果你在自己的项目里也画过这种"看着对、量着歪"的剖面图,欢迎留言说说你最后是怎么校准的------是靠改用数值轴,还是干脆在服务端算完再丢给前端画。
🌟 项目已开源,欢迎 Star!(你的 Star ⭐ 就是对我最大的支持!)
仓库地址:https://github.com/jianlei-wang/CESIUM-TREASURE-BOX
代码在 src/cases/profile-analysis/ 目录下,是一个独立的 Vue 单文件组件,复制进你的项目、把 Bing 影像换成自己的底图就能跑。
Cesium酱の百宝箱 · 第 55 篇
红线绕球走大圆,剖面按格子走直线
2026 年的秋夜,与你共探"一条线的高程"