<template>
|
<div class="cesium-example">
|
<!-- 主图 cesium 容器 -->
|
<div ref="cesiumContainer" id="cesiumContainer"></div>
|
<!-- 相机视角容器 -->
|
<div ref="cesiumCamera" id="cesiumCamera"></div>
|
<div class="zoom-ui-container" @wheel="handleWheel">
|
<!-- 中间黄色准星框 -->
|
<div
|
class="reticle"
|
:style="{ width: boxWidth + 'px', height: boxHeight + 'px' }"
|
></div>
|
|
<!-- 使用 ElementUI 的 Slider 替代变倍轴 -->
|
<div class="zoom-bar-slider">
|
<el-slider
|
v-model="zoomRatio"
|
vertical
|
height="200px"
|
:min="minZoom"
|
:max="maxZoom"
|
:step="0.1"
|
:format-tooltip="formatTooltip"
|
:marks="marks"
|
@input="onSliderInput"
|
/>
|
</div>
|
|
<!-- 左下角倍率信息 -->
|
<div class="zoom-info">
|
<el-tag type="success">倍率: {{ zoomRatio.toFixed(1) }}X</el-tag>
|
</div>
|
</div>
|
|
|
|
<!-- botton -->
|
<div class="cesiumBotton">
|
<div class="cesiumButtonGroup">
|
<el-button id="groundPoi" @click="addGroundPoi()" type="primary">地面点</el-button>
|
<el-input v-model="mergeNumber" placeholder="请输入合并范围" type="number" style="width: 150px;"></el-input>
|
<el-button @click="mergePoint()" type="success">合并</el-button>
|
</div>
|
<!-- <div class="key-container">
|
<div class="arrow-row">
|
<div
|
class="arrow"
|
:class="{ active: activeKey === 'w' }"
|
>W</div>
|
</div>
|
<div class="key-row">
|
<div
|
class="key"
|
:class="{ active: activeKey === 'a' }"
|
@mousedown="handleClick('a')"
|
>A</div>
|
<div
|
class="key"
|
:class="{ active: activeKey === 's' }"
|
@mousedown="handleClick('s')"
|
>S</div>
|
<div
|
class="key"
|
:class="{ active: activeKey === 'd' }"
|
@mousedown="handleClick('d')"
|
>D</div>
|
</div>
|
</div>
|
<div class="key-container">
|
<div class="arrow-row">
|
<div
|
class="arrow"
|
:class="{ active: activeKey === 'ArrowUp' }"
|
@mousedown="handleClick('ArrowUp')"
|
>↑</div>
|
</div>
|
<div class="key-row">
|
<div
|
class="key"
|
:class="{ active: activeKey === 'ArrowLeft' }"
|
@mousedown="handleClick('ArrowLeft')"
|
>←</div>
|
<div
|
class="key"
|
:class="{ active: activeKey === 'ArrowDown' }"
|
@mousedown="handleClick('ArrowDown')"
|
>↓</div>
|
<div
|
class="key"
|
:class="{ active: activeKey === 'ArrowRight' }"
|
@mousedown="handleClick('ArrowRight')"
|
>→</div>
|
</div>
|
</div> -->
|
</div>
|
|
<!-- 动态坐标、动态相机参数显示 -->
|
<!-- <div id="moveResult" class="moveResult">
|
<div id="moveResultCon" class="moveResultCon"></div>
|
<div class="box_lt"></div><div class="box_lb"></div><div class="box_rt"></div><div class="box_rb"></div>
|
</div> -->
|
<div id="cammoveResult" class="cammoveResult">
|
<div id="cammoveResultCon" class="cammoveResultCon"></div>
|
<div class="box_lt"></div><div class="box_lb"></div><div class="box_rt"></div><div class="box_rb"></div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
let cameraPostion;
|
let cameraHeading;
|
let cameraPitch;
|
let cameraRoll;
|
let viewerM;
|
let viewerC;
|
let counter = 0;
|
let globalon = 0
|
let globalat = 0
|
let pois = [globalon, globalat,0]
|
let cameraVideo
|
let ScopeElement; //光轴、视频
|
let preVideoScopePrimitiveArrTie = [];
|
let scratchSetViewMatrix3 = new Cesium.Matrix3();
|
let chooseId
|
// let pois = [0, 0,0]
|
let holdingPosition;
|
let rightKeyMove = false
|
let distance = 100;
|
export default {
|
props: {
|
towerUrl: {
|
type: String,
|
default() {
|
return ''
|
}
|
},
|
},
|
data(){
|
return{
|
moveSpeed: 1,
|
speedMultiplier: 5,
|
slowMultiplier: 0.2,
|
keyState: {
|
w: false,
|
s: false,
|
a: false,
|
d: false,
|
q: false,
|
e: false,
|
up: false,
|
down: false,
|
left: false,
|
right: false,
|
shift: false,
|
ctrl: false,
|
space: false,
|
},
|
animationFrameId: null,
|
mergeNumber:null,
|
activeKey: null,
|
timer: null,
|
zoomRatio: 1,
|
minZoom: 1,
|
maxZoom: 56,
|
baseBoxSize: 400,
|
marks: {
|
1: '1X',
|
7: '7X',
|
14: '14X',
|
28: '28X',
|
56: '56X'
|
}
|
}
|
},
|
mounted(){
|
this.initCesium();
|
},
|
computed: {
|
boxWidth() {
|
return this.baseBoxSize / this.zoomRatio;
|
},
|
boxHeight() {
|
return (this.baseBoxSize * 0.75) / this.zoomRatio; // 高度保持 4:3 比例
|
}
|
},
|
beforeDestroy() {
|
counter = 0
|
this.removeKeyboardEvents();
|
if (this.animationFrameId) cancelAnimationFrame(this.animationFrameId);
|
window.removeEventListener("keydown", this.handleKeydown);
|
if (viewerC) viewerC.destroy();
|
},
|
methods:{
|
initCesium() {
|
viewerM = this.initViewer(this.$refs.cesiumContainer.id);
|
viewerC = this.initViewer(this.$refs.cesiumCamera.id);
|
window.viewerM = viewerM;
|
window.viewerC = viewerC;
|
// 💡 在这里继续调用 measure.js / camera.js / botton.js 等逻辑
|
// 例如:window.registerMeasureTools(viewerM)
|
setTimeout(()=>{
|
this.loadModel()
|
this.addSyncListener()
|
this.registerKeyboardEvents();
|
let control = viewerC.scene.screenSpaceCameraController;
|
control.enableRotate = false;
|
control.enableTranslate = false;
|
control.enableZoom = false;
|
control.enableTilt = false;
|
control.enableLook = false;
|
viewerC.scene.camera.percentageChanged = 0.05
|
this.cammove_measure_point();
|
},1000)
|
},
|
|
/**
|
* groundPoi 按钮点击处理
|
*/
|
handleGroundPoi() {
|
if (typeof window.handleGroundPoi === "function") {
|
window.handleGroundPoi(viewerM, viewerC);
|
}
|
// 其他自定义逻辑...
|
},
|
initViewer(cesiumId){
|
//cesium全球30米分辨率地形资源token
|
let imageryProvider1 = new Cesium.TileMapServiceImageryProvider({
|
url: `https://192.168.1.2:9999/daqing/dom-sea/`,
|
});
|
let viewer = new Cesium.Viewer(cesiumId, {
|
fullscreenButton: false,
|
homeButton: false,
|
timeline: false,
|
geocoder: false,
|
scene3DOnly: true,
|
sceneModePicker: false,
|
baseLayerPicker: false,
|
shouldAnimate: false,
|
navigationHelpButton: false,
|
navigationInstructionsInitiallyVisible: false,
|
imageryProvider: imageryProvider1,
|
// imageryProvider: false,
|
shadows: false,
|
animation: false,
|
infoBox: false,
|
timeline: false,
|
selectionIndicator: false ,
|
fullscreenButton: false,
|
vrButton: false,
|
});
|
// 关闭天空盒效果
|
// viewer.scene.skyAtmosphere = undefined; // 或者设置为 null
|
|
// // 关闭光照效果
|
// viewer.scene.globe.enableLighting = false; // 关闭光照效果
|
|
// viewer.imageryLayers.add(bdtvectoranoimagery);
|
viewer._cesiumWidget._creditContainer.style.display="none";
|
return viewer;
|
|
},
|
loadModel(){
|
const modelUrl = this.towerUrl; // GLB模型路径
|
// 沿原点坐标和地面点坐标方向上选取空中点的距离
|
//初始化相机窗口视角参数
|
cameraPostion = viewerM.camera.position
|
cameraHeading = viewerM.camera.heading;
|
cameraPitch = viewerM.camera.pitch;
|
cameraRoll = viewerM.camera.roll;
|
|
// 设置视角,使其默认查看某个地理位置
|
viewerM.scene.camera.setView({
|
destination: Cesium.Cartesian3.fromDegrees(globalon, globalat, 349.0), // 以经纬度设置位置(例如:大庆龙兴路)
|
// destination: Cesium.Cartesian3.fromDegrees(0.0005, 0.0017, 207.8), // 以经纬度设置位置(例如:大庆龙兴路)
|
orientation: {
|
heading: Cesium.Math.toRadians(194.65), // 方向
|
pitch: Cesium.Math.toRadians(-31.43), // 俯角
|
roll: 0
|
}
|
});
|
|
//创建模型
|
const glbModelPoi = Cesium.Cartesian3.fromDegrees(pois[0], pois[1],pois[2]);
|
const heading = Cesium.Math.toRadians(0);
|
const pitch = Cesium.Math.toRadians(0);
|
const roll = Cesium.Math.toRadians(0);
|
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
|
const orientations = Cesium.Transforms.headingPitchRollQuaternion(glbModelPoi, hpr);
|
this.creatModel(glbModelPoi,orientations,modelUrl,viewerM);
|
this.creatModel(glbModelPoi,orientations,modelUrl,viewerC);
|
},
|
creatModel(position,orientations,url,viewer){
|
viewer.entities.add({
|
id: "tower",
|
position: position,
|
orientation: orientations,
|
model: {
|
uri: url,
|
shadows: Cesium.ShadowMode.DISABLED,
|
imageBasedLightingFactor: new Cesium.Cartesian2(1, 0.5)
|
}
|
});
|
},
|
addSyncListener() {
|
// 存下回调以便销毁
|
// this._preRenderCallback = () => this.syncViewer();
|
viewerM.scene.preRender.addEventListener( this.syncViewer());
|
window.addEventListener("keydown", this.handleKeydown);
|
},
|
syncViewer(){
|
console.log(viewerM.camera)
|
viewerC.camera.flyTo({
|
destination: viewerM.camera.position,
|
orientation: {
|
heading: viewerM.camera.heading,
|
pitch: viewerM.camera.pitch,
|
roll: cameraRoll
|
},
|
duration: 0.0 // 设置为 0.0 以立即完成同步
|
});
|
},
|
creatPin(id, position, font, color, viewer) {
|
let pinBuilder = new Cesium.PinBuilder();
|
let addPin = viewer.entities.getById(id);
|
if (addPin == undefined) {
|
viewer.entities.add({
|
id: id,
|
position: position,
|
billboard: {
|
image: pinBuilder.fromText(font, color, 36).toDataURL(),
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
|
}
|
});
|
} else {
|
addPin.position = position;
|
}
|
},
|
addGroundPoi() {
|
let createPinHandler = new Cesium.ScreenSpaceEventHandler(viewerM.canvas);
|
let _this = this
|
createPinHandler.setInputAction(function (click) {
|
let cartesian = viewerM.scene.pickPosition(click.position);
|
if (Cesium.defined(cartesian)) {
|
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
let height = cartographic.height;//模型高度
|
if(Number(height)<0){
|
let ray = viewerM.camera.getPickRay(click.position);
|
cartesian = viewerM.scene.globe.pick(ray,viewerM.scene);
|
}
|
cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
let lng = Cesium.Math.toDegrees(cartographic.longitude);
|
let lat = Cesium.Math.toDegrees(cartographic.latitude);
|
height = cartographic.height;
|
console.log(lng+','+lat+','+height)
|
}
|
|
//在两个窗口创建标注
|
_this.creatPin("groundPoiId"+counter,cartesian.clone(),`观${counter}`,Cesium.Color.BLUE,viewerM);
|
_this.creatPin("groundPoiId"+counter,cartesian.clone(),`观${counter}`,Cesium.Color.BLUE,viewerC);
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
// 右键点击结束监听,并创建空中点
|
createPinHandler.setInputAction(function (click) {
|
// 获取实体
|
const groupEntity = viewerM.entities.getById("groundPoiId"+counter);
|
const groupPosition = groupEntity.position.getValue(Cesium.JulianDate.now());
|
if (groupPosition) {
|
// 如果位置存在,将其转换为经纬度
|
const cartographic = Cesium.Cartographic.fromCartesian(groupPosition);
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
const height = cartographic.height;
|
//根据两点坐标计算方位角
|
const positionA = Cesium.Cartesian3.fromDegrees(pois[0],pois[1],height);
|
holdingPosition = Cesium.Cartesian3.fromDegrees(longitude,latitude,height);
|
// 计算方向向量
|
const direction = Cesium.Cartesian3.subtract(holdingPosition, positionA, new Cesium.Cartesian3());
|
/**计算沿方向向量移动的点**/
|
const normalize = Cesium.Cartesian3.normalize(direction,new Cesium.Cartesian3());
|
//根据偏移量求偏移向量
|
const scalerNormalize = Cesium.Cartesian3.multiplyByScalar(normalize,distance,new Cesium.Cartesian3());
|
const tagert = Cesium.Cartesian3.add(holdingPosition,scalerNormalize,new Cesium.Cartesian3());
|
console.log(Cesium.Cartographic.fromCartesian(tagert))
|
_this.creatPin("aerialPoiId"+counter,tagert,`空${counter}`,Cesium.Color.RED,viewerM);
|
// preVideoScopePrimitiveArrTie = []
|
console.log(preVideoScopePrimitiveArrTie)
|
|
console.log(Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(tagert).height))
|
_this.updateCameraMovement();
|
// if(cameraVideo){
|
// viewerM.scene.primitives.remove(cameraVideo)
|
// }
|
chooseId = crypto.randomUUID()
|
//创建视锥体
|
let data = {
|
"FHorFieldAngle": 32.13,
|
"FVerFieldAngle": 18.39,
|
"altitude": Cesium.Cartographic.fromCartesian(tagert).height,
|
"id": chooseId,
|
"chanNo": 1,
|
"latitude": Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(tagert).latitude),
|
"longitude": Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(tagert).longitude),
|
"p": 26.8,
|
"t": 351.3,
|
"z": 1,
|
}
|
const point1 = Cesium.Cartesian3.fromDegrees(0, 0, 50);
|
const point2 = Cesium.Cartesian3.fromDegrees(Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(tagert).longitude), Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(tagert).latitude), Cesium.Cartographic.fromCartesian(tagert).height);
|
|
// 计算距离(单位:米)
|
const distance1 = Cesium.Cartesian3.distance(point1, point2)
|
console.log(distance1)
|
data.distance = distance1
|
_this.zoomRatio = 1
|
_this.createVideoScope(data)
|
setTimeout(() => {
|
viewerC.camera.changed.addEventListener(_this.onCameraChange)
|
}, 100);
|
|
|
//结束右键监听
|
createPinHandler.destroy();
|
//结束主图联动鹰眼监听
|
viewerM.scene.preRender.removeEventListener(_this.syncViewer());
|
_this.changeCamera(tagert,_this.azimuthtwopoi(longitude,latitude,pois[0],pois[1]));
|
rightKeyMove = true
|
counter += 1;
|
}
|
console.log('右键点击监听已停止');
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); // 监听右键点击
|
},
|
azimuthtwopoi(x1, y1, x2, y2) {
|
let result;
|
let a = Math.abs(Math.atan((y2 - y1) / (x2 - x1))) * 180 / Math.PI;
|
if ((x2 - x1) < 0) {
|
if ((y2 - y1) > 0) {
|
result = a + 270;
|
} else {
|
result = 90 - a + 180;
|
}
|
} else {
|
if ((y2 - y1) > 0) {
|
result = 90 - a;
|
} else {
|
result = a + 90;
|
}
|
}
|
return result;
|
},
|
changeCamera(position,heading){
|
console.log(position)
|
console.log(heading)
|
viewerC.camera.flyTo({
|
destination: position,
|
orientation: {
|
heading: Cesium.Math.toRadians(heading),
|
pitch: 0,
|
roll: 0
|
},
|
duration: 0.0 // 设置为 0.0 以立即完成同步
|
});
|
},
|
registerKeyboardEvents() {
|
window.addEventListener("keydown", this.onKeyDown);
|
window.addEventListener("keyup", this.onKeyUp);
|
},
|
removeKeyboardEvents() {
|
window.removeEventListener("keydown", this.onKeyDown);
|
window.removeEventListener("keyup", this.onKeyUp);
|
},
|
onKeyDown(event) {
|
const key = event.key;
|
const lower = key.toLowerCase();
|
switch (lower) {
|
case "w":
|
this.keyState.w = true;
|
break;
|
case "s":
|
this.keyState.s = true;
|
break;
|
case "a":
|
this.keyState.a = true;
|
break;
|
case "d":
|
this.keyState.d = true;
|
break;
|
case "q":
|
this.keyState.q = true;
|
break;
|
case "e":
|
this.keyState.e = true;
|
break;
|
case " ": // Space
|
this.keyState.space = true;
|
event.preventDefault();
|
break;
|
case "shift":
|
this.keyState.shift = true;
|
break;
|
case "control":
|
this.keyState.ctrl = true;
|
event.preventDefault();
|
break;
|
}
|
|
// Arrow keys & mode toggle
|
switch (key) {
|
case "ArrowUp":
|
this.keyState.up = true;
|
break;
|
case "ArrowDown":
|
this.keyState.down = true;
|
break;
|
case "ArrowLeft":
|
this.keyState.left = true;
|
break;
|
case "ArrowRight":
|
this.keyState.right = true;
|
break;
|
case "1":
|
viewerC.scene.mode = Cesium.SceneMode.SCENE3D;
|
break;
|
case "3":
|
viewerC.scene.mode = Cesium.SceneMode.COLUMBUS_VIEW;
|
break;
|
}
|
},
|
onKeyUp(event) {
|
//键盘抬起进行更新左侧空中点pin
|
//获取右侧相机
|
if(rightKeyMove){
|
const camera = viewerC.camera;
|
const carto = Cesium.Cartographic.fromCartesian(camera.position);
|
//右侧相机视角转化为经纬度
|
const lon = Cesium.Math.toDegrees(carto.longitude);
|
const lat = Cesium.Math.toDegrees(carto.latitude);
|
const height = carto.height;
|
const position = Cesium.Cartesian3.fromDegrees(lon, lat, height)
|
let pinBuilder = new Cesium.PinBuilder();
|
const groupEntity = viewerM.entities.getById("aerialPoiId"+(counter-1));
|
//左侧地图同步更新pin空中点位置
|
if (groupEntity == undefined) {
|
viewer.entities.add({
|
id: `aerialPoiId+${(counter-1)}`,
|
position: position,
|
billboard: {
|
image: pinBuilder.fromText(font, color, 36).toDataURL(),
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
|
}
|
});
|
} else {
|
groupEntity.position = position;
|
}
|
console.log(groupEntity)
|
}
|
const key = event.key;
|
const lower = key.toLowerCase();
|
switch (lower) {
|
case "w":
|
this.keyState.w = false;
|
break;
|
case "s":
|
this.keyState.s = false;
|
break;
|
case "a":
|
this.keyState.a = false;
|
break;
|
case "d":
|
this.keyState.d = false;
|
break;
|
case "q":
|
this.keyState.q = false;
|
break;
|
case "e":
|
this.keyState.e = false;
|
break;
|
case " ":
|
this.keyState.space = false;
|
this.resetView();
|
break;
|
case "shift":
|
this.keyState.shift = false;
|
break;
|
case "control":
|
this.keyState.ctrl = false;
|
break;
|
}
|
|
switch (key) {
|
case "ArrowUp":
|
this.keyState.up = false;
|
break;
|
case "ArrowDown":
|
this.keyState.down = false;
|
break;
|
case "ArrowLeft":
|
this.keyState.left = false;
|
break;
|
case "ArrowRight":
|
this.keyState.right = false;
|
break;
|
case "r":
|
this.resetPitch(30);
|
break;
|
case "f":
|
this.resetPitch();
|
break;
|
case "v":
|
this.resetPitch(-30);
|
break;
|
}
|
},
|
updateCameraMovement() {
|
const camera = viewerC.camera;
|
// Adjust speed by modifiers
|
let currentSpeed = this.moveSpeed;
|
if (this.keyState.shift) currentSpeed *= this.speedMultiplier;
|
if (this.keyState.ctrl) currentSpeed *= this.slowMultiplier;
|
|
const deltaTime = 1.0; // simplified frame delta
|
|
// WASD / QE
|
if (this.keyState.w) this.uavMoveForward(1);
|
if (this.keyState.s) this.uavMoveForward(-1);
|
if (this.keyState.a) camera.moveLeft(currentSpeed * deltaTime);
|
if (this.keyState.d) camera.moveRight(currentSpeed * deltaTime);
|
if (this.keyState.q) camera.moveUp(currentSpeed * deltaTime);
|
if (this.keyState.e) camera.moveDown(currentSpeed * deltaTime);
|
|
// Arrow‑key rotation
|
let rotSpeed = 1;
|
if (this.keyState.shift) rotSpeed *= this.speedMultiplier;
|
if (this.keyState.ctrl) rotSpeed *= this.slowMultiplier;
|
if (holdingPosition) {
|
const distance = Cesium.Cartesian3.distance(holdingPosition, camera.position);
|
const surroundSpeed = 0.005; //环绕速度,角度
|
let cameraHeading = camera.heading;
|
let cameraPitch = camera.pitch;
|
if (this.keyState.up) cameraPitch -= surroundSpeed;
|
if (this.keyState.down) cameraPitch += surroundSpeed;
|
if (this.keyState.left) cameraHeading += surroundSpeed
|
if (this.keyState.right) cameraHeading -= surroundSpeed
|
viewerC.camera.lookAt(holdingPosition, new Cesium.HeadingPitchRange(cameraHeading, cameraPitch, distance));
|
viewerC.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
} else {
|
if (this.keyState.up) camera.lookUp(rotateAmount);
|
if (this.keyState.down) camera.lookDown(rotateAmount);
|
if (this.keyState.left) camera.lookLeft(rotateAmount);
|
if (this.keyState.right) camera.lookRight(rotateAmount);
|
}
|
this.resetRoll();
|
// Schedule next frame
|
// this.animationFrameId = requestAnimationFrame(this.updateCameraMovement);
|
},
|
calculateDestinationPoint(lon1, lat1, bearing, distance, radius = 6371000) {
|
const toRad = (d) => d * Math.PI / 180;
|
const toDeg = (r) => r * 180 / Math.PI;
|
|
const lat1Rad = toRad(lat1);
|
const lon1Rad = toRad(lon1);
|
const bearingRad = bearing;
|
const angularDistance = distance / radius;
|
|
const lat2Rad = Math.asin(
|
Math.sin(lat1Rad) * Math.cos(angularDistance) +
|
Math.cos(lat1Rad) * Math.sin(angularDistance) * Math.cos(bearingRad)
|
);
|
const lon2Rad = lon1Rad + Math.atan2(
|
Math.sin(bearingRad) * Math.sin(angularDistance) * Math.cos(lat1Rad),
|
Math.cos(angularDistance) - Math.sin(lat1Rad) * Math.sin(lat2Rad)
|
);
|
const lon2Norm = ((lon2Rad + 3 * Math.PI) % (2 * Math.PI)) - Math.PI;
|
|
return { lon: toDeg(lon2Norm), lat: toDeg(lat2Rad) };
|
},
|
uavMoveForward(speed) {
|
const camera = viewerC.camera;
|
const carto = Cesium.Cartographic.fromCartesian(camera.position);
|
const lon = Cesium.Math.toDegrees(carto.longitude);
|
const lat = Cesium.Math.toDegrees(carto.latitude);
|
const height = carto.height;
|
const dest = this.calculateDestinationPoint(lon, lat, camera.heading, speed);
|
camera.setView({
|
destination: Cesium.Cartesian3.fromDegrees(dest.lon, dest.lat, height),
|
orientation: {
|
heading: camera.heading,
|
pitch: camera.pitch,
|
roll: 0,
|
},
|
});
|
},
|
resetRoll() {
|
const camera = viewerC.camera;
|
camera.setView({
|
destination: camera.destination,
|
orientation: {
|
heading: camera.heading,
|
pitch: camera.pitch,
|
roll: 0,
|
},
|
});
|
},
|
|
resetPitch(amount) {
|
if (amount === undefined) amount = 0.1;
|
const camera = viewerC.camera;
|
camera.setView({
|
destination: camera.destination,
|
orientation: {
|
heading: camera.heading,
|
pitch: Cesium.Math.toRadians(amount),
|
roll: 0,
|
},
|
});
|
},
|
|
resetView() {
|
// Fly back to the initial view (home) in 1 second
|
viewerC.camera.flyHome(1.0);
|
},
|
move_measure_point(){
|
CesiumSurvey.measureMovePoint(viewerC,'moveResultCon');
|
},
|
cammove_measure_point(){
|
CesiumSurvey.cammeasureMovePoint(viewerC,'cammoveResultCon');
|
},
|
mergePoint(){
|
let allEntities = viewerM.entities.values; // 所有实体对象组成的数组
|
const grouped = {};
|
//所有实体对象组成的数组转换成左侧树需要的格式
|
allEntities.forEach(item => {
|
const match = item.id.match(/(groundPoiId|aerialPoiId)(\d+)/);
|
if (match) {
|
const type = match[1]; // groundPoiId or aerialPoiId
|
const index = match[2];
|
if (!grouped[index]) grouped[index] = {};
|
grouped[index][type] = item.position;
|
}
|
})
|
const result = Object.keys(grouped).map((key, idx) => {
|
const group = grouped[key];
|
const positionGround = group.groundPoiId.getValue(Cesium.JulianDate.now())
|
const positionAerialPo = group.aerialPoiId.getValue(Cesium.JulianDate.now())
|
const groundPos = Cesium.Cartographic.fromCartesian(positionGround);
|
const aerialPos = Cesium.Cartographic.fromCartesian(positionAerialPo);
|
return {
|
id: this.genId(),
|
label: `地面点${idx}`,
|
longitude: Cesium.Math.toDegrees(groundPos.longitude),
|
latitude: Cesium.Math.toDegrees(groundPos.latitude),
|
height: groundPos.height,
|
children: [
|
{
|
id: this.genId(),
|
label: `空中点${idx}`,
|
longitude: Cesium.Math.toDegrees(aerialPos.longitude),
|
latitude: Cesium.Math.toDegrees(aerialPos.latitude),
|
height: aerialPos.height
|
}
|
]
|
};
|
});
|
console.log(result)
|
this.$emit('mergePoint',result,this.mergeNumber,viewerM)
|
},
|
genId() {
|
return (Date.now().toString(16) + Math.random().toString(16).slice(2, 10)).slice(0, 16);
|
},
|
convertPosition(position) {
|
const cartesian = new Cesium.Cartesian3(position.x, position.y, position.z);
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
return {
|
longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
height: cartographic.height
|
};
|
},
|
handleClick(key) {
|
this.triggerKey(key);
|
},
|
triggerKey(key) {
|
this.activeKey = key;
|
clearTimeout(this.timer);
|
this.timer = setTimeout(() => {
|
this.activeKey = null;
|
}, 200);
|
|
// 自定义动作触发
|
console.log("Key triggered:", key);
|
// 这里可以发出事件或调用其它方法
|
},
|
handleKeydown(e) {
|
const key = e.key;
|
console.log(key)
|
const validKeys = ["a", "s", "d", "w",'q','e','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','q','e'];
|
if (validKeys.includes(key)) {
|
this.triggerKey(key);
|
}
|
this.updateCameraMovement()
|
},
|
// 创建初始化的光轴
|
createVideoScope(data) {
|
console.log(data)
|
console.log(data.distance)
|
if (data == undefined) {
|
this.$message({
|
showClose: true,
|
message: '暂无数据',
|
type: "warning",
|
});
|
} else {
|
ScopeElement = this.createVideoElementOld(
|
"http://192.168.5.122:9998/live?port=1234&app=live&stream=mystream",
|
data.id,
|
1.7778
|
);
|
// let poi = data.camPosition.split(",");
|
// let hight = data.pe_offset.split(",");
|
let positionCt3 = Cesium.Cartesian3.fromDegrees(
|
Number(data.longitude),
|
Number(data.latitude),
|
// Number(data.altitude) + Number(hight[1]) + 1.7
|
Number(data.altitude)
|
);
|
console.log(data)
|
let inverseViewMatrix = this.hpr2m({
|
position: positionCt3, // 相机坐标
|
// heading: Cesium.Math.toRadians(Number(120)),
|
// pitch: Cesium.Math.toRadians(Number(-5)),
|
// roll: Cesium.Math.toRadians(Number(0)),
|
heading: Cesium.Math.toRadians(180),
|
pitch: Cesium.Math.toRadians(0),
|
roll: Cesium.Math.toRadians(0),
|
});
|
let frustum = new Cesium.PerspectiveFrustum({
|
fov: Cesium.Math.toRadians(1),
|
aspectRatio: Number(1.77778), // 宽高比
|
near: Number(0.05), // 设备焦距
|
far: Number(data.distance), // 拍摄距离
|
});
|
cameraVideo = new Cesium.XbsjCameraVideo({
|
inverseViewMatrix: inverseViewMatrix,
|
frustum: frustum,
|
videoElement: ScopeElement,
|
showHelperPrimitive: true,
|
});
|
cameraVideo._primitive.classificationType = 2; // 同时投影地形和3dtiles数据
|
cameraVideo.id = data.id;
|
let isHole = false;
|
cameraVideo._primitive.appearance.material = new Cesium.Material({
|
fabric: {
|
type: "Color",
|
uniforms: {
|
color: new Cesium.Color(0, 1, 0, 0.2),
|
},
|
},
|
});
|
cameraVideo._helperPrimitive.geometryInstances.attributes.color =
|
Cesium.ColorGeometryInstanceAttribute.fromColor(
|
new Cesium.Color(0, 1.0, 0, 1.0)
|
);
|
// viewer.scene.primitives.add(cameraVideo);
|
let pmObj = {};
|
console.log(data)
|
pmObj.id = data.id;
|
console.log(pmObj)
|
pmObj.primitive = cameraVideo;
|
// pmCollection.push(pmObj);//幕布,视频转换使用
|
viewerM.scene.primitives.add(cameraVideo);
|
preVideoScopePrimitiveArrTie.push(pmObj);
|
preVideoScopePrimitiveArrTie.map((item)=>{
|
console.log(item)
|
console.log(chooseId)
|
if(item.id != chooseId){
|
item.primitive.show = false
|
}
|
})
|
}
|
// }
|
},
|
// 创建光轴下的幕布
|
createVideoElementOld(videoSrc, id) {
|
var videoElement = document.createElement("div");
|
videoElement.id = "video" + id;
|
videoElement.style.position = "absolute";
|
videoElement.style.zIndex = "-100";
|
videoElement.style.background = "green";
|
document.getElementById("videoSource").appendChild(videoElement);
|
return videoElement;
|
},
|
//h,p,r转matrix4
|
hpr2m(obj, result) {
|
// const inverseViewMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, headingPitchRoll, undefined, undefined, result);
|
const inverseViewMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
|
obj.position,
|
undefined,
|
result
|
);
|
let rotMat = Cesium.Matrix3.fromRotationX(
|
Cesium.Math.PI_OVER_TWO,
|
scratchSetViewMatrix3
|
);
|
Cesium.Matrix4.multiplyByMatrix3(
|
inverseViewMatrix,
|
rotMat,
|
inverseViewMatrix
|
);
|
rotMat = Cesium.Matrix3.fromRotationY(
|
-obj.heading,
|
scratchSetViewMatrix3
|
);
|
Cesium.Matrix4.multiplyByMatrix3(
|
inverseViewMatrix,
|
rotMat,
|
inverseViewMatrix
|
);
|
|
rotMat = Cesium.Matrix3.fromRotationX(obj.pitch, scratchSetViewMatrix3);
|
Cesium.Matrix4.multiplyByMatrix3(
|
inverseViewMatrix,
|
rotMat,
|
inverseViewMatrix
|
);
|
|
rotMat = Cesium.Matrix3.fromRotationZ(-obj.roll, scratchSetViewMatrix3);
|
Cesium.Matrix4.multiplyByMatrix3(
|
inverseViewMatrix,
|
rotMat,
|
inverseViewMatrix
|
);
|
|
return inverseViewMatrix;
|
},
|
onCameraChange() {
|
const camera = viewerC.camera;
|
const position = camera.positionCartographic;
|
const heading = Cesium.Math.toDegrees(camera.heading);
|
const pitch = Cesium.Math.toDegrees(camera.pitch);
|
const roll = Cesium.Math.toDegrees(camera.roll);
|
const point1 = Cesium.Cartesian3.fromDegrees(0, 0, 50);
|
const point2 = Cesium.Cartesian3.fromDegrees(Cesium.Math.toDegrees(position.longitude),Cesium.Math.toDegrees(position.latitude),position.height)
|
// 计算距离(单位:米)
|
const distanceBetween = Cesium.Cartesian3.distance(point1, point2)
|
console.log(distanceBetween)
|
console.log(preVideoScopePrimitiveArrTie)
|
console.log(chooseId)
|
preVideoScopePrimitiveArrTie.map((item)=>{
|
if(item.id == chooseId){
|
let obj = this.updateSZScope(Cesium.Math.toDegrees(position.longitude),Cesium.Math.toDegrees(position.latitude),position.height,heading,pitch,roll,this.zoomRatio,distanceBetween)
|
item.primitive.inverseViewMatrix = obj.M
|
item.primitive.frustum = obj.F
|
}
|
})
|
},
|
//更新视椎体位置
|
updateSZScope(lon, lat, alt, camHeading, camPitch, camRoll, LightView,distanceBetween) {
|
let positionCt3 = Cesium.Cartesian3.fromDegrees(
|
Number(lon),
|
Number(lat),
|
Number(alt)
|
);
|
console.log(positionCt3)
|
console.log(distanceBetween)
|
let frustum = new Cesium.PerspectiveFrustum({
|
fov: Cesium.Math.toRadians(LightView),
|
aspectRatio: Number(1.77778),
|
near: Number(0.05),
|
far: Number(distanceBetween),
|
});
|
console.log(frustum)
|
let inverseViewMatrixNew = this.hpr2m({
|
position: positionCt3,
|
heading: Cesium.Math.toRadians(Number(camHeading)),
|
pitch: Cesium.Math.toRadians(Number(camPitch)),
|
roll: Cesium.Math.toRadians(Number(camRoll)),
|
});
|
console.log(inverseViewMatrixNew)
|
return {M: inverseViewMatrixNew, F: frustum};
|
},
|
handleWheel(event) {
|
const delta = event.deltaY;
|
const step = 0.5;
|
if (delta > 0) {
|
this.zoomRatio = Math.min(this.zoomRatio + step, this.maxZoom);
|
} else {
|
this.zoomRatio = Math.max(this.zoomRatio - step, this.minZoom);
|
}
|
this.onCameraChange()
|
},
|
formatTooltip(val) {
|
if(val){
|
return `${val.toFixed(1)}X`;
|
}
|
},
|
onSliderInput(val) {
|
this.zoomRatio = val;
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
|
.cesium-example{
|
height: calc(100vh - 185px);
|
}
|
#cesiumContainer {
|
margin: 0;
|
padding: 0;
|
width: 73%;
|
height: calc(100vh - 205px);
|
}
|
#cesiumCamera {
|
position: absolute;
|
z-index: 2;
|
background-color: rgba(47, 53, 60, 1);
|
/* padding: 0 5px; */
|
right: 10px;
|
/* top: 0px; */
|
bottom: 80px;
|
margin: 0;
|
width: 400px;
|
height: 300px;
|
z-index: 6;
|
/* height: calc(100vh - 400px); */
|
/* border: 1px solid #000; */
|
}
|
.cesiumBotton {
|
position: absolute;
|
z-index: 2;
|
background-color: rgb(239, 242, 245);
|
/* padding: 0 5px; */
|
margin-top: 10px;
|
right: 0px;
|
width: 420px;
|
height: calc(100vh - 210px);
|
bottom: 30px;
|
margin: 0;
|
}
|
.cesiumButtonGroup{
|
position: absolute;
|
bottom: 0;
|
/* margin-top: 10px;
|
margin-left: 10px; */
|
}
|
.moveResult{position:absolute;z-index:2;background-color:rgba(47,53,60,1);padding:0 5px;right:50%;bottom:30px;
|
border:1px solid rgba(255,255,255,.1);box-sizing:content-box;width:220px;height:30px;font-size:14px;text-align:center}
|
.cammoveResult{position:absolute;z-index:2;background-color:rgba(47,53,60,1);padding:0 5px;right:calc(50% - 40px);bottom:30px;
|
border:1px solid rgba(255,255,255,.1);box-sizing:content-box;width:400px;height:30px;font-size:14px;text-align:center}
|
.moveResultCon{margin-top:4px}
|
.cammoveResultCon{margin-top:4px;color: white;}
|
.box_lt{width:10px;height:10px;position:absolute;border-top:2px solid #00d3e7;border-left:2px solid #00d3e7;left:0px;top:0px;}
|
.box_rt{width:10px;height:10px;position:absolute;border-top:2px solid #00d3e7;border-right:2px solid #00d3e7;right:0px;top:0px;}
|
.box_rb{width:10px;height:10px;position:absolute;border-bottom:2px solid #00d3e7;border-right:2px solid #00d3e7;right:0px;bottom:0px;}
|
.box_lb{width:10px;height:10px;position:absolute;border-bottom:2px solid #00d3e7;border-left:2px solid #00d3e7;left:0px;bottom:0px;}
|
</style>
|
|
<style scoped>
|
.key-container {
|
display: inline-block;
|
background-color: rgba(0, 0, 0, 0.7);
|
padding: 8px 10px;
|
border-radius: 10px;
|
color: white;
|
font-family: sans-serif;
|
margin-top: 55px;
|
}
|
|
.arrow-row{
|
display: flex;
|
justify-content: center;
|
margin-bottom: 4px;
|
}
|
.key-row {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 4px;
|
}
|
|
.arrow,
|
.key {
|
width: 30px;
|
height: 30px;
|
margin: 0 2px;
|
background-color: #333;
|
border-radius: 4px;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
font-weight: bold;
|
transition: background-color 0.2s;
|
cursor: pointer;
|
}
|
|
.arrow.active,
|
.key.active {
|
background-color: #00aaff;
|
}
|
</style>
|
<style scoped>
|
.zoom-ui-container {
|
/* width: 100%;
|
height: 100%;
|
position: relative;
|
background: #1e1e1e; /* 你可以替换成图像或视频背景 */
|
/* overflow: hidden; */
|
width: 400px;
|
height: 300px;
|
position: absolute;
|
right: 10px;
|
bottom:80px;
|
/* background-color: red; */
|
z-index: 9;
|
|
}
|
/* 黄色准星框 */
|
.reticle {
|
position: absolute;
|
left: 50%;
|
top: 50%;
|
border: 2px dashed orange;
|
transform: translate(-50%, -50%);
|
pointer-events: none;
|
}
|
|
/* ElementUI 滑块样式区域 */
|
.zoom-bar-slider {
|
position: absolute;
|
right: 20px;
|
top: 50px;
|
height: 200px;
|
}
|
|
/* 左下角倍率标签 */
|
.zoom-info {
|
position: absolute;
|
bottom: 20px;
|
left: 20px;
|
}
|
|
</style>
|
<style>
|
.el-slider.is-vertical .el-slider__marks-text{
|
width: 30px!important;
|
}
|
</style>
|