DC-SDK 实战指南:基于 Cesium 的三维数字孪生大屏开发 前言 在当今数字孪生、智慧城市等领域的开发中,三维地图可视化已经成为核心需求。

DC-SDK 实战指南:基于 Cesium 的三维数字孪生大屏开发

前言

在当今数字孪生、智慧城市等领域的开发中,三维地图可视化已经成为核心需求。本文将结合一个实际的政务大屏项目,分享如何使用 DC-SDK(@dvgis/dc-sdk)快速构建高性能的三维可视化应用。

本文基于实际生产项目 databoard-ui,涉及人口、房屋、场所、事件等多维度数据的三维可视化展示。


一、DC-SDK 是什么

DC-SDK 是一款基于 Cesium 的 WebGL 三维地图可视化开发框架,它在 Cesium 原生 API 的基础上进行了封装和增强,提供了:

  • 更简洁的 API 设计
  • 丰富的图层类型(矢量图层、聚合图层、GeoJSON图层、3DTiles图层等)
  • 完善的事件交互体系
  • Vue/React 友好的集成方式

安装方式

bash 复制代码
npm install @dvgis/dc-sdk
javascript 复制代码
// main.js 中引入
import * as DC from '@dvgis/dc-sdk'
import '@dvgis/dc-sdk/dist/dc.min.css'

// 挂载到全局,方便使用
window.DC = DC

二、初始化三维地球

基础配置

在 Vue 项目中,我们通常将地图实例管理放在 Vuex 中:

javascript 复制代码
// store/modules/map.js
initMapInstance({ commit, state }) {
  return new Promise((resolve, reject) => {
    DC.ready().then(async () => {
      const viewer = new DC.Viewer('viewer-container', {
        sceneMode: DC.SceneMode.SCENE3D,      // 3D模式
        baseLayer: true,                       // 启用基础图层
        enableCursorStyle: false,              // 自定义鼠标样式
        scene3DOnly: true,                     // 仅3D场景
        requestRenderMode: true,               // 请求渲染模式(性能优化)
      })
      
      // 配置地球外观和相机控制
      viewer.setOptions({
        showAtmosphere: false,                 // 隐藏大气层
        showSun: false,                        // 隐藏太阳
        showMoon: false,                       // 隐藏月亮
        cameraController: {
          minimumZoomDistance: 280,            // 最小缩放距离
          maximumZoomDistance: 500000,         // 最大缩放距离
        },
        globe: {
          show: true,
          showGroundAtmosphere: false,         // 隐藏地面大气层
          enableLighting: false,               // 禁用光照
          baseColor: DC.Color.fromCssColorString("rgba(0, 23, 75, 1)"),
        },
        skyBox: { show: false }                // 隐藏天空盒
      })
      
      commit('SET_MAP_INSTANCE', viewer)
      resolve(viewer)
    })
  })
}

添加影像底图

javascript 复制代码
// 创建 XYZ 瓦片底图
const baseLayer = DC.ImageryLayerFactory.createXYZImageryLayer({
  url: process.env.VUE_APP_IMAGERYLAYER_URL_XYZ,
  style: "elec",
  tilingScheme: new DC.WebMercatorTilingScheme(),
  crs: "WGS84",
  rectangle: {
    west: -180 * Math.PI / 180,
    south: -85.05 * Math.PI / 180,
    east: 180 * Math.PI / 180,
    north: 85.05 * Math.PI / 180
  }
})

await viewer.addBaseLayer([baseLayer], { brightness: 0.1 })

三、核心图层实战

1. 聚合图层 (ClusterLayer) - 海量点位展示

聚合图层是处理海量点位数据的利器,自动根据缩放级别聚合显示:

javascript 复制代码
// 创建聚合图层
this.layers.clusterLayer = new DC.ClusterLayer('clusterLayer', {
  radius: 40,                                    // 聚合像素范围
  maxZoom: 25,                                   // 最大聚合缩放级别
  image: require('@/assets/img/icon-cs.png'),   // 单点图标
  gradientColors: {                              // 聚合颜色渐变
    "0.0001": DC.Color.BLUEVIOLET,
    "0.001": DC.Color.BLUEVIOLET,
    "0.01": DC.Color.BLUEVIOLET,
    "0.1": DC.Color.BLUEVIOLET
  },
  fontColor: DC.Color.WHITE,                     // 数字颜色
  style: 'circle',                               // 样式:circle/clustering/custom
  clusterSize: 16,                              // 聚合图标尺寸
})

// 设置点位数据
const positions = dataList.map(item => ({
  attr: item,           // 自定义属性,点击时可获取
  lng: item.lng,
  lat: item.lat
}))

this.layers.clusterLayer.setPoints(positions)
this.viewer.addLayer(this.layers.clusterLayer)

聚合点点击事件处理

javascript 复制代码
this.layers.clusterLayer.on(DC.MouseEventType.CLICK, (movement) => {
  const { attr } = movement.overlay
  
  // 判断是聚合点还是单个点
  if (Object.keys(attr).includes('count')) {
    // 聚合点:获取聚合内的点列表
    const leaves = this.layers.clusterLayer.getLeaves(attr.properties.cluster_id)
    
    // 或者放大到展开级别
    const expansionZoom = this.layers.clusterLayer.getClusterExpansionZoom(attr.properties.cluster_id)
    this.viewer.camera.flyTo({ destination: ... })
  } else {
    // 单个点:显示详情信息
    this.showDetailPopup(attr)
  }
})

2. GeoJSON 图层 - 区域边界可视化

加载 GeoJSON 数据展示行政区划:

javascript 复制代码
async renderGeoJson(geoJson) {
  // 创建 GeoJsonLayer
  this.layers.geoJsonLayer = new DC.GeoJsonLayer('geoJsonLayer', geoJson, {
    clampToGround: true,        // 贴地
    pickable: true,             // 可点击(重要!)
    fill: DC.Color.fromCssColorString("rgba(0, 0, 0, 0)"),
    stroke: DC.Color.fromCssColorString("rgba(0, 171, 255, 1)"),
    strokeWidth: 10,
  })
  
  this.viewer.addLayer(this.layers.geoJsonLayer)
  
  // 遍历每个多边形,添加拉伸效果
  const delegate = await this.layers.geoJsonLayer.delegate
  this.layers.geoJsonLayer.eachOverlay((item) => {
    if (item.polygon) {
      let polygon = DC.Polygon.fromEntity(item)
      polygon.setStyle({
        height: 198,
        material: DC.Color.fromCssColorString("rgba(0, 171, 255, 0.2)"),
        extrudedHeight: 200,    // 拉伸高度,形成3D效果
      })
      this.vectorLayer.addOverlay(polygon)
    }
  })
}

3. 3DTiles 图层 - 城市建筑白模

加载城市级 3DTiles 数据,实现建筑白模渲染:

javascript 复制代码
loadWhiteTileset() {
  const tileset = new DC.Tileset(tilesetUrl, {
    // ⭐ 核心性能优化参数
    maximumScreenSpaceError: 16,           // 降低值更流畅
    maximumNumberOfLoadedTiles: 1000,      // 限制同时加载瓦片数
    skipLevelOfDetail: true,               // 跳过中间LOD
    maximumMemoryUsage: 512,               // 内存限制(MB)
    
    // 视锥体优化
    cullRequestsWhileMoving: true,         // 移动时暂停新请求
    cullRequestsWhileMovingMultiplier: 60,
    
    // 动态调整精度
    dynamicScreenSpaceError: true,
    dynamicScreenSpaceErrorDensity: 0.00278,
    dynamicScreenSpaceErrorFactor: 4.0,
  })
  
  // 自定义着色器,统一建筑颜色
  tileset.ready((tileset) => {
    tileset.customShader = new Cesium.CustomShader({
      fragmentShaderText: `
        void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
          material.diffuse = vec3(0.0/255.0, 191.0/255.0, 255.0/255.0);
        }
      `
    })
  })
  
  this.tilesetLayer.addOverlay(tileset)
  this.viewer.addLayer(this.tilesetLayer)
}

4. HTML 图层 - 自定义弹窗

使用 HtmlLayer 实现 Vue 组件弹窗:

javascript 复制代码
// 创建 HTML 图层
this.layers.htmlLayer = new DC.HtmlLayer('htmlLayer')
this.viewer.addLayer(this.layers.htmlLayer)

// 在指定位置创建 HTML 图标
createHtmlIcon(options) {
  const { position, htmlLayer, props, component } = options
  const id_html = "id_" + Date.now()
  
  // 创建 DivIcon
  let htmlIcon = new DC.DivIcon(
    new DC.Position(position.lng, position.lat),
    `<div id="${id_html}"></div>`
  )
  htmlIcon.setStyle({
    className: 'custom-popup',
    zIndex: '2200'
  })
  
  htmlLayer.addOverlay(htmlIcon)
  
  // 将 Vue 组件挂载到 DOM
  const vm = mountRemote(id_html, props, component)
  return vm
}

// 使用示例
const vm = this.createHtmlIcon({
  position: { lng: 118.18, lat: 28.42 },
  htmlLayer: this.layers.htmlLayer,
  props: { title: '详情信息' },
  component: PlaceDetails,  // Vue 组件
})

vm.$on('close', () => {
  this.layers.htmlLayer.clear()
})

四、相机控制与视角管理

常用相机操作

javascript 复制代码
// 1. 设置初始视角
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(118.18, 28.42, 1500),
  orientation: {
    heading: Cesium.Math.toRadians(0),     // 正北
    pitch: Cesium.Math.toRadians(-45),     // 俯角45度
    roll: 0
  }
})

// 2. 飞行到指定位置(带动画)
viewer.camera.flyTo({
  destination: Cesium.Cartesian3.fromDegrees(lng, lat, height),
  orientation: { heading: 0, pitch: -45, roll: 0 },
  duration: 2,                              // 飞行时间
  complete: () => console.log('到达目标')
})

// 3. 飞行到边界范围(适配数据)
viewer.flyToBounds([minLon, minLat, maxLon, maxLat], {
  heading: 0,
  pitch: -90,
  roll: 0
})

// 4. 计算边界球飞行(适配大量点位)
const positions = points.map(p => 
  Cesium.Cartesian3.fromDegrees(p.lng, p.lat)
)
const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
viewer.camera.flyToBoundingSphere(boundingSphere, {
  duration: 2,
  padding: new Cesium.HeadingPitchRange(0, -0.5, boundingSphere.radius * 1.5)
})

监听相机高度变化

javascript 复制代码
viewer.camera.changed.addEventListener(() => {
  const zoom = viewer.zoom
  const currentHeight = viewer.camera.positionCartographic.height
  console.log('当前相机高度: ' + currentHeight.toFixed(2) + ' 米')
})

五、性能优化技巧

1. 渲染模式优化

javascript 复制代码
const viewer = new DC.Viewer('viewer-container', {
  requestRenderMode: true,        // 请求渲染模式,只在需要时渲染
  maximumRenderTimeChange: 0.5,   // 最大渲染间隔
})

2. 3DTiles 性能调优

javascript 复制代码
const tileset = new DC.Tileset(url, {
  // 随相机移动动态调整精度
  maximumScreenSpaceError: 16,
  
  // 移动时降低精度
  cullRequestsWhileMoving: true,
  cullRequestsWhileMovingMultiplier: 60.0,
  
  // 相机停止后恢复精度
})

viewer.camera.moveStart.addEventListener(() => {
  tileset.maximumScreenSpaceError = 32
})
viewer.camera.moveEnd.addEventListener(() => {
  setTimeout(() => {
    tileset.maximumScreenSpaceError = 8
  }, 500)
})

3. 图层生命周期管理

javascript 复制代码
// 页面离开时清理图层
beforeDestroy() {
  for (let key in this.layers) {
    this.viewer.removeLayer(this.layers[key])
  }
}

六、完整业务封装示例

将地图操作封装为业务类,便于各页面复用:

javascript 复制代码
// layout/dcMap/common.js
export default class Common {
  layers = {}
  viewer = null
  vueInstance = null
  mapUtils = null

  constructor(viewer, vueInstance) {
    this.viewer = viewer
    this.vueInstance = vueInstance
    this.mapUtils = new MapUtils(this.viewer)
  }

  initCommon() {
    // 原型方法扩展
    for (const key in viewerRewrite) {
      DC.Viewer.prototype[key] = viewerRewrite[key]
    }
  }

  // 工具方法
  zoomIn() {
    this.mapUtils._zoomIn()
  }

  zoomOut() {
    this.mapUtils._zoomOut()
  }

  homeLayer() {
    const homePosition = store.state.map.currentView.homePosition
    this.viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(
        homePosition.longitude, 
        homePosition.latitude, 
        homePosition.height
      ),
      orientation: { ... }
    })
  }
}

业务类继承

javascript 复制代码
// layout/dcMap/dashboard.js
import Common from './common.js'

export default class Dashboard extends Common {
  constructor(viewer, vueInstance) {
    super(viewer, vueInstance)
    this.initMap()
  }
  
  initMap() {
    this.initCommon()
    // 初始化业务图层
    this.layers.vectorLayer = new DC.VectorLayer('vectorLayer')
    this.viewer.addLayer(this.layers.vectorLayer)
  }
  
  // 业务方法...
  renderPopulation(data) { }
  renderHouse(data) { }
}

七、项目结构参考

csharp 复制代码
src/
├── layout/
│   ├── DcMap.vue           # 地图容器组件
│   └── dcMap/
│       ├── common.js       # 基础地图操作类
│       ├── dashboard.js    # 首页业务操作类
│       ├── place.js        # 场所业务操作类
│       └── event.js        # 事件业务操作类
├── store/
│   └── modules/
│       └── map.js          # 地图状态管理
├── utils/
│   └── mapUtils.js         # 地图工具函数
└── views/
    └── Dashboard.vue       # 首页大屏

八、常见问题与解决方案

Q1: 弹窗被地球遮挡?

javascript 复制代码
// 设置 disableDepthTestDistance 禁用深度测试
billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY

Q2: 图层点击事件不响应?

javascript 复制代码
// 创建图层时必须开启 pickable
const layer = new DC.GeoJsonLayer(id, geoJson, {
  pickable: true  // 必须开启!
})

Q3: 3DTiles 加载太慢?

javascript 复制代码
// 调整 maximumScreenSpaceError
const tileset = new DC.Tileset(url, {
  maximumScreenSpaceError: 32,  // 值越大越模糊但越快
  skipLevelOfDetail: true
})

Q4: 内存持续增长?

javascript 复制代码
// 及时清理图层和实体
this.viewer.entities.removeAll()
this.viewer.dataSources.removeAll()
this.viewer.scene.primitives.removeAll()

总结

DC-SDK 为 Cesium 开发提供了更友好的开发体验,通过本文介绍的:

  1. 基础初始化 - 地球创建与底图配置
  2. 核心图层 - ClusterLayer、GeoJsonLayer、Tileset、HtmlLayer
  3. 相机控制 - 视角管理与飞行控制
  4. 性能优化 - 渲染模式与资源管理
  5. 工程封装 - 面向业务的类封装

希望这篇实战指南能帮助你快速上手 DC-SDK,构建出高性能的三维数字孪生应用。


参考资源

相关推荐
少云清2 小时前
【UI自动化测试】5_web自动化测试 _元素操作和元素信息获取
前端·web自动化测试
lyyl啊辉3 小时前
2. Vue数据双向绑定
前端·vue.js
CappuccinoRose4 小时前
CSS 语法学习文档(十七)
前端·css·学习·布局·houdini·瀑布流布局·csspaintingapi
keyborad pianist4 小时前
Web开发 Day1
开发语言·前端·css·vue.js·前端框架
Never_Satisfied4 小时前
在HTML & CSS中,可能导致父元素无法根据子元素的尺寸自动调整大小的情况
前端·css·html
We་ct4 小时前
LeetCode 101. 对称二叉树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
码云数智-大飞4 小时前
微前端架构落地实战:qiankun vs Module Federation 2026 深度对比与选型指南
前端·架构
IT枫斗者4 小时前
MyBatis批量插入性能优化:从5分钟到3秒的工程化实践
前端·vue.js·mysql·mongodb·性能优化·mybatis
前端 贾公子5 小时前
深入理解 Vue3 的 v-model 及自定义指令的实现原理(中)
前端·html