多目标轨迹dd和hj,卫星

This commit is contained in:
严争鸣 2025-04-27 12:47:15 +08:00
parent df5b2bdbe7
commit af7b6adf5e
37 changed files with 2017 additions and 732 deletions

View File

@ -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,
},
},

Binary file not shown.

Before

(image error) Size: 3.3 KiB

View File

@ -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

Binary file not shown.

Before

(image error) Size: 3.3 KiB

View File

@ -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

Binary file not shown.

Before

(image error) Size: 3.3 KiB

View File

@ -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

Binary file not shown.

Before

(image error) Size: 3.3 KiB

View 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

Binary file not shown.

Before

(image error) Size: 3.6 KiB

View File

@ -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

View 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

Binary file not shown.

Before

(image error) Size: 3.6 KiB

View 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

Binary file not shown.

Before

(image error) Size: 3.5 KiB

View File

@ -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

View 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

View 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

View 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

View File

@ -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`,

View File

@ -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

View File

@ -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
}

View File

@ -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) {

View 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()
}

View File

@ -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,
}
}

View File

@ -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()
}

View File

@ -280,6 +280,7 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
return {
addHangjingPolygon,
removeHangjingPolygon,
addPrimitivePolygon,
}
}

View File

@ -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>

View File

@ -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() {}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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') {
}

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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)