You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在ol-cesium中设置3D地图范围?2D范围正常3D无效

解决Ol-Cesium 3D视图下范围限制失效的问题

我明白你遇到的困扰——OpenLayers视图的extent参数在2D模式下正常工作,但切换到Cesium 3D模式后就失去了作用。这是因为Ol-Cesium并不会自动将OpenLayers的视图限制同步到Cesium的相机系统中,我们需要手动为Cesium相机添加范围约束。

下面是具体的解决方案,我会一步步说明并给出修改后的完整代码:

核心思路

Cesium的相机系统有自己的边界控制机制,我们需要:

  1. 将OpenLayers使用的EPSG:3857坐标系的范围转换为Cesium支持的WGS84(经纬度)坐标系
  2. 创建Cesium的Rectangle对象来定义允许的相机活动区域
  3. 启用Cesium相机的范围约束,确保相机无法超出指定区域

修改后的完整代码

<!DOCTYPE html>
<html>
<head>
<title>Ol-Cesium base example</title>
<link rel="stylesheet" href="http://www.3daysofprogramming.com/playground/pg.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL,Map,Set,Promise"></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/css/ol.css">
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/build/ol.js"></script>
<script src="https://openlayers.org/ol-cesium/node_modules/@camptocamp/cesium/Build/Cesium/Cesium.js"></script>
<script src="https://openlayers.org/ol-cesium/olcesium.js"></script>
<style>
/**
* Create a position for the map
* on the page
*/
#map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="container" class="main">
<div id="map"></div>
</div>
<button id="toggle">Toggle Cesium</button>
<script>
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [raster],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
extent: [-572513.341856, 5211017.966314, 916327.095083, 6636950.728974],
zoom: 8
}),
});
var ol3d = new olcs.OLCesium({map: map, }); // map is the ol.Map instance
var show = true;
ol3d.setEnabled(show);

// 新增:为Cesium相机添加范围约束
const setupCesiumExtentConstraint = () => {
  const scene = ol3d.getCesiumScene();
  if (!scene) return;
  
  const camera = scene.camera;
  const screenSpaceCameraController = scene.screenSpaceCameraController;
  
  // 获取OpenLayers视图的extent
  const olExtent = map.getView().getExtent();
  // 将EPSG:3857坐标转换为WGS84经纬度
  const bottomLeft = ol.proj.transform([olExtent[0], olExtent[1]], 'EPSG:3857', 'EPSG:4326');
  const topRight = ol.proj.transform([olExtent[2], olExtent[3]], 'EPSG:3857', 'EPSG:4326');
  
  // 创建Cesium的Rectangle范围
  const cesiumExtent = Cesium.Rectangle.fromDegrees(
    bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]
  );
  
  // 限制相机只能在指定范围内移动
  camera.constrainToRectangle(cesiumExtent);
  // 启用碰撞检测,避免相机穿入地形
  screenSpaceCameraController.enableCollisionDetection = true;
  
  // 可选:同步OpenLayers的缩放级别限制到Cesium
  const olView = map.getView();
  const minZoom = olView.getMinZoom();
  const maxZoom = olView.getMaxZoom();
  
  if (minZoom !== undefined) {
    screenSpaceCameraController.maximumZoomDistance = calculateCameraDistance(minZoom);
  }
  if (maxZoom !== undefined) {
    screenSpaceCameraController.minimumZoomDistance = calculateCameraDistance(maxZoom);
  }
};

// 辅助函数:根据OpenLayers的zoom级别计算Cesium相机的距离
const calculateCameraDistance = (zoom) => {
  const metersPerUnit = ol.proj.get('EPSG:3857').getMetersPerUnit();
  const metersPerPixel = metersPerUnit / Math.pow(2, zoom + 8);
  const viewportHeight = map.getSize()[1];
  const fovyRadians = Cesium.Math.toRadians(ol3d.getCesiumScene().camera.frustum.fovy * 0.5);
  
  return metersPerPixel * viewportHeight * 0.5 / Math.tan(fovyRadians);
};

// 初始化时设置约束
setupCesiumExtentConstraint();

var btn = document.querySelector('#toggle');
btn.addEventListener('click', function () {
  show = !show;
  ol3d.setEnabled(show);
  // 切换到3D后重新确认约束(可选,确保状态同步)
  if (show) {
    setupCesiumExtentConstraint();
  }
});
</script>
</body>
</html>

代码说明

  • setupCesiumExtentConstraint函数:负责将OpenLayers的范围转换并应用到Cesium相机上
  • camera.constrainToRectangle:这是核心方法,强制相机只能在指定的矩形区域内移动
  • 坐标转换:因为OpenLayers默认用的是Web墨卡托(EPSG:3857),而Cesium基于WGS84经纬度,所以必须进行坐标转换
  • 可选的缩放同步:让Cesium的缩放限制和OpenLayers保持一致,提供更统一的体验

现在你切换到3D模式后,相机就无法超出你设定的范围了,和2D模式的表现一致。

内容的提问来源于stack exchange,提问作者Niru

火山引擎 最新活动