Cesium 入门实战:GeoJSON 数据加载与地区边界可视化

本文通过一个完整的 HTML 示例,带你掌握在 Cesium 中加载和渲染 GeoJSON 数据的核心技巧,包括点、线、面的绘制,以及行政区划边界的自定义着色与标注。


一、前言

GeoJSON 是地理信息领域最通用的数据交换格式之一。无论是地图上的兴趣点(POI)、道路线,还是行政区划边界,都可以用 GeoJSON 来表达。

在 Cesium 中,Cesium.GeoJsonDataSource 提供了开箱即用的 GeoJSON 解析和渲染能力。今天我们就通过一个完整示例,把从底图加载到数据渲染的全流程跑通。


二、运行效果预览

运行这段代码后,你会看到:

  • 高德路网底图作为基础地图
  • 三个基础地理要素:黄鹤楼点标记、一条折线航线、一个三角形面
  • 武汉市行政区划示意:市级边界(橙色)和两个区级边界(蓝色/紫色),每个区域中心都带有名称标注
  • 帧率显示 :开启 debugShowFramesPerSecond 方便性能观察
  • 无绿色选择框 :通过 selectionIndicator: false 禁用了默认的实体选择指示器

视角会飞向武汉市区,初始视野高度约 150 公里。


三、完整代码

html 复制代码
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/cesium@1.140.0/Build/Cesium/Cesium.js"></script>
    <link
      href="https://cdn.jsdelivr.net/npm/cesium@1.140.0/Build/Cesium/Widgets/widgets.css"
      rel="stylesheet"
    />
  </head>

  <body style="margin:0; overflow:hidden; background:#fff; width:100%; height:100%; position:absolute; top:0;">
    <div id="map" style="margin:0 auto; width:100%; height:100%;"></div>
    <script type="text/javascript">
      (async function () {
        // ================= 1. 初始化 Viewer =================
        const viewer = new Cesium.Viewer("map", {
          baseLayer: false,          // 不加载默认底图,避免需要 Cesium Ion Token
          baseLayerPicker: false,
          selectionIndicator: false, // 禁用绿色选择框
          infoBox: false,            // 禁用右上角信息框
        });
        viewer.scene.debugShowFramesPerSecond = true; // 显示帧率

        // ================= 2. 加载高德底图(路网图) =================
        const xyz = new Cesium.UrlTemplateImageryProvider({
          url: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
        });
        viewer.imageryLayers.addImageryProvider(xyz);

        // ================= 3. 示例1:简单 GeoJSON(点 / 线 / 面) =================
        const simpleGeoJson = {
          type: "FeatureCollection",
          features: [
            // 点要素:黄鹤楼
            {
              type: "Feature",
              properties: { name: "黄鹤楼", type: "landmark" },
              geometry: { type: "Point", coordinates: [114.3055, 30.5433] },
            },
            // 线要素:示例航线
            {
              type: "Feature",
              properties: { name: "示例航线", type: "route" },
              geometry: {
                type: "LineString",
                coordinates: [
                  [114.2, 30.5],
                  [114.24, 30.53],
                  [114.29, 30.56],
                  [114.34, 30.59],
                ],
              },
            },
            // 面要素:三角形(闭合)
            {
              type: "Feature",
              properties: { name: "示例面", type: "area" },
              geometry: {
                type: "Polygon",
                coordinates: [[
                  [114.15, 30.45],
                  [114.25, 30.45],
                  [114.2, 30.5],
                  [114.15, 30.45], // 首尾闭合
                ]],
              },
            },
          ],
        };

        const simpleDs = await Cesium.GeoJsonDataSource.load(simpleGeoJson, {
          stroke: Cesium.Color.HOTPINK,
          fill: Cesium.Color.PINK.withAlpha(0.4),
          strokeWidth: 3,
          markerColor: Cesium.Color.RED,
        });
        viewer.dataSources.add(simpleDs);

        // ================= 4. 示例2:行政区划边界(多级 Polygon) =================
        const adminGeoJson = {
          type: "FeatureCollection",
          features: [
            // 市级边界(外圈)
            {
              type: "Feature",
              properties: { name: "武汉市(示意边界)", level: "city", adcode: "420100" },
              geometry: {
                type: "Polygon",
                coordinates: [[
                  [114.2, 30.48], [114.28, 30.44], [114.38, 30.45],
                  [114.47, 30.5],  [114.52, 30.58], [114.49, 30.66],
                  [114.42, 30.73], [114.32, 30.76], [114.23, 30.72],
                  [114.17, 30.64], [114.15, 30.56], [114.2, 30.48],
                ]],
              },
            },
            // 区级边界:江岸区(示意)
            {
              type: "Feature",
              properties: { name: "江岸区(示意)", level: "district" },
              geometry: {
                type: "Polygon",
                coordinates: [[
                  [114.28, 30.52], [114.38, 30.53], [114.45, 30.58],
                  [114.42, 30.64], [114.33, 30.62], [114.28, 30.58],
                  [114.28, 30.52],
                ]],
              },
            },
            // 区级边界:武昌区(示意)
            {
              type: "Feature",
              properties: { name: "武昌区(示意)", level: "district" },
              geometry: {
                type: "Polygon",
                coordinates: [[
                  [114.3, 30.5], [114.42, 30.5], [114.47, 30.56],
                  [114.4, 30.6], [114.31, 30.57], [114.3, 30.5],
                ]],
              },
            },
          ],
        };

        const adminDs = await Cesium.GeoJsonDataSource.load(adminGeoJson, {
          stroke: Cesium.Color.ORANGE,
          fill: Cesium.Color.ORANGE.withAlpha(0.2),
          strokeWidth: 3,
        });
        viewer.dataSources.add(adminDs);

        // ================= 5. 按属性动态着色 =================
        adminDs.entities.values.forEach((entity) => {
          const name = entity.properties?.name?.getValue(Cesium.JulianDate.now()) || "";
          if (name.includes("江岸")) {
            entity.polygon.material = Cesium.Color.BLUE.withAlpha(0.35);
            entity.polygon.outlineColor = Cesium.Color.BLUE;
          } else if (name.includes("武昌")) {
            entity.polygon.material = Cesium.Color.PURPLE.withAlpha(0.35);
            entity.polygon.outlineColor = Cesium.Color.PURPLE;
          } else {
            entity.polygon.material = Cesium.Color.ORANGE.withAlpha(0.25);
            entity.polygon.outlineColor = Cesium.Color.ORANGE;
          }
        });

        // ================= 6. 为行政边界添加中心标注 =================
        function addBoundaryLabel(feature) {
          const ring = feature.geometry.coordinates[0];
          let lon = 0, lat = 0;
          ring.forEach((c) => { lon += c[0]; lat += c[1]; });
          lon /= ring.length;
          lat /= ring.length;

          viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(lon, lat),
            label: {
              text: feature.properties.name,
              font: "14px sans-serif",
              fillColor: Cesium.Color.WHITE,
              outlineColor: Cesium.Color.BLACK,
              outlineWidth: 3,
              style: Cesium.LabelStyle.FILL_AND_OUTLINE,
              showBackground: true,
              backgroundColor: Cesium.Color.BLACK.withAlpha(0.6),
              backgroundPadding: new Cesium.Cartesian2(8, 5),
            },
          });
        }
        adminGeoJson.features.forEach(addBoundaryLabel);

        // ================= 7. 视角飞向武汉 =================
        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(114.32, 30.58, 150000),
        });
      })();
    </script>
  </body>
</html>

四、技术要点解析

1. 无需 Cesium Ion Token 的底图方案

示例中禁用了 Cesium 默认的底图(baseLayer: false),转而使用高德地图的 XYZ 瓦片服务。这对国内开发者非常友好,无需注册和配置 Ion Token 即可快速开始。

javascript 复制代码
const xyz = new Cesium.UrlTemplateImageryProvider({
  url: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
});
viewer.imageryLayers.addImageryProvider(xyz);

2. GeoJSON 数据结构

示例中的 simpleGeoJson 演示了三种最基本的几何类型:

类型 用途 coordinates 格式
Point 点 / 标记 [经度, 纬度]
LineString 线 / 路径 [[经度, 纬度], ...]
Polygon 面 / 区域 [[[经度, 纬度], ...]](注意:Polygon 的坐标数组比 LineString 多一层嵌套,且首尾必须闭合)

3. 按属性动态着色

通过遍历 dataSource.entities.values,我们可以读取每个要素的 properties,并据此修改其样式:

javascript 复制代码
adminDs.entities.values.forEach((entity) => {
  const name = entity.properties?.name?.getValue(Cesium.JulianDate.now()) || "";
  if (name.includes("江岸")) {
    entity.polygon.material = Cesium.Color.BLUE.withAlpha(0.35);
  }
});

4. 边界中心点标注

由于 GeoJSON 中的 Polygon 没有直接提供中心点坐标,示例采用了一个简化方案:对边界的所有顶点经纬度取平均值,得到一个近似的几何中心。实际项目中,如果需要更精确的中心点,可以改用多边形质心算法。


五、运行方式

  1. 将上述完整代码保存为 .html 文件。
  2. 在浏览器中直接打开即可运行(需要联网加载 Cesium CDN 资源和高德瓦片)。
  3. 建议使用 Chrome / Edge 等现代浏览器,并确保网络畅通。

六、常见扩展场景

基于这个基础示例,你可以轻松扩展到以下实际应用场景:

  • 加载远程 GeoJSON 文件 :将 Cesium.GeoJsonDataSource.load() 的参数改为文件 URL,如 './data/wuhan.geojson'
  • 点击弹出信息 :监听 viewer.selectedEntity 变化,配合 infoBox 或自定义弹窗展示要素属性。
  • 结合数据可视化:将 GeoJSON 的属性值映射为颜色、高度或大小,制作热力图或 3D 柱状图。
  • 动态更新数据:定时刷新数据源,实现实时位置更新或轨迹回放。

七、结语

Cesium 对 GeoJSON 的支持非常完善,GeoJsonDataSource 封装了从解析到渲染的全流程,让开发者可以把更多精力放在业务逻辑和数据本身。

希望这篇示例能帮你快速上手 Cesium 的 GeoJSON 可视化。如果有任何问题或想了解更深入的话题(如自定义样式、大数据量优化等),欢迎在评论区交流讨论。😊


相关标签#Cesium #GeoJSON #地理可视化 #三维地图 #JavaScript

相关推荐
毕安格 - BimAngle6 天前
国家电网 GIM 格式模型一键输出 3D Tiles (for Cesium) 和 glTF/glb 更新时间:2026-08-18
3d·gis·cesium·gltf·glb·3d tiles·gim
探索前端7 天前
3dtiles加载时被地形遮挡问题研究及处理思路
前端·3d·cesium
兔年鸿运Q小Q17 天前
cesium1.140以上版本加载地形
arcgis·cesium
用户831348593069818 天前
Vue3+Cesium实现阴天乌云+下雨天气特效
vue.js·webgl·cesium
REDcker1 个月前
Cesium三维WebGIS入门详解
前端·gis·web·cesium·webgis
用户83134859306981 个月前
Cesium 实现行政区内部遮罩
vue.js·webgl·cesium
探索前端1 个月前
Cesium图层加载及影像服务添加
前端·cesium
用户83134859306981 个月前
Cesium 实现行政区反向遮罩镂空效果(自定义暗色蒙层+矢量/影像底图切换)
vue.js·webgl·cesium
全栈项目管理程序猿1 个月前
Cesium 实战 11 - 调整饱和度、对比度等参数,加载渲染美化影像底图
cesium