前面写了Cesium的基本初始化,视角切换和测量工具的使用。这一篇,我们要实现在地球上添加标记点和标记线的功能,并且提供了添加wms影像底图的方法。
一、初始化地球
1.环境变量配置
Cesium的部分功能使用需要 Ion Token, 前往Cesium 官网申请,并在.env.development文件中配置 :
VITE_CESIUM_ION_TOKEN=你的Cesium Ion Token2. 封装组件 CesiumEntity.tsx
引入Cesium核心库与样式,创建Viewer实例
import { useEffect, useRef, useState } from 'react'; import * as Cesium from 'cesium'; import { Viewer, Cartesian3, Color, Ion, Entity } from 'cesium'; // 导入 Cesium 样式文件(必须引入,否则控件样式异常) import 'cesium/Build/Cesium/Widgets/widgets.css'; import './index.scss'; const {VITE_CESIUM_ION_TOKEN} = import.meta.env // 使用你的Token令牌 Cesium.Ion.defaultAccessToken = VITE_CESIUM_ION_TOKEN const CesiumEntity: React.FC<{}> = () => { const center = [116.41, 39.92, 100000] as [number, number, number]; //北京市 const containerRef = useRef<HTMLDivElement>(null); const viewerRef = useRef<Cesium.Viewer | null>(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { if (!containerRef.current) return; try { setIsLoading(true); // 创建 Viewer const viewer = new Cesium.Viewer(containerRef.current, { // 控件配置 显示/隐藏 shouldAnimate: false, animation: false, //动画 baseLayerPicker: true, //图层选择器 fullscreenButton: true, //全屏按钮 vrButton: false, //VR效果 homeButton: true, //返回默认视角 infoBox: true, sceneModePicker: true, //场景模式切换(2D/3D) selectionIndicator: false, timeline: false, //时间轴 navigationHelpButton: true, //帮助说明按钮 navigationInstructionsInitiallyVisible: false, //帮助说明是否默认打开 // 性能优化 targetFrameRate: 60, useBrowserRecommendedResolution: false, }); viewerRef.current = viewer; // 视角定位到中国-北京区域 if (viewerRef.current) { viewerRef.current.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(center[0], center[1], center[2]), duration: 2 // 动画时长(秒),默认3 }); } setIsLoading(false); } catch (err) { console.error('Failed to initialize Cesium:', err); setError(err instanceof Error ? err.message : 'Failed to initialize Cesium'); setIsLoading(false); } // 清理函数 return () => { if (viewerRef.current) { moveHandlerRef.current?.destroy(); viewerRef.current.destroy(); viewerRef.current = null; } }; }, []); // 只在组件挂载时初始化一次 return ( <div className="cesium-earth"> <div ref={containerRef} className="cesium-viewer-container" /> {isLoading && ( <div className="loading-overlay"> <div className="loading-spinner" /> <p>加载地球中...</p> </div> )} {error && ( <div className="error-overlay"> <p>错误: {error}</p> <button onClick={() => window.location.reload()}>重试</button> </div> )} </div> ) }; export default CesiumEntity;二、添加标记点和线
1. html结构定义
<div className='toolbar'> <button onClick={addPointDrawer}>添加标记点</button> <button onClick={addLineDrawer}>添加标记线</button> <button onClick={clearEntityAll}>全部清空</button> </div>2. 定义绘制方法
我们先定义个模拟数据
const mockData = [ { id: '1', name: '天安门', lon: 116.392, lat: 39.899 }, { id: '2', name: '前门', lon: 116.395, lat: 39.879 }, { id: '3', name: '鸟巢', lon: 116.391, lat: 39.99 }, { id: '4', name: '圆明园', lon: 116.298, lat: 40.006 }, { id: '5', name: '长城', lon: 116.03, lat: 40.36 }, ](1)标记点
这里添加的标记点用到了图片,React语法引入图片
import markerImg from '../assets/marker.png';定义绘制点的方法
// 添加标记点 const addPointDrawer = () => { if (!viewerRef.current) return; mockData.forEach(item => { if (!item.lon || !item.lat) return; // 移除已存在的实体 const existingEntity = viewerRef.current?.entities.getById('point_' + item.id); if (existingEntity) { viewerRef.current?.entities.remove(existingEntity); } const position = Cartesian3.fromDegrees(item.lon, item.lat, 0); // 创建实体 viewerRef.current!.entities.add({ id: 'point_' + item.id, //实体id position, // 使用billboard添加图片 billboard: { image: markerImg, // 图片URL verticalOrigin: VerticalOrigin.BOTTOM, // 垂直对齐 horizontalOrigin: HorizontalOrigin.CENTER, // 水平对齐 }, // 添加标签 label: { text: `${item.name}`, font: '16px "Microsoft YaHei", Arial, sans-serif', fillColor: Color.WHITE, pixelOffset: new Cartesian3(0, -60), verticalOrigin: VerticalOrigin.BOTTOM, horizontalOrigin: HorizontalOrigin.CENTER, showBackground: true, backgroundColor: Color.fromCssColorString('radial-gradient( 124.72% 50% at 50% 50%, rgba(35,50,58,0.88) 0%, rgba(35,50,58,0.88) 61.5%, rgba(0,42,66,0) 100%);'), backgroundPadding: new Cesium.Cartesian2(10, 5), }, properties: { ...item } }); }) }(2)标记线
我们就还用模拟的数据作为线的连接点。要注意:线的经纬度是放在一个数组中的,
格式:[经度1, 纬度1, 经度2, 纬度2, ...]
// 添加线 const addLineDrawer = () => { if (!viewerRef.current) return; // 定义线的经纬度坐标点 [经度1, 纬度1, 经度2, 纬度2, ...] const degrees = [mockData[0].lon, mockData[0].lat, mockData[2].lon, mockData[2].lat, mockData[3].lon, mockData[3].lat, mockData[4].lon, mockData[4].lat]; // 转换为 Cesium 需要的笛卡尔坐标 const positions = Cesium.Cartesian3.fromDegreesArray(degrees); // 添加一条带箭头的红色线 viewerRef.current!.entities.add({ polyline: { positions, width: 8, material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED), //可以定制线的样式,如颜色、发光、轮廓或箭头等 }, }); }(3)清除方法
清除某个entity实体
// 移除已存在的实体 const existingEntity = viewerRef.current?.entities.getById('point_2'); if (existingEntity) { viewerRef.current?.entities.remove(existingEntity); }清除单个实体的方法是 remove(),像这样就移除了 [前门] 这个标记点
清空全部entity
// 清掉全部entity const clearEntityAll = () => { viewerRef.current?.entities.removeAll(); }清除全部实体的方法是 removeAll()。这个方法会把地图上的所有entity标记都清楚掉
三、添加wms影像底图
有的项目中不想用Cesium默认的底图,想使用自己提供的wms底图。方法如下(写在Viewer创建之后)
const wmsImageryProvider = new Cesium.WebMapServiceImageryProvider({ url: 'http://111/geoserver/daxing_img/wms', //后端给的url layers: 'daxing', parameters: { service: 'WMS', version: '1.1.0', request: 'GetMap', format: 'image/png', //一定是png格式,jpg不支持透明背景 transparent: true, srs: 'EPSG:4326' // 注意: WMS 1.3.0使用crs而不是srs }, tilingScheme: new Cesium.GeographicTilingScheme(), // 地理分块方案 }) const wmsLayer = new Cesium.ImageryLayer(wmsImageryProvider, { show: true, // 是否显示 alpha: 1.0, // 透明度(0-1) brightness: 1.0, // 亮度 contrast: 1.0, // 对比度 hue: 0.0, // 色调 saturation: 1.0, // 饱和度 gamma: 1.0, // 伽马校正 minimumTerrainLevel: 4, // 最小地形级别 maximumTerrainLevel: 18 // 最大地形级别 } ); viewer.imageryLayers.add(wmsLayer) //添加底图图层四、代码仓库
项目的完整代码已上传至Gitee:cesium-react-demo,可直接克隆运行