|
|
|
@ -7,11 +7,13 @@ |
|
|
|
:drawing-mode="drawingMode" |
|
|
|
:has-entities="allEntities.length > 0" |
|
|
|
:tool-mode="toolMode" |
|
|
|
:auxiliary-line-constraint="auxiliaryLineConstraint" |
|
|
|
@toggle-drawing="toggleDrawing" |
|
|
|
@clear-all="clearAll" |
|
|
|
@export-data="exportData" |
|
|
|
@import-data="importData" |
|
|
|
@locate="handleLocate" |
|
|
|
@auxiliary-line-constraint="auxiliaryLineConstraint = $event" |
|
|
|
/> |
|
|
|
<measurement-panel |
|
|
|
v-if="measurementResult" |
|
|
|
@ -391,6 +393,16 @@ export default { |
|
|
|
mapDragEnabled: { |
|
|
|
type: Boolean, |
|
|
|
default: false |
|
|
|
}, |
|
|
|
/** 白板模式:隐藏主地图绘制/平台/航线,仅显示白板实体 */ |
|
|
|
whiteboardMode: { |
|
|
|
type: Boolean, |
|
|
|
default: false |
|
|
|
}, |
|
|
|
/** 白板模式下要显示的实体列表 */ |
|
|
|
whiteboardEntities: { |
|
|
|
type: Array, |
|
|
|
default: () => [] |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
@ -476,6 +488,10 @@ export default { |
|
|
|
opacity: 0.3, |
|
|
|
visible: true |
|
|
|
}, |
|
|
|
/** 白板模式:进入时保存的主地图实体原始 show 状态,退出时还原 */ |
|
|
|
whiteboardHiddenEntityShows: {}, |
|
|
|
/** 白板平台实体 id -> entityData,用于拖拽/旋转时更新并回传 */ |
|
|
|
whiteboardEntityDataMap: {}, |
|
|
|
/** 独立平台图标(非航线)的探测区/威力区样式缓存:platformIconId -> { ... } */ |
|
|
|
platformIconZoneStyles: {}, |
|
|
|
/** 探测区/威力区弹窗预选颜色(可与透明度组合) */ |
|
|
|
@ -525,6 +541,8 @@ export default { |
|
|
|
text: { color: '#000000', font: '14px PingFang SC, Microsoft YaHei, Helvetica Neue, sans-serif', backgroundColor: 'rgba(255, 255, 255, 0.92)' }, |
|
|
|
image: { width: 150, height: 150 } |
|
|
|
}, |
|
|
|
/** 辅助线:水平/竖直约束,'none' | 'horizontal' | 'vertical' */ |
|
|
|
auxiliaryLineConstraint: 'none', |
|
|
|
// 鼠标经纬度 |
|
|
|
coordinatesText: '经度: --, 纬度: --', |
|
|
|
currentCoordinates: null, |
|
|
|
@ -657,6 +675,29 @@ export default { |
|
|
|
if (!val) { |
|
|
|
this.clearMissilePreview() |
|
|
|
} |
|
|
|
}, |
|
|
|
whiteboardMode: { |
|
|
|
handler(active) { |
|
|
|
this.applyWhiteboardMode(active) |
|
|
|
if (active) { |
|
|
|
// 双 nextTick 确保父组件已更新 whiteboardEntities(如从主地图切回时 loadWhiteboards 完成) |
|
|
|
this.$nextTick(() => { |
|
|
|
this.$nextTick(() => { |
|
|
|
const ents = this.whiteboardEntities || [] |
|
|
|
if (ents.length > 0) this.renderWhiteboardEntities(ents) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
whiteboardEntities: { |
|
|
|
deep: true, |
|
|
|
immediate: true, |
|
|
|
handler(entities) { |
|
|
|
if (this.whiteboardMode && entities && entities.length > 0) { |
|
|
|
this.renderWhiteboardEntities(entities) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
@ -1646,9 +1687,15 @@ export default { |
|
|
|
/** 从手柄实体 id 解析出平台图标 entityData 与类型(rotate / scale-0~3) */ |
|
|
|
getPlatformIconDataFromHandleId(handleEntityId) { |
|
|
|
if (!handleEntityId || typeof handleEntityId !== 'string') return null; |
|
|
|
const findEd = (baseId) => { |
|
|
|
const ed = this.allEntities.find(e => e.type === 'platformIcon' && e.id === baseId); |
|
|
|
if (ed) return ed; |
|
|
|
if (baseId.startsWith('wb_') && this.whiteboardEntityDataMap) return this.whiteboardEntityDataMap[baseId]; |
|
|
|
return null; |
|
|
|
}; |
|
|
|
if (handleEntityId.endsWith('-rotate-handle')) { |
|
|
|
const baseId = handleEntityId.replace(/-rotate-handle$/, ''); |
|
|
|
const ed = this.allEntities.find(e => e.type === 'platformIcon' && e.id === baseId); |
|
|
|
const ed = findEd(baseId); |
|
|
|
return ed ? { entityData: ed, type: 'rotate' } : null; |
|
|
|
} |
|
|
|
const scaleIdx = handleEntityId.lastIndexOf('-scale-'); |
|
|
|
@ -1656,7 +1703,7 @@ export default { |
|
|
|
const baseId = handleEntityId.substring(0, scaleIdx); |
|
|
|
const cornerIndex = parseInt(handleEntityId.substring(scaleIdx + 7), 10); |
|
|
|
if (!isNaN(cornerIndex) && cornerIndex >= 0 && cornerIndex <= 3) { |
|
|
|
const ed = this.allEntities.find(e => e.type === 'platformIcon' && e.id === baseId); |
|
|
|
const ed = findEd(baseId); |
|
|
|
return ed ? { entityData: ed, type: 'scale', cornerIndex } : null; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -3702,10 +3749,10 @@ export default { |
|
|
|
|
|
|
|
/** |
|
|
|
* 从 Redis 加载指定房间+航线列表的导弹数据并创建实体(会先清空当前导弹实体) |
|
|
|
* routePlatforms: [{ routeId, platformId }] |
|
|
|
* routePlatforms: [{ routeId, platformId, roomId? }],roomId 可选,大房间时按航线所属方案用 plan.roomId |
|
|
|
*/ |
|
|
|
async loadMissilesFromRedis(roomId, routePlatforms) { |
|
|
|
if (!this.viewer || !this.viewer.entities || !roomId || !Array.isArray(routePlatforms)) return |
|
|
|
if (!this.viewer || !this.viewer.entities || !Array.isArray(routePlatforms)) return |
|
|
|
// 先清空当前所有导弹实体(包括 routePlatforms 为空时,如删除航线后需清除地图上的导弹) |
|
|
|
while (this.missileEntities.length) { |
|
|
|
const { entity } = this.missileEntities.pop() |
|
|
|
@ -3715,9 +3762,12 @@ export default { |
|
|
|
if (this.viewer.scene) this.viewer.scene.requestRender() |
|
|
|
return |
|
|
|
} |
|
|
|
for (const { routeId, platformId } of routePlatforms) { |
|
|
|
for (const item of routePlatforms) { |
|
|
|
const { routeId, platformId } = item |
|
|
|
const rId = item.roomId != null ? item.roomId : roomId |
|
|
|
if (!rId) continue |
|
|
|
try { |
|
|
|
const res = await getMissileParams({ roomId, routeId, platformId: platformId != null ? platformId : 0 }) |
|
|
|
const res = await getMissileParams({ roomId: rId, routeId, platformId: platformId != null ? platformId : 0 }) |
|
|
|
let data = res.data |
|
|
|
if (!data) continue |
|
|
|
const arr = Array.isArray(data) ? data : [data] |
|
|
|
@ -3825,6 +3875,8 @@ export default { |
|
|
|
} |
|
|
|
this.loadOfflineMap() |
|
|
|
this.setup2DConstraints() |
|
|
|
// 禁用右键拖拽缩放,仅保留滚轮缩放 |
|
|
|
this.viewer.scene.screenSpaceCameraController.zoomEventTypes = Cesium.CameraEventType.WHEEL |
|
|
|
// 移除 Cesium 自带的双击缩放,使双击实体/空白均无任何效果 |
|
|
|
try { |
|
|
|
const handler = this.viewer.screenSpaceEventHandler || (this.viewer.cesiumWidget && this.viewer.cesiumWidget.screenSpaceEventHandler) |
|
|
|
@ -4254,6 +4306,9 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (!entityData && idStr.startsWith('wb_')) { |
|
|
|
entityData = this.whiteboardEntityDataMap && this.whiteboardEntityDataMap[idStr] |
|
|
|
} |
|
|
|
if (!entityData) { |
|
|
|
// 查找对应的实体数据 |
|
|
|
entityData = this.allEntities.find(e => e.entity === pickedEntity || e === pickedEntity) |
|
|
|
@ -4466,7 +4521,10 @@ export default { |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
const entityData = this.allEntities.find(e => e.type === 'platformIcon' && e.entity === picked.id); |
|
|
|
let entityData = this.allEntities.find(e => e.type === 'platformIcon' && e.entity === picked.id); |
|
|
|
if (!entityData && idStr.startsWith('wb_')) { |
|
|
|
entityData = this.whiteboardEntityDataMap && this.whiteboardEntityDataMap[idStr]; |
|
|
|
} |
|
|
|
if (entityData) { |
|
|
|
this.pendingDragIcon = entityData; |
|
|
|
this.dragStartScreenPos = { x: click.position.x, y: click.position.y }; |
|
|
|
@ -4582,22 +4640,39 @@ export default { |
|
|
|
} |
|
|
|
this.clickedOnEmpty = false; |
|
|
|
if (this.draggingRotateHandle) { |
|
|
|
if (this.draggingRotateHandle.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-platform-updated', this.draggingRotateHandle); |
|
|
|
} else { |
|
|
|
this.$emit('platform-icon-updated', this.draggingRotateHandle); |
|
|
|
} |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = this.platformIconDragCameraEnabled; |
|
|
|
this.draggingRotateHandle = null; |
|
|
|
} |
|
|
|
if (this.draggingScaleHandle) { |
|
|
|
this.$emit('platform-icon-updated', this.draggingScaleHandle.entityData); |
|
|
|
const ed = this.draggingScaleHandle.entityData; |
|
|
|
if (ed && ed.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-platform-updated', ed); |
|
|
|
} else { |
|
|
|
this.$emit('platform-icon-updated', ed); |
|
|
|
} |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = this.platformIconDragCameraEnabled; |
|
|
|
this.draggingScaleHandle = null; |
|
|
|
} |
|
|
|
if (this.draggingPlatformIcon) { |
|
|
|
if (this.draggingPlatformIcon.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-platform-updated', this.draggingPlatformIcon); |
|
|
|
} else { |
|
|
|
this.$emit('platform-icon-updated', this.draggingPlatformIcon); |
|
|
|
} |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = this.platformIconDragCameraEnabled; |
|
|
|
this.draggingPlatformIcon = null; |
|
|
|
} |
|
|
|
if (this.rotatingPlatformIcon) { |
|
|
|
if (this.rotatingPlatformIcon.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-platform-updated', this.rotatingPlatformIcon); |
|
|
|
} else { |
|
|
|
this.$emit('platform-icon-updated', this.rotatingPlatformIcon); |
|
|
|
} |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = this.platformIconDragCameraEnabled; |
|
|
|
this.rotatingPlatformIcon = null; |
|
|
|
this.platformIconRotateTip = ''; |
|
|
|
@ -5100,14 +5175,26 @@ export default { |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
finishLineDrawing() { |
|
|
|
// 将预览线段转换为最终线段 |
|
|
|
if (this.drawingPoints.length > 1) { |
|
|
|
// 移除预览线段 |
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
} |
|
|
|
// 创建最终的实线实体(updateLineSegmentLabels 会在 addLineEntity 中设置各段终点标签,含累计长度和角度) |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const points = this.drawingPoints.map(p => this.cartesianToLatLng(p)) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_line_' + Date.now(), |
|
|
|
type: 'line', |
|
|
|
label: '测距', |
|
|
|
color: this.defaultStyles.line ? this.defaultStyles.line.color : '#008aff', |
|
|
|
data: { points, width: (this.defaultStyles.line && this.defaultStyles.line.width) || 2 } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
this.drawingPointEntities = [] |
|
|
|
this.tempEntity = null |
|
|
|
return null |
|
|
|
} |
|
|
|
const entity = this.addLineEntity([...this.drawingPoints], [...this.drawingPointEntities]); |
|
|
|
// 计算长度 |
|
|
|
const length = this.calculateLineLength([...this.drawingPoints]); |
|
|
|
@ -5203,6 +5290,20 @@ export default { |
|
|
|
}, |
|
|
|
finishPolygonDrawing() { |
|
|
|
const positions = [...this.drawingPoints] |
|
|
|
if (this.whiteboardMode && positions.length >= 3) { |
|
|
|
const points = positions.map(p => this.cartesianToLatLng(p)) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_polygon_' + Date.now(), |
|
|
|
type: 'polygon', |
|
|
|
label: '面', |
|
|
|
color: this.defaultStyles.polygon.color, |
|
|
|
data: { points, opacity: 0, width: this.defaultStyles.polygon.width } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
if (this.tempEntity) { this.viewer.entities.remove(this.tempEntity); this.tempEntity = null } |
|
|
|
return |
|
|
|
} |
|
|
|
const entity = this.addPolygonEntity(positions) |
|
|
|
// 计算面积 |
|
|
|
const area = this.calculatePolygonArea(positions) |
|
|
|
@ -5300,6 +5401,29 @@ export default { |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
finishRectangleDrawing() { |
|
|
|
if (this.whiteboardMode && this.drawingPoints.length >= 2) { |
|
|
|
const rect = Cesium.Rectangle.fromCartesianArray(this.drawingPoints) |
|
|
|
const west = Cesium.Math.toDegrees(rect.west) |
|
|
|
const south = Cesium.Math.toDegrees(rect.south) |
|
|
|
const east = Cesium.Math.toDegrees(rect.east) |
|
|
|
const north = Cesium.Math.toDegrees(rect.north) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_rectangle_' + Date.now(), |
|
|
|
type: 'rectangle', |
|
|
|
label: '矩形', |
|
|
|
color: this.defaultStyles.rectangle.color, |
|
|
|
data: { |
|
|
|
coordinates: { west, south, east, north }, |
|
|
|
points: this.drawingPoints.map(p => this.cartesianToLatLng(p)), |
|
|
|
opacity: this.defaultStyles.rectangle.opacity, |
|
|
|
width: this.defaultStyles.rectangle.width |
|
|
|
} |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
if (this.tempEntity) { this.viewer.entities.remove(this.tempEntity); this.tempEntity = null } |
|
|
|
return |
|
|
|
} |
|
|
|
// 1. 获取最终的矩形范围对象 |
|
|
|
const rect = Cesium.Rectangle.fromCartesianArray(this.drawingPoints); |
|
|
|
// 2. 移除动态预览的临时实体 |
|
|
|
@ -5539,13 +5663,25 @@ export default { |
|
|
|
}, |
|
|
|
finishCircleDrawing(edgePosition) { |
|
|
|
const centerPoint = this.drawingPoints[0]; |
|
|
|
// 1. 计算最终半径 |
|
|
|
const radius = Cesium.Cartesian3.distance(centerPoint, edgePosition); |
|
|
|
// 2. 移除动态预览实体 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
} |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const center = this.cartesianToLatLng(centerPoint) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_circle_' + Date.now(), |
|
|
|
type: 'circle', |
|
|
|
label: '圆形', |
|
|
|
color: this.defaultStyles.circle.color, |
|
|
|
data: { center: { lat: center.lat, lng: center.lng }, radius, opacity: this.defaultStyles.circle.opacity, width: this.defaultStyles.circle.width } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
this.activeCursorPosition = null |
|
|
|
return |
|
|
|
} |
|
|
|
// 3. 创建最终显示的静态实体 |
|
|
|
this.entityCounter++; |
|
|
|
const id = `circle_${this.entityCounter}`; |
|
|
|
@ -5734,11 +5870,23 @@ export default { |
|
|
|
const radius = Cesium.Cartesian3.distance(centerPoint, radiusPoint); |
|
|
|
const startAngle = this.calculatePointAngle(centerPoint, radiusPoint); |
|
|
|
const endAngle = this.calculatePointAngle(centerPoint, anglePoint); |
|
|
|
// 1. 移除动态预览实体 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
} |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const center = this.cartesianToLatLng(centerPoint) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_sector_' + Date.now(), |
|
|
|
type: 'sector', |
|
|
|
label: '扇形', |
|
|
|
color: this.defaultStyles.sector.color, |
|
|
|
data: { center: { lat: center.lat, lng: center.lng }, radius, startAngle, endAngle, opacity: this.defaultStyles.sector.opacity, width: this.defaultStyles.sector.width } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
return |
|
|
|
} |
|
|
|
// 2. 生成扇形顶点 |
|
|
|
const positions = this.generateSectorPositions(centerPoint, radius, startAngle, endAngle); |
|
|
|
// 3. 创建最终显示的静态实体 |
|
|
|
@ -5886,6 +6034,20 @@ export default { |
|
|
|
calculateDistance(point1, point2) { |
|
|
|
return Cesium.Cartesian3.distance(point1, point2); |
|
|
|
}, |
|
|
|
/** 根据辅助线约束修正第二点坐标(水平:同纬度;竖直:同经度) */ |
|
|
|
applyAuxiliaryLineConstraint(firstPos, secondPos) { |
|
|
|
if (!firstPos || !secondPos || this.auxiliaryLineConstraint === 'none') return secondPos |
|
|
|
const firstLL = this.cartesianToLatLng(firstPos) |
|
|
|
const secondLL = this.cartesianToLatLng(secondPos) |
|
|
|
if (!firstLL || !secondLL) return secondPos |
|
|
|
if (this.auxiliaryLineConstraint === 'horizontal') { |
|
|
|
return Cesium.Cartesian3.fromDegrees(secondLL.lng, firstLL.lat) |
|
|
|
} |
|
|
|
if (this.auxiliaryLineConstraint === 'vertical') { |
|
|
|
return Cesium.Cartesian3.fromDegrees(firstLL.lng, secondLL.lat) |
|
|
|
} |
|
|
|
return secondPos |
|
|
|
}, |
|
|
|
// 绘制辅助线(两点定线段,默认黑色 1px) |
|
|
|
startAuxiliaryLineDrawing() { |
|
|
|
this.drawingPoints = [] |
|
|
|
@ -5893,15 +6055,22 @@ export default { |
|
|
|
if (this.tempPreviewEntity) this.viewer.entities.remove(this.tempPreviewEntity) |
|
|
|
this.tempEntity = null |
|
|
|
this.tempPreviewEntity = null |
|
|
|
const that = this |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
const newPosition = this.getClickPosition(movement.endPosition) |
|
|
|
let newPosition = that.getClickPosition(movement.endPosition) |
|
|
|
if (newPosition && that.drawingPoints.length === 1) { |
|
|
|
newPosition = that.applyAuxiliaryLineConstraint(that.drawingPoints[0], newPosition) |
|
|
|
} |
|
|
|
if (newPosition) { |
|
|
|
this.activeCursorPosition = newPosition |
|
|
|
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender() |
|
|
|
that.activeCursorPosition = newPosition |
|
|
|
if (that.viewer.scene.requestRenderMode) that.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
const position = this.getClickPosition(click.position) |
|
|
|
let position = this.getClickPosition(click.position) |
|
|
|
if (position && this.drawingPoints.length === 1) { |
|
|
|
position = this.applyAuxiliaryLineConstraint(this.drawingPoints[0], position) |
|
|
|
} |
|
|
|
if (position) { |
|
|
|
this.drawingPoints.push(position) |
|
|
|
if (this.drawingPoints.length === 1) { |
|
|
|
@ -5935,6 +6104,19 @@ export default { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity) |
|
|
|
this.tempPreviewEntity = null |
|
|
|
} |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const points = this.drawingPoints.map(p => this.cartesianToLatLng(p)) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_auxiliaryLine_' + Date.now(), |
|
|
|
type: 'auxiliaryLine', |
|
|
|
label: '辅助线', |
|
|
|
color: this.defaultStyles.auxiliaryLine.color, |
|
|
|
data: { points, width: this.defaultStyles.auxiliaryLine.width } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
return null |
|
|
|
} |
|
|
|
const entity = this.addAuxiliaryLineEntity([...this.drawingPoints]) |
|
|
|
this.drawingPoints = [] |
|
|
|
this.notifyDrawingEntitiesChanged() |
|
|
|
@ -6041,14 +6223,24 @@ export default { |
|
|
|
}, |
|
|
|
// 完成箭头绘制 |
|
|
|
finishArrowDrawing() { |
|
|
|
// 将预览箭头转换为最终箭头 |
|
|
|
if (this.drawingPoints.length > 1) { |
|
|
|
// 移除预览箭头 |
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
} |
|
|
|
// 创建最终的箭头实体 |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const points = this.drawingPoints.map(p => this.cartesianToLatLng(p)) |
|
|
|
const entityData = { |
|
|
|
id: 'wb_arrow_' + Date.now(), |
|
|
|
type: 'arrow', |
|
|
|
label: '箭头', |
|
|
|
color: this.defaultStyles.arrow.color, |
|
|
|
data: { points, width: this.defaultStyles.arrow.width } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
this.drawingPoints = [] |
|
|
|
return null |
|
|
|
} |
|
|
|
const entity = this.addArrowEntity([...this.drawingPoints]); |
|
|
|
// 重置绘制点数组,保持绘制状态以继续绘制 |
|
|
|
this.drawingPoints = []; |
|
|
|
@ -6121,10 +6313,20 @@ export default { |
|
|
|
}, |
|
|
|
// 添加文本实体 |
|
|
|
addTextEntity(position, text) { |
|
|
|
const { lat, lng } = this.cartesianToLatLng(position) |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const entityData = { |
|
|
|
id: 'wb_text_' + Date.now(), |
|
|
|
type: 'text', |
|
|
|
label: text, |
|
|
|
color: this.defaultStyles.text.color, |
|
|
|
data: { position: { lat, lng }, text, lat, lng, fontSize: 14, color: this.defaultStyles.text.color } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
return |
|
|
|
} |
|
|
|
this.entityCounter++ |
|
|
|
const id = `text_${this.entityCounter}` |
|
|
|
// 获取经纬度坐标 |
|
|
|
const {lat, lng} = this.cartesianToLatLng(position) |
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id: id, |
|
|
|
name: `文本 ${this.entityCounter}`, |
|
|
|
@ -6248,10 +6450,19 @@ export default { |
|
|
|
}, |
|
|
|
// 添加图片实体 |
|
|
|
addImageEntity(position, imageUrl) { |
|
|
|
const { lat, lng } = this.cartesianToLatLng(position) |
|
|
|
if (this.whiteboardMode) { |
|
|
|
const entityData = { |
|
|
|
id: 'wb_image_' + Date.now(), |
|
|
|
type: 'image', |
|
|
|
label: '图片', |
|
|
|
data: { lat, lng, imageUrl, width: this.defaultStyles.image.width, height: this.defaultStyles.image.height } |
|
|
|
} |
|
|
|
this.$emit('whiteboard-draw-complete', entityData) |
|
|
|
return |
|
|
|
} |
|
|
|
this.entityCounter++ |
|
|
|
const id = `image_${this.entityCounter}` |
|
|
|
// 获取经纬度坐标 |
|
|
|
const {lat, lng} = this.cartesianToLatLng(position) |
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id: id, |
|
|
|
name: `图片 ${this.entityCounter}`, |
|
|
|
@ -7349,12 +7560,15 @@ export default { |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
case 'circle': |
|
|
|
case 'circle': { |
|
|
|
entityData.entity.position = newCenter; |
|
|
|
const radius = entityData.radius || 1000; |
|
|
|
entityData.entity.polyline.positions = this.generateCirclePositions(newCenter, radius); |
|
|
|
entityData.points = [this.cartesianToLatLng(newCenter), entityData.points && entityData.points[1] ? entityData.points[1] : this.cartesianToLatLng(newCenter)]; |
|
|
|
const circleCenterLL = this.cartesianToLatLng(newCenter); |
|
|
|
entityData.center = circleCenterLL; |
|
|
|
entityData.points = [circleCenterLL, entityData.points && entityData.points[1] ? entityData.points[1] : circleCenterLL]; |
|
|
|
break; |
|
|
|
} |
|
|
|
case 'sector': { |
|
|
|
const newCenterLL = this.cartesianToLatLng(newCenter); |
|
|
|
entityData.center = newCenterLL; |
|
|
|
@ -7429,7 +7643,11 @@ export default { |
|
|
|
break; |
|
|
|
} |
|
|
|
entityData.entity.show = true; |
|
|
|
if (entityData.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-drawing-updated', this.serializeWhiteboardDrawingEntity(entityData)); |
|
|
|
} else { |
|
|
|
this.notifyDrawingEntitiesChanged(); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 清除空域位置调整模式 */ |
|
|
|
@ -8313,12 +8531,15 @@ export default { |
|
|
|
deleteEntityFromContextMenu() { |
|
|
|
if (this.contextMenu.entityData) { |
|
|
|
const entityData = this.contextMenu.entityData |
|
|
|
if (entityData.id) { |
|
|
|
if (entityData.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-entity-deleted', entityData) |
|
|
|
if (entityData.entity) this.viewer.entities.remove(entityData.entity) |
|
|
|
if (this.whiteboardEntityDataMap && entityData.id) delete this.whiteboardEntityDataMap[entityData.id] |
|
|
|
} else if (entityData.id) { |
|
|
|
this.removeEntity(entityData.id) |
|
|
|
} else if (entityData.entity && entityData.entity.id) { |
|
|
|
this.removeEntity(entityData.entity.id) |
|
|
|
} |
|
|
|
// 隐藏右键菜单 |
|
|
|
this.contextMenu.visible = false |
|
|
|
} |
|
|
|
}, |
|
|
|
@ -8458,11 +8679,12 @@ export default { |
|
|
|
} |
|
|
|
// 更新实体样式 |
|
|
|
this.updateEntityStyle(entityData) |
|
|
|
// 若修改的是空域/威力区图形的样式,触发自动保存到房间,避免刷新后样式丢失 |
|
|
|
if (this.getDrawingEntityTypes().includes(entityData.type)) { |
|
|
|
// 白板实体:通知父组件更新 contentByTime |
|
|
|
if (entityData.isWhiteboard && this.getDrawingEntityTypes().includes(entityData.type)) { |
|
|
|
this.$emit('whiteboard-drawing-updated', this.serializeWhiteboardDrawingEntity(entityData)) |
|
|
|
} else if (this.getDrawingEntityTypes().includes(entityData.type)) { |
|
|
|
this.notifyDrawingEntitiesChanged() |
|
|
|
} |
|
|
|
// 隐藏右键菜单 |
|
|
|
this.contextMenu.visible = false |
|
|
|
} |
|
|
|
}, |
|
|
|
@ -8474,7 +8696,9 @@ export default { |
|
|
|
this.contextMenu.visible = false |
|
|
|
return |
|
|
|
} |
|
|
|
const ed = this.allEntities.find(e => e.type === 'platformIcon' && e.id === fromMenu.id) || fromMenu |
|
|
|
let ed = this.allEntities.find(e => e.type === 'platformIcon' && e.id === fromMenu.id) |
|
|
|
if (!ed && fromMenu.id && this.whiteboardEntityDataMap) ed = this.whiteboardEntityDataMap[fromMenu.id] |
|
|
|
if (!ed) ed = fromMenu |
|
|
|
if (!ed.entity) { |
|
|
|
this.contextMenu.visible = false |
|
|
|
return |
|
|
|
@ -8738,6 +8962,67 @@ export default { |
|
|
|
if (this.loadingDrawingsFromRoom) return |
|
|
|
this.$emit('drawing-entities-changed') |
|
|
|
}, |
|
|
|
/** 白板绘制实体序列化(排除 entity 引用,供父组件更新 contentByTime;输出可复用的 data 结构) */ |
|
|
|
serializeWhiteboardDrawingEntity(entityData) { |
|
|
|
if (!entityData || !entityData.id) return null |
|
|
|
const base = { id: entityData.id, type: entityData.type, color: entityData.color || '#008aff' } |
|
|
|
const data = entityData.data ? { ...entityData.data } : {} |
|
|
|
switch (entityData.type) { |
|
|
|
case 'polygon': |
|
|
|
if (entityData.points && entityData.points.length >= 3) { |
|
|
|
data.points = entityData.points |
|
|
|
data.opacity = entityData.opacity != null ? entityData.opacity : 0 |
|
|
|
data.width = entityData.width != null ? entityData.width : 2 |
|
|
|
data.borderColor = entityData.borderColor || entityData.color |
|
|
|
} |
|
|
|
break |
|
|
|
case 'rectangle': |
|
|
|
if (entityData.points && entityData.points.length >= 2) { |
|
|
|
const lngs = entityData.points.map(p => p.lng) |
|
|
|
const lats = entityData.points.map(p => p.lat) |
|
|
|
data.coordinates = { west: Math.min(...lngs), south: Math.min(...lats), east: Math.max(...lngs), north: Math.max(...lats) } |
|
|
|
data.points = entityData.points |
|
|
|
} |
|
|
|
data.opacity = entityData.opacity != null ? entityData.opacity : 0 |
|
|
|
data.width = entityData.width != null ? entityData.width : 2 |
|
|
|
data.borderColor = entityData.borderColor || entityData.color |
|
|
|
break |
|
|
|
case 'circle': |
|
|
|
data.center = entityData.center || (entityData.points && entityData.points[0] ? entityData.points[0] : null) |
|
|
|
data.radius = entityData.radius != null ? entityData.radius : 1000 |
|
|
|
data.opacity = entityData.opacity != null ? entityData.opacity : 0 |
|
|
|
data.width = entityData.width != null ? entityData.width : 2 |
|
|
|
data.borderColor = entityData.borderColor || entityData.color |
|
|
|
break |
|
|
|
case 'sector': |
|
|
|
data.center = entityData.center || (entityData.points && entityData.points[0] ? entityData.points[0] : null) |
|
|
|
data.radius = entityData.radius != null ? entityData.radius : 1000 |
|
|
|
data.startAngle = entityData.startAngle != null ? entityData.startAngle : 0 |
|
|
|
data.endAngle = entityData.endAngle != null ? entityData.endAngle : Math.PI * 0.5 |
|
|
|
data.opacity = entityData.opacity != null ? entityData.opacity : 0.5 |
|
|
|
data.width = entityData.width != null ? entityData.width : 2 |
|
|
|
data.borderColor = entityData.borderColor || entityData.color |
|
|
|
break |
|
|
|
case 'line': |
|
|
|
case 'auxiliaryLine': |
|
|
|
if (entityData.points && entityData.points.length >= 2) { |
|
|
|
data.points = entityData.points |
|
|
|
data.width = entityData.width != null ? entityData.width : 2 |
|
|
|
data.color = entityData.color || entityData.data?.color |
|
|
|
} |
|
|
|
break |
|
|
|
case 'arrow': |
|
|
|
if (entityData.points && entityData.points.length >= 2) { |
|
|
|
data.points = entityData.points |
|
|
|
data.width = entityData.width != null ? entityData.width : 12 |
|
|
|
data.color = entityData.color || entityData.data?.color |
|
|
|
} |
|
|
|
break |
|
|
|
default: |
|
|
|
return { ...base, data: entityData.data || {} } |
|
|
|
} |
|
|
|
return { ...base, data } |
|
|
|
}, |
|
|
|
/** 将单个实体序列化为可存储的 JSON 结构 */ |
|
|
|
serializeEntityForSave(entity) { |
|
|
|
const base = { id: entity.id, type: entity.type, label: entity.label || '', color: entity.color || '#008aff' } |
|
|
|
@ -8878,6 +9163,402 @@ export default { |
|
|
|
}) |
|
|
|
this.allEntities = this.allEntities.filter(item => !types.includes(item.type)) |
|
|
|
}, |
|
|
|
/** 从屏幕坐标获取经纬度(用于白板拖放等) */ |
|
|
|
getLatLngFromScreen(clientX, clientY) { |
|
|
|
if (!this.viewer || !this.viewer.scene || !this.viewer.scene.canvas) return null |
|
|
|
const canvas = this.viewer.scene.canvas |
|
|
|
const rect = canvas.getBoundingClientRect() |
|
|
|
const x = clientX - rect.left |
|
|
|
const y = clientY - rect.top |
|
|
|
const cartesian = this.viewer.camera.pickEllipsoid(new Cesium.Cartesian2(x, y), this.viewer.scene.globe.ellipsoid) |
|
|
|
if (!cartesian) return null |
|
|
|
return this.cartesianToLatLng(cartesian) |
|
|
|
}, |
|
|
|
/** 白板模式:遍历 viewer 全部实体,隐藏非白板实体(含航点、航线、平台等),退出时还原 */ |
|
|
|
applyWhiteboardMode(active) { |
|
|
|
if (!this.viewer) return |
|
|
|
const now = Cesium.JulianDate.now() |
|
|
|
if (active) { |
|
|
|
this.whiteboardHiddenEntityShows = {} |
|
|
|
this.viewer.entities.values.forEach(entity => { |
|
|
|
const id = entity.id |
|
|
|
if (!id || String(id).startsWith('wb_')) return |
|
|
|
let origShow = true |
|
|
|
if (entity.show != null) { |
|
|
|
origShow = typeof entity.show.getValue === 'function' ? entity.show.getValue(now) : entity.show |
|
|
|
} |
|
|
|
this.whiteboardHiddenEntityShows[id] = origShow |
|
|
|
entity.show = false |
|
|
|
}) |
|
|
|
this.renderWhiteboardEntities(this.whiteboardEntities || []) |
|
|
|
} else { |
|
|
|
Object.keys(this.whiteboardHiddenEntityShows || {}).forEach(id => { |
|
|
|
const entity = this.viewer.entities.getById(id) |
|
|
|
if (entity) entity.show = this.whiteboardHiddenEntityShows[id] |
|
|
|
}) |
|
|
|
this.whiteboardHiddenEntityShows = {} |
|
|
|
this.clearWhiteboardEntities() |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 清除白板实体(id 以 wb_ 开头) */ |
|
|
|
clearWhiteboardEntities() { |
|
|
|
if (!this.viewer) return |
|
|
|
const toRemove = [] |
|
|
|
this.viewer.entities.values.forEach(e => { |
|
|
|
if (e.id && String(e.id).startsWith('wb_')) toRemove.push(e) |
|
|
|
}) |
|
|
|
toRemove.forEach(e => this.viewer.entities.remove(e)) |
|
|
|
this.whiteboardEntityDataMap = {} |
|
|
|
}, |
|
|
|
/** 渲染白板实体到地图(平台图标:已存在则就地更新,避免清除重建导致渲染闪烁/消失) */ |
|
|
|
renderWhiteboardEntities(entities) { |
|
|
|
if (!this.viewer || !Array.isArray(entities)) return |
|
|
|
const scene = this.viewer.scene |
|
|
|
const wantIds = new Set() |
|
|
|
entities.forEach(ed => { |
|
|
|
if (ed.type === 'platformIcon') { |
|
|
|
const id = (ed.id || '').startsWith('wb_') ? ed.id : ('wb_' + (ed.id || '')) |
|
|
|
if (id) wantIds.add(id) |
|
|
|
} else { |
|
|
|
const id = (ed.id || '').startsWith('wb_') ? ed.id : ('wb_' + (ed.id || '')) |
|
|
|
if (id) wantIds.add(id) |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
this.whiteboardEntityDataMap = this.whiteboardEntityDataMap || {} |
|
|
|
const toRemove = [] |
|
|
|
this.viewer.entities.values.forEach(e => { |
|
|
|
if (e.id && String(e.id).startsWith('wb_') && !wantIds.has(e.id)) toRemove.push(e) |
|
|
|
}) |
|
|
|
toRemove.forEach(e => this.viewer.entities.remove(e)) |
|
|
|
Object.keys(this.whiteboardEntityDataMap).forEach(id => { |
|
|
|
if (!wantIds.has(id)) { |
|
|
|
const ed = this.whiteboardEntityDataMap[id] |
|
|
|
if (ed && ed.entity) this.viewer.entities.remove(ed.entity) |
|
|
|
delete this.whiteboardEntityDataMap[id] |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
entities.forEach(ed => { |
|
|
|
try { |
|
|
|
if (ed.type === 'platformIcon') { |
|
|
|
const id = (ed.id || 'wb_platform_' + Date.now()).startsWith('wb_') ? ed.id : 'wb_' + ed.id |
|
|
|
const existing = this.whiteboardEntityDataMap[id] |
|
|
|
if (existing && existing.entity) { |
|
|
|
this.updateWhiteboardPlatformIconInPlace(existing, ed) |
|
|
|
} else { |
|
|
|
this.addWhiteboardPlatformIcon(ed) |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.addWhiteboardDrawingEntity(ed) |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
console.warn('renderWhiteboardEntity failed', ed?.type, err) |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
if (scene && scene.requestRender) { |
|
|
|
scene.requestRender() |
|
|
|
setTimeout(() => scene.requestRender(), 50) |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 就地更新白板平台图标(不销毁实体,避免 requestRenderMode 下渲染消失) */ |
|
|
|
updateWhiteboardPlatformIconInPlace(existing, ed) { |
|
|
|
existing.lat = ed.lat |
|
|
|
existing.lng = ed.lng |
|
|
|
existing.heading = ed.heading != null ? ed.heading : 0 |
|
|
|
existing.iconScale = ed.iconScale != null ? ed.iconScale : 1.5 |
|
|
|
existing.entity.position = Cesium.Cartesian3.fromDegrees(ed.lng, ed.lat) |
|
|
|
existing.entity.billboard.rotation = Math.PI / 2 - (existing.heading * Math.PI / 180) |
|
|
|
this.updatePlatformIconBillboardSize(existing) |
|
|
|
if (existing.transformHandles) this.updateTransformHandlePositions(existing) |
|
|
|
if (this.viewer.scene && this.viewer.scene.requestRender) this.viewer.scene.requestRender() |
|
|
|
}, |
|
|
|
/** 白板平台图标:添加 billboard,并存入 whiteboardEntityDataMap 以支持拖拽/旋转 */ |
|
|
|
addWhiteboardPlatformIcon(ed) { |
|
|
|
const platform = ed.platform || {} |
|
|
|
const iconUrl = platform.imageUrl || platform.iconUrl |
|
|
|
const imageSrc = iconUrl ? this.formatPlatformIconUrl(iconUrl) : this.getDefaultPlatformIconDataUrl() |
|
|
|
const id = (ed.id || 'wb_platform_' + Date.now()).startsWith('wb_') ? ed.id : 'wb_' + ed.id |
|
|
|
const lat = ed.lat |
|
|
|
const lng = ed.lng |
|
|
|
const heading = ed.heading != null ? ed.heading : 0 |
|
|
|
const rotation = Math.PI / 2 - (heading * Math.PI / 180) |
|
|
|
const size = this.PLATFORM_ICON_BASE_SIZE * (ed.iconScale != null ? ed.iconScale : 1.5) |
|
|
|
const cartesian = Cesium.Cartesian3.fromDegrees(lng, lat) |
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id, |
|
|
|
name: ed.label || platform.name || '平台', |
|
|
|
position: cartesian, |
|
|
|
billboard: { |
|
|
|
image: imageSrc, |
|
|
|
width: size, |
|
|
|
height: size, |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
rotation, |
|
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 1.2, 200000, 0.35), |
|
|
|
translucencyByDistance: new Cesium.NearFarScalar(1000, 1.0, 500000, 0.6) |
|
|
|
} |
|
|
|
}) |
|
|
|
const entityData = { id, entity, lat, lng, heading, platform, platformName: platform.name, label: ed.label, type: 'platformIcon', isWhiteboard: true, iconScale: ed.iconScale != null ? ed.iconScale : 1.5 } |
|
|
|
this.whiteboardEntityDataMap = this.whiteboardEntityDataMap || {} |
|
|
|
this.whiteboardEntityDataMap[id] = entityData |
|
|
|
}, |
|
|
|
/** 白板空域/图形实体:复用 importEntity 逻辑但不加入 allEntities */ |
|
|
|
addWhiteboardDrawingEntity(ed) { |
|
|
|
const id = (ed.id || 'wb_draw_' + Date.now()).startsWith('wb_') ? ed.id : 'wb_' + ed.id |
|
|
|
const entityData = { ...ed, id } |
|
|
|
if (ed.data) { |
|
|
|
entityData.data = ed.data |
|
|
|
} |
|
|
|
const existing = this.whiteboardEntityDataMap && this.whiteboardEntityDataMap[id] |
|
|
|
if (existing && existing.entity) { |
|
|
|
this.viewer.entities.remove(existing.entity) |
|
|
|
delete this.whiteboardEntityDataMap[id] |
|
|
|
} |
|
|
|
this.importEntityAsWhiteboard(entityData) |
|
|
|
}, |
|
|
|
/** 仅添加图形到 viewer,不加入 allEntities(用于白板) */ |
|
|
|
importEntityAsWhiteboard(entityData) { |
|
|
|
const color = entityData.color || '#008aff' |
|
|
|
const types = this.getDrawingEntityTypes() |
|
|
|
if (!types.includes(entityData.type)) return |
|
|
|
const data = entityData.data || {} |
|
|
|
let entity |
|
|
|
switch (entityData.type) { |
|
|
|
case 'rectangle': { |
|
|
|
const rectCoords = data.coordinates || (data.points && data.points.length >= 2 |
|
|
|
? { west: Math.min(...data.points.map(p => p.lng)), south: Math.min(...data.points.map(p => p.lat)), east: Math.max(...data.points.map(p => p.lng)), north: Math.max(...data.points.map(p => p.lat)) } |
|
|
|
: null) |
|
|
|
if (!rectCoords) return |
|
|
|
const rectOpacity = data.opacity != null ? data.opacity : 0 |
|
|
|
const rectWidth = data.width != null ? data.width : 2 |
|
|
|
const rectBorderColor = data.borderColor || color |
|
|
|
const southwest = Cesium.Cartesian3.fromDegrees(rectCoords.west, rectCoords.south) |
|
|
|
const southeast = Cesium.Cartesian3.fromDegrees(rectCoords.east, rectCoords.south) |
|
|
|
const northeast = Cesium.Cartesian3.fromDegrees(rectCoords.east, rectCoords.north) |
|
|
|
const northwest = Cesium.Cartesian3.fromDegrees(rectCoords.west, rectCoords.north) |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
rectangle: { |
|
|
|
coordinates: Cesium.Rectangle.fromDegrees(rectCoords.west, rectCoords.south, rectCoords.east, rectCoords.north), |
|
|
|
material: Cesium.Color.fromCssColorString(color).withAlpha(rectOpacity) |
|
|
|
}, |
|
|
|
polyline: { |
|
|
|
positions: [southwest, southeast, northeast, northwest, southwest], |
|
|
|
width: rectWidth, |
|
|
|
material: Cesium.Color.fromCssColorString(rectBorderColor), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.points = [this.cartesianToLatLng(southwest), this.cartesianToLatLng(southeast), this.cartesianToLatLng(northeast), this.cartesianToLatLng(northwest)] |
|
|
|
entityData.opacity = rectOpacity |
|
|
|
entityData.width = rectWidth |
|
|
|
entityData.borderColor = rectBorderColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'polygon': { |
|
|
|
const polygonPositions = (data.points || []).map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat)) |
|
|
|
if (polygonPositions.length < 3) return |
|
|
|
const polyOpacity = data.opacity != null ? data.opacity : 0 |
|
|
|
const polyWidth = data.width != null ? data.width : 2 |
|
|
|
const polyBorderColor = data.borderColor || color |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
polygon: { |
|
|
|
hierarchy: polygonPositions, |
|
|
|
material: Cesium.Color.fromCssColorString(color).withAlpha(polyOpacity) |
|
|
|
}, |
|
|
|
polyline: { |
|
|
|
positions: [...polygonPositions, polygonPositions[0]], |
|
|
|
width: polyWidth, |
|
|
|
material: Cesium.Color.fromCssColorString(polyBorderColor), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.positions = polygonPositions |
|
|
|
entityData.points = data.points || polygonPositions.map(p => this.cartesianToLatLng(p)) |
|
|
|
entityData.opacity = polyOpacity |
|
|
|
entityData.width = polyWidth |
|
|
|
entityData.borderColor = polyBorderColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'circle': { |
|
|
|
const center = data.center || {} |
|
|
|
const radius = data.radius || 1000 |
|
|
|
const circleCenter = Cesium.Cartesian3.fromDegrees(center.lng || 0, center.lat || 0) |
|
|
|
const circlePositions = this.generateCirclePositions(circleCenter, radius) |
|
|
|
const circleOpacity = data.opacity != null ? data.opacity : 0 |
|
|
|
const circleWidth = data.width != null ? data.width : 2 |
|
|
|
const circleBorderColor = data.borderColor || color |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
position: circleCenter, |
|
|
|
ellipse: { |
|
|
|
semiMajorAxis: radius, |
|
|
|
semiMinorAxis: radius, |
|
|
|
material: Cesium.Color.fromCssColorString(color).withAlpha(circleOpacity), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
}, |
|
|
|
polyline: { |
|
|
|
positions: circlePositions, |
|
|
|
width: circleWidth, |
|
|
|
material: Cesium.Color.fromCssColorString(circleBorderColor), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.radius = radius |
|
|
|
entityData.center = center |
|
|
|
entityData.points = [center.lng != null && center.lat != null ? center : this.cartesianToLatLng(circleCenter)] |
|
|
|
entityData.opacity = circleOpacity |
|
|
|
entityData.width = circleWidth |
|
|
|
entityData.borderColor = circleBorderColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'powerZone': { |
|
|
|
const center = data.center || {} |
|
|
|
const radius = (data.radius || 50000) * 1000 |
|
|
|
const centerPos = Cesium.Cartesian3.fromDegrees(center.lng || 0, center.lat || 0) |
|
|
|
const zoneColor = data.color || color |
|
|
|
const zoneOpacity = data.opacity != null ? data.opacity : 0.3 |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
position: centerPos, |
|
|
|
ellipse: { |
|
|
|
semiMajorAxis: radius, |
|
|
|
semiMinorAxis: radius, |
|
|
|
material: Cesium.Color.fromCssColorString(zoneColor).withAlpha(zoneOpacity), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
break |
|
|
|
} |
|
|
|
case 'text': { |
|
|
|
const pos = data.position || (data.lat != null && data.lng != null ? { lat: data.lat, lng: data.lng } : null) |
|
|
|
const text = data.text || entityData.label || '' |
|
|
|
if (!pos || (pos.lng == null && pos.lat == null)) return |
|
|
|
const lng = pos.lng != null ? pos.lng : (data.lng != null ? data.lng : 0) |
|
|
|
const lat = pos.lat != null ? pos.lat : (data.lat != null ? data.lat : 0) |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
position: Cesium.Cartesian3.fromDegrees(lng, lat), |
|
|
|
label: { |
|
|
|
text, |
|
|
|
font: (data.fontSize || 14) + 'px sans-serif', |
|
|
|
fillColor: Cesium.Color.fromCssColorString(data.color || color), |
|
|
|
outlineColor: Cesium.Color.BLACK, |
|
|
|
outlineWidth: 2, |
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -10) |
|
|
|
} |
|
|
|
}) |
|
|
|
break |
|
|
|
} |
|
|
|
case 'sector': { |
|
|
|
const center = data.center || {} |
|
|
|
const radius = data.radius || 1000 |
|
|
|
const startAngle = data.startAngle != null ? data.startAngle : 0 |
|
|
|
const endAngle = data.endAngle != null ? data.endAngle : Math.PI * 0.5 |
|
|
|
const centerPos = Cesium.Cartesian3.fromDegrees(center.lng || 0, center.lat || 0) |
|
|
|
const positions = this.generateSectorPositions(centerPos, radius, startAngle, endAngle) |
|
|
|
const sectorOpacity = data.opacity != null ? data.opacity : 0.5 |
|
|
|
const sectorWidth = data.width != null ? data.width : 2 |
|
|
|
const sectorBorderColor = data.borderColor || color |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
polygon: { |
|
|
|
hierarchy: new Cesium.PolygonHierarchy(positions), |
|
|
|
material: Cesium.Color.fromCssColorString(color).withAlpha(sectorOpacity) |
|
|
|
}, |
|
|
|
polyline: { |
|
|
|
positions: positions, |
|
|
|
width: sectorWidth, |
|
|
|
material: Cesium.Color.fromCssColorString(sectorBorderColor), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.center = center |
|
|
|
entityData.radius = radius |
|
|
|
entityData.startAngle = startAngle |
|
|
|
entityData.endAngle = endAngle |
|
|
|
entityData.positions = positions |
|
|
|
entityData.opacity = sectorOpacity |
|
|
|
entityData.width = sectorWidth |
|
|
|
entityData.borderColor = sectorBorderColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'line': |
|
|
|
case 'auxiliaryLine': { |
|
|
|
const pts = (data.points || []).map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat)) |
|
|
|
if (pts.length < 2) return |
|
|
|
const lineWidth = data.width != null ? data.width : 2 |
|
|
|
const lineColor = data.color || color |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
polyline: { |
|
|
|
positions: pts, |
|
|
|
width: lineWidth, |
|
|
|
material: Cesium.Color.fromCssColorString(lineColor), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.positions = pts |
|
|
|
entityData.points = data.points || pts.map(p => this.cartesianToLatLng(p)) |
|
|
|
entityData.width = lineWidth |
|
|
|
entityData.color = lineColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'arrow': { |
|
|
|
const arrowPts = (data.points || []).map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat)) |
|
|
|
if (arrowPts.length < 2) return |
|
|
|
const arrowWidth = data.width != null ? data.width : 12 |
|
|
|
const arrowColor = data.color || color |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
polyline: { |
|
|
|
positions: arrowPts, |
|
|
|
width: arrowWidth, |
|
|
|
material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.fromCssColorString(arrowColor)), |
|
|
|
arcType: Cesium.ArcType.NONE, |
|
|
|
widthInMeters: false |
|
|
|
} |
|
|
|
}) |
|
|
|
entityData.positions = arrowPts |
|
|
|
entityData.points = data.points || arrowPts.map(p => this.cartesianToLatLng(p)) |
|
|
|
entityData.width = arrowWidth |
|
|
|
entityData.color = arrowColor |
|
|
|
break |
|
|
|
} |
|
|
|
case 'image': { |
|
|
|
if (!data.imageUrl || data.lat == null || data.lng == null) return |
|
|
|
const imgPos = Cesium.Cartesian3.fromDegrees(data.lng, data.lat) |
|
|
|
entity = this.viewer.entities.add({ |
|
|
|
id: entityData.id, |
|
|
|
position: imgPos, |
|
|
|
billboard: { |
|
|
|
image: data.imageUrl, |
|
|
|
width: data.width != null ? data.width : 64, |
|
|
|
height: data.height != null ? data.height : 64, |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
scaleByDistance: new Cesium.NearFarScalar(200, 1.12, 1200000, 0.72), |
|
|
|
translucencyByDistance: new Cesium.NearFarScalar(300, 1.0, 600000, 0.88) |
|
|
|
} |
|
|
|
}) |
|
|
|
break |
|
|
|
} |
|
|
|
default: |
|
|
|
return |
|
|
|
} |
|
|
|
if (entity && entityData.id) { |
|
|
|
entityData.entity = entity |
|
|
|
entityData.isWhiteboard = true |
|
|
|
this.whiteboardEntityDataMap = this.whiteboardEntityDataMap || {} |
|
|
|
this.whiteboardEntityDataMap[entityData.id] = entityData |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 从房间/方案加载的 frontend_drawings JSON 恢复空域图形(先清空当前图形再导入;加载期间不触发自动保存) */ |
|
|
|
loadFrontendDrawings(data) { |
|
|
|
if (!this.viewer) return |
|
|
|
|