关于 cesiumjs-imagery
cesiumjs-imagery 是面向 CesiumJS 开发者的技能,介绍如何在地球或 3D Tileset 上显示栅格影像,涵盖影像提供器、图层堆叠与样式,以及分屏比较。
主要功能
- 01常见瓦片服务的影像提供器
- 02添加、排序和替换影像图层
- 03调整图层透明度、颜色和可见性
- 04分屏比较影像
- 05WMS 和 WMTS 要素查询
适用场景
- 01为地球场景设置底图
- 02叠加并调整栅格影像样式
- 03并排比较两个影像图层
- 04通过 WMS 或 WMTS 查询地图要素
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cesiumgs/ cesiumjs-skills --skill cesiumjs-imagery安装后在 Claude Code 中输入/cesiumjs-imagery确认已加载。
详细介绍
CesiumJS 影像图层
CesiumJS v1.144 -- 影像提供程序提供栅格瓦片数据,这些数据会渲染在地球上 或覆盖在 Cesium3DTileset 上。三个核心抽象分别是 ImageryProvider (获取瓦片)、ImageryLayer(显示设置)和 ImageryLayerCollection(地球上的有序图层堆栈)。
ImageryProvider (abstract -- fetches tile images)
-> ImageryLayer (wraps one provider; alpha, brightness, split, etc.)
-> ImageryLayerCollection (ordered stack; index 0 = base layer)
-> Globe / Cesium3DTileset
图层按从下到上的顺序渲染。索引 0 是基础图层,即使其矩形未覆盖整个世界,也会拉伸以填满地球。
快速入门和 ImageryLayer 工厂方法
创建用于影像工作的 Viewer 时,请禁用不需要的小部件,让影像成为视觉焦点。需要立即将相机定位到指定位置时,请使用 camera.setView(而非 flyTo)——flyTo 会播放动画,代码继续执行前动画可能尚未完成。
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, Math as CesiumMath } from "cesium";
// Clean viewer -- disable widgets that distract from imagery
const viewer = new Viewer("cesiumContainer", {
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
maximumLevel: 18,
})),
baseLayerPicker: false,
animation: false,
timeline: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
});
// Position camera immediately (no animation)
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(-73.0, 41.0, 1500000),
orientation: {
heading: 0.0,
pitch: CesiumMath.toRadians(-90), // look straight down
roll: 0.0,
},
});
// Public URL-backed overlay
const nightLayer = new ImageryLayer(new UrlTemplateImageryProvider({
url: "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_CityLights_2012/default/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpeg",
maximumLevel: 8,
credit: "NASA GIBS",
}));
nightLayer.alpha = 0.5;
nightLayer.brightness = 2.0;
viewer.imageryLayers.add(nightLayer);
仅当运行时具备所需的 Cesium ion 使用权限时,才使用 IonImageryProvider 和 ImageryLayer.fromWorldImagery。对于公开/无需令牌的示例,优先使用 OpenStreetMap、ArcGIS、NASA GIBS、WMS、WMTS 或 URL 模板提供程序。
影像场景的相机高度参考
使用 camera.setView,并参照以下近似高度:
| 比例尺 | 高度(m) | 示例 |
|---|---|---|
| 街道/街区 | 500–2,000 | 市中心路口 |
| 城市 | 5,000–25,000 | 华盛顿特区、巴黎 |
| 都市圈 | 50,000–200,000 | 大伦敦地区 |
| 区域/州 | 300,000–1,500,000 | 佛罗里达、日本 |
| 大洲 | 3,000,000–8,000,000 | 欧洲、北美 |
对于俯视(地图式)视图,请设置 pitch: CesiumMath.toRadians(-90)。
对于倾斜的 3D 视图,请将 pitch 设置为 CesiumMath.toRadians(-35) 至 CesiumMath.toRadians(-60)。
当场景指定了某个国家、城市或区域时,默认使用俯视构图。这样可以让指定地点居中,避免透视变形。倾斜的高空视图很容易显示相邻的陆地,因为目标可能落在视锥之外。
指定地点的构图参考
根据地点的实际经纬度构图,不要使用附近的估算位置。使用 camera.setView 和 Cartesian3.fromDegrees(lon, lat, height),并采用俯视角度,除非提示词明确要求倾斜视图。
| 地点 | lon, lat | 建议高度 |
|---|---|---|
| 伦敦 | -0.12, 51.50 | 60,000 |
| 巴黎 | 2.35, 48.86 | 30,000 |
| 纽约市 | -74.00, 40.71 | 60,000 |
| 纽约—波士顿走廊 | -72.5, 41.5 | 800,000 |
| 华盛顿特区国家广场 | -77.03, 38.89 | 25,000 |
| 佛罗里达半岛 | -81.5, 28.0 | 1,500,000 |
| 大峡谷 | -112.5, 36.3 | 200,000 |
| 夏威夷群岛 | -157.0, 20.5 | 1,800,000 |
| 冰岛 | -19.0, 64.9 | 1,200,000 |
| 意大利半岛 | 12.5, 42.0 | 2,500,000 |
| 南欧分屏视图 | 13.0, 42.0 | 4,500,000 |
| 格陵兰 | -42.0, 72.0 | 5,000,000 |
| 日本本州 | 138.0, 36.5 | 2,500,000 |
ImageryLayerCollection API
通过 viewer.imageryLayers 访问(与 viewer.scene.imageryLayers 相同)。
const layers = viewer.imageryLayers;
layers.add(myLayer); // add on top
layers.add(myLayer, 0); // add at index
layers.addImageryProvider(provider); // create layer + add
layers.raise(myLayer); // move up one
layers.lower(myLayer); // move down one
layers.raiseToTop(myLayer); // move to top
layers.lowerToBottom(myLayer); // move to bottom
layers.remove(myLayer); // remove and destroy
layers.remove(myLayer, false); // remove without destroying
layers.removeAll();
const count = layers.length;
const base = layers.get(0);
const idx = layers.indexOf(myLayer);
const has = layers.contains(myLayer);
事件:layerAdded(layer, index)、layerRemoved(layer, index)、
layerMoved(layer, newIndex, oldIndex)、layerShownOrHidden(layer, index, show)。
ImageryLayer 显示属性
属性值可以是数字,也可以是逐瓦片回调 (frameState, layer, x, y, level) => value。
| 属性 | 默认值 | 说明 |
|---|---|---|
alpha | 1.0 | 0 = 透明,1 = 不透明 |
brightness | 1.0 | < 1 表示更暗,> 1 表示更亮 |
contrast | 1.0 | < 1 表示更低,> 1 表示更高 |
hue | 0.0 | 弧度偏移 |
saturation | 1.0 | < 1 表示降低饱和度,> 1 表示过饱和 |
gamma | 1.0 | 伽马校正 |
show | true | 显示切换 |
splitDirection | SplitDirection.NONE | LEFT、RIGHT 或 NONE |
nightAlpha / dayAlpha | 1.0 | 需要启用 Globe.enableLighting |
其他选项:rectangle、minimumTerrainLevel / maximumTerrainLevel、
cutoutRectangle、colorToAlpha / colorToAlphaThreshold、
minificationFilter / magnificationFilter(默认值为 LINEAR,也可以设为 NEAREST)。
// Adjust appearance at runtime
layer.alpha = 0.7;
layer.brightness = 1.3;
layer.contrast = 1.5;
layer.saturation = 0.5;
layer.gamma = 1.2;
替换基础图层
移除默认基础图层,并在索引 0 处替换它。替换后的图层将成为新的基础图层,并 拉伸以填满地球仪。
import { ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
// Remove default Bing aerial
viewer.imageryLayers.remove(viewer.imageryLayers.get(0));
// Add OSM as new base layer at index 0
const osmLayer = new ImageryLayer(
new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
maximumLevel: 19,
credit: "OpenStreetMap contributors",
}),
);
viewer.imageryLayers.add(osmLayer, 0);
选择可见的浅色底图
当场景要求使用浅色或日常底图时,不要将夜间灯光、深色画布或卫星夜景瓦片用作基础图层。这些瓦片渲染后大多是黑色像素,明显不符合要求。可靠的公开浅色底图:
- OpenStreetMap(
https://tile.openstreetmap.org/) - Carto Positron(
https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png) - ArcGIS World Street Map / World Topographic
- USGS Shaded Relief(WMTS)
将 VIIRS_CityLights_2012、BlackMarble 和 CANVAS_DARK 留作叠加在浅色底图上的图层,或用于明确要求夜景视图的场景。
影像提供程序
IonImageryProvider
需要访问 Cesium ion 资产。除非调用方明确要求使用 ion 影像资产,否则不要在公开或无令牌示例中使用。
// Always use fromAssetId (async factory); never call constructor directly
const layer = ImageryLayer.fromProviderAsync(
IonImageryProvider.fromAssetId(3812),
);
viewer.imageryLayers.add(layer);
OpenStreetMapImageryProvider
扩展了面向 Slippy 瓦片服务的 UrlTemplateImageryProvider。
const osm = new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
maximumLevel: 19,
credit: "OpenStreetMap contributors",
// retinaTiles: true, // request @2x tiles
});
viewer.imageryLayers.addImageryProvider(osm);
UrlTemplateImageryProvider
最灵活的提供程序。占位符:{x}、{y}、{z}、{s}、
{reverseX/Y/Z}、{west/south/east/northDegrees}、
{west/south/east/northProjected}、{width}、{height}。
import { UrlTemplateImageryProvider, GeographicTilingScheme, buildModuleUrl } from "cesium";
// TMS-style with Geographic tiling
const tms = new UrlTemplateImageryProvider({
url: buildModuleUrl("Assets/Textures/NaturalEarthII") + "/{z}/{x}/{reverseY}.jpg",
tilingScheme: new GeographicTilingScheme(),
maximumLevel: 5,
});
viewer.imageryLayers.addImageryProvider(tms);
// Carto Positron with subdomains
const positron = new UrlTemplateImageryProvider({
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
subdomains: "abcd",
credit: "Map tiles by CartoDB, under CC BY 3.0. Data by OpenStreetMap, under ODbL.",
});
// Custom tags for time-varying data
const custom = new UrlTemplateImageryProvider({
url: "https://yourserver/{Time}/{z}/{y}/{x}.png",
customTags: {
Time: (imageryProvider, x, y, level) => "20240101",
},
});
WebMapServiceImageryProvider(WMS)
import { WebMapServiceImageryProvider, ImageryLayer, Rectangle } from "cesium";
const wms = new WebMapServiceImageryProvider({
url: "https://basemap.nationalmap.gov:443/arcgis/services/USGSHydroCached/MapServer/WMSServer",
layers: "0",
rectangle: Rectangle.fromDegrees(-180, -90, 180, 90),
// parameters: { transparent: true, format: "image/png" },
// crs: "EPSG:4326", // WMS >= 1.3.0
// srs: "EPSG:4326", // WMS 1.1.x
});
viewer.imageryLayers.add(new ImageryLayer(wms));
WebMapTileServiceImageryProvider(WMTS)
必需选项:url、layer、style、tileMatrixSetID。
import { WebMapTileServiceImageryProvider, Credit } from "cesium";
const wmts = new WebMapTileServiceImageryProvider({
url: "https://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS",
layer: "USGSShadedReliefOnly",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "default028mm",
maximumLevel: 19,
credit: new Credit("U. S. Geological Survey"),
});
viewer.imageryLayers.addImageryProvider(wmts);
GetFeatureInfo(1.140+,#13196): WebMapTileServiceImageryProvider 现在支持
对 KVP 和 RESTful WMTS 服务调用 pickFeatures。使用新的构造函数选项
enablePickFeatures、getFeatureInfoFormats、getFeatureInfoUrl 和
getFeatureInfoParameters 启用该功能;然后调用
provider.pickFeatures(x, y, level, longitude, latitude)(与 WMS 使用的签名相同)
即可查询某个位置的属性。
ArcGisMapServerImageryProvider
import { ArcGisMapServerImageryProvider, ArcGisMapService, ArcGisBaseMapType, ImageryLayer } from "cesium";
ArcGisMapService.defaultAccessToken = "<YOUR_ARCGIS_TOKEN>";
// From basemap type enum: SATELLITE, OCEANS, HILLSHADE
const arcgis = ImageryLayer.fromProviderAsync(
ArcGisMapServerImageryProvider.fromBasemapType(ArcGisBaseMapType.SATELLITE),
);
viewer.imageryLayers.add(arcgis);
// From a specific MapServer URL
const streets = ImageryLayer.fromProviderAsync(
ArcGisMapServerImageryProvider.fromUrl(
"https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
),
);
BingMapsImageryProvider
import { BingMapsImageryProvider, BingMapsStyle, ImageryLayer } from "cesium";
const bing = ImageryLayer.fromProviderAsync(
BingMapsImageryProvider.fromUrl("https://dev.virtualearth.net", {
key: "<YOUR_BING_KEY>",
mapStyle: BingMapsStyle.AERIAL_WITH_LABELS_ON_DEMAND,
}),
);
viewer.imageryLayers.add(bing);
样式:AERIAL、AERIAL_WITH_LABELS_ON_DEMAND、ROAD_ON_DEMAND、
CANVAS_DARK、CANVAS_LIGHT、CANVAS_GRAY。
MapboxStyleImageryProvider
import { MapboxStyleImageryProvider, ImageryLayer } from "cesium";
const mapbox = new MapboxStyleImageryProvider({
styleId: "streets-v11",
accessToken: "<YOUR_MAPBOX_TOKEN>",
// tilesize: 512, scaleFactor: true // retina
});
viewer.imageryLayers.add(new ImageryLayer(mapbox));
SingleTileImageryProvider
import { SingleTileImageryProvider, ImageryLayer, Rectangle } from "cesium";
const logo = ImageryLayer.fromProviderAsync(
SingleTileImageryProvider.fromUrl("/images/overlay.png", {
rectangle: Rectangle.fromDegrees(-75.0, 28.0, -67.0, 29.75),
}),
);
viewer.imageryLayers.add(logo);
1.140+(#13297):
OffscreenCanvas现在是受支持的ImageryTypes值, 因此你可以在需要图像源的任何位置传入由工作线程渲染或动态绘制的OffscreenCanvas—— 这适用于程序生成或实时更新的叠加层,无需通过数据 URL 往返传输。
分屏对比
import { ImageryLayer, SplitDirection, UrlTemplateImageryProvider } from "cesium";
// Add an overlay that only appears on the left side of the split
const nightLayer = new ImageryLayer(new UrlTemplateImageryProvider({
url: "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_CityLights_2012/default/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpeg",
maximumLevel: 8,
}));
nightLayer.splitDirection = SplitDirection.LEFT;
viewer.imageryLayers.add(nightLayer);
viewer.scene.splitPosition = 0.5; // 0-1 fraction of viewport width
SplitDirection:LEFT(-1)、NONE(0)、RIGHT(1)。
分屏显示两个底图时,如果提示词中提到了国家或地区,应先将相机对准该地区,再设置分屏。如果分屏视图落在邻近的陆地上,即使叠加层覆盖了正确的一侧,也不符合取景意图。
矩形镂空
import { Rectangle } from "cesium";
// Cover the full named region. Florida peninsula needs roughly -87..-80 lon,
// 24..31 lat; a narrow strip or off-center box will miss the visual target.
const cutout = Rectangle.fromDegrees(-87.6, 24.5, -80.0, 31.0);
// Cut a hole in the base layer to reveal imagery beneath
const base = viewer.imageryLayers.get(0);
base.cutoutRectangle = cutout;
镂空区域的大小应与要显示的地物范围相匹配。对于半岛、州或岛屿,矩形应在经度和纬度方向上覆盖所指地物,然后将相机对准同一地区,使显露出的空洞位于画面中央。
颜色转透明度
colorToAlpha 会移除与目标颜色匹配的像素;colorToAlphaThreshold 控制移除相似颜色的强度。默认阈值对于夜间灯光叠加层来说过于严格,因为不同瓦片中的“黑色”背景会有所差异。应使用明显更高的阈值,让浅色底图确实透显出来。
import { Color } from "cesium";
// On a VIIRS-style night-lights overlay added above an OSM base
const overlay = viewer.imageryLayers.get(1);
overlay.colorToAlpha = new Color(0.0, 0.016, 0.059); // dark ocean blue
overlay.colorToAlphaThreshold = 0.2; // generous tolerance
如果结果仍然整体偏暗,可进一步提高阈值(大约为 0.3-0.5),或降低叠加层的不透明度。若要使用半透明或增强亮度的叠加层,请明确同时设置 layer.alpha < 1.0 和 layer.brightness > 1.0。
在 3D Tiles 上贴覆影像
import { Cesium3DTileset, ImageryLayer, IonImageryProvider } from "cesium";
const tileset = await Cesium3DTileset.fromUrl("/path/to/tileset.json");
viewer.scene.primitives.add(tileset);
const labelLayer = ImageryLayer.fromProviderAsync(
IonImageryProvider.fromAssetId(2411391),
);
tileset.imageryLayers.add(labelLayer); // drape on tileset, not globe
labelLayer.show = false; // toggle off
演示贴覆在公共示例瓦片集上的影像时,请适当缩小视图,以便完整显示整个瓦片集,让影像覆盖整个模型表面,而不只是一个局部近景。
从 1.144 开始,贴覆在瓦片集上的图层也支持要素信息拾取:Viewer InfoBox 和 ImageryLayerCollection.pickImageryLayerFeatures 在地球仪拾取未找到结果时,会回退到 tileset.imageryLayers。因此,配置了 enablePickFeatures 的 WMS/WMTS 图层也会在 3D Tiles 表面返回元数据(参见 Imagery Providers 下的 WMTS GetFeatureInfo 选项)。
调试提供程序
import { TileCoordinatesImageryProvider, GridImageryProvider, ImageryLayer, Color } from "cesium";
// Show x/y/level labels on every tile
viewer.imageryLayers.add(new ImageryLayer(
new TileCoordinatesImageryProvider({ color: Color.YELLOW }),
));
// Wireframe grid overlay
viewer.imageryLayers.add(new ImageryLayer(new GridImageryProvider()));
瓦片丢弃策略
| 策略 | 行为 |
|---|---|
DiscardEmptyTileImagePolicy | 丢弃零字节图像(Bing Maps 默认策略) |
DiscardMissingTileImagePolicy | 将像素与已知的“缺失”瓦片进行比较 |
NeverTileDiscardPolicy | 从不丢弃(用于服务器始终返回有效瓦片的情况) |
import { NeverTileDiscardPolicy, UrlTemplateImageryProvider } from "cesium";
const provider = new UrlTemplateImageryProvider({
url: "https://my-server/{z}/{x}/{y}.png",
tileDiscardPolicy: new NeverTileDiscardPolicy(),
});
错误处理
const layer = ImageryLayer.fromProviderAsync(IonImageryProvider.fromAssetId(3812));
viewer.imageryLayers.add(layer);
// Provider creation failure
layer.errorEvent.addEventListener((error) => {
console.error("Layer creation failed:", error);
});
// Provider resolved -- listen for per-tile errors
if (layer.readyEvent) {
layer.readyEvent.addEventListener((provider) => {
provider.errorEvent.addEventListener((tileError) => {
console.warn("Tile error:", tileError.message);
});
});
}
只有在需要明确处理就绪状态和错误时,才等待 readyEvent。对于普通静态地图场景,请立即添加图层并调整相机视野,以免缺失或特定版本的就绪事件导致渲染流程中断。
随时间变化的 WMTS
为时变图层传入 clock 和 times(一个 TimeIntervalCollection)。如果场景需要使用随时间变化的图层,请保持 Viewer 的 timeline 和 animation 小部件启用;渲染出的时钟控件是时间间隔配置已生效的最直观证据。
import { WebMapTileServiceImageryProvider, TimeIntervalCollection, JulianDate, Credit } from "cesium";
const times = TimeIntervalCollection.fromIso8601({
iso8601: "2015-07-30/2017-06-16/P1D",
dataCallback: (interval) => ({ Time: JulianDate.toIso8601(interval.start) }),
});
const weather = new WebMapTileServiceImageryProvider({
url: "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/AMSR2_Snow_Water_Equivalent/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png",
layer: "AMSR2_Snow_Water_Equivalent", style: "default",
tileMatrixSetID: "2km", maximumLevel: 5, format: "image/png",
clock: viewer.clock, times: times,
credit: new Credit("NASA Global Imagery Browse Services for EOSDIS"),
});
viewer.imageryLayers.addImageryProvider(weather);
性能提示
- 限制同时加载的图层数 -- 通常为 2-3 个;每增加一个图层,瓦片请求量和 GPU 纹理内存都会随之增加。
- 对不透明的提供程序设置
hasAlphaChannel: false,以减少内存占用和上传时间。 - 使用
minimumTerrainLevel/maximumTerrainLevel,跳过无关缩放级别的瓦片获取。 - 优先使用
ImageryLayer.fromProviderAsync,而不是手动等待 -- 避免提供程序加载期间地球仪显示空白。 - 为区域提供程序设置严格的
rectangle边界,防止请求范围外的瓦片。 - 重复使用提供程序实例 -- 使用
destroy: false移除后重新添加,而不是重新创建。 - 瓦片始终有效时使用
NeverTileDiscardPolicy;像素比较会增加开销。 - 仅对分类栅格数据选择 NEAREST 过滤;LINEAR(默认)速度更快。
常见的视野调整问题
- 选错陆地。 请求冰岛却渲染格陵兰,是严重的视野调整错误。请严格使用参考坐标。
- 区域位于角落。 切割区域、分割区域或有界矩形必须位于相机下方的中心位置,而不是挤在一侧。
- 浅色底图提示却使用深色基础图层。 请将 OSM、ArcGIS World Street Map、Carto Positron 或 USGS Shaded Relief 用作基础图层。
- 默认缩放范围过宽。 即使城市实际可见,若场景高度达到大陆尺度,视野也会显得不合适。
另请参阅
- cesiumjs-viewer-setup -- Viewer 构造函数、Ion 令牌、
createWorldImageryAsync - cesiumjs-terrain-environment -- 地球仪、地形提供程序、大气、光照