Browse Source

test

master
茹嘉辉 1 week ago
parent
commit
cc23858c07
4 changed files with 239 additions and 60 deletions
  1. +134
    -16
      src/App.vue
  2. +0
    -1
      src/basemap.ts
  3. +97
    -33
      src/wayline.ts
  4. +8
    -10
      src/waypoint.ts

+ 134
- 16
src/App.vue View File

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

+ 0
- 1
src/basemap.ts View File

@ -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
src/wayline.ts View File

@ -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
src/waypoint.ts View File

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

Loading…
Cancel
Save