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>
相关推荐
CG_MAGIC16 小时前
三渲二材质:Blender/SU生成动漫风格效果图
3d·blender·材质·贴图·效果图·渲云渲染
jiayong2319 小时前
数字孪生、虚拟仿真、3D建模 概念区别联系与技术清单
3d·3d建模·数字孪生·虚幻引擎
FrameNotWork20 小时前
HarmonyOS 6.1 云应用客户端适配实战(一):环境搭建与编译系统
数码相机·华为·harmonyos
lqqjuly1 天前
3D 视觉与点云处理(3D Vision & Point Cloud Processing)
人工智能·3d·目标跟踪
AI视觉网奇1 天前
免费3d资产下载网站
3d
蓝速科技1 天前
蓝速科技 3D 全息数字人舱:像真人一样的交互体验展示
科技·3d·交互
GlobalInfo1 天前
十五五规划开启,无人机RGB相机市场走向何方?2026-2032年市场前景深度分析
数码相机·无人机
谷谷地图下载器2 天前
全球、台湾省的无水印·街景数据(离线数据),专为可视化项目定制,支持国产化
javascript·c++·3d·arcgis·sqlite
福建佰胜张工2 天前
3DX-RAY 便携式 X 射线系统系列技术解析与应用指南
3d·智能手机·自动化