Skip to content

相机书签(视点收藏 / 恢复)

把当前相机视角(世界坐标 + heading/pitch/roll,可选同步捕获场景缩略图)保存为书签, 之后按 id 用 camera.flyTo(平滑飞行)或 camera.setView(瞬移)恢复视角。 对齐源案例 Scene/BookMark:源实现把书签列表放在 React state 里,本能力把 「保存 / 恢复 / 管理」下沉为 viewer.bookMark 扩展,列表面板属示例层编排,不入 SDK。


能力定位

viewerBookMarkMixin 适合:

  • 场景收藏:操作者把有价值的观察视角存下来,随时飞回;
  • 多视点切换:巡检/演示时在预设视点间往返;
  • 视角分享:书签含完整相机方位,团队可按同一视角复盘。

保存的缩略图是尽力而为scene.render() 后读 canvas,dataURL):渲染管线或画布不可读时 自动降级为 undefined,书签仍正常保存,不影响后续 flyTo/setView。

安装与挂载

ts
import { CesiumViewer, viewerBookMarkMixin } from '@nexa/gis-cesium'

const nexaGis = new CesiumViewer('container')
const viewer = nexaGis.viewer

// 安装相机书签扩展:viewer 上挂载 bookMark API
nexaGis.extend(viewerBookMarkMixin)
// 裸 viewer 等价写法:extendViewer(viewer, viewerBookMarkMixin)

本能力是插件 mixin,通过 nexaGis.extend(...)(托管入口)或 extendViewer(viewer, ...) (函数式入口)安装,二者共用同一实现,SDK 不在裸 Cesium.Viewer 上打补丁。

API

保存书签:add(options?)

ts
add(options?: BookMarkAddOptions): CameraBookMark

interface BookMarkAddOptions {
  name?: string             // 书签名称
  captureThumbnail?: boolean // 是否同步捕获当前场景缩略图(默认 false)
}

interface CameraBookMark {
  id: string                 // 唯一标识(时间戳 + 随机后缀,add 时生成)
  name?: string              // 书签名称
  position: Cartesian3       // 保存时刻相机世界坐标
  heading: number            // 方位角(弧度)
  pitch: number              // 俯仰角(弧度)
  roll: number               // 翻滚角(弧度)
  thumbnail?: string         // 场景缩略图(dataURL;捕获失败则省略)
  createdAt: JulianDate      // 创建时刻
}

恢复视角:flyTo(id, options?) / setView(id)

ts
flyTo(id: string, options?: { duration?: number }): void  // camera.flyTo,平滑飞行
setView(id: string): void                                  // camera.setView,瞬移

未知 id 为空操作(不抛错)。flyToduration 控制飞行时长,缺省用 Cesium 默认。

管理:list() / remove(id) / clear() / destroy()

ts
list(): readonly CameraBookMark[]   // 全部书签(浅拷贝,外部修改不影响内部)
remove(id: string): boolean          // 删除书签,返回是否删除成功
clear(): void                        // 清空全部
destroy(): void                      // 清空并卸载(幂等)

基础示例

ts
import { CesiumViewer, viewerBookMarkMixin } from '@nexa/gis-cesium'

const nexaGis = new CesiumViewer('container')
const viewer = nexaGis.viewer
nexaGis.extend(viewerBookMarkMixin)

// 保存当前视角(含缩略图)
const mark = viewer.bookMark.add({ name: '二环视点', captureThumbnail: true })

// 任意移动相机后,飞回书签视角(带 2s 平滑飞行)
viewer.bookMark.flyTo(mark.id, { duration: 2 })

// 或瞬移
viewer.bookMark.setView(mark.id)

// 管理
const all = viewer.bookMark.list()   // 全部书签(浅拷贝)
viewer.bookMark.remove(mark.id)      // 删除
viewer.bookMark.clear()              // 清空

进阶示例

  • 多个视点往返:依次 add 多本书签,按需 flyTo 任意一本;
  • 不捕获缩略图add({ name: '视点A' }) → 缩略图为 undefined,书签仍可 flyTo;
  • 保存前校准视角:先 camera.setViewflyTo 到目标观察点,再 add({ captureThumbnail: true }), 缩略图即该视角画面。

生命周期与清理

ts
viewer.bookMark.destroy() // 清空书签并卸载(幂等)
  • destroy() 可重复调用,重复调用安全;
  • viewer 销毁(viewer.destroy())时经销毁钩子自动清理书签;
  • 示例层面:书签列表面板由示例代码自建 DOM 挂到容器内,切换/重置示例时随容器清空, 无需额外销毁计时器。

实现说明

  • 快照add 时读取 camera.positionWC(克隆)+ heading/pitch/roll?? 0 兜底), 生成 idDate.now().toString(36) + 随机后缀createdAtJulianDate.now()
  • 缩略图captureThumbnailscene.render() 再同步读 canvas toDataURL('image/png') ——同步读取避免依赖 preserveDrawingBuffer,失败降级为 undefined
  • 恢复flyTo/setView 用书签 position 为 destination、heading/pitch/roll 为 orientation;
  • 资源所有权:mixin 只维护书签数组,不触碰任何 Entity/Primitive;uninstall 回调清空数组, 经 extendViewer 注册为 viewer destroy 钩子(幂等去重)。

Cesium 版本限制

  • 依赖 Cesium 公共 camera.positionWC/heading/pitch/rollcamera.flyTo/setViewscene.renderJulianDate,不使用私有 API;
  • 已验证目标版本:Cesium 1.133.1。

与原生 Cesium API 的组合方式

  • viewer.bookMark.add() 等价于手动 Cartesian3.clone(viewer.camera.positionWC) + 读取相机方位,并把「列表管理 + 缩略图 + 按 id 恢复」收进一处;
  • viewer.cameraControlviewerCameraMixin)互不冲突:飞行定位能力在 cameraControl, 视点收藏/恢复在 bookMark,可同时挂载。