|
|
|
@ -9,6 +9,99 @@ import * as egm96 from 'egm96-universal' |
|
|
|
const store = useStore() |
|
|
|
const isDraggingSlider = ref(false) |
|
|
|
const isMinimapExpanded = ref(false) |
|
|
|
const canCreateWayline = ref(false) |
|
|
|
|
|
|
|
// 导出航线 |
|
|
|
function exportWayline() { |
|
|
|
if (window.wayline) { |
|
|
|
const json = window.wayline.exportToJson() |
|
|
|
const blob = new Blob([json], { type: 'application/json' }) |
|
|
|
const url = URL.createObjectURL(blob) |
|
|
|
const a = document.createElement('a') |
|
|
|
a.href = url |
|
|
|
a.download = `wayline_${new Date().toISOString().slice(0, 19).replace(/[-:]/g, '')}.json` |
|
|
|
document.body.appendChild(a) |
|
|
|
a.click() |
|
|
|
document.body.removeChild(a) |
|
|
|
URL.revokeObjectURL(url) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 导入航线 |
|
|
|
function importWayline() { |
|
|
|
const input = document.createElement('input') |
|
|
|
input.type = 'file' |
|
|
|
input.accept = '.json' |
|
|
|
input.onchange = (e) => { |
|
|
|
const target = e.target as HTMLInputElement |
|
|
|
if (target.files && target.files[0]) { |
|
|
|
const file = target.files[0] |
|
|
|
const reader = new FileReader() |
|
|
|
reader.onload = (event) => { |
|
|
|
try { |
|
|
|
const json = event.target?.result as string |
|
|
|
if (window.wayline) { |
|
|
|
window.wayline.destroy() |
|
|
|
} |
|
|
|
window.wayline = Wayline.importFromJson(json, false) |
|
|
|
} catch (error) { |
|
|
|
console.error('Failed to import wayline:', error) |
|
|
|
alert('导入航线失败,请检查文件格式') |
|
|
|
} |
|
|
|
} |
|
|
|
reader.readAsText(file) |
|
|
|
} |
|
|
|
} |
|
|
|
input.click() |
|
|
|
} |
|
|
|
|
|
|
|
// 开始创建航线 |
|
|
|
function startCreateWayline() { |
|
|
|
canCreateWayline.value = true |
|
|
|
// 更改光标样式 |
|
|
|
viewer.scene.canvas.style.cursor = 'crosshair' |
|
|
|
} |
|
|
|
|
|
|
|
// 结束创建航线 |
|
|
|
function endCreateWayline() { |
|
|
|
canCreateWayline.value = false |
|
|
|
// 恢复默认光标 |
|
|
|
viewer.scene.canvas.style.cursor = 'default' |
|
|
|
} |
|
|
|
|
|
|
|
// 保存航点状态 |
|
|
|
function saveWaypointState() { |
|
|
|
if (window.wayline && window.wayline.selectedWaypoint) { |
|
|
|
const selectedWaypoint = window.wayline.selectedWaypoint |
|
|
|
const minimapViewer = window.wayline.minimapViewer |
|
|
|
|
|
|
|
// 同步偏航角和云台状态 |
|
|
|
selectedWaypoint.orientation = { |
|
|
|
heading: minimapViewer.camera.heading, |
|
|
|
pitch: minimapViewer.camera.pitch, |
|
|
|
roll: minimapViewer.camera.roll |
|
|
|
} |
|
|
|
|
|
|
|
// 同步视锥体状态(FOV) |
|
|
|
selectedWaypoint.fov = minimapViewer.camera.frustum.fov |
|
|
|
|
|
|
|
// 同步高度信息 |
|
|
|
const cartographic = Cartographic.fromCartesian(selectedWaypoint.position) |
|
|
|
selectedWaypoint.alt = cartographic.height |
|
|
|
const height = egm96.meanSeaLevel( |
|
|
|
cesiumMath.toDegrees(cartographic.latitude), |
|
|
|
cesiumMath.toDegrees(cartographic.longitude) |
|
|
|
) |
|
|
|
selectedWaypoint.asl = selectedWaypoint.alt - height |
|
|
|
|
|
|
|
// 更新航点选择状态,触发UI更新 |
|
|
|
selectedWaypoint.unselect() |
|
|
|
selectedWaypoint.select() |
|
|
|
|
|
|
|
// 同步到store |
|
|
|
window.wayline.syncPointListToStore() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 声明全局变量类型 |
|
|
|
declare global { |
|
|
|
@ -151,6 +244,8 @@ onMounted(async () => { |
|
|
|
const scene = viewer.scene |
|
|
|
const handler = new ScreenSpaceEventHandler(scene.canvas) |
|
|
|
handler.setInputAction(async function (click: ScreenSpaceEventHandler.PositionedEvent) { |
|
|
|
if (!canCreateWayline.value) return |
|
|
|
|
|
|
|
const ray = scene.camera.getPickRay(click.position) |
|
|
|
|
|
|
|
if (!ray) { |
|
|
|
@ -159,15 +254,20 @@ onMounted(async () => { |
|
|
|
|
|
|
|
const cartesian = scene.globe.pick(ray, scene) |
|
|
|
if (defined(cartesian)) { |
|
|
|
// 恢复默认光标 |
|
|
|
viewer.scene.canvas.style.cursor = 'default' |
|
|
|
canCreateWayline.value = false |
|
|
|
|
|
|
|
const cartographic = Cartographic.fromCartesian(cartesian) |
|
|
|
|
|
|
|
const height = egm96.meanSeaLevel(cesiumMath.toDegrees(cartographic.latitude), cesiumMath.toDegrees(cartographic.longitude)) |
|
|
|
const terrainPosition = await getTerrainPosition(cartographic) |
|
|
|
window.wayline = new Wayline(terrainPosition) |
|
|
|
// 直接创建Wayline实例,第一个创建的点就是航点 |
|
|
|
window.wayline = new Wayline() |
|
|
|
// 将点击位置作为第一个航点 |
|
|
|
window.wayline.createWayPoint(terrainPosition) |
|
|
|
|
|
|
|
handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK) |
|
|
|
// 不移除点击事件,因为用户可能需要创建多个航线 |
|
|
|
} |
|
|
|
}, ScreenSpaceEventType.LEFT_CLICK) |
|
|
|
|
|
|
|
@ -256,7 +356,7 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
|
|
|
|
<!-- 底部居中:保存 --> |
|
|
|
<div class="minimap-bottom-row"> |
|
|
|
<button class="minimap-save-btn" type="button"> |
|
|
|
<button class="minimap-save-btn" type="button" @click="saveWaypointState"> |
|
|
|
保存 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
@ -296,20 +396,15 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
<div class="point-list-container"> |
|
|
|
<div class="point-list-header"> |
|
|
|
<span>航点列表 ({{ store.pointList.length }})</span> |
|
|
|
<div class="wayline-mode-toggle"> |
|
|
|
<button |
|
|
|
:class="['wayline-mode-btn', { active: store.waylineMode === 'edit' }]" |
|
|
|
type="button" |
|
|
|
@click="store.setWaylineMode('edit')" |
|
|
|
> |
|
|
|
编辑 |
|
|
|
<div class="wayline-import-export"> |
|
|
|
<button class="import-export-btn" @click="startCreateWayline" title="创建航线"> |
|
|
|
创建航线 |
|
|
|
</button> |
|
|
|
<button |
|
|
|
:class="['wayline-mode-btn', { active: store.waylineMode === 'preview' }]" |
|
|
|
type="button" |
|
|
|
@click="store.setWaylineMode('preview')" |
|
|
|
> |
|
|
|
预览 |
|
|
|
<button class="import-export-btn" @click="importWayline" title="导入航线"> |
|
|
|
导入 |
|
|
|
</button> |
|
|
|
<button class="import-export-btn" @click="exportWayline" title="导出航线"> |
|
|
|
导出 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -588,6 +683,29 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
color: #2F80ED; |
|
|
|
} |
|
|
|
|
|
|
|
/* 导入导出按钮 */ |
|
|
|
.wayline-import-export { |
|
|
|
display: flex; |
|
|
|
gap: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
.import-export-btn { |
|
|
|
padding: 4px 10px; |
|
|
|
border: none; |
|
|
|
border-radius: 4px; |
|
|
|
background: rgba(0, 214, 144, 0.2); |
|
|
|
color: #00D690; |
|
|
|
font-size: 12px; |
|
|
|
font-weight: 600; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.2s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.import-export-btn:hover { |
|
|
|
background: rgba(0, 214, 144, 0.3); |
|
|
|
color: #00F0A0; |
|
|
|
} |
|
|
|
|
|
|
|
/* 顶部中央:镜头模式切换 */ |
|
|
|
/* 左上角:放大/缩小按钮(按截图) */ |
|
|
|
.minimap-expand-btn { |
|
|
|
|