Cesium 计算新坐标教程
计算新坐标 ·computerNewPoint· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么Cesium Viewer 初始化与场景配置Cesium Entity / DataSource 高层 APICesium 屏幕空间拾取交互GUI 参数调试面板效果说明本案例演示计算新坐标效果基于 WebGL 实现「计算新坐标」可视化效果附完整可运行源码核心用到 Cesium。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Viewer封装地球、相机、图层与 clock可关闭 animation/timeline 精简 UI。Entity适合业务对象GeoJsonDataSource加载 GeoJSON 面线点。ScreenSpaceEventHandler监听点击scene.pick取 EntitypickPosition取地表坐标。dat.GUI / lil-gui 绑定 uniform 或配置对象实时调参。实现步骤创建 Viewer配置地形/影像若案例需要并设置初始相机在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点/**Cesium点位计算与绘制工具本工具提供交互式点位绘制功能可以根据起始点、方位角和距离计算新点位坐标并绘制在地图上使用球面三角学算法保证计算精度* 功能说明1. 用户点击绘制原始点按钮开始交互式绘制2. 在地图上点击选择起始点3. 输入方位角和距离参数4. 系统自动计算并绘制终点5. 调整视角以完整显示两个点*/// 导入模块和初始化 import * as Cesium from cesium import { GUI } from dat.gui// 获取地图容器元素 const box document.getElementById(box)// GUI控制面板 /**定义图形绘制操作对象namespace obj*/ const obj { /**绘制原始点功能 - 启动交互式绘制模式functionmemberof obj*/ 绘制原始点: () { setupInteractiveDrawing() }, };// 创建GUI控制面板并添加操作按钮 const gui new GUI(); for (const key in obj) gui.add(obj, key)// Cesium Viewer初始化 /**初始化Cesium Viewer实例type {Cesium.Viewer}*/ const viewer new Cesium.Viewer(box, { animation: false, // 不显示动画控件 baseLayerPicker: false, // 不显示图层选择器 baseLayer: Cesium.ImageryLayer.fromProviderAsync( Cesium.ArcGisMapServerImageryProvider.fromUrl(https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer) ), // 设置基础影像图层 fullscreenButton: false, // 不显示全屏按钮 timeline: false, // 不显示时间线 infoBox: false, // 不显示信息框 })// 隐藏Cesium默认的Logo信息 viewer._cesiumWidget._creditContainer.style.display none; // 添加瓦片坐标信息 viewer.imageryLayers.addImageryProvider(new Cesium.TileCoordinatesImageryProvider()); // 状态管理 /**存储已创建的实体对象用于后续操作和清理type {Array }*/ let entitys [];/**存储用户输入的距离值米用于视角调整type {number}*/ let distance;/**全局事件处理器引用用于管理交互事件type {Cesium.ScreenSpaceEventHandler}*/ let globalHandler null;// 交互功能 /**设置交互式绘制模式用户点击地图时绘制点并请求输入方位角和距离以计算新点遵循事件管理规范确保不会重复注册事件*/ function setupInteractiveDrawing() { viewer.entities.removeAll(); entitys []; // 清空实体数组 // 遵循事件管理规范在注册新事件前清除旧事件避免重复注册 if (globalHandler) { globalHandler.destroy(); globalHandler null; } // 创建屏幕空间事件处理器 globalHandler new Cesium.ScreenSpaceEventHandler(viewer.canvas);// 注册鼠标左键点击事件 globalHandler.setInputAction(function (movement) { // 获取点击位置的射线 const ray viewer.camera.getPickRay(movement.position); // 在地球上拾取点击位置 const cartesian viewer.scene.globe.pick(ray, viewer.scene);if (cartesian) { // 将笛卡尔坐标转换为地理坐标经纬度 const cartographic Cesium.Cartographic.fromCartesian(cartesian); const lon Cesium.Math.toDegrees(cartographic.longitude); const lat Cesium.Math.toDegrees(cartographic.latitude);// 绘制第一个点起始点 drawPointOnMap([lon, lat], 起点);// 遵循事件管理规范完成一次绘制后清除事件处理器 if (globalHandler) { globalHandler.destroy(); globalHandler null; } } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); }/**在地图上绘制点param {Array } coordinates - 坐标 [lon, lat]param {string} name - 点的名称起点/终点*/ function drawPointOnMap(coordinates, name) { // 创建点实体并添加到Viewer中 const pointEntity viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(coordinates[0], coordinates[1]), point: { pixelSize: 15, // 点的像素大小 color: Cesium.Color.RED, // 点的颜色 outlineColor: Cesium.Color.WHITE, // 点的边框颜色 outlineWidth: 3 // 点的边框宽度 }, label: { // 根据点的索引设置标签文本 text:${name}\n[${coordinates[0].toFixed(4)}, ${coordinates[1].toFixed(4)}], font: 16px sans-serif, // 字体样式 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, -15) // 像素偏移量 } });// 将创建的实体添加到实体数组中便于后续管理 entitys.push(pointEntity);// 根据点的类型执行不同操作 if (name 起点) { // 如果是起点请求用户输入方位角和距离 requestAngleAndDistance(coordinates[0], coordinates[1]); } else { // 如果是终点调整视角以完整显示所有点 viewer.flyTo(entitys, { duration: 1, offset: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-90), range: distance * 1.5, // 根据距离调整视角范围 }, }); } }/**弹出输入框请求方位角和距离使用setTimeout确保在事件处理完成后才弹出对话框param {number} lon - 起始点经度param {number} lat - 起始点纬度*/ function requestAngleAndDistance(lon, lat) { setTimeout(() { // 弹出提示框请求用户输入 const value prompt(请输入方位角距离(km/0)\n4510 表示从正北方向顺时针45度方向,距离10公里, 4510 );// 如果用户输入了值 if (value) { // 按-分割输入值 const splitValue value.split();// 检查分割后的数组长度是否为2 if (splitValue.length 2) { // 解析方位角和距离 const angle parseFloat(splitValue[0]); distance parseFloat(splitValue[1]) * 1000; // 转换为米// 检查解析后的值是否为有效数字 if (!isNaN(angle) distance 0) { // 计算新点坐标 const newCoordinates newPointByAngleAndDistance(lon, lat, distance, angle); // 绘制新点 drawPointOnMap(newCoordinates, 终点); } else { alert(输入格式不正确请输入有效的数字); viewer.entities.removeAll(); entitys []; // 清空实体数组 } } else { alert(输入格式不正确请按照格式方位角距离); viewer.entities.removeAll(); entitys []; // 清空实体数组 } } }, 500); }/**根据起始点、距离和角度计算新坐标点位置方法1使用球面三角学的公式计算更精确但计算复杂度较高* 该方法基于球面三角学公式考虑地球曲率的影响适用于中短距离的坐标计算采用椭球体模型通过线性插值计算不同纬度的地球半径* 球面三角学原理在球面上给定一个起始点和移动方向方位角及距离可以计算出终点坐标使用了球面三角学中的正弦定律和余弦定律* param {number} lon - 起始点经度度param {number} lat - 起始点纬度度param {number} distance - 距离米param {number} angle - 方位角度正北为0度顺时针增加returns {Array } [经度, 纬度] - 新点的经纬度坐标*/ function newPointByAngleAndDistance(lon, lat, distance, angle) { // 将角度转换为弧度因为三角函数需要弧度值 let angleRad (angle * Math.PI) / 180;// 根据纬度计算地球半径地球是椭球体 // 赤道半径6378137米极地半径6356752米 // 纬度越高半径越小这里使用线性插值近似计算 let R 6378137 - ((6378137 - 6356752) * lat) / 90;// 将起始点的经纬度转换为弧度 let latRad (lat * Math.PI) / 180; let lonRad (lon * Math.PI) / 180;// 使用球面三角学公式计算新点的纬度 // 这是球面三角学中的正弦定律应用 // sin(newLat) sin(latRad)cos(distance/R) cos(latRad)sin(distance/R) * cos(angleRad) // // 原理解释 // 1. 我们在球面上从起始点(latRad, lonRad)出发沿着方位角angleRad方向移动distance距离 // 2. distance/R 是在球面上移动的弧度因为弧长半径×弧度 // 3. 这个公式考虑了 // - 起始点纬度的影响: sin(latRad) * cos(distance/R) // - 方位角的影响: cos(latRad)sin(distance/R)cos(angleRad) // 4. 最终通过反正弦函数(asin)得到新点的纬度弧度值 let newLat Math.asin( Math.sin(latRad) * Math.cos(distance / R) // 起始点纬度分量保持原有纬度的部分影响 Math.cos(latRad)Math.sin(distance / R)Math.cos(angleRad) // 方位角影响分量方位角和移动距离共同决定的纬度变化 );// 计算新点的经度 // 使用球面三角学中的公式计算经度变化 // 这里使用atan2函数确保结果在正确的象限内 // // 原理解释 // 1. atan2(y, x)函数用于计算从x轴到点(x,y)的弧度角结果范围是[-π, π] // 2. 分子部分Math.sin(angleRad)Math.sin(distance / R)Math.cos(latRad) // 表示东西向经度方向的变化分量 // 3. 分母部分Math.cos(distance / R) - Math.sin(latRad) * Math.sin(newLat) // 表示南北向纬度方向的变化分量 // 4. 将这个角度变化量加到原始经度上得到新点的经度 let newLon lonRad Math.atan2( Math.sin(angleRad)Math.sin(distance / R)Math.cos(latRad), // 东西向分量方位角和距离在经度方向的投影 Math.cos(distance / R) - Math.sin(latRad) * Math.sin(newLat) // 南北向分量考虑起始点和终点纬度影响的修正项 );// 将计算出的弧度转换回角度 newLat (newLat * 180) / Math.PI; newLon (newLon * 180) / Math.PI;return [newLon, newLat]; }完整源码GitHub小结本文提供计算新坐标完整 Cesium.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Cesium.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库