|
|
|
@ -42,6 +42,7 @@ |
|
|
|
@toggle-route-lock="toggleRouteLock" |
|
|
|
@start-route-before-platform="handleStartRouteBeforePlatform" |
|
|
|
@start-route-after-platform="handleStartRouteAfterPlatform" |
|
|
|
@copy-route="handleCopyRouteFromMenu" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 定位弹窗 --> |
|
|
|
@ -58,6 +59,11 @@ |
|
|
|
{{ platformIconRotateTip }} |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 航线复制预览提示 --> |
|
|
|
<div v-if="copyPreviewWaypoints && copyPreviewWaypoints.length >= 2" class="copy-route-tip"> |
|
|
|
点击地图放置复制航线,右键取消 |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 半径输入弹窗 --> |
|
|
|
<radius-dialog |
|
|
|
:visible="radiusDialogVisible" |
|
|
|
@ -123,6 +129,11 @@ export default { |
|
|
|
coordinateFormat: { |
|
|
|
type: String, |
|
|
|
default: 'dms' // 'decimal' 或 'dms' |
|
|
|
}, |
|
|
|
/** 航线上锁状态(由父组件维护,与右侧列表锁图标同步) */ |
|
|
|
routeLocked: { |
|
|
|
type: Object, |
|
|
|
default: () => ({}) |
|
|
|
} |
|
|
|
}, |
|
|
|
watch: { |
|
|
|
@ -186,8 +197,7 @@ export default { |
|
|
|
}, |
|
|
|
// 航线飞机标牌显示状态:routeId -> true 显示 / false 隐藏,不设则默认显示 |
|
|
|
routeLabelVisible: {}, |
|
|
|
// 航线上锁状态:routeId -> true 上锁(不可编辑)/ false 或未设 可编辑 |
|
|
|
routeLocked: {}, |
|
|
|
// 航线上锁状态由父组件通过 prop routeLocked 传入,与右侧列表锁图标同步 |
|
|
|
// 从平台右键进入的航线绘制:{ platformInfo: { platformId, platform }, mode: 'before'|'after' } |
|
|
|
platformRouteDrawing: null, |
|
|
|
// 默认样式 |
|
|
|
@ -240,7 +250,18 @@ export default { |
|
|
|
// 实体点击防抖,避免双击误触导致相机高度剧烈变化 |
|
|
|
entityClickDebounceTimer: null, |
|
|
|
lastEntityClickTime: 0, |
|
|
|
cameraStateBeforeEntityClick: null |
|
|
|
cameraStateBeforeEntityClick: null, |
|
|
|
// 航点拖拽编辑:{ entity, routeId, dbId, originalAlt };未超过阈值前为 pending |
|
|
|
waypointDragging: null, |
|
|
|
waypointDragPending: null, |
|
|
|
WAYPOINT_DRAG_THRESHOLD_PX: 8, |
|
|
|
/** 拖拽航点前相机 enableInputs 状态,松开时恢复 */ |
|
|
|
waypointDragCameraInputsEnabled: undefined, |
|
|
|
lastClickWasDrag: false, |
|
|
|
// 航线复制预览:整条航线跟随鼠标,左键放置 |
|
|
|
copyPreviewWaypoints: null, |
|
|
|
copyPreviewEntity: null, |
|
|
|
copyPreviewMouseCartesian: null |
|
|
|
} |
|
|
|
}, |
|
|
|
components: { |
|
|
|
@ -1246,7 +1267,7 @@ export default { |
|
|
|
dashLength: dashLen |
|
|
|
}) |
|
|
|
: Cesium.Color.fromCssColorString(lineColor); |
|
|
|
// 先收集所有航点的世界坐标 |
|
|
|
// 先收集所有航点的世界坐标,并缓存航点 id 列表供拖拽时动态连线 |
|
|
|
const originalPositions = []; |
|
|
|
waypoints.forEach((wp) => { |
|
|
|
const lon = parseFloat(wp.lng); |
|
|
|
@ -1254,6 +1275,8 @@ export default { |
|
|
|
const altValue = Number(wp.alt || 5000); |
|
|
|
originalPositions.push(Cesium.Cartesian3.fromDegrees(lon, lat, altValue)); |
|
|
|
}); |
|
|
|
if (!this._routeWaypointIdsByRoute) this._routeWaypointIdsByRoute = {}; |
|
|
|
this._routeWaypointIdsByRoute[routeId] = waypoints.map((wp) => wp.id); |
|
|
|
// 判断航点 i 是否为“转弯半径”航点(将用弧线两端两个点替代中心点) |
|
|
|
const isTurnWaypointWithArc = (i) => { |
|
|
|
if (i < 1 || i >= waypoints.length - 1) return false; |
|
|
|
@ -1291,11 +1314,11 @@ export default { |
|
|
|
}, |
|
|
|
label: { |
|
|
|
text: wp.name || `WP${index + 1}`, |
|
|
|
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px Microsoft YaHei`, |
|
|
|
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`, |
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)), |
|
|
|
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#333333'), |
|
|
|
outlineColor: Cesium.Color.BLACK, |
|
|
|
outlineWidth: 1, |
|
|
|
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#2c2c2c'), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'), |
|
|
|
outlineWidth: 0.5, |
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE |
|
|
|
} |
|
|
|
}); |
|
|
|
@ -1349,14 +1372,11 @@ export default { |
|
|
|
properties: { routeId: routeId }, |
|
|
|
label: { |
|
|
|
text: labelText, |
|
|
|
font: '16px Microsoft YaHei', |
|
|
|
fillColor: Cesium.Color.fromCssColorString('#333333'), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString('#e0e0e0'), |
|
|
|
outlineWidth: 1, |
|
|
|
font: '15px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif', |
|
|
|
fillColor: Cesium.Color.fromCssColorString('#2c2c2c'), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'), |
|
|
|
outlineWidth: 0.5, |
|
|
|
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), |
|
|
|
@ -1452,11 +1472,11 @@ export default { |
|
|
|
}, |
|
|
|
label: { |
|
|
|
text: wpName, |
|
|
|
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px Microsoft YaHei`, |
|
|
|
font: `${wp.labelFontSize != null ? Math.min(28, Math.max(10, Number(wp.labelFontSize))) : 14}px PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif`, |
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)), |
|
|
|
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#333333'), |
|
|
|
outlineColor: Cesium.Color.BLACK, |
|
|
|
outlineWidth: 1, |
|
|
|
fillColor: Cesium.Color.fromCssColorString(wp.labelColor || '#2c2c2c'), |
|
|
|
outlineColor: Cesium.Color.fromCssColorString('#e8e8e8'), |
|
|
|
outlineWidth: 0.5, |
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE |
|
|
|
} |
|
|
|
}); |
|
|
|
@ -1469,11 +1489,14 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// 绘制包含弧线的 Polyline(lineWidth/lineMaterial 已在上方与航点样式一起计算,默认紫色线宽3) |
|
|
|
// 绘制主航线:用 CallbackProperty 从各航点实体取当前位置,拖拽时实时连线 |
|
|
|
const that = this; |
|
|
|
const routeEntity = this.viewer.entities.add({ |
|
|
|
id: lineId, |
|
|
|
polyline: { |
|
|
|
positions: finalPathPositions, |
|
|
|
positions: new Cesium.CallbackProperty(function () { |
|
|
|
return that.getRouteLinePositionsFromWaypointEntities(routeId) || finalPathPositions; |
|
|
|
}, false), |
|
|
|
width: lineWidth, |
|
|
|
material: lineMaterial, |
|
|
|
arcType: Cesium.ArcType.NONE, |
|
|
|
@ -1486,6 +1509,41 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 从各航点/弧线/盘旋实体取当前位置,供主航线折线实时连线(拖拽时动态跟随) */ |
|
|
|
getRouteLinePositionsFromWaypointEntities(routeId) { |
|
|
|
const ids = this._routeWaypointIdsByRoute && this._routeWaypointIdsByRoute[routeId]; |
|
|
|
if (!ids || !ids.length || !this.viewer) return null; |
|
|
|
const now = Cesium.JulianDate.now(); |
|
|
|
const positions = []; |
|
|
|
for (let i = 0; i < ids.length; i++) { |
|
|
|
const ent = this.viewer.entities.getById(`wp_${routeId}_${ids[i]}`); |
|
|
|
if (ent && ent.position) { |
|
|
|
const pos = ent.position.getValue(now); |
|
|
|
if (pos) { |
|
|
|
positions.push(Cesium.Cartesian3.clone(pos)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
const arcEnt = this.viewer.entities.getById(`arc-line-${routeId}-${i}`); |
|
|
|
if (arcEnt && arcEnt.polyline && arcEnt.polyline.positions) { |
|
|
|
const arr = arcEnt.polyline.positions.getValue(now); |
|
|
|
if (arr && arr.length) { |
|
|
|
for (let k = 0; k < arr.length; k++) positions.push(Cesium.Cartesian3.clone(arr[k])); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
const holdEnt = this.viewer.entities.getById(`hold-line-${routeId}-${i}`); |
|
|
|
if (holdEnt && holdEnt.polyline && holdEnt.polyline.positions) { |
|
|
|
const arr = holdEnt.polyline.positions.getValue(now); |
|
|
|
if (arr && arr.length) { |
|
|
|
for (let k = 0; k < arr.length; k++) positions.push(Cesium.Cartesian3.clone(arr[k])); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return positions.length > 0 ? positions : null; |
|
|
|
}, |
|
|
|
|
|
|
|
// 计算单个点的转弯半径 |
|
|
|
getWaypointRadius(wp) { |
|
|
|
const speed = wp.speed || 800; |
|
|
|
@ -1883,6 +1941,7 @@ export default { |
|
|
|
} |
|
|
|
if (shouldRemove) this.viewer.entities.remove(entity); |
|
|
|
} |
|
|
|
if (this._routeWaypointIdsByRoute) delete this._routeWaypointIdsByRoute[routeId]; |
|
|
|
this.allEntities = this.allEntities.filter(item => item.id !== routeId && item.id !== `route-platform-${routeId}` && item.id !== `route-platform-label-${routeId}`); |
|
|
|
}, |
|
|
|
/** |
|
|
|
@ -2042,6 +2101,43 @@ export default { |
|
|
|
|
|
|
|
if (this.isDrawing) return; |
|
|
|
|
|
|
|
// 若上一次为拖拽结束,本次左键不打开弹窗 |
|
|
|
if (this.lastClickWasDrag) { |
|
|
|
this.lastClickWasDrag = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
// 航线复制预览模式:左键放置 |
|
|
|
if (this.copyPreviewWaypoints && this.copyPreviewWaypoints.length > 0) { |
|
|
|
const placePosition = this.getClickPosition(click.position); |
|
|
|
if (placePosition) { |
|
|
|
const carto = Cesium.Cartographic.fromCartesian(placePosition); |
|
|
|
const placeLat = Cesium.Math.toDegrees(carto.latitude); |
|
|
|
const placeLng = Cesium.Math.toDegrees(carto.longitude); |
|
|
|
const first = this.copyPreviewWaypoints[0]; |
|
|
|
const firstLat = parseFloat(first.lat); |
|
|
|
const firstLng = parseFloat(first.lng); |
|
|
|
const dLat = placeLat - firstLat; |
|
|
|
const dLng = placeLng - firstLng; |
|
|
|
const isHold = (pt) => pt === 'hold_circle' || pt === 'hold_ellipse'; |
|
|
|
const points = this.copyPreviewWaypoints.map((wp, idx) => ({ |
|
|
|
name: isHold(wp.pointType || wp.point_type) ? 'HOLD' : `WP${idx + 1}`, |
|
|
|
lat: parseFloat(wp.lat) + dLat, |
|
|
|
lng: parseFloat(wp.lng) + dLng, |
|
|
|
alt: wp.alt != null ? Number(wp.alt) : 5000, |
|
|
|
speed: wp.speed != null ? wp.speed : 800, |
|
|
|
startTime: wp.startTime || 'K+00:00:00', |
|
|
|
turnAngle: wp.turnAngle != null ? wp.turnAngle : (idx === 0 || idx === this.copyPreviewWaypoints.length - 1 ? 0 : 45), |
|
|
|
labelFontSize: wp.labelFontSize != null ? wp.labelFontSize : 14, |
|
|
|
labelColor: wp.labelColor || '#333333', |
|
|
|
...(wp.pointType && { pointType: wp.pointType }), |
|
|
|
...(wp.holdParams != null && { holdParams: typeof wp.holdParams === 'string' ? wp.holdParams : JSON.stringify(wp.holdParams) }) |
|
|
|
})); |
|
|
|
this.clearCopyPreview(); |
|
|
|
this.$emit('route-copy-placed', points); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const pickedObject = this.viewer.scene.pick(click.position); |
|
|
|
if (Cesium.defined(pickedObject) && pickedObject.id) { |
|
|
|
const entity = pickedObject.id; |
|
|
|
@ -2116,6 +2212,100 @@ export default { |
|
|
|
this.lastEntityClickTime = 0; |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); |
|
|
|
|
|
|
|
// 航点拖拽:左键按下仅记录 pending,移动超过阈值后才真正开始拖拽(避免小范围移动带跑地图) |
|
|
|
this.handler.setInputAction((click) => { |
|
|
|
if (this.isDrawing || this.copyPreviewWaypoints) return; |
|
|
|
const pickedObject = this.viewer.scene.pick(click.position); |
|
|
|
if (!Cesium.defined(pickedObject) || !pickedObject.id) return; |
|
|
|
const now = Cesium.JulianDate.now(); |
|
|
|
const props = pickedObject.id.properties ? pickedObject.id.properties.getValue(now) : null; |
|
|
|
if (!props || !props.isMissionWaypoint) return; |
|
|
|
const routeId = props.routeId && (props.routeId.getValue ? props.routeId.getValue() : props.routeId); |
|
|
|
const dbId = props.dbId && (props.dbId.getValue ? props.dbId.getValue() : props.dbId); |
|
|
|
if (routeId == null || dbId == null) return; |
|
|
|
if (this.routeLocked[routeId]) return; |
|
|
|
const entity = pickedObject.id; |
|
|
|
const carto = Cesium.Cartographic.fromCartesian(entity.position.getValue(Cesium.JulianDate.now())); |
|
|
|
this.waypointDragPending = { |
|
|
|
entity, |
|
|
|
routeId, |
|
|
|
dbId, |
|
|
|
originalAlt: carto.height, |
|
|
|
startScreenX: click.position.x, |
|
|
|
startScreenY: click.position.y |
|
|
|
}; |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN); |
|
|
|
|
|
|
|
// 拖拽 / 复制预览:共用一个 MOUSE_MOVE;航点拖拽需超过阈值才生效 |
|
|
|
this.handler.setInputAction((movement) => { |
|
|
|
if (this.waypointDragPending) { |
|
|
|
const dx = movement.endPosition.x - this.waypointDragPending.startScreenX; |
|
|
|
const dy = movement.endPosition.y - this.waypointDragPending.startScreenY; |
|
|
|
const dist = Math.sqrt(dx * dx + dy * dy); |
|
|
|
if (dist >= this.WAYPOINT_DRAG_THRESHOLD_PX) { |
|
|
|
if (this.entityClickDebounceTimer) { |
|
|
|
clearTimeout(this.entityClickDebounceTimer); |
|
|
|
this.entityClickDebounceTimer = null; |
|
|
|
} |
|
|
|
this.waypointDragging = { |
|
|
|
entity: this.waypointDragPending.entity, |
|
|
|
routeId: this.waypointDragPending.routeId, |
|
|
|
dbId: this.waypointDragPending.dbId, |
|
|
|
originalAlt: this.waypointDragPending.originalAlt |
|
|
|
}; |
|
|
|
this.waypointDragPending = null; |
|
|
|
// 拖拽航点时锁定地图,方便操作 |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.screenSpaceCameraController) { |
|
|
|
this.waypointDragCameraInputsEnabled = this.viewer.scene.screenSpaceCameraController.enableInputs; |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = false; |
|
|
|
} |
|
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
|
if (pos) { |
|
|
|
this.waypointDragging.entity.position = pos; |
|
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.waypointDragPending) return; |
|
|
|
} |
|
|
|
if (this.waypointDragging) { |
|
|
|
const pos = this.getClickPositionWithHeight(movement.endPosition, this.waypointDragging.originalAlt); |
|
|
|
if (pos) { |
|
|
|
this.waypointDragging.entity.position = pos; |
|
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.copyPreviewWaypoints && this.copyPreviewWaypoints.length >= 2) { |
|
|
|
const cartesian = this.getClickPosition(movement.endPosition); |
|
|
|
if (cartesian) { |
|
|
|
this.copyPreviewMouseCartesian = cartesian; |
|
|
|
this.updateCopyPreviewPolyline(); |
|
|
|
if (this.viewer.scene.requestRender) this.viewer.scene.requestRender(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
// 拖拽结束:恢复地图操作、持久化新位置并清除状态;未超过阈值则只清 pending(视为点击) |
|
|
|
this.handler.setInputAction((click) => { |
|
|
|
if (this.waypointDragging) { |
|
|
|
if (this.viewer && this.viewer.scene && this.viewer.scene.screenSpaceCameraController && this.waypointDragCameraInputsEnabled !== undefined) { |
|
|
|
this.viewer.scene.screenSpaceCameraController.enableInputs = this.waypointDragCameraInputsEnabled; |
|
|
|
this.waypointDragCameraInputsEnabled = undefined; |
|
|
|
} |
|
|
|
const entity = this.waypointDragging.entity; |
|
|
|
const routeId = this.waypointDragging.routeId; |
|
|
|
const dbId = this.waypointDragging.dbId; |
|
|
|
const pos = entity.position.getValue(Cesium.JulianDate.now()); |
|
|
|
const ll = this.cartesianToLatLngAlt(pos); |
|
|
|
if (ll) { |
|
|
|
this.$emit('waypoint-position-changed', { dbId, routeId, lat: ll.lat, lng: ll.lng, alt: ll.alt }); |
|
|
|
} |
|
|
|
this.lastClickWasDrag = true; |
|
|
|
this.waypointDragging = null; |
|
|
|
} |
|
|
|
this.waypointDragPending = null; |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP); |
|
|
|
} catch (error) { |
|
|
|
console.error('地图错误:', error) |
|
|
|
// 如果Cesium加载失败,显示错误信息 |
|
|
|
@ -2131,6 +2321,12 @@ export default { |
|
|
|
if (this.isDrawing) { |
|
|
|
return; |
|
|
|
} |
|
|
|
// 若处于航线复制预览,右键取消 |
|
|
|
if (this.copyPreviewWaypoints) { |
|
|
|
this.clearCopyPreview(); |
|
|
|
this.contextMenu.visible = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 隐藏之前可能显示的菜单 |
|
|
|
this.contextMenu.visible = false; |
|
|
|
@ -4149,6 +4345,23 @@ export default { |
|
|
|
lng: Cesium.Math.toDegrees(cartographic.longitude) |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 笛卡尔坐标转经纬高(含高度,用于航点拖拽持久化) */ |
|
|
|
cartesianToLatLngAlt(cartesian) { |
|
|
|
if (!Cesium.defined(cartesian)) return null |
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) |
|
|
|
return { |
|
|
|
lat: Cesium.Math.toDegrees(cartographic.latitude), |
|
|
|
lng: Cesium.Math.toDegrees(cartographic.longitude), |
|
|
|
alt: cartographic.height |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 屏幕坐标转笛卡尔,指定高度(用于拖拽时保持航点高度) */ |
|
|
|
getClickPositionWithHeight(pixelPosition, height) { |
|
|
|
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid) |
|
|
|
if (!Cesium.defined(cartesian)) return null |
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) |
|
|
|
return Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, height != null ? height : cartographic.height) |
|
|
|
}, |
|
|
|
degreesToDMS(decimalDegrees) { |
|
|
|
const degrees = Math.floor(decimalDegrees) |
|
|
|
const minutesDecimal = (decimalDegrees - degrees) * 60 |
|
|
|
@ -4442,6 +4655,65 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 右键航线:上锁/解锁,上锁后该航线不可编辑 */ |
|
|
|
/** 右键菜单「复制航线」:通知父组件,由父组件拉取航点后调用 startRouteCopyPreview */ |
|
|
|
handleCopyRouteFromMenu() { |
|
|
|
const ed = this.contextMenu.entityData; |
|
|
|
if (!ed || ed.type !== 'route' || ed.routeId == null) { |
|
|
|
this.contextMenu.visible = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
this.contextMenu.visible = false; |
|
|
|
this.$emit('copy-route', ed.routeId); |
|
|
|
}, |
|
|
|
/** 开始航线复制预览:整条航线跟随鼠标,左键放置后父组件弹窗保存 */ |
|
|
|
startRouteCopyPreview(waypoints) { |
|
|
|
if (!waypoints || waypoints.length < 2) return; |
|
|
|
this.clearCopyPreview(); |
|
|
|
this.copyPreviewWaypoints = waypoints; |
|
|
|
this.copyPreviewMouseCartesian = this.viewer.camera.pickEllipsoid( |
|
|
|
new Cesium.Cartesian2(this.viewer.scene.canvas.clientWidth / 2, this.viewer.scene.canvas.clientHeight / 2), |
|
|
|
this.viewer.scene.globe.ellipsoid |
|
|
|
) || Cesium.Cartesian3.fromDegrees(parseFloat(waypoints[0].lng), parseFloat(waypoints[0].lat), Number(waypoints[0].alt) || 5000); |
|
|
|
this.updateCopyPreviewPolyline(); |
|
|
|
}, |
|
|
|
/** 更新复制预览折线:以当前鼠标位置为起点偏移整条航线 */ |
|
|
|
updateCopyPreviewPolyline() { |
|
|
|
if (!this.copyPreviewWaypoints || this.copyPreviewWaypoints.length < 2 || !this.copyPreviewMouseCartesian) return; |
|
|
|
const first = this.copyPreviewWaypoints[0]; |
|
|
|
const firstLon = parseFloat(first.lng) * (Math.PI / 180); |
|
|
|
const firstLat = parseFloat(first.lat) * (Math.PI / 180); |
|
|
|
const carto = Cesium.Cartographic.fromCartesian(this.copyPreviewMouseCartesian); |
|
|
|
const dLon = carto.longitude - firstLon; |
|
|
|
const dLat = carto.latitude - firstLat; |
|
|
|
const positions = this.copyPreviewWaypoints.map(wp => |
|
|
|
Cesium.Cartesian3.fromRadians( |
|
|
|
parseFloat(wp.lng) * (Math.PI / 180) + dLon, |
|
|
|
parseFloat(wp.lat) * (Math.PI / 180) + dLat, |
|
|
|
Number(wp.alt) || 5000 |
|
|
|
) |
|
|
|
); |
|
|
|
if (this.copyPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.copyPreviewEntity); |
|
|
|
this.copyPreviewEntity = null; |
|
|
|
} |
|
|
|
this.copyPreviewEntity = this.viewer.entities.add({ |
|
|
|
polyline: { |
|
|
|
positions: positions, |
|
|
|
width: 4, |
|
|
|
material: Cesium.Color.fromCssColorString('rgba(0, 120, 255, 0.75)'), |
|
|
|
arcType: Cesium.ArcType.NONE |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
/** 清除航线复制预览 */ |
|
|
|
clearCopyPreview() { |
|
|
|
if (this.copyPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.copyPreviewEntity); |
|
|
|
this.copyPreviewEntity = null; |
|
|
|
} |
|
|
|
this.copyPreviewWaypoints = null; |
|
|
|
this.copyPreviewMouseCartesian = null; |
|
|
|
}, |
|
|
|
toggleRouteLock() { |
|
|
|
const ed = this.contextMenu.entityData; |
|
|
|
if (!ed || ed.type !== 'route' || ed.routeId == null) { |
|
|
|
@ -4450,7 +4722,6 @@ export default { |
|
|
|
} |
|
|
|
const routeId = ed.routeId; |
|
|
|
const nextLocked = !this.routeLocked[routeId]; |
|
|
|
this.$set(this.routeLocked, routeId, nextLocked); |
|
|
|
this.contextMenu.visible = false; |
|
|
|
this.$message && this.$message.success(nextLocked ? '航线已上锁,无法修改' : '航线已解锁,可以编辑'); |
|
|
|
this.$emit('route-lock-changed', { routeId, locked: nextLocked }); |
|
|
|
@ -5933,6 +6204,23 @@ export default { |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
/* 航线复制预览提示 */ |
|
|
|
.copy-route-tip { |
|
|
|
position: absolute; |
|
|
|
bottom: 60px; |
|
|
|
left: 50%; |
|
|
|
transform: translateX(-50%); |
|
|
|
z-index: 99; |
|
|
|
background: rgba(0, 120, 255, 0.9); |
|
|
|
color: #fff; |
|
|
|
padding: 10px 20px; |
|
|
|
border-radius: 6px; |
|
|
|
font-size: 14px; |
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2); |
|
|
|
pointer-events: none; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
/* 地图右下角信息面板:比例尺在上、经纬度在下,整体略下移减少遮挡 */ |
|
|
|
.map-info-panel { |
|
|
|
position: absolute; |
|
|
|
|