在 Cesium 中加载矢量数据源
在 Cesium 里画 3D 地球,光有底图和地形还不够,很多时候你还需要在上面叠行政区边界、业务区域、多边形分析结果…… 这些东西通常以 GeoJSON 提供,有时也会是 TopoJSON。
好消息是:Cesium 自带了一个 GeoJsonDataSource,帮你把这两种格式统一处理成 Cesium 的实体(Entity),不用自己去解析。
这篇文章就围绕一个简单问题展开: 我有一份 GeoJSON 文件,怎么把它加到 Cesium 里,并且控制样式、贴地、访问实体?
下面是一步步的教程。
准备一个基础 Viewer
先假设你已经把 Cesium 引入页面,并在 HTML 里有一个容器:
<div id="cesiumContainer"></div>
JavaScript 里创建一个最基本的 Viewer:
var viewer = new Cesium.Viewer("cesiumContainer");
之后所有的矢量数据(包括 GeoJSON)都会挂在 viewer.dataSources 上。
用 GeoJsonDataSource 加载 GeoJSON / TopoJSON
GeoJsonDataSource 是一个能同时处理 GeoJSON 和 TopoJSON 的数据源。
你可以直接把 load 的结果加入到 viewer.dataSources 里,一行搞定:
viewer.dataSources.add(
Cesium.GeoJsonDataSource.load("../../SampleData/ne_10m_us_states.topojson", {
stroke: Cesium.Color.HOTPINK,
fill: Cesium.Color.PINK,
strokeWidth: 3,
markerSymbol: "?",
})
);
这里有几个点值得关注:
ne_10m_us_states.topojson:示例里用的是 TopoJSON 文件,换成.geojson也同样适用。-
load(url, options):url是你的 GeoJSON / TopoJSON 文件路径。options用来控制样式,后面单独展开说。
viewer.dataSources.add(...):把这个数据源挂到 Viewer 上,Cesium 会自动把里面的 feature 转成实体并渲染出来。
如果你只是想简单加载加个样式,这种“链式写法”就够用了。
常用样式参数
上面的代码里顺便演示了几种常见的样式参数:
{
stroke: Cesium.Color.HOTPINK, // 线和多边形边界颜色
fill: Cesium.Color.PINK, // 多边形填充颜色
strokeWidth: 3, // 线宽
markerSymbol: '?' // 点要素使用的标记符号(比如 '?'、'!' 等)
}
大致规则可以这么理解:
-
线 / 多边形边界 用
stroke和strokeWidth控制。如果你的 GeoJSON 里有LineString或Polygon,这些样式就会生效。 -
多边形填充
fill决定多边形内部的颜色。 -
点要素 如果是
Point,markerSymbol可以设置点的图标符号(具体样式会和 Cesium 默认的样式有关)。
如果后面你想对单个实体做更细的控制,也可以在数据加载完成后对 dataSource.entities 逐个修改,这里先用全局样式把它跑起来。
贴地显示
默认情况下,GeoJSON 里的几何要素会悬浮在地球上(跟 height 数据有关)。
如果你希望它紧贴地形,比如行政区边界贴在地表,就可以用 clampToGround 选项:
var geojsonOptions = {
clampToGround: true,
};
然后在加载时传入:
var neighborhoodsPromise = Cesium.GeoJsonDataSource.load(
"./Source/SampleData/neighborhoods.geojson",
geojsonOptions
);
当 clampToGround: true 时,Cesium 会尝试根据地形(Terrain)把几何“压”到地面上,适合用来画各种边界、路径等贴地图层。
注意:
- 要让贴地效果真正好看,最好启用真实地形(而不是默认的椭球体)。
- 贴地会有一定性能开销,大量要素时要注意评估性能。
在数据加载完成后做事
GeoJsonDataSource.load 返回的是一个 Promise,而不是立即可用的对象。
上面的示例里,先把 Promise 保存下来:
var neighborhoodsPromise = Cesium.GeoJsonDataSource.load(
"./Source/SampleData/neighborhoods.geojson",
geojsonOptions
);
var neighborhoods;
neighborhoodsPromise.then(function (dataSource) {
// 把数据源加到 viewer 中
viewer.dataSources.add(dataSource);
// 如果你想后面单独访问这些实体,可以在这里保存下来
neighborhoods = dataSource.entities;
});
这一段的使用思路是:
- 先
load,得到一个 Promise。 - 等数据真正加载好 (
then回调被触发) 后,把dataSource加入viewer。 - 同时把
dataSource.entities存到一个变量里,方便后面做高亮、筛选、点击交互等操作。
比如后续你可以做这样的事:
// 简单示例:把所有实体边界改成蓝色
neighborhoods.values.forEach(function (entity) {
if (entity.polygon) {
entity.polygon.outlineColor = Cesium.Color.BLUE;
}
});
和 KML 的关系顺便一提
如果要加载 KML 格式的矢量数据,将代码中的 GeoJsonDataSource.load(...) 改为 KmlDataSource.load(...) 即可。
可以理解为:在 Cesium 里,KML 和 GeoJSON 是两条不同的加载路径:
- 加载 KML:用
KmlDataSource - 加载 GeoJSON / TopoJSON:用
GeoJsonDataSource
两者在 Cesium 中最终都会变成一堆实体(Entity),只是输入格式和使用的 DataSource 不一样而已。 如果你的数据来源从 KML 迁移到 GeoJSON,只需要:
- 换一个 DataSource(
KmlDataSource→GeoJsonDataSource) - 换一下文件路径和参数
整体使用方式是类似的。