问题背景
在实际开发中,我们经常遇到这样的需求:加载一个地形数据后,希望视图自动定位到该地形覆盖的区域,以便用户直观地看到完整的地形数据。默认情况下,Cesium 并不提供这样的功能,加载地形后视图不会自动调整,用户需要手动寻找地形所在位置。
这个问题的关键在于:
CesiumTerrainProvider 默认不接收地形边界(bounds)参数
即使有了 bounds 信息,也缺少一个自动飞行到该区域的便捷方法
不同坐标系(EPSG:4326 和 EPSG:3857)的 bounds 处理方式不同
解决方案
我们的解决方案分为两个部分:
从地形元数据中获取 bopunds
修改 CesiumTerrainProvider 源码,使其支持传入地形的 bounds 参数
实现一个 flyTo 方法,方便地形加载后自动飞行到指定区域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const bounds = data.bounds if (!data.projection || data.projection === "EPSG:4326") { terrainProviderBuilder.tilingScheme = new GeographicTilingScheme({ numberOfLevelZeroTilesX: 2, numberOfLevelZeroTilesY: 1, ellipsoid: terrainProviderBuilder.ellipsoid, rectangle: bounds.length ? Rectangle.fromDegrees(...bounds) : Rectangle.MAX_VALUE }); } else if (data.projection === "EPSG:3857") { const epsg3857Bounds = Math.PI * terrainProviderBuilder.ellipsoid.maximumRadius; if(bounds[0] < -epsg3857Bounds) { bounds[0] = -epsg3857Bounds } if(bounds[1] < -epsg3857Bounds) { bounds[1] = -epsg3857Bounds } if(bounds[2] > epsg3857Bounds) { bounds[2] = epsg3857Bounds } if(bounds[3] > epsg3857Bounds) { bounds[3] = epsg3857Bounds } terrainProviderBuilder.tilingScheme = new WebMercatorTilingScheme({ numberOfLevelZeroTilesX: 1, numberOfLevelZeroTilesY: 1, ellipsoid: terrainProviderBuilder.ellipsoid, rectangleSouthwestInMeters: new Cartesian2( bounds[0], bounds[1] ), rectangleNortheastInMeters: new Cartesian2( bounds[2], bounds[3] ) }); }
|
1 2 3 4
| const layer = await Cesium.CesiumTerrainProvider.fromUrl( options.url, options );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
flyTo(options = {}) { const option = options; option.orientation = Cesium.defaultValue(options.orientation, { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-60), roll: Cesium.Math.toRadians(0), });
function webMercatorToLatLng(x, y) { const earthRadius = 6378137; const originShift = Math.PI * earthRadius;
const lon = (x / originShift) * 180.0; const lat = (180.0 / Math.PI) * (2.0 * Math.atan(Math.exp((y / originShift) * Math.PI)) - Math.PI / 2.0);
return { lon, lat }; }
if ( this._parameter.sourceType === "ctb" || this._parameter.sourceType === "arcgis" ) { const rectangle = this._primitive._tilingScheme.rectangle; const westDegrees = Cesium.Math.toDegrees(rectangle.west);
if (westDegrees <= 180 && westDegrees >= -180) { const lon = Cesium.Math.toDegrees( (rectangle.west + rectangle.east) / 2 ); const lat = Cesium.Math.toDegrees( (rectangle.south + rectangle.north) / 2 ); const height = options.height || 10000; option.destination = Cesium.defaultValue( options.destination, Cesium.Cartesian3.fromDegrees(lon, lat, height) ); } else { const min = webMercatorToLatLng(rectangle.west, rectangle.south); const max = webMercatorToLatLng(rectangle.east, rectangle.north); const lon = (min.lon + max.lon) / 2; const lat = (min.lat + max.lat) / 2; const height = options.height || 10000; option.destination = Cesium.defaultValue( options.destination, Cesium.Cartesian3.fromDegrees(lon, lat, height) ); } this._coreMap.camera.flyTo(option); } } }
|