map-cesium-utils
v1.0.0
Published
cesium utils library
Maintainers
Readme
map-cesium-utils
基于 CesiumJS 的实例化地图与空间业务库。每次 createMap() 都创建独立的 Viewer、运行时、图层、要素、交互和资源生命周期,可在同一页面安全运行多个地图实例。
旧的全局 createViewer()、createLayer()、startDraw()、createPlayback() 等 API 已删除,不提供弃用别名。
安装
pnpm add map-cesium-utils cesiumcesium 是可选 peer dependency,由宿主项目安装。使用 createMap()、包根其余能力或 map-cesium-utils/interop 时必须安装;只使用 map-cesium-utils/spatial(纯领域的坐标、路径、区域、分析与校验能力)时可以不装 Cesium:
pnpm add map-cesium-utils快速开始
import { DrawGeometryType, createMap } from 'map-cesium-utils'
const map = createMap('cesium-container', {
viewerOptions: { baseLayer: false }
})
const business = map.createLayer('business')
const [entity] = business.add([
{
_uuid: 'station-1',
geometry: { type: 'Point', coordinates: [120.15, 30.28] }
}
])
const drawing = await business.draw(DrawGeometryType.Polygon)
map.feature.updateStyle(entity, { strokeColor: '#ff7a00' })
await business.flyTo()
drawing.cancel()
await map.destroy()map.viewer 是 Cesium 原生互操作出口,但 Viewer 生命周期仍由当前 Map 管理;调用方不要直接执行 viewer.destroy()。
map.pointer.pickObject() 与 onObjectClick() / onObjectHover() 会把场景命中归一化为 entity、point-primitive 或 3d-tiles-feature,同时继续复用当前 Map 的输入路由:
const stop = map.pointer.onObjectClick({
onHit: ({ result }) => {
if (result.kind === '3d-tiles-feature') {
console.log(result.object.getPropertyIds())
}
}
})
stop()矩形框选使用 Cesium 原生区域拾取,结果可直接交给现有 Selection:
map.pointer
.pickAllInRectangle({ start, end }, map.feature.isManaged)
.forEach((entity) => map.selection.select(entity, { additive: true }))实例 API
| 入口 | 能力 |
| --- | --- |
| map.createLayer() / getLayer() / listLayers() | 稳定名称图层与 MapLayer 句柄 |
| map.feature / map.selection | 要素增删改查、样式、选择和移动 |
| map.pointer | 位置、Entity、PointPrimitive 与 3D Tiles 要素拾取及监听 |
| map.draw / map.edit / map.measure | 互斥的绘制、单/多要素编辑和量测会话 |
| map.scene | 相机、地形、影像、3D Tiles、高度、海量点与原生聚合图层 |
| map.motion | 回放、动态对象和相机跟踪 |
| map.interop | GeoJSON、KML/KMZ、WKT 导入导出及 KML/CZML 原生资源 |
| map.spatial | 路径编辑和区域运行时 |
| map.onStateChange() / destroy() | 实例状态与幂等清理 |
完整方法、参数和返回类型以发布包的 dist/index.d.mts / dist/index.d.cts 为准;公开值导出由 test/index.test.ts 固定。
图层句柄(MapLayer)
createLayer(name) 是同步、幂等的 get-or-create 操作,返回绑定稳定名称的 MapLayer:
const layer = map.createLayer('business', { autoHover: true })
layer.add(data, style)
layer.append(moreData)
layer.style(patch)
await layer.draw(DrawGeometryType.LineString)
layer.importGeoJson(input)
await layer.flyTo()
layer.removeLayer()等待 Cesium DataSource 实际挂载时使用 await layer.ready()。
MapLayer 只在其名称仍绑定创建时的 DataSource 期间有效。删除图层或用同名图层替换后,旧句柄的后续操作会抛出“已失效”;正在等待的 ready()、flyTo()、绘制会话及 GeoJSON/KML/KMZ 异步导入也会随图层删除而失败、取消或中止,避免结果写入后来创建的同名图层。
removeRawEntity(entity) 只删除当前图层拥有的 Entity;由要素模块管理的 Entity 会经过统一要素生命周期清理,其他图层的 Entity 返回 false。
会话句柄
绘制、编辑、量测和路径编辑的 start() 返回本次会话的句柄。保存句柄后调用 finish()、cancel()、undo() 或 redo();句柄不会误操作后来替换它的新会话。
const edit = await map.edit.start(entity)
edit.undo()
edit.redo()
edit.finish()
const group = await map.edit.startMany([first, second])
group.translate({ longitude: 0.01, latitude: -0.02 })
group.undo()
group.finish()这些能力共用 primary-canvas 互斥组,新会话会先清理同组旧会话。
资源句柄
场景和运动资源使用绑定创建记录的句柄:
const imagery = map.scene.imagery.add('base', imageryLayer)
imagery.setOpacity(0.8)
const clustered = await map.scene.clusterLayers.add('stations', points)
const flight = await map.interop.czml.load('flight', czml)
const playback = map.motion.playback.create('flight', source)
await playback.play()
await playback.remove()删除并重建同名资源后,旧句柄会失效,不会操作替代对象。
原生 KML/KMZ 资源可直接导出当前 Entity 集合,保留 Cesium 导出器支持的几何、高度、样式和时间信息:
const resource = await map.interop.kml.load('survey', kmlBlob)
resource.dataSource.entities.getById('route')!.name = '修改后的路线'
const { kml, externalFiles } = await resource.export()
const kmz = await resource.exportKmz()export() 返回 KML 文本和外部资源文件;需要单文件交付时使用 exportKmz()。两者接受 Cesium 的 time、modelCallback 等导出选项及 signal,资源删除、同名重建或 Map 销毁会中止待处理导出。originalInput 始终是加载时的输入,不随 Entity 编辑同步。
删除图层或 Tileset、销毁 Map 时会取消所属的相机飞行;飞行 Promise 返回 false,不会继续占用新资源的相机状态。
地形通视分析复用可取消的地形剖面采样,并显式区分可见、遮挡和采样未解析:
const result = await map.scene.heights.analyzeLineOfSight(observer, target, {
spacingMeters: 30
})
if (result.status === 'blocked') {
console.log(result.firstObstruction)
}轻量子路径
只使用无 Viewer 的纯函数时可从子路径导入:
import { parseWktGeometry } from 'map-cesium-utils/interop'
import { calculatePathStatistics } from 'map-cesium-utils/spatial'map-cesium-utils/spatial:坐标、路径、区域、分析、校验和量测。map-cesium-utils/interop:GeoJSON 与 WKT 纯函数。- 实例运行时始终从包根导入
createMap。
大 GeoJSON 导入与取消
const controller = new AbortController()
const layer = map.createLayer('imported')
const importing = layer.importGeoJsonAsync(featureCollection, {
batchSize: 500,
timeBudgetMs: 8,
signal: controller.signal,
onProgress: ({ processed, total }) => console.log(processed, total)
})
// 用户取消或路由切换时调用;取消后本批次已创建的 Entity 会回滚。
controller.abort()
await importing.catch((error) => {
if (error.name !== 'AbortError') throw error
})异步处理按输入要素分批校验和展开,避免先同步校验整个 FeatureCollection;onProgress 报告 Entity 创建进度。timeBudgetMs 是创建阶段批次之间的让出目标,不是硬时间上限。文本的 JSON.parse、单个复杂几何处理和最终 UUID 去重仍同步执行;十万级数据应先测量主线程长任务和内存,再决定是否使用 Worker。
三维 WKT 编解码
import { geometryToWkt, parseWktGeometry } from 'map-cesium-utils/interop'
const geometry = parseWktGeometry('LINESTRING Z (120 30 100, 121 31 200)')
const text = geometryToWkt(geometry)纯函数支持七类二维几何及 Point Z、LineString Z,第三维原样保留,不执行高度基准转换。三维点和线导出时显式写入 Z;不支持隐式三维 WKT、M/ZM、三维 Polygon、Multi 或 GeometryCollection。map.interop.wkt.import() 仍遵守业务图形的二维约束,拒绝三维输入,避免静默丢失高度。
数据约定
- 二维坐标为
[longitude, latitude],单位为度。 - 支持 Point、LineString、Polygon、Rectangle、Circle。
- Rectangle 使用 GeoJSON Polygon;Circle 使用圆心 Point,并通过
shapeType、半径和控制点恢复语义。 - Polygon 保留外环和内洞;绘制和编辑默认阻止增加自相交。
- 未解析高度不会冒充椭球高度;不同高度基准不会直接比较。
- Cesium
Entity依赖对象身份;Vue 中保存 Entity 时使用shallowRef。 - 拾取回调收到的
Cartesian2是本次事件的独立快照,可以安全保存;Cesium 原生ScreenSpaceEventHandler的复用对象不会外泄。 - 自行注册到
dataSource.entities.collectionChanged的监听器必须自己try/catch:Cesium 的EntityCollection在监听器抛出后会永久停止派发变更事件。
生命周期
每个 Map 独占 Viewer、MapRuntime、业务状态和画布输入。销毁顺序为:取消异步任务、释放交互和租约、释放其余资源、销毁 Viewer。
await map.destroy()destroy() 幂等。单个清理失败不会阻断后续资源释放;多个错误通过 AggregateError 报告。销毁后所有实例命令都会拒绝继续工作。
销毁默认没有总时长上界(逐项清理各有 cleanupTimeoutMs 上限)。需要可预期的上界时传入总预算:
await map.destroy({ timeoutMs: 3000 })预算耗尽后,剩余清理的同步部分仍会执行(解绑句柄、移除监听不会被跳过),只是不再等待其异步收尾,未完成项通过错误报告出来。
项目结构
src/
├── api/ # createMap、CesiumMap、公共契约与组合根
├── runtime/ # 生命周期、任务、事件和交互租约
├── modules/ # Scene、Layer、Feature、Interaction、Motion、Interop、Spatial
├── platform/cesium/ # Viewer、Graphics、Picking 与交互适配
├── shared/ # 跨模块契约和无状态 Cesium 工具
├── entries/ # spatial、interop 子路径
└── index.ts # 包根公共入口模块边界、所有权和销毁顺序见 ARCHITECTURE.md,开发约束见 AGENTS.md。
开发验证
从仓库根目录运行:
pnpm --dir packages/map-cesium check:types
pnpm --dir packages/map-cesium test # 已串 check:types 与 check:boundaries
pnpm --dir packages/map-cesium check:boundaries # 分层与依赖方向门禁
pnpm lint:check # 仓库统一的只读 lint
pnpm --dir packages/map-cesium build
pnpm --dir packages/map-cesium check:size
pnpm --dir packages/map-cesium check:package
pnpm --dir packages/map-cesium benchmark:check
pnpm --dir packages/example build涉及 WebGL、鼠标或视觉行为时,还需在示例应用中做真实浏览器验收。
当前未承诺能力
- KML
<ExtendedData>完全无损往返。 - 任意原生 KML 文档的完全无损往返:资源句柄导出当前 Entity,但不保证恢复原 XML 结构、未被 Cesium 支持的扩展、NetworkLink 配置或任意 ExtendedData。
需要保留 Cesium 支持的 KML/KMZ 样式、时间、模型、路径与网络链接运行时语义时,使用 map.interop.kml.load(name, input);返回的句柄可控制显隐、移除资源,并通过 originalInput 取回未转换的原始字符串、Blob、Document 或 Resource。
