Skip to content

HTML 标注绘制与编辑

组合 DrawManagerDivPointLayer,完成 HTML 点标注的绘制、选中、拖拽编辑、 GeoJSON 导入导出和完整资源清理。

能力边界

HTML 标注不需要新的集合管理器。它由两个可组合的公共 API 完成:

能力责任
DrawManager从 Cesium 场景中采集新标注的世界坐标
DivPointLayer管理 HTML DOM、逐帧屏幕投影、位置更新与图层生命周期
调用方决定 HTML 模板、属性表单、选中规则和 GeoJSON 业务字段

PopupManager 面向短时交互弹窗,GeoJsonLayer 面向 Cesium DataSource 矢量数据, 都不代替持久 DOM 标注图层。

基础用法:点击地图创建标注

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

const nexaGis = new CesiumViewer('container')
const viewer = nexaGis.viewer
const layer = new DivPointLayer({ hideHeight: 8000 })
const drawManager = new DrawManager(viewer)

layer.on(viewer)

drawManager.onDrawComplete(({ entity, positions }) => {
  // DrawManager 的结果点只用于采集坐标,调用方只移除这个自有 Entity
  viewer.entities.remove(entity)

  const card = document.createElement('div')
  card.textContent = '新建标注'
  layer.addPoint({ position: positions[0], content: card })
})

drawManager.startDraw('point')

// 页面或场景退出
drawManager.destroy()
layer.destroy(viewer)

DrawManager 完成点绘制时会返回一个结果 Entity。若它只是 HTML 标注的坐标采集器, 应立即移除该 Entity,避免 DOM 标注底部遗留重复的 Cesium 点。

进阶用法:选中与拖拽

DivPointMarker.element 可以直接绑定 DOM 选中事件。拖拽时将指针屏幕坐标转为 Cesium 世界坐标,再调用 updatePosition

ts
const marker = layer.addPoint({ position, content: card })

marker.element.addEventListener('pointerdown', () => {
  // 拖拽开始时先保存原始相机控制状态,再临时禁用旋转/平移
})

window.addEventListener('pointermove', (event) => {
  const rect = viewer.scene.canvas.getBoundingClientRect()
  const screen = new Cesium.Cartesian2(event.clientX - rect.left, event.clientY - rect.top)
  const next = viewer.scene.pickPositionSupported
    ? viewer.scene.pickPosition(screen)
    : viewer.camera.pickEllipsoid(screen, viewer.scene.globe.ellipsoid)

  if (next) marker.updatePosition(next)
})

拖拽结束、取消或页面销毁时,必须同时:

  • 移除 windowpointermove / pointerup / pointercancel 监听;
  • 恢复拖拽前的 enableRotate / enableTranslate / enableTilt 值;
  • 恢复页面 cursor;
  • 不得使用 viewer._element 等 Cesium 私有字段。

GeoJSON 导入导出

DOM 模板是调用方 UI,不应直接写入 GeoJSON。持久化时仅保存稳定的点几何与业务属性:

ts
const cartographic = Cesium.Cartographic.fromCartesian(marker.position)
const feature = {
  type: 'Feature',
  properties: {
    id: 'station-a',
    plotType: 'gradient',
    label: '通信中继站',
  },
  geometry: {
    type: 'Point',
    coordinates: [
      Cesium.Math.toDegrees(cartographic.longitude),
      Cesium.Math.toDegrees(cartographic.latitude),
      cartographic.height,
    ],
  },
}

导入时应验证 FeatureCollectionPoint 和有限数值坐标。浏览器下载通过 URL.createObjectURL 创建地址后,需调用 URL.revokeObjectURL 释放。

资源所有权与清理

资源创建者清理方式
绘制事件处理器、Tooltip、临时 EntityDrawManagerdrawManager.destroy()
图层 DOM、标注 DOM、postRender 监听DivPointLayerlayer.destroy(viewer)
面板、样式、window pointer 监听调用方页面 cleanup 中显式移除
GeoJSON Blob URL调用方下载触发后 URL.revokeObjectURL()

不要在清理时调用 viewer.entities.removeAll()imageryLayers.removeAll();这会误删其他模块拥有的资源。

迁移说明

  • 源案例使用 React 内部 _reactInternals、Cesium viewer._element 和 jQuery document 事件, 目标实现全部改为 DOM 和 Cesium 公共 API。
  • 源案例逐标注注册 postRenderDivPointLayer 改为整层单监听批量投影。
  • 源案例的 HTTP 3D Tiles、高德底图和业务 GeoJSON 仅是演示背景/数据,不属于 HTML 标注能力,本案例使用默认地球与内联中性数据。
  • 依赖 Cesium 1.133.1 公共 SceneTransforms.worldToWindowCoordinatespickPositioncamera.pickEllipsoidscene.postRender