• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

arc3dlab

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

第七篇:告别手动拼 URL!我们封装自己的“地图超市”

第七篇:告别手动拼 URL!我们封装自己的“地图超市” 本专栏旨在手把手带你从零开始,基于开源三维地球引擎 **Cesium** 封装一套功能完善、可复用的 **WebGIS 增强型 SDK**。内容涵盖核心封装思路、关键代码实现、常用 GIS 功能抽象,以及基于该 SDK 构建的 UI 组件库开发。

📘 专栏说明

本专栏旨在手把手带你从零开始,基于开源三维地球引擎 Cesium 封装一套功能完善、可复用的 WebGIS 增强型 SDK。内容涵盖核心封装思路、关键代码实现、常用 GIS 功能抽象,以及基于该 SDK 构建的 UI 组件库开发。如果你更关注结果而非实现过程,也可直接使用已发布的成果:

🌟 GitHub仓库 📦 NPM 包 ✨ 公众号:经纬码客(欢迎关注)

💡 建议:即便你打算直接使用 SDK,也推荐订阅本专栏 -- 理解设计思路,才能更灵活地扩展属于你自己的专属 GIS 能力!

由于作者需兼顾全职工作,更新主要安排在晚间或节假日,无法保证高频发布,但会持续迭代,直至 SDK 达到实际项目落地标准。届时将完整开源所有源码,供学习与商用(遵循许可证协议)。

大家好,我是 Cesium 酱(也可以叫我“本猿”),一名在 WebGIS 领域摸爬滚打多年的前端开发者。前几期,我们封装了 Viewer、增强了事件系统。

但还有一个高频痛点没解决:底图和地形的初始化太繁琐!

比如用天地图影像,原生要写:

const provider = new WebMapTileServiceImageryProvider({
  url: "https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&...&tk=你的密钥",
  layer: "img",
  tileMatrixSetID: "w",
  // ... 还有七八个参数
})

不仅冗长,还容易出错,更别说换图时要重写整个配置。

今天,我们就把这一切简化成:

viewer.baseImagery = BaseLayer.DefaultTdtImg

准备好了吗?打开你的编辑器,继续我们的SDK建设之旅,我们一起来建这个“地图超市”!


🧱 第一步:明确目标 —— 我们要支持哪些图源?

国内开发者最常用的底图/地形包括:

  • ✅ 单张静态图(用于离线或简单场景)
  • ✅ ArcGIS Online 全球影像/街道图(现在链接好像失效了)
  • ✅ 天地图影像 / 矢量 / 注记(需 token)
  • ✅ Cesium 官方全球地形
  • ✅ 自定义 TMS 地形(如本地部署的 terrain)

我们的 BaseLayer 就要为这些场景提供预设快捷方式。


📦 第二步:创建工具函数 —— 天地图参数生成器

首先,在 src/utils/imagery/ImageryOption.ts 中,封装天地图的 URL 拼接逻辑:

// src/utils/imagery/ImageryOption.ts
import { TDT_KEY } from "../def/Default" // 这里是我申请的默认 token
/**
 * 生成天地图 WMTS 服务配置
 * @param type 'img' 影像, 'vec' 矢量, 'cva' 矢量注记, 'cia' 影像注记
 * @param token 天地图开发者密钥
 */
export function getTdtOption(
  type: "img" | "vec" | "cva" | "cia",
  token = TDT_KEY
) {
  const url = `https://{s}.tianditu.gov.cn/${type}_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${type}&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={TileCol}&TILEROW={TileRow}&TILEMATRIX={TileMatrix}&tk=${token}`
 
  return {
    url,
    layer: type,
    style: "default",
    format: "tiles",
    tileMatrixSetID: "w",
    maximumLevel: 18,
    subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
    token,
  }
}

✅ 从此以后,不用再手拼天地图 URL!

✅ 支持四种图层类型,自动适配子域名和最大层级。

💡 提示:TDT_KEY 可在 src/utils/def/Default.ts 中定义为你的默认 token,方便团队共享。


🏗️ 第三步:实现 BaseLayer 核心模块

新建 src/core/layers/BaseLayer.ts,这是我们的“地图超市”货架:

// src/core/layers/BaseLayer.ts
import {
  ImageryLayer,
  SingleTileImageryProvider,
  ArcGisMapServerImageryProvider,
  WebMapTileServiceImageryProvider,
  createWorldTerrainAsync,
  Terrain,
  CesiumTerrainProvider,
} from "cesium"
// 引入本地静态图(sdk里面就是一张蓝色地球背景)
import globeImg from "src/static/globe-img"
// 引入天地图配置生成器
import { getTdtOption } from "src/utils/imagery/ImageryOption"
/**
 * 基础图层类,包括影像底图和地形底图
 */
const BaseLayer = {
  /**
   * 默认单图像底图(适用于离线或兜底场景)
   */
  get DefaultSingleImg() {
    return ImageryLayer.fromProviderAsync(
      SingleTileImageryProvider.fromUrl(globeImg),
      {}
    )
  },
  /**
   * 默认 ArcGIS 全球街道图
   */
  get DefaultArcgisImg() {
    return ImageryLayer.fromProviderAsync(
      ArcGisMapServerImageryProvider.fromUrl(
        "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
      ),
      {}
    )
  },
  /**
   * 默认天地图影像地图
   */
  get DefaultTdtImg() {
    const option = getTdtOption("img")
    return ImageryLayer.fromProviderAsync(
      Promise.resolve(new WebMapTileServiceImageryProvider(option)),
      {}
    )
  },
  /**
   * 默认天地图矢量地图
   */
  get DefaultTdtVec() {
    const option = getTdtOption("vec")
    return ImageryLayer.fromProviderAsync(
      Promise.resolve(new WebMapTileServiceImageryProvider(option)),
      {}
    )
  },
  /**
   * 默认 Cesium 全球地形(带水体和法线)
   */
  get DefaultTerrain() {
    const terrainProvider = createWorldTerrainAsync({
      requestWaterMask: true,
      requestVertexNormals: true,
    })
    return new Terrain(terrainProvider)
  },
  /**
   * 加载自定义 TMS 格式地形(如本地部署的 terrain)
   * @param url 地形服务根路径,如 "http://localhost:8080/terrain/"
   */
  getTerrain(url: string): Terrain {
    return new Terrain(CesiumTerrainProvider.fromUrl(url))
  },
}
export default BaseLayer

✅ 所有图层都通过 getter 或方法暴露,使用时自动初始化。

✅ fromProviderAsync + Promise.resolve 确保异步兼容性。

✅ getTerrain() 支持任意 TMS 地形路径,灵活适配私有部署。


🔌 第四步:导出模块,让别人能用

修改 src/index.ts,将 BaseLayer 加入主入口:

// src/index.ts
export { Viewer } from "./core/Viewer"
export { default as BaseLayer } from "./core/layers/BaseLayer"
// 后续继续添加...

然后运行:

npm run build

构建成功后,BaseLayer 就可以在外部使用了!


🧪 第五步:本地测试 —— 一键切换底图!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>003-底图调整</title>
    <script src="./Cesium/Cesium.js"></script>
    <link rel="stylesheet" href="./Cesium/Widgets/widgets.css" />
    <script src="./lib/arc3dlab.umd.js"></script>
    <script src="./assests/dat.gui.min.js"></script>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
      }
      #cesiumContainer {
        width: 100%;
        height: 100%;
        position: relative;
      }
      #gui-box {
        position: absolute;
        left: 5px;
        top: 5px;
        z-index: 99;
      }
    </style>
  </head>
  <body>
    <div id="cesiumContainer">
      <div id="gui-box"></div>
    </div>
    <script>
      const viewer = new Arc3DLab.Viewer("cesiumContainer", {
        fpsShow: true,
        mapboxController: true,
      })
      initGui()
      function initGui() {
        const params = { message: "GUI面板-底图调整", "base-imagery": 0 }
        const gui = new dat.GUI({ autoPlace: false })
        const customContainer = document.getElementById("gui-box")
        customContainer.appendChild(gui.domElement)
        gui.add(params, "message")
        const baseImg = gui.add(params, "base-imagery", {
          SingleImage: 0,
          TDTImagery: 1,
          TDTVector: 2,
          ArcgisImagery: 3,
        })
        baseImg.onChange((val) => {
          const baseLayer = Arc3DLab.BaseLayer
          const option = {
            0: baseLayer.DefaultSingleImg,
            1: baseLayer.DefaultTdtImg,
            2: baseLayer.DefaultTdtVec,
            3: baseLayer.DefaultArcgisImg,
          }
          const imagery = viewer.imageryLayers.add(option[val])
          viewer.baseImagery = option[val]
        })
      }
    </script>
  </body>
</html>

刷新页面——

🎉 地球瞬间变成高清卫星影像!

✅ 无需拼 URL,无需查文档,一行代码搞定!

ScreenShot_2026-01-20_235830_512


❤️ 写在最后

今天我们做的,不是炫技,而是解决真实开发中的重复劳动。

BaseLayer 就像一个“地图超市”,你只需要说“我要天地图影像”,它就给你打包好。

而这个模块还可以继续优化,后续我也确实会做如下扩展:

  • 支持高德、百度等国内地图(通过 XYZ 瓦片)
  • 添加 BaseLayer.fromXYZ(url) 快捷方法
  • 内置离线地形缓存策略

你的需求,就是下一次迭代的方向。

欢迎来 GitHub 提 Issue 或 PR!

让我们一起,把它打磨成你真正想用的工具。


🌟 项目开源,欢迎Star✨!

GitHub:https://github.com/jianlei-wang/Arc3DLab_SDK

NPM:https://www.npmjs.com/package/arc3dlab

Cesium 酱の百宝箱 · 第 7 篇

地图万千,取用一瞬。

2026 年元月之初,与你共建一张好用的地图。

posted on 2026-01-21 00:06  Arc3DLab  阅读(0)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3