关于 cesiumjs-viewer-setup
cesiumjs-viewer-setup 是 CesiumJS Viewer 与场景初始化参考,面向构建三维地球和地图应用的开发者。它汇总 Viewer 配置、地形与图层、地理编码及内容定位要点。
主要功能
- 01Viewer 与 CesiumWidget 初始化
- 02地形、影像和场景配置
- 03Ion、Google Maps 与 iTwin 配置
- 04地理编码器与 Viewer mixin
- 05为加载的内容定位视角
适用场景
- 01搭建定制控件或轻量 Cesium 场景
- 02配置地形、影像或 3D Tiles
- 03为加载的模型和数据源调整相机
- 04设置地理编码与拖放数据加载
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cesiumgs/ cesiumjs-skills --skill cesiumjs-viewer-setup安装后在 Claude Code 中输入/cesiumjs-viewer-setup确认已加载。
详细介绍
CesiumJS 查看器与场景设置
CesiumJS 应用程序初始化参考:Viewer、CesiumWidget、Ion/GoogleMaps/ITwinPlatform 配置、微件、工厂辅助函数、地理编码器服务、Viewer 混入、Credits 和相关枚举。
快速开始
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
const viewer = new Viewer("cesiumContainer", {
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
maximumLevel: 18,
})),
baseLayerPicker: false,
});
必需的 HTML:<div id="cesiumContainer" style="width:100%;height:100vh"></div>
仅当目标运行时具有所需的 ion 或 Google 授权时,才使用 Cesium ion 默认项(Terrain.fromWorldTerrain、
ImageryLayer.fromWorldImagery、createOsmBuildingsAsync、Google
Photorealistic 3D Tiles)。对于公共/无令牌示例,请选择明确指定的公共提供程序和基于 URL 的 3D Tiles。
为已加载内容调整视野(重要)
添加 tileset、模型或数据源后,必须显式调整视野。
使用默认选项构造的 Viewer 会将相机置于地球的固定视角。它不会自动缩放到你添加的图元。忘记这点是导致“我只看到灰色表面”或“资产只是角落里的一个小点”问题的最常见原因。
const tileset = await Cesium3DTileset.fromUrl(url);
viewer.scene.primitives.add(tileset);
// Pick ONE of:
await viewer.zoomTo(tileset); // instant fit to bounding sphere
await viewer.flyTo(tileset, { duration: 0 }); // animated; duration 0 = instant
viewer.zoomTo / viewer.flyTo 接受 Entity、Entity[]、EntityCollection、
DataSource、ImageryLayer、Cesium3DTileset、VoxelPrimitive、Model 或
TimeDynamicPointCloud。它们使用目标的包围球;对于具有任意局部坐标系的 tileset,这种方式有效,因为手动计算 ECEF 相机位置会完全错过资产。
如果确实需要显式指定相机位置(例如满足航向/俯仰角要求),仍应使用 viewer.zoomTo(target, headingPitchRange),这样距离会根据包围球计算,而不是靠猜测:
import { HeadingPitchRange, Math as CesiumMath } from "cesium";
await viewer.zoomTo(tileset, new HeadingPitchRange(
CesiumMath.toRadians(45), // heading
CesiumMath.toRadians(-30), // pitch
/* range omitted -> auto-fit */
));
Ion 与平台配置
Cesium Ion
import { Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN"; // required for ion assets
Ion.defaultServer = "https://your-ion-server.example.com/"; // optional: self-hosted
IonResource
import { IonResource, Cesium3DTileset } from "cesium";
const resource = await IonResource.fromAssetId(96188);
const tileset = await Cesium3DTileset.fromUrl(resource);
viewer.scene.primitives.add(tileset);
await viewer.zoomTo(tileset);
Google Maps Platform
import { GoogleMaps, createGooglePhotorealistic3DTileset, Viewer, IonGeocodeProviderType } from "cesium";
GoogleMaps.defaultApiKey = "YOUR_GOOGLE_MAPS_API_KEY"; // optional: without key, served via ion
const viewer = new Viewer("cesiumContainer", {
geocoder: IonGeocodeProviderType.GOOGLE, // required with Google 3D Tiles
});
const tileset = await createGooglePhotorealistic3DTileset({
onlyUsingWithGoogleGeocoder: true,
});
viewer.scene.primitives.add(tileset);
iTwin Platform(实验性)
import { ITwinPlatform, ITwinData } from "cesium";
ITwinPlatform.defaultAccessToken = "YOUR_ITWIN_TOKEN";
const tileset = await ITwinData.createTilesetForIModel(viewer, "imodel-id");
// 1.140+ (#13208): Reality Data of type GaussianSplat3DTiles is now supported
const splats = await ITwinData.createTilesetForRealityDataId(
iTwinId,
realityDataId,
ITwinPlatform.RealityDataType.GaussianSplat3DTiles,
);
viewer.scene.primitives.add(splats);
Viewer 构造函数选项
new Viewer(container, options?) -- container 是 DOM 元素或其字符串 ID。
微件开关
| 选项 | 默认值 | 用途 |
|---|---|---|
animation | true | 播放控制 |
baseLayerPicker | true | 影像/地形切换器 |
fullscreenButton | true | 全屏切换 |
vrButton | false | WebVR 切换 |
geocoder | IonGeocodeProviderType.DEFAULT | 搜索栏(设为 false 可隐藏) |
homeButton | true | 重置为主页视图 |
infoBox | true | 实体信息弹窗 |
sceneModePicker | true | 2D/3D/Columbus 切换 |
selectionIndicator | true | 选中标记 |
timeline | true | 时间滑块 |
navigationHelpButton | true | 鼠标/触控帮助 |
projectionPicker | false | 透视/正交切换 |
场景与渲染
| 选项 | 默认值 | 用途 |
|---|---|---|
sceneMode | SceneMode.SCENE3D | 初始场景模式 |
scene3DOnly | false | 锁定为 3D;每个几何实例可节省 GPU 内存 |
shadows | false | 阴影投射 |
terrainShadows | ShadowMode.RECEIVE_ONLY | 地形阴影模式 |
requestRenderMode | false | 仅在发生变化时渲染 |
maximumRenderTimeChange | 0.0 | 渲染时允许的最大模拟时间变化量 |
msaaSamples | 4 | MSAA(设为 1 可禁用) |
orderIndependentTranslucency | true | 透明对象排序 |
mapMode2D | MapMode2D.INFINITE_SCROLL | 2D 滚动行为 |
对于低功耗应用/仪表板,可将 requestRenderMode: true 与 scene3DOnly: true 搭配使用;在需要节省 GPU 资源的场景中,通常两者都需要设置。
图层与地形
| 选项 | 默认值 | 用途 |
|---|---|---|
baseLayer | ImageryLayer.fromWorldImagery() | 基础影像(设为 false 表示不使用;需同时设置 baseLayerPicker: false) |
terrain | 无 | 异步地形辅助项(不能与 terrainProvider 同时使用) |
terrainProvider | EllipsoidTerrainProvider | 同步地形提供程序 |
globe | new Globe() | 设为 false 表示不显示地球(太空场景) |
skyBox | 自动(WGS84) | 设为 false 可禁用天空/太阳/月亮 |
skyAtmosphere | 自动(WGS84) | 设为 false 可禁用地球边缘辉光 |
极简查看器(无控件)
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
const viewer = new Viewer("cesiumContainer", {
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
maximumLevel: 18,
})),
animation: false, baseLayerPicker: false, fullscreenButton: false,
geocoder: false, homeButton: false, infoBox: false,
sceneModePicker: false, selectionIndicator: false,
timeline: false, navigationHelpButton: false,
});
CesiumWidget(轻量替代方案)
没有 UI 控件,也不依赖 Knockout。适用于自定义 UI 或嵌入式场景。
import { CesiumWidget, Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const widget = new CesiumWidget("cesiumContainer", { shouldAnimate: true });
// Exposes: widget.scene, widget.camera, widget.entities
SceneMode 枚举
| 值 | 描述 |
|---|---|
SceneMode.SCENE3D | 标准 3D 地球仪(默认) |
SceneMode.SCENE2D | 自上而下的正交地图 |
SceneMode.COLUMBUS_VIEW | 带高度信息的 2.5D 平面地图 |
SceneMode.MORPHING | 正在切换模式 |
import { Viewer, SceneMode } from "cesium";
const viewer = new Viewer("cesiumContainer", { sceneMode: SceneMode.SCENE2D });
viewer.scene.morphTo3D(2.0); // animated transition
viewer.scene.morphToColumbusView(2.0);
场景配置
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true; // entities interact with terrain
scene.globe.enableLighting = true; // sun-based lighting
// Key sub-objects
scene.camera; // Camera
scene.primitives; // PrimitiveCollection
scene.groundPrimitives; // PrimitiveCollection (ground-clamped)
scene.imageryLayers; // ImageryLayerCollection
scene.postProcessStages;
scene.requestRender(); // trigger frame in requestRenderMode
重要:如果对应的构造函数选项设为 false,切勿访问 scene.globe.* 或 scene.skyAtmosphere.*。 在 Viewer 选项中禁用这些功能后,场景上的对应属性将为 undefined,访问 .enableLighting、.depthTestAgainstTerrain、.show 或任何其他字段都会抛出 TypeError: Cannot set properties of undefined。对于太空场景,请在构造函数中一次性完成配置,之后不要修改这些属性(参见下文“太空场景”)。
工厂辅助函数
createOsmBuildingsAsync
import { createOsmBuildingsAsync, Cesium3DTileStyle } from "cesium";
// Default styling (colors from OSM tags)
const tileset = await createOsmBuildingsAsync();
viewer.scene.primitives.add(tileset);
// Custom style
const styled = await createOsmBuildingsAsync({
style: new Cesium3DTileStyle({
color: { conditions: [
["${feature['building']} === 'hospital'", "color('#0000FF')"],
[true, "color('#ffffff')"],
]},
}),
});
createGooglePhotorealistic3DTileset
import { createGooglePhotorealistic3DTileset, IonGeocodeProviderType } from "cesium";
// Must use Google geocoder
const viewer = new Viewer("cesiumContainer", { geocoder: IonGeocodeProviderType.GOOGLE });
const tileset = await createGooglePhotorealistic3DTileset({ onlyUsingWithGoogleGeocoder: true });
viewer.scene.primitives.add(tileset);
Terrain.fromWorldTerrain / fromWorldBathymetry
建议用于 terrain 构造函数选项。此方式不会阻塞,并提供错误事件。
import { Viewer, Terrain } from "cesium";
// World terrain with normals and water
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain({ requestVertexNormals: true, requestWaterMask: true }),
});
// Bathymetry (ocean floor)
const viewer2 = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldBathymetry({ requestVertexNormals: true }),
});
Terrain 事件处理
import { Terrain, CesiumTerrainProvider } from "cesium";
const terrain = new Terrain(CesiumTerrainProvider.fromUrl("https://my-terrain.example.com"));
viewer.scene.setTerrain(terrain);
terrain.readyEvent.addEventListener((provider) => {
viewer.scene.globe.enableLighting = true;
});
terrain.errorEvent.addEventListener((error) => console.error("Terrain failed:", error));
createWorldTerrainAsync / createWorldImageryAsync
较底层:返回原始提供程序。需要直接使用提供程序时可采用此方式。
import { createWorldTerrainAsync, createWorldImageryAsync, IonWorldImageryStyle } from "cesium";
const terrainProvider = await createWorldTerrainAsync({ requestVertexNormals: true });
viewer.terrainProvider = terrainProvider;
const imageryProvider = await createWorldImageryAsync({ style: IonWorldImageryStyle.AERIAL_WITH_LABELS });
IonWorldImageryStyle:AERIAL(默认) | AERIAL_WITH_LABELS | ROAD
地理编码器配置
geocoder 选项接受 false、IonGeocodeProviderType 或 GeocoderService[]。
IonGeocodeProviderType:DEFAULT | GOOGLE(与 Google 瓦片搭配时必需) | BING
import { Viewer, CartographicGeocoderService, IonGeocoderService, OpenCageGeocoderService } from "cesium";
// Multiple services (searched in order)
const viewer = new Viewer("cesiumContainer", {
geocoder: [
new CartographicGeocoderService(), // accepts "lat, lon" input
new IonGeocoderService({ scene: viewer.scene }),
],
});
自定义 GeocoderService
const myGeocoder = {
async geocode(input, type) {
// type: GeocodeType.SEARCH or GeocodeType.AUTOCOMPLETE
const resp = await fetch(`https://api.example.com/search?q=${input}`);
const data = await resp.json();
return data.map((item) => ({
displayName: item.name,
destination: Cartesian3.fromDegrees(item.lon, item.lat),
}));
},
};
const viewer = new Viewer("cesiumContainer", { geocoder: [myGeocoder] });
Viewer 混入项
import { Viewer, viewerDragDropMixin, viewerCesium3DTilesInspectorMixin,
viewerCesiumInspectorMixin, viewerPerformanceWatchdogMixin, viewerVoxelInspectorMixin } from "cesium";
const viewer = new Viewer("cesiumContainer");
// Drag-and-drop CZML/GeoJSON/KML loading
viewer.extend(viewerDragDropMixin, { dropTarget: "cesiumContainer", clearOnDrop: true });
viewer.dropError.addEventListener((handler, name, error) => console.error(error));
viewer.extend(viewerCesium3DTilesInspectorMixin); // 3D Tiles debug panel
viewer.extend(viewerCesiumInspectorMixin); // general scene inspector
viewer.extend(viewerPerformanceWatchdogMixin); // low-FPS warning
viewer.extend(viewerVoxelInspectorMixin); // voxel debug panel
Viewer 的关键属性和方法
| 属性 | 类型 |
|---|---|
viewer.scene | Scene |
viewer.camera | Camera |
viewer.entities | EntityCollection |
viewer.dataSources | DataSourceCollection |
viewer.imageryLayers | ImageryLayerCollection |
viewer.terrainProvider | TerrainProvider |
viewer.clock / clockViewModel | Clock / ClockViewModel |
viewer.canvas | HTMLCanvasElement |
viewer.screenSpaceEventHandler | ScreenSpaceEventHandler |
viewer.selectedEntity / trackedEntity | Entity |
viewer.shadows | boolean |
viewer.resolutionScale | number(默认 1.0) |
await viewer.flyTo(entity, { duration: 3.0, offset: headingPitchRange }); // animated
await viewer.zoomTo(tileset); // instant; uses bounding sphere
viewer.destroy(); // free all resources
版权信息与 FrameRateMonitor
import { Credit, FrameRateMonitor } from "cesium";
// Custom credit (showOnScreen = true)
viewer.creditDisplay.addStaticCredit(new Credit("Data by Example Corp", true));
// Monitor frame rate
const monitor = FrameRateMonitor.fromScene(viewer.scene);
monitor.lowFrameRate.addEventListener(() => console.warn("Low FPS"));
monitor.nominalFrameRate.addEventListener(() => console.log("FPS recovered"));
常见模式
配置地形和 OSM 建筑的生产级 Viewer
import { Ion, Viewer, Terrain, createOsmBuildingsAsync, Cartesian3, Math as CesiumMath } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain(), animation: false, timeline: false,
});
viewer.scene.primitives.add(await createOsmBuildingsAsync());
viewer.scene.camera.flyTo({
destination: Cartesian3.fromDegrees(-74.019, 40.6912, 750),
orientation: { heading: CesiumMath.toRadians(20), pitch: CesiumMath.toRadians(-20) },
});
加载并定位示例 3D 瓦片集
如果瓦片集的局部坐标系未知(示例资源、离散 LOD 演示),切勿手动计算 ECEF 相机位置;这样会错过资源,导致只渲染出一片灰色表面。使用 viewer.zoomTo 配合 HeadingPitchRange,根据瓦片集的包围球计算适配视图。
import { Cesium3DTileset, HeadingPitchRange, Math as CesiumMath } from "cesium";
const tileset = await Cesium3DTileset.fromUrl(url);
viewer.scene.primitives.add(tileset);
await viewer.zoomTo(
tileset,
new HeadingPitchRange(
CesiumMath.toRadians(45),
CesiumMath.toRadians(-30),
// omit range -> Cesium auto-fits the bounding sphere
),
);
太空场景(无地球仪)
只能通过构造函数选项禁用地球仪和大气层。场景的 globe 和 skyAtmosphere 属性会变为 undefined,因此之后任何 viewer.scene.globe.<anything> = ... 或 viewer.scene.skyAtmosphere.show = false 操作都会抛出 Cannot set properties of undefined。请在构造函数中配置光照、地形深度测试、大气可见性等选项;对于太空场景,也可以完全跳过这些配置。
const viewer = new Viewer("cesiumContainer", {
globe: false, // viewer.scene.globe will be undefined
skyAtmosphere: false, // viewer.scene.skyAtmosphere will be undefined
baseLayerPicker: false,
baseLayer: false, // no imagery layer is needed without a globe
});
// Safe post-construction tweaks (skyBox/sun/moon remain defined):
viewer.scene.skyBox.show = true; // default star field
viewer.scene.sun.show = false;
viewer.scene.moon.show = false;
// DO NOT do this, throws because globe/skyAtmosphere are undefined:
// viewer.scene.globe.enableLighting = true;
// viewer.scene.skyAtmosphere.show = false;
显式渲染模式(低功耗仪表板)
当不需要 2D 或 Columbus View 时,搭配 scene3DOnly: true 使用;这是标准的低 GPU 配置。
const viewer = new Viewer("cesiumContainer", {
requestRenderMode: true,
maximumRenderTimeChange: Infinity,
scene3DOnly: true,
animation: false,
timeline: false,
});
// Call viewer.scene.requestRender() after programmatic changes
自定义底图图层
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
const viewer = new Viewer("cesiumContainer", {
baseLayerPicker: false,
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
})),
});
使用 Web Mercator 的 Columbus View
import { Viewer, SceneMode, WebMercatorProjection } from "cesium";
const viewer = new Viewer("cesiumContainer", {
sceneMode: SceneMode.COLUMBUS_VIEW, mapProjection: new WebMercatorProjection(),
});
性能提示
- 对大多静态的应用,设置
requestRenderMode: true。可降低 CPU/GPU 和电池消耗。发生更改后调用scene.requestRender()。 - 不需要 2D/哥伦布视图时,使用
scene3DOnly: true。可节省每个几何实例占用的 GPU 内存。在仪表盘/低功耗场景中,通常需要同时启用requestRenderMode。 - 禁用未使用的小部件(
animation: false、timeline: false),以减少 DOM 开销。 - 在低功耗设备上设置
msaaSamples: 1。默认值4可平衡画质。 - 在 HiDPI 显示屏上降低
resolutionScale(例如0.75),以提高帧率。 - 优先使用
Terrain.fromWorldTerrain(),而非await createWorldTerrainAsync();它不会阻塞,并且会提供错误事件。 - 在地形上启用
requestVertexNormals: true,几乎不增加开销即可获得正确的光照效果。 - 从 DOM 中移除时调用
viewer.destroy(),以释放 WebGL 上下文。 - 将影像图层限制在 2-3 个。每个图层都会为每个片段增加一次纹理查询。
视图定位检查清单(避免渲染空白或内容位于视野之外)
在宣布查看器设置任务完成之前,请确认:
- 添加任何切片集、模型、GeoJSON/KML/CZML 数据源或实体集合后,已调用
await viewer.zoomTo(target)或await viewer.flyTo(target)。 - 对于局部坐标系未知的切片集,相机位置是根据包围球(
HeadingPitchRange)计算得出的,而不是手动指定Cartesian3.fromDegrees(...)。 - 如果场景指定了朝向/俯仰角,这些值会通过
HeadingPitchRange传给zoomTo/flyTo,而不是在猜测目标位置后设置到camera.flyTo上。 - 如果场景要求在资产下方显示地图,则必须存在所需的底图影像图层(数量 == 1)。
- 如果构造函数选项中设置了
globe: false或skyAtmosphere: false,后续代码不会访问viewer.scene.globe.*或viewer.scene.skyAtmosphere.*;它们均为undefined,访问时会抛出错误。
另请参阅
- cesiumjs-camera -- 相机定位、flyTo、lookAt、导航约束
- cesiumjs-entities -- 实体 API、数据源、GeoJSON/KML/CZML 加载
- cesiumjs-imagery -- 影像提供程序、图层管理、分屏
- cesiumjs-terrain-environment -- 地形提供程序、Globe、大气、天空、光照