Globe 的构成
Cesium 的地球本质是:
- 一个 WGS84 椭球
- 上面可以贴各种影像
- 可以叠加真实地形高程
影像和地形不是一类东西:
ini
影像 Imagery = 地球表面的贴图,决定你看到的颜色、道路、卫星图
地形 Terrain = 地球表面的高度,决定山体、谷地、地表起伏
更准确地说:
Cesium 的 Globe = WGS84 椭球 + 影像图层 + 可选地形数据
常见地理数据服务
| 类型 | 作用 | 典型 | 在 Cesium 中的位置 |
|---|---|---|---|
| 影像瓦片 | 地球表面的贴图 | 天地图影像、高德影像 | viewer.imageryLayers |
| 地形瓦片 | 地表高程起伏 | Cesium World Terrain | viewer.terrainProvider |
| 3D Tiles | 大规模三维数据 | 倾斜摄影、城市模型、点云 | scene.primitives |
| 矢量瓦片 | 道路、文字、面数据 | MVT、高德矢量 | 通常需要额外处理 |
影像图层(Imagery)
ImageryLayer ------ 图层控制
ImageryLayer 用来在 Cesium 的地球表面(椭球体)上叠加各种影像数据,例如:
- 卫星影像(高德、Bing、ArcGIS)
- 地图瓦片(XYZ、WMTS)
- 自定义图片服务
- 黑白底图 / 地形阴影图
主要功能
1. 添加多个图层(叠加效果)
csharp
viewer.imageryLayers.add(layer1)
viewer.imageryLayers.add(layer2)
常用于底图 + 道路标注层 + 热力图层 + 自定义覆盖层等多层叠加。
2. 控制透明度
ini
layer.alpha = 0.5
3. 控制显示顺序
scss
viewer.imageryLayers.raise(layer) // 上移一层
viewer.imageryLayers.lower(layer) // 下移一层
viewer.imageryLayers.raiseToTop(layer) // 移到最顶层
viewer.imageryLayers.lowerToBottom(layer) // 移到最底层
4. 控制显示/隐藏
ini
layer.show = false
5. 图像处理效果
| 属性 | 作用 |
|---|---|
brightness |
调整亮度,可模拟夜间模式 |
contrast |
增强对比度,让模糊影像更清晰 |
hue |
改变色调,实现特殊视觉效果 |
saturation |
调整饱和度 |
gamma |
伽马校正,优化显示效果 |
ini
layer.brightness = 0.6 // 暗化,常用于夜间风格底图
layer.saturation = 0 // 去色,灰度底图
6. 裁剪区域显示
只在某个矩形范围内显示影像,减少无效请求:
arduino
layer.rectangle = Cesium.Rectangle.fromDegrees(
116.0, 39.5, // 西南角(经度、纬度)
117.0, 40.5 // 东北角
)
7. 淡入动画示例
php
const layer = viewer.imageryLayers.addImageryProvider(provider)
layer.alpha = 0
Cesium.Tween.start({
duration: 2.0,
easingFunction: Cesium.EasingFunction.LINEAR,
startObject: { alpha: 0 },
stopObject: { alpha: 1 },
update(value) {
layer.alpha = value.alpha
}
})
ImageryProvider ------ 数据源
ImageryProvider 是影像数据提供者(数据源) ,告诉 Cesium 某一块地图瓦片从哪里获取。
markdown
ImageryProvider(数据源)
↓
ImageryLayer(显示控制)
↓
Globe(地球)
通用 / 标准瓦片类(最常用)
| Provider | 说明 |
|---|---|
UrlTemplateImageryProvider |
通过模板 URL 加载影像服务,最通用 |
TileMapServiceImageryProvider |
TMS 瓦片服务(Y 轴与 XYZ 相反) |
WebMapTileServiceImageryProvider |
WMTS 标准服务 |
WebMapServiceImageryProvider |
WMS 标准服务 |
主流地图服务 Provider
| Provider | 说明 |
|---|---|
OpenStreetMapImageryProvider |
OSM 开放街图 |
BingMapsImageryProvider |
Bing Maps |
MapboxImageryProvider |
Mapbox 影像 |
MapboxStyleImageryProvider |
Mapbox 可自定义样式的影像 |
ArcGisMapServerImageryProvider |
ArcGIS Map Server |
Cesium 官方 / 云服务
| Provider | 说明 |
|---|---|
IonImageryProvider |
Cesium Ion 云服务影像 |
createWorldImageryAsync |
创建 Cesium Ion 默认全球影像底图 |
csharp
// 使用官方全球影像底图
const imagery = await Cesium.createWorldImageryAsync()
viewer.imageryLayers.addImageryProvider(imagery)
调试 / 测试用 Provider
| Provider | 说明 |
|---|---|
GridImageryProvider |
网格影像,调试坐标系用 |
TileCoordinatesImageryProvider |
显示每块瓦片的 X/Y/Level,调试用 |
SingleTileImageryProvider |
单张图片作为全球贴图 |
UrlTemplateImageryProvider 详解
UrlTemplateImageryProvider 是实际项目中最常用、几乎所有项目都会用到的 Provider。
核心逻辑:根据 {z}/{x}/{y} 拼接 URL → 请求图片 → 显示到地图。
基础用法
php
const provider = new Cesium.UrlTemplateImageryProvider({
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
})
viewer.imageryLayers.addImageryProvider(provider)
TMS 兼容(reverseY)
TMS 服务的 Y 轴方向与 XYZ 相反,使用 {reverseY} 自动换算:
vbnet
url: '/tiles/{z}/{x}/{reverseY}.png'
子域名(提高并发)
php
new Cesium.UrlTemplateImageryProvider({
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c']
})
浏览器对同一域名有并发请求数限制(通常 6 个左右),使用多个子域名可以变相提高并发:3 个域名 × 6 请求 = 更快加载。
重要配置参数
| 参数 | 作用 |
|---|---|
minimumLevel / maximumLevel |
控制可缩放的层级范围 |
rectangle |
限制加载区域,减少无效请求 |
tilingScheme |
决定坐标体系 |
credit |
版权说明文字 |
tileWidth / tileHeight |
瓦片尺寸,默认 256 |
tilingScheme 配置
arduino
tilingScheme: new Cesium.WebMercatorTilingScheme() // 默认,EPSG:3857
tilingScheme: new Cesium.GeographicTilingScheme() // 经纬度,EPSG:4326(天地图 4326 服务)
ImageryLayer 集合操作
viewer.imageryLayers 是 ImageryLayerCollection,提供完整的集合管理能力。
查询
csharp
// 图层总数
const count = viewer.imageryLayers.length
// 按索引获取图层
const layer = viewer.imageryLayers.get(0)
// 判断是否包含某图层
const has = viewer.imageryLayers.contains(layer)
添加
csharp
// 方式一:先创建 provider,再包装成 layer 添加
const provider = new Cesium.UrlTemplateImageryProvider({ url: '...' })
const layer = viewer.imageryLayers.addImageryProvider(provider)
// 方式二:创建 ImageryLayer 对象后添加
const layer = new Cesium.ImageryLayer(provider, { alpha: 0.8 })
viewer.imageryLayers.add(layer)
// 第二个参数可指定插入索引
viewer.imageryLayers.add(layer, 0) // 插入到底层
移除
csharp
// 移除指定图层
viewer.imageryLayers.remove(layer)
// 移除所有图层(含默认底图)
viewer.imageryLayers.removeAll()
// 移除默认底图
viewer.imageryLayers.remove(viewer.imageryLayers.get(0))
排序
scss
viewer.imageryLayers.raise(layer) // 上移一层
viewer.imageryLayers.lower(layer) // 下移一层
viewer.imageryLayers.raiseToTop(layer) // 移到最顶
viewer.imageryLayers.lowerToBottom(layer) // 移到最底
天地图接入示例
天地图是国内项目中最常用的底图之一,提供影像图和矢量图,需要申请 Token。
影像底图 + 注记叠加(EPSG:3857)
php
const token = '你的天地图token'
// 影像底图
const imgLayer = new Cesium.WebMapTileServiceImageryProvider({
url: `https://t{s}.tianditu.gov.cn/img_w/wmts?token=${token}`,
layer: 'img',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'w',
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
// 中文注记层(叠加在影像上)
const ciaLayer = new Cesium.WebMapTileServiceImageryProvider({
url: `https://t{s}.tianditu.gov.cn/cia_w/wmts?token=${token}`,
layer: 'cia',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'w',
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
viewer.imageryLayers.addImageryProvider(imgLayer)
viewer.imageryLayers.addImageryProvider(ciaLayer)
矢量底图(EPSG:4326)
php
const vecLayer = new Cesium.WebMapTileServiceImageryProvider({
url: `https://t{s}.tianditu.gov.cn/vec_c/wmts?token=${token}`,
layer: 'vec',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'c', // 注意是 'c' 不是 'w'
tilingScheme: new Cesium.GeographicTilingScheme(), // 4326 需要指定
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
tileMatrixSetID: 'w'对应 Web Mercator(3857);tileMatrixSetID: 'c'对应经纬度(4326),需要配合GeographicTilingScheme。
影像加载常见坑
1. XYZ 和 TMS 的 Y 轴方向不同
常见 XYZ 地址使用 {z}/{x}/{y},TMS 服务 Y 轴方向通常相反,在 Cesium 里使用 {reverseY} 自动处理。
2. EPSG:3857 和 EPSG:4326 不要混用
大多数互联网地图使用 Web Mercator(3857),天地图、部分 WMTS 服务提供经纬度切片(4326)时要配置:
vbnet
tilingScheme: new Cesium.GeographicTilingScheme()
3. 国内地图坐标系偏移
| 地图服务 | 坐标系 | 与 WGS84 偏差 |
|---|---|---|
| 高德、腾讯 | GCJ02 | 几十米级 |
| 百度 | BD09 | 更大 |
| 天地图 | WGS84 | 无偏移 |
把 WGS84 的点叠加到高德/百度底图上,会出现几十到几百米偏移。国内项目优先选天地图作为底图,可以避免坐标转换。
4. 地图服务访问限制
常见限制:跨域、Token / Key、Referer 白名单、并发限制。瓦片请求失败时优先查看浏览器 Network 面板,看返回的是 401/403 还是 CORS 错误。
5. rectangle 不只是裁剪显示
设置 layer.rectangle 后,Cesium 只在指定范围内请求瓦片,减少无效请求。适合局部影像、项目区域底图、无人机正射影像。
地形(Terrain)
TerrainProvider 类型总览
TerrainProvider 用来给地球提供地表高程数据,让原本光滑的 WGS84 椭球拥有真实地形起伏。
地形数据主要分两类:
| 类型 | 说明 | 现状 |
|---|---|---|
| HeightMap | 二维高度图(类似灰度图) | 老方案,逐渐被淘汰 |
| Quantized Mesh | 三维网格,支持 LOD、高压缩、GPU 友好 | 现在 Cesium 主流格式 |
官方 TerrainProvider 全览:
| TerrainProvider | 作用 | 适用场景 |
|---|---|---|
CesiumTerrainProvider |
最常用,Cesium 官方格式(quantized-mesh) | 数字孪生、无人机、山体真实地形 |
EllipsoidTerrainProvider |
默认光滑椭球(无地形) | 纯地图展示、不需要山体 |
ArcGISTiledElevationTerrainProvider |
ArcGIS 高程服务 | ArcGIS / ESRI 企业 GIS |
VRTheWorldTerrainProvider |
VR-TheWorld 地形服务 | 比较老,较少使用 |
GoogleEarthEnterpriseTerrainProvider |
Google Earth Enterprise | Google 企业 GIS |
Cesium3DTilesTerrainProvider |
使用 3D Tiles 作为地形(Mesh Terrain) | 高精度城市、摄影测量 |
terrainProvider是唯一的,不能像影像图层那样叠加多个,只能替换。
使用官方全球地形
ini
Cesium.Ion.defaultAccessToken = '你的Ion Token'
viewer.terrainProvider = await Cesium.createWorldTerrainAsync()
或者在 Viewer 初始化时直接传入:
csharp
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: await Cesium.createWorldTerrainAsync()
})
地形参数:法线与水面
createWorldTerrainAsync 支持两个常用参数:
csharp
viewer.terrainProvider = await Cesium.createWorldTerrainAsync({
requestVertexNormals: true, // 请求顶点法线,光照效果更真实
requestWaterMask: true // 请求水面遮罩,海洋/湖泊有水面动效
})
| 参数 | 说明 | 开启代价 |
|---|---|---|
requestVertexNormals |
法线数据,让地形光照更立体真实 | 数据量略增,视觉效果明显提升 |
requestWaterMask |
水面遮罩,海洋/水体显示为动态水面 | 需要地形数据支持水面信息 |
加载自定义地形服务
rust
viewer.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
'https://your-server/terrain'
)
也可以动态替换地形:
csharp
// 切换到真实地形
viewer.terrainProvider = await Cesium.createWorldTerrainAsync()
// 切回光滑椭球
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider()
自制地形切片流程
在地理空间数据云下载高程数据:
数据资源 → 公开数据 → DEM 数字高程数据 → GDEMV3 30M 分辨率数字高程数据
下载后处理流程:
markdown
下载 .tif 高程文件
↓
Docker 运行 ctb-quantized-mesh 镜像切片
↓
生成 {z}/{x}/{y}.terrain 切片 + layer.json
↓
Nginx 代理静态文件
Nginx 关键配置 (.terrain 文件已经 gzip 压缩,不能让 Nginx 重复压缩):
ini
location /terrain/ {
alias "C:/Users/you/Downloads/terrain_tiles/";
# 关闭 Nginx 动态压缩,terrain 文件已经预压缩
gzip off;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 告诉浏览器 .terrain 文件是 gzip 内容
if ($request_uri ~* ".(terrain|terrain?.*)$") {
add_header Content-Encoding gzip;
add_header Access-Control-Allow-Origin *;
}
# 兼容无后缀名的瓦片文件
if ($request_uri ~* "/terrain/\d+/\d+/\d+") {
add_header Content-Encoding gzip;
add_header Access-Control-Allow-Origin *;
}
types {
application/json json;
application/octet-stream terrain;
}
autoindex on;
}
其他可用地形服务:火星科技地形、MapTiler 地形。
查询地形高度
如果想知道某个经纬度的真实地形高度,不能直接用坐标 height,要用采样 API。
sampleTerrainMostDetailed(最高精度采样)
arduino
const positions = [
Cesium.Cartographic.fromDegrees(116.39, 39.9)
]
const result = await Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
positions
)
console.log(result[0].height) // 米,椭球高
sampleTerrain(指定 LOD 层级采样)
arduino
const positions = [
Cesium.Cartographic.fromDegrees(116.39, 39.9)
]
// 第二个参数是层级,数字越大精度越高
const result = await Cesium.sampleTerrain(
viewer.terrainProvider,
11,
positions
)
console.log(result[0].height)
| API | 说明 | 适用 |
|---|---|---|
sampleTerrainMostDetailed |
自动选最高可用精度,结果最准 | 单点查询、精确定位 |
sampleTerrain |
指定层级,速度更快 | 批量粗略查询、性能敏感场景 |
常见用途:贴地飞行高度计算、模型落地对齐、地面标注高度修正。
地形常见坑
1. 没有地形时,地球只是光滑椭球
不设置地形时默认使用 EllipsoidTerrainProvider,看起来有地球,但没有山体起伏。
2. depthTestAgainstTerrain 影响对象显示
这是地形开启后最容易踩的坑。默认情况下 depthTestAgainstTerrain = false,对象不会被地形遮挡,飘浮在地面上方。
ini
// 开启后,地形会遮挡其后方的对象(更真实)
viewer.scene.globe.depthTestAgainstTerrain = true
开启后的影响:
- 被地形挡住的 Entity / Primitive 会正确消失
- label / billboard / point 可能被地形遮挡,需要配合
disableDepthTestDistance - 地下数据可能被地形整体挡住
根据业务决定是否开启,通常"室外真实场景"开启,"纯地图展示"关闭。
3. 贴地效果依赖地形数据
CLAMP_TO_GROUND、RELATIVE_TO_GROUND 这类高度模式需要地形支持。没有地形时,贴的是光滑椭球,不是真实地面。
4. 开启地形后高度表现可能变化
模型、点位、线面、相机高度都可能因为地形起伏看起来"变高"或"被遮挡"。做无人机航线、测距、贴地线时尤其注意。
5. 地形精度决定业务上限
30m DEM 适合中小比例尺地形展示,不适合精细低空避障、贴地飞行、工程测量。如果业务要求高,需要更高精度 DEM、倾斜摄影或局部 Mesh 地形。
Globe 全局属性
viewer.scene.globe 提供了一些影响整体地球显示效果的属性,常见配置如下:
显示控制
ini
// 隐藏地球(常用于纯室内、纯模型场景)
viewer.scene.globe.show = false
// 地球底色(无影像时的填充色)
viewer.scene.globe.baseColor = Cesium.Color.BLACK
光照
ini
// 开启地球光照效果(白天/黑夜随时间变化)
viewer.scene.globe.enableLighting = true
大气与雾
ini
// 关闭大气层(夜间模式、暗色风格时常关闭)
viewer.scene.skyAtmosphere.show = false
// 关闭地面雾气
viewer.scene.globe.showGroundAtmosphere = false
地形深度测试
ini
// 是否用地形遮挡其后方对象
viewer.scene.globe.depthTestAgainstTerrain = true
瓦片加载并发
arduino
// 同时最多请求多少个地形瓦片,默认 16
viewer.scene.globe.maximumScreenSpaceError = 2 // 越小精度越高,但消耗越大
常见问题 FAQ
1. 影像底图加载后颜色整体偏暗
检查 layer.brightness 是否被修改,默认应为 1.0。
2. 天地图瓦片不显示
检查 Token 是否正确,检查 tileMatrixSetID 是否和 tilingScheme 对应(w 对应 3857,c 对应 4326)。
3. 同一位置影像和标注层对不上
通常是两个图层用了不同的 tilingScheme,一个 3857 一个 4326,统一即可。
4. 开启地形后模型悬浮在空中
模型高度是椭球高,地形起伏导致地面高于模型底部。需要用 sampleTerrainMostDetailed 查询实际地面高度,再重新设置模型位置。
5. 开启地形后 label / billboard 被遮挡
是 depthTestAgainstTerrain = true 生效导致的。可以对这类标注单独设置:
ini
entity.label.disableDepthTestDistance = Number.POSITIVE_INFINITY
6. 不同图层切换时出现闪烁
通常是移除旧图层和添加新图层不在同一帧完成。可以先 add 新图层,等加载完成后再 remove 旧图层,或者用 layer.show 切换可见性。
7. 地形采样返回 height 为 0 或负数
sampleTerrainMostDetailed 返回的是椭球高(HAE) ,不是海拔高。海平面附近的椭球高接近 0,低洼地区可能为负数,属于正常现象。
小结
| 需求 | 推荐方案 |
|---|---|
| 添加自定义底图(XYZ/WMTS) | UrlTemplateImageryProvider / WebMapTileServiceImageryProvider |
| 国内项目底图 | 天地图(WGS84,无坐标偏移) |
| 多图层叠加管理 | viewer.imageryLayers 集合操作 |
| 真实地形 | createWorldTerrainAsync() 或自建 CesiumTerrainProvider |
| 地形遮挡效果 | globe.depthTestAgainstTerrain = true |
| 地形法线与水面 | requestVertexNormals / requestWaterMask |
| 查询某点地形高度 | sampleTerrainMostDetailed |
| 无地形纯地图 | EllipsoidTerrainProvider(默认) |
| 隐藏地球 | viewer.scene.globe.show = false |
三句话总结:
ini
影像 = 贴在地球上的图片(可以叠多层)
地形 = 地球表面的起伏高度(只有一个)
Globe = 椭球 + 影像 + 地形,三者组合才是完整地球