|
|
|
@ -37,6 +37,7 @@ |
|
|
|
@edit-platform-position="openPlatformIconPositionDialog" |
|
|
|
@edit-platform-heading="openPlatformIconHeadingDialog" |
|
|
|
@show-transform-box="showPlatformIconTransformBox" |
|
|
|
@toggle-route-label="toggleRouteLabelVisibility" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 定位弹窗 --> |
|
|
|
@ -168,6 +169,8 @@ export default { |
|
|
|
position: { x: 0, y: 0 }, |
|
|
|
entityData: null |
|
|
|
}, |
|
|
|
// 航线飞机标牌显示状态:routeId -> true 显示 / false 隐藏,不设则默认显示 |
|
|
|
routeLabelVisible: {}, |
|
|
|
// 默认样式 |
|
|
|
defaultStyles: { |
|
|
|
point: { color: '#FF0000', size: 12 }, |
|
|
|
@ -888,12 +891,17 @@ export default { |
|
|
|
if (existingLine) { |
|
|
|
this.viewer.entities.remove(existingLine); |
|
|
|
} |
|
|
|
// 清理属于该航线的旧平台图标 |
|
|
|
// 清理属于该航线的旧平台图标与标牌 |
|
|
|
const platformBillboardId = `route-platform-${routeId}`; |
|
|
|
const platformLabelId = `route-platform-label-${routeId}`; |
|
|
|
const existingPlatform = this.viewer.entities.getById(platformBillboardId); |
|
|
|
if (existingPlatform) { |
|
|
|
this.viewer.entities.remove(existingPlatform); |
|
|
|
} |
|
|
|
const existingLabel = this.viewer.entities.getById(platformLabelId); |
|
|
|
if (existingLabel) { |
|
|
|
this.viewer.entities.remove(existingLabel); |
|
|
|
} |
|
|
|
// 清理所有属于该航线的旧点(含转弯半径两侧的 entry/exit 点) |
|
|
|
waypoints.forEach((wp,index) => { |
|
|
|
const waypointEntityId = `wp_${routeId}_${wp.id}`; |
|
|
|
@ -1017,6 +1025,43 @@ export default { |
|
|
|
...(initialRotation !== undefined && { rotation: initialRotation }) |
|
|
|
} |
|
|
|
}); |
|
|
|
// 飞机标牌:显示名字、高度、速度、航向,随飞机实时运动 |
|
|
|
const firstWp = waypoints[0]; |
|
|
|
const firstAlt = firstWp && (firstWp.alt != null) ? Number(firstWp.alt) : 0; |
|
|
|
const firstSpeed = firstWp && (firstWp.speed != null) ? Number(firstWp.speed) : 800; |
|
|
|
const initialHeadingRad = pathData.path && pathData.path.length >= 2 |
|
|
|
? this.computeHeadingFromPositions(pathData.path[0], pathData.path[1]) : undefined; |
|
|
|
const initialHeadingDeg = initialHeadingRad != null ? (initialHeadingRad * 180 / Math.PI) : 0; |
|
|
|
const labelText = this.formatPlatformLabelText({ |
|
|
|
name: (platform && platform.name) || '平台', |
|
|
|
altitude: firstAlt, |
|
|
|
speed: firstSpeed, |
|
|
|
headingDeg: initialHeadingDeg |
|
|
|
}); |
|
|
|
const labelShow = this.routeLabelVisible[routeId] !== false |
|
|
|
this.viewer.entities.add({ |
|
|
|
id: platformLabelId, |
|
|
|
name: '平台标牌', |
|
|
|
position: originalPositions[0], |
|
|
|
show: labelShow, |
|
|
|
properties: { routeId: routeId }, |
|
|
|
label: { |
|
|
|
text: labelText, |
|
|
|
font: '16px Microsoft YaHei', |
|
|
|
fillColor: Cesium.Color.fromCssColorString('#333333'), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString('#e0e0e0'), |
|
|
|
outlineWidth: 1, |
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
|
|
|
showBackground: true, |
|
|
|
backgroundColor: Cesium.Color.fromCssColorString('rgba(255, 255, 255, 0.95)'), |
|
|
|
backgroundPadding: new Cesium.Cartesian2(10, 6), |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -42), |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY, |
|
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 1.0, 200000, 0.65) |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
// 绘制连线(含盘旋弧) |
|
|
|
if (waypoints.length > 1) { |
|
|
|
@ -1527,8 +1572,8 @@ export default { |
|
|
|
for (let i = entityList.length - 1; i >= 0; i--) { |
|
|
|
const entity = entityList[i]; |
|
|
|
let shouldRemove = false; |
|
|
|
// 平台图标实体:通过 id 匹配 |
|
|
|
if (entity.id === `route-platform-${routeId}`) { |
|
|
|
// 平台图标与标牌实体:通过 id 匹配 |
|
|
|
if (entity.id === `route-platform-${routeId}` || entity.id === `route-platform-label-${routeId}`) { |
|
|
|
shouldRemove = true; |
|
|
|
} else if (entity.properties && entity.properties.routeId) { |
|
|
|
const id = entity.properties.routeId.getValue && entity.properties.routeId.getValue(); |
|
|
|
@ -1536,7 +1581,7 @@ export default { |
|
|
|
} |
|
|
|
if (shouldRemove) this.viewer.entities.remove(entity); |
|
|
|
} |
|
|
|
this.allEntities = this.allEntities.filter(item => item.id !== routeId && item.id !== `route-platform-${routeId}`); |
|
|
|
this.allEntities = this.allEntities.filter(item => item.id !== routeId && item.id !== `route-platform-${routeId}` && item.id !== `route-platform-label-${routeId}`); |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 根据当前点与另一点计算航向角(弧度),用于飞机图标朝向。 |
|
|
|
@ -1561,8 +1606,18 @@ export default { |
|
|
|
return heading; |
|
|
|
}, |
|
|
|
|
|
|
|
/** 动态推演:更新某条航线的平台图标位置与朝向(position: { lng, lat, alt } 或 Cesium.Cartesian3;directionPoint 为用于计算机头朝向的另一点,如下一位置或上一位置) */ |
|
|
|
updatePlatformPosition(routeId, position, directionPoint) { |
|
|
|
/** 格式化飞机标牌文案:名字、高度(m)、速度(km/h)、航向(°) */ |
|
|
|
formatPlatformLabelText(data) { |
|
|
|
const name = (data && data.name != null) ? String(data.name) : '—'; |
|
|
|
const alt = (data && data.altitude != null) ? Number(data.altitude) : 0; |
|
|
|
const speed = (data && data.speed != null) ? Number(data.speed) : 0; |
|
|
|
const hdg = (data && data.headingDeg != null) ? Number(data.headingDeg) : 0; |
|
|
|
const headingNorm = ((hdg % 360) + 360) % 360; |
|
|
|
return `${name}\n高度: ${Math.round(alt)}m 速度: ${Math.round(speed)}km/h 航向: ${Math.round(headingNorm)}°`; |
|
|
|
}, |
|
|
|
|
|
|
|
/** 动态推演:更新某条航线的平台图标位置与朝向(position: { lng, lat, alt } 或 Cesium.Cartesian3;directionPoint 为用于计算机头朝向的另一点;labelData 可选,用于更新标牌 { name, altitude, speed, headingDeg }) */ |
|
|
|
updatePlatformPosition(routeId, position, directionPoint, labelData) { |
|
|
|
if (!this.viewer) return; |
|
|
|
const entity = this.viewer.entities.getById(`route-platform-${routeId}`); |
|
|
|
if (!entity || !entity.position) return; |
|
|
|
@ -1583,6 +1638,14 @@ export default { |
|
|
|
entity.billboard.rotation = Math.PI / 2 - heading; |
|
|
|
} |
|
|
|
} |
|
|
|
// 更新标牌位置与文案(与数据库对应:名字、高度、速度、航向) |
|
|
|
const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`); |
|
|
|
if (labelEntity && labelEntity.position) { |
|
|
|
labelEntity.position = cartesian; |
|
|
|
if (labelData && labelEntity.label) { |
|
|
|
labelEntity.label.text = this.formatPlatformLabelText(labelData); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
checkCesiumLoaded() { |
|
|
|
if (typeof Cesium === 'undefined') { |
|
|
|
@ -1626,8 +1689,11 @@ export default { |
|
|
|
maximumRenderTimeChange: Infinity, |
|
|
|
// 允许截图时读取 canvas 内容(若截图仅用 readPixels 可改为 false 以提升性能) |
|
|
|
contextOptions: { |
|
|
|
preserveDrawingBuffer: true |
|
|
|
} |
|
|
|
preserveDrawingBuffer: true, |
|
|
|
antialias: true // 开启 WebGL 全局抗锯齿 |
|
|
|
}, |
|
|
|
// 多重采样抗锯齿(WebGL2 下生效,常用 2/4/8,数值越大越平滑、性能消耗越高) |
|
|
|
msaaSamples: 4 |
|
|
|
}) |
|
|
|
this.viewer.cesiumWidget.creditContainer.style.display = "none" |
|
|
|
// 按需渲染时:实体增删改后主动请求一帧,避免界面不刷新 |
|
|
|
@ -1654,7 +1720,7 @@ export default { |
|
|
|
this.initHoverHandler() |
|
|
|
this.initMouseCoordinates() |
|
|
|
console.log('Cesium离线二维地图已加载') |
|
|
|
console.log('Cesium离线二维地图已加载') |
|
|
|
|
|
|
|
// 1. 定义全局拾取处理器 |
|
|
|
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); |
|
|
|
this.handler.setInputAction((click) => { |
|
|
|
@ -1716,31 +1782,43 @@ export default { |
|
|
|
const pickedObject = this.viewer.scene.pick(click.position) |
|
|
|
if (Cesium.defined(pickedObject) && pickedObject.id) { |
|
|
|
const pickedEntity = pickedObject.id |
|
|
|
// 查找对应的实体数据 |
|
|
|
let entityData = this.allEntities.find(e => e.entity === pickedEntity || e === pickedEntity) |
|
|
|
// 特殊处理:如果点击的是线段上的点,找到对应的线实体 |
|
|
|
const idStr = typeof pickedEntity.id === 'string' ? pickedEntity.id : (pickedEntity.id || '') |
|
|
|
let entityData = null |
|
|
|
// 航线上的飞机(平台图标):右键可切换标牌显示/隐藏 |
|
|
|
if (idStr.startsWith('route-platform-') && !idStr.startsWith('route-platform-label-')) { |
|
|
|
const routeId = idStr.replace('route-platform-', '') |
|
|
|
entityData = { |
|
|
|
type: 'routePlatform', |
|
|
|
routeId, |
|
|
|
entity: pickedEntity, |
|
|
|
labelVisible: this.routeLabelVisible[routeId] !== false |
|
|
|
} |
|
|
|
} |
|
|
|
if (!entityData) { |
|
|
|
// 检查是否是线段上的点 |
|
|
|
for (const lineEntity of this.allEntities) { |
|
|
|
if (lineEntity.type === 'line' && lineEntity.pointEntities) { |
|
|
|
if (lineEntity.pointEntities.includes(pickedEntity)) { |
|
|
|
entityData = lineEntity |
|
|
|
break |
|
|
|
// 查找对应的实体数据 |
|
|
|
entityData = this.allEntities.find(e => e.entity === pickedEntity || e === pickedEntity) |
|
|
|
// 特殊处理:如果点击的是线段上的点,找到对应的线实体 |
|
|
|
if (!entityData) { |
|
|
|
for (const lineEntity of this.allEntities) { |
|
|
|
if (lineEntity.type === 'line' && lineEntity.pointEntities) { |
|
|
|
if (lineEntity.pointEntities.includes(pickedEntity)) { |
|
|
|
entityData = lineEntity |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// 特殊处理:如果点击的是威力区的圆心,找到对应的威力区实体 |
|
|
|
if (!entityData) { |
|
|
|
for (const powerZoneEntity of this.allEntities) { |
|
|
|
if (powerZoneEntity.type === 'powerZone' && powerZoneEntity.centerEntity === pickedEntity) { |
|
|
|
entityData = powerZoneEntity |
|
|
|
break |
|
|
|
// 特殊处理:如果点击的是威力区的圆心,找到对应的威力区实体 |
|
|
|
if (!entityData) { |
|
|
|
for (const powerZoneEntity of this.allEntities) { |
|
|
|
if (powerZoneEntity.type === 'powerZone' && powerZoneEntity.centerEntity === pickedEntity) { |
|
|
|
entityData = powerZoneEntity |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (entityData && entityData.type !== 'route') { |
|
|
|
// 显示上下文菜单 |
|
|
|
this.contextMenu = { |
|
|
|
visible: true, |
|
|
|
position: { |
|
|
|
@ -3971,6 +4049,24 @@ export default { |
|
|
|
this.selectedEntity = null |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 右键飞机:切换该航线飞机标牌的显示/隐藏 */ |
|
|
|
toggleRouteLabelVisibility() { |
|
|
|
const ed = this.contextMenu.entityData |
|
|
|
if (!ed || ed.type !== 'routePlatform' || ed.routeId == null) { |
|
|
|
this.contextMenu.visible = false |
|
|
|
return |
|
|
|
} |
|
|
|
const routeId = ed.routeId |
|
|
|
const nextVisible = !(this.routeLabelVisible[routeId] !== false) |
|
|
|
this.$set(this.routeLabelVisible, routeId, nextVisible) |
|
|
|
const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`) |
|
|
|
if (labelEntity) { |
|
|
|
labelEntity.show = nextVisible |
|
|
|
} |
|
|
|
this.contextMenu.visible = false |
|
|
|
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender() |
|
|
|
this.$message && this.$message.success(nextVisible ? '已显示标牌' : '已隐藏标牌') |
|
|
|
}, |
|
|
|
// 从右键菜单删除实体 |
|
|
|
deleteEntityFromContextMenu() { |
|
|
|
if (this.contextMenu.entityData) { |
|
|
|
|