Cesium 入门(三):BaseLayerPicker 底图切换与国产地图接入

Cesium 入门(三):BaseLayerPicker 底图切换与国产地图接入

前两篇我们把三维地球跑起来,并把搜索框接入了天地图。这一篇来聊聊底图(Imagery) ------Cesium 自带的 BaseLayerPicker 怎么用,以及如何把高德、天地图等国产底图整合进去,做成一个可切换的底图选择器。

一、Cesium 自带的 BaseLayerPicker

Cesium Viewer 有一个非常方便的内置控件:baseLayerPicker。只要在初始化时把它打开:

js 复制代码
viewer = new Cesium.Viewer('cesium-container', {
  baseLayerPicker: true,
  // ...
})

地图右上角就会出现一个底图选择按钮,点击后展开 Cesium 默认提供的底图列表:

默认列表主要来自 Cesium ion,包括 Bing Maps、Google Maps、Sentinel-2、ArcGIS、OpenStreetMap 等。对海外数据来说很方便,但在国内项目中往往不够接地气:

  • 国产卫星影像、矢量电子地图不够精细;
  • 部分服务在国内访问不稳定;
  • 项目需要统一用天地图/高德作为合规底图。

所以下一步就是:把国产底图也注册进 BaseLayerPicker


二、核心概念:ProviderViewModel

BaseLayerPicker 里每一个可选的底图,本质上是一个 Cesium.ProviderViewModel 实例。它的核心结构是:

js 复制代码
new Cesium.ProviderViewModel({
  name: '显示名称',
  tooltip: '悬停提示',
  iconUrl: '缩略图 data URI',
  category: '分组名',
  creationFunction: () => ImageryProvider | ImageryProvider[],
})

关键点是 creationFunction

  • 返回一个 ImageryProvider 时,表示只有一层底图;
  • 返回一个数组时,数组顺序就是图层叠放顺序,Cesium 会把整组图层一起切换。

这就为我们做"影像 + 注记"的分层底图提供了便利------比如天地图的卫星影像 img 在下,地名注记 cia 在上。


三、接入高德底图

高德地图的瓦片可以通过 UrlTemplateImageryProvider 直接加载,不需要走 JS API,因此也不需要 key( key 只在调用高德 Web 服务接口时才需要)。

高德常用的三种瓦片风格:

style 含义 用法
6 卫星影像 需要叠加 8 路网注记
7 矢量地图 自带路网和地名注记
8 路网注记 通常叠加在 6 上使用

封装一下:

js 复制代码
function createAmapLayer(style) {
  return new Cesium.UrlTemplateImageryProvider({
    url: `https://webst0{s}.is.autonavi.com/appmaptile?style=${style}&x={x}&y={y}&z={z}`,
    subdomains: ['1', '2', '3', '4'],
    maximumLevel: 18,
  })
}

四、天地图底图回顾

天地图我们第一篇已经用过,这里快速回顾一下 WMTS 图层:

layerName 含义
img 卫星影像底图
vec 矢量地图底图
ter 地形晕渲底图
cia 影像注记(叠加在 img 上)
cva 矢量注记(叠加在 vec/ter 上)

封装(token 用占位符):

js 复制代码
function createTiandituLayer(layerName) {
  return new Cesium.WebMapTileServiceImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/${layerName}_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=${layerName}&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${TIANDITU_TOKEN}`,
    layer: layerName,
    style: 'default',
    format: 'tiles',
    tileMatrixSetID: 'w',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
  })
}

五、生成缩略图图标

ProviderViewModel 需要一个 iconUrl。为了不用准备一堆图片,可以用 SVG 动态生成 data URI:

js 复制代码
function icon(bg, text) {
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><rect width="64" height="64" rx="8" fill="${bg}"/><text x="32" y="41" font-size="22" text-anchor="middle" fill="#fff" font-family="sans-serif">${text}</text></svg>`
  return `data:image/svg+xml,${encodeURIComponent(svg)}`
}

这样每个底图选项都能有一个带文字的小方块图标,配合 category 字段自动分组。


六、完整代码:自定义底图选择器

下面是完整的 App.vue。关键逻辑:

  1. customImageryViewModels 注册高德、天地图底图;
  2. 把它和 Cesium.createDefaultImageryProviderViewModels() 合并;
  3. 通过 imageryProviderViewModels 传给 Viewer;
  4. 通过 terrainProviderViewModels 保留地形切换能力;
  5. 默认选中第一个国产底图(高德卫星)。
vue 复制代码
<script setup>
import * as Cesium from 'cesium'
import { onBeforeUnmount, onMounted } from 'vue'

// 高德 key(瓦片直连不需要 key;调用高德 Web 服务 API 时才用到)
const AMAP_KEY = '*****'
const AMAP_SECRET = '*****'

// 天地图 token(底图 WMTS 都要用)
const TIANDITU_TOKEN = '*****'

let viewer = null

// 创建高德底图图层
// style: 6-卫星影像 / 7-矢量地图 / 8-路网注记
function createAmapLayer(style) {
  return new Cesium.UrlTemplateImageryProvider({
    url: `https://webst0{s}.is.autonavi.com/appmaptile?style=${style}&x={x}&y={y}&z={z}`,
    subdomains: ['1', '2', '3', '4'],
    maximumLevel: 18,
  })
}

// 创建天地图 WMTS 图层
// layerName: img-影像 / vec-矢量 / ter-地形晕渲 / cia-影像注记 / cva-矢量注记
function createTiandituLayer(layerName) {
  return new Cesium.WebMapTileServiceImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/${layerName}_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=${layerName}&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${TIANDITU_TOKEN}`,
    layer: layerName,
    style: 'default',
    format: 'tiles',
    tileMatrixSetID: 'w',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
  })
}

// 生成 BaseLayerPicker 缩略图图标(SVG 转 data URI)
function icon(bg, text) {
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><rect width="64" height="64" rx="8" fill="${bg}"/><text x="32" y="41" font-size="22" text-anchor="middle" fill="#fff" font-family="sans-serif">${text}</text></svg>`
  return `data:image/svg+xml,${encodeURIComponent(svg)}`
}

// 自定义底图选项。creationFunction 返回数组时,数组顺序 = 叠放顺序
const customImageryViewModels = [
  new Cesium.ProviderViewModel({
    name: '高德卫星影像',
    tooltip: '高德卫星影像 + 路网注记',
    iconUrl: icon('#1b2a4a', '卫星'),
    category: '高德地图',
    creationFunction: () => [createAmapLayer(6), createAmapLayer(8)],
  }),
  new Cesium.ProviderViewModel({
    name: '高德矢量地图',
    tooltip: '高德矢量地图(自带路网注记)',
    iconUrl: icon('#2e7d32', '矢量'),
    category: '高德地图',
    creationFunction: () => [createAmapLayer(7)],
  }),
  new Cesium.ProviderViewModel({
    name: '天地图卫星影像',
    tooltip: '天地图卫星影像 + 地名注记',
    iconUrl: icon('#37474f', '天卫'),
    category: '天地图',
    creationFunction: () => [
      createTiandituLayer('img'),
      createTiandituLayer('cia'),
    ],
  }),
  new Cesium.ProviderViewModel({
    name: '天地图矢量地图',
    tooltip: '天地图矢量地图 + 地名注记',
    iconUrl: icon('#1565c0', '天矢'),
    category: '天地图',
    creationFunction: () => [
      createTiandituLayer('vec'),
      createTiandituLayer('cva'),
    ],
  }),
  new Cesium.ProviderViewModel({
    name: '天地图地形晕渲',
    tooltip: '天地图地形晕渲 + 地名注记',
    iconUrl: icon('#8d6e63', '地形'),
    category: '天地图',
    creationFunction: () => [
      createTiandituLayer('ter'),
      createTiandituLayer('cva'),
    ],
  }),
]

onMounted(() => {
  // 默认底图列表 + 自定义国产底图
  const imageryViewModels = [
    ...customImageryViewModels,
    ...Cesium.createDefaultImageryProviderViewModels(),
  ]

  viewer = new Cesium.Viewer('cesium-container', {
    baseLayerPicker: true,
    imageryProviderViewModels: imageryViewModels,
    selectedImageryProviderViewModel: imageryViewModels[0], // 默认高德卫星
    // 地形切换:椭球(无地形)+ Cesium 全球地形
    terrainProviderViewModels: Cesium.createDefaultTerrainProviderViewModels(),
    animation: false,
    timeline: false,
    geocoder: false,
    homeButton: false,
    sceneModePicker: false,
    navigationHelpButton: false,
    fullscreenButton: false,
  })

  window.viewer = viewer
  window.Cesium = Cesium

  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 50000),
  })
})

onBeforeUnmount(() => {
  if (viewer) {
    viewer.destroy()
    viewer = null
  }
})
</script>

<template>
  <div id="cesium-container"></div>
</template>

<style scoped>
#cesium-container {
  width: 100%;
  height: 100%;
}
</style>

运行效果:

国产底图被放在了最前面,并按 category 分组为"高德地图"、"天地图":

默认的 Cesium ion 底图仍然保留,作为可选补充:

下方还有 Terrain(地形)分组,可以在"无地形"和"Cesium World Terrain"之间切换:


七、关键细节与踩坑提醒

1. creationFunction 返回数组的含义

返回数组时,Cesium 会按数组顺序把图层逐个加入 imageryLayers。所以卫星影像要放前面,注记图层放后面:

js 复制代码
creationFunction: () => [
  createTiandituLayer('img'), // 底图
  createTiandituLayer('cia'), // 注记,叠在底图之上
]

如果顺序写反了,注记会被影像盖住。

2. category 决定分组

相同 categoryProviderViewModel 会自动归到同一组。中文 category 可以直接显示,不需要额外转码。

3. imageryProviderViewModelsselectedImageryProviderViewModel

  • imageryProviderViewModels:传入你想让用户看到的所有底图选项;
  • selectedImageryProviderViewModel:页面加载时默认选中的那一项,必须是 imageryProviderViewModels 数组里的某个实例。

4. 地形和影像分开配置

baseLayerPicker 同时控制影像底图和地形。影像通过 imageryProviderViewModels 配置,地形通过 terrainProviderViewModels 配置。两者互不影响,可以分别扩展。

5. Token 安全

天地图 token 必须传入。高德瓦片直连不需要 key,但为了代码结构完整,通常还是会保留 AMAP_KEY 变量。发布前请把示例里的 '*****' 替换为真实值,并且不要直接提交到公网仓库,建议通过环境变量注入。


八、写在最后

这一篇我们把 Cesium 的 BaseLayerPicker 从"默认 Bing/Google 列表"扩展成了"国产底图 + 默认底图"的混合选择器。核心思路就是:

把每一种底图封装成 ProviderViewModel,通过 creationFunction 返回单图层或多图层组合,再用 category 做分组。

3D Tiles、倾斜摄影等专题数据我们会单独放到后面的文章里讲,因为它们不属于"底图"范畴,而是叠加在底图之上的三维模型层。


本文是《Cesium 入门》系列第 3 篇。示例代码中的 TIANDITU_TOKENAMAP_KEYAMAP_SECRET 均为占位符,运行前请替换为你自己的真实密钥。

相关推荐
用户61595868000223 小时前
Cesium 入门(二):Geocoder 搜索框参数详解与天地图搜索接入
cesium
灵境(虚幻知音)1 天前
Cesium动态轨迹性能瓶颈深度拆解:翼带与尾迹的底层优化实践
性能优化·cesium·3d引擎·afsim·翼带·尾迹·自定义着色器
fxshy3 天前
WebGIS 游戏化实践:基于 Cesium + Vue3 实现全球飞行模拟系统:从球体坐标、飞行动力学到地形碰撞实战
游戏·vue3·cesium·webgis·飞行模拟
用户83134859306984 天前
Cesium实现动态流动火烧云晚霞效果(可用slider调整火烧云浓度)
vue.js·webgl·cesium
CBX6 天前
Cesium 入门实战:GeoJSON 数据加载与地区边界可视化
cesium
毕安格 - BimAngle12 天前
国家电网 GIM 格式模型一键输出 3D Tiles (for Cesium) 和 glTF/glb 更新时间:2026-08-18
3d·gis·cesium·gltf·glb·3d tiles·gim
探索前端13 天前
3dtiles加载时被地形遮挡问题研究及处理思路
前端·3d·cesium
兔年鸿运Q小Q23 天前
cesium1.140以上版本加载地形
arcgis·cesium
用户831348593069824 天前
Vue3+Cesium实现阴天乌云+下雨天气特效
vue.js·webgl·cesium