1.鼠标点击添加点和线
handler.setInputAction(function (event) {
//返回地球表面上的点坐标
const earthPosition = positionUtil.cartesian2ToCartesian3(event.position)
if (Cesium.defined(earthPosition)) {
const ellipsoid = viewer.scene.globe.ellipsoid;
const cartographic = ellipsoid.cartesianToCartographic(earthPosition);
const lon = Cesium.Math.toDegrees(cartographic.longitude);
const lat = Cesium.Math.toDegrees(cartographic.latitude);
const alt = cartographic.height + 20 // 高度加20
AddPoint({
lon,
lat,
alt,
color: Cesium.Color.YELLOW
}, viewer);
positions.push(Cesium.Cartesian3.fromDegrees(lon, lat, alt))
positons2.push({x:lon,y:lat,z:alt})
if (positions.length >=2) {
if (!polyline)
polyline = AddPolyline(viewer)
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 添加点
const AddPoint = (params, viewer) => {
const entity:any = new Cesium.Entity({
id:`${params.lon}点`,
name: '点',
show: true,
position: Cesium.Cartesian3.fromDegrees(params.lon, params.lat, params.alt),
point: new Cesium.PointGraphics({
show: true, //是否展示
pixelSize: 10, //点大小
color: params.color
})
});
//收集Id
viewer.entities.add(entity);
return entity;
}
// 添加线
const AddPolyline = (viewer) => {
const entity = new Cesium.Entity({
id:`${new Date().getTime()}线`,
name: '线',
polyline: new Cesium.PolylineGraphics({
show: true,
clampToGround: false,
positions: new Cesium.CallbackProperty(function () {
return positions;
}, false),//params.positions,
width: 10,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.2,
color: Cesium.Color.BLUE
})
})
} as any);
viewer.entities.add(entity);
return entity;
}
2.右键结束绘制时,计算折线中心点和绘制方向;折线中心点可以替换为模型中心点,这样就会看着这个点飞行;计算方向主要是判断绘制的顺序是顺时针绘制的还是逆时针绘制的
handler.setInputAction(function (event) {
// 计算折线中心点
centerPos = caclPolyCenter(viewer)
isRight = _judgeDirection(positions)
// console.log(isRight, '逆时针');
if (handler) {
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
const caclPolyCenter = (viewer) => {
const entity = new Cesium.Entity({
name: "Red polygon on surface",
polygon:{
hierarchy: new Cesium.PolygonHierarchy(positions),
material: Cesium.Color.RED.withAlpha(0.5),
},
});
const polyPositions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
let polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center;//中心点
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
const cartographic = Cesium.Cartographic.fromCartesian(polyCenter, viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
const lat = Cesium.Math.toDegrees(cartographic.latitude);
const lng = Cesium.Math.toDegrees(cartographic.longitude);
const height = cartographic.height;
return {x: lng, y: lat, z: height}
}
/**
* 判断点的顺序是顺时针还是逆时针
* @param {Cesium.Cartesian3[]} points
*/
const _judgeDirection = (points) => {
const lonlat1 = Cesium.Cartographic.fromCartesian(points[0])
const lonlat2 = Cesium.Cartographic.fromCartesian(points[1])
const lonlat3 = Cesium.Cartographic.fromCartesian(points[2])
const x1 = lonlat1.longitude,
y1 = lonlat1.latitude,
x2 = lonlat2.longitude,
y2 = lonlat2.latitude,
x3 = lonlat3.longitude,
y3 = lonlat3.latitude,
dirRes = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2)
const isR = dirRes > 0
return isR
}
3.点击飞行
pitch默认为-5
setExtentTime: 在viewer的clock中设置时间间隔,flytime默认为15,也可以根据线段的长度来定义每一段的飞行时间
TimeExecution: 添加clock.onTick事件监听,每一帧调用,根据时间动态计算下一个点的坐标,然后更新相机位置.关于viewer.clock.onTick可查看api深入学习
changeCameraHeading: 如果当前的时间=设定的停止时间,移除监听并开始转向,当前时间=设定的停止时间时,开始下一段线的飞行marksIndex+1
const flyExtent = (viewer) => {
// 相机看点的角度,如果大于0那么则是从地底往上看,所以要为负值
const pitch = Cesium.Math.toRadians(pitchValue); // 默认-5
// 时间间隔15秒钟
setExtentTime(flytime, viewer);
Exection = function TimeExecution() {
let preIndex = marksIndex - 1;
if (marksIndex === 0) {
preIndex = positons2.length - 1;
}
//计算俯仰角
// 当前已经过去的时间,单位s
const delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);
const originLat = marksIndex == 0 ? positons2[positons2.length - 1].y : positons2[marksIndex - 1].y;
const originLng = marksIndex == 0 ? positons2[positons2.length - 1].x : positons2[marksIndex - 1].x;
const endPosition = Cesium.Cartesian3.fromDegrees(
(originLng + (positons2[marksIndex].x - originLng) / flytime * delTime),
(originLat + (positons2[marksIndex].y - originLat) / flytime * delTime),
positons2[marksIndex].z
);
let heading = bearing(originLat,originLng, centerPos.y, centerPos.x);
// let heading = bearing(centerPos.y, centerPos.x, originLat,originLng);
heading = Cesium.Math.toRadians(heading);
viewer.scene.camera.setView({
destination: endPosition,
orientation: {
heading: heading,
pitch: pitch,
}
});
if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {
viewer.clock.onTick.removeEventListener(Exection);
//有个转向的功能
changeCameraHeading(viewer);
}
};
viewer.clock.onTick.addEventListener(Exection);
}
// 相机原地定点转向
const changeCameraHeading = (viewer) => {
// 计算两点之间的方向
let nextIndex = marksIndex + 1;
if (marksIndex == positons2.length - 1) {
nextIndex = 0;
}
const heading = bearing(positons2[marksIndex].y, positons2[marksIndex].x,centerPos.y, centerPos.x );
console.log(heading, Cesium.Math.toDegrees(viewer.camera.heading));
// 相机看点的角度,如果大于0那么则是从地底往上看,所以要为负值
const pitch = Cesium.Math.toRadians(pitchValue);
// 给定飞行一周所需时间,比如10s, 那么每秒转动度数
// 相机是始终往角度小的方向转, 先确定绘制的线路是顺时针还是逆时针_judgeDirection
// 判断当前camera.heading > 两点的heading
// 如果大于,则360 - camera.heading + heading, eg: 360-340+20
//这部分主要解决的是相机朝哪个方向转的问题,顺时针绘制的线和逆时针绘制的线转向不同,所以有了下面这些判断,或许还有更好的办法处理
let angle = 0
if (!isRight) { // 顺时针
if (Cesium.Math.toDegrees(viewer.camera.heading) > heading) {
angle = ((Cesium.Math.toDegrees(Cesium.Math.TWO_PI - viewer.camera.heading) + heading) / changeCameraTime);
} else {
angle = ((heading - Cesium.Math.toDegrees(viewer.camera.heading)) / changeCameraTime);
}
} else {
if (Cesium.Math.toDegrees(viewer.camera.heading) < heading) {
angle = ((Cesium.Math.toDegrees(Cesium.Math.toRadians(heading) - Cesium.Math.TWO_PI) - Cesium.Math.toDegrees(viewer.camera.heading)) / changeCameraTime);
} else {
angle = ((heading - Cesium.Math.toDegrees(viewer.camera.heading)) / changeCameraTime);
}
// angle = ((heading - Cesium.Math.toDegrees(viewer.camera.heading)) / changeCameraTime);
}
// const angle = ((heading - Cesium.Math.toDegrees(viewer.camera.heading)) / changeCameraTime);
console.log(angle, 'angle');
// 时间间隔2秒钟
setExtentTime(changeCameraTime, viewer);
// 相机的当前heading
const initialHeading = viewer.camera.heading;
Exection = function TimeExecution() {
// 当前已经过去的时间,单位s
const delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);
// console.log(Cesium.Math.toRadians(delTime * angle), '1s转动的角度');
const heading = Cesium.Math.toRadians(delTime * angle) + initialHeading;
viewer.scene.camera.setView({
orientation: {
heading: heading,
pitch: pitch
}
});
if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {
viewer.clock.onTick.removeEventListener(Exection);
marksIndex = ++marksIndex >= positons2.length ? 0 : marksIndex;
if (marksIndex != 0) {
console.log(marksIndex, 'marksIndex');
flyExtent(viewer);
}
}
};
viewer.clock.onTick.addEventListener(Exection);
}
//计算俯仰角
const bearing = (startLat, startLng, destLat, destLng) => {
startLat = toRadians(startLat);
startLng = toRadians(startLng);
destLat = toRadians(destLat);
destLng = toRadians(destLng);
const y = Math.sin(destLng - startLng) * Math.cos(destLat);
const x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
const brng = Math.atan2(y, x);
const brngDgr = toDegrees(brng);
return (brngDgr + 360) % 360;
}
// 角度转弧度
const toRadians = (degrees) => {
return degrees * Math.PI / 180;
}
// 弧度转角度
const toDegrees = (radians) => {
return radians * 180 / Math.PI;
}
desktop 2023-04-03 11-55-18
参考大佬文章: https://blog.csdn.net/CFXXXL/article/details/125365952