test
This commit is contained in:
1 parent
81981ad1f4
commit
cc23858c07
4 files changed
+239
-60
No files matched your search
+134
-16
@@ -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 {
|
||||
|
||||
@@ -20,7 +20,6 @@ export async function createViewer(containerId: string) {
|
||||
|
||||
viewer.scene.msaaSamples = 4
|
||||
viewer.scene.globe.depthTestAgainstTerrain = true
|
||||
viewer.scene.canvas.style.cursor = 'url("/takeoff-active.svg") 16 16, auto'
|
||||
viewer.camera.setView({
|
||||
destination: new Cartesian3(
|
||||
-2353775.3111441266,
|
||||
|
||||
+97
-33
@@ -44,43 +44,41 @@ export class Wayline {
|
||||
pointList: WayPoint[] = []
|
||||
selectedWaypoint: WayPoint | null = null
|
||||
baseHeight: number = 120
|
||||
takeOffPoint: Cartesian3
|
||||
takeOffHeight: number
|
||||
top: Cartesian3
|
||||
minimapViewer!: Viewer
|
||||
debugFrustum!: DebugCameraPrimitive
|
||||
wayLineEntity!: Entity
|
||||
takeOffPointEntity!: Entity
|
||||
uavEntity!: Entity
|
||||
editable: boolean
|
||||
|
||||
constructor(takeOffPoint: Cartesian3) {
|
||||
this.takeOffPoint = takeOffPoint
|
||||
const cartographic = Cartographic.fromCartesian(takeOffPoint)
|
||||
|
||||
this.takeOffHeight = cartographic.height
|
||||
this.top = Cartesian3.fromRadians(
|
||||
cartographic.longitude,
|
||||
cartographic.latitude,
|
||||
120
|
||||
)
|
||||
constructor(editable: boolean = true) {
|
||||
this.editable = editable
|
||||
|
||||
this.createMinimap()
|
||||
this.createWayline()
|
||||
this.createUav()
|
||||
if (this.editable) {
|
||||
this.createUav()
|
||||
}
|
||||
this.setupMiniCameraEvents()
|
||||
|
||||
this.createFrustum()
|
||||
if (this.editable) {
|
||||
this.createFrustum()
|
||||
}
|
||||
|
||||
// 设置初始相机位置(使用默认位置)
|
||||
this.minimapViewer.camera.setView({
|
||||
destination: this.top,
|
||||
destination: Cartesian3.fromRadians(0, 0, 10000000),
|
||||
orientation: {
|
||||
pitch: 0,
|
||||
roll: 0
|
||||
roll: 0,
|
||||
heading: 0
|
||||
}
|
||||
})
|
||||
|
||||
this.setupDragEvent()
|
||||
this.setupMovementEvent()
|
||||
this.setupSelectEvent()
|
||||
if (this.editable) {
|
||||
this.setupDragEvent()
|
||||
this.setupMovementEvent()
|
||||
this.setupSelectEvent()
|
||||
}
|
||||
this.syncPointListToStore()
|
||||
|
||||
// 初始化云台数据
|
||||
@@ -106,6 +104,9 @@ export class Wayline {
|
||||
|
||||
createWayPoint(position: Cartesian3): void {
|
||||
const pointList = this.pointList
|
||||
|
||||
// 直接使用相机当前位置作为航点位置,包括高度
|
||||
// position参数就是从相机位置获取的,所以直接使用
|
||||
const wayPoint = new WayPoint(position, this, pointList.length)
|
||||
pointList.push(wayPoint)
|
||||
|
||||
@@ -488,12 +489,12 @@ export class Wayline {
|
||||
}
|
||||
|
||||
createWayline(): void {
|
||||
const { takeOffPoint, pointList } = this
|
||||
const { pointList } = this
|
||||
|
||||
this.wayLineEntity = viewer.entities.add({
|
||||
polyline: {
|
||||
positions: new CallbackProperty(() => {
|
||||
return [takeOffPoint, this.top, ...pointList.map(item => item.position)]
|
||||
return pointList.map(item => item.position)
|
||||
}, false),
|
||||
width: 5,
|
||||
material: new CustomMaterialProperty({
|
||||
@@ -504,14 +505,6 @@ export class Wayline {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.takeOffPointEntity = viewer.entities.add({
|
||||
position: takeOffPoint,
|
||||
billboard: {
|
||||
image: './takeoff-active.svg',
|
||||
scale: 1.0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
createUav(): void {
|
||||
@@ -607,14 +600,85 @@ export class Wayline {
|
||||
|
||||
destroy(): void {
|
||||
viewer.entities.remove(this.wayLineEntity)
|
||||
viewer.entities.remove(this.takeOffPointEntity)
|
||||
|
||||
viewer.scene.primitives.remove(this.debugFrustum)
|
||||
if (this.editable) {
|
||||
if (this.debugFrustum) {
|
||||
viewer.scene.primitives.remove(this.debugFrustum)
|
||||
}
|
||||
if (this.uavEntity) {
|
||||
viewer.entities.remove(this.uavEntity)
|
||||
}
|
||||
}
|
||||
|
||||
for (const point of this.pointList) {
|
||||
(point as any).delete()
|
||||
}
|
||||
}
|
||||
|
||||
exportToJson(): string {
|
||||
const waylineData = {
|
||||
pointList: this.pointList.map(point => {
|
||||
const cartographic = Cartographic.fromCartesian(point.position)
|
||||
return {
|
||||
longitude: CesiumMath.toDegrees(cartographic.longitude),
|
||||
latitude: CesiumMath.toDegrees(cartographic.latitude),
|
||||
height: cartographic.height,
|
||||
asl: point.asl,
|
||||
alt: point.alt,
|
||||
fov: point.fov,
|
||||
orientation: {
|
||||
heading: CesiumMath.toDegrees(point.orientation.heading),
|
||||
pitch: CesiumMath.toDegrees(point.orientation.pitch),
|
||||
roll: CesiumMath.toDegrees(point.orientation.roll)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return JSON.stringify(waylineData, null, 2)
|
||||
}
|
||||
|
||||
static importFromJson(json: string, editable: boolean = true): Wayline {
|
||||
try {
|
||||
const waylineData = JSON.parse(json)
|
||||
|
||||
if (!waylineData.pointList) {
|
||||
throw new Error('Invalid wayline data format')
|
||||
}
|
||||
|
||||
const wayline = new Wayline(editable)
|
||||
|
||||
waylineData.pointList.forEach((pointData: any, index: number) => {
|
||||
if (!pointData.longitude || !pointData.latitude || !pointData.height) {
|
||||
throw new Error(`Invalid point data at index ${index}`)
|
||||
}
|
||||
|
||||
const position = Cartesian3.fromRadians(
|
||||
CesiumMath.toRadians(pointData.longitude),
|
||||
CesiumMath.toRadians(pointData.latitude),
|
||||
pointData.height
|
||||
)
|
||||
|
||||
const wayPoint = new WayPoint(position, wayline, index)
|
||||
wayPoint.asl = pointData.asl
|
||||
wayPoint.alt = pointData.alt
|
||||
wayPoint.fov = pointData.fov
|
||||
wayPoint.orientation = {
|
||||
heading: CesiumMath.toRadians(pointData.orientation.heading),
|
||||
pitch: CesiumMath.toRadians(pointData.orientation.pitch),
|
||||
roll: CesiumMath.toRadians(pointData.orientation.roll)
|
||||
}
|
||||
|
||||
wayline.pointList.push(wayPoint)
|
||||
})
|
||||
|
||||
wayline.syncPointListToStore()
|
||||
return wayline
|
||||
} catch (error) {
|
||||
console.error('Failed to import wayline:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getVerticalMetersPerPixel(viewer: Viewer, worldPosition: Cartesian3): number {
|
||||
|
||||
+8
-10
@@ -61,9 +61,8 @@ export class WayPoint {
|
||||
|
||||
const url = generateNumberSvgDataUrl(index + 1)
|
||||
|
||||
this.wayPointEntity = viewer.entities.add({
|
||||
const entityOptions: any = {
|
||||
position,
|
||||
wayPoint: this,
|
||||
point: {
|
||||
pixelSize: 10,
|
||||
color: Color.RED,
|
||||
@@ -86,7 +85,13 @@ export class WayPoint {
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.wayline.editable) {
|
||||
entityOptions.wayPoint = this
|
||||
}
|
||||
|
||||
this.wayPointEntity = viewer.entities.add(entityOptions)
|
||||
}
|
||||
|
||||
updatePosition(position: Cartesian3): void {
|
||||
@@ -180,7 +185,6 @@ export class WayPoint {
|
||||
this.selected = false
|
||||
this.wayPointEntity.billboard.image = generateNumberSvgDataUrl(this.index + 1)
|
||||
|
||||
if (this.distanceLabel0) viewer.entities.remove(this.distanceLabel0)
|
||||
if (this.distanceLabel1) viewer.entities.remove(this.distanceLabel1)
|
||||
if (this.distanceLabel2) viewer.entities.remove(this.distanceLabel2)
|
||||
if (this.heightLabel) viewer.entities.remove(this.heightLabel)
|
||||
@@ -188,9 +192,6 @@ export class WayPoint {
|
||||
if (this.debugCameraPrimitive) viewer.scene.primitives.remove(this.debugCameraPrimitive)
|
||||
this.wayline.syncPointListToStore()
|
||||
}
|
||||
distanceLabel0(distanceLabel0: any) {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
const pointList = this.wayline.pointList
|
||||
@@ -262,9 +263,6 @@ export class WayPoint {
|
||||
|
||||
if (prev) {
|
||||
this.distanceLabel1 = this.createMidpointLabel(prev.position, current.position)
|
||||
} else {
|
||||
this.distanceLabel0 = this.createMidpointLabel(this.wayline.takeOffPoint, this.wayline.top)
|
||||
this.distanceLabel1 = this.createMidpointLabel(this.wayline.top, current.position)
|
||||
}
|
||||
|
||||
if (next) {
|
||||
|
||||
Reference in new issue
Block a user