SuperMap iClient3D for Cesium 如何限制相机位置在边界内

SuperMap iClient3D for Cesium 如何限制相机位置在边界内

功能介绍

Cesium 中,操作相机时,如果操作不当惠导致相机乱飞,找不到预设的场景,而且Cesium 中也没有可以限制相机的接口,本篇文章通过每帧检测相机位置,实现了将相机限制在了矩形边界内。

效果展示

实现思路

主要实现思路是通过viewer.scene.postUpdate方法注册了一个检测相机位置的方法,每当相机超出了设置的位置就会被强制拉回范围内

设置限制位置

首先设置一个变量确定好相机可以移动的边界位置。

javascript 复制代码
 // 定义矩形区域 (经度, 纬度)
  const boundary = {
       west: -120,   // 西边界经度
       south: 30,    // 南边界纬度
       east: -100,   // 东边界经度
       north: 50     // 北边界纬度
   };

获取相机位置并且限制经度在边界内

首先通过viewer.camera.position获取相机位置,然后和boundary 进行比较,看是否超出了范围,如果超出了范围就通过viewer.camera.setView将相机拉回范围内。

javascript 复制代码
  // 相机限制函数
 function constrainCameraToBoundary() {
      const position = viewer.camera.position;
      const cartographicPosition = Cesium.Cartographic.fromCartesian(position);
      
      // 获取当前相机位置的经纬度
      let longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
      let latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
      let height = cartographicPosition.height;
      let heading = viewer.camera.heading;
      let pitch = viewer.camera.pitch;
      let roll = viewer.camera.roll;

      // 限制经度在边界内
      if (longitude < boundary.west) {
          longitude = boundary.west;
      } else if (longitude > boundary.east) {
          longitude = boundary.east;
      }
      
      // 限制纬度在边界内
      if (latitude < boundary.south) {
          latitude = boundary.south;
      } else if (latitude > boundary.north) {
          latitude = boundary.north;
      }
      // 如果相机超出了边界,将其拉回边界内
      if (longitude !== Cesium.Math.toDegrees(cartographicPosition.longitude) || 
          latitude !== Cesium.Math.toDegrees(cartographicPosition.latitude)) {
          
          // 保持相机高度不变,只调整水平位置
          const newPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
          viewer.camera.setView({
              destination: newPosition,
              orientation:{
                    heading:0,
                 		 roll :  6.283185307179586,
                     pitch : viewer.camera.pitch,
              }
            }
          )
      }
  }

注册监听事件

将检测相机位置事件注册到场景每帧更新事件中。只要相机超出范围就被拉回之前的位置。

javascript 复制代码
 // 注册每帧更新事件
viewer.scene.postUpdate.addEventListener(constrainCameraToBoundary);

完整代码

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Cesium 相机矩形范围限制</title>
   <script src="http://support.supermap.com.cn:8090/webgl/Cesium/Build/Cesium/Cesium.js"> </script>
 <link href="http://support.supermap.com.cn:8090/webgl/Cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
 <style>
     body {
         margin: 0;
         padding: 0;
         overflow: hidden;
         font-family: Arial, sans-serif;
     }
     #cesiumContainer {
         position: absolute;
         width: 100%;
         height: 100%;
     }
     #infoPanel {
         position: absolute;
         top: 10px;
         left: 10px;
         background: rgba(0, 0, 0, 0.7);
         color: white;
         padding: 15px;
         border-radius: 5px;
         z-index: 100;
         max-width: 300px;
     }
     h1 {
         font-size: 18px;
         margin-top: 0;
     }
     .boundary-line {
         position: absolute;
         pointer-events: none;
     }
     button {
         background: #4CAF50;
         border: none;
         color: white;
         padding: 8px 15px;
         text-align: center;
         text-decoration: none;
         display: inline-block;
         font-size: 14px;
         margin: 4px 2px;
         cursor: pointer;
         border-radius: 4px;
     }
 </style>
</head>
<body>
 <div id="cesiumContainer"></div>
 <div id="infoPanel">
     <h1>相机矩形范围限制演示</h1>
     <p>相机被限制在蓝色矩形区域内移动。</p>
     <p>尝试拖动地图,观察相机如何被限制在边界内。</p>
     <div id="coordinates">
         <p>经度: <span id="longitude">--</span></p>
         <p>纬度: <span id="latitude">--</span></p>
         <p>高度: <span id="height">--</span> 米</p>
         <p>方位角: <span id="heading">--</span></p>
         <p>倾斜角: <span id="pitch">--</span></p>
         <p>旋转角: <span id="roll">--</span> 米</p>
     </div>
     <button id="resetView">重置视图</button>
 </div>

 <script>
     // 初始化Cesium视图
     const viewer = new Cesium.Viewer('cesiumContainer', {
         terrainProvider: Cesium.createWorldTerrain()
     });

     // 禁用默认的相机控制限制
     viewer.scene.screenSpaceCameraController.enableTranslate = true;
     viewer.scene.screenSpaceCameraController.enableLook = true;
     viewer.scene.screenSpaceCameraController.enableTilt = true;
     viewer.scene.screenSpaceCameraController.enableZoom = true;

     // 定义矩形区域 (经度, 纬度)
     const boundary = {
         west: -120,   // 西边界经度
         south: 30,    // 南边界纬度
         east: -100,   // 东边界经度
         north: 50     // 北边界纬度
     };

     // 计算矩形中心点
     const centerLongitude = (boundary.west + boundary.east) / 2;
     const centerLatitude = (boundary.south + boundary.north) / 2;

     // 设置初始相机位置到矩形中心
     viewer.camera.setView({
         destination: Cesium.Cartesian3.fromDegrees(centerLongitude, centerLatitude, 1500000) // 高度设为1500公里
     });

     // 绘制矩形边界
     const boundaryPolygon = viewer.entities.add({
         polygon: {
             hierarchy: [
                 new Cesium.Cartesian3.fromDegrees(boundary.west, boundary.south),
                 new Cesium.Cartesian3.fromDegrees(boundary.east, boundary.south),
                 new Cesium.Cartesian3.fromDegrees(boundary.east, boundary.north),
                 new Cesium.Cartesian3.fromDegrees(boundary.west, boundary.north)
             ],
             material: Cesium.Color.BLUE.withAlpha(0.2),
             outline: true,
             outlineColor: Cesium.Color.BLUE
         }
     });

     // 相机限制函数
     function constrainCameraToBoundary() {
         const position = viewer.camera.position;
         const cartographicPosition = Cesium.Cartographic.fromCartesian(position);
         
         // 获取当前相机位置的经纬度
         let longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
         let latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
         let height = cartographicPosition.height;
         let heading = viewer.camera.heading;
         let pitch = viewer.camera.pitch;
         let roll = viewer.camera.roll;
         
         // 更新UI显示
         document.getElementById('longitude').textContent = longitude.toFixed(4);
         document.getElementById('latitude').textContent = latitude.toFixed(4);
         document.getElementById('height').textContent = Math.round(height);
          document.getElementById('heading').textContent = heading.toFixed(4);
         document.getElementById('pitch').textContent = pitch.toFixed(4);
         document.getElementById('roll').textContent = Math.round(roll);
         // 限制经度在边界内
         if (longitude < boundary.west) {
             longitude = boundary.west;
         } else if (longitude > boundary.east) {
             longitude = boundary.east;
         }
         
         // 限制纬度在边界内
         if (latitude < boundary.south) {
             latitude = boundary.south;
         } else if (latitude > boundary.north) {
             latitude = boundary.north;
         }
         // 如果相机超出了边界,将其拉回边界内
         if (longitude !== Cesium.Math.toDegrees(cartographicPosition.longitude) || 
             latitude !== Cesium.Math.toDegrees(cartographicPosition.latitude)) {
             
             // 保持相机高度不变,只调整水平位置
             const newPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
             // viewer.camera.position = newPosition;
             // console.log(viewer.camera)
             // 保持相机方向不变
             // viewer.camera.direction = viewer.camera.direction.clone();
             // viewer.camera.up = viewer.camera.up.clone();
             // viewer.camera.heading = 0
             // viewer.camera.roll = 0
             viewer.camera.setView({
                 destination: newPosition,
                 orientation:{
                       heading:0,
                     roll :  6.283185307179586,
                        pitch : viewer.camera.pitch,
                 }
               
             }
                 
             )
         }
     }

     // 注册每帧更新事件
     viewer.scene.postUpdate.addEventListener(constrainCameraToBoundary);

     // 重置视图按钮
     document.getElementById('resetView').addEventListener('click', function() {
         viewer.camera.setView({
             destination: Cesium.Cartesian3.fromDegrees(centerLongitude, centerLatitude, 1500000)
         });
     });

     // 添加一些示例标记以增强可视化
     const markers = [
         {lon: boundary.west, lat: boundary.south, name: "西南角"},
         {lon: boundary.east, lat: boundary.south, name: "东南角"},
         {lon: boundary.east, lat: boundary.north, name: "东北角"},
         {lon: boundary.west, lat: boundary.north, name: "西北角"}
     ];

     markers.forEach(marker => {
         viewer.entities.add({
             position: Cesium.Cartesian3.fromDegrees(marker.lon, marker.lat),
             point: {
                 pixelSize: 10,
                 color: Cesium.Color.RED
             },
             label: {
                 text: marker.name,
                 font: '14px Helvetica',
                 fillColor: Cesium.Color.WHITE,
                 horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                 verticalOrigin: Cesium.VerticalOrigin.BOTTOM
             }
         });
     });
 </script>
</body>
</html>
相关推荐
胖咕噜的稞达鸭8 天前
如何写好一个skill
人工智能·数码相机
Hello-FPGA8 天前
CameraLink相机模拟器 信号源加速激光雷达系统算法开发
数码相机
双翌视觉8 天前
机器视觉系统为何离不开光学滤光片?
人工智能·数码相机·视觉检测·制造
探物 AI8 天前
【3D·感知】从PointNet到PointPillars:如何让自动驾驶汽车“实时“看见3D世界?
3d·自动驾驶·汽车
苏州邦恩精密8 天前
GOM三维扫描在制造中的真实价值:让“修模”从经验动作变成数据动作
人工智能·科技·机器学习·3d·自动化·制造
YHHLAI8 天前
CSS 3D 硬核解析:四个属性手写旋转立方体
前端·css·3d
3DVisionary8 天前
XTDIC-VG视频引伸计技术原理解析:金属疲劳测试的“非接触革命“
python·数码相机·音视频·非接触测量·xtdic-vg·视频引伸计·金属疲劳测试
云飞云共享云桌面9 天前
传统工作站 vs 云飞云共享云桌面:制造业设计云桌面选型深度对比
运维·服务器·前端·网络·3d·架构·制造
_李小白9 天前
【智能驾驶:视觉感知后处理 阅读笔记】Day4: 相机成像模型与畸变
笔记·数码相机
LONGZETECH9 天前
无人机仿真教学软件选型实战:5 个硬核技术维度,避开实训建设踩坑
3d·无人机·交互·cocos2d