当前位置: 首页 > news >正文

Cesium 计算方位角教程

计算方位角 ·computerAngle· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • Cesium Viewer 初始化与场景配置
  • Cesium Entity / DataSource 高层 API
  • Cesium 屏幕空间拾取交互
  • GUI 参数调试面板

效果说明

本案例演示计算方位角效果:基于 WebGL 实现「计算方位角」可视化效果,附完整可运行源码;核心用到 Cesium。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Viewer封装地球、相机、图层与 clock;可关闭 animation/timeline 精简 UI。
  • Entity适合业务对象;GeoJsonDataSource加载 GeoJSON 面线点。
  • ScreenSpaceEventHandler监听点击;scene.pick取 Entity,pickPosition取地表坐标。
  • dat.GUI / lil-gui 绑定 uniform 或配置对象实时调参。

实现步骤

  • 创建 Viewer,配置地形/影像(若案例需要)并设置初始相机
  • requestAnimationFrame循环中更新状态并 render(Cesium 为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 = { /**
    • 绘制线段功能 - 启动交互式线段绘制模式
    • @function
    • @memberof obj
    */ '绘制线段': () => { initLineDrawing() }, };

    // 创建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 {Cesium.ScreenSpaceEventHandler}
    */ let globalHandler = null;

    /**

    • 存储线段绘制过程中的点坐标
    • @type {Array }
    */ let drawLinePositions = [];

    /**

    • 临时线条实体,用于实时显示绘制过程中的线段
    • @type {Cesium.Entity}
    */ let tempLineEntity = null;

    /**

    • 距离标签实体,用于显示线段的角度信息
    • @type {Cesium.Entity}
    */ let distanceLabelEntity = null;

    // ==================== 交互功能 ==================== /**

    • 初始化绘制线功能
    • 注册鼠标事件处理程序,实现交互式线段绘制
    */ function initLineDrawing() { viewer.entities.removeAll(); tempLineEntity = null; distanceLabelEntity = null; drawLinePositions = []; // 遵循事件管理规范:在注册新事件前清除旧事件避免重复注册 if (globalHandler) { globalHandler.destroy(); globalHandler = null } // 创建屏幕空间事件处理器 globalHandler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); // 注册鼠标左键点击事件 - 用于确定线段的端点 globalHandler.setInputAction(function (movement) { // 获取点击位置的笛卡尔坐标 const cartesian = viewer.scene.pickPosition(movement.position);

    // 如果成功获取到坐标 if (cartesian) { // 将坐标添加到绘制线的点数组中 drawLinePositions.push(cartesian);

    // 在点击位置添加一个标记点 viewer.entities.add({ position: cartesian, point: { pixelSize: 10, // 点的像素大小 color: Cesium.Color.RED, // 点的颜色 outlineColor: Cesium.Color.WHITE, // 点的边框颜色 outlineWidth: 3 // 点的边框宽度 }, });

    // 如果已经有两个点,则创建永久线段 if (drawLinePositions.length === 2) { // 创建永久线条实体 viewer.entities.add({ polyline: { positions: [ drawLinePositions[0], drawLinePositions[1] ], width: 5, // 线条宽度 material: Cesium.Color.RED.withAlpha(1), // 红色不透明线条 clampToGround: true // 贴地绘制 } });

    // 遵循事件管理规范:在注册新事件前清除旧事件避免重复注册 if (globalHandler) { globalHandler.destroy(); globalHandler = null } // 计算并显示线段的角度信息 updateRealTimeAngle(drawLinePositions[0], drawLinePositions[1]); } } else { // 如果没有获取到坐标,提示点击位置不在地球上 alert('点击位置不在地球表面'); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // 注册鼠标移动事件,用于实时绘制临时线条和角度信息 globalHandler.setInputAction(function (movement) { // 获取鼠标位置的笛卡尔坐标 const cartesian = viewer.scene.pickPosition(movement.endPosition);

    // 如果成功获取到坐标且至少有一个点 if (cartesian && drawLinePositions.length > 0) { // 如果存在之前的临时线条,则更新其位置 if (tempLineEntity) { // 更新临时线条的点集合 tempLineEntity.polyline.positions = [ drawLinePositions[0], cartesian ]; } else { // 否则创建新的临时线条实体 tempLineEntity = viewer.entities.add({ polyline: { positions: [ drawLinePositions[0], cartesian ], width: 3, // 临时线条较细 material: Cesium.Color.RED.withAlpha(0.5), // 半透明红色 clampToGround: true // 贴地绘制 } }); }

    // 实时计算并显示当前两点间的角度信息 if (drawLinePositions.length >= 1) { updateRealTimeAngle(drawLinePositions[0], cartesian); } } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); }

    /**

    • 实时更新临时线段的角度显示
    • @param {Cesium.Cartesian3} point1 - 起始点
    • @param {Cesium.Cartesian3} point2 - 终止点(当前鼠标位置)
    */ function updateRealTimeAngle(point1, point2) { // 以第一个点为原点建立局部坐标系(东方向为x轴,北方向为y轴,垂直于地面为z轴) // 得到一个局部坐标到世界坐标转换的变换矩阵 const localToWorld = Cesium.Transforms.eastNorthUpToFixedFrame(point1);

    // 求世界坐标到局部坐标的变换矩阵 const worldToLocal = Cesium.Matrix4.inverse(localToWorld, new Cesium.Matrix4());

    // A点在局部坐标的位置,其实就是局部坐标原点 const localPosition_A = Cesium.Matrix4.multiplyByPoint( worldToLocal, point1, new Cesium.Cartesian3() );

    // B点在以A点为原点的局部的坐标位置 const localPosition_B = Cesium.Matrix4.multiplyByPoint( worldToLocal, point2, new Cesium.Cartesian3() );

    // 计算角度(弧度) const angle = Math.atan2( localPosition_B.x - localPosition_A.x, localPosition_B.y - localPosition_A.y );

    // 转换为角度(0-360度) let theta = angle * (180 / Math.PI); if (theta < 0) { theta = theta + 360; }

    // 计算线段中点,用于放置角度标签 const midPoint = Cesium.Cartesian3.midpoint(point1, point2, new Cesium.Cartesian3());

    // 如果距离标签实体已存在,则更新其位置和文本 if (distanceLabelEntity) { distanceLabelEntity.position = midPoint; distanceLabelEntity.label.text =${theta.toFixed(3)}°; } else { // 否则创建新的距离标签实体 distanceLabelEntity = viewer.entities.add({ position: midPoint, label: { text:${theta.toFixed(3)}°, // 显示角度值,保留两位小数 font: '18px sans-serif', // 字体样式 fillColor: Cesium.Color.WHITE, // 字体颜色 outlineColor: Cesium.Color.BLACK, // 字体边框颜色 outlineWidth: 0, // 字体边框宽度 style: Cesium.LabelStyle.FILL_AND_OUTLINE, // 标签样式 verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 垂直对齐方式 } }); } }

    完整源码:GitHub

    小结

    • 本文提供计算方位角完整 Cesium.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Cesium.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库
http://www.jsqmd.com/news/1083851/

相关文章:

  • 判断力:钱学森说的“性智”,今天终于可以工程化了
  • CVE-2024-50967漏洞复现:DATAGerry接口未授权访问与敏感信息泄露
  • 技术问答自动整理:用 OpenClaw 爬取并整理 Stack Overflow/CSDN 优质问答
  • 市面上专业的CD3E膜蛋白供应商口碑
  • 3分钟解放你的QQ音乐:macOS专属格式转换全攻略
  • 5分钟上手!在IDEA中打造你的专属阅读空间:Thief-Book插件完全指南
  • 如何诊断和修复Steam Achievement Manager成就数据加载异常问题
  • ALVR:开源无线VR串流方案,彻底解放你的虚拟现实体验
  • 工业机器人五大核心趋势:重构智能制造新生态
  • Hermes Agent 从入门到企业实战-09:Hermes-Agent工具集与MCP-Server配置实战
  • 内景 文化展馆博物馆模型
  • 2026精密高速模具机新机选型:高端台系工艺如何重塑国产增压器标杆
  • Cesium 计算新坐标教程
  • UvSquares:Blender UV编辑的终极网格重塑插件指南
  • 雷达信号通信过程仿真MATLAB 实现
  • 4G与Lora结合的远程光照监测系统设计与优化
  • 深入理解 Musl libc 线程等待机制:从 pthread_join 到超时控制
  • 2026年国内GEO培训深度复盘,信源基建教学:为什么普通学员做不出权威权重
  • 数据中心固态变压器:算力时代供电架构的范式迁移
  • 微信小程序逆向工程终极指南:5步快速掌握wxapkg文件完整解包技术
  • Claude Code /powerup 教程
  • 微分不是微小差值近似,是剥离宏观螺旋、单独提取无穷小微观生长单元的原生尺度-《全域数学vs传统数学:人类文明进阶200讲》第52讲 高中通俗版逐字稿
  • Elsevier-Tracker:科研投稿者的智能审稿状态追踪解决方案
  • 终极免费屏幕标注工具:3倍提升演示效率的完整指南
  • Loop Engineering:从提示工程到循环工程的范式跃迁
  • 甜菊糖苷 Reb M 全酶法工艺再进阶:从代糖到抗衰老,合成生物学的又一场胜利
  • 微信聊天记录备份新方案:用WeChatExporter永久保存珍贵对话
  • 终极摸鱼阅读神器:Thief-Book IDEA插件完整使用指南
  • 如何解决文献管理效率瓶颈:Zotero GPT的AI驱动自动化实践指南
  • Codex 实战篇:如何安装、创建第一个项目,并完成第一次运行