Appearance
可视域分析
ts
import { Viewer, VectorTileImageryProvider, ViewShed3D } from "joDVF";
import { Pane } from "tweakpane";
import {
Cartesian3,
Color,
Cartographic,
SceneMode,
RectangleGraphics,
SampledPositionProperty,
Math as CesiumMath,
Cesium3DTileset,
HeadingPitchRange,
JulianDate,
ClockRange,
Transforms,
HeadingPitchRoll,
ShadowMode,
TimeIntervalCollection,
TimeInterval,
VelocityOrientationProperty,
PolylineGlowMaterialProperty
} from "joCesium";
import { vi } from "element-plus/es/locales.mjs";
const { mapContainer } = createContainer();
const viewer = new Viewer(mapContainer, {
sceneOptions: SceneMode.SCENE3D
});
const scene = viewer.scene;
const camera = viewer.camera;
const dataBaseURL =
"https://www.southsmart.com/w3d-data/modeldata/shaoguanshuju/shaoguantestbaimo/tileset.json";
const tileset = await Cesium3DTileset.fromUrl(dataBaseURL);
scene.primitives.add(tileset);
// Set the camera to view the newly added tileset
const boundingSphere = tileset.boundingSphere;
const radius = boundingSphere.radius;
viewer.zoomTo(tileset, new HeadingPitchRange(-1.2, -0.6, radius * 4.0));
scene.globe.depthTestAgainstTerrain = true;
let shed3d;
let gltfModel, pathEntity;
const start = JulianDate.fromDate(new Date(2024, 2, 25, 16));
const stop = JulianDate.addSeconds(start, 16, new JulianDate());
const clock = viewer.clock;
// Make sure viewer is at the desired time.
clock.startTime = start.clone();
clock.stopTime = stop.clone();
clock.currentTime = start.clone();
clock.clockRange = ClockRange.LOOP_STOP; //Loop at the end
clock.multiplier = 0;
const flyPositions = [
[113.583671, 24.775492],
[113.584071, 24.776239],
[113.584772, 24.777343],
[113.584772, 24.777343],
[113.584846, 24.778412],
[113.58532, 24.779481],
[113.58599, 24.780662],
[113.58626, 24.781563],
[113.586955, 24.782855],
[113.587077, 24.783909],
[113.589087, 24.784334],
[113.58868, 24.78513],
[113.587913, 24.786173],
[113.587209, 24.786362],
[113.586346, 24.786333],
[113.585409, 24.784744]
];
addModel();
addPath();
let radar;
function addModel() {
// 模型加载 by entity
gltfModel = viewer.entities.add({
orientation: Transforms.headingPitchRollQuaternion(
new Cartesian3.fromDegrees(117.081376, 31.655497, 150.8386),
new HeadingPitchRoll(
CesiumMath.toRadians(50), // 顺时针旋转的角度值
CesiumMath.toRadians(0),
CesiumMath.toRadians(0)
)
),
position: Cartesian3.fromDegrees(113.583671, 24.775492, 287.8386),
model: {
uri: "./assets/model/glb/15-0.glb", //模型的地址
scale: 0.05, //模型缩放比例
// minimumPixelSize: 128, // 最小像素大小
// maximumScale: 1, // 模型的最大比例尺大小。 minimumPixelSize 的上限
incrementallyLoadTextures: true, // 加载模型后纹理是否可以继续流入
runAnimations: true, // 是否应启动模型中指定的 glTF 动画
clampAnimations: true, // 指定 glTF 动画是否应在没有关键帧的持续时间内保持最后一个姿势
eyeOffset: new Cartesian3(0, 0, -10000), // 设置模型的可见度
disableDepthTestDistance: Number.POSITIVE_INFINITY,
// 指定模型是否投射或接收来自光源的阴影 type:ShadowMode
// DISABLED 对象不投射或接收阴影; ENABLED 对象投射并接收阴影; CAST_ONLY 对象仅投射阴影; RECEIVE_ONLY 对象仅接收阴影
shadows: ShadowMode.DISABLED,
show: true
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 设置模型贴地,
}
});
}
function addPath() {
const position = computeClularFlight();
pathEntity = viewer.entities.add({
availability: new TimeIntervalCollection([
new TimeInterval({
start: start,
stop: stop
})
]),
// Use our computed positions
position,
// Automatically compute orientation based on position movement.
orientation: new VelocityOrientationProperty(position),
// Show the path as a pink line sampled in 1 second increments.
path: {
resolution: 1,
material: new PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Color.YELLOW
}),
width: 10
}
});
// pathEntity.position.setInterpolationOptions({
// interpolationDegree: 2,
// interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
// });
}
// Generate a random circular pattern with varying heights.
function computeClularFlight() {
const property = new SampledPositionProperty();
for (let i = 0; i < flyPositions.length; i++) {
const radians = CesiumMath.toRadians(i);
const time = JulianDate.addSeconds(start, i, new JulianDate());
const position = Cartesian3.fromDegrees(
flyPositions[i][0],
flyPositions[i][1],
287.8386
);
property.addSample(time, position);
// Also create a point for each sample we generate.
viewer.entities.add({
position: position,
point: {
pixelSize: 8,
color: Color.TRANSPARENT,
outlineColor: Color.YELLOW,
outlineWidth: 3
}
});
}
return property;
}
addUI();
function addUI() {
const pane = new Pane({
title: "操作"
});
const startBtn = pane.addButton({ title: "开始" });
startBtn.on("click", () => {
if (shed3d) shed3d = shed3d.destroy();
if (!shed3d) shed3d = new ViewShed3D(viewer);
// viewer.scene.primitives.add(shed3d)
scene.primitives.add(shed3d);
shed3d.start();
});
const clearBtn = pane.addButton({ title: "清除" });
clearBtn.on("click", () => {
if (shed3d) shed3d = shed3d.destroy();
});
const flyBtn = pane.addButton({ title: "开始飞行" });
flyBtn.on("click", () => {
const position = computeClularFlight();
clock.multiplier = 0.3;
gltfModel.position = position;
gltfModel.orientation = new VelocityOrientationProperty(position);
pathEntity.position = position;
pathEntity.orientation = new VelocityOrientationProperty(position);
clock.onTick.addEventListener((clock) => {
const cartesian = position.getValue(clock.currentTime);
if (cartesian) {
const cartographic = Cartographic.fromCartesian(cartesian);
// if (shed3d) shed3d = shed3d.destroy();
if (shed3d) {
// shed3d = shed3d.destroy();
const currentPosition = [
CesiumMath.toDegrees(cartographic.longitude),
CesiumMath.toDegrees(cartographic.latitude),
0
];
shed3d.viewer?.scene.postProcessStages.remove(shed3d.postProcess);
shed3d.primitiveCollection.removeAll();
// viewer.scene.primitives.add(shed3d);
shed3d.cameraPosition = cartesian;
shed3d.viewPosition = Cartesian3.fromDegrees(...currentPosition);
shed3d._addToScene();
// shed3d.addRadar(shed3d.cameraPosition, shed3d.frustumQuaternion);
if (radar) {
// viewer.entities.remove(radar);
radar.position = shed3d.cameraPosition;
// @ts-ignore
radar.orientation = shed3d.frustumQuaternion;
} else {
const rectangularSensor = new RectangleGraphics({
radius: shed3d.distance,
xHalfAngle: CesiumMath.toRadians(shed3d.horizontalAngle / 2),
yHalfAngle: CesiumMath.toRadians(shed3d.verticalAngle / 2),
material: new Color(0, 1, 1, 0.4),
lineColor: new Color(1, 1, 1, 1),
slice: 8,
showScanPlane: false,
scanPlaneColor: new Color(0, 1, 1, 1),
scanPlaneMode: "vertical",
scanPlaneRate: 3,
showThroughEllipsoid: false,
showLateralSurfaces: false,
showDomeSurfaces: false
});
radar = viewer.entities.add({
position: shed3d.cameraPosition,
// @ts-ignore
orientation: shed3d.frustumQuaternion,
show: true,
// @ts-ignore
rectangularSensor
});
}
}
if (!shed3d) {
shed3d = new ViewShed3D(viewer, {
isOutputTexture: true
});
scene.primitives.add(shed3d);
}
// 进行录制视频
if (shed3d._reflectionImageData) {
const canvas = document.getElementById(
"canvas-cap"
) as HTMLCanvasElement;
const ctx = canvas.getContext("2d");
canvas.width = shed3d._reflectionImageData.width;
canvas.height = shed3d._reflectionImageData.height;
ctx?.putImageData(shed3d._reflectionImageData, 0, 0);
}
// const currentPosition = [
// Cesium.Math.toDegrees(cartographic.longitude),
// Cesium.Math.toDegrees(cartographic.latitude),
// 0,
// ];
// // viewer.scene.primitives.add(shed3d);
// shed3d.cameraPosition = cartesian;
// shed3d.viewPosition = Cesium.Cartesian3.fromDegrees(
// ...currentPosition,
// );
// shed3d._addToScene();
// shed3d.addRadar(shed3d.cameraPosition, shed3d.frustumQuaternion);
}
});
});
}
function createContainer() {
const container = document.createElement("div");
container.style.width = "100%";
container.style.height = "100%";
const uiContainer = document.createElement("div");
uiContainer.style.position = "fixed";
uiContainer.style.top = "5px";
uiContainer.style.left = "5px";
const video = document.createElement("canvas");
video.id = "canvas-cap";
video.style.position = "absolute";
video.style.bottom = "10px";
video.style.left = "10px";
video.style.height = "360px";
video.style.width = "640px";
video.style.zIndex = "20000";
video.style.background = "#fff";
document.body.appendChild(container);
document.body.appendChild(uiContainer);
document.body.appendChild(video);
return {
mapContainer: container,
uiContainer
};
}
