最近遇到一个需求,当加载gpx后,需要将视角定位到实体的合适位置。刚开始想这不是很简单嘛,直接使用zoomTo flyTo 跳转到Gpx实体就好了,于是开始了尝试。
尝试后发现了问题,当gpx范围很小时,使用zoomTo flyTo等定位过去后视角离实体很远,看不清细节,这就不是我们能够接受的,于是就开始寻找替代方法。

于是我想到了使用BoundingSphere手动创建一个边界球,再定位到这个边界球,这样就变相解决了定位的问题,并且viewer.camera.flyToBoundingSphere定位时还会自动调整合适的俯仰角。
BoundingSphere 需要两个参数 一个是坐标点,一个是球半径。坐标点我们可以根据gpx实体中的polyline中获取,然后计算其最大外边界矩形,再计算矩形的中心点,及其最大最远两个点的距离(球直径)。如此我们就获取到两个核心参数了。
这里直接上代码。
这里用的turf
npm i @turf/turf
/**
* 根据几何图形计算中心点 最大矩形及其半径
*
* @export
* @param {*} geometry
* @return {{
* center?:[number,number],
* radius?:number
* }}
*/
function getCenterAndRadius(geometry) {
// 计算边界
const bbox = turf.bbox(geometry);
// 计算最大边界矩形
const squared = turf.square(bbox);
// 计算半径
const features = turf.points([
[squared[0], squared[1]],
[squared[2], squared[3]],
]);
const center = turf.center(features);
const from = turf.point([squared[0], squared[1]]);
const to = turf.point([squared[2], squared[3]]);
const options = { units: 'kilometers' };//公里
const distance = turf.distance(from, to, options);
return {
// 这里÷2是因为计算的值为直径,而边界球需要半径
radius: distance * 1000 / 2,
center: center.geometry?.coordinates,
};
}
/**
*
* 自定义 flyToPoint
* @export
* @param {Viewer} viewer 地图实例
* @param {{
* longitude: number;
* latitude: number;
* radius?: number;
* complete:Function;
* }} option 配置项
* @param {*} option.longitude
* @param {*} option.latitude
* @param {Function} option.complete 飞行结束后执行的回调
* @param {number} option.height 球高度
* @param {number} [option.radius=1000] 值越大 看到范围越大。反之缩小
*/
export function customFlyToPoint(viewer = map, option) {
if (!viewer) return;
const { longitude, latitude, radius = 1000, complete } = option;
viewer.camera.flyToBoundingSphere(
// 边界盒
new Cesium.BoundingSphere(
Cesium.Cartesian3.fromDegrees(longitude, latitude, Math.abs(getHeightFast(viewer, longitude, latitude))),
radius,
),
{
duration: 4, // 飞行时间(秒)
// pitchAdjustHeight: 4000,
complete: complete,
},
);
// 这里可以查看边界盒 中心点
// viewer.entities.add({
// position: Cesium.Cartesian3.fromDegrees(longitude, latitude, Math.abs(getHeightFast(viewer, longitude, latitude))),
// point: {
// pixelSize: 50,
// color: Cesium.Color.fromCssColorString('#ff0'),
// outline: true,
// outlineColor: Cesium.Color.fromCssColorString('#000'),
// outlineWidth: 2,
// },
// });
}
viewer.dataSources
.add(
Cesium.GpxDataSource.load('../SampleData/gpx/route.gpx', {
clampToGround: true,
}),
)
.then(function (dataSource) {
let positions = [];
// 设置所有 polyline 的宽度
dataSource.entities.values.forEach((entity) => {
if (entity.polyline && entity.polyline.positions) {
entity.polyline.width = 10;
const polylinePositions = entity.polyline.positions.getValue(Cesium.JulianDate.now());
if (polylinePositions && Array.isArray(polylinePositions)) {
positions.push(...polylinePositions);
}
}
});
// Cesium.Cartesian3 转为 经纬度
positions = positions.map((item) => {
const ellipsoid = Cesium.Ellipsoid.WGS84; // 使用 WGS84 椭球体模型
const cartographic = ellipsoid.cartesianToCartographic(item);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = Cesium.Math.toDegrees(cartographic.height);
return [longitude, latitude, height];
});
const line = turf.lineString(arr2);
const { center, radius } = getCenterAndRadius(line);
customFlyToPoint(map, {
latitude: center[1],
longitude: center[0],
radius: radius,
complete: () => {
// 定位完成后回调
},
});
});
到这里就基本完成了,但是 我在测试的时候发现在计算高度时,高度的值十分奇怪,致使该效果未达到我预期,变得时灵时不灵。
function getHeightFast(viewer, longitude, latitude) {
const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude);
const height = viewer.scene.globe.getHeight(cartographic);
return height;
}
这个方法根据当前地形拾取而来。我就有了一点猜想,可能是因为在gpx加载时,此时视角并不在gpx实体的区域,此时gpx实体区域的地形也未加载,我通过这个方法获取高度,就给了我一个不正确的值。于是我将代码进行了细微的修改
下面是更改的部分
......
// 前面代码未改动
viewer.dataSources
.add(
Cesium.GpxDataSource.load('你的gpx文件地址', {
clampToGround: true,
}),
)
.then(function (dataSource) {
let positions = [];
// 设置所有 polyline 的宽度
dataSource.entities.values.forEach((entity) => {
if (entity.polyline && entity.polyline.positions) {
entity.polyline.width = 10;
// 获取gpx所有点位
const polylinePositions = entity.polyline.positions.getValue(Cesium.JulianDate.now());
if (polylinePositions && Array.isArray(polylinePositions)) {
positions.push(...polylinePositions);
}
}
});
// Cesium.Cartesian3 转为 经纬度
positions = positions.map((item) => {
const ellipsoid = Cesium.Ellipsoid.WGS84; // 使用 WGS84 椭球体模型
const cartographic = ellipsoid.cartesianToCartographic(item);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = Cesium.Math.toDegrees(cartographic.height);
return [longitude, latitude, height];
});
const line = turf.lineString(arr2);
const { center, radius } = getCenterAndRadius(line);
// 这里先跳转至gpx实体,加载地形再调用方法从地形上获取高度
viewer.zoomTo(dataSource.entities).then(() => {
// 检查是否收集到有效位置
if (positions.length === 0) {
console.error('No valid positions found in entities');
return;
}
customFlyToPoint(map, {
latitude: center[1],
longitude: center[0],
radius: radius,
complete: () => {
// 定位完成后回调
},
});
});
});
完美解决

2672

被折叠的 条评论
为什么被折叠?



