|
|
|
@ -68,8 +68,10 @@ |
|
|
|
@launch-missile="openLaunchMissileDialog" |
|
|
|
@adjust-airspace-position="startAirspacePositionEdit" |
|
|
|
@apply-whiteboard-platform-style="applyWhiteboardPlatformStyleFromMenu" |
|
|
|
@apply-room-platform-icon-color="applyRoomPlatformIconColorFromMenu" |
|
|
|
@close-menu="contextMenu.visible = false" |
|
|
|
@delete-box-selected-platforms="deleteBoxSelectedPlatformsFromMenu" |
|
|
|
@copy-box-selected-platforms="copyBoxSelectedPlatformsFromMenu" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 定位弹窗 --> |
|
|
|
@ -89,7 +91,7 @@ |
|
|
|
<!-- 框选平台模式:说明 + 已选列表 --> |
|
|
|
<div v-if="platformBoxSelectMode" class="platform-box-select-hud"> |
|
|
|
<div class="platform-box-select-tip"> |
|
|
|
已进入框选平台模式 |
|
|
|
{{ whiteboardMode ? '已进入白板框选模式' : '已进入框选平台模式' }} |
|
|
|
</div> |
|
|
|
<div |
|
|
|
v-if="platformBoxSelectSelectedSummary.length" |
|
|
|
@ -112,7 +114,8 @@ |
|
|
|
v-else |
|
|
|
class="platform-box-select-panel platform-box-select-panel--empty" |
|
|
|
> |
|
|
|
尚未框选到平台;在空白处拖拽矩形,框住图标即可加入列表。 |
|
|
|
<template v-if="whiteboardMode">尚未框选到白板平台;在空白处拖拽矩形框住图标即可。</template> |
|
|
|
<template v-else>尚未框选到平台;在空白处拖拽矩形,框住图标即可加入列表。</template> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-show="platformBoxSelectOverlayVisible" class="platform-box-select-layer"> |
|
|
|
@ -131,6 +134,9 @@ |
|
|
|
</template> |
|
|
|
<template v-else>点击地图放置复制航线,右键取消</template> |
|
|
|
</div> |
|
|
|
<div v-if="platformCopyPlaceState && platformCopyPlaceState.items && platformCopyPlaceState.items.length" class="copy-route-tip"> |
|
|
|
实时预览中:半透明图标为待放置的复制平台,随鼠标移动;在目标位置左键点击地图完成部署,右键取消 |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 空域位置调整提示 --> |
|
|
|
<div v-if="airspacePositionEditContext" class="copy-route-tip"> |
|
|
|
@ -659,6 +665,15 @@ export default { |
|
|
|
TRANSFORM_HANDLE_POINT_SIZE: 11, |
|
|
|
TRANSFORM_HANDLE_POINT_OUTLINE_WIDTH: 2, |
|
|
|
TRANSFORM_HANDLE_GLOW_ALPHA: 0.10, |
|
|
|
/** 2D:deg/px 超过此值开始缩小伸缩框(地图拉远) */ |
|
|
|
TRANSFORM_BOX_ZOOM_SHRINK_START_DEG_PER_PX: 4e-6, |
|
|
|
/** 2D:deg/px 达到此值缩放到最小比例 */ |
|
|
|
TRANSFORM_BOX_ZOOM_SHRINK_END_DEG_PER_PX: 5.5e-5, |
|
|
|
/** 3D:相机高度超过此值开始缩小(非 2D 时 dpp 为常数 fallback,用高度判断) */ |
|
|
|
TRANSFORM_BOX_ZOOM_3D_SHRINK_START_HEIGHT: 180000, |
|
|
|
TRANSFORM_BOX_ZOOM_3D_SHRINK_END_HEIGHT: 4500000, |
|
|
|
/** 拉远时伸缩框与手柄的最小视觉比例(相对 DESIRED_* 像素目标) */ |
|
|
|
TRANSFORM_BOX_ZOOM_MIN_VISUAL_SCALE: 0.5, |
|
|
|
// 实体点击防抖,避免双击误触导致相机高度剧烈变化 |
|
|
|
entityClickDebounceTimer: null, |
|
|
|
lastEntityClickTime: 0, |
|
|
|
@ -680,6 +695,11 @@ export default { |
|
|
|
routeSegmentPickContext: null, |
|
|
|
/** 航段移动预览元数据,与 copyPreviewWaypoints 同时使用 */ |
|
|
|
routeSegmentPlaceMeta: null, |
|
|
|
/** 框选复制平台:{ items: [{ ecefDelta, template }], previewEntityDataList };ecefDelta 相对首点地心直角坐标偏移,保持编队形状 */ |
|
|
|
platformCopyPlaceState: null, |
|
|
|
platformCopyMouseCartesian: null, |
|
|
|
/** 上一帧鼠标屏幕坐标,preRender 中拾取并更新预览(与 MOUSE_MOVE 同步) */ |
|
|
|
platformCopyPointerScreen: null, |
|
|
|
// 在航点前/后增加航点:{ routeId, waypointIndex, mode: 'before'|'after', waypoints },预览折线实体 |
|
|
|
addWaypointContext: null, |
|
|
|
addWaypointSolidEntity: null, |
|
|
|
@ -1850,6 +1870,45 @@ export default { |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板平台:color 为空表示与列表原图一致(Cesium 使用原纹理 + 白色乘色) */ |
|
|
|
isWhiteboardPlatformNativeColor(c) { |
|
|
|
return c == null || c === '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 同步白板平台 billboard:null 为原图;有颜色则转白底后乘色 |
|
|
|
*/ |
|
|
|
applyWhiteboardPlatformIconVisual(entityData) { |
|
|
|
if (!entityData || !entityData.entity || !entityData.entity.billboard) return |
|
|
|
const platform = entityData.platform || {} |
|
|
|
const iconUrl = platform.imageUrl || platform.iconUrl |
|
|
|
const imageSrc = iconUrl ? this.formatPlatformIconUrl(iconUrl) : this.getDefaultPlatformIconDataUrl() |
|
|
|
const bb = entityData.entity.billboard |
|
|
|
const native = this.isWhiteboardPlatformNativeColor(entityData.color) |
|
|
|
if (native) { |
|
|
|
entityData._whiteboardIconPrepared = true |
|
|
|
bb.image = imageSrc |
|
|
|
bb.color = Cesium.Color.WHITE |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRender) { |
|
|
|
this.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
entityData._whiteboardIconPrepared = false |
|
|
|
this.loadAndWhitenImage(imageSrc).then((whiteImage) => { |
|
|
|
if (!entityData.entity || !entityData.entity.billboard) return |
|
|
|
if (this.isWhiteboardPlatformNativeColor(entityData.color)) return |
|
|
|
entityData.entity.billboard.image = whiteImage |
|
|
|
entityData.entity.billboard.color = Cesium.Color.fromCssColorString( |
|
|
|
entityData.color || '#008aff' |
|
|
|
) |
|
|
|
entityData._whiteboardIconPrepared = true |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRender) { |
|
|
|
this.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
}).catch(() => {}) |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 生成圆角矩形 + 多色文本的 Canvas 图像 |
|
|
|
* @param {Object} options 配置项 |
|
|
|
@ -2041,6 +2100,32 @@ export default { |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 地图拉远时缩小伸缩框/手柄的屏幕占比(框按像素锚定时仍会比 footprint 显大,需随视野收小)。 |
|
|
|
* @param {{ degPerPxLng: number, degPerPxLat: number }} dpp |
|
|
|
* @returns {number} 0~1,乘在 DESIRED_BOX_HALF_PX、线宽、点大小等上 |
|
|
|
*/ |
|
|
|
getPlatformTransformBoxVisualScale(dpp) { |
|
|
|
if (!this.viewer) return 1; |
|
|
|
const minS = this.TRANSFORM_BOX_ZOOM_MIN_VISUAL_SCALE; |
|
|
|
if (this.viewer.scene.mode === Cesium.SceneMode.SCENE2D) { |
|
|
|
const degPerPx = Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const t0 = this.TRANSFORM_BOX_ZOOM_SHRINK_START_DEG_PER_PX; |
|
|
|
const t1 = this.TRANSFORM_BOX_ZOOM_SHRINK_END_DEG_PER_PX; |
|
|
|
if (degPerPx <= t0) return 1; |
|
|
|
if (degPerPx >= t1) return minS; |
|
|
|
const u = (degPerPx - t0) / (t1 - t0); |
|
|
|
return 1 - u * (1 - minS); |
|
|
|
} |
|
|
|
const h = this.viewer.camera.positionCartographic.height; |
|
|
|
const h0 = this.TRANSFORM_BOX_ZOOM_3D_SHRINK_START_HEIGHT; |
|
|
|
const h1 = this.TRANSFORM_BOX_ZOOM_3D_SHRINK_END_HEIGHT; |
|
|
|
if (h <= h0) return 1; |
|
|
|
if (h >= h1) return minS; |
|
|
|
const u = (h - h0) / (h1 - h0); |
|
|
|
return 1 - u * (1 - minS); |
|
|
|
}, |
|
|
|
|
|
|
|
/** 更新平台图标 billboard 的宽高(根据 iconScale) */ |
|
|
|
updatePlatformIconBillboardSize(entityData) { |
|
|
|
if (!entityData || !entityData.entity || !entityData.entity.billboard) return; |
|
|
|
@ -2059,17 +2144,24 @@ export default { |
|
|
|
const lng = entityData.lng; |
|
|
|
const lat = entityData.lat; |
|
|
|
const dpp = this.getDegreesPerPixelAt(lng, lat); |
|
|
|
const baseHalfDeg = this.DESIRED_BOX_HALF_PX * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const vis = this.getPlatformTransformBoxVisualScale(dpp); |
|
|
|
const baseHalfDeg = this.DESIRED_BOX_HALF_PX * vis * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const half = Math.max((entityData.iconScale || 1) * baseHalfDeg, baseHalfDeg * 0.5); |
|
|
|
const rotOffsetLat = this.DESIRED_ROTATE_OFFSET_PX * dpp.degPerPxLat; |
|
|
|
const rotOffset = Math.max(rotOffsetLat, this.PLATFORM_ROTATE_HANDLE_OFFSET_DEG); |
|
|
|
const rotOffsetLat = this.DESIRED_ROTATE_OFFSET_PX * vis * dpp.degPerPxLat; |
|
|
|
const rotOffMin = this.PLATFORM_ROTATE_HANDLE_OFFSET_DEG * vis; |
|
|
|
const rotOffset = Math.max(rotOffsetLat, rotOffMin); |
|
|
|
const rotImg = Math.max(20, Math.round(36 * vis)); |
|
|
|
const ptSize = Math.max(5, this.TRANSFORM_HANDLE_POINT_SIZE * vis); |
|
|
|
const ptOutline = Math.max(1, Math.round(this.TRANSFORM_HANDLE_POINT_OUTLINE_WIDTH * vis)); |
|
|
|
const fw = Math.max(1, this.TRANSFORM_HANDLE_FRAME_WIDTH * vis); |
|
|
|
const glowW = Math.max(1, fw + 3 * vis); |
|
|
|
const rotationHandle = this.viewer.entities.add({ |
|
|
|
id: id + '-rotate-handle', |
|
|
|
position: Cesium.Cartesian3.fromDegrees(lng, lat + rotOffset), |
|
|
|
billboard: { |
|
|
|
image: this.getRotationHandleIconDataUrl(), |
|
|
|
width: 36, |
|
|
|
height: 36, |
|
|
|
width: rotImg, |
|
|
|
height: rotImg, |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
|
@ -2086,10 +2178,10 @@ export default { |
|
|
|
id: id + '-scale-' + i, |
|
|
|
position: pos, |
|
|
|
point: { |
|
|
|
pixelSize: this.TRANSFORM_HANDLE_POINT_SIZE, |
|
|
|
pixelSize: ptSize, |
|
|
|
color: Cesium.Color.WHITE.withAlpha(0.98), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.TRANSFORM_HANDLE_PRIMARY_COLOR), |
|
|
|
outlineWidth: this.TRANSFORM_HANDLE_POINT_OUTLINE_WIDTH, |
|
|
|
outlineWidth: ptOutline, |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
|
} |
|
|
|
}) |
|
|
|
@ -2100,7 +2192,7 @@ export default { |
|
|
|
id: id + '-scale-frame-glow', |
|
|
|
polyline: { |
|
|
|
positions: linePositions, |
|
|
|
width: this.TRANSFORM_HANDLE_FRAME_WIDTH + 3, |
|
|
|
width: glowW, |
|
|
|
material: Cesium.Color.fromCssColorString(this.TRANSFORM_HANDLE_PRIMARY_COLOR).withAlpha(this.TRANSFORM_HANDLE_GLOW_ALPHA), |
|
|
|
arcType: Cesium.ArcType.NONE, |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
|
@ -2110,7 +2202,7 @@ export default { |
|
|
|
id: id + '-scale-frame', |
|
|
|
polyline: { |
|
|
|
positions: linePositions, |
|
|
|
width: this.TRANSFORM_HANDLE_FRAME_WIDTH, |
|
|
|
width: fw, |
|
|
|
material: Cesium.Color.fromCssColorString(this.TRANSFORM_HANDLE_PRIMARY_COLOR).withAlpha(0.98), |
|
|
|
arcType: Cesium.ArcType.NONE, |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
|
@ -2141,11 +2233,33 @@ export default { |
|
|
|
const lng = entityData.lng; |
|
|
|
const lat = entityData.lat; |
|
|
|
const dpp = this.getDegreesPerPixelAt(lng, lat); |
|
|
|
const baseHalfDeg = this.DESIRED_BOX_HALF_PX * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const vis = this.getPlatformTransformBoxVisualScale(dpp); |
|
|
|
const baseHalfDeg = this.DESIRED_BOX_HALF_PX * vis * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const half = Math.max((entityData.iconScale || 1) * baseHalfDeg, baseHalfDeg * 0.5); |
|
|
|
const rotOffsetLat = this.DESIRED_ROTATE_OFFSET_PX * dpp.degPerPxLat; |
|
|
|
const rotOffset = Math.max(rotOffsetLat, this.PLATFORM_ROTATE_HANDLE_OFFSET_DEG); |
|
|
|
const rotOffsetLat = this.DESIRED_ROTATE_OFFSET_PX * vis * dpp.degPerPxLat; |
|
|
|
const rotOffMin = this.PLATFORM_ROTATE_HANDLE_OFFSET_DEG * vis; |
|
|
|
const rotOffset = Math.max(rotOffsetLat, rotOffMin); |
|
|
|
entityData.transformHandles.rotation.position = Cesium.Cartesian3.fromDegrees(lng, lat + rotOffset); |
|
|
|
const rot = entityData.transformHandles.rotation; |
|
|
|
if (rot && rot.billboard) { |
|
|
|
const rotImg = Math.max(20, Math.round(36 * vis)); |
|
|
|
rot.billboard.width = rotImg; |
|
|
|
rot.billboard.height = rotImg; |
|
|
|
} |
|
|
|
const ptSize = Math.max(5, this.TRANSFORM_HANDLE_POINT_SIZE * vis); |
|
|
|
const ptOutline = Math.max(1, Math.round(this.TRANSFORM_HANDLE_POINT_OUTLINE_WIDTH * vis)); |
|
|
|
entityData.transformHandles.scale.forEach((ent) => { |
|
|
|
if (ent && ent.point) { |
|
|
|
ent.point.pixelSize = ptSize; |
|
|
|
ent.point.outlineWidth = ptOutline; |
|
|
|
} |
|
|
|
}); |
|
|
|
const fw = Math.max(1, this.TRANSFORM_HANDLE_FRAME_WIDTH * vis); |
|
|
|
const glowW = Math.max(1, fw + 3 * vis); |
|
|
|
const fr = entityData.transformHandles.frame; |
|
|
|
const fg = entityData.transformHandles.frameGlow; |
|
|
|
if (fr && fr.polyline) fr.polyline.width = fw; |
|
|
|
if (fg && fg.polyline) fg.polyline.width = glowW; |
|
|
|
const corners = [ |
|
|
|
Cesium.Cartesian3.fromDegrees(lng + half, lat + half), |
|
|
|
Cesium.Cartesian3.fromDegrees(lng - half, lat + half), |
|
|
|
@ -2318,6 +2432,15 @@ export default { |
|
|
|
getPlatformStyle(query).then(res => { |
|
|
|
const style = res.data; |
|
|
|
if (!style) return; |
|
|
|
const platEd = this.allEntities.find(e => e.type === 'platformIcon' && e.id === platformIconId) |
|
|
|
if (platEd && platEd.entity && platEd.entity.billboard) { |
|
|
|
const pc = style.platformColor != null && String(style.platformColor).trim() !== '' |
|
|
|
? String(style.platformColor).trim() |
|
|
|
: null |
|
|
|
platEd.color = pc |
|
|
|
platEd._whiteboardIconPrepared = false |
|
|
|
this.applyWhiteboardPlatformIconVisual(platEd) |
|
|
|
} |
|
|
|
const normalized = this.normalizeZonesFromStyle(style) |
|
|
|
const detectionZones = normalized.detectionZones || [] |
|
|
|
const powerZones = normalized.powerZones || [] |
|
|
|
@ -5135,6 +5258,56 @@ export default { |
|
|
|
this.lastClickWasDrag = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
// 框选复制平台:左键在地图确认放置 |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) { |
|
|
|
const placePosition = this.pickMapPlaceCartesian(click.position) |
|
|
|
if (placePosition) { |
|
|
|
const isWbCopy = !!this.platformCopyPlaceState.isWhiteboardCopy |
|
|
|
const { items } = this.platformCopyPlaceState |
|
|
|
let platforms |
|
|
|
if (isWbCopy) { |
|
|
|
platforms = items.map((it) => { |
|
|
|
const pos = Cesium.Cartesian3.add(placePosition, it.ecefDelta, new Cesium.Cartesian3()) |
|
|
|
const ll = this.cartesianToLatLng(pos) |
|
|
|
const t = it.template |
|
|
|
return { |
|
|
|
platformId: t.platformId, |
|
|
|
platform: t.platform || {}, |
|
|
|
platformName: t.platformName, |
|
|
|
label: t.label, |
|
|
|
heading: t.heading != null ? t.heading : 0, |
|
|
|
iconScale: t.iconScale != null ? t.iconScale : 1.5, |
|
|
|
color: |
|
|
|
t.color != null && String(t.color).trim() !== '' |
|
|
|
? t.color |
|
|
|
: null, |
|
|
|
lat: ll.lat, |
|
|
|
lng: ll.lng |
|
|
|
} |
|
|
|
}) |
|
|
|
} else { |
|
|
|
platforms = items.map((it) => { |
|
|
|
const pos = Cesium.Cartesian3.add(placePosition, it.ecefDelta, new Cesium.Cartesian3()) |
|
|
|
const ll = this.cartesianToLatLng(pos) |
|
|
|
return { |
|
|
|
...it.template, |
|
|
|
lat: ll.lat, |
|
|
|
lng: ll.lng |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
const roomId = this.roomId != null ? this.roomId : (this.$route && this.$route.query && this.$route.query.roomId) |
|
|
|
this.clearPlatformCopyPreview() |
|
|
|
if (isWbCopy) { |
|
|
|
this.$emit('whiteboard-platforms-copy-placed', { platforms }) |
|
|
|
} else { |
|
|
|
this.$emit('platform-icons-copy-placed', { roomId, platforms }) |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.$message && this.$message.warning('此处无法拾取地面坐标,请缩小地图或移动视角后再左键放置') |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
// 航段范围选取:依次拾取两个航点 |
|
|
|
if (this.routeSegmentPickContext) { |
|
|
|
const ctx = this.routeSegmentPickContext; |
|
|
|
@ -5266,7 +5439,9 @@ export default { |
|
|
|
entity = pickedObject.id; |
|
|
|
const idStr = (entity && entity.id) ? entity.id : ''; |
|
|
|
if (idStr && (idStr.endsWith('-rotate-handle') || idStr.indexOf('-scale-') !== -1)) return; |
|
|
|
const platformIconData = this.allEntities.find(e => e.type === 'platformIcon' && e.entity === entity); |
|
|
|
const platformIconData = this.allEntities.find( |
|
|
|
e => e.type === 'platformIcon' && e.entity === entity && !e.isCopyPreview |
|
|
|
); |
|
|
|
const now = Cesium.JulianDate.now(); |
|
|
|
props = entity.properties ? entity.properties.getValue(now) : null; |
|
|
|
isWaypoint = props && props.isMissionWaypoint; |
|
|
|
@ -5335,7 +5510,9 @@ export default { |
|
|
|
const entity = pickedObject.id; |
|
|
|
const idStr = (entity && entity.id) ? entity.id : ''; |
|
|
|
if (idStr && (idStr.endsWith('-rotate-handle') || idStr.indexOf('-scale-') !== -1)) return; |
|
|
|
const platformIconData = this.allEntities.find(e => e.type === 'platformIcon' && e.entity === entity); |
|
|
|
const platformIconData = this.allEntities.find( |
|
|
|
e => e.type === 'platformIcon' && e.entity === entity && !e.isCopyPreview |
|
|
|
); |
|
|
|
const now = Cesium.JulianDate.now(); |
|
|
|
const props = entity.properties ? entity.properties.getValue(now) : null; |
|
|
|
const isEntity = platformIconData || (props && (props.isMissionWaypoint || props.isMissionRouteLine)); |
|
|
|
@ -5351,7 +5528,7 @@ export default { |
|
|
|
|
|
|
|
// 航点拖拽:左键按下仅记录 pending,移动超过阈值后才真正开始拖拽(避免小范围移动带跑地图) |
|
|
|
this.handler.setInputAction((click) => { |
|
|
|
if (this.isDrawing || this.copyPreviewWaypoints || this.routeSegmentPickContext) return; |
|
|
|
if (this.isDrawing || this.copyPreviewWaypoints || this.routeSegmentPickContext || this.platformCopyPlaceState) return; |
|
|
|
const pickedObject = this.viewer.scene.pick(click.position); |
|
|
|
if (!Cesium.defined(pickedObject) || !pickedObject.id) return; |
|
|
|
const now = Cesium.JulianDate.now(); |
|
|
|
@ -5377,6 +5554,14 @@ export default { |
|
|
|
|
|
|
|
// 拖拽 / 复制预览:共用一个 MOUSE_MOVE;航点拖拽需超过阈值才生效 |
|
|
|
this.handler.setInputAction((movement) => { |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) { |
|
|
|
if (!this.platformCopyPointerScreen) { |
|
|
|
this.platformCopyPointerScreen = new Cesium.Cartesian2() |
|
|
|
} |
|
|
|
this.platformCopyPointerScreen.x = movement.endPosition.x |
|
|
|
this.platformCopyPointerScreen.y = movement.endPosition.y |
|
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
if (this.waypointDragPending) { |
|
|
|
const dx = movement.endPosition.x - this.waypointDragPending.startScreenX; |
|
|
|
const dy = movement.endPosition.y - this.waypointDragPending.startScreenY; |
|
|
|
@ -5531,9 +5716,22 @@ export default { |
|
|
|
const p = drillPicks[i] |
|
|
|
const obj = p && p.id |
|
|
|
if (!obj) continue |
|
|
|
const ed = this.allEntities.find( |
|
|
|
e => e.type === 'platformIcon' && !e.isWhiteboard && e.entity === obj |
|
|
|
) |
|
|
|
const ed = |
|
|
|
this.allEntities.find( |
|
|
|
e => |
|
|
|
e.type === 'platformIcon' && |
|
|
|
!e.isWhiteboard && |
|
|
|
!e.isCopyPreview && |
|
|
|
e.entity === obj |
|
|
|
) || |
|
|
|
Object.values(this.whiteboardEntityDataMap || {}).find( |
|
|
|
e => |
|
|
|
e && |
|
|
|
e.type === 'platformIcon' && |
|
|
|
e.isWhiteboard && |
|
|
|
!e.isCopyPreview && |
|
|
|
e.entity === obj |
|
|
|
) |
|
|
|
if (ed && multi.indexOf(ed) !== -1) { |
|
|
|
hitInSelection = ed |
|
|
|
break |
|
|
|
@ -5573,6 +5771,12 @@ export default { |
|
|
|
this.contextMenu.visible = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) { |
|
|
|
this.clearPlatformCopyPreview(); |
|
|
|
this.contextMenu.visible = false; |
|
|
|
this.$message && this.$message.info('已取消复制平台'); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 若处于“增加航点”预览,右键取消 |
|
|
|
if (this.addWaypointContext) { |
|
|
|
this.clearAddWaypointContext(); |
|
|
|
@ -5992,6 +6196,18 @@ export default { |
|
|
|
this.draggingPlatformGroup = false |
|
|
|
}, |
|
|
|
|
|
|
|
/** 框选统计用的候选平台:白板模式仅白板平台;否则仅房间地图平台 */ |
|
|
|
getPlatformBoxSelectCandidates() { |
|
|
|
if (this.whiteboardMode) { |
|
|
|
return Object.values(this.whiteboardEntityDataMap || {}).filter( |
|
|
|
e => e && e.type === 'platformIcon' && e.isWhiteboard && e.entity && !e.isCopyPreview |
|
|
|
) |
|
|
|
} |
|
|
|
return (this.allEntities || []).filter( |
|
|
|
e => e && e.type === 'platformIcon' && !e.isWhiteboard && !e.isCopyPreview && e.entity |
|
|
|
) |
|
|
|
}, |
|
|
|
|
|
|
|
_finalizePlatformBoxSelect() { |
|
|
|
if (!this.platformBoxSelectAnchor || !this.platformBoxSelectCurrent) return |
|
|
|
const ax = this.platformBoxSelectAnchor.x |
|
|
|
@ -6012,8 +6228,7 @@ export default { |
|
|
|
const now = Cesium.JulianDate.now() |
|
|
|
const scene = this.viewer.scene |
|
|
|
const next = [] |
|
|
|
for (const ed of this.allEntities) { |
|
|
|
if (ed.type !== 'platformIcon' || ed.isWhiteboard) continue |
|
|
|
for (const ed of this.getPlatformBoxSelectCandidates()) { |
|
|
|
if (!ed.entity || !ed.entity.position) continue |
|
|
|
const pos = ed.entity.position.getValue(now) |
|
|
|
if (!pos) continue |
|
|
|
@ -6027,7 +6242,11 @@ export default { |
|
|
|
this.platformMultiSelected = next |
|
|
|
this.applyPlatformMultiSelectHighlight(next) |
|
|
|
if (next.length === 0) { |
|
|
|
this.$message && this.$message.info('框选范围内没有房间平台图标(仅统计地图上的独立平台图标)') |
|
|
|
this.$message && this.$message.info( |
|
|
|
this.whiteboardMode |
|
|
|
? '框选范围内没有白板平台图标' |
|
|
|
: '框选范围内没有房间平台图标(仅统计地图上的独立平台图标)' |
|
|
|
) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
@ -6036,7 +6255,7 @@ export default { |
|
|
|
this.exitPlatformBoxSelectMode() |
|
|
|
this.contextMenu.visible = false |
|
|
|
this.$emit('request-exit-platform-box-select') |
|
|
|
this.$message && this.$message.info('已退出框选平台模式') |
|
|
|
this.$message && this.$message.info('已退出框选模式') |
|
|
|
}, |
|
|
|
|
|
|
|
/** 退出框选模式时由父组件 ref 或 watch 调用:清理状态与相机锁定 */ |
|
|
|
@ -6062,6 +6281,7 @@ export default { |
|
|
|
|
|
|
|
this.platformIconHandler.setInputAction((click) => { |
|
|
|
if (this.isDrawing || this.rotatingPlatformIcon) return; |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) return; |
|
|
|
const picked = this.viewer.scene.pick(click.position); |
|
|
|
this.clickedOnEmpty = !Cesium.defined(picked) || !picked.id; |
|
|
|
let idStr = ''; |
|
|
|
@ -6094,13 +6314,17 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.platformBoxSelectMode && !this.rotatingPlatformIcon) { |
|
|
|
const isRoomPlatform = entityData && entityData.type === 'platformIcon' && !entityData.isWhiteboard; |
|
|
|
if (isRoomPlatform && this.platformMultiSelected.length > 0 && this.platformMultiSelected.indexOf(entityData) !== -1) { |
|
|
|
const isBoxTarget = |
|
|
|
entityData && |
|
|
|
entityData.type === 'platformIcon' && |
|
|
|
!entityData.isCopyPreview && |
|
|
|
(this.whiteboardMode ? !!entityData.isWhiteboard : !entityData.isWhiteboard) |
|
|
|
if (isBoxTarget && this.platformMultiSelected.length > 0 && this.platformMultiSelected.indexOf(entityData) !== -1) { |
|
|
|
this._startPlatformGroupDrag(click.position); |
|
|
|
this.clickedOnEmpty = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!isRoomPlatform) { |
|
|
|
if (!isBoxTarget) { |
|
|
|
this._startPlatformBoxSelect(click.position); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -6114,6 +6338,7 @@ export default { |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN); |
|
|
|
|
|
|
|
this.platformIconHandler.setInputAction((movement) => { |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) return; |
|
|
|
if (this.platformBoxSelectAnchor && this.platformBoxSelectMode) { |
|
|
|
this.platformBoxSelectCurrent = { x: movement.endPosition.x, y: movement.endPosition.y }; |
|
|
|
const ax = this.platformBoxSelectAnchor.x; |
|
|
|
@ -6156,7 +6381,7 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.draggingPlatformGroup && this.groupDragPickStart && this.groupDragEntityCartesians) { |
|
|
|
const cur = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid); |
|
|
|
const cur = this.pickMapPlaceCartesian(movement.endPosition) |
|
|
|
if (cur) { |
|
|
|
if (!this._cartScratchDelta) this._cartScratchDelta = new Cesium.Cartesian3(); |
|
|
|
Cesium.Cartesian3.subtract(cur, this.groupDragPickStart, this._cartScratchDelta); |
|
|
|
@ -6203,7 +6428,8 @@ export default { |
|
|
|
const lng = ed.lng; |
|
|
|
const lat = ed.lat; |
|
|
|
const dpp = this.getDegreesPerPixelAt(lng, lat); |
|
|
|
const baseHalf = this.DESIRED_BOX_HALF_PX * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
const vis = this.getPlatformTransformBoxVisualScale(dpp); |
|
|
|
const baseHalf = this.DESIRED_BOX_HALF_PX * vis * Math.min(dpp.degPerPxLng, dpp.degPerPxLat); |
|
|
|
let newHalfDeg; |
|
|
|
if (cornerIndex === 0) newHalfDeg = Math.min(newLng - lng, newLat - lat); |
|
|
|
else if (cornerIndex === 1) newHalfDeg = Math.min(lng - newLng, newLat - lat); |
|
|
|
@ -6261,6 +6487,7 @@ export default { |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
this.platformIconHandler.setInputAction(() => { |
|
|
|
if (this.platformCopyPlaceState && this.platformCopyPlaceState.items && this.platformCopyPlaceState.items.length) return; |
|
|
|
if (this.platformBoxSelectAnchor) { |
|
|
|
if (this.platformBoxSelectDragging) { |
|
|
|
this._finalizePlatformBoxSelect(); |
|
|
|
@ -6277,7 +6504,20 @@ export default { |
|
|
|
} |
|
|
|
if (this.pendingGroupDrag || this.draggingPlatformGroup) { |
|
|
|
if (this.draggingPlatformGroup && this.platformMultiSelected.length) { |
|
|
|
this.$emit('platform-icons-batch-updated', [...this.platformMultiSelected]); |
|
|
|
const list = [...this.platformMultiSelected] |
|
|
|
if (this.whiteboardMode && list.some(e => e && e.isWhiteboard)) { |
|
|
|
this.$emit( |
|
|
|
'whiteboard-platforms-batch-updated', |
|
|
|
list.filter(e => e && e.isWhiteboard).map(ed => ({ |
|
|
|
id: ed.id, |
|
|
|
lat: ed.lat, |
|
|
|
lng: ed.lng, |
|
|
|
heading: ed.heading != null ? ed.heading : 0 |
|
|
|
})) |
|
|
|
) |
|
|
|
} else { |
|
|
|
this.$emit('platform-icons-batch-updated', list) |
|
|
|
} |
|
|
|
} |
|
|
|
this.pendingGroupDrag = false; |
|
|
|
this.draggingPlatformGroup = false; |
|
|
|
@ -8369,6 +8609,28 @@ export default { |
|
|
|
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid) |
|
|
|
return cartesian |
|
|
|
}, |
|
|
|
/** 放置/拖拽用:椭球拾取失败时尝试深度拾取与 globe 射线求交,便于 2D 与宽视角下复制预览跟随鼠标 */ |
|
|
|
pickMapPlaceCartesian(pixelPosition) { |
|
|
|
if (!this.viewer || !pixelPosition) return undefined |
|
|
|
const scene = this.viewer.scene |
|
|
|
const ellipsoid = scene.globe.ellipsoid |
|
|
|
let c = this.viewer.camera.pickEllipsoid(pixelPosition, ellipsoid) |
|
|
|
if (c) return c |
|
|
|
if (scene.pickPositionSupported) { |
|
|
|
try { |
|
|
|
const p = scene.pickPosition(pixelPosition) |
|
|
|
if (p) return p |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
const ray = this.viewer.camera.getPickRay(pixelPosition) |
|
|
|
if (ray && scene.globe) { |
|
|
|
try { |
|
|
|
const g = scene.globe.pick(ray, scene) |
|
|
|
if (g) return g |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
return undefined |
|
|
|
}, |
|
|
|
cartesianToLatLng(cartesian) { |
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) |
|
|
|
return { |
|
|
|
@ -8748,9 +9010,14 @@ export default { |
|
|
|
if (data.iconScale != null) { |
|
|
|
this.updatePlatformIconBillboardSize(data) |
|
|
|
} |
|
|
|
if (data.color) { |
|
|
|
entity.billboard.color = Cesium.Color.fromCssColorString(data.color) |
|
|
|
data._whiteboardIconPrepared = false |
|
|
|
if (data.isWhiteboard) { |
|
|
|
const plat = data.platform || {} |
|
|
|
const iu = plat.imageUrl || plat.iconUrl || '' |
|
|
|
const cc = data.color != null && String(data.color).trim() !== '' ? data.color : null |
|
|
|
data._wbVisualSignature = `${cc == null ? '' : cc}\0${iu}` |
|
|
|
} |
|
|
|
this.applyWhiteboardPlatformIconVisual(data) |
|
|
|
} |
|
|
|
break |
|
|
|
case 'text': { |
|
|
|
@ -8988,6 +9255,254 @@ export default { |
|
|
|
this.copyPreviewMouseCartesian = null; |
|
|
|
this.routeSegmentPlaceMeta = null; |
|
|
|
}, |
|
|
|
|
|
|
|
_buildWhiteboardCopyTemplate(ed) { |
|
|
|
if (!ed) return null |
|
|
|
const plat = ed.platform || {} |
|
|
|
const platformId = ed.platformId != null ? ed.platformId : plat.id |
|
|
|
if (platformId == null) return null |
|
|
|
return { |
|
|
|
platformId, |
|
|
|
platform: { ...plat, id: platformId }, |
|
|
|
platformName: ed.platformName || plat.name || '', |
|
|
|
label: ed.label || plat.name || '平台', |
|
|
|
iconUrl: ed.imageUrl || plat.imageUrl || plat.iconUrl || '', |
|
|
|
heading: ed.heading != null ? ed.heading : 0, |
|
|
|
iconScale: ed.iconScale != null ? ed.iconScale : 1.5, |
|
|
|
color: |
|
|
|
ed.color != null && String(ed.color).trim() !== '' ? ed.color : null |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
_buildPlatformCopyTemplate(ed) { |
|
|
|
if (!ed) return null |
|
|
|
const plat = ed.platform || {} |
|
|
|
const cache = this.platformIconZoneStyles && this.platformIconZoneStyles[ed.id] |
|
|
|
let detectionZones = [] |
|
|
|
let powerZones = [] |
|
|
|
if (cache) { |
|
|
|
try { |
|
|
|
detectionZones = JSON.parse(JSON.stringify(cache.detectionZones || [])) |
|
|
|
powerZones = JSON.parse(JSON.stringify(cache.powerZones || [])) |
|
|
|
} catch (e) { |
|
|
|
detectionZones = [...(cache.detectionZones || [])] |
|
|
|
powerZones = [...(cache.powerZones || [])] |
|
|
|
} |
|
|
|
} |
|
|
|
return { |
|
|
|
platformId: ed.platformId != null ? ed.platformId : plat.id, |
|
|
|
platformName: plat.name || ed.name || '', |
|
|
|
platformType: plat.type || '', |
|
|
|
iconUrl: ed.imageUrl || plat.imageUrl || plat.iconUrl || '', |
|
|
|
heading: ed.heading != null ? ed.heading : 0, |
|
|
|
iconScale: ed.iconScale != null ? ed.iconScale : 1, |
|
|
|
detectionZones, |
|
|
|
powerZones |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
updatePlatformCopyPreviewPositions() { |
|
|
|
if (!this.platformCopyPlaceState || !this.platformCopyMouseCartesian || !this.viewer) return |
|
|
|
const { items, previewEntityDataList } = this.platformCopyPlaceState |
|
|
|
if (!items || !previewEntityDataList || items.length !== previewEntityDataList.length) return |
|
|
|
const anchor = this.platformCopyMouseCartesian |
|
|
|
for (let i = 0; i < items.length; i++) { |
|
|
|
const ed = previewEntityDataList[i] |
|
|
|
const it = items[i] |
|
|
|
if (!ed || !it || !ed.entity || !it.ecefDelta) continue |
|
|
|
const newPos = Cesium.Cartesian3.add(anchor, it.ecefDelta, new Cesium.Cartesian3()) |
|
|
|
const ll = this.cartesianToLatLng(newPos) |
|
|
|
ed.lat = ll.lat |
|
|
|
ed.lng = ll.lng |
|
|
|
ed.entity.position = newPos |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
_bindPlatformCopyPreRender() { |
|
|
|
if (this._platformCopyPreRenderListener || !this.viewer) return |
|
|
|
this._platformCopyPreRenderListener = () => { |
|
|
|
if (!this.platformCopyPlaceState || !this.platformCopyPointerScreen || !this.viewer) return |
|
|
|
const c = this.pickMapPlaceCartesian(this.platformCopyPointerScreen) |
|
|
|
if (c) { |
|
|
|
this.platformCopyMouseCartesian = c |
|
|
|
this.updatePlatformCopyPreviewPositions() |
|
|
|
} |
|
|
|
} |
|
|
|
this.viewer.scene.preRender.addEventListener(this._platformCopyPreRenderListener) |
|
|
|
}, |
|
|
|
|
|
|
|
_unbindPlatformCopyPreRender() { |
|
|
|
if (!this._platformCopyPreRenderListener) return |
|
|
|
if (this.viewer && this.viewer.scene) { |
|
|
|
try { |
|
|
|
this.viewer.scene.preRender.removeEventListener(this._platformCopyPreRenderListener) |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
this._platformCopyPreRenderListener = null |
|
|
|
}, |
|
|
|
|
|
|
|
clearPlatformCopyPreview() { |
|
|
|
this._unbindPlatformCopyPreRender() |
|
|
|
this.platformCopyPointerScreen = null |
|
|
|
if (!this.platformCopyPlaceState || !this.viewer) { |
|
|
|
this.platformCopyPlaceState = null |
|
|
|
this.platformCopyMouseCartesian = null |
|
|
|
return |
|
|
|
} |
|
|
|
const list = this.platformCopyPlaceState.previewEntityDataList || [] |
|
|
|
for (const ed of list) { |
|
|
|
this.removeTransformHandles(ed) |
|
|
|
if (this.selectedPlatformIcon === ed) this.selectedPlatformIcon = null |
|
|
|
if (ed.entity) this.viewer.entities.remove(ed.entity) |
|
|
|
const idx = this.allEntities.indexOf(ed) |
|
|
|
if (idx !== -1) this.allEntities.splice(idx, 1) |
|
|
|
} |
|
|
|
this.platformCopyPlaceState = null |
|
|
|
this.platformCopyMouseCartesian = null |
|
|
|
}, |
|
|
|
|
|
|
|
/** 框选右键菜单:复制为新实例并进入跟随鼠标摆放(房间平台 / 白板平台) */ |
|
|
|
copyBoxSelectedPlatformsFromMenu() { |
|
|
|
this.contextMenu.visible = false |
|
|
|
const raw = [...(this.platformMultiSelected || [])] |
|
|
|
const isWb = !!this.whiteboardMode |
|
|
|
const list = raw.filter((e) => { |
|
|
|
if (!e || e.type !== 'platformIcon' || e.isCopyPreview) return false |
|
|
|
if (isWb) return !!e.isWhiteboard |
|
|
|
return !e.isWhiteboard && e.serverId != null |
|
|
|
}) |
|
|
|
if (!list.length) { |
|
|
|
this.$message && this.$message.warning( |
|
|
|
isWb ? '请先框选白板上的平台图标后再复制' : '请先框选已保存到房间的地图平台图标后再复制' |
|
|
|
) |
|
|
|
return |
|
|
|
} |
|
|
|
if (!this.viewer) return |
|
|
|
this.clearPlatformCopyPreview() |
|
|
|
const refLat = list[0].lat |
|
|
|
const refLng = list[0].lng |
|
|
|
const roomId = !isWb ? list[0].roomId : null |
|
|
|
const now = Cesium.JulianDate.now() |
|
|
|
const refCart = Cesium.Cartesian3.fromDegrees(refLng, refLat) |
|
|
|
const items = [] |
|
|
|
const previewEntityDataList = [] |
|
|
|
const tseq = Date.now() |
|
|
|
let idx = 0 |
|
|
|
for (const ed of list) { |
|
|
|
const tpl = isWb ? this._buildWhiteboardCopyTemplate(ed) : this._buildPlatformCopyTemplate(ed) |
|
|
|
if (!tpl || tpl.platformId == null) continue |
|
|
|
let p = null |
|
|
|
if (ed.entity && ed.entity.position) { |
|
|
|
try { |
|
|
|
p = ed.entity.position.getValue(now) |
|
|
|
} catch (err) {} |
|
|
|
} |
|
|
|
if (!p) p = Cesium.Cartesian3.fromDegrees(ed.lng, ed.lat) |
|
|
|
const ecefDelta = Cesium.Cartesian3.subtract(p, refCart, new Cesium.Cartesian3()) |
|
|
|
items.push({ ecefDelta, template: tpl }) |
|
|
|
const platform = isWb |
|
|
|
? tpl.platform |
|
|
|
: { |
|
|
|
id: tpl.platformId, |
|
|
|
name: tpl.platformName, |
|
|
|
type: tpl.platformType, |
|
|
|
imageUrl: tpl.iconUrl, |
|
|
|
iconUrl: tpl.iconUrl |
|
|
|
} |
|
|
|
const iconUrl = |
|
|
|
tpl.iconUrl || (platform && (platform.imageUrl || platform.iconUrl || '')) |
|
|
|
const imageSrc = iconUrl ? this.formatPlatformIconUrl(iconUrl) : this.getDefaultPlatformIconDataUrl() |
|
|
|
this.entityCounter++ |
|
|
|
const id = `platformIcon_copypreview_${tseq}_${idx++}` |
|
|
|
const headingDeg = tpl.heading != null ? tpl.heading : 0 |
|
|
|
const rotation = Math.PI / 2 - (headingDeg * Math.PI / 180) |
|
|
|
const iconScale = Math.max( |
|
|
|
0.2, |
|
|
|
Math.min(3, tpl.iconScale != null ? tpl.iconScale : (isWb ? 1.5 : 1)) |
|
|
|
) |
|
|
|
const size = this.PLATFORM_ICON_BASE_SIZE * iconScale |
|
|
|
const cartesian = Cesium.Cartesian3.add(refCart, ecefDelta, new Cesium.Cartesian3()) |
|
|
|
const ll0 = this.cartesianToLatLng(cartesian) |
|
|
|
const lat = ll0.lat |
|
|
|
const lng = ll0.lng |
|
|
|
const platName = |
|
|
|
(platform && (platform.name || platform.platformName)) || |
|
|
|
tpl.platformName || |
|
|
|
'平台' |
|
|
|
const previewLabel = `[复制] ${platName}` |
|
|
|
const wbNative = isWb && this.isWhiteboardPlatformNativeColor(tpl.color) |
|
|
|
const billboardColor = isWb |
|
|
|
? (wbNative |
|
|
|
? Cesium.Color.WHITE.withAlpha(0.88) |
|
|
|
: Cesium.Color.fromCssColorString(tpl.color || '#008aff').withAlpha(0.88)) |
|
|
|
: Cesium.Color.WHITE.withAlpha(0.88) |
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id, |
|
|
|
name: platName, |
|
|
|
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), |
|
|
|
color: billboardColor |
|
|
|
}, |
|
|
|
label: { |
|
|
|
text: previewLabel, |
|
|
|
font: '13px sans-serif', |
|
|
|
fillColor: Cesium.Color.fromCssColorString('#0d47a1'), |
|
|
|
outlineColor: Cesium.Color.WHITE, |
|
|
|
outlineWidth: 3, |
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
pixelOffset: new Cesium.Cartesian2(0, 36), |
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY, |
|
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 1.0, 200000, 0.45) |
|
|
|
} |
|
|
|
}) |
|
|
|
const entityData = { |
|
|
|
id, |
|
|
|
type: 'platformIcon', |
|
|
|
isCopyPreview: true, |
|
|
|
platformId: tpl.platformId, |
|
|
|
platform, |
|
|
|
name: platName, |
|
|
|
heading: headingDeg, |
|
|
|
lat, |
|
|
|
lng, |
|
|
|
entity, |
|
|
|
imageUrl: iconUrl, |
|
|
|
label: tpl.label || platName, |
|
|
|
iconScale: tpl.iconScale != null ? tpl.iconScale : (isWb ? 1.5 : 1), |
|
|
|
transformHandles: null, |
|
|
|
serverId: null, |
|
|
|
roomId: roomId || undefined |
|
|
|
} |
|
|
|
this.allEntities.push(entityData) |
|
|
|
previewEntityDataList.push(entityData) |
|
|
|
} |
|
|
|
if (!previewEntityDataList.length) { |
|
|
|
this.$message && this.$message.warning('无法复制:缺少平台类型信息') |
|
|
|
return |
|
|
|
} |
|
|
|
this.platformCopyPlaceState = { items, previewEntityDataList, isWhiteboardCopy: isWb } |
|
|
|
const canvas = this.viewer.scene.canvas |
|
|
|
this.platformCopyPointerScreen = new Cesium.Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2) |
|
|
|
this.platformCopyMouseCartesian = |
|
|
|
this.pickMapPlaceCartesian(this.platformCopyPointerScreen) || |
|
|
|
Cesium.Cartesian3.fromDegrees(refLng, refLat) |
|
|
|
this._bindPlatformCopyPreRender() |
|
|
|
this.updatePlatformCopyPreviewPositions() |
|
|
|
this.exitPlatformBoxSelectMode() |
|
|
|
this.$emit('request-exit-platform-box-select') |
|
|
|
this.$message && this.$message.info('预览随鼠标移动,左键点击地图放置') |
|
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender() |
|
|
|
}, |
|
|
|
/** 右键菜单:打开航点编辑(支持 dbId 或 waypointIndex) */ |
|
|
|
handleContextMenuOpenWaypointDialog(dbId, routeId, waypointIndex) { |
|
|
|
this.contextMenu.visible = false; |
|
|
|
@ -11208,8 +11723,18 @@ export default { |
|
|
|
type: 'warning' |
|
|
|
}) |
|
|
|
.then(() => { |
|
|
|
const ids = list.map(ed => ed.id).filter(Boolean) |
|
|
|
ids.forEach(id => this.removeEntity(id)) |
|
|
|
list.forEach((ed) => { |
|
|
|
if (!ed || !ed.id) return |
|
|
|
if (ed.isWhiteboard) { |
|
|
|
this.$emit('whiteboard-entity-deleted', ed) |
|
|
|
if (ed.entity) this.viewer.entities.remove(ed.entity) |
|
|
|
if (this.whiteboardEntityDataMap && this.whiteboardEntityDataMap[ed.id]) { |
|
|
|
delete this.whiteboardEntityDataMap[ed.id] |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.removeEntity(ed.id) |
|
|
|
} |
|
|
|
}) |
|
|
|
this.platformMultiSelected = [] |
|
|
|
this.$message && this.$message.success(`已删除 ${n} 个平台图标`) |
|
|
|
}) |
|
|
|
@ -11623,7 +12148,10 @@ export default { |
|
|
|
heading: entityData.heading != null ? entityData.heading : 0, |
|
|
|
iconScale: entityData.iconScale != null ? entityData.iconScale : 1.5, |
|
|
|
label: entityData.label || '', |
|
|
|
color: entityData.color || '#ffffff' |
|
|
|
color: |
|
|
|
entityData.color != null && String(entityData.color).trim() !== '' |
|
|
|
? entityData.color |
|
|
|
: null |
|
|
|
}) |
|
|
|
} else if (this.getDrawingEntityTypes().includes(entityData.type)) { |
|
|
|
this.notifyDrawingEntitiesChanged() |
|
|
|
@ -11675,7 +12203,16 @@ export default { |
|
|
|
this.contextMenu.visible = false |
|
|
|
return |
|
|
|
} |
|
|
|
const color = (payload && payload.color) || ed.color || '#008aff' |
|
|
|
let color |
|
|
|
if (payload && Object.prototype.hasOwnProperty.call(payload, 'color')) { |
|
|
|
color = |
|
|
|
payload.color != null && String(payload.color).trim() !== '' |
|
|
|
? payload.color |
|
|
|
: null |
|
|
|
} else { |
|
|
|
color = |
|
|
|
ed.color != null && String(ed.color).trim() !== '' ? ed.color : null |
|
|
|
} |
|
|
|
const rawScale = payload && payload.iconScale != null ? Number(payload.iconScale) : (ed.iconScale != null ? Number(ed.iconScale) : 1.5) |
|
|
|
const iconScale = Math.max(0.2, Math.min(3, Number.isFinite(rawScale) ? rawScale : 1.5)) |
|
|
|
ed.color = color |
|
|
|
@ -11711,6 +12248,46 @@ export default { |
|
|
|
this.$message && this.$message.success('白板平台样式已更新') |
|
|
|
}, |
|
|
|
|
|
|
|
/** 非白板:房间地图独立平台右键「图标颜色」,与白板相同着色逻辑;持久化走 platformStyle.platformColor(routeId=0) */ |
|
|
|
applyRoomPlatformIconColorFromMenu(payload) { |
|
|
|
const targetId = payload && payload.id |
|
|
|
const ed = this.allEntities.find( |
|
|
|
e => e.type === 'platformIcon' && !e.isWhiteboard && !e.isCopyPreview && e.id === targetId |
|
|
|
) |
|
|
|
this.contextMenu.visible = false |
|
|
|
if (!ed || !ed.entity || !ed.entity.billboard) { |
|
|
|
return |
|
|
|
} |
|
|
|
let color = null |
|
|
|
if (payload && Object.prototype.hasOwnProperty.call(payload, 'color')) { |
|
|
|
color = |
|
|
|
payload.color != null && String(payload.color).trim() !== '' |
|
|
|
? String(payload.color).trim() |
|
|
|
: null |
|
|
|
} |
|
|
|
ed.color = color |
|
|
|
ed._whiteboardIconPrepared = false |
|
|
|
this.applyWhiteboardPlatformIconVisual(ed) |
|
|
|
if (this.selectedPlatformIcon && this.selectedPlatformIcon.id === ed.id && ed.transformHandles) { |
|
|
|
this.updateTransformHandlePositions(ed) |
|
|
|
} |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRender) { |
|
|
|
this.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
if (!ed.serverId || !ed.roomId) { |
|
|
|
this.$message && |
|
|
|
this.$message.warning('图标颜色已预览,保存到房间后可永久保存着色(请等待拖拽放置完成后再试)') |
|
|
|
return |
|
|
|
} |
|
|
|
this.$emit('room-platform-icon-style-updated', { |
|
|
|
serverId: ed.serverId, |
|
|
|
roomId: ed.roomId, |
|
|
|
platformId: ed.platformId, |
|
|
|
platformColor: color |
|
|
|
}) |
|
|
|
this.$message && this.$message.success('图标颜色已更新') |
|
|
|
}, |
|
|
|
|
|
|
|
/** 右键「在此之前插入航线」:先画平台前的航点,右键结束时将平台作为最后一站 */ |
|
|
|
handleStartRouteBeforePlatform() { |
|
|
|
const entityData = this.contextMenu.entityData |
|
|
|
@ -12362,36 +12939,25 @@ export default { |
|
|
|
existing.lng = ed.lng |
|
|
|
existing.heading = ed.heading != null ? ed.heading : 0 |
|
|
|
existing.iconScale = ed.iconScale != null ? ed.iconScale : 1.5 |
|
|
|
existing.color = ed.color || existing.color || '#008aff' |
|
|
|
if (Object.prototype.hasOwnProperty.call(ed, 'color')) { |
|
|
|
existing.color = |
|
|
|
ed.color != null && String(ed.color).trim() !== '' ? ed.color : null |
|
|
|
} |
|
|
|
if (ed.platform) existing.platform = ed.platform |
|
|
|
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.entity.billboard) { |
|
|
|
existing.entity.billboard.color = Cesium.Color.fromCssColorString(existing.color) |
|
|
|
const plat = existing.platform || {} |
|
|
|
const iconUrl = plat.imageUrl || plat.iconUrl || '' |
|
|
|
const visSig = `${existing.color == null ? '' : existing.color}\0${iconUrl}` |
|
|
|
if (existing._wbVisualSignature !== visSig) { |
|
|
|
existing._wbVisualSignature = visSig |
|
|
|
existing._whiteboardIconPrepared = false |
|
|
|
this.applyWhiteboardPlatformIconVisual(existing) |
|
|
|
} |
|
|
|
this.ensureWhiteboardPlatformColorableImage(existing) |
|
|
|
if (existing.transformHandles) this.updateTransformHandlePositions(existing) |
|
|
|
if (this.viewer.scene && this.viewer.scene.requestRender) this.viewer.scene.requestRender() |
|
|
|
}, |
|
|
|
/** 白板平台图标转白底后再着色,避免黑色图标乘色后看不出颜色变化 */ |
|
|
|
ensureWhiteboardPlatformColorableImage(entityData) { |
|
|
|
if (!entityData || !entityData.entity || !entityData.entity.billboard) return |
|
|
|
if (entityData._whiteboardIconPrepared) return |
|
|
|
const platform = entityData.platform || {} |
|
|
|
const iconUrl = platform.imageUrl || platform.iconUrl |
|
|
|
const imageSrc = iconUrl ? this.formatPlatformIconUrl(iconUrl) : this.getDefaultPlatformIconDataUrl() |
|
|
|
this.loadAndWhitenImage(imageSrc).then((whiteImage) => { |
|
|
|
if (!entityData || !entityData.entity || !entityData.entity.billboard) return |
|
|
|
entityData.entity.billboard.image = whiteImage |
|
|
|
if (entityData.color) { |
|
|
|
entityData.entity.billboard.color = Cesium.Color.fromCssColorString(entityData.color) |
|
|
|
} |
|
|
|
entityData._whiteboardIconPrepared = true |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRender) { |
|
|
|
this.viewer.scene.requestRender() |
|
|
|
} |
|
|
|
}).catch(() => {}) |
|
|
|
}, |
|
|
|
/** 白板平台图标:添加 billboard,并存入 whiteboardEntityDataMap 以支持拖拽/旋转 */ |
|
|
|
addWhiteboardPlatformIcon(ed) { |
|
|
|
const platform = ed.platform || {} |
|
|
|
@ -12401,10 +12967,12 @@ export default { |
|
|
|
const lat = ed.lat |
|
|
|
const lng = ed.lng |
|
|
|
const heading = ed.heading != null ? ed.heading : 0 |
|
|
|
const color = ed.color || '#008aff' |
|
|
|
const color = |
|
|
|
ed.color != null && String(ed.color).trim() !== '' ? ed.color : null |
|
|
|
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 native = this.isWhiteboardPlatformNativeColor(color) |
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id, |
|
|
|
name: ed.label || platform.name || '平台', |
|
|
|
@ -12413,7 +12981,9 @@ export default { |
|
|
|
image: imageSrc, |
|
|
|
width: size, |
|
|
|
height: size, |
|
|
|
color: Cesium.Color.fromCssColorString(color), |
|
|
|
color: native |
|
|
|
? Cesium.Color.WHITE |
|
|
|
: Cesium.Color.fromCssColorString(color || '#008aff'), |
|
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, |
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
|
rotation, |
|
|
|
@ -12437,7 +13007,10 @@ export default { |
|
|
|
} |
|
|
|
this.whiteboardEntityDataMap = this.whiteboardEntityDataMap || {} |
|
|
|
this.whiteboardEntityDataMap[id] = entityData |
|
|
|
this.ensureWhiteboardPlatformColorableImage(entityData) |
|
|
|
entityData._whiteboardIconPrepared = false |
|
|
|
entityData._wbVisualSignature = null |
|
|
|
this.applyWhiteboardPlatformIconVisual(entityData) |
|
|
|
entityData._wbVisualSignature = `${color == null ? '' : color}\0${iconUrl}` |
|
|
|
}, |
|
|
|
/** 白板空域/图形实体:复用 importEntity 逻辑但不加入 allEntities */ |
|
|
|
addWhiteboardDrawingEntity(ed) { |
|
|
|
@ -13579,6 +14152,7 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
destroyViewer() { |
|
|
|
this.clearPlatformCopyPreview() |
|
|
|
this.clearAirspaceHighlight(); |
|
|
|
this.stopDrawing() |
|
|
|
this.clearAll(false) |
|
|
|
|