cesium修改CesiumTerrainProvider源码

问题背景
在实际开发中,我们经常遇到这样的需求:加载一个地形数据后,希望视图自动定位到该地形覆盖的区域,以便用户直观地看到完整的地形数据。默认情况下,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
/**
* 地形飞跃
* @method
* @param {Object} options 构造参数
* @param {Object} options.orientation 飞行到达地形的视角角度
* @param {Number} options.orientation.heading 航向
* @param {Number} options.orientation.pitch 俯仰
* @param {Number} options.orientation.roll 滚动
* @param {Number} options.height 飞行到达地形的视角高度 (默认10000米)
* @param {Number} options.duration 以秒为单位的飞行持续时间
* @param {Function} options.complete 飞行完成时执行的函数
*/
flyTo(options = {}) {
//const that = this;
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"
) {
//'EPSG:4326'
const rectangle = this._primitive._tilingScheme.rectangle;
const westDegrees = Cesium.Math.toDegrees(rectangle.west);
// const westDegrees2 = Cesium.Math.toDegrees(rectangle.east);
// const westDegrees3 = Cesium.Math.toDegrees(rectangle.north);
// const westDegrees4 = Cesium.Math.toDegrees(rectangle.south);
// console.log(
// "westDegrees",
// westDegrees,
// westDegrees2,
// westDegrees3,
// westDegrees4
// );

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);
}
}
}

作者

wangyawei

发布于

2025-03-21

更新于

2025-03-21

许可协议