Cesium 是一个开源的 JavaScript 库,用于构建高性能的 3D 地球和地理空间可视化应用,无需插件即可在现代浏览器中运行。它由美国空间技术公司 AGI 于 2011 年启动,2012 年开源发布,现已成为地理信息系统(GIS)、智慧城市、国防等领域的重要开发工具。对于我们开发人员最实际的也是免费+酷炫。
天地图(MAPWORLD) 是由中国自然资源部主导建设的国家地理信息公共服务平台,于2011年1月18日正式上线。作为政府公益性网站,它旨在向社会提供权威、标准、统一的在线地理信息服务,推动地理信息数据开放共享,对我们开发人员最实际的就是免费+卫星图高清晰。
使用前注意
本身cesium有自己的地图瓦块,由于我们要使用的是天地图关于国内地图卫星地图的高清晰度,需要替换为天地图的瓦片,但是天地图自身支持的cesium有版本要求,当前文章引入cesium还是使用天地图的cdn,具体可根据天地图文档进行调整。 三维服务 - 天地图 帮助文档
安装
引入组件
<script type="text/javascript" cesium="true" src="https://api.tianditu.gov.cn/cdn/demo/sanwei/static/cesium/Cesium.js"></script>
<script type="text/javascript" cesium="true" src="https://api.tianditu.gov.cn/cdn/plugins/cesium/Cesium_ext_min.js"></script>
<script type="text/javascript" cesium="true" src="https://api.tianditu.gov.cn/cdn/plugins/cesium/long.min.js"></script>
<script type="text/javascript" cesium="true" src="https://api.tianditu.gov.cn/cdn/plugins/cesium/bytebuffer.min.js"></script>
<script type="text/javascript" cesium="true" src="https://api.tianditu.gov.cn/cdn/plugins/cesium/protobuf.min.js"></script>
<link rel="stylesheet" cesium="true" href="https://api.tianditu.gov.cn/cdn/demo/sanwei/static/cesium/Widgets/widgets.css">
挂载全局Cesium对象
创建global.d.ts,将Cesium挂载在window对象上
declare global {
interface Window {
Cesium: any;
}
}
代码
初始化位置添加地图加载初始化,添加组件绑定关系,完整代码如下:
import React, { useEffect, useRef, useState } from "react";
const TIANDITU_KEY = "xxxxx";
var tdtUrl = "https://t{s}.tianditu.gov.cn/";
const SUBDOMAINS = ["0", "1", "2", "3", "4", "5", "6", "7"];
export default function MapPage() {
const cesiumContainer = useRef<HTMLDivElement>(null);
const viewerRef = useRef<any>(null);
useEffect(() => {
if (!cesiumContainer.current) return;
if (typeof window.Cesium === "undefined") {
console.error("Cesium not loaded.");
return;
}
const Cesium = window.Cesium;
// 初始化地图
const viewer = new Cesium.Map(cesiumContainer.current, {
shouldAnimate: true,
selectionIndicator: false,
baseLayerPicker: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
timeline: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
showRenderLoopErrors: false,
shadows: false,
imageryProvider: false,
});
viewerRef.current = viewer;
// 水印取消
viewer.cesiumWidget.creditContainer.style.display = "none";
// sps显示
viewer.scene.debugShowFramesPerSecond = true;
viewer.scene.fxaa = true;
viewer.scene.postProcessStages.fxaa.enabled = false;
// 添加底图
const imgMap = new Cesium.UrlTemplateImageryProvider({
url:
tdtUrl +
"img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" +
TIANDITU_KEY,
subdomains: SUBDOMAINS,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
maximumLevel: 16,
preloadAncestors: 1, // 预加载1级父级瓦片(平衡速度与内存)
preloadSiblings: 1, // 预加载1级同级瓦片
tileMatrixSetID: "GoogleMapsCompatible",
});
viewer.imageryLayers.addImageryProvider(imgMap);
// 添加第二层底图
const ciaMap = new Cesium.UrlTemplateImageryProvider({
url:
tdtUrl +
"cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" +
TIANDITU_KEY,
subdomains: SUBDOMAINS,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
maximumLevel: 16,
preloadAncestors: 1,
preloadSiblings: 1,
tileMatrixSetID: "GoogleMapsCompatible",
});
viewer.imageryLayers.addImageryProvider(ciaMap);
// 添加第三层底图
const iboMap = new Cesium.UrlTemplateImageryProvider({
url:
tdtUrl +
"ibo_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ibo&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" +
TIANDITU_KEY,
subdomains: SUBDOMAINS,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
maximumLevel: 16,
preloadAncestors: 1,
preloadSiblings: 1,
tileMatrixSetID: "GoogleMapsCompatible",
});
viewer.imageryLayers.addImageryProvider(iboMap);
// 聚焦到北京
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 2000000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-70),
roll: Cesium.Math.toRadians(0),
},
});
return () => {
if (viewerRef.current) {
viewerRef.current.destroy();
viewerRef.current = null;
}
};
}, []);
return (
<div>
<div
ref={cesiumContainer}
style={{
width: "100%",
height: "100vh",
position: "fixed",
top: 0,
left: 0,
}}
/>
</div>
);
}
怎么调参数
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| 容器与场景 | |||
container | string/Element | - | 地图渲染容器的 ID 或 DOM 元素 |
sceneMode | SceneMode | SCENE3D | 场景模式:SCENE3D(3D)/SCENE2D(2D)/COLUMBUS_VIEW(2.5D) |
globe | Object | - | 地球配置(椭球体、地形开关等) |
mapProjection | Object | WGS84 | 地图投影方式(2D/Columbus View 模式下使用) |
| 地图与地形 | |||
imageryProvider | ImageryProvider | Bing Maps | 底图服务(如 ArcGisMapServer/BingMaps) |
terrainProvider | TerrainProvider | - | 地形服务(如 Cesium.createWorldTerrain()) |
| 交互控件 | |||
animation | boolean | true | 是否显示时间轴动画控件 |
baseLayerPicker | boolean | true | 是否显示底图切换器 |
geocoder | boolean | true | 是否显示地理编码搜索框 |
sceneModePicker | boolean | true | 是否显示 2D/3D 切换按钮 |
timeline | boolean | true | 是否显示时间轴控件 |
fullscreenButton | boolean | true | 是否显示全屏按钮 |
| 渲染与性能 | |||
shadows | boolean | false | 是否启用阴影效果(需配合光源) |
requestRenderMode | boolean | false | 启用按需渲染(节省性能) |
maximumScreenSpaceError | number | 2 | 模型 LOD 细节级别(值越低越精细) |
| 其他参数 | |||
infoBox | boolean | true | 是否显示实体信息弹窗 |
skyBox | Object | - | 自定义天空盒纹理 |
creditContainer | string/Element | - | 指定版权信息显示容器 |
vrButton | boolean | false | 是否启用 VR 模式 |
怎么加标记和柱状图
假设我们有mapItems列表,作为标记的数据集。
const markers: Cesium.Entity[] = [];
mapItems.forEach((item) => {
const marker = viewer.entities.add({
name: item.id.toString(),
position: Cesium.Cartesian3.fromDegrees(
item.coordinate_y,
item.coordinate_x,
1000
),
// 优化标签样式(新增背景、阴影、字体调整)
label: {
text: item.name,
font: "18px 'Microsoft YaHei', sans-serif", // 更易读的字体
fillColor: Cesium.Color.WHITE, // 白色文字
outlineColor: Cesium.Color.fromCssColorString("#2c3e50"), // 深灰色轮廓
outlineWidth: 3,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
pixelOffset: new Cesium.Cartesian2(0, -40), // 向上偏移更多避免遮挡
// 新增背景框(半透明圆角)
backgroundColor: new Cesium.Color(0.1, 0.3, 0.6, 0.8), // 蓝灰色半透明
backgroundPadding: new Cesium.Cartesian2(8, 4), // 文字与背景边距
cornerRadius: 6, // 圆角
// 新增阴影效果
showBackground: true,
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 避免被地形遮挡
},
// 优化图标样式(使用自定义图标+动态缩放)
billboard: {
// image: "@/asserts/images/direction-marker-arrow-down.png", // 替换为项目内图标(需将图片放入 public/markers 目录)
scale: 0.9, // 适当放大图标
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐坐标点
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
// 新增鼠标悬停缩放效果
scaleByDistance: new Cesium.NearFarScalar(1000, 1.2, 100000, 0.8), // 近大远小
// 新增透明度渐变(避免远处过亮)
translucencyByDistance: new Cesium.NearFarScalar(
1000,
1.0,
100000,
0.7
),
},
});
// 添加3D柱状图(假设item包含:total, online, active, alert)
const barChartOrigin = Cesium.Cartesian3.fromDegrees(
item.coordinate_y + 0.003, // 增加经度偏移避免重叠
item.coordinate_x,
3000 // 提升基准高度
);
// 总设备数(蓝色柱)
const totalColumn = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
item.coordinate_y + 0.003,
item.coordinate_x,
3000
),
cylinder: {
length: item.total * 100 + 5000, // 增加基础高度1000米
topRadius: 10000, // 增大半径
bottomRadius: 10000,
material: Cesium.Color.BLUE.withAlpha(0.9),
},
label: {
text: `${item.total}`,
font: '16px "Microsoft YaHei"',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -50), // 下移标签
},
});
const onlineColumn = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
item.coordinate_y,
item.coordinate_x,
3000
),
cylinder: {
length: item.online * 100 + 5000, // 增加基础高度1000米
topRadius: 10000, // 增大半径
bottomRadius: 10000,
material: Cesium.Color.GREEN.withAlpha(0.9),
},
label: {
text: `${item.online}`,
font: '16px "Microsoft YaHei"',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -50), // 下移标签
},
});
markers.push(marker);
});