mcpField
C
cesiumjs-viewer-setup

cesiumjs-viewer-setup

github.com/cesiumgs开发193258 次安装6 周前更新
代码仓库

关于 cesiumjs-viewer-setup

cesiumjs-viewer-setup 是 CesiumJS Viewer 与场景初始化参考,面向构建三维地球和地图应用的开发者。它汇总 Viewer 配置、地形与图层、地理编码及内容定位要点。

主要功能

  1. 01Viewer 与 CesiumWidget 初始化
  2. 02地形、影像和场景配置
  3. 03Ion、Google Maps 与 iTwin 配置
  4. 04地理编码器与 Viewer mixin
  5. 05为加载的内容定位视角

适用场景

  1. 01搭建定制控件或轻量 Cesium 场景
  2. 02配置地形、影像或 3D Tiles
  3. 03为加载的模型和数据源调整相机
  4. 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。

微件开关
选项默认值用途
animationtrue播放控制
baseLayerPickertrue影像/地形切换器
fullscreenButtontrue全屏切换
vrButtonfalseWebVR 切换
geocoderIonGeocodeProviderType.DEFAULT搜索栏(设为 false 可隐藏)
homeButtontrue重置为主页视图
infoBoxtrue实体信息弹窗
sceneModePickertrue2D/3D/Columbus 切换
selectionIndicatortrue选中标记
timelinetrue时间滑块
navigationHelpButtontrue鼠标/触控帮助
projectionPickerfalse透视/正交切换
场景与渲染
选项默认值用途
sceneModeSceneMode.SCENE3D初始场景模式
scene3DOnlyfalse锁定为 3D;每个几何实例可节省 GPU 内存
shadowsfalse阴影投射
terrainShadowsShadowMode.RECEIVE_ONLY地形阴影模式
requestRenderModefalse仅在发生变化时渲染
maximumRenderTimeChange0.0渲染时允许的最大模拟时间变化量
msaaSamples4MSAA(设为 1 可禁用)
orderIndependentTranslucencytrue透明对象排序
mapMode2DMapMode2D.INFINITE_SCROLL2D 滚动行为

对于低功耗应用/仪表板,可将 requestRenderMode: true 与 scene3DOnly: true 搭配使用;在需要节省 GPU 资源的场景中,通常两者都需要设置。

图层与地形
选项默认值用途
baseLayerImageryLayer.fromWorldImagery()基础影像(设为 false 表示不使用;需同时设置 baseLayerPicker: false)
terrain无异步地形辅助项(不能与 terrainProvider 同时使用)
terrainProviderEllipsoidTerrainProvider同步地形提供程序
globenew 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.sceneScene
viewer.cameraCamera
viewer.entitiesEntityCollection
viewer.dataSourcesDataSourceCollection
viewer.imageryLayersImageryLayerCollection
viewer.terrainProviderTerrainProvider
viewer.clock / clockViewModelClock / ClockViewModel
viewer.canvasHTMLCanvasElement
viewer.screenSpaceEventHandlerScreenSpaceEventHandler
viewer.selectedEntity / trackedEntityEntity
viewer.shadowsboolean
viewer.resolutionScalenumber(默认 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(),
});

性能提示

  1. 对大多静态的应用,设置 requestRenderMode: true。可降低 CPU/GPU 和电池消耗。发生更改后调用 scene.requestRender()。
  2. 不需要 2D/哥伦布视图时,使用 scene3DOnly: true。可节省每个几何实例占用的 GPU 内存。在仪表盘/低功耗场景中,通常需要同时启用 requestRenderMode。
  3. 禁用未使用的小部件(animation: false、timeline: false),以减少 DOM 开销。
  4. 在低功耗设备上设置 msaaSamples: 1。默认值 4 可平衡画质。
  5. 在 HiDPI 显示屏上降低 resolutionScale(例如 0.75),以提高帧率。
  6. 优先使用 Terrain.fromWorldTerrain(),而非 await createWorldTerrainAsync();它不会阻塞,并且会提供错误事件。
  7. 在地形上启用 requestVertexNormals: true,几乎不增加开销即可获得正确的光照效果。
  8. 从 DOM 中移除时调用 viewer.destroy(),以释放 WebGL 上下文。
  9. 将影像图层限制在 2-3 个。每个图层都会为每个片段增加一次纹理查询。

视图定位检查清单(避免渲染空白或内容位于视野之外)

在宣布查看器设置任务完成之前,请确认:

  1. 添加任何切片集、模型、GeoJSON/KML/CZML 数据源或实体集合后,已调用 await viewer.zoomTo(target) 或 await viewer.flyTo(target)。
  2. 对于局部坐标系未知的切片集,相机位置是根据包围球(HeadingPitchRange)计算得出的,而不是手动指定 Cartesian3.fromDegrees(...)。
  3. 如果场景指定了朝向/俯仰角,这些值会通过 HeadingPitchRange 传给 zoomTo/flyTo,而不是在猜测目标位置后设置到 camera.flyTo 上。
  4. 如果场景要求在资产下方显示地图,则必须存在所需的底图影像图层(数量 == 1)。
  5. 如果构造函数选项中设置了 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、大气、天空、光照

评论

支持 Markdown