多目标轨迹dd和hj,卫星
@ -36,54 +36,63 @@ window['settings'] = {
|
||||
color: '#d00',
|
||||
model: './models/甲.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
预警机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-7100-0-预警机.svg',
|
||||
color: '#d00',
|
||||
model: './models/预警机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
战斗机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-7300-0-强击机.svg',
|
||||
color: '#d00',
|
||||
model: './models/战斗机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
运输机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-7900-0-运输机.svg',
|
||||
color: '#d00',
|
||||
model: './models/运输机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
轰炸机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-7400-0-轰炸机.svg',
|
||||
color: '#d00',
|
||||
model: './models/轰炸机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
武装直升机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-8402-0-攻击直升机.svg',
|
||||
color: '#d00',
|
||||
model: './models/武装直升机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
加油机: {
|
||||
icon: './images/icons/10-7600-0-侦察机.svg',
|
||||
icon: './images/icons/10-8000-0-加油机.svg',
|
||||
color: '#ff0',
|
||||
model: './models/加油机.glb',
|
||||
payload: 'radar',
|
||||
height: 20000,
|
||||
},
|
||||
乙: {
|
||||
icon: './images/icons/10-5900-0-航空母舰.svg',
|
||||
color: '#ff0',
|
||||
model: './models/驱逐舰2.glb',
|
||||
payload: 'radar',
|
||||
height: 10,
|
||||
},
|
||||
丙: {
|
||||
icon: './images/icons/10-6100-0-驱逐舰.svg',
|
||||
color: '#dd0',
|
||||
model: './models/驱逐舰1.glb',
|
||||
payload: 'radar',
|
||||
height: 10,
|
||||
},
|
||||
},
|
||||
|
||||
|
Before ![]() (image error) Size: 3.3 KiB |
@ -1 +1 @@
|
||||
<svg width="756" height="352" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 40 L 280 52 L 728 52 L 728 308 L 282 308 L 280 314 L 274 312 L 28 178 L 278 40 Z M 280 84 L 280 162 L 282 164 L 696 164 L 696 86 L 694 84 L 280 84 Z M 242 96 L 96 178 L 248 260 L 248 98 L 242 96 Z M 280 196 L 280 274 L 282 276 L 696 276 L 696 198 L 694 196 L 280 196 Z "></path></svg>
|
||||
<svg style='transform:rotate(180deg)' width="756" height="352" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 40 L 280 52 L 728 52 L 728 308 L 282 308 L 280 314 L 274 312 L 28 178 L 278 40 Z M 280 84 L 280 162 L 282 164 L 696 164 L 696 86 L 694 84 L 280 84 Z M 242 96 L 96 178 L 248 260 L 248 98 L 242 96 Z M 280 196 L 280 274 L 282 276 L 696 276 L 696 198 L 694 196 L 280 196 Z "></path></svg>
|
Before (image error) Size: 496 B After (image error) Size: 529 B |
Before ![]() (image error) Size: 3.3 KiB |
@ -1 +1 @@
|
||||
<svg width="756" height="352" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 44 L 280 56 L 728 56 L 728 312 L 282 312 L 280 318 L 274 316 L 28 182 L 278 44 Z M 280 88 L 280 278 L 282 280 L 304 280 L 304 90 L 302 88 L 280 88 Z M 336 88 L 336 280 L 696 280 L 696 88 L 336 88 Z M 242 100 L 96 182 L 248 264 L 248 102 L 242 100 Z "></path></svg>
|
||||
<svg style='transform:rotate(180deg)' width="756" height="352" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 44 L 280 56 L 728 56 L 728 312 L 282 312 L 280 318 L 274 316 L 28 182 L 278 44 Z M 280 88 L 280 278 L 282 280 L 304 280 L 304 90 L 302 88 L 280 88 Z M 336 88 L 336 280 L 696 280 L 696 88 L 336 88 Z M 242 100 L 96 182 L 248 264 L 248 102 L 242 100 Z "></path></svg>
|
Before (image error) Size: 476 B After (image error) Size: 509 B |
Before ![]() (image error) Size: 3.3 KiB |
@ -1 +1 @@
|
||||
<svg width="756" height="364" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 48 L 280 60 L 728 60 L 728 316 L 282 316 L 280 322 L 274 320 L 28 186 L 278 48 Z M 280 92 L 280 284 L 696 284 L 696 92 L 280 92 Z M 242 104 L 96 186 L 248 268 L 248 106 L 242 104 Z "></path></svg>
|
||||
<svg style='transform:rotate(180deg)' width="756" height="364" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 278 48 L 280 60 L 728 60 L 728 316 L 282 316 L 280 322 L 274 320 L 28 186 L 278 48 Z M 280 92 L 280 284 L 696 284 L 696 92 L 280 92 Z M 242 104 L 96 186 L 248 268 L 248 106 L 242 104 Z "></path></svg>
|
Before (image error) Size: 408 B After (image error) Size: 441 B |
Before ![]() (image error) Size: 3.3 KiB |
1
public/images/icons/10-7100-0-预警机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="408" height="604" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 186 L 372 342 L 350 364 L 216 234 L 216 410 L 226 412 L 264 442 L 276 482 L 268 514 L 252 534 L 252 542 L 264 562 L 232 576 L 224 552 L 178 552 L 176 558 L 168 576 L 158 576 L 136 562 L 152 534 L 132 502 L 132 462 L 158 424 L 184 410 L 182 236 L 54 364 L 32 342 L 184 190 L 184 28 Z M 218 444 L 216 470 L 232 502 L 240 498 L 240 466 L 232 452 L 218 444 Z M 182 448 L 172 452 L 164 466 L 164 498 L 170 504 L 184 478 L 182 448 Z "></path></svg>
|
After (image error) Size: 713 B |
Before ![]() (image error) Size: 3.6 KiB |
@ -1 +1 @@
|
||||
<svg width="408" height="564" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 470 L 260 518 L 238 536 L 202 500 L 166 536 L 144 518 L 184 474 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z "></path></svg>
|
||||
<svg style='transform:rotate(90deg)' width="408" height="564" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 470 L 260 518 L 238 536 L 202 500 L 166 536 L 144 518 L 184 474 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z "></path></svg>
|
Before (image error) Size: 407 B After (image error) Size: 439 B |
1
public/images/icons/10-7300-0-强击机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="408" height="580" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 474 L 256 552 L 144 550 L 184 482 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z "></path></svg>
|
After (image error) Size: 409 B |
Before ![]() (image error) Size: 3.6 KiB |
1
public/images/icons/10-7400-0-轰炸机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="408" height="584" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 462 L 222 468 L 240 482 Q 251.3 492.7 248 518 Q 242.5 542.5 222 552 L 202 556 Q 175.5 552.5 164 534 L 156 506 L 160 490 L 174 472 L 184 466 L 182 220 L 54 348 L 32 326 L 184 174 L 184 28 Z M 198 496 L 188 510 L 192 520 L 202 524 L 216 514 L 212 502 Q 216 490 198 496 Z "></path></svg>
|
After (image error) Size: 595 B |
Before ![]() (image error) Size: 3.5 KiB |
@ -1 +1 @@
|
||||
<svg width="408" height="520" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 458 L 232 460 L 232 492 L 172 492 L 172 460 L 184 458 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z "></path></svg>
|
||||
<svg style='transform:rotate(90deg)' width="408" height="520" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 458 L 232 460 L 232 492 L 172 492 L 172 460 L 184 458 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z "></path></svg>
|
Before (image error) Size: 397 B After (image error) Size: 429 B |
1
public/images/icons/10-7900-0-运输机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="408" height="576" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 458 L 218 460 L 272 460 L 272 548 L 128 548 L 128 460 L 182 460 L 184 458 L 184 222 L 54 348 L 32 326 L 184 174 L 184 28 Z M 160 492 L 160 514 L 162 516 L 240 516 L 240 494 L 238 492 L 160 492 Z "></path></svg>
|
After (image error) Size: 521 B |
1
public/images/icons/10-8000-0-加油机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="408" height="636" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 184 28 L 214 28 L 216 30 L 216 170 L 372 326 L 350 348 L 216 218 L 216 462 L 222 468 Q 245.2 470.8 256 486 L 276 522 L 276 534 L 264 574 L 242 596 L 210 608 L 190 608 Q 156.6 599.4 140 574 L 128 522 L 154 480 L 184 468 L 182 220 L 54 348 L 32 326 L 184 174 L 184 28 Z M 190 496 L 182 500 L 168 520 L 240 518 Q 231 490 190 496 Z "></path></svg>
|
After (image error) Size: 583 B |
1
public/images/icons/10-8402-0-攻击直升机.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style='transform:rotate(90deg)' width="360" height="504" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.5"><path fill="rgb(255, 102, 102)" stroke="#f66" stroke-width="0" opacity="1" d="M 50 28 L 170 88 L 182 88 L 302 28 L 316 54 L 216 104 Q 211.5 114.7 222 112 L 320 162 L 308 188 L 298 188 L 192 136 L 192 398 L 232 476 L 120 474 L 160 402 L 160 136 L 50 188 L 36 162 L 136 112 Q 140.5 101.3 130 104 L 36 54 L 50 28 Z "></path></svg>
|
After (image error) Size: 487 B |
@ -32,6 +32,14 @@ export function getMubiaoHisTraj(params) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getMubiaoHisTrajByIds(data) {
|
||||
return request({
|
||||
url: `${baseUrl}/traj/queryTraj2`,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateMbPayload(data = {}) {
|
||||
return request({
|
||||
url: `${baseUrl}/traj/extendInfo`,
|
||||
|
@ -1,26 +1,19 @@
|
||||
export const drag = {
|
||||
mounted(el, binding, vnode, prevVnode) {
|
||||
// let oDiv = el // 获取当前元素
|
||||
// oDiv.onmousedown = e => {
|
||||
// // 算出鼠标相对元素的位置
|
||||
// let disX = e.clientX - oDiv.offsetLeft
|
||||
// let disY = e.clientY - oDiv.offsetTop
|
||||
// document.onmousemove = e => {
|
||||
// // 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
|
||||
// let left = e.clientX - disX
|
||||
// let top = e.clientY - disY
|
||||
// oDiv.style.left = left + 'px'
|
||||
// oDiv.style.top = top + 'px'
|
||||
// }
|
||||
const targetClass = binding.arg === 'target' ? binding.value : null
|
||||
|
||||
// document.onmouseup = e => {
|
||||
// document.onmousemove = null
|
||||
// document.onmouseup = null
|
||||
// }
|
||||
// }
|
||||
el.style.cursor = 'move'
|
||||
el.style.position = 'absolute'
|
||||
if (targetClass && el.querySelector(`.${targetClass}`)) {
|
||||
el.querySelector('.content-title').style.cursor = 'move'
|
||||
} else {
|
||||
el.style.cursor = 'move'
|
||||
}
|
||||
|
||||
el.onmousedown = function (e) {
|
||||
if (targetClass && !e.target.classList.contains(`${targetClass}`)) {
|
||||
return
|
||||
}
|
||||
|
||||
let disX = e.pageX - el.offsetLeft
|
||||
let disY = e.pageY - el.offsetTop
|
||||
|
||||
|
@ -135,4 +135,13 @@ export function createTrajectory(p1, p2, options = {}) {
|
||||
return result
|
||||
}
|
||||
|
||||
// 示例:每秒插值
|
||||
export function getDirection(point1, point2) {
|
||||
const carto1 = Cesium.Cartographic.fromCartesian(point1)
|
||||
const carto2 = Cesium.Cartographic.fromCartesian(point2)
|
||||
|
||||
const dx = carto2.longitude - carto1.longitude
|
||||
const dy = carto2.latitude - carto1.latitude
|
||||
|
||||
const heading = Math.atan2(dy, dx)
|
||||
return heading
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export const useTree = () => {
|
||||
return {
|
||||
getTreeNodeById,
|
||||
filterTreeNodeByField,
|
||||
getAllKeys,
|
||||
getLeafNodeIds,
|
||||
@ -8,6 +9,22 @@ export const useTree = () => {
|
||||
}
|
||||
}
|
||||
|
||||
function getTreeNodeById(treeData, id) {
|
||||
let node = null
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
const element = treeData[i]
|
||||
if (element.id === id) {
|
||||
node = element
|
||||
break
|
||||
} else {
|
||||
if (element.children) {
|
||||
getTreeNodeById(element.children, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
function filterTreeNodeByField({ treeData, params, paramName, icon = '' }) {
|
||||
return treeData.reduce((acc, node) => {
|
||||
if (params.includes(node[paramName]) && !node.children) {
|
||||
|
856
src/views/Daodan/components/hooks copy/testHooks.jsx
Normal file
@ -0,0 +1,856 @@
|
||||
import { ref, toRaw } from 'vue'
|
||||
import { useDaodan } from '../../ddHooks'
|
||||
import {
|
||||
cartesian32LonLat,
|
||||
getPositionFromTime,
|
||||
getOnePositionFromTime,
|
||||
test,
|
||||
createTrajectory,
|
||||
} from '@/utils/pos'
|
||||
import ExplosionEffect from '@/js/Explosion'
|
||||
import { generateId } from '@/utils/id'
|
||||
import store from 'store2'
|
||||
import { lineString, bezierSpline } from '@turf/turf'
|
||||
// import { useDaodan } from '../../ddHooks'
|
||||
|
||||
const ddScale = 30
|
||||
|
||||
const trajData = ref({
|
||||
id: 'dd',
|
||||
data: [
|
||||
{
|
||||
name: '起始点',
|
||||
lon: 120,
|
||||
lat: 21,
|
||||
alt: 0,
|
||||
time: 1183135260000,
|
||||
},
|
||||
{
|
||||
name: '最高点',
|
||||
lon: 125,
|
||||
lat: 25,
|
||||
alt: 2000000,
|
||||
time: 1183135280000,
|
||||
},
|
||||
{
|
||||
name: '落点',
|
||||
lon: 160,
|
||||
lat: 40,
|
||||
alt: 0,
|
||||
time: 1183135300000,
|
||||
},
|
||||
],
|
||||
})
|
||||
const boosterList = ref([
|
||||
{
|
||||
id: 'booster-1',
|
||||
data: [
|
||||
{
|
||||
// name: '落点',
|
||||
lon: 130,
|
||||
lat: 23,
|
||||
alt: 0,
|
||||
time: 1183135265000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'booster-2',
|
||||
data: [
|
||||
{
|
||||
// name: '落点',
|
||||
lon: 135,
|
||||
lat: 28,
|
||||
alt: 0,
|
||||
time: 1183135270000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'booster-3',
|
||||
data: [
|
||||
{
|
||||
// name: '落点',
|
||||
lon: 140,
|
||||
lat: 35,
|
||||
alt: 0,
|
||||
time: 1183135275000,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
const interceptData = ref([
|
||||
{
|
||||
id: 'intercept-1',
|
||||
data: [
|
||||
{
|
||||
// name: '起始点',
|
||||
lon: 137,
|
||||
lat: 25,
|
||||
alt: 0,
|
||||
time: 1183135285000,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const { daodanData, showDdConfigCom } = useDaodan()
|
||||
|
||||
export function useTestConfig() {
|
||||
return {
|
||||
trajData,
|
||||
boosterList,
|
||||
interceptData,
|
||||
loadStoreData,
|
||||
addIntercept,
|
||||
handleClickPoint,
|
||||
initDaodan,
|
||||
}
|
||||
}
|
||||
|
||||
function saveDataToStore() {
|
||||
// test()
|
||||
const daodanDataRaw = toRaw(daodanData.value)
|
||||
const storeData = store.get('daodanData')
|
||||
|
||||
store.set('daodanData', {
|
||||
...storeData,
|
||||
[daodanDataRaw.id]: {
|
||||
...daodanDataRaw,
|
||||
trajData: trajData.value,
|
||||
boosterList: boosterList.value,
|
||||
interceptData: interceptData.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function loadStoreData() {
|
||||
const storeData = store.get('daodanData')
|
||||
if (storeData) {
|
||||
const daodanDataRaw = toRaw(daodanData.value)
|
||||
const data = storeData[daodanDataRaw.id]
|
||||
if (data) {
|
||||
trajData.value = data.trajData
|
||||
interceptData.value = data.interceptData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addIntercept() {
|
||||
// 添加拦截d
|
||||
interceptData.value.push({
|
||||
id: generateId(),
|
||||
data: [
|
||||
{
|
||||
name: '起始点',
|
||||
lon: 120,
|
||||
lat: 21,
|
||||
alt: 0,
|
||||
time: 1183135260000,
|
||||
},
|
||||
{
|
||||
name: '中间特征点',
|
||||
lon: 120,
|
||||
lat: 21,
|
||||
alt: 0,
|
||||
time: 1183135260000,
|
||||
detached: false,
|
||||
},
|
||||
trajData.value.data.at(-1),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
let handler = null
|
||||
function handleClickPoint(rowData) {
|
||||
// 点击点
|
||||
// console.log(rowData)
|
||||
showDdConfigCom.value = false
|
||||
viewer._container.style.cursor = 'crosshair'
|
||||
handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
|
||||
handler.setInputAction(movement => {
|
||||
// const cartesian = viewer.scene.pickPosition(movement.position)
|
||||
const ray = viewer.camera.getPickRay(movement.position)
|
||||
const cartesian = viewer.scene.globe.pick(ray, viewer.scene)
|
||||
const position = cartesian32LonLat(cartesian)
|
||||
// console.log(position)
|
||||
viewer._container.style.cursor = ''
|
||||
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
showDdConfigCom.value = true
|
||||
rowData.lon = position[0]
|
||||
rowData.lat = position[1]
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
}
|
||||
|
||||
let nodes = []
|
||||
let ddMap = new Map()
|
||||
function addDaodan(trajData, type = 0) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const position = Cesium.Cartesian3.fromDegrees(
|
||||
ddTrajData[0].lon,
|
||||
ddTrajData[0].lat,
|
||||
ddTrajData[0].alt
|
||||
)
|
||||
const hpRoll = new Cesium.HeadingPitchRoll()
|
||||
// const fixedFrameTransform = Cesium.Transforms.eastNorthUpToFixedFrame
|
||||
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(
|
||||
'north',
|
||||
'west'
|
||||
)
|
||||
// hpRoll.pitch = (90 * Math.PI) / 180
|
||||
const modelObj = Cesium.Model.fromGltf({
|
||||
// url: './models/launchvehicle.glb',
|
||||
url: './models/美三叉戟2动画.glb',
|
||||
modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpRoll,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform
|
||||
),
|
||||
scale: ddScale,
|
||||
minimumPixelSize: ddScale,
|
||||
})
|
||||
|
||||
const ddPrimitive = viewer.scene.primitives.add(modelObj)
|
||||
|
||||
ddMap.set(id, {
|
||||
primitive: ddPrimitive,
|
||||
nodes,
|
||||
})
|
||||
|
||||
// ddPrimitive.readyEvent.addEventListener(() => {
|
||||
// nodes = ddPrimitive.pickIds.map(item => item.object.detail.node)
|
||||
// nodes.forEach(i => {
|
||||
// // console.log(i._name, model.getNode(i._name))
|
||||
// if (new RegExp(/SRB\d/).test(i._name)) {
|
||||
// ddPrimitive.getNode(i._name).show = false
|
||||
// }
|
||||
// })
|
||||
// computePath(ddPrimitive, ddTrajData, type)
|
||||
// playDaodan(ddPrimitive, nodes)
|
||||
// })
|
||||
|
||||
ddPrimitive.readyPromise.then(model => {
|
||||
console.log('model', model)
|
||||
nodes = model.pickIds.map(item => item.object.detail.node)
|
||||
console.log('nodes', nodes)
|
||||
// nodes.forEach(i => {
|
||||
// // console.log(i._name, model.getNode(i._name))
|
||||
// if (new RegExp(/SRB\d/).test(i._name)) {
|
||||
// model.getNode(i._name).show = false
|
||||
// }
|
||||
// })
|
||||
computePath(ddPrimitive, trajData, type)
|
||||
})
|
||||
}
|
||||
|
||||
let minTime = 0
|
||||
function initDaodan() {
|
||||
saveDataToStore()
|
||||
minTime = getMinTime([
|
||||
...toRaw(trajData.value.data),
|
||||
// ...toRaw(interceptData.value.map(item => toRaw(item.data))).flat(Infinity),
|
||||
])
|
||||
// console.log(minTime)
|
||||
addDaodan(trajData.value)
|
||||
aniIndexMap.set(trajData.value.id, 0)
|
||||
// interceptData.value.forEach(item => {
|
||||
// const { id, data } = item
|
||||
// addDaodan(item, 1)
|
||||
// aniIndexMap.set(id, 0)
|
||||
// })
|
||||
}
|
||||
|
||||
function getMinTime(data) {
|
||||
let minTime = Infinity
|
||||
data.forEach(item => {
|
||||
minTime = Math.min(item.time, minTime)
|
||||
})
|
||||
return minTime
|
||||
}
|
||||
|
||||
const pathLine = new Map()
|
||||
|
||||
function computePath(daodan, trajData, type) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const points = ddTrajData.map(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: time - minTime,
|
||||
}
|
||||
})
|
||||
const totalAnimationTime = points.at(-1).time
|
||||
const startTime = Cesium.JulianDate.now()
|
||||
|
||||
const positionProperty = new Cesium.SampledPositionProperty()
|
||||
// 添加路径点到位置属性
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
// const point = viewer.entities.add({
|
||||
// position: point.position,
|
||||
// point: {
|
||||
// color: Cesium.Color.WHITE,
|
||||
// pixelSize: 5,
|
||||
// },
|
||||
// })
|
||||
positionProperty.addSample(time, point.position)
|
||||
})
|
||||
positionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
// interpolationDegree: 5,
|
||||
// interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
|
||||
})
|
||||
const line = createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
})
|
||||
|
||||
pathLine.set(id, line)
|
||||
if (type === 0) {
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: boosterList.value,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'booster',
|
||||
})
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: interceptData.value,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'intercept',
|
||||
})
|
||||
}
|
||||
daodanAnimation({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
daodan,
|
||||
trajData,
|
||||
})
|
||||
}
|
||||
|
||||
const boosterMap = new Map()
|
||||
const interceptMap = new Map()
|
||||
const interceptTime = ref()
|
||||
function computeBoosterOrInterceptPath({
|
||||
dataList,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
}) {
|
||||
dataList.forEach((dataItem, index) => {
|
||||
const { id, data } = dataItem
|
||||
let points = []
|
||||
data.forEach(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
const position = getOnePositionFromTime(
|
||||
startTime,
|
||||
(time - minTime) / 1000,
|
||||
positionProperty
|
||||
)
|
||||
|
||||
// points.push({
|
||||
// position,
|
||||
// time: time - minTime,
|
||||
// })
|
||||
|
||||
const lonlat = cartesian32LonLat(position)
|
||||
|
||||
const pointTime = time - minTime
|
||||
|
||||
const aniPointTime = pointTime + (type === 'booster' ? 10000 : -10000)
|
||||
type === 'intercept' && (interceptTime.value = pointTime / 1000)
|
||||
const pointInline = {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: aniPointTime,
|
||||
}
|
||||
// points.push(pointInline)
|
||||
|
||||
points = createTrajectory(
|
||||
[lon, lat, alt, aniPointTime],
|
||||
[...lonlat, time - minTime],
|
||||
{
|
||||
useEndAsPeak: true,
|
||||
peakBias: 0.65,
|
||||
midOffset: 1000,
|
||||
}
|
||||
).map(item => {
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(item.lon, item.lat, item.alt),
|
||||
time: item.t,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const dataPositionProperty = new Cesium.SampledPositionProperty()
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
|
||||
// viewer.entities.add({
|
||||
// position: point.position,
|
||||
// point: {
|
||||
// color: Cesium.Color.ORANGE,
|
||||
// pixelSize: 5,
|
||||
// },
|
||||
// })
|
||||
dataPositionProperty.addSample(time, point.position)
|
||||
})
|
||||
dataPositionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
})
|
||||
|
||||
const dataLine = createLine({
|
||||
totalAnimationTime: points.at(-1).time,
|
||||
startTime,
|
||||
positionProperty: dataPositionProperty,
|
||||
type: 2,
|
||||
color: type === 'booster' ? 'ORANGE' : 'RED',
|
||||
})
|
||||
|
||||
const modelUrl =
|
||||
type === 'booster'
|
||||
? `./models/美三叉戟2第${['一', '二', '三'][index]}级.glb`
|
||||
: './models/美三叉戟2动画.glb'
|
||||
|
||||
dataLine.model = {
|
||||
show: true,
|
||||
uri: modelUrl,
|
||||
runAnimations: false,
|
||||
// uri: `./models/第${['一', '二', '三'][index]}级.glb`,
|
||||
scale: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
minimumPixelSize: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
}
|
||||
dataLine.orientation = new Cesium.VelocityOrientationProperty(
|
||||
dataPositionProperty
|
||||
)
|
||||
|
||||
type === 'booster'
|
||||
? boosterMap.set(id, dataLine)
|
||||
: interceptMap.set(id, dataLine)
|
||||
})
|
||||
}
|
||||
|
||||
function daodanAnimation(params) {
|
||||
const { totalAnimationTime, startTime, positionProperty, daodan, trajData } =
|
||||
params
|
||||
// const { data: ddTrajData } = trajData
|
||||
// dianhuo(daodan)
|
||||
|
||||
// setTimeout(() => {
|
||||
// modelAnimationController({
|
||||
// primitive: daodan,
|
||||
// type: '第一级发动机 MoveZ',
|
||||
// initVal: 0,
|
||||
// minVal: -450,
|
||||
// step: -3,
|
||||
// fn: () => {
|
||||
// console.log(
|
||||
// `一级脱离`,
|
||||
// 'color: red;font-size: 20px;border: 1px solid red'
|
||||
// )
|
||||
// nodes.forEach(i => {
|
||||
// const nodeName = i._name
|
||||
// if (new RegExp(/第一级发动机/).test(nodeName)) {
|
||||
// daodan.getNode(nodeName).show = false
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
// })
|
||||
// }, 3000)
|
||||
|
||||
let lastFrameTime = performance.now()
|
||||
let customElapsedTime = 0
|
||||
let isAnimationRunning = true
|
||||
let explosion = null
|
||||
|
||||
viewer.scene.preRender.addEventListener(() => {
|
||||
if (!isAnimationRunning) {
|
||||
return
|
||||
}
|
||||
const currentFrameTime = performance.now() // 当前帧的时间戳
|
||||
const deltaTime = (currentFrameTime - lastFrameTime) / 1000
|
||||
lastFrameTime = currentFrameTime
|
||||
customElapsedTime += deltaTime
|
||||
if (customElapsedTime >= totalAnimationTime) {
|
||||
customElapsedTime = totalAnimationTime // 限制时间为总时长
|
||||
isAnimationRunning = false // 停止动画
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
// ddNodesAnimationController({
|
||||
// ddPrimitive: daodan,
|
||||
// curTime: minTime + customElapsedTime * 1000,
|
||||
// trajData,
|
||||
// })
|
||||
|
||||
// 计算自定义时间下的位置
|
||||
const customTime = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
// console.log(customTime)
|
||||
|
||||
const position = positionProperty.getValue(customTime)
|
||||
const nextPosition = positionProperty.getValue(
|
||||
Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime + 0.1,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
)
|
||||
|
||||
if (interceptTime.value && customElapsedTime >= interceptTime.value) {
|
||||
customElapsedTime = totalAnimationTime
|
||||
isAnimationRunning = false
|
||||
explosion = explosionEffect(position)
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
if (position && !nextPosition && !explosion) {
|
||||
// const [lon, lat, height] = cartesian32LonLat(position)
|
||||
// explosion = new ExplosionEffect(viewer, {
|
||||
// lng: lon,
|
||||
// lat: lat,
|
||||
// height,
|
||||
// })
|
||||
// setTimeout(() => {
|
||||
// explosion && explosion.remove()
|
||||
// }, 6000)
|
||||
explosion = explosionEffect(position)
|
||||
}
|
||||
if (position && nextPosition) {
|
||||
const fixedFrameTransform =
|
||||
Cesium.Transforms.localFrameToFixedFrameGenerator('north', 'west')
|
||||
const hpr = getHeadingPitchRoll(position, nextPosition)
|
||||
// console.log(hpr, 'hpr')
|
||||
Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpr,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform,
|
||||
daodan.modelMatrix
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function explosionEffect(position) {
|
||||
const [lon, lat, height] = cartesian32LonLat(position)
|
||||
const explosion = new ExplosionEffect(viewer, {
|
||||
lng: lon,
|
||||
lat: lat,
|
||||
height,
|
||||
})
|
||||
setTimeout(() => {
|
||||
explosion && explosion.remove()
|
||||
}, 6000)
|
||||
return explosion
|
||||
}
|
||||
function dianhuo(ddPrimitive) {
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'BoosterFlames Size',
|
||||
initVal: 0,
|
||||
maxVal: 1,
|
||||
step: 0.1,
|
||||
fn: () => {
|
||||
console.log('%c点火', 'color: red;font-size: 20px;border: 1px solid red')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const aniIndexMap = new Map()
|
||||
function ddNodesAnimationController(params) {
|
||||
const { ddPrimitive, curTime, trajData } = params
|
||||
const { id: ddId, data: ddTrajData } = trajData
|
||||
const nodeAniList = ddTrajData.filter(item => item.detached)
|
||||
|
||||
if (!ddPrimitive || !aniIndexMap.has(ddId)) return
|
||||
|
||||
const aniIndex = aniIndexMap.get(ddId)
|
||||
const aniTime = nodeAniList[aniIndex]?.time || Infinity
|
||||
if (curTime >= aniTime) {
|
||||
if (aniIndex === 0) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'BoosterFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: function () {
|
||||
console.log(
|
||||
`%c${ddId}--熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 0,
|
||||
maxVal: 1,
|
||||
step: 0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级点火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--一级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Booster/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveY',
|
||||
initVal: 0,
|
||||
minVal: -15,
|
||||
step: -0.1,
|
||||
})
|
||||
},
|
||||
})
|
||||
} else if (aniIndex === 1) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Open',
|
||||
initVal: 0,
|
||||
maxVal: 45,
|
||||
step: 0.5,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Separate',
|
||||
initVal: 0,
|
||||
minVal: -10,
|
||||
step: -0.1,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Drop',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Fairing\d/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing MoveY',
|
||||
initVal: 0,
|
||||
minVal: -150,
|
||||
step: -3,
|
||||
})
|
||||
} else if (aniIndex === 2) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -200,
|
||||
step: -2,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--三级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/InterstageAdapter/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveY',
|
||||
initVal: 0,
|
||||
minVal: -300,
|
||||
step: -2,
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
// modelAnimationController({
|
||||
// primitive: ddPrimitive,
|
||||
// type: 'Booster MoveZ',
|
||||
// initVal: 0,
|
||||
// minVal: -150,
|
||||
// step: -1,
|
||||
// fn: () => {
|
||||
// console.log(
|
||||
// `%c${ddId}--三级掉落`,
|
||||
// 'color: red;font-size: 20px;border: 1px solid red'
|
||||
// )
|
||||
// },
|
||||
// })
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHeadingPitchRoll(curPos, nextPos) {
|
||||
if (!curPos || !nextPos || Cesium.Cartesian3.equals(curPos, nextPos)) {
|
||||
return Cesium.Quaternion.IDENTITY // 默认方向
|
||||
}
|
||||
|
||||
// 计算方向向量
|
||||
const direction = Cesium.Cartesian3.subtract(
|
||||
nextPos,
|
||||
curPos,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
Cesium.Cartesian3.normalize(direction, direction)
|
||||
|
||||
// 计算 ENU 变换矩阵
|
||||
let transformMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(curPos)
|
||||
|
||||
// 将方向向量转换到 ENU 坐标系
|
||||
let localDirection = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transformMatrix, new Cesium.Matrix4()),
|
||||
direction,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
let heading = Math.atan2(localDirection.x, localDirection.y)
|
||||
heading = Cesium.Math.negativePiToPi(heading)
|
||||
|
||||
const pitch = Math.asin(Cesium.Math.clamp(localDirection.z, -1.0, 1.0))
|
||||
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, 0)
|
||||
return hpr
|
||||
}
|
||||
|
||||
function createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
color,
|
||||
}) {
|
||||
const positionList = getPositionFromTime(
|
||||
startTime,
|
||||
positionProperty,
|
||||
totalAnimationTime
|
||||
)
|
||||
// if (type === 2) {
|
||||
// console.log('positionList', positionList)
|
||||
// }
|
||||
|
||||
return viewer.entities.add({
|
||||
position: positionProperty,
|
||||
polyline: {
|
||||
positions: positionList,
|
||||
width: 8,
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
glowPower: 0.1,
|
||||
color: type
|
||||
? type === 2 && Cesium.Color[color].withAlpha(0.5)
|
||||
: Cesium.Color.BLUE.withAlpha(0.5),
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function modelAnimationController(controller) {
|
||||
const { type, initVal, maxVal, fn, step, minVal, primitive } = controller
|
||||
let num = initVal
|
||||
let stopFrame
|
||||
const max = maxVal ?? 1
|
||||
const min = minVal ?? -99999
|
||||
const duration = step ?? 0.1
|
||||
const render = () => {
|
||||
num += duration
|
||||
primitive.setArticulationStage(type, num)
|
||||
primitive.applyArticulations()
|
||||
stopFrame = requestAnimationFrame(render)
|
||||
// console.log(num >= max || num <= min, type, num, min)
|
||||
if (num > max || num <= min) {
|
||||
window.cancelAnimationFrame(stopFrame)
|
||||
fn && fn()
|
||||
}
|
||||
}
|
||||
render()
|
||||
}
|
||||
|
||||
function removeAllEntity() {
|
||||
boosterMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
boosterMap.clear()
|
||||
|
||||
interceptMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
interceptMap.clear()
|
||||
|
||||
pathLine.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
pathLine.clear()
|
||||
|
||||
ddMap.values().forEach(i => {
|
||||
viewer.scene.primitives.remove(i.primitive)
|
||||
})
|
||||
ddMap.clear()
|
||||
}
|
@ -0,0 +1,589 @@
|
||||
import ExplosionEffect from '@/js/Explosion'
|
||||
import {
|
||||
cartesian32LonLat,
|
||||
getPositionFromTime,
|
||||
getOnePositionFromTime,
|
||||
test,
|
||||
createTrajectory,
|
||||
} from '@/utils/pos'
|
||||
|
||||
const ddScale = 30
|
||||
export function useDDPlay({
|
||||
ddData: { trajData, interceptData, boosterList },
|
||||
startTimeStampOfAnimation,
|
||||
isAniCalcBasedOnCurrentTime,
|
||||
minTime,
|
||||
}) {
|
||||
const baseTime = isAniCalcBasedOnCurrentTime
|
||||
? minTime
|
||||
: startTimeStampOfAnimation
|
||||
const pathLine = new Map()
|
||||
|
||||
const ddMap = new Map()
|
||||
|
||||
let nodes = []
|
||||
|
||||
function addDaodan(type = 0) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const position = Cesium.Cartesian3.fromDegrees(
|
||||
ddTrajData[0].lon,
|
||||
ddTrajData[0].lat,
|
||||
ddTrajData[0].alt
|
||||
)
|
||||
const hpRoll = new Cesium.HeadingPitchRoll()
|
||||
const fixedFrameTransform =
|
||||
Cesium.Transforms.localFrameToFixedFrameGenerator('north', 'west')
|
||||
const modelObj = Cesium.Model.fromGltf({
|
||||
url: './models/美三叉戟2动画.glb',
|
||||
modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpRoll,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform
|
||||
),
|
||||
scale: ddScale,
|
||||
minimumPixelSize: ddScale,
|
||||
})
|
||||
|
||||
const ddPrimitive = viewer.scene.primitives.add(modelObj)
|
||||
|
||||
ddMap.set(id, {
|
||||
primitive: ddPrimitive,
|
||||
nodes,
|
||||
})
|
||||
|
||||
ddPrimitive.readyPromise.then(model => {
|
||||
nodes = model.pickIds.map(item => item.object.detail.node)
|
||||
computePath(ddPrimitive, trajData, type)
|
||||
})
|
||||
}
|
||||
|
||||
function computePath(daodan, trajData, type) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const points = ddTrajData.map(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
console.log(time, baseTime, time - baseTime)
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: time - baseTime,
|
||||
}
|
||||
})
|
||||
const totalAnimationTime = points.at(-1).time
|
||||
const startTime = Cesium.JulianDate.fromDate(
|
||||
new Date(startTimeStampOfAnimation)
|
||||
)
|
||||
|
||||
const positionProperty = new Cesium.SampledPositionProperty()
|
||||
// 添加路径点到位置属性
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
positionProperty.addSample(time, point.position)
|
||||
})
|
||||
positionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
})
|
||||
const line = createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
})
|
||||
|
||||
pathLine.set(id, line)
|
||||
if (type === 0) {
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: boosterList,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'booster',
|
||||
})
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: interceptData,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'intercept',
|
||||
})
|
||||
}
|
||||
daodanAnimation({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
daodan,
|
||||
trajData,
|
||||
})
|
||||
}
|
||||
|
||||
const boosterMap = new Map()
|
||||
const interceptMap = new Map()
|
||||
const interceptTime = ref()
|
||||
function computeBoosterOrInterceptPath({
|
||||
dataList,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
}) {
|
||||
dataList.forEach((dataItem, index) => {
|
||||
const { id, data } = dataItem
|
||||
let points = []
|
||||
data.forEach(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
const position = getOnePositionFromTime(
|
||||
startTime,
|
||||
(time - baseTime) / 1000,
|
||||
positionProperty
|
||||
)
|
||||
|
||||
const lonlat = cartesian32LonLat(position)
|
||||
|
||||
const pointTime = time - baseTime
|
||||
|
||||
const aniPointTime = pointTime + (type === 'booster' ? 10000 : -10000)
|
||||
type === 'intercept' && (interceptTime.value = pointTime / 1000)
|
||||
const pointInline = {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: aniPointTime,
|
||||
}
|
||||
// points.push(pointInline)
|
||||
|
||||
points = createTrajectory(
|
||||
[lon, lat, alt, aniPointTime],
|
||||
[...lonlat, time - baseTime],
|
||||
{
|
||||
useEndAsPeak: true,
|
||||
peakBias: 0.65,
|
||||
midOffset: 1000,
|
||||
}
|
||||
).map(item => {
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(
|
||||
item.lon,
|
||||
item.lat,
|
||||
item.alt
|
||||
),
|
||||
time: item.t,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const dataPositionProperty = new Cesium.SampledPositionProperty()
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
|
||||
dataPositionProperty.addSample(time, point.position)
|
||||
})
|
||||
dataPositionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
})
|
||||
|
||||
const dataLine = createLine({
|
||||
totalAnimationTime: points.at(-1).time,
|
||||
startTime,
|
||||
positionProperty: dataPositionProperty,
|
||||
type: 2,
|
||||
color: type === 'booster' ? 'ORANGE' : 'RED',
|
||||
})
|
||||
|
||||
const modelUrl =
|
||||
type === 'booster'
|
||||
? `./models/美三叉戟2第${['一', '二', '三'][index]}级.glb`
|
||||
: './models/美三叉戟2动画.glb'
|
||||
|
||||
dataLine.model = {
|
||||
show: true,
|
||||
uri: modelUrl,
|
||||
runAnimations: false,
|
||||
// uri: `./models/第${['一', '二', '三'][index]}级.glb`,
|
||||
scale: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
minimumPixelSize: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
}
|
||||
dataLine.orientation = new Cesium.VelocityOrientationProperty(
|
||||
dataPositionProperty
|
||||
)
|
||||
|
||||
type === 'booster'
|
||||
? boosterMap.set(id, dataLine)
|
||||
: interceptMap.set(id, dataLine)
|
||||
})
|
||||
}
|
||||
|
||||
function daodanAnimation(params) {
|
||||
const {
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
daodan,
|
||||
trajData,
|
||||
} = params
|
||||
|
||||
let lastFrameTime = isAniCalcBasedOnCurrentTime
|
||||
? performance.now()
|
||||
: Cesium.JulianDate.toDate(viewer.clock.currentTime).getTime()
|
||||
let customElapsedTime = 0
|
||||
let isAnimationRunning = true
|
||||
let explosion = null
|
||||
|
||||
viewer.scene.preRender.addEventListener(() => {
|
||||
if (!isAnimationRunning) {
|
||||
return
|
||||
}
|
||||
const currentFrameTime = isAniCalcBasedOnCurrentTime
|
||||
? performance.now()
|
||||
: Cesium.JulianDate.toDate(viewer.clock.currentTime).getTime() // 当前帧的时间戳
|
||||
const deltaTime = (currentFrameTime - lastFrameTime) / 1000
|
||||
lastFrameTime = currentFrameTime
|
||||
customElapsedTime += deltaTime
|
||||
if (customElapsedTime >= totalAnimationTime) {
|
||||
customElapsedTime = totalAnimationTime // 限制时间为总时长
|
||||
isAnimationRunning = false // 停止动画
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
// 计算自定义时间下的位置
|
||||
const customTime = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
// console.log(customTime)
|
||||
|
||||
const position = positionProperty.getValue(customTime)
|
||||
const nextPosition = positionProperty.getValue(
|
||||
Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime + 0.1,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
)
|
||||
|
||||
if (interceptTime.value && customElapsedTime >= interceptTime.value) {
|
||||
customElapsedTime = totalAnimationTime
|
||||
isAnimationRunning = false
|
||||
explosion = explosionEffect(position)
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
if (position && !nextPosition && !explosion) {
|
||||
explosion = explosionEffect(position)
|
||||
}
|
||||
if (position && nextPosition) {
|
||||
const fixedFrameTransform =
|
||||
Cesium.Transforms.localFrameToFixedFrameGenerator('north', 'west')
|
||||
const hpr = getHeadingPitchRoll(position, nextPosition)
|
||||
// console.log(hpr, 'hpr')
|
||||
Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpr,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform,
|
||||
daodan.modelMatrix
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function explosionEffect(position) {
|
||||
const [lon, lat, height] = cartesian32LonLat(position)
|
||||
const explosion = new ExplosionEffect(viewer, {
|
||||
lng: lon,
|
||||
lat: lat,
|
||||
height,
|
||||
})
|
||||
setTimeout(() => {
|
||||
explosion && explosion.remove()
|
||||
}, 6000)
|
||||
return explosion
|
||||
}
|
||||
|
||||
function modelAnimationController(controller) {
|
||||
const { type, initVal, maxVal, fn, step, minVal, primitive } = controller
|
||||
let num = initVal
|
||||
let stopFrame
|
||||
const max = maxVal ?? 1
|
||||
const min = minVal ?? -99999
|
||||
const duration = step ?? 0.1
|
||||
const render = () => {
|
||||
num += duration
|
||||
primitive.setArticulationStage(type, num)
|
||||
primitive.applyArticulations()
|
||||
stopFrame = requestAnimationFrame(render)
|
||||
// console.log(num >= max || num <= min, type, num, min)
|
||||
if (num > max || num <= min) {
|
||||
window.cancelAnimationFrame(stopFrame)
|
||||
fn && fn()
|
||||
}
|
||||
}
|
||||
render()
|
||||
}
|
||||
|
||||
const aniIndexMap = new Map()
|
||||
function ddNodesAnimationController(params) {
|
||||
const { ddPrimitive, curTime, trajData } = params
|
||||
const { id: ddId, data: ddTrajData } = trajData
|
||||
const nodeAniList = ddTrajData.filter(item => item.detached)
|
||||
|
||||
if (!ddPrimitive || !aniIndexMap.has(ddId)) return
|
||||
|
||||
const aniIndex = aniIndexMap.get(ddId)
|
||||
const aniTime = nodeAniList[aniIndex]?.time || Infinity
|
||||
if (curTime >= aniTime) {
|
||||
if (aniIndex === 0) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'BoosterFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: function () {
|
||||
console.log(
|
||||
`%c${ddId}--熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 0,
|
||||
maxVal: 1,
|
||||
step: 0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级点火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--一级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Booster/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveY',
|
||||
initVal: 0,
|
||||
minVal: -15,
|
||||
step: -0.1,
|
||||
})
|
||||
},
|
||||
})
|
||||
} else if (aniIndex === 1) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Open',
|
||||
initVal: 0,
|
||||
maxVal: 45,
|
||||
step: 0.5,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Separate',
|
||||
initVal: 0,
|
||||
minVal: -10,
|
||||
step: -0.1,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Drop',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Fairing\d/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing MoveY',
|
||||
initVal: 0,
|
||||
minVal: -150,
|
||||
step: -3,
|
||||
})
|
||||
} else if (aniIndex === 2) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -200,
|
||||
step: -2,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--三级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/InterstageAdapter/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveY',
|
||||
initVal: 0,
|
||||
minVal: -300,
|
||||
step: -2,
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
// modelAnimationController({
|
||||
// primitive: ddPrimitive,
|
||||
// type: 'Booster MoveZ',
|
||||
// initVal: 0,
|
||||
// minVal: -150,
|
||||
// step: -1,
|
||||
// fn: () => {
|
||||
// console.log(
|
||||
// `%c${ddId}--三级掉落`,
|
||||
// 'color: red;font-size: 20px;border: 1px solid red'
|
||||
// )
|
||||
// },
|
||||
// })
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHeadingPitchRoll(curPos, nextPos) {
|
||||
if (!curPos || !nextPos || Cesium.Cartesian3.equals(curPos, nextPos)) {
|
||||
return Cesium.Quaternion.IDENTITY // 默认方向
|
||||
}
|
||||
|
||||
// 计算方向向量
|
||||
const direction = Cesium.Cartesian3.subtract(
|
||||
nextPos,
|
||||
curPos,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
Cesium.Cartesian3.normalize(direction, direction)
|
||||
|
||||
// 计算 ENU 变换矩阵
|
||||
let transformMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(curPos)
|
||||
|
||||
// 将方向向量转换到 ENU 坐标系
|
||||
let localDirection = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transformMatrix, new Cesium.Matrix4()),
|
||||
direction,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
let heading = Math.atan2(localDirection.x, localDirection.y)
|
||||
heading = Cesium.Math.negativePiToPi(heading)
|
||||
|
||||
const pitch = Math.asin(Cesium.Math.clamp(localDirection.z, -1.0, 1.0))
|
||||
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, 0)
|
||||
return hpr
|
||||
}
|
||||
|
||||
function createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
color,
|
||||
}) {
|
||||
const positionList = getPositionFromTime(
|
||||
startTime,
|
||||
positionProperty,
|
||||
totalAnimationTime
|
||||
)
|
||||
|
||||
return viewer.entities.add({
|
||||
position: positionProperty,
|
||||
polyline: {
|
||||
positions: positionList,
|
||||
width: 8,
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
glowPower: 0.1,
|
||||
color: type
|
||||
? type === 2 && Cesium.Color[color].withAlpha(0.5)
|
||||
: Cesium.Color.BLUE.withAlpha(0.5),
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function removeAllEntity() {
|
||||
boosterMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
boosterMap.clear()
|
||||
|
||||
interceptMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
interceptMap.clear()
|
||||
|
||||
pathLine.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
pathLine.clear()
|
||||
|
||||
ddMap.values().forEach(i => {
|
||||
viewer.scene.primitives.remove(i.primitive)
|
||||
})
|
||||
ddMap.clear()
|
||||
}
|
||||
|
||||
return {
|
||||
addDaodan,
|
||||
}
|
||||
}
|
@ -1,20 +1,11 @@
|
||||
import { ref, toRaw } from 'vue'
|
||||
import { useDaodan } from '../../ddHooks'
|
||||
import {
|
||||
cartesian32LonLat,
|
||||
getPositionFromTime,
|
||||
getOnePositionFromTime,
|
||||
test,
|
||||
createTrajectory,
|
||||
} from '@/utils/pos'
|
||||
import ExplosionEffect from '@/js/Explosion'
|
||||
import { cartesian32LonLat } from '@/utils/pos'
|
||||
import { generateId } from '@/utils/id'
|
||||
import store from 'store2'
|
||||
import { lineString, bezierSpline } from '@turf/turf'
|
||||
import { useDDPlay } from './ddPlayHooks'
|
||||
// import { useDaodan } from '../../ddHooks'
|
||||
|
||||
const ddScale = 30
|
||||
|
||||
const trajData = ref({
|
||||
id: 'dd',
|
||||
data: [
|
||||
@ -182,78 +173,27 @@ function handleClickPoint(rowData) {
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
}
|
||||
|
||||
let nodes = []
|
||||
let ddMap = new Map()
|
||||
function addDaodan(trajData, type = 0) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const position = Cesium.Cartesian3.fromDegrees(
|
||||
ddTrajData[0].lon,
|
||||
ddTrajData[0].lat,
|
||||
ddTrajData[0].alt
|
||||
)
|
||||
const hpRoll = new Cesium.HeadingPitchRoll()
|
||||
// const fixedFrameTransform = Cesium.Transforms.eastNorthUpToFixedFrame
|
||||
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(
|
||||
'north',
|
||||
'west'
|
||||
)
|
||||
// hpRoll.pitch = (90 * Math.PI) / 180
|
||||
const modelObj = Cesium.Model.fromGltf({
|
||||
// url: './models/launchvehicle.glb',
|
||||
url: './models/美三叉戟2动画.glb',
|
||||
modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpRoll,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform
|
||||
),
|
||||
scale: ddScale,
|
||||
minimumPixelSize: ddScale,
|
||||
})
|
||||
|
||||
const ddPrimitive = viewer.scene.primitives.add(modelObj)
|
||||
|
||||
ddMap.set(id, {
|
||||
primitive: ddPrimitive,
|
||||
nodes,
|
||||
})
|
||||
|
||||
// ddPrimitive.readyEvent.addEventListener(() => {
|
||||
// nodes = ddPrimitive.pickIds.map(item => item.object.detail.node)
|
||||
// nodes.forEach(i => {
|
||||
// // console.log(i._name, model.getNode(i._name))
|
||||
// if (new RegExp(/SRB\d/).test(i._name)) {
|
||||
// ddPrimitive.getNode(i._name).show = false
|
||||
// }
|
||||
// })
|
||||
// computePath(ddPrimitive, ddTrajData, type)
|
||||
// playDaodan(ddPrimitive, nodes)
|
||||
// })
|
||||
|
||||
ddPrimitive.readyPromise.then(model => {
|
||||
console.log('model', model)
|
||||
nodes = model.pickIds.map(item => item.object.detail.node)
|
||||
console.log('nodes', nodes)
|
||||
// nodes.forEach(i => {
|
||||
// // console.log(i._name, model.getNode(i._name))
|
||||
// if (new RegExp(/SRB\d/).test(i._name)) {
|
||||
// model.getNode(i._name).show = false
|
||||
// }
|
||||
// })
|
||||
computePath(ddPrimitive, trajData, type)
|
||||
})
|
||||
}
|
||||
|
||||
let minTime = 0
|
||||
function initDaodan() {
|
||||
saveDataToStore()
|
||||
minTime = getMinTime([
|
||||
const minTime = getMinTime([
|
||||
...toRaw(trajData.value.data),
|
||||
// ...toRaw(interceptData.value.map(item => toRaw(item.data))).flat(Infinity),
|
||||
])
|
||||
// console.log(minTime)
|
||||
addDaodan(trajData.value)
|
||||
aniIndexMap.set(trajData.value.id, 0)
|
||||
const { addDaodan } = useDDPlay({
|
||||
ddData: {
|
||||
...toRaw(daodanData.value),
|
||||
trajData: trajData.value,
|
||||
boosterList: boosterList.value,
|
||||
interceptData: interceptData.value,
|
||||
},
|
||||
startTimeStampOfAnimation: Date.now(),
|
||||
isAniCalcBasedOnCurrentTime: true,
|
||||
minTime: minTime,
|
||||
})
|
||||
|
||||
addDaodan()
|
||||
// aniIndexMap.set(trajData.value.id, 0)
|
||||
// interceptData.value.forEach(item => {
|
||||
// const { id, data } = item
|
||||
// addDaodan(item, 1)
|
||||
@ -269,304 +209,6 @@ function getMinTime(data) {
|
||||
return minTime
|
||||
}
|
||||
|
||||
const pathLine = new Map()
|
||||
|
||||
function computePath(daodan, trajData, type) {
|
||||
const { id, data: ddTrajData } = trajData
|
||||
const points = ddTrajData.map(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: time - minTime,
|
||||
}
|
||||
})
|
||||
const totalAnimationTime = points.at(-1).time
|
||||
const startTime = Cesium.JulianDate.now()
|
||||
|
||||
const positionProperty = new Cesium.SampledPositionProperty()
|
||||
// 添加路径点到位置属性
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
// const point = viewer.entities.add({
|
||||
// position: point.position,
|
||||
// point: {
|
||||
// color: Cesium.Color.WHITE,
|
||||
// pixelSize: 5,
|
||||
// },
|
||||
// })
|
||||
positionProperty.addSample(time, point.position)
|
||||
})
|
||||
positionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
// interpolationDegree: 5,
|
||||
// interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
|
||||
})
|
||||
const line = createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
})
|
||||
|
||||
pathLine.set(id, line)
|
||||
if (type === 0) {
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: boosterList.value,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'booster',
|
||||
})
|
||||
computeBoosterOrInterceptPath({
|
||||
dataList: interceptData.value,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type: 'intercept',
|
||||
})
|
||||
}
|
||||
daodanAnimation({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
daodan,
|
||||
trajData,
|
||||
})
|
||||
}
|
||||
|
||||
const boosterMap = new Map()
|
||||
const interceptMap = new Map()
|
||||
const interceptTime = ref()
|
||||
function computeBoosterOrInterceptPath({
|
||||
dataList,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
}) {
|
||||
dataList.forEach((dataItem, index) => {
|
||||
const { id, data } = dataItem
|
||||
let points = []
|
||||
data.forEach(item => {
|
||||
const { time, lon, lat, alt } = item
|
||||
|
||||
const position = getOnePositionFromTime(
|
||||
startTime,
|
||||
(time - minTime) / 1000,
|
||||
positionProperty
|
||||
)
|
||||
|
||||
// points.push({
|
||||
// position,
|
||||
// time: time - minTime,
|
||||
// })
|
||||
|
||||
const lonlat = cartesian32LonLat(position)
|
||||
|
||||
const pointTime = time - minTime
|
||||
|
||||
const aniPointTime = pointTime + (type === 'booster' ? 10000 : -10000)
|
||||
type === 'intercept' && (interceptTime.value = pointTime / 1000)
|
||||
const pointInline = {
|
||||
position: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
|
||||
time: aniPointTime,
|
||||
}
|
||||
// points.push(pointInline)
|
||||
|
||||
points = createTrajectory(
|
||||
[lon, lat, alt, aniPointTime],
|
||||
[...lonlat, time - minTime],
|
||||
{
|
||||
useEndAsPeak: true,
|
||||
peakBias: 0.65,
|
||||
midOffset: 1000,
|
||||
}
|
||||
).map(item => {
|
||||
return {
|
||||
position: Cesium.Cartesian3.fromDegrees(item.lon, item.lat, item.alt),
|
||||
time: item.t,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const dataPositionProperty = new Cesium.SampledPositionProperty()
|
||||
points.forEach(point => {
|
||||
const time = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
point.time / 1000,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
|
||||
// viewer.entities.add({
|
||||
// position: point.position,
|
||||
// point: {
|
||||
// color: Cesium.Color.ORANGE,
|
||||
// pixelSize: 5,
|
||||
// },
|
||||
// })
|
||||
dataPositionProperty.addSample(time, point.position)
|
||||
})
|
||||
dataPositionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
})
|
||||
|
||||
const dataLine = createLine({
|
||||
totalAnimationTime: points.at(-1).time,
|
||||
startTime,
|
||||
positionProperty: dataPositionProperty,
|
||||
type: 2,
|
||||
color: type === 'booster' ? 'ORANGE' : 'RED',
|
||||
})
|
||||
|
||||
const modelUrl =
|
||||
type === 'booster'
|
||||
? `./models/美三叉戟2第${['一', '二', '三'][index]}级.glb`
|
||||
: './models/美三叉戟2动画.glb'
|
||||
|
||||
dataLine.model = {
|
||||
show: true,
|
||||
uri: modelUrl,
|
||||
runAnimations: false,
|
||||
// uri: `./models/第${['一', '二', '三'][index]}级.glb`,
|
||||
scale: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
minimumPixelSize: type === 'booster' ? ddScale / 4 : ddScale,
|
||||
}
|
||||
dataLine.orientation = new Cesium.VelocityOrientationProperty(
|
||||
dataPositionProperty
|
||||
)
|
||||
|
||||
type === 'booster'
|
||||
? boosterMap.set(id, dataLine)
|
||||
: interceptMap.set(id, dataLine)
|
||||
})
|
||||
}
|
||||
|
||||
function daodanAnimation(params) {
|
||||
const { totalAnimationTime, startTime, positionProperty, daodan, trajData } =
|
||||
params
|
||||
// const { data: ddTrajData } = trajData
|
||||
// dianhuo(daodan)
|
||||
|
||||
// setTimeout(() => {
|
||||
// modelAnimationController({
|
||||
// primitive: daodan,
|
||||
// type: '第一级发动机 MoveZ',
|
||||
// initVal: 0,
|
||||
// minVal: -450,
|
||||
// step: -3,
|
||||
// fn: () => {
|
||||
// console.log(
|
||||
// `一级脱离`,
|
||||
// 'color: red;font-size: 20px;border: 1px solid red'
|
||||
// )
|
||||
// nodes.forEach(i => {
|
||||
// const nodeName = i._name
|
||||
// if (new RegExp(/第一级发动机/).test(nodeName)) {
|
||||
// daodan.getNode(nodeName).show = false
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
// })
|
||||
// }, 3000)
|
||||
|
||||
let lastFrameTime = performance.now()
|
||||
let customElapsedTime = 0
|
||||
let isAnimationRunning = true
|
||||
let explosion = null
|
||||
|
||||
viewer.scene.preRender.addEventListener(() => {
|
||||
if (!isAnimationRunning) {
|
||||
return
|
||||
}
|
||||
const currentFrameTime = performance.now() // 当前帧的时间戳
|
||||
const deltaTime = (currentFrameTime - lastFrameTime) / 1000
|
||||
lastFrameTime = currentFrameTime
|
||||
customElapsedTime += deltaTime
|
||||
if (customElapsedTime >= totalAnimationTime) {
|
||||
customElapsedTime = totalAnimationTime // 限制时间为总时长
|
||||
isAnimationRunning = false // 停止动画
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
// ddNodesAnimationController({
|
||||
// ddPrimitive: daodan,
|
||||
// curTime: minTime + customElapsedTime * 1000,
|
||||
// trajData,
|
||||
// })
|
||||
|
||||
// 计算自定义时间下的位置
|
||||
const customTime = Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
// console.log(customTime)
|
||||
|
||||
const position = positionProperty.getValue(customTime)
|
||||
const nextPosition = positionProperty.getValue(
|
||||
Cesium.JulianDate.addSeconds(
|
||||
startTime,
|
||||
customElapsedTime + 0.1,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
)
|
||||
|
||||
if (interceptTime.value && customElapsedTime >= interceptTime.value) {
|
||||
customElapsedTime = totalAnimationTime
|
||||
isAnimationRunning = false
|
||||
explosion = explosionEffect(position)
|
||||
|
||||
setTimeout(() => {
|
||||
removeAllEntity()
|
||||
}, 6000)
|
||||
}
|
||||
if (position && !nextPosition && !explosion) {
|
||||
// const [lon, lat, height] = cartesian32LonLat(position)
|
||||
// explosion = new ExplosionEffect(viewer, {
|
||||
// lng: lon,
|
||||
// lat: lat,
|
||||
// height,
|
||||
// })
|
||||
// setTimeout(() => {
|
||||
// explosion && explosion.remove()
|
||||
// }, 6000)
|
||||
explosion = explosionEffect(position)
|
||||
}
|
||||
if (position && nextPosition) {
|
||||
const fixedFrameTransform =
|
||||
Cesium.Transforms.localFrameToFixedFrameGenerator('north', 'west')
|
||||
const hpr = getHeadingPitchRoll(position, nextPosition)
|
||||
// console.log(hpr, 'hpr')
|
||||
Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpr,
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
fixedFrameTransform,
|
||||
daodan.modelMatrix
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function explosionEffect(position) {
|
||||
const [lon, lat, height] = cartesian32LonLat(position)
|
||||
const explosion = new ExplosionEffect(viewer, {
|
||||
lng: lon,
|
||||
lat: lat,
|
||||
height,
|
||||
})
|
||||
setTimeout(() => {
|
||||
explosion && explosion.remove()
|
||||
}, 6000)
|
||||
return explosion
|
||||
}
|
||||
function dianhuo(ddPrimitive) {
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
@ -579,278 +221,3 @@ function dianhuo(ddPrimitive) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const aniIndexMap = new Map()
|
||||
function ddNodesAnimationController(params) {
|
||||
const { ddPrimitive, curTime, trajData } = params
|
||||
const { id: ddId, data: ddTrajData } = trajData
|
||||
const nodeAniList = ddTrajData.filter(item => item.detached)
|
||||
|
||||
if (!ddPrimitive || !aniIndexMap.has(ddId)) return
|
||||
|
||||
const aniIndex = aniIndexMap.get(ddId)
|
||||
const aniTime = nodeAniList[aniIndex]?.time || Infinity
|
||||
if (curTime >= aniTime) {
|
||||
if (aniIndex === 0) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'BoosterFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: function () {
|
||||
console.log(
|
||||
`%c${ddId}--熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 0,
|
||||
maxVal: 1,
|
||||
step: 0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级点火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--一级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Booster/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Booster MoveY',
|
||||
initVal: 0,
|
||||
minVal: -15,
|
||||
step: -0.1,
|
||||
})
|
||||
},
|
||||
})
|
||||
} else if (aniIndex === 1) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Open',
|
||||
initVal: 0,
|
||||
maxVal: 45,
|
||||
step: 0.5,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Separate',
|
||||
initVal: 0,
|
||||
minVal: -10,
|
||||
step: -0.1,
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing Drop',
|
||||
initVal: 0,
|
||||
minVal: -450,
|
||||
step: -3,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/Fairing\d/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'Fairing MoveY',
|
||||
initVal: 0,
|
||||
minVal: -150,
|
||||
step: -3,
|
||||
})
|
||||
} else if (aniIndex === 2) {
|
||||
aniIndexMap.set(ddId, aniIndexMap.get(ddId) + 1)
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveZ',
|
||||
initVal: 0,
|
||||
minVal: -200,
|
||||
step: -2,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--三级脱离`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
nodes.forEach(i => {
|
||||
const nodeName = i._name
|
||||
if (new RegExp(/InterstageAdapter/).test(nodeName)) {
|
||||
ddPrimitive.getNode(nodeName).show = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'InterstageAdapter MoveY',
|
||||
initVal: 0,
|
||||
minVal: -300,
|
||||
step: -2,
|
||||
})
|
||||
|
||||
modelAnimationController({
|
||||
primitive: ddPrimitive,
|
||||
type: 'UpperStageFlames Size',
|
||||
initVal: 1,
|
||||
minVal: 0,
|
||||
step: -0.05,
|
||||
fn: () => {
|
||||
console.log(
|
||||
`%c${ddId}--二级熄火`,
|
||||
'color: red;font-size: 20px;border: 1px solid red'
|
||||
)
|
||||
// modelAnimationController({
|
||||
// primitive: ddPrimitive,
|
||||
// type: 'Booster MoveZ',
|
||||
// initVal: 0,
|
||||
// minVal: -150,
|
||||
// step: -1,
|
||||
// fn: () => {
|
||||
// console.log(
|
||||
// `%c${ddId}--三级掉落`,
|
||||
// 'color: red;font-size: 20px;border: 1px solid red'
|
||||
// )
|
||||
// },
|
||||
// })
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHeadingPitchRoll(curPos, nextPos) {
|
||||
if (!curPos || !nextPos || Cesium.Cartesian3.equals(curPos, nextPos)) {
|
||||
return Cesium.Quaternion.IDENTITY // 默认方向
|
||||
}
|
||||
|
||||
// 计算方向向量
|
||||
const direction = Cesium.Cartesian3.subtract(
|
||||
nextPos,
|
||||
curPos,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
Cesium.Cartesian3.normalize(direction, direction)
|
||||
|
||||
// 计算 ENU 变换矩阵
|
||||
let transformMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(curPos)
|
||||
|
||||
// 将方向向量转换到 ENU 坐标系
|
||||
let localDirection = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transformMatrix, new Cesium.Matrix4()),
|
||||
direction,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
let heading = Math.atan2(localDirection.x, localDirection.y)
|
||||
heading = Cesium.Math.negativePiToPi(heading)
|
||||
|
||||
const pitch = Math.asin(Cesium.Math.clamp(localDirection.z, -1.0, 1.0))
|
||||
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, 0)
|
||||
return hpr
|
||||
}
|
||||
|
||||
function createLine({
|
||||
totalAnimationTime,
|
||||
startTime,
|
||||
positionProperty,
|
||||
type,
|
||||
color,
|
||||
}) {
|
||||
const positionList = getPositionFromTime(
|
||||
startTime,
|
||||
positionProperty,
|
||||
totalAnimationTime
|
||||
)
|
||||
// if (type === 2) {
|
||||
// console.log('positionList', positionList)
|
||||
// }
|
||||
|
||||
return viewer.entities.add({
|
||||
position: positionProperty,
|
||||
polyline: {
|
||||
positions: positionList,
|
||||
width: 8,
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
glowPower: 0.1,
|
||||
color: type
|
||||
? type === 2 && Cesium.Color[color].withAlpha(0.5)
|
||||
: Cesium.Color.BLUE.withAlpha(0.5),
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function modelAnimationController(controller) {
|
||||
const { type, initVal, maxVal, fn, step, minVal, primitive } = controller
|
||||
let num = initVal
|
||||
let stopFrame
|
||||
const max = maxVal ?? 1
|
||||
const min = minVal ?? -99999
|
||||
const duration = step ?? 0.1
|
||||
const render = () => {
|
||||
num += duration
|
||||
primitive.setArticulationStage(type, num)
|
||||
primitive.applyArticulations()
|
||||
stopFrame = requestAnimationFrame(render)
|
||||
// console.log(num >= max || num <= min, type, num, min)
|
||||
if (num > max || num <= min) {
|
||||
window.cancelAnimationFrame(stopFrame)
|
||||
fn && fn()
|
||||
}
|
||||
}
|
||||
render()
|
||||
}
|
||||
|
||||
function removeAllEntity() {
|
||||
boosterMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
boosterMap.clear()
|
||||
|
||||
interceptMap.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
interceptMap.clear()
|
||||
|
||||
pathLine.values().forEach(i => {
|
||||
viewer.entities.remove(i)
|
||||
})
|
||||
pathLine.clear()
|
||||
|
||||
ddMap.values().forEach(i => {
|
||||
viewer.scene.primitives.remove(i.primitive)
|
||||
})
|
||||
ddMap.clear()
|
||||
}
|
||||
|
@ -280,6 +280,7 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
||||
return {
|
||||
addHangjingPolygon,
|
||||
removeHangjingPolygon,
|
||||
addPrimitivePolygon,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ const {
|
||||
changeTime,
|
||||
} = useMultiTraj()
|
||||
|
||||
const timeRange = ref<string[]>()
|
||||
const timeRange = ref<string[]>([1183135200000 + 40 * 1000, 1183135360000])
|
||||
|
||||
const rangeShortcuts = {
|
||||
近一天: () => {
|
||||
@ -78,7 +78,6 @@ const speedOptions = [
|
||||
},
|
||||
]
|
||||
watch(times, timeArray => {
|
||||
|
||||
const segments = divideTimeRangeIntoParts(
|
||||
timeArray[0],
|
||||
timeArray[timeArray.length - 1],
|
||||
@ -115,7 +114,7 @@ function divideTimeRangeIntoParts(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-drag>
|
||||
<div v-drag:target="'content-title'">
|
||||
<panel
|
||||
title="多目标轨迹回放"
|
||||
showClose
|
||||
@ -164,7 +163,7 @@ function divideTimeRangeIntoParts(
|
||||
<n-button
|
||||
tertiary
|
||||
circle
|
||||
@click="play(timelineValue || times[0])"
|
||||
@click="play()"
|
||||
v-show="!isAnimationRunning || isPaused"
|
||||
>
|
||||
<template #icon>
|
||||
|
@ -1,4 +1,25 @@
|
||||
import { ref } from 'vue'
|
||||
export default function useMultiDDTraj() {}
|
||||
import { useDDPlay } from '@/views/Daodan/components/hooks/ddPlayHooks'
|
||||
|
||||
export function useMultiDDTraj() {
|
||||
return {
|
||||
addMultiDDTraj,
|
||||
removeAllMultiDDTraj,
|
||||
}
|
||||
}
|
||||
|
||||
const multiDDTrajMap = new Map()
|
||||
|
||||
function addMultiDDTraj(ddList = [], timeRange) {
|
||||
console.log(ddList)
|
||||
ddList.forEach(dd => {
|
||||
console.log(dd)
|
||||
const { addDaodan } = useDDPlay({
|
||||
ddData: dd,
|
||||
startTimeStampOfAnimation: timeRange[0],
|
||||
isAniCalcBasedOnCurrentTime: false,
|
||||
})
|
||||
addDaodan()
|
||||
})
|
||||
}
|
||||
|
||||
function removeAllMultiDDTraj() {}
|
||||
|
@ -1,4 +1,86 @@
|
||||
import { ref } from 'vue'
|
||||
export default function useMultiHJTraj() {}
|
||||
import { useHjPolygon } from '@/views/Hangjing/hooks/hangjingPolygon'
|
||||
import { parseWKT } from '@/utils/parseWKT'
|
||||
import chroma from 'chroma-js'
|
||||
export function useMultiHJTraj() {
|
||||
return {
|
||||
addHJTraj,
|
||||
removeAllMultiHJTraj,
|
||||
}
|
||||
}
|
||||
|
||||
const { addPrimitivePolygon } = useHjPolygon()
|
||||
|
||||
const multiHJTrajMap = new Map()
|
||||
function addHJTraj(hjList = [], timeRange) {
|
||||
console.log(hjList, 'hjList')
|
||||
|
||||
hjList.forEach(hj => {
|
||||
const { zoneList, styleJsonData } = hj
|
||||
zoneList.forEach(item => {
|
||||
const { id, geom, title, timeRange } = item
|
||||
const feature = parseWKT(geom)
|
||||
const position = feature.coordinates[0].map(pos => {
|
||||
return Cesium.Cartesian3.fromDegrees(...pos)
|
||||
})
|
||||
const randomColor = chroma.random().brighten().alpha(0.6).hex()
|
||||
const style = JSON.parse(styleJsonData || '{}')
|
||||
const hjPolygon = addPrimitivePolygon({
|
||||
id,
|
||||
title,
|
||||
color: randomColor,
|
||||
position,
|
||||
style,
|
||||
})
|
||||
|
||||
hjPolygon.properties = { timeRange }
|
||||
|
||||
multiHJTrajMap.set(id, hjPolygon)
|
||||
})
|
||||
})
|
||||
|
||||
addTickHandler()
|
||||
}
|
||||
|
||||
function removeAllMultiHJTraj() {
|
||||
removeEventListener()
|
||||
multiHJTrajMap.keys().forEach(item => {
|
||||
viewer.entities.remove(multiHJTrajMap.get(item))
|
||||
})
|
||||
multiHJTrajMap.clear()
|
||||
}
|
||||
|
||||
let tickHandler = null
|
||||
function addTickHandler() {
|
||||
if (tickHandler) {
|
||||
return
|
||||
}
|
||||
tickHandler = viewer.clock.onTick.addEventListener(time => {
|
||||
for (const [key, hjPolygon] of multiHJTrajMap.entries()) {
|
||||
const { timeBegin, timeEnd } = hjPolygon.properties.timeRange._value
|
||||
|
||||
const currentTimeStamp = Cesium.JulianDate.toDate(
|
||||
time.currentTime
|
||||
).getTime()
|
||||
console.log(
|
||||
currentTimeStamp,
|
||||
new Date(timeBegin).getTime(),
|
||||
new Date(timeEnd).getTime()
|
||||
)
|
||||
if (
|
||||
currentTimeStamp >= new Date(timeBegin).getTime() &&
|
||||
currentTimeStamp <= new Date(timeEnd).getTime()
|
||||
) {
|
||||
hjPolygon.show = true
|
||||
} else {
|
||||
hjPolygon.show = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function removeEventListener() {
|
||||
if (tickHandler) {
|
||||
viewer.clock.onTick.removeEventListener(tickHandler)
|
||||
tickHandler = null
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,31 @@
|
||||
import { ref } from 'vue'
|
||||
import { useSatellite } from '../../Satellite/hooks/satellite'
|
||||
export function useMultiSatTraj() {
|
||||
return { addSatelliteTraj }
|
||||
return { addSatelliteTraj, removeAllMultiSatelliteTraj }
|
||||
}
|
||||
|
||||
const multiSatTrajMap = new Map()
|
||||
|
||||
const { addSatellite } = useSatellite()
|
||||
|
||||
function addSatelliteTraj(satelliteList = []) {
|
||||
function addSatelliteTraj(satelliteList = [], timeRange) {
|
||||
console.log(satelliteList)
|
||||
satelliteList.forEach(({ id, tle }) => {
|
||||
if (!multiSatTrajMap.has(id)) {
|
||||
const satellite = addSatellite({ id, tle })
|
||||
console.log(satellite, 'satellite')
|
||||
const satellite = addSatellite(
|
||||
{ id, tle },
|
||||
{
|
||||
startTime: new Date(timeRange[0]).toISOString() || new Date(),
|
||||
}
|
||||
)
|
||||
multiSatTrajMap.set(id, satellite)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function removeAllMultiSatelliteTraj() {
|
||||
for (let [key, satellite] of multiSatTrajMap.entries()) {
|
||||
satellite.destroy()
|
||||
multiSatTrajMap.delete(key)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { ref } from 'vue'
|
||||
import { useMultiTrajReq } from './useMultiTrajReq'
|
||||
import { useMultiSatTraj } from './useMultiSatTraj'
|
||||
import { useMultiZBTraj } from './useMultiZBTraj'
|
||||
import { useMultiHJTraj } from './useMultiHJTraj'
|
||||
import { useMultiDDTraj } from './useMultiDDTraj'
|
||||
|
||||
const timesMap = ref(new Map())
|
||||
const times = ref([])
|
||||
@ -13,26 +16,46 @@ const isPaused = ref(false)
|
||||
export function useMultiTraj() {
|
||||
function play() {
|
||||
isAnimationRunning.value = true
|
||||
viewer.clock.shouldAnimate = true
|
||||
isPaused.value = false
|
||||
}
|
||||
function pause() {
|
||||
isAnimationRunning.value = false
|
||||
isPaused.value = true
|
||||
viewer.clock.shouldAnimate = false
|
||||
}
|
||||
|
||||
function reset() {
|
||||
isAnimationRunning.value = false
|
||||
isPaused.value = false
|
||||
customElapsedTime.value = 0
|
||||
viewer.clock.shouldAnimate = false
|
||||
viewer.clock.currentTime = viewer.clock.startTime
|
||||
}
|
||||
|
||||
function setSpeed(speed) {
|
||||
animationSpeed.value = speed
|
||||
viewer.clock.multiplier = animationSpeed.value
|
||||
}
|
||||
|
||||
function changeTime(time) {
|
||||
customElapsedTime.value = time
|
||||
viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date(time))
|
||||
}
|
||||
|
||||
function setCustomElapsedTime(time) {
|
||||
const timeTmp = Cesium.JulianDate.toDate(time.currentTime)?.getTime()
|
||||
customElapsedTime.value = timeTmp
|
||||
}
|
||||
|
||||
watch(isAnimationRunning, run => {
|
||||
if (run) {
|
||||
viewer.clock.onTick.addEventListener(setCustomElapsedTime)
|
||||
} else {
|
||||
viewer.clock.onTick.removeEventListener(setCustomElapsedTime)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
checkedAllKeys,
|
||||
showMultiHisTrajCom,
|
||||
@ -71,10 +94,12 @@ const showMultiHisTrajCom = ref(false)
|
||||
// 显示或隐藏多目标历史轨迹组件
|
||||
function showOrHideMultiHisTraj() {
|
||||
showMultiHisTrajCom.value = !showMultiHisTrajCom.value
|
||||
resetTime()
|
||||
|
||||
!showMultiHisTrajCom.value && (resetTime(), removeAllEntity())
|
||||
}
|
||||
|
||||
const { getZBHisTraj, getSatelliteHisTraj } = useMultiTrajReq()
|
||||
const { getZBHisTraj, getSatelliteHisTraj, getDDHisTraj, getHJHisTraj } =
|
||||
useMultiTrajReq()
|
||||
|
||||
// 加载多目标历史轨迹
|
||||
function loadMultiHisTraj(timeRange) {
|
||||
@ -82,9 +107,9 @@ function loadMultiHisTraj(timeRange) {
|
||||
|
||||
const dictFunc = {
|
||||
zb: getZBHisTraj,
|
||||
dd: null,
|
||||
dd: getDDHisTraj,
|
||||
satellite: getSatelliteHisTraj,
|
||||
hj: null,
|
||||
hj: getHJHisTraj,
|
||||
}
|
||||
const reqList = Object.keys(checkedAllKeys.value).filter(key => {
|
||||
if (
|
||||
@ -110,17 +135,35 @@ function loadMultiHisTraj(timeRange) {
|
||||
})
|
||||
|
||||
times.value = timeRange
|
||||
// setTimeRange(timeRange)
|
||||
setTimeRange(timeRange)
|
||||
}
|
||||
|
||||
const { addSatelliteTraj } = useMultiSatTraj()
|
||||
const { addMultiZBTraj, removeAllMultiZBTraj } = useMultiZBTraj()
|
||||
const { addSatelliteTraj, removeAllMultiSatelliteTraj } = useMultiSatTraj()
|
||||
const { addHJTraj, removeAllMultiHJTraj } = useMultiHJTraj()
|
||||
const { addMultiDDTraj, removeAllMultiDDTraj } = useMultiDDTraj()
|
||||
|
||||
const typeFunc = {
|
||||
zb: { add: addMultiZBTraj, remove: removeAllMultiZBTraj },
|
||||
dd: { add: addMultiDDTraj, remove: removeAllMultiDDTraj },
|
||||
satellite: { add: addSatelliteTraj, remove: removeAllMultiSatelliteTraj },
|
||||
hj: { add: addHJTraj, remove: removeAllMultiHJTraj },
|
||||
}
|
||||
// 添加轨迹实体
|
||||
function addAllEntity(data) {
|
||||
const { zb, satellite } = data
|
||||
// zb && useMultiSatTraj(satellite)
|
||||
// dd && useMultiSatTraj(dd)
|
||||
// hj && useMultiSatTraj(hj)
|
||||
satellite && addSatelliteTraj(satellite)
|
||||
Object.keys(typeFunc).forEach(key => {
|
||||
if (typeof typeFunc[key].add === 'function' && data[key]) {
|
||||
typeFunc[key].add(data[key], times.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function removeAllEntity() {
|
||||
Object.keys(typeFunc).forEach(key => {
|
||||
if (typeof typeFunc[key].remove === 'function') {
|
||||
typeFunc[key].remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setTimeRange(timeRange) {
|
||||
@ -131,7 +174,7 @@ function setTimeRange(timeRange) {
|
||||
viewer.clock.startTime = startTime
|
||||
viewer.clock.stopTime = stopTime
|
||||
viewer.clock.currentTime = startTime
|
||||
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
|
||||
// viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
|
||||
viewer.clock.multiplier = 1
|
||||
viewer.clock.shouldAnimate = false
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
import { ref } from 'vue'
|
||||
import { getMubiao, getMubiaoHisTraj } from '../../../api/Mubiao'
|
||||
import { getMubiao, getMubiaoHisTrajByIds } from '../../../api/Mubiao'
|
||||
import { getSatellite } from '../../../api/Satellite'
|
||||
import { getHangjing } from '../../../api/Hangjing'
|
||||
import { getDaodanTree } from '../../../api/Daodan'
|
||||
import { time2Format } from '@/utils/date'
|
||||
|
||||
export function useMultiTrajReq() {
|
||||
return { allTreeData, getAllTree, getZBHisTraj, getSatelliteHisTraj }
|
||||
return {
|
||||
allTreeData,
|
||||
getAllTree,
|
||||
getZBHisTraj,
|
||||
getSatelliteHisTraj,
|
||||
getDDHisTraj,
|
||||
getHJHisTraj,
|
||||
}
|
||||
}
|
||||
|
||||
const allTreeData = ref([])
|
||||
@ -63,10 +71,10 @@ async function getHJTree() {
|
||||
|
||||
async function getZBHisTraj({ ids, timeRange }) {
|
||||
const [timeBegin, timeEnd] = timeRange
|
||||
const { code, data } = await getMubiaoHisTraj({
|
||||
target_id: ids,
|
||||
timeBegin,
|
||||
timeEnd,
|
||||
const { code, data } = await getMubiaoHisTrajByIds({
|
||||
ids,
|
||||
timeBegin: time2Format(timeBegin),
|
||||
timeEnd: time2Format(timeEnd),
|
||||
})
|
||||
if (code === '200') {
|
||||
return data
|
||||
@ -75,30 +83,183 @@ async function getZBHisTraj({ ids, timeRange }) {
|
||||
}
|
||||
|
||||
async function getSatelliteHisTraj({ ids, timeRange }) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: 'CALSPHERE 1',
|
||||
name: 'CALSPHERE 1',
|
||||
tle: `CALSPHERE 1
|
||||
1 00900U 64063C 25112.58517632 .00001163 00000+0 11864-2 0 9998
|
||||
2 00900 90.2148 62.1269 0027145 120.7790 303.4225 13.75994622 13579`,
|
||||
},
|
||||
])
|
||||
const { timeBegin, timeEnd } = params
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve([
|
||||
{
|
||||
id: 'ONEWEB-0012',
|
||||
name: 'ONEWEB-0012',
|
||||
tle: `ONEWEB-0012
|
||||
1 44057U 19010A 24310.58073783 .00000090 00000+0 20246-3 0 9997
|
||||
2 44057 87.9113 346.0642 0002611 86.8295 273.3135 13.16596165274116`,
|
||||
},
|
||||
])
|
||||
}, 200)
|
||||
})
|
||||
const { timeBegin, timeEnd } = timeRange
|
||||
const { code, data } = await getSatellite()
|
||||
if (code === '200') {
|
||||
}
|
||||
}
|
||||
|
||||
async function getDDHisTraj(id, params) {
|
||||
const { timeBegin, timeEnd } = params
|
||||
async function getDDHisTraj({ ids, timeRange }) {
|
||||
return new Promise(resolve => {
|
||||
resolve([
|
||||
{
|
||||
id: 'dd4',
|
||||
name: 'aDaodan4',
|
||||
country: '美国',
|
||||
trajData: {
|
||||
id: 'dd',
|
||||
data: [
|
||||
{
|
||||
name: '起始点',
|
||||
lon: 120,
|
||||
lat: 21,
|
||||
alt: 0,
|
||||
time: 1183135260000,
|
||||
},
|
||||
{
|
||||
name: '最高点',
|
||||
lon: 125,
|
||||
lat: 25,
|
||||
alt: 2000000,
|
||||
time: 1183135280000,
|
||||
},
|
||||
{
|
||||
name: '落点',
|
||||
lon: 160,
|
||||
lat: 40,
|
||||
alt: 0,
|
||||
time: 1183135300000,
|
||||
},
|
||||
],
|
||||
},
|
||||
boosterList: [
|
||||
{
|
||||
id: 'booster-1',
|
||||
data: [
|
||||
{
|
||||
lon: 130,
|
||||
lat: 23,
|
||||
alt: 0,
|
||||
time: 1183135265000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'booster-2',
|
||||
data: [
|
||||
{
|
||||
lon: 135,
|
||||
lat: 28,
|
||||
alt: 0,
|
||||
time: 1183135270000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'booster-3',
|
||||
data: [
|
||||
{
|
||||
lon: 140,
|
||||
lat: 35,
|
||||
alt: 0,
|
||||
time: 1183135275000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
interceptData: [
|
||||
{
|
||||
id: 'intercept-1',
|
||||
data: [
|
||||
{
|
||||
lon: 137,
|
||||
lat: 25,
|
||||
alt: 0,
|
||||
time: 1183135285000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
const { timeBegin, timeEnd } = timeRange
|
||||
const { code, data } = await getSatellite()
|
||||
if (code === '200') {
|
||||
}
|
||||
}
|
||||
|
||||
async function getHJHisTraj(id, params) {
|
||||
const { timeBegin, timeEnd } = params
|
||||
async function getHJHisTraj({ ids, timeRange }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve([
|
||||
{
|
||||
id: 'hj1',
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
geom: 'MULTIPOLYGON (((116.1297035056198 20.986182515154653, 112.12538153402544 18.17622780534863, 114.9856115137357 16.185453923687888, 119.0716543418932 14.332810897111074, 121.11467575597193 16.577470131430132, 121.23725704081666 19.4525424578878, 118.2544457762617 21.138704515710984, 116.1297035056198 20.986182515154653)), ((111.92107939261757 16.10695653664817, 110.98128954214134 13.022687766823779, 114.53614680263837 10.743719746606217, 116.86519121468814 10.743719746606217, 116.6200286449987 13.460201590872217, 113.26947352590955 15.753335789981833, 111.92107939261757 16.10695653664817)), ((110.20494140479141 9.778766374531031, 107.7941761361785 9.174227108383391, 107.6307344230522 6.705834308313385, 109.75547669369408 4.265624811621066, 114.1684029481042 3.0015236942218024, 116.66088907328026 5.771620736018747, 114.41356551779364 8.366580943618544, 110.20494140479141 9.778766374531031)))',
|
||||
title: 'hhh1',
|
||||
detailContent: 'xxxxxxxxxxxxxxxxx',
|
||||
timeRange: {
|
||||
timeBegin: '2024-11-08 13:00:00',
|
||||
timeEnd: '2024-11-08 14:00:00',
|
||||
excludeBegin: false,
|
||||
excludeEnd: true,
|
||||
},
|
||||
hjType: 'KONG',
|
||||
zoneList: [
|
||||
{
|
||||
id: '90000001',
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
geom: 'POLYGON ((116.1297035056198 20.986182515154653, 112.12538153402544 18.17622780534863, 114.9856115137357 16.185453923687888, 119.0716543418932 14.332810897111074, 121.11467575597193 16.577470131430132, 121.23725704081666 19.4525424578878, 118.2544457762617 21.138704515710984, 116.1297035056198 20.986182515154653))',
|
||||
title: 'xx',
|
||||
detailContent: 'xxxxxxxxxxxxxxxxxxx',
|
||||
timeRange: {
|
||||
timeBegin: '2024-11-08 13:00:00',
|
||||
timeEnd: '2024-11-08 14:00:00',
|
||||
excludeBegin: false,
|
||||
excludeEnd: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '90000003',
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
geom: 'POLYGON ((110.20494140479141 9.778766374531031, 107.7941761361785 9.174227108383391, 107.6307344230522 6.705834308313385, 109.75547669369408 4.265624811621066, 114.1684029481042 3.0015236942218024, 116.66088907328026 5.771620736018747, 114.41356551779364 8.366580943618544, 110.20494140479141 9.778766374531031))',
|
||||
title: 'bb',
|
||||
detailContent: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
timeRange: {
|
||||
timeBegin: '2024-11-08 13:00:00',
|
||||
timeEnd: '2024-11-08 14:00:00',
|
||||
excludeBegin: false,
|
||||
excludeEnd: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '90000002',
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
geom: 'POLYGON ((111.92107939261757 16.10695653664817, 110.98128954214134 13.022687766823779, 114.53614680263837 10.743719746606217, 116.86519121468814 10.743719746606217, 116.6200286449987 13.460201590872217, 113.26947352590955 15.753335789981833, 111.92107939261757 16.10695653664817))',
|
||||
title: 'ss',
|
||||
detailContent: 'xxxxxxxxx',
|
||||
timeRange: {
|
||||
timeBegin: '2024-11-08 13:00:00',
|
||||
timeEnd: '2024-11-08 14:00:00',
|
||||
excludeBegin: false,
|
||||
excludeEnd: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
styleJsonData:
|
||||
'{"fontFamily":"微软雅黑","fontSize":14,"textColor":"rgba(255,255,255,1)","polygonColor":"rgba(255, 255, 255, 0)","lineColor":"rgba(255,255,255,1)","lineWidth":1,"lineType":"solid"}',
|
||||
},
|
||||
])
|
||||
})
|
||||
const { timeBegin, timeEnd } = timeRange
|
||||
|
||||
const { code, data } = await getSatellite()
|
||||
if (code === '200') {
|
||||
}
|
||||
|
@ -1,12 +1,123 @@
|
||||
import { ref } from 'vue'
|
||||
export default function useMultiZBTraj() {
|
||||
import { useEntity } from '@/hooks/entity'
|
||||
import { getDirection } from '@/utils/pos'
|
||||
export function useMultiZBTraj() {
|
||||
return {
|
||||
multiZBTrajMap,
|
||||
addMultiZBTraj,
|
||||
removeAllMultiZBTraj,
|
||||
}
|
||||
}
|
||||
|
||||
const multiZBTrajMap = new Map()
|
||||
|
||||
function addMultiZBTraj(id, traj) {
|
||||
multiZBTrajMap.set(id, traj)
|
||||
const { getMBEntityOpt } = useEntity()
|
||||
|
||||
// 添加多目标目标和轨迹
|
||||
function addMultiZBTraj(zbList) {
|
||||
removeAllMultiZBTraj()
|
||||
Object.keys(zbList).forEach(async id => {
|
||||
if (!multiZBTrajMap.has(id)) {
|
||||
const { info, list: trajList } = zbList[id]
|
||||
const { targetType, country, extendInfo } = info
|
||||
const ZBOpt = await getMBEntityOpt({
|
||||
id,
|
||||
targetType,
|
||||
country,
|
||||
extendInfo,
|
||||
})
|
||||
|
||||
const startTime = trajList[0].target_time
|
||||
const endTime = trajList.at(-1).target_time
|
||||
|
||||
const positions = getPositions(trajList, targetType)
|
||||
|
||||
ZBOpt.billboard.rotation = new Cesium.CallbackProperty(currentTime => {
|
||||
const point1 = positions.position.getValue(currentTime)
|
||||
const nextTime = Cesium.JulianDate.addSeconds(
|
||||
currentTime,
|
||||
2,
|
||||
new Cesium.JulianDate()
|
||||
)
|
||||
const point2 = positions.position.getValue(nextTime)
|
||||
if (point2 && point1) {
|
||||
const heading = getDirection(point1, point2)
|
||||
|
||||
const angleDegrees = Cesium.Math.toDegrees(heading)
|
||||
return Cesium.Math.toRadians(angleDegrees)
|
||||
}
|
||||
}, false)
|
||||
|
||||
Object.assign(ZBOpt, {
|
||||
...getPositions(trajList, targetType),
|
||||
...getPath(startTime, endTime),
|
||||
// ...getAvailability(startTime, endTime),
|
||||
})
|
||||
|
||||
console.log('ZBOpt', ZBOpt)
|
||||
|
||||
const ZBEntity = viewer.entities.add(ZBOpt)
|
||||
multiZBTrajMap.set(id, ZBEntity)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getPositions(trajList, targetType) {
|
||||
const positionProperty = new Cesium.SampledPositionProperty()
|
||||
|
||||
trajList.forEach(({ target_time, target_lon, target_lat }, index) => {
|
||||
const time = Cesium.JulianDate.fromDate(new Date(target_time))
|
||||
const position = Cesium.Cartesian3.fromDegrees(
|
||||
target_lon,
|
||||
target_lat,
|
||||
window.settings.mbDict[targetType].height
|
||||
)
|
||||
positionProperty.addSample(time, position)
|
||||
// viewer.entities.add({
|
||||
// position,
|
||||
// point: {
|
||||
// pixelSize: 3,
|
||||
// color: Cesium.Color.RED,
|
||||
// },
|
||||
// })
|
||||
})
|
||||
positionProperty.setInterpolationOptions({
|
||||
interpolationDegree: 2,
|
||||
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
|
||||
})
|
||||
|
||||
return {
|
||||
position: positionProperty,
|
||||
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
|
||||
}
|
||||
}
|
||||
|
||||
function getPath(startTime, endTime) {
|
||||
console.log(new Date(startTime).getTime() - new Date(endTime).getTime())
|
||||
return {
|
||||
path: new Cesium.PathGraphics({
|
||||
width: 0.5,
|
||||
// show: true,
|
||||
resolution: 1,
|
||||
leadTime: new Date(startTime).getTime() - new Date(endTime).getTime(),
|
||||
trailTime: new Date(startTime).getTime() - new Date(endTime).getTime(),
|
||||
material: Cesium.Color.fromRandom().withAlpha(1),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function getAvailability(startTime, endTime) {
|
||||
const start = Cesium.JulianDate.fromDate(new Date(startTime))
|
||||
const stop = Cesium.JulianDate.fromDate(new Date(endTime))
|
||||
return {
|
||||
availability: new Cesium.TimeIntervalCollection([
|
||||
new Cesium.TimeInterval({ start: start, stop: stop }),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
function removeAllMultiZBTraj() {
|
||||
for (let [key, entity] of multiZBTrajMap.entries()) {
|
||||
viewer.entities.remove(entity)
|
||||
multiZBTrajMap.delete(key)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export default defineComponent({
|
||||
hj: 'hj',
|
||||
}
|
||||
function renderSuffix({ option }) {
|
||||
return option.data ? (
|
||||
return option.extendInfo || option.data?.extendInfo ? (
|
||||
<div class="pr-2">
|
||||
<NIcon
|
||||
size={14}
|
||||
@ -57,7 +57,9 @@ export default defineComponent({
|
||||
key-field={key === 'satellite' ? 'id' : 'dataId'}
|
||||
label-field={key === 'satellite' ? 'name' : 'nodeName'}
|
||||
showSearch
|
||||
renderSuffix={renderSuffix}
|
||||
renderSuffix={
|
||||
['zb', 'satellite'].includes(key) ? renderSuffix : () => ({})
|
||||
}
|
||||
v-model:checked={checkedAllKeys.value[key]}
|
||||
/>
|
||||
</div>
|
||||
|
@ -55,8 +55,8 @@ export function useSatellite() {
|
||||
}
|
||||
}
|
||||
// 创建satellite entity 实例
|
||||
function addSatellite({ id, tle }: ISatellite) {
|
||||
const satellite = new SatelliteEntity(tle)
|
||||
function addSatellite({ id, tle }: ISatellite, options: any) {
|
||||
const satellite = new SatelliteEntity(tle, options)
|
||||
const cesiumSateEntity = satellite.createSatelliteEntity()
|
||||
// const result = viewer.entities.add(cesiumSateEntity)
|
||||
|
||||
|